Cyclic-learning-rate and Stochastic Gradient Descent with Restarts

یافتن Learning Rate مناسب

مروری بر ایده learning rate decay

جلسه قبل راجع به راهکار learning rate decay صحبت کردیم.این راهکار بدین صورت بود که ابتدا با lr بزرگ شروع می کردیم و ذره ذره lr را کم میکردیم.چرا؟!چون در ابتدای آموزش شبکه که هنوز وزنهای شبکه خوب مقداردهی نشده اند و از global minimum  دور هستیم میتوانیم گامهای بلندی برداریم که این گامهای بلند هم در تسریع اموزش کمک میکند  و به اصطلاح زودتر همگرا میشویم وهم از گیر افتادن در local minimum ها جلوگیری میکنند.

اما رفته رفته با به روز شدن وزنها ماهم به global minimum نزدیکتر میشویم و بهتر است گامهایمان را محتاطانه تر یعنی کوتاهتر برداریم زیرا گامی بزرگ مارا از نقطه بهینه پرت خواهد کرد.

حال برای کم کردن lr بعضی از افراد به صورت exponential رفتار میکنند.مثلا هربار بعد از چند epoch آن را بر 10 تقسیم میکنند و بعضی دیگر به صورت خطی رفتار کرده و میگویند مثلا بعد از تعدادی epoch بر 2 تقسیم شود.

ایده cyclical learning rate

دراین ایده ابتدا از lr کوچکی شروع کرده و سپس در هر step(mini batch) آن را 2 برابر کرده تا به نقطه ای میرسیم که با اینکه کمترین loss  را دارد اما lr آن بزرگ بوده و مارا از global minimum  دور ساخته است.پس آن نقطه را نباید انتخاب کنیم وlr  موردنظر را ازیک پله عقبتر ینی از آن قسمتهایی که نزدیک به این نقطه (که دارای loss کمینه است) انتخاب میکنیم.

نقطه نشان داده شده در تصویر همان نقطه ی با loss کمینه و lr بزرگ است که نباید انتخاب کنیم.

پس با توجه به حرفهای گفته شده در نمودار بالا باید lr را در محدوده ی 0.001 و 0.01 انتخاب کنیم.

ایده stochastic gradient descent with restarts

ممکن است ما مینیمم های زیادی داشته باشیم که تقریبا به یک اندازه برای ما تابع loss  را تعیین کنند ولی برای ما به اندازه ی یکسان اهمیت ندارند.یک سری robust تر هستند و سری دیگر اینطور نیستند.(ناپایدارند)

در این راهکار ابتدا یک lr  نسبتا زیادی را انتخاب میکنیم .سپس آن را به صورت کسینوسی کم میکنیم.به این صورت که اول یک مقداری lr اولیه میماند سپس کم میشود و باز مدتی با lr کاهش یافته باقی میماند سپس دوباره مثلا یک ایپاک میزند و lr را زیاد میکند.(به صورت کلی به صورت کسینوسی مدام lr را زیاد و کم میکنیم)

علت اینکار چیست؟!

فرض کنید ما به نقطه ای رسیدیم که loss آن کمینه است اما ما میخواهیم با انتخاب یک lr به جایی از این مساله برویم که هم loss کمینه بوده و هم آن نقطه نسبت به تغییرات جزیی robust تر(پایدارتر) باشد.پس با زیاد کردن lr  این احتمال به وجود می آید که از این قله ها بپریم به مکان های flat تر برویم.

چرا مکان های flat تر بهتر هستند؟

فرض کنید دیتای train و test ما از منابع متفاوتی باشند.در این صورت در قسمت هایی که تابع ما flat تر میباشد تغییرات جزیی تر بوده و احتمال یکی بودن loss داده های train  و  test با وجود تفاوتشان،بیشتر میباشد.پس با تغییرات جزیی loss  ما در زمان test  خراب نمیشود.

توضیح شکل:فرض کنید منحنی آبی توزیع دیتای آموزش و منحنی سبز توزیع دیتای تست است.اگر نقطه ی قرمز رنگ را انتخاب کرده باشیم در دیتای تست loss بسیار متفاوتی خواهیم داشت اما همانطور ک میبینید در ناحیه flat تغییرات loss بین توزیع آموزش و تست بسیار جزیی است.

