image-captioning

caption generation یا تولید یک توصیف به عنوان یک چالش در هوش مصنوعی مطرح شده است که در آن باید یک توصیف متنی برای یک عکس دلخواه داده شده به آن تولید کند .

این عملیات به هر دو روش از بینایی کامپیوتر برای درک محتوای تصویر و یک مدل زبان از زمینه پردازش زبان طبیعی برای تبدیل درک یک تصویر به کلمات برای تولید یک عنوان یا توصیف در جهت درست است .

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

آنچه در این روش ها تاثیر گذار است یک مدل end to end به منظور پیش بینی یک عنوان یا توصیف با توجه به یک عکس است بجای نیاز به تهیه داده های پیچیده و یا یک pipeline از مدل های طراحی شده خاص .

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

این آموزش به 7 بخش تقسیم میشود که عبارت است از :

  1. دیتاست عکس و عنوان یا توصیف
  2. آماده کردن داده عکس
  3. آماده کردن داده متن
  4. توسعه مدل یادگیری عمیق
  5. آموزش با loading پیشرفته
  6. ارزیابی مدل
  7. تولید عنوان یا زیرنویس جدید

نیازمندی های این آموزش :

  1. نصب محیط scipy ایده آل با pyhton 3
  2. نصب کراس ورژن 2.1.5 یا بالاتر با tensorflow یا theano backend
  3. نصب Scikit-Learn , NumPy , Pandas ,Matplotlib
دیتاست عکس و عنوان

یک مجموعه داده خوب برای استفاده در هنگام گرفتن عناوین تصویر  ، مجموعه داده Flickr8k است . به دلیل اینکه این مجموعه داده واقعی و نسبتا کوچک است به همین خاطر شما میتوانید آن را دانلود کنید و روی سیستم خودتان (روی CPU) مدل های مختلف بسازید .

نویسندگان مقاله ای که این مجموعه دیتاست را در اختیار ما قرار داده اند ، این مجموعه داده را بصورت زیر شرح میدهند :

ما یک مجموعه معیار جدید برای توصیف تصویر و جستجو را که شامل 8000 تصویر است ارائه داده ایم که هر کدام با 5 عنوان مختلف مرتبط شده اند.

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

دیتاست به صورت رایگان در دسترس است و بعد از دانلود دیتاست شما 2 فایل دارید .

این مجموعه  دارای مجموعه داده های از پش تعریف شده (6000 عکس) ، محموعه داده های توسعه داده شده (1000 عکس) و مجموعه داده آزمون (1000 عکس) است .

یک معیار برای ارزیابی مهارت مدل ، نمرات BLEU است . این معیار را در ادامه هنگام ارزیابی مدل بیشتر توضیح میدهیم .

آماده سازی دیتاست عکس

در اینجا از یک مدل از پیش آموزش داده شده برای تفسیر محتوای عکس استفاده شده است . مدل های زیادی برای انتخاب وجود دارند . در اینجا از مدل oxford visual geometry  یا VGG استفاده شده است .

keras این مدل از قبل آموزش داده شده را بطور مستقیم فراهم میکند . توجه داشته باشید که شما اولین بار که از این مذل استفاده میکنید ، کراس وزن ها را دانلود میکند که حدود 500 مگابایت است .

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

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

این کار در VGG به همین صورت است با این تفتوت که در آنجا فقط یک بار انجام میگیرد .

این عملیات همان بهینه سازی است که باعث تسریع در آموزش مدل و مصرف حافظه کمتر میشود .

مدل VGG را در کراس با کلاس VGG بارگذاری میکنیم و لایه ی آخر را از مدل load شده حذف میکنیم زیرا این مدل برای پیش بینی طبقه بندی یک تصویر استفاده میشود .

keras ابزار هایی برای تغییر اندازه تصویر load شده دارد (مثلا 3 کانال 224 * 224 pixel image)

در زیر تابعی به اسم ()extract_features داریم با اسم دایرکتوری که میگیرد آن تصویر را load میکند ، آن را برای VGG آماده میکند ویژگی های پیش بینی شده از مدل VGG را جمع آوری میکند . ویژگی های تصویر یک بردار 1 بعدی 4096 تایی از عناصر است . این تابع یک دیکشنری از شناسه ی تصویر برای ویژگی های تصویر بر میگرداند .

 

این تابع را میتوان برای تهیه داده های تصویر برای آزمایش مدل هایمان صدا بزنیم ، سپس دیکشنری تولید شده را با نام features.pkl ذخیره کنیم . کد بصورت کامل تر :

 

آماده کردن داده متنی

این مجموعه داده شامل توضیحات متعدد برای هر تصویر است و متن توضیحات نیاز به مقداری تغییرات دارد . در ابتدا فایل شامل همه توصیفات را load میکنیم .

 

هر تصویر شناسه ی واحد مختص به خود دارد . این شناسه روی اسم فایل عکس و در فایل متنی توضیحات استفاده میشود .

سپس براساس لیست توضیحات عکس قدم برمیداریم . در زیر تابعی داریم به اسم ()load_descriptions تعریف شده است که یک دیکشنری از شناسه تصویر برای توضیحات برمیگرداند . هر شناسه تصویر به یک لیستی از یک یا چند توصیف متنی map میکند .

 

سپس باید متن توصیف را تمیز کنیم . توصیفات همواره نشانه گذاری شده هستند و کار با آن ها آسان است .

متن را با شیوه های زیر تمیز میکنیم تا اندازه کلماتی که باید با آن ها کار شود را کاهش دهیم :

  1. تبدیل تمامی کلمات به حروف کوچک
  2. حذف تمامی نشانه ها
  3. حذف تمامی کلماتی که تک کاراکتر یا کمتر هستند (مثلا a)
  4. حذف کلمات شامل عدد

در زیر تابع ()clean_descriptions تعریف شده است که دیکشنری از شناسه ی تصاویر به توضیحات را میگیرد و با قدم برداشتن در طول هر توصیف آن را تمیز میکند .

 

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

 

 

در نهایت میتوان دیکشنری حاصل و توضیحات را در یک فایل جدید با نام descriptions.txt با یک شناسه تصویر و توضیحات در هر خط ذخیره کرد.

در زیر تابع ()save_descriptions را داریم که به عنوان ورودی یک دیکشنری حاوی mappinf شناسه ها به توضیحات و یک اسم فایل میگیرد و mappind را در آن فایل ذخیره میکند.

 

توسعه مدل یادگیری عمیق

در این بخش مدل یادگیری عمیق تعریف میشود و آن را در مجموعه داده های fit ، train میکنیم . این بخش اینگونه تقسیم میشود:

  1. بارگذاری داده
  2. تعریف مدل
  3. fit کردن مدل
  4. تکمیل مثال
بارگذاری داده

ابتدا باید داده های آماده شده عکس و متن را بارگذاری کنیم تا بتوانیم از آن در fit کردن مدل استفاده کنیم . تصمیم گرفتیم داده را روی همه عکس ها و متن های داده train آموزش دهیم . در حین آموزش تصمیم داریم کارایی مدل روی داده توسعه یافته را مانیتور کنیم و از آن برای تصمیم گیری اینکه چه زمان مدل ها را در فایل ذخیره کنیم ، استفاده کنیم . داده train و توسعه یافته به ترتیب در فایل های Flickr_8k.trainImages.txt و Flickr_8k.devImages.txt ذخیره شده است که هردو حاوی لیستی از نام های فایل عکس هاست . از این اسم فایل ها میتوانیم شناسه های تصاویر را استخراج کنیم و از این شناسه ها برای فیلتر کردن تصاویر و توضیحات برای هر مجموعه استفاده کنیم.

تابع ()load_set یک مجموعه از شناسه های داده شده از اسم فایل مجموعه های train و توسعه یافته را بارگذاری میکند .

 

در حال حاضر میتوانیم عکس ها و توصیفات را با استفاده از مجموعه از پیش تعریف شده شناسه های train یا توسعه یافته

در زیر تابع ()load_clean_description برای مجموعه ای از شناسه های داده شده توصیفات متن تمیز شده را از description.txt بارگذاری میکند  و یک دیکشنری از شناسه ها به لیستی از توصیفات متنی برمیگرداند .

مدلی که ما توسعه خواهیم داد یک توصیف یا عنوان را برای عکس داده شده تولید میکند و توصیف در هر لحظه زمانی یک عنوان ایجاد میشود . دنباله ای از کلمات قبلا تولید شده به عنوان ورودی ارائه میشود . بنابراین نیاز به یک کلمه اول برای اولین بار از فرایند تولید و یک آخرین کلمه برای سیگنال پایان توصیف داریم . بدین منظور از رشته “startseq” و “endseq” استفاده میکنیم . این نشانه ها به توصیفات بارگذاری شده اضافه میشوند . مهم است که قبل از اینکه متن را encode کنیم این کار را انجام دهیم تا token ها به درستی encode شوند .

 

در زیر تابعی داریم به نام ()load_photo_features که تمام مجموعه توصیفات عکس را بارگذاری میکند و زیر مجموعه ای از interest را برای یک مجموعه شناسه های عکس داده شده برمیگرداند . این کار خیلی کارآمد نیست با این وجود کار را سریع و اجرا را سریع میکند .

 

گام اول در رمزگذاری داده ها ، ایجاد یک mapping سازگار از کلمات مقادیر عدد صحیح منحصر به فرد است . keras کلاس tokenizer را فراهم میکند که میتواند این mapping را از داده ی توصیف بارگذاری شده ، یاد بگیرد .

در زیر تابع ()to_lines را تعریف میکنیم تا دیکشنری توصیفات را به یک لیستی از رشته ها تبدیل کنیم و تابع ()create_tokenizer هم یک tokenizer داده شده به متن توصیف عکس load شده را fit میکند .

 

حالا میتوانیم متن را encode کنیم . هر توصیف به کلمات تقسیم میشود. مدل یک کلمه و عکس را میگیرد و کلمه بعدی را ایجاد میکند . سپس  به عنوان ورودی اولین 2 کلمه از توصیف با یک عکس به مدل داده میشود برای تولید کلمه بعدی . نحوه آموزش مدل :

 

زمانیکه این مدل برای تولید توصیفات استفاده میشود ،  کلمات تولید شده پیوند داده میشوند و به عنوان ورودی برای تولید یک توصیف برای یک عکس بصورت بازگشتی ارائه میشود.

تابع زیر به اسم ()build_sequences با توجه به tokenizer ، حداکثر طول sequence و دیکشنری از همه ی توصیفات و عکس ها ، داده را به جفت داده های ورودی/خروجی از داده برای آموزش مدل منتقل میکند.

متن ورودی به عنوان عدد صحیح encode میشود که به یک لایه word embedding داده میشود . ویژگی های عکس بطور مستقیم به قسمت های دیگری از مدل تغذیه میشوند . این مدل پیش بینی میکند که توزیع احتمالی بیش از همه کلمات در واژگان خواهد بود.

داده خروجی یک بردار one-hot از هر کلمه است .

 

ما باید حداکثر تعداد کلمات را در طولانی ترین caption محاسبه کنیم. بدین منظور تابع کمکی به نام ()max_length را تعریف میکنیم .

 

تعریف مدل

ما یک مدل یادگیری عمیق را بر اساس “مدل ادغام” توصیف میکنیم :

مدل را در 3 بخش تعریف میکنیم :

  1. استخراج ویژگی عکس : آن یک مدل از پیش آموزش داده شده  VGG -16 -layer روی دیتاست imageNet است . ما روی عکس ها پیش پردازشی با مدل VGG انجام دادیم (بدون لایه خروجی) و از ویژگی های استخراج شده پیش بینی شده توسط این مدل به عنوان ورودی استفاده خواهیم کرد .
  2. پردازنده Sequence : این یک لایه embedding برای کنترل ورودی متن است که توسط یک لایه شبکه عصبی بازگشتی LSTM دنبال میشود .
  3. Decoder : خروجی استخراج ویژگی و پردازنده sequence هردو یک آرایه با طول ثابت است . اینها با یکدیگر ترکیب شده و با یک لایه dense برای پیش بینی نهایی پردازش می شوند .

مدل استخراج ویژگی عکس ها انتظار دارد ویژگی های عکس ورودی  یک وکتور 4096 عنصری باشد . اینها توسط یک لایه dense برای تولید 256 عنصر مورد استفاده قرار میگیرند .

مدل پردازنده sequence انتظار دارد sequence های ورودی با یک طول از قبل تعریف شده (34 کلمه) که به یک لایه embedding که یک mask برای نادیده گرفتن مقادیر padded استفاده میکند ، fed میشود . این توسط یک لایه LSTM با 256 واحد حافظه دنبال میشود .

هر دو مدل ورودی یک آرایه 256 عنصری تولید میکند . علاوه بر این هر دو مدل ورودی از Regularization بصورت 50% dropout استفاده میکند . این کار در جهت کاهش overfitting روی دیتاست training ، باعث یادگیری سریع مدل میشود .

مدل decoder وکتور هایی از هردو مدل های ورودی را با استفاده از یک عملیات اضافه ادغام میکند . سپس به یک لایه نورون 256 تایی dense و بعد از آن به یک لایه dense خروجی نهایی که یک پیش بینی softmax را در طول همه ی واژگان خروجی برای کلمه بعدی در جمله ایجاد میکند ، میفرستد .

تابع ()define_model بصورت زیر تعریف میشود و مدل را آماده برای fit شدن برمیگرداند .

 

یک طرح برای مشاهده ساختار این شبکه برای درک بهتر در زیر آورده شده است :

fit کردن مدل

این مدل overfit های داده training را سریع یاد میگیرد به همین دلیل ما مهارت مدل آموزش داده شده را روی مجموعه داده های توسعه monitor خواهیم کرد. وقتی مهارت مدل در مجموعه داده توسعه در پایان یک epoch بهبود یافت ، ما کل مدل را در فایلی ذخیره میکنیم .

در پایان اجرا ، ما میتوانیم از مدل ذخیره شده با بهترین مهارت روی دیتاست training به عنوان مدل نهایی استفاده کنیم .

ما میتوانیم این کار را با تعریف یک ModelCheckpoint در کراس انجام دهیم آن را مشخص کنیم تا مینیمم loss را روی دیتاست validation داشته باشیم و مدل را در یک فایلی که هم training loss و هم validation loss دارد ذخیره میکنیم .