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

انتقال سبک (Style transfer) با ونگوگ

در قسمت‌های قبلی با مفاهیم مربوط به انتقال یادگیری آشنا شدیم و دیدیم که چگونه از اطلاعاتی که در یک حوزه وجود دارد، میتوان در حوزه‌های دیگر استفاده کرد. یکی از کارهای مرتبط با انتقال یادگیری که در سال‌های اخیر توجه زیادی رو به خودش جلب کرده انتقال سبک یا Style Transfer است. انتقال سبک یعنی سبک (style) یک عکس رو نسبت به یک عکس دیگه بدست بیاریم. مثلا سه تا عکس زیر رو در نظر بگیرید:

در عکس بالا، سبک (style) عکس گربه رو عوض کردیم و بجای اون سبک نقاشی ونگوگ (vangug) رو قرار دادیم و نتیجه نهایی عکس سمت راست شد که الگوهای نقاشی ونگوگ در آن مشخص است.

این ایده در سال ۲۰۱۵ و در کنفرانس CVPR ارائه شد:

این مقاله مساله رو اینجوری مطرح کرده که در ابتدای کار ما یک عکس content (گربه)،‌ یک عکس style (ونگوگ) و یک عکس نویز (x) داریم. (در مقاله عکس نویز دار براساس عکس content ساخته شده است و شبیه زیر است که اندکی نسبت به عکس اصلی نویز به آن اضافه شده است):

و در نهایت قرار است این عکس نویز رو به عکسی تبدیل کنیم که به نسبت متناسبی عکس‌های content و style در آن دخیل هستند:

 

در این مقاله،‌ برای به دست آوردن نتیجه نهایی هیچ شبکه‌ای آموزش داده نشده است و از تکنیک انتقال یادگیری استفاده شده است. در واقع برای اینکه عکس نهایی رو به دست بیاریم از مدل‌های پیش‌اموزش داده شده قبلی مثل VGG، AlexNet و… کمک گرفته شده است.

ایده کلی مقاله به این صورت است که دو تابع خطا مختلف به نام‌های content loss و style loss تعریف شده است (به صورت زیر):

  • content loss: میزان شباهت عکس نویزی به عکس content (گربه) را اندازه میگیرد.
  • style loss: میزان شباهت عکس نویزی به عکس style (ونگوگ) را اندازه میگیرد.

و قرار است این دو تابع خطا به صورت وزن‌دار (مقادیر alpha , betha در فرمول پایین) با یکدیگر ترکیب میشوند تا به نسبت متوازنی از این دوتابع خطا عکس نهایی را برای ما ایجاد کنند:

خب بیایید یکم بیشتر در دو تابع بالا دقیق شویم.

content loss:

همانطور که فرمول بالا ملاحظه میکنید content loss، تابع معروف Mean Square Error است. متغیرهای F و P که با اندیس L (به معنای لایه) مشخص شده‌اند، خروجی‌های شبکه‌ی از پیش آموزش داده شده ما به ازای عکس content (گربه) و نویز (x) هستند (که اصلاحا به آن feature map میگویند). مثلا اگر شبکه از پیش آموزش داده شده VGG19 را داشته باشیم:

مقادیر F و P میتواند خروجی هرکدام از لایه‌های conv1_1، conv1_2، conv2_1, conv2_2 و … باشد. (F خروجی گربه و P خروجی عکس نویز). این تابع خطا (content loss) مشخص میکند که عکس نویزی ما در لایه‌های مختلف تا حد امکان شبیه عکس اصلی (گربه) باشد. از طرفی ما نمیخواهیم عکس نهایی که میخواهیم تولید کنیم دقیقا شبیه عکس content باشد. بخاطر همین تنها خروجی conv4_2 رو به عنوان معیار مقایسه انتخاب میکنیم. ما این امکان رو داریم که هرکدام از خروجی‌های شبکه بالا (conv1_1 , conv2_2, …) رو انتخاب کنیم ولی معمولا از لایه‌های انتهایی انتخاب میکنیم که ویژگی‌های بهتری و مشخص‌تری هستند.

اگر بخواهیم این تابع رو در تنسورفلو پیاده کنیم باید ابتدا مقادیر P و F را بدست بیاریم.

این مقادیر با عبور عکس ورودی (گربه) و عکس نویزی به لایه‌های شبکه VGG بدست میاد یعنی:

در تیکه کد بالا، ابتدا مقدار نویز (gen_image_content) یا همان F که در واقع خروجی عکس نویز از لایه conv4_2 است بدست میاوریم. و بعد مقدار عکس اصلی content_img_content یا همان P  رو بدست میاریم و به تابع content_loss میفرستیم تا میزان خطا را برایمان محاسبه کند.

style loss:

تابع خطای سبک (style loss) اندکی متفاوت تعریف شده است. این تابع از ضرب دو متغییر تشکیل شده است.

متغییر E به صورت زیر تعریف می‌شود:

خب همانطور که ملاحظه میکنید متغیر E هم از تابع خطای Mean Square Error استفاده کرده است. در اینجا متغیرهای G و A ماتریس‌های گرم (Gram matrix) هستند. ماتریس Gram به صورت زیر محاسبه میشود:

خود بردار در ترانهادش ضرب میشه. که اگر بخواهیم در تنسورفلو آن را پیاده کنیم اینجوری میشه:

مقادیر G و A در فرمول E در واقع ماتریس Gram برای feature mapهای عکس نویزی (x) و عکس Style در لایه L هستند. که برای نرمال‌سازی در یک ضریبی ضرب شدند 1/4MN. پس برای محاسبه E در لایه L خواهیم داشت:

خب حالا یکبار دیگه به فرمول style loss نگاه کنید یک w هم داریم. در اینجا میخواهیم تعیین کنیم که به کدام feature mapها اهمیت بیشتری بدهیم. هرچه feature mapها (خروجی لایه‌های کانولوشن) جلوتر برن اهمیت بیشتری دارند و وزن بیشتری میگیرند. مثلا فرض کنید ما لایه‌های زیر رو انتخاب کردیم تا عکس نویزی و عکس style رو از آنها عبور بدیم و feature vectorهامون رو بسازیم:

['conv1_1', 'conv2_1', 'conv3_1', 'conv4_1', 'conv5_1']

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

[0.5, 1.0, 1.5, 3.0, 4.0]

خب عکس‌های ورودی رو با توجه مقدار خطای کلی اینقدر تغییر میدهیم،‌ تا عکس نهایی به نسبت مناسبی از پیکسل‌های هردو عکس را داشته باشد. مثلا در iteration ۰ عکس ما به صورت زیر است:

در iteration ۵۰۰:

و در نهایت به این شکل خواهد شد:

 

پیاده‌سازی بالا در روی تنسورفلو و colab وجود داره و میتونید خودتون اون رو امتحان کنید. کافی است دوتا عکس ورودی (style و content) رو بهش بدید و نتیجه رو ملاحظه کنید. همچین میتونید با پارامترها بازی کنید تا عکس نهایی متفاوتی بدست بیارید.

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

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

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

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