اما ممکن است با این ایده lr را زودتر از موعد عوض کنیم یعنی درست وقتی نزدیک به جای خوبی هستیم دوباره lr زیادی بزنیم.پس برای رفع این مشکل ایده ی دیگری مطرح شد.در این ایده میگوییم ابتدا درطی ایپاک اول lr سریع از زیاد(حداکثر) به کم(حداقل) برسد سپس در طی دو ایپاک بعدی lr از زیاد به کم برسد سپس در طی 4 ایپاک الی آخر و میشود از یک جا به بعد فقط lr کم را داشت و دیگر زیاد نکرد.(اوایل تلاش میکنیم و در نهایت با lr کم جایی که پیدا کردیم بمانیم.)

 

یک ایده ی دیگر این است که هرموقع به پایین کسینوس رسیدیم وزنهای مدل را ذخیره کنیم و بین آنها ensemble بزنیم.یعنی عکس را به شبکه های حاصل از هرکدام از این وزنهای ذخیره شده بدهیم و میانگین گیری روی آن انجام دهیم.

اما به دلیل overhead زیاد این ایده را جرمی هاوارد(نویسنده کتابخانه fastAI) پیاده سازی نکرده است.

در نقاط 1 و  2 و 3 شبکه های حاصل از وزنهای ذخیره شده را داریم.

ایده  transfer learning

هنگامی که مساله ما خیلی دورتر از مساله imagenet است به طوری که ویژگی های لایه ی اول نیز؛که ویژگی های پایه و عمومی و ساده بودند، به درد ما نمیخورد باید چه کار کرد؟

میدانیم که به هرصورت بودن وزنهای train شده بهتر از نبودنشان است(خیلی خیلی بهتر از وزنهای رندوم است.)

اما برای حل این  مساله چه کار میکنیم؟!لایه های شبکه را به 3 قسمت low level(لایه های ابتدایی کانولوشنالی) و medium level (لایه های میانی کانولوشنالی)و high level(fully connected ها)تقسیم میکنیم.سپس به هرکدام از این قسمت ها یک lr مجزا تخصیص میدهیم.(differential learning rate)

لایه های ابتدایی:با lr مثلا 00001/0

لایه های میانی:با lr مثلا 01/0

لایه های نهایی:با lr مثلا 1/0 (بعد از train کردن لایه های fully connected که خراب نکند.)

که این ایده بیانگر این است که لایه های ابتدایی کمتر تغییر کنند(کمتر train  شوند) و لایه های میانی کمی بیشتر تغییر کنند و لایه های نهایی به طبع بیشتر از هردو تغییر کند.

دستکاری تصویر (2) – دوران، انتقال، مقیاس و درون‌یابی

Rotation (دوران)

همانطور که در درس هندسه تحلیلی خوانده ایم برای چرخاندن نقطه ای به اندازه ی α خلاف جهت عقربه ساعت باید مختصات آن نقطه را در ماتریس چرخش ضرب کنیم.که نقطه دوران یافته به صورت زیر میباشد.

تابعی که در opencv برای این کار گمارده شده است به صورت زیر است:

 

که آرگومان اول و دوم به ترتیب x و y مرکز دوران را میگیرند و آرگومان بعدی زاویه چرخش و آرگومان اخر آپشن اضافه ای در اختیار ما قرار داده است و عددی برای بزرگنمایی یا کوچک نمایی تصویر میگیرد و بدون درگیر کردن ما با ماتریس چرخش و سینوس و کسینوس این تبدیل را به خوبی انجام میدهد.=)

خروجی این تابع؛ ماتریس چرخش یا دوران می باشد.که میتوان با تابع پرینت آن را نمایش داد.

اما برای بدست آوردن تصویر دوران یافته کافیست از تابع زیر استفاده کرده و این ماتریس چرخش را به آن بدهیم و در خروجی این تابع عکس دوران یافته را بگیریم.

 

2D Translation (انتقال دوبعدی)

برای انتقال یک نقطه از مکانی به مکان دیگر از ماتریس زیر استفاده میکنیم:

که در اینجا:

x´ = x + Tx = 1 * x + 0 * y + Tx

y´ = y + Ty =  0 * x + 1 * y + Ty

و به این صورت میتوانیم تصویر را جابجا کنیم.

به صورت خلاصه تر این ماتریس به این صورت بیان میشود:

1 ها مقدار مقیاس گذاری را مشخص میکنند یعنی 1 خانه ی (0 و 0) نشان دهنده scale در راستای ایکس و 1 خانه ی (1 و 1) نشان دهنده scale  در راستای ایگرگ میباشد.که چون در اینجا مقادیر 1 هستند یعنی اندازه ی تصویر بدون تغییر میماند  و بزرگ نمایی یا کوچک نمایی انجام نمیشود.

تابعی که برای این کار در opencv در نظر گرفته اند بدین صورت است:

 

آرگومان اول تصویر میباشد و آرگومان دوم ماتریس انتقال و آرگومان اخر سایز عکس خروجی است.خروجی این تابع عکس انتقال یافته می باشد.

و خود ماتریس انتقال را میتوانیم بدین صورت بسازیم:

Np.float32([[1 , 0 , tx] , [0 , 1 , ty]])

Scalling (مقیاس گذاری)

ما اگر بخواهیم مثلا یک ماتریس 2 در 2 را به ماتریس 6 در 6 تبدیل کنیم یک سری نقاط(پیکسل) را نداریم.

با این 4 روش میخواهیم این نقاط را بسازیم.

  1.  Nearest Neighbor (نزدیکترین همسایه)
  2.  Bilinear Interpolation (درج وابسته به ۴ پیکسل ۴ طرف)
  3. Bicubic interpolation  (درج وابسته به ۴ پیکسل ۴ طرف به علاوه ۴ پیکسل اریب)
  4. استفاده از ماتریس تغییر شکل (با استفاده از تابع warpaffine با ارقام دلخواه و ترتیب گفته شده در ماتریس scalling، که در ابتدا آمده است)

این مقیاس گذاری دو حالت دارد:

  • بزرگ نمایی
  • کوچک نمایی

روش نزدیکترین همسایه(ساده ترین روش):

در این روش مثلا فلان پیکسل را نسبت می بندیم که آن روی یک پیکسل متناظرش در ماتریس جدید می افتد پس همان مقدار پیکسل را به آن میدهیم.سپس پیکسل دیگری را بررسی میکنیم که مثلا هیچ پیکسلی را متناظرش در ماتریس جدید نمی یابیم و در آن پیکسل مقدار نزدیکترین پیکسل را قرار می دهیم.

روش درج وابسته به۴ پیکسل ۴ طرف:نسبت به روش قبلی دقیقتر است.همانطور که مشخص است مقادیر 4 پیکسل اطراف را میانگین گرفته و پیکسل های خالی را پر میکنیم.

روش درج وابسته به ۴ پیکسل ۴ طرف به علاوه ۴ پیکسل اریب: همانطور که مشخص است مقادیر 16 پیکسل اطراف را میانگین گرفته و پیکسل های خالی را پر میکنیم. این روش از همه دقیقتر و کندتر است(به دلیل محاسبات بیشتر).

حال میخواهیم تابعی در opencvکه این کار را انجام میدهد بیان کنیم:

 

آرگومان اول تصویر مورد نظر است.آرگومان بعدی سایز عکس خروجی ست و دو آرگومان بعدی مقدار یا درصد مقیاس گذاری را مشخص میکند.(مثلا اگر عدد ۲ را به هردو آنها پاس دهیم یعنی میخواهیم تصویر در راستای طول و عرض ۲ برابر شود) و آرگومان اخر روش مقیاس گذاری(که در بالا توضیح دادیم) را تعیین میکند.خروجی این تابع نیز تصویر scale شده میباشد.

توجه:اگر هم اندازه تصویر خروجی را پاس دهیم هم مقادیر مقیاس گذاری؛ تابع اندازه را بر عدد یا درصد مقیاس ترجیح داده و آن را عملی میکند.

در آخر مقایسه انواع  روش های scaleرا باهم میبینیم: