Neural Style Transfer

 Style Transferیکی از کاربردهای Deep Learning در هنر است که مشابه Deep Dream می‌باشد.

در این کاربرد یک عکس content و یک عکس style داریم. هدف این شبکه ساختن عکسی است که image آن از عکس content و style آن از عکس style گرفته شده باشد. یعنی content را حفظ کرده و آنرا به سبک style ببرد.

به طور کلی به object ها content و به  .pattern,texture,color,. ها style گفته می‌شود.

نمونه ای از خروجی‌های این شبکه :

آن‌چه ConvNet ها یاد می‌گیرند

همان‌طور که میدانید لایه‌های اول به featureهای generalتر مانند خط و لبه و لایه‌های آخر به feature‌های abstractتر حساس‌اند.

از visualization برای درک اینکه هر لایه چه تفسیری از image دارد، استفاده می‌شود. و این به عنوان hyper parameter برای style trafnsfer استفاده می‌شود. (برای مثال: استفاده از کدام لایه style را بهتر حفظ می‌کند.)

در مقاله Zeiler & Fergus سه متد درباره visualization معرفی شده است. که در اینجا به یکی از آن‌ها می‌پردازیم.

متد اول:

یک  unit در لایه اول برداشته، تمام عکس‌های training set را به شبکه می‌دهیم، و 9 تا از عکس‌هایی که unit’s activation را ماکزیمم می‌کنند، پیدا می‌کنیم.

این کار را 9بار برای 9 تا unit  مختلف انجام می‌دهیم. و این کار را برای لایه‌های مختلف تکرار می‌کنیم.

Cost Function

از یک شبکه pre-trained استفاده می‌کنیم، برای مثال یک AlexNet یا Vgg را برداشته و آن را freeze می‌کنیم. و مسئله را optimization عکس ورودی فرض می‌کنیم. یک عکس رندوم به شبکه داده، و cost آن را طوری تعریف میکنیم که content آن شبیه عکس content و style آن مانند عکس style باشد.

در هر iteration عکس رندوم تغییر می‌کند. اما در این training وزن‌های شبکه train نمی‌شوند، بلکه هدف بهینه کردن pixelهای عکس ورودی است.

پس تابع cost function به این صورت تعریف می‌شود:

طبق فرمول،  این تابع به عنوان ورودی content و generated image (که در ابتدا همان عکس رندوم است) را گرفته، و فاصله‌ی content آن‌ها را حساب می‌کند. همچنین فاصله‌ی style عکس style و generated image را محاسبه می‌کند و این دو مقدار را با یک ضریب باهم جمع می‌کند.

یک مثال:

در مرحله اول عکس رندوم به صورت نویز است :

که می‌خواهیم content آن شبیه عکس سمت چپ و style آن شبیه عکس سمت راست شود.

طی چند generated image،epoch به این صورت تغییر میکند:

پس دیدیم که تابع cost function به دو تابع content cost function و style cost function تقسیم می‌شود. حال جداگانه به بررسی هرکدام می‌پردازیم:

Content Cost Function

یک شبکهpre-trained مانند VGG  برمیداریم، یک hidden layer(L) را انتخاب کرده، که باید content ها را خوب حدس بزند. این لایه hyper parameter است و بسته به نوع مسئله تغییر می‌کند و از روی visualization مسئله قابل تشخیص است.

Content با activation map مقایسه می‌شود، یعنی یک لایه L را در نظر می‌گیریم، عکس content و generated image را به عنوان ورودی به آن می‌دهیم، و اگر activation map لایه روی این دو عکس را مقایسه کنیم، میزان تفاوت content آن‌ها مشخص می‌شود. اگر بخواهیم خیلی سخت‌گیرانه، بگوییم content باید مثل عکس ورودی باشد، باید لایه اول را انتخاب کنیم، زیرا به هر تغییر کوچکی در خط و رنگ و لبه حساس است. پس انتخاب لایه اول انعطاف کمی به ما می‌دهد. و لایه‌های آخر هم به دلیل میزان زیاد abstract بودن مناسب نیستند. به همین دلیل این لایه، معمولا از لایه‌های وسطی انتخاب می‌شود.

سپس باید activation map های عکس اصلی و عکس content مثل هم شوند.

در نهایت اگر a[L](C) و a[L](G) مثل هم بودند، می‌توان گفت هر دو عکسcontent یکسان دارند.

برای مقایسه میزان شباهت این دو، از MSE استفاده می‌کنیم.

Style

در این مقاله، correlation، بین activation ها در کانال‌های مختلف عکس style تعریف می‌شود.

Activation map هر کانال، نمایان‌گر تعدادی feature است. سپس کانال‌های عکس در هم ضرب می‌شوند و correlation آن‌ها محاسبه می‌شود. اگر این مقدار بالا باشد، یعنی آن فیلترها با هم زیاد دیده شده‌اند. و اگر منفی باشد یعنی اصلا باهم دیده نشده‌اند.

برای مثال، اگر شکل زیر، نمایانگر 9تا از کانال‌ها باشد، و 9تا از object هایی که در هر کانال بیشتر fire میکند، اگر correlation یک کانال، با یک کانال دیگر زیاد باشد، یعنی دو pattern همیشه باهم رخ داده‌اند. پس در style هنگام استفاده از یکی از patternها، از دیگری هم استفاده می‌شود. و اگر این correlation پایین باشد، هنگام استفاده از یکی، به هیچ‌ وجه از دیگری استفاده نمی‌شود.

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

Gram Matrix

برای مقایسه correlation بین کانال‌ها ازGram Matrix  استفاده می‌کنند. اگر برای مثال، یک عکس با ابعاد 10*10*20 داشته باشیم، ابعاد این ماتریس 20*20 خواهد بود. یعنی 20*20 حالت کانال‌های این عکس در هم ضرب می‌شوند.

Gram Matrix برای عکس style :

Gram Matrix برای عکس generated :

Style Cost Function

در نتیجه، style cost function به این صورت تعریف می‌شود:

گفته شد که برای content از لایه‌های وسط استفاده می‌شود، اما برای style از لایه‌های مختلف استفاده می‌شود. یعنی  loss آن را ترکیبی از loss لایه‌های مختلف در نظر می‌گیرند.

پیاده‌سازی

مسیر دو عکس content و style را مشخص می‌کنیم. (در این کد، target image همان content image است.) وسپس سایز عکس را برای راحتی کار یکسان می‌کنیم.

image preprocessing

دو عکس content و style را به صورت constant تعریف می‌کنیم. چون نمی‌خواهیم در طول برنامه تغییری کنند.

سپس یک placeholder تعریف می‌کنیم تا چیزی که قرار است به شبکه feed کنیم را تعیین کنیم.

سپس این سه مورد را باهم concat می‌کنیم. تا شبکه VGG ، activation map را برای هر سه آن‌ها حساب کند. و سرعت کار افزایش یابد.
include top را هم false قرار می‌دهیم. چون فقط به لایه‌های کانولوشنی به عنوان activation map نیاز داریم. نه لایه‌های fully connected.

سپس content loss را تعریف می‌کنیم. که با MSE محاسبه می‌شود.

سپس gram matrix و style loss را همراه با نرمال‌سازی تعریف می‌کنیم.

همچنین یک نوع loss دیگر به عنوان regularization loss تعریف می‌کنیم که در مقاله گفته شده بود. و بیانگر این است که هر پیکسل نباید با پیکسل مجاور خیلی فاصله داشته باشد. چون در صورد زیاد بودن فاصله‌شان تصویر نهایی smooth نخواهد شد. همچنین باید ضریب آن کوچک باشد تا روی  loss اصلی خیلی تاثیر نگذارد.

سپس یک dictionary می‌سازیم و activation map output هر لایه را میگیریم.

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

و ضریب هر loss را نیز مشخص می‌کنیم.

سپس loss را یک متغیر با مقدار صفر در نظر می‌گیریم و در هر مرحله با محاسبه انواع loss آن را تغییر می‌دهیم. outputs_dict[content_layer] نشان‌دهنده activation map هر لایه است.
content loss و style loss و total_variation loss را محاسبه می‌کنیم و به loss اضافه می‌کنیم.

در مقاله از LBFGS به عنوان optimizer استفاده شده است. که در کتابخانه scipy موجود است. که با دادن loss، gradian را حساب می‌کند.
هدف از نوشتن این تابع این است که یکبار loss و gradian حساب شود و به عنوان cache ذخیره شود. تا دفعات بعدی نیازی به محاسبه مجدد نباشد.

در مرحله بعد از LBFGS را لود می‌کنیم. سپس روی عکس pre processing انجام می‌‌دهیم. و آن را flat می‌کنیم. و روی عکس iteration انجام می‌دهیم.
سپس از تابع lbfgs استفاده می‌کنیم. و x را به روز می‌کنیم.

و در آخر نتیجه را مشاهده میکنیم:

فوتر سایت

اسلایدر سایدبار

درباره ما

درباره ما

ما گروهی از دانشجویان رشته کامپیوتر دانشگاه شهید رجایی هستیم که به راهنمایی و تدریس استاد علی‌رضا اخوان‌پور به دنیای یادگیری عمیق پا گذاشتیم و دوست داریم چیزهایی که در این مسیر یادمی‌گیریم رو به اشتراک بذاریم.

شبکه های اجتماعی

مطالب اخیر