پیدا کردن اسامی خاص توی متون یکی از تسکهای پیشپردازشی توی پردازش زبان طبیعی محسوب میشه. توی این تسک دنبال این هستیم تا اسامی خاص مثل اسم افراد یا اسم سازمانها و یا اسم مکانها رو بتونیم پیدا کنیم و تگ بزنیم. تگ هر یک از این کلمات میتونه به عنوان فیچر برای تسکهای بعدی پردازش زبان طبیعی محسوب بشه. البته دیده شده که همین تسک به تنهایی هم میتونه کاربردهایی داشته باشه. مثلا قابلیت X-ray توی کیندلهای آمازون دقیقا از همین استفاده میکنه. اگه از کتابخوانهای آمازون استفاده کرده باشید میدونید که موقع مطالعه کتابتون میتونید از این قابلیت استفاده کنید و اطلاعات مختصری درباره هر یک از اسامی خاص توی متن رو ببینید. تو این پست قصد داریم درباره این تسک و روند پیشرفتش و مدلهای موجود صحبت کنیم.
نامداران وارد میشوند!: معرفی مساله
برای اینکه بتونیم این تسک رو انجام بدیم روشهای متفاوتی در طول زمان استفاده شده. روشهای اولیه مبتنی بر دانش بودند. به این صورت که یه دیکشنری از اسامی خاص موجود بوده و توی متن باید بر اساس اون اسامی خاص برچسب میخوردن. بعد از اون که یادگیری ماشینی سری توی سرا در آورد سراغ روشهای بدون نظارت رفتن که این مدلها پیچیدگی زیادی داشتن و کیفیت کار در حد مطلوب نبود. البته علت اینکه ملت سراغ این روش رفتن این بوده که اون موقع داده برچسب خورده مثل امروز فراوون! نبوده ولی امروزه که دادههای برچسب خورده زیاد داریم بیشتر سراغ روشهای با نظارت رفتن و از این روشها جواب گرفتن. تا قبل از ظهور یادگیری عمیق از روشهای آماری استفاده میشده که در اون روشهای نیاز به feature engineering شدید بوده و همین کار رو سخت میکرده. علتش هم اینه که توی این حوزه بدست آوردن ویژگیهایی برای کلمات که بشه با اونا کلمات رو برچسب اسامی خاص زد خیلی سخته. اما حالا که یادگیری عمیق داریم دیگه این فاز رو به عهده شبکههای عصبی گذاشتیم و از شرش خلاص شدیم. البته لازمه که دیتاستهای قدر قدرتی! داشته باشیم که اونها رو هم معرفی میکنیم.
یک سیستم مبتنی بر یادگیری عمیق برای تشخیص اسامی خاص سه قسمت اصلی داره که توی تصویر زیر مشخصه. در ادامه درباره هر یک از این قسمتا توضیحاتی میدیم.
معماری کلی یه سیستم تشخیص اسامی خاص که مبتنی بر یادگیری عمیق هست
معماری کلی یه سیستم تشخیص اسامی خاص که مبتنی بر یادگیری عمیق هست
بازنمایی دیتای ورودی
توی هر مساله پردازش زبان طبیعی یکی از مهمترین قسمتها نحوه ورودی دادن دیتا ست. ورودی مساله ما از جنس کلمات و جملهها هستن اما باید تبدیل به اعداد بشن. اینجا ست که مساله word embedding وارد بازی میشه. شاید جا داشته باشه که توی یه پست جدا درباره انواع و اقسام روشهاش توضیح بدیم ولی اینجا هم یه سری توضیحات میدیم که در کار خود وا نمانیم!!
یکی از روشهای رایج، بازنمایی در سطح کلمه ست. برای این کار چندین روش وجود داره که یکی از معروفترین اونها Word2Vec هست که همین روش رو به دو صورت continues bag of words یا CBOW و skip-gram میشه انجام داد. تصویر کلی این دو روش رو در زیر میبینید. از معایب بزرگ این روش اینه که محتوا رو بازتاب نمیدن. ینی اینکه برای کلمه مثل "شیر" کلا یه بردار ایجاد میکنن. اما پر واضحه که این کلمه در context های مختلف میتونه معانی متفاوت داشته باشه. از طرفی به برخی ساختارهای زیرکلمهای توی زبان هم توجه نمیکنن. اما یه خوبی بزرگی که دارن اینه که معانی رو حفظ میکنن. ینی کلمات هممعنی برداراشون هم فاصله کمی با هم دارن.
مدل CBOW در سمت چپ که از روی پنجره کلمات اطراف کلمه هدف یه بازنمایی برای کلمه هدف ایجاد میکنه و در سمت راست هم مدل skip-gram که از روی یه کلمه مرکزی برای کلمات اطراف اون یه بازنمایی میسازه.
مدل CBOW در سمت چپ که از روی پنجره کلمات اطراف کلمه هدف یه بازنمایی برای کلمه هدف ایجاد میکنه و در سمت راست هم مدل skip-gram که از روی یه کلمه مرکزی برای کلمات اطراف اون یه بازنمایی میسازه.
از روشهای بازنمایی سطح کلمات که بگذریم میتونیم بریم سراغ بازنمایی کلمات در سطح حروف! توی این روشهای به ویژگیهای زیرکلمهای یه کلمه توجه میشه. از طرفی یکی دیگه از مشکلات بازنمایی سطح کلمه رو که در بالا نگفتیم رو هم نداره و اون کلمات خارج از واژگان هست. توی تصویر زیر نشون دادیم که میشه با استفاده از بازنمایی سطح حروف بردارهای حساس به محتوا تولید کرد. برای این کار از شبکههای LSTM یا CNN استفاده میشه. البته این روش در مقایسه با روش قبلی که گفتیم طبیعتا به محاسبات بیشتر و همچنین به منابع پردازشی بیشتر احتیاج داره چرا که روشهای قبلی حتی بعضا جزو یادگیری عمیق هم محسوب نمیشن اما توی این روش شما باید یه شبکه عمیق رو آموزش بدید.
حروف به صورت بردار one-hot به یه شبکه LSTM دوطرفه داده میشن. دوطرفه بودن باعث میشه که اطلاعات پنهان مربوط به محتوا از حرف اول کلمه George تا حرف آخر کلمه Washington در جهت قرمز و اطلاعات پنهان محتوا از حرف آخر کلمه born تا حرف اول کلمه Washington در جهت آبی برای کلمه Washington ذخیره بشه. بعد میان این دو تا بردار رو بهم پیوست میدن.
حروف به صورت بردار one-hot به یه شبکه LSTM دوطرفه داده میشن. دوطرفه بودن باعث میشه که اطلاعات پنهان مربوط به محتوا از حرف اول کلمه George تا حرف آخر کلمه Washington در جهت قرمز و اطلاعات پنهان محتوا از حرف آخر کلمه born تا حرف اول کلمه Washington در جهت آبی برای کلمه Washington ذخیره بشه. بعد میان این دو تا بردار رو بهم پیوست میدن.
روشهایی هم هستن که اومدن بردار کلمه حاصل از دو روش بالا رو بهم پیوست میدن و همزمان استفاده میکنن. اما یه تکنیک دیگه ای هم سوار میکنن. همونطور که اول پست گفتیم قبل از یادگیری عمیق مجبور بودیم فاز مهندسی ویژگی داشته باشیم. اما الان که شبکه عمیق داریم هم دلیل نمیشه اصلا از ویژگیهایی که میشه به صورت دستی برای این تسک درآورد استفاده نکنیم! چراااا؟ دلیلش واضحه. چون میتونه فضای جستوجو رو برای شبکه عصبی کوچیک کنه و در نتیجه هم سریعتر به کیفیت مطلوب برسه و هم داده کمتری رو مصرف کنه. اما یه مقدار باید باهوش باشیم که بدونیم چهطوری اینا رو کنار هم قرار بدیم. توی تصویر زیر معماری این سیستم نشون داده شده. مثلا فرض کنین چند تا فیچر دستی در آوردیم یکی برچسب POS هر کلمه و یکی هم اینکه آیا حرف اولش بزرگ هست یا نه ( توی زبان انگلیسی ) و یکی هم مثلا وجود کسره اضافه بعد از کلمه ( توی زبان فارسی میتونه به تشخیص مرزهای یه کلمه خاص کمک کنه ) . میایم بردار مربوط به این ویژگیهای دستی رو با بردار حاصل از بازنمایی سطح کلمه و بردار حاصل از بازنمایی سطح حروف هر کلمه پیوست میکنیم. بعد این بردار رو به شبکه دوطرفه LSTM میدیم و بعد حالات پنهان خروجی LSTM به طور همزمان به یه شبکه خودرمزنگار و برچسب زننده میدیم. خروجی شبکه خودرمزنگار باید با بردارهای حاصل از ویژگیهای دستی برابر باشه در نتیجه میزان loss به صورت ترکیبی میتونه حساب بشه و loss هر دو شبکه به صورت همزمان سعی میکنه کمینه بشه. معماری این روش رو هم میتونین توی تصویر زیر ببینین.
معماری نحوه اضافه کردن ویژگیهای دستی در سیستم تشخیص اسامی خاص
معماری نحوه اضافه کردن ویژگیهای دستی در سیستم تشخیص اسامی خاص
بازنمایی وابستگی کلمات
همون طور که توی قسمت اول گفتیم بعد از بخش word embeddings نوبت به بخش Generate Representation using Dependencies میرسه. توی بخش قبل سعی کردیم هر کلمه رو به یه بازنمایی عددی تبدیل کنیم. اما برای اینکه بتونیم مساله رو کامل حل کنیم به یه سری اطلاعات دیگه هم نیاز داریم. جملات توی زبان طبیعی دنبالهای از کلمات هستن و به عبارت دیگه هر دنبالهای از کلمات توی زبان طبیعی نمیتونه معنیدار باشه. همین موضوع ینی اینکه توی وابستگی بین کلمات هم اطلاعاتی نهفته ست. پس باید بتونیم از این وابستگیها هم استفاده کنیم. طبعا اولین گزینهای که به ذهن میرسه استفاده از RNN هست اما میدونیم که با مشکل فراموشی بردار گرادیان مواجه میشیم. به خاطر همین از LSTM استفاده میشه. در واقع خروجیهای فاز قبلی که بازنمایی کلمات هستن رو میشه به شبکههای LSTM داد و با استفاده از اونها وابستگی بین کلمات رو کپچر کرد. اما طبیعتا استفاده از این نوع شبکهها امکان موازی سازی رو از ما میگیره که همین میتونه نقطه ضعف این روش باشه. به خاطر همین سعی شده تا از شبکههای کانولوشنی هم استفاده بشه. در این روش با استفاده از بردار N بعدی برای هر کلمه و جمله متناظر اون وابستگی بین دادهای بازنمایی میشه. البته این مدلها توی پردازش جملات طولانی به مشکل میخورن که برای مقابله با این مشکل مدل Iterated Dilated Convolutions معرفی شده. ID-CNN ها به صورت عمق ثابت به طوری که طول موثر ورودی به صورت نمایی با این عمق رشد کنه طراحی شدن. بر خلاف LSTM که پیچیدگی محاسباتی اون به صورت خطی همراه با طول ورودی رشد میکنه این شبکه میتونه به صورت موازی عملیات پردازش روی جملهها رو انجام بده. در عمل نشون داده شده که سرعت این شبکه در حدود ۱۴ الی ۲۰ برابر شبکههای LSTM هست در حالیکه دقت این شبکه فقط کمی بدتر از LSTM هست. در زیر میتونید معماری این نوع شبکهها رو ببینید.
یک بلوک ID-CNN که طول فیلتر آن ۳ و عمیق آن ۴ است. با این بلوک یک جمله حداکثر ۱۵ کلمهای رو میشه پردازش کرد.
یک بلوک ID-CNN که طول فیلتر آن ۳ و عمیق آن ۴ است. با این بلوک یک جمله حداکثر ۱۵ کلمهای رو میشه پردازش کرد.
اما در نهایت با ظهور و بروز ترنسفورمرها کلا بازی عوض شد! این شبکهها تنها با درنظر گرفتن مکانیزم توجه میتونن وابستگیهای دادهای رو منظور کنن. بردار توجهی که از داده ورودی به دست میآد رو میشه به صورت موازی به دست آورد و بعد این بردار رو به یه شبکه fully connected داد تا فضای ورودی رو انکود کنه. بردار توجه یه کلمه به صورت زیر به دست میاد:
برای اینکه ترنسفورمرها رو بهتر بفهمید توصیه میکنم حتما این سری پستها رو مطالعه کنین. برای اینکه تشخیص اسامی خاص انجام بشه با استفاده از بخش انکودر توی ترنسفورها فضای ورودی رو انکود میکنن و بعد با استفاده از یه شبکه برچسبزن کار برچسب زدن رو انجام میدن.
یه پیاده سازی خوب از ترنسفورمرها، BERT هست. برت یه معماری عمومی با استفاده از ترنسفورمر هست که میشه به تسکهای مختلفی توی حوزه پردازش زبان طبیعی اون رو اپلای کرد. برای این کار باید فرمت ورودی و خروجی برت رو بشناسین و همچنین روی دیتای موردنظرتون اون رو fine-tune بکنین. توی تصویر زیر نحوه ورودی دادن به شبکه برت توی یه تسک NER رو میتونین ببینین.
- چهارشنبه ۲۴ دی ۹۹