در قسمت قبلی آموزش تنسورفلو دیدیم که چطوری آن را نصب کنیم، چطوری گرادیان یک تابع خطا را حساب کنیم و در نهایت یک مدل رگرسیون خطی (linear regression) را باهم پیادهسازی کردیم.
حالا بیایید ببینیم چطوری میتونیم یک دستهبندیکننده متن (sentence classifier) با استفاده از شبکههای MLP و CNN و LSTM ایجاد کرد.
قبل از ادامه مطلب رو بخونید، بهتر است موارد زیر رو بلد (آشنا) باشید:
- مفاهیم مقدماتی تنسورفلو (قسمت قبلی آموزش)
- شیگرایی کد زدن در پایتون (تعریف کلاس، متد، وراثت (inheritance)، سازنده (constructor)، استفاده از super() و call())
- آشنایی با شبکههای چندلایه با پرسپترون (MLP)، کانولوشن (CNN) و شبکههای بازگشتی (RNN)
۱) پس انتشار یا Back-propagation
یک مسالهای که از آموزش قبلی جا موند و حرفی هم راجع بهش نزدیم پسانتشار یا همون backpropagation است. یعنی اشاراتی شد ولی نه اونقدرا! در صورتی که شما هر آموزش یادگیری عمیق رو نگاه کنید راجع به این موضوع مفصل بحث شده است. پس بیایید به صورت خیلی سطحی به این مساله بپردازیم.
پسانتشار که مخفف backward propagation of error است. در واقع میخواد میزان خطایی که ما در خروجی حدس زدیم را محاسبه کنه و به لایههای مدل ما منتقل کنه. یعنی چی؟ همانطور که دیدیم ما پارامترهامون رو (w و b) رو با صفر مقداردهی میکردیم ولی در نهایت مقداری مثلا ۰.۱ برای b و ۰.۹ برای w بدست آمد. سوالی که پیش میاد این است که چطوری مقادیر w و b تغییر کردند؟ جواب: با استفاده از optimizer و backpropagation.
وظیفه backpropagation محاسبه گرادیان loss function شما نسبت به پارامترهای مدل (w,b در مثال بالا) است و وظیفه optimizer به روزرسانی کردن آنها. یعنی این دوتا مکمل همدیگر هستند. backpropagation گرادیان را محاسبه میکنه و optimizer مقدار پارامترها را تغییر میدهد.
پس انتشار (backpropagation) یک روش کارآمد برای محاسبه گرادیان در گراف محاسباتی است. در واقع پس انتشار چیزی نیست جز یک پیادهسازی مشتق زنجیرهای که تو دبیرستان خوندیم (علت محوبیبت پس انتشار این است که بر اساس اندازه گراف محاسباتی پیچیدگی آن به صورت خطی رشد میکند در حالی که روشهای دیگر اغلب با اندازه گراف به صورت نمایی رشد میکنند).
اگر یکبار دیگه برگردید و به مدل Numpy که در آموزش قبلی پیادهسازی کردیم نگاه کنید میبینید که ما هم دقیقا دو مفهوم بالا رو پیاده سازی کردیم ولی به صورت دستی!
در کد بالا ما گرادیان رو به دست آوردیم (backpropagation پیادهسازی کردیم!). یا عبارت دیگر، گرادیان loss function رو نسبت به پارامترهای a و b نوشتیم. علاوه بر این، optimizer نوشتیم! یعنی برای بهروزرسانی متغیرهای مدلمون آمدیم یک مقدار اندکی (نرخ یادگیری-1e-3) نسبت به مقدار قبلی آن تغییرش دادیم:
فک کنم الان خودتون حدس میزنید که در تنسورفلو بابت این موضوع داره چه اتفاقی میافته؟ با استفاده از GradientTape ابتدا میاییم گرادیان تابع خطا رو نسبت به متغیرهای مدل محاسبه میکنیم. و سپس با استفاده از بهینهکننده و تابع apply_gradients آنها به روزرسانی میکنیم. حالا سوالی که احتمالا از خودتون باید بپرسید این است که داخل GradientTape داره چه اتفاقی میافته؟ چطوری گرادیان رو حساب میکنه؟ به قسمت قبلی و گراف محاسباتی نگاه کنید!
نکته: گاهی اوقات ما نمیاییم جداگانه یکبار گرادیان بگیریم و یکبار پارامترها رو به روزرسانی کنیم بلکه یهو مینویسم:
که خودش ابتدا گرادیان رو نسبت به پارامترهای مدل حساب میکنه و سپس بهینهکننده آنها را به روزرسانی میکند.
امیدوارم که قسمتهای قبلی رو خوب گفته باشم. حالا نوبت به پیادهسازی مدلهای شبکه عصبی از جمله شبکههای پیشرو، شبکههای کانولوشن و شبکههای بازگشتی میرسد. در قسمت بعدی به پیادهسازی این مدلها روی دادگانی که خودم ساختم! میپردازیم.
۲) مدل (model) و لایه (layer)
در پست چگونه در تنسورفلو شیگرا کد بزنیم یک روش برای پیادهسازی مدلها گفته شد. مطالب این قسمت در راستای همون مطالب است و ترکیبش با اون قسمت لذت حداکثری رو فراهم خواهد کرد!
یکی از چیزهایی که در شیگرایی اغلب بهش توجه میشه کلاسبندی و تعریف درست متدهاست. اگر کد ماشینلرنینگی رو به شما بدن که به صورت Model(x)= y_pred باشه قاعدتا فهمیدنش برای شما خیلی راحت خواهد بود. مدل شما ورودیهای لازم رو بگیره و پیشبینی رو برای شما انجام بده. تنسورفلو (با API کراس) این قابلیت رو از دو طریق برای شما فراهم میکنه: ۱) مدل ۲) لایهها.
برای درک بهتر بیایید همون مدل رگرسیون خطی که در قسمت قبلی پیادهسازی کردیم رو با استفاده از Model پیادهسازی کنیم.
قسمت اولی که در مدل رگرسیون خطیمون داشتیم نرمالسازی دادهها بود که در اینجا هم دقیقا مشابه قبل خواهد بود با یک تفاوت کوچک:
تنها فرق کد بالا با قسمت قبل expand_dims هست. API که در مرحله بعدی از آن استفاده میکنیم در ورودی خود یک ماتریس (حداقل) دو بعدی دریافت کند. یعنی چی؟ دادههایی که ما تعریف کردیم اگر shape اشون رو چاپ کنید به صورت (5,) هستند در صورتی که tf.layers.Dense یک ماتریس دو بعدی میخواد پس shape اون رو به (5,1) تبدیل کردیم (بُعدش رو افزایش دادیم تا فرمتاش مناسب کارمون بشه). هیچ تغییری در خود دادهها انجام نمیشه فقط یک بُعد به آن اضافه میشه.
مرحله بعدی نوبت به تعریف مدلمون میرسه. مدلی که داشتیم یک خط بود که به صورت \(y=aX+b\) تعریف کردیم. بیایید این مدل رو با استفاده از ارثبری در تنسورفلو پیادهسازی کنیم. برای ارثبری میگیم که کلاس LinearRegression از کلاس tf.keras.Model ارث بری کند.
در نتیجه کد ما یه چیزی شبیه زیر خواهد شد:
هنگامی که به صورت بالا کد میزنیم (از کلاس tf.keras.Model ارثبری میکنیم) لازم است حداقل ۲ تا تابع رو تعریف کنیم (توابع اضافی دیگر هم میتونید تعریف کنید ولی این دو واجب هستند):
- تابع __init__: مقدار دهی اولیه را انجام میدهیم و در واقع ما مدلمون رو در اینجا میسازیم.
- تابع __call__: که به ازای ورودی (X) خروجی مدل را (y_pred) را به ما بازمیگرداند.
تنسورفلو \(aX+b\) یا (\(wX+b\)) را که قبلا ما به صورت y_pred = tf.matmul(X, a) + b تعریف کرده بودیم در قالب کلاسی به نام tf.layers.Dense پیادهسازی کرده است. یعنی دیگر لازم نیست a و b رو تعریف کنیم و آنها را در ورودی (X) ضرب و سپس با هم جمع کنیم همه این کارها را در کلاس Dense برای ما پیادهسازی کرده است. بیاید ببینیم این کلاس Dense چه پارامترهای به عنوان ورودی سازنده (constructor) میگیرد:
اولین پارامتر کلاس tf.layers.Dense یک عدد طبیعی (integer or long) است که تعداد خروجیهای شبکه را مشخص میکند. اگر خاطرتون باشه ما قرار بود قیمت خونه رو حدس بزنیم. مثلا قرار بود سال ۲۰۱۹ رو بدن و ما هم یک عدد در خروجی بدیم. تعداد خروجی ما یدونه است پس ما هم تعداد units رو یک در نظر میگیریم.
همچنین اگر یادتون باشه ما مقادیر a و b رو با صفر مقداردهی کردیم. که اصلاحا به a وزن (weight) گفته میشه و به b بایاس (bias) گفته میشود. ما این دو مقدار را با صفر مقداردهی کردیم. در تنسورفلو به وزنها اصطلاحا کرنل (kernel) گفته میشود (فرقی نمیکند شبکه ما چی باشه. در تمام مدلهای پیشرو، کانولوشن و بازگشتی به وزنها کرنل گفته میشود). پس در نتیجه ما هم با استفاده از tf.zero_intializer مقدار وزن و بایاس رو با صفر مقداردهی میکنیم.
فعلا ما با همین ورودیها نیاز داریم و مابقی رو ازش میگذریم.
همچنین اگر دقت کنید ما یک تابع دیگر تعریف کردهایم. در تابع __call__ یک ورودی X گرفتیم از لایه dense رد کردیم و خروجی آن را بازگرداندیم. کاری که قبلا هم انجامش میدادیم. ما \(aX+b\) رو حساب میکردیم نتیجه رو در یک متغیر دیگه به نام y_pred ذخیره میکردیم اینجا هم همون کارو کردیم و اسمشو output گذاشتیم.
خب مابقی چیزا میشه همون کدی که در آموزش قبلی برای تنسورفلو پیادهسازی کردیم. یعنی optimizer و GradientTape تعریف میکنیم و گرادیان خطا رو نسبت به پارامترهای مدل حساب میکنیم (قبلا پارامترمون a و b بود اینبار شد model.variables). اگر کلاس بالا رو جایگزین مطالب گذشته کنیم چیزی که خواهیم داشت شبیه زیر خواهد بود:
همانطور که میبینید در اینجا Model مون از مابقی کدمون جدا شد. این کار باعث میشه خیلی کدتون منظم و مرتب بشه. بخصوص وقتی که مدل شما پیچیده و از چندین لایه تشکیل میشه.
بیایید یک مثال پیچیدهتر رو باهم بررسی کنیم.
۳) شبکه چند لایه با پرسپترون (MLP) در تنسورفلو
شبکه چندلایه با پرسپترون که شبکه پیشرو (feedforward) هم گفته میشه یکی از راحتترین مدلهایی است که در تنسورفلو میتونیم پیادهسازی کنیم. ایده پشت شبکههای پیشرو ساده است و عملکرد نسبتا خوبی دارند. در ادامه یک شبکه چندلایه با پرسپترون برای دستهبندی کردن جملات (sentence classifier) پیادهسازی خواهیم کرد. در این مدل قرار است که جملات به شبکه پیشرو داده شوند و بعد از آن شبکه حدس بزند که بار معنایی جمله مثبت است یا منفی (به عبارت دیگر قرار است یک سیستم تحلیل متن یا سنتیمنت پیادهسازی کنیم).
۳-۱) پیشپردازش
قبل از اینکه به پیادهسازی شبکه چندلایه با پرسپترون بپردازیم لازم است که یک پیش پردازش روی دادههای ورودی انجام بدیم. پیشپردازش عبارت است از کارهایی که ما روی دادههای ورودیامون انجام میدهیم تا به فرمت دلخواه ما در بیان. همانطور که احتمالا میدونید در شبکههای عصبی معمولا با یه سری عدد سر و کار داریم. حالا فرق هم نمیکنه تسکی که انجام میدهیم مربوط به بینایی کامپیوتر (computer vision) یا پردازش زبان طبیعی (NLP) یا …. اول باید دادههای خودمون رو به فرمت مناسب برای خوراندن (feed) به شبکههای عصبی تبدیل کنیم.
مثلا تسک معروف تشخیص اعداد (digit recognition) رو در نظر بگیرید. چیزی که ما در اختیار داریم یک سری عکس 28*28 هستند که یک عدد روی آنها نوشته شده است. این عکسها در حالت عادی برای شبکه عصبی قابل مفهموم نیست. ولی وقتی میایم این عکس رو به مقدارهای عددی که معادل پیکسلهای عکس هستند تبدیل میکنیم، شبکه قادر خواهد بود که آن را بفهمد. (چون تصویر grayscale است، هر پیکسل معادل یک عدد بین ۰ الی ۲۵۵ خواهد بود).
این موضوع تقریبا در تمام کارهای دیگر شبکه عصبی صادق است. مثلا در بحث پردازش زبان طبیعی جملات به خودی خود نمیتوانند به شبکه فرستاده شوند بلکه ابتدا باید یک پیشپردازش روی آنها انجام شود و به یک سری عدد تبدیل شوند. در پردازش زبان طبیعی مثل بینایی کامپیوتر نیست که از پیکسلها کمک بگیریم و کلمات رو به عدد تبدیل کنیم. کاری که معمولا انجام میشود این است که هر کلمه منحصر به فرد در دیتاست به یک عدد منحصر به فرد تبدیل خواهد شد. مثلا جمله “عجب هوای خوبی عجب” به برداری مثل [1, 2, 3, 1] تبدیل خواهد شد و چون عجب دو بار تکرار شده است اول و انتهای این بردار ۲تا یک داریم.
دادگان (دیتاست) زیادی برای کارهای مختلف پردازش زبان وجود دارند (به خصوص برای انگلیسی). دادگان IMDB در تنسورفلو به صورت آماده وجود دارد و شما به راحتی میتونید در طی چند دقیقه اون رو دانلود کنید و ازش استفاده کنید (این دادگان حتی نیاز به پیش پردازش خاصی هم ندارد). ولی در دنیای واقعی دادههایی که ما داریم (به خصوص در فارسی) خیلی مرتب و تر و تمیز نیستند و لازم است که خودمون به فرمت دلخواه درشون بیاریم. چون دادگان فارسی تحلیل متن رایگان در فارسی نداریم (یا حداقل من ازش اطلاع ندارم) یک دادگان مصنوعی ساختم که فرمتش مشابه خیلی از دادگان خارجی است. این دیتاست یک فایل CSV است که از سه تا ستون شناسه (id)، برچسب(label) و جمله (sentence) تشکیل شده است. جمعا دارای ۱۶ جمله است که ستونها با کاما از یکدیگر جدا شدهاند.
اگر دادههای ورودی شما مشابه بالا باشد احتمالا باید چندتا باید روی آنها انجام بدید:
- ستون آیدی حذف شود.
- هر برچسب (positive و negative) به مقدار عددی ۰ و ۱ تبدیل شود.
- هر جلمه رو کلمه کلمه (توکنایز) و سپس هر کلمه را به یک عدد منحصر به فرد نگاشت شود (مانند مثال بالا: عجب هوای خوبی عجب).
روشهای زیادی برای این کار وجود دارد ولی من ترجیح میدهم که از API خود تنسورفلو استفاده کنم و تمام کارهامو با استفاده از اون انجام بدم. API که برای این کار وجود دارد tf.data هست. قبلا یک آموزش درباره tf.data و نحوه استفاده از آن نوشته بودم که شاید بد نباشه یک نگاهی بهش بندازید.
کدی که من برای انجام سه تا کار بالا زدم به صورت زیر است:
در ابتدا آدرس فایل ساختگی که اسمشو sentiment.csv گذاشتم میخونم. این کار با استفاده از API جدید tf.data.experimental.CsvDataset انجام دادم. این کلاس یک فایل CSV را برای شما میخونه و tf.data.Dataset تبدیل میکند. بیایید یک نگاهی به پارامترهای این کلاس بندازیم:
پارامتر اول file_name است که آدرس فایل ورودی شما است. پارامتر دوم record_defaults است که مقادیر پیشفرض دادههای شماست (دادههایی که miss شدن). این پارامتر برای مواقعی که برخی از سلولهای csv خالی هستند بسیار مفید است. پارامتر سوم header است که چون فایل CSV من ستون داشت (id, label, sentence) این فلگ رو True قرار دادم. از طرفی چون ستون id رو لازم ندارم، فقط ستونهای اول و دوم رو انتخاب کردم و select_cols رو مساوی [1,2] قرار دادم.
همانطور که در sentiment.csv میتونید ببینید، ستون دوم (label) هست و ستون سوم (sentence) برای اینکه بخوام با دادگانم راحتتر کار کنم جای این دوتارو باهم جابجا کردم تا به صورت x و y باشه (قبلش به صورت y و x بود).
در مرحله بعدی برچسبهای positive و negative رو به مقدار عددی (۰ و ۱) تبدیل کردم. این کاری که انجام دادم تقریبا معادل LabelEncoder توی پکیج scikit هست. تقریبا هر تبدیل که بخواهیم انجام بدیم میتونیم از map استفاده کنیم!
همچنین چون برچسبهای من اسپارس بودن اونها به تک روشن (one hot) تبدیل کردم که کار باهاشون راحتتر باشه. البته میتونستم از تابع خطای sparse هم استفاده کنم ولی اینجا ترجیح دادم تک روشن باشه. ۲ تعداد درایههای تنسور رو نشون میده. چون باینری هست پس ۲ درایه لازم داریم.
مرحله بعدی، تبدیل جملات به یک توالی از Id های منحصر به فرد است. لازم است که هر کلمه به یک Id نگاشت شود. من این کار رو با استفاده از index_table_from_file انجام دادم. این تابع یک فایل vocab.txt به عنوان ورودی لازم دارد. فایل vocab باید شامل تمام کلمات منحصر به فرد دیتاست ما باشد.
در خط بعدی ابتدا کلمات را توکنایز میکنیم (توکنایز بر اساس اسپیس (space) بین کلمات انجام شده است) و در نهایت با استفاده از lookup table هر کلمه رو به آیدی منحصر به فردش نگاشت میکنیم.
تنسورفلو و تقریبا تمام فریمورکهای یادگیری عمیق، برای پردازش سریع لازم دارند که جملاتی که در یک دسته قرار دارند یکسان باشد (چون طول جملات ما متفاوت است). به همین خاطر ابتدا گفتم دستههای که به عنوان ورودی شبکه میخوایی بدی shuffle کن و در ادامه دستههای به اندازه ۳ درست کن که اندازه هر دسته یکسان باشد (به عبات دیگر تنسورفلو بر اساس بیشترین طول در هر دسته padding اضافه میکند).
۳-۲) تعریف و آموزش مدل
مرحله بعدی تعریف مدل و لایهها است. همانطور که در قسمت ۲ مشاهده کردیم تقریبا هر مدلی که بخواهیم رو میتونیم با ارثبری از کلاس tf.keras.Models پیادهسازی کنیم. کدی که من برای این قسمت زدم به صورت زیر است:
همانطور که گفتم دو تابع اصلی رو باید پیاده کنیم. ۱) تابع __init__ و ۲) تابع __call__.
مدلی که پیاده شده است خیلی ساده است و خودتون میتونید بهترش کنید. در اینجا ابتدا کلماتمون رو به بردار نهفته (embedding) تبدیل میکنیم تا هر کلمه در جمله به یک بردار 128 بعدی تبدیل شود. در نتیجه اگر طول جمله اول ۸ و طول دسته (batch) امون ۳ باشه خروجی این مرحله یک تنسور 3*8*128 خواهد بود.
سپس این تنسور رو از شبکه چندلایه با پرسپترون عبور میدهیم تا ویژگیهای بهتری استخراج کنیم. من اندازه تعداد لایههای مخفیامو 100 در نظر گرفتم. در نتیجه خروجی شبکه MLPام یک تنسور 3*8*100 خواهد شد.
تا به اینجا من یک تنسور 3*8*100 دارم که هر کدام از کلمات با یک بردار ۱۰۰ تایی بازنمایی شدهاند. از اونجایی که برای آموزش شبکه میخوام یک بردار ویژگی واحد در سطح جمله داشته باشم. لازم است که یک بازنمایی مناسب برای جملاتم بدست بیارم پس در نتیجه از کلاس GlobalAveragePooling1D استفاده میکنم و یک تنسور 3*100 به دست میارم. به طور دقیقتر میام بردارهای که از خروجی MLP گرفته بودم را میانگینگیری میکنم.
در نهایت این تنسور رو به یک لایه dense میفرستم تا برچسب موردنظر را برای من حدس بزنه.
قسمت __init__ برای ساخت مدل است و هیچ داخل اتفاقی نمیافتند (ما اینجا فقط مدلامون رو میسازیم). در قسمت __call__ دقیقا روالی که __init__ ساختیم را باید یکی یکی صدا میزنم تا شبکه پیشبینی خودشو انجام دهد.
مرحله آخر نوبت به آموزش مدل میرسد. مشابه چیزی که برای رگرسیون خطی انجام دادیم گرادیان تابع خطا را نسبت به پارامترهای مدل به دست میاریم و بعد توسط بهینهکننده آنها را بهروزرسانی میکنیم:
همانطور که در بالا مشاهده میشود در بالا از بهینهکننده Adam استفاده کردم که نسبت به گرادیان نزولی (GradientDescent) سریعتر همگرا میشود. همچنین بر خلاف رگرسیون خطی که تابع خطا رو خودمون نوشتیم در اینجا از تابع sigmoid_cross_entropy که از قبل در تنسورفلو پیاده سازی شده است. شاید بپرسید چرا از sigmoid استفاده کردم و از softmax_cross_entropy استفاده نکردم که جواب این سوال رو میزارم به عهده خودتون 🙂
اگر کد بالا رو اجرا کنید ملاحظه خواهید که خطا به صفر خواهد رسید که معناش این است که مدل رو درست آموزش دادیم.
کد کامل رو میتونید از اینجا دانلود کنید و روی سیستم خودتون تست کنید.
۴) شبکه عصبی کانولوشن یا پیچشی
اگر بخواهیم مدل تحلیل احساسات متنامون رو با استفاده از شبکه کانولوشن پیادهسازی کنیم به راحتی آب خوردن است. کافی است که کلاس MLP امون رو تغییر بدیم و به جای لایه چندلایه با پرسپترون از کانولوشن ۱بعدی استفاده کنیم یعنی:
همانطور که در بالا مشاهده میکنید تمام قسمتها تقریبا یکسان است و فقط لایه Dense با یک Convolution1D جایگزین شده است. در کارهای متنی ما اغلب از کانولوشن ۱بعدی استفاده میکنیم. کانولوشن ۱بعدی دقیقا شبیه کانولوشن ۲ بعدی است که در بینایی کامپیوتر وجود دارد با این تفاوت که در کارهای بینایی کامپیوتر تصاویر معمولا طول و ارتفاق دارند ولی در کارهای متنی ما فقط طول داریم!
اگر پارامترهای کانولوشن رو ببینیم به صورت زیر خواهد بود:
فیلتر (filters) یا گاهی اوقات دریچه اندازه قدمهای ما را مشخص میکند. یعنی چه؟ عکس زیر رو ملاحظه کنید:
در اینجا اندازه فیلترهای ما ۲ است و ما دوتا دوتا کلمات جمله را پیمایش میکنیم. ما میتونیم از چندین فیلتر استفاده کنیم و ارایهای از فیلترها داشته باشیم که هرکدام خواص مختلفی از جمله را استخراج کنند. این فیلترها یه جورایی مثل n-تاییها در مدلهای زبانی هستند (ngram language model). میتونید با استخراج 2تاییها، 3تاییها و … و چسباندن آنها به یکدیگر ویژگیهای بهتری استخراج کنید که در اینجا از توضیح آنها میگذریم.
پارامتر بعدی اندازه کرنل (kernel size) است. همانطور که در قسمت قبلی هم گفتم کرنل معادل وزنهای (weights) شبکه است.
استراید (stride) میزان جابجایی فیلترهای مارو مشخص میکند. در شکل بالا همانطور که میبینید ما یکی یکی داریم میریم جلو (ابتدا 0-3 بعد 3-4 بعد 4-5) میزان جابجایی ما ۱ است.
پارامتر بعدی padding است که ما ۳ تا حالت casual , valid, same براش داریم (به حروف کوچک و بزرگ حساس هستند). valid به این معنی است که padding اضافه نکنیم. same به این معناست که همون اندازه که تنسورمون بود همون اندازه حفظ بشه. مثلا شکل زیر رو در نظر بگیرید (شکل زیر برای تصویر است ولی برای فهمیدن مفهوم فرقی نمیکند):
اون خونههای بدون رنگ که اطراف شبکه آبی وجود دارد padding نام دارند. برای اینکه بتونیم شبکه رو پیمایش کنیم و خاصیت لبهها رو داشته باشیم padding به اندازه ۱ اضافه شده است. شبکه سبز رنگ فیلترهای ماست که روی تمام متنامون پیمایش میکنیم. شکل زیر هم گفتههای بالا رو به شکل دیگری نشان میدهد:
در اینجا هر کلمه به بردارهای نهفته (۲ دایره) تبدیل میشود به یکدیگر چسبانده میشوند و سپس فیلتر W روی آنها اعمال میشود و از یک تابع فعالیت tanh عبور داده میشوند. در نهایت هم max-pooling انجام شده است و مهمترین خواص جمله استخراج شده است.
پیادهسازی شبکه عصبی کانولوشن برای تحلیل متن رو میتونید از اینجا مشاهده کنید.
۵) شبکه عصبی بازگشتی
راجع به شبکههای عصبی بازگشتی در پست شبکههای عصبی بازگشتیبه طور کامل بحث کردیم و با پیادهسازی آن آشنا شدیم. بخاطر همین در اینجا ازش میگذریم و فقط مدل پیادهسازی شده رو قرار میدهیم:
پیادهسازی شبکه عصبی بازگشتی برای تحلیل متن رو میتونید از اینجا مشاهده کنید.
خب فکر کنم تا همینجا کافی باشه و امیدوارم تونسته باشم مطالب رو به درستی بیان کرده باشم. این پست خیلی ناقصه و مطالب بسیاری از قلم افتاده است. ارزیابی مدل، ذخیره و بازیابی (save and restore) و تنسوربورد هیچ صحبتی نشد که آنها هم در تنسورفلو و پیادهسازی مدلها بسیار کمک میکنند. در قسمتهای بعدی سعی میکنم به توضیح این قسمتها بپردازم.
استاد عزیز
بسیار لذت بخش درس دادید.
ممنون
ممنون دوست عزیز 🙂