رفتن به نوشته‌ها

شبکه‌های عصبی بازگشتی – قسمت دوم (پیاده‌سازی)

در قسمت قبلی با مفاهیم مربوط به شبکه بازگشتی ساده (Simple RNN) آشنا شدیم. در این قسمت اون چیزی که گفته شد رو با استفاده از تنسورفلو پیاده‌سازی میکنیم. برای همین یک شبکه بسیار ساده برای تشخیص احساسات متن (sentiment) روی دادگان IMDB آموزش میدهیم.

خب اولین کاری که باید انجام بدید این است که تنسورفلو رو نصب کنید. برای اینکه تنسورفلو رو چطوری نصب کنید میتونید به این لینک مراجعه کنید. ولی به طور کلی میتونید با دستور زیر تنسورفلو رو نصب کنید:

خب بعد از اینکه تنسورفلو رو نصب کردید یک پروژه پایتون درست کنید (ترجیحا پایتون ۳) و چک کنید ببینید که آیا تنسورفلو به درستی نصب شده است یا خیر. برای اینکار میتونید دستور زیر رو اجرا کنید و اگر خروجی داد یعنی اینکه موفقیت‌آمیز بوده است.

خب امیدوارم که تونسته باشید نصبش کرده‌ باشید.

در مرحله اول لازم است که دادگان IDMB رو دانلود کنید. خوشبختانه تنسورفلو این دادگان رو در ماژول dataset قرار داده است و شما به راحتی با کمک دستور زیر به داده‌های آموزش و آزمون دسترسی پیدا کنید.

معمولا در هر دادگانی تعدادی کلمه وجود دارند که فرکانس آنها بسیار کم است و عملا در شبکه آموزش نمیبیند. برای اینکه از این موضوع جلوگیری کنیم همانطور که در دستور بالا میبینید تنها ۱۰۰۰۰ کلمه پرکاربرد را انتخاب میکنیم و مابقی کلمات دور ریخته میشوند (به طور دقیق‌تر کلماتی که جز این ۱۰۰۰۰ کلمه پرکاربرد نباشد، نادیده گرفته میشوند). همچنین هر کلمه به یک آیدی نگاشت میشود و در نتیجه جمله‌ی اول داده‌های آموزشی به صورت زیر خواهد بود (ورودی شبکه عصبی باید به عددی باشد و به همین خاطر ما این نگاشت را انجام میدهیم):

در شبکه‌های عصبی برای اینکه بتوانیم به صورت دسته‌ای (batch) آموزش بدهیم، لازم است تا طول جملات ما یکسان باشند (مثلا همه جملات طولشون ۲۰۰ در نظر گرفته شود). بخاطر همین باید کاری کنیم که طول تمام جملات پیکره ما یکسان شوند. کاری که معمولا صورت میگیرد این است که به انتهای یا ابتدای جمله 0 اضافه میشود. ما هم همین کارو میکنیم و همه جملات رو به طول ثابت ۲۵۶ میبریم:

که اگر حالا جملات رو چاپ کنیم چیزی شبیه زیر خواهیم داشت:

همانطور که در بالا دیدید هنگام دانلود، داده‌های ما به دو قسمت تقسیم شدند آموزش و آزمون (test و train). برای اینکه بتونیم پارامترها رو تنظیم کنیم بهتر است که داده‌های validation هم ایجاد کنیم. برای اینکار ۱۰۰۰۰ نمونه از داده‌های آموزش رو برای validation در نظر میگیریم:

۱) پیاده‌سازی در کراس

خب حالا به تعریف مدل میرسیم. همانطور که در قسمت شبکه‌های عصبی بازگشتی دیدیم، اینجوری بود که ما داده‌هامون رو به بردارهای نهفته (embedding) (مثلا ورد۲وک) تبدیل میکردیم و سپس به شبکه بازگشتی میدادیم و در نهایت یک بردار چگال (dense) به دست می‌آوردیم و از اون برای دسته‌بندی استفاده میکردیم. همین کار و دقیقا میتونیم اینجا هم انجام بدیم و برای اینکار به صورت زیر عمل میکنیم:

به طور کلی کراس دو تا API برای ساختن مدل‌ها ارائه میکند که یکی Sequential و دیگری Functional است. که در اینجا ما از Sequential استفاده کردیم و یکی یکی لایه‌هامون رو بهش اضافه کردیم. و در نهایت هم مدل رو کامپایل کردیم. برای بهینه کننده (optimizer) از آدم (adam) و برای سنجش از معیار دقت (accuracy) استفاده کردیم. و چون تسک ما تشخیص یک کامنت مثبت یا منفی است از binary_crossentropy استفاده شده است (از categorical_crossentropy هم می‌توانستیم استفاده کنیم).

تنها نکته‌ای که در بالا احتمالا براتون وجود داشته باشه لایه‌ی embedding هست. ما میتونیم از لایه‌های از قبل آموزش داده شده استفاده کنیم (که این بهتر است) و هم اینکه خودمون لایه‌ embedding تعبیه کنیم. در اینجا برای سادگی من یک لایه embedding درست کردم که اندازه اش ماتریس ۱۰۰۰۰ (تعداد کلمات) و طول هر بردارش ۱۶ است (طول بردار هر کلمه ۱۶ است).

برای آموزش تنها کافی است که داده‌های آموزش و برچسب آنها را به مدل بفرستید و تعداد epochها و اندازه batch رو مشخص کنید:

و در نهایت هم میتونید روی دادگان آزمون (test) دقت مدل خودتون رو ارزیابی کنید:

شما تونستید یک شبکه عصبی بازگشتی درست کنید و آموزش بدید و روی دادگان idmb اون رو تست کنید 🙂

پیاده‌سازی کامل با کراس را میتوانید از اینجا مشاهده کنید.

۲) پیاده‌سازی در تنسورفلو

خب همانطور که دیدیم به راحتی میشه در کراس یک شبکه بازگشتی رو تعریف و از آن استفاده کرد. کراس یک API سطح بالا به ما می‌دهد که در بسیاری موارد می‌تونه حجم کد زدن ما را کم کند ولی خب گاهی اوقات ما می‌خواهیم کنترل عمل بیشتری داشته باشیم و مستقیم در تنسورفلو کد بزنیم. پیاده‌سازی مدل بالا در تنسورفلو هم خیلی راحت است. بیایید ببینیم چطوری کد بالا رو در تنسورفلو میتونیم پیاده‌سازی کنیم.

قسمت‌های اول مربوط به پیش‌پردازش داده مثل: دانلود دیتاست، اضافه کردن padding به انتهای اسناد دقیقا مثل قبل انجام خواهد شد:

همانطور که دیدید، در کراس ما مستقیما دیتامون رو برای تابع fit فرستادیم ولی تابع fit در تنسورفلو وجود ندارد و بخاطر همین اندکی باید تغییرات در کدمون ایجاد کنیم. برای اینکه دیتامون رو مناسب برای آموزش و آزمون کنیم از API دیتاست تنسورفلو استفاده میکنیم:

در کد بالا دوتا دیتاست درست کردیم یکی برای قسمت آزمون و یکی برای آموزش. همچنین تعداد دسته‌ها (batch) و تعداد epochها و … را تعریف کردیم. همچنین همانطور که ملاحضه می‌کنید، با استفاده از tf.data.Dataset دیگر نیازی به تعریف placeholder که در اغلب آموزش‌های قدیمی احتمالا کردید نداریم و مستقیما ورودی‌ها برای مدلمون فرستاده میشه. به طور دقیق‌تر با فراخوانی دستور iterator.get_next یک دسته (batch) از داده‌های آموزش در x و y قرار داده می‌شود.

برای تعریف بردار نهفته (embedding) تنسورفلو تابعی به نام tf.nn.embedding_lookup ارائه کرده است. این تابع یک توالی از اعداد می‌گیرد و بردارهای نهفته آنها را از ماتریس بردار کلمات واکشی می‌کند. یعنی اگر جمله اولیه ما Hello world باشه و آن را به توالی از اعداد تبدیل کنیم و بردارهای نهفته ما به طول ۲ تبدیل‌ش کنیم چیزی شبیه زیر به ما خواهد داد:

"Hello world" -> [5,3] -> [[0.01, -0.38], [-0.063, 0.0082]]

که در کد می‌تونیم آن را به صورت زیر بنویسیم:

در بالا x همان توالی ورودی ماست (کلمات اسناد ما که با اعداد صحیح بازنمایی شده‌اند). این اعداد به تابع lookup فرستاده می‌شوند و بردارهای آنها از embedding_matrix واکشی می‌شود (این ماتریس به اندازه تعداد vocab ماست).

قسمت بعدی میرسیم به تعریف مدل‌امون:

همانطور که می‌بینید خیلی شبیه کد کراس است. تنها تفاوت در اینجاست که ما ابتدا نوع سلول شبکه بازگشتی را مشخص کردیم (SimpleRNNCell) و بعد آن را به تابع dynamic_rnn ارسال می‌کنیم (در واقع تابع dynamic_rnn نقش باز کردن unroll سلول‌ها در طول زمان را برای ما خواهد داشت). همچنین همانطور که قبلا گفته شد برای دسته‌بندی ما تنها از خروجی مرحله آخر استفاده می‌کنیم در نتیجه خروجی dynamic_rnn را می‌شکونیم (split) و تنها درایه آخر آن را برمی‌داریم:

outputs[:, -1, :]

حالا نوبت به تعریف تابع خطا (loss) و بهینه‌کننده (optimizer) و معیارهای ارزیابی می‌رسد. خوشبختانه دو مورد در تنسورفلو پیاده‌سازی شده است و برای ارزیابی اگر فرض کنیم معیارمون accuracy است به سادگی میتوانیم آن را پیاده کنیم:

و در انتها نوبت به آموزش و ارزیابی مدل میرسد:

در ابتدای تعریف tf.data.Dataset اگر دقت کنید ما repeat را برابر ۵ قرار دادیم که این برابر است با تعداد epochها ما. در نتیجه while اول تا ۵ بار برای ما اجرا خواهد شد و سپس اکسپشن خواهد خورد که آن را با استفاده از tf.errors.OutOfRangeError هندل کرده‌ایم. بعد در حلقه دوم برای محاسبه دقت (accuracy) روی دیتای آزمون (test_dataset) پیمایش می‌کنیم و مدل‌امون رو ارزیابی می‌کنیم.

پیاده‌سازی کامل با تنسورفلو را میتوانید از اینجا مشاهده کنید.

۳) پیاده‌سازی در تنسورفلو ۲

خب در قسمت بالا ما باز هم از API سطح بالا استفاده کردیم و خب شاید بگید این مورد هم با کدی که در کراس زدیم زیاد فرقی نکرد! چطوری می‌تونیم کد سطح پایین‌تری بزنیم! بیایید یکم قسمت بازگشتی رو سطح‌پایین‌تر بنویسیم.

همانطور که در قسمت قبلی دیدیم داخل هر سلول شبکه بازگشتی چیزی نبود جز یک تانژانت هیپربولیک (\(tanh(W^ss_{t-1} + W^xx_i)\)) که اگر بخواهیم برای اون یک تابع بنویسیم میتونیم بگیم:

خب حالا سوال اینجاست که چطوری توالی ورودی‌امون (بردارهای کلمات) را پیمایش کنیم و اطلاعات قبلی رو به نحوی حفظ کنیم؟ خب احتمالا چیزی شبیه یک حلقه باید داشته باشیم که اطلاعات مرحله قبلی رو به مراحل بعدی بفرسته! خوشبختانه همچین چیزی رو تنسورفلو با استفاده از تابع tf.scan پیاده‌سازی شده است و می‌تونیم بگیم:

اگر میخواهید بدونید که tf.scan چطوری کار میکنه به مثال زیر نگاه کنید:

سوال دیگری که براتون پیش بیاد این است که چرا transpose انجام دادیم؟ یک قلم و خودکار بردارید و خودتون ضرب و جمع‌های بالا رو انجام بدید تا متوجه بشید (دقت کنید که اندازه تنسورهای ما به صورت \([batchSize * sequenceLength * dimension]\) است).

در نهایت نوبت به پیاده‌سازی tf.layers.dense میرسد. اگر بخواهیم قسمت tf.layers.dense هم جایگزین کنیم می‌تونیم بگیم:

مابقی قسمت‌ها هم دقیقا مثل قبل خواهد بود.

کد کامل این قسمت رو می‌تونید از اینجا مشاهده کنید.

منتشر شده در آموزشتنسورفلویادگیری عمیق

اولین باشید که نظر می دهید

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *