به نام خدا
اَپ Prisma یک نرم افزار اندرویدی است که این امکان را می دهد، عکسی را انتخاب کنیم و استایل مورد نظر را از سبک نقاشی یا هر استایل دیگری به آن منتقل کنیم. در این پست قصد داریم به طور خلاصه نحوه ی انجام آن را توضیح دهیم.
قبل از اینکه به سراغ تعریف مسئله برویم لازم است تا مطلبی را درباره ی لایه های کانولوشنی در شبکه های عصبی بررسی کنیم. در مقاله ی Visualizing and Understanding Convolutional Networks گفته شده است که در لایه های ابتدایی یک شبکه عصبی، خروجی لایه های کانولوشنی یا همان نقشه های ویژگی می توانند ویژگی های بسیار ساده را استخراج کنند مثل خط و لبه. هر چه لایه های بیشتری بعد از لایه های اول بگذاریم باعث می شود ویژگی های استخراج شده ویژگی های پیچیده تری باشند. اگر تعداد لایه ها زیاد باشد لایه های انتهایی شبکه می توانند ویژگی های پیچیده تری مثل سر انسان یا چشم و غیره را استخراج کنند. شکل1 این نقشه های ویژگی را به خوبی نمایش می دهد.
در این مسئله دو تصویر محتوا و استایل را به عنوان ورودی داریم و خروجی باید ترکیب تصویر اصلی(Content) و استایل(Style) باشد که آن را Generated می نامیم.
در شکل2، نام تصویر اصلی C، تصویر استایل S و تصویری که در نهایت خروجی مسئله است را G نامیده است. برای اینکه استایل را به تصویر اصلی منتقل کنیم نیاز است تا برای هر دو تصویر نسبت به تصویری که قرار است تولید شود یک loss در نظر بگیریم. در ابتدا تصویری را به صورت رندم تولید می کنیم و آن را به عنوان تصویر خروجی(Generated) در نظر می گیریم که در هر مرحله با توجه به loss که بدست می آید تصحیح می شود. تابع loss کلی این مسئله در زیر آمده است که شامل جمع هر دو loss است که هر یک ضریبی دارند که به عنوان ابر پارامتر در نظر گرفته می شود.
تابع loss برای content
برای این مسئله از شبکه های از قبل train شده ای مثل VGG استفاده می کنند. برای محاسبه ی تابع loss تصویر اصلی، باید در شبکه ی آموزش دیده یک لایه ای مثل L را انتخاب کنیم و loss را برای آن محاسبه کنیم که در اینجا با activation map یا همان feature map آن لایه کار می کنیم. تقریبا در لایه های اولیه این activation map ها خیلی دقیق ورودی را نمایش می دهند و لایه های آخر هم خروجی خیلی کلی دارند مثلا اعلام می کنند آیا در تصویر ورودی سگ بوده است یا خیر. پس باید در نظر بگیریم که لایه ای برای loss content انتخاب می کنیم نباید لایه های اول یا لایه های انتهایی شبکه باشد زیرا اگر لایه های ابتدایی باشد شبکه خیلی سعی می کند تصویر خروجی را شبیه تصویر ورودی بسازد. این لایه را برنامه نویس باید با تست کردن به آن برسد اما معمولا لایه های میانی بهتر هستند.
در این حالت باید تصویر content و تصویری که رندم ساخته ایم به شبکه بدهیم برای هر دو activation map لایه L آن ها را استخراج کنیم و طبق رابطه ی MSE سعی کنیم که تصویر رندم را طوری تغییر دهیم که این خطا به حداقل برسد در این صورت تصویر رندم سعی می کند ساختار و شکل تصویر content را حفظ کند.
تابع loss برای Style
در این جا برای محاسبه ی تابع loss از correlation نقشه های ویژگی خروجی لایه ی موردنظر استفاده می کنیم. correlation در این جا به این معناست که اگر مثل شکل3، 9 تا نقشه ی ویژگی داشته باشیم آن ها را در هم ضرب کنیم اگر برای دو نقشه ی ویژگی مقدار زیادی بدست آمد یعنی به هم وابسته هستند و در واقع می توان این گونه تفسیر کرد که اگر یکی از این ویژگی ها در تصویر باشد، دیگری نیز هست و اگر مقدار منفی داشته باشد یعنی این دو با هم رخ نمی دهند.
در این مرحله برای محاسبه ی correlation از gram matrix ها استفاده می کنیم که به شکل زیر بدست می آیند. متغیر i , j سطر و ستون و k نشان دهنده ی کانال های نقشه های ویژگی است.
برای محاسبه ی loss باید تفاوت gram matrix استایل و generated را پیدا کنیم و حداقل کنیم. برای حداقل کردن این تفاوت می بایست تصویر رندمی که داریم را تغییر بدهیم و اینگونه استایل را به آن انتقال می دهیم.
در مقاله ای که در تصویر زیر آمده است در فرمول loss style یک ضریبی هم برای نرمالسازی در نظر گرفته است.
معمولا در پیاده سازی از چند لایه ی شبکه برای استایل استفاده می شود.
در آخر هر مقداری که برای loss بدست آید سعی می کنیم که با تغییر تصویر رندم(generated) آن مقدار را کاهش دهیم و در نتیجه تصویر generated خروجی این مسئله خواهد بود.
سعی شد در این پست این مسئله خیلی خلاصه گفته شود اگر هنوز سوالی از آن برایتان باقی مانده است در کامنت ها حتما بپرسید در حد دانشم پاسخ خواهم داد.
کد:
https://github.com/sanazkhalili/StyleTransfer/blob/main/Neural_Style_Transfer.ipynb
منابع:
https://www.aparat.com/v/JRItx?playlist=156620
https://www.youtube.com/playlist?list=PLkDaE6sCZn6Gl29AoE31iwdVwSG-KnDzF