در جلسات قبل با مفهوم multi label classification آشنا شده و یک نمونه از پیاده سازی آن را در فریم ورک کراس مشاهده کردیم.
Multi Label Classification
به طور خلاصه میتوان گفت در single label classification ما به طور همزمان تنها میتوانیم یک کلاس به عنوان پاسخ داشته باشیم مثلا گربه یا سگ اما در multi label classification میتوان همزمان چند جواب داشت مانند تصاویر ماهواره ای که در آن ها میشود همزمان رودخانه، کوه، جنگل و … را مشاهده کرد.
پیاده سازی multi label classification در فریم ورک کراس برای ما محدودیت هایی به همراه داشت که مهمترین آن این است که در کراس ما باید به ازای هر label یک پوشه داشته باشیم که این مسئله زمانی که تعداد کلاس های ما زیاد میشود و ما میخواهیم هربار ترکیبات مختلفی از این کلاس ها را به عنوان label نهایی داشته باشیم برای ما مسئله ساز میشود.
بنابراین برای مسئله های اینچنین که پاسخ ما یک کلاس نیست بهتر است تمامی تصاویر در یک پوشه و label ها در یک فایل اکسل ذخیره شود که فرمت آن به صورت زیر میباشد (ستون اول نام تصویر و ستون دوم کلاس های آن است ).
در این مطلب ما از دیتاست عکس های ماهواره ای استفاده میکنیم و multi label classification را این بار در فریم ورک FastAI پیاده سازی میکنیم.
در مسئله های اینچنین که پاسخ ما میتواند همچنان چند کلاس باشد Acuracy نمیتواند معیار مناسبی برای ارزیابی مدل ما باشد چون اگر مدل یاد بگیرد همواره برای همه ی کلاس ها No پیش بینی کند هم به یه درصد بالایی از Acuracy میتواند برسد.
به همین دلیل از معیاری به نام f2 استفاده میکنیم که براساس تعداد False Postive ها و True Postive ها تعریف میشود.
1 2 3 4 5 6 |
from planet import f2 metrics=[f2] f_model = resnet34 label_csv = f'{PATH}train_v2.csv' n = len(list(open(label_csv)))-1 val_idxs = get_cv_idxs(n) |
در قطعه کد بالا ما معماری شبکه و متریک آن را تعرف کرده سپس دیتاست را خوانده طول و ایندکس های آن را بدست آورده و ذخیره کرده ایم.
1 2 3 4 |
def get_data(sz): tfms = tfms_from_model(f_model, sz, aug_tfms=transforms_top_down, max_zoom=1.05) return ImageClassifierData.from_csv(PATH, 'train-jpg', label_csv, tfms=tfms, suffix='.jpg', val_idxs=val_idxs, test_name='test-jpg') |
سپس در تابع get_data انواع Transformation هایی که میخواهیم انجام دهیم را تعریف میکنیم
transform_top_down:
این پارامتر کاری بیش تر از یک vertical flip ساده انجام میدهد به دلیل اینکه زاویه ی نگاه کردن ما در دیتای عکس ماهواره ای تاثیر چندانی ندارد اگر هر بار عکس را ۹۰ درجه بچرخانیم میتوانیم ۸ حالت مختلف از دیتا را داشته باشیم.
1 |
x,y = next(iter(data.val_dl)) |
اگر از data.val_dl استفاده کنیم دیتاهای ما در دسته های مختلف چندتایی (mini-batch) لود میشوند اما اگر از data.val_ds استفاده کنیم دیتا ها به صورت پشت سرهم لود میشوندو با ایندکس میتوانیم به آن دسترسی داشته باشیم که بیش تر به منظور نمایش و بررسی دیتا استفاده میشود.
در اینجا x دیتای ما و y لیبل های ما است.
که فرمت لیبل های ما ۶۴*۱۷ است که ۶۴ سایز mini-batch و ۱۷ هم تعداد کلاس های مختلف ما هست که همانطور که مشاهده میشود در هر سطر ماتریس لیبل ها به صورت one-hot نیستند و در هر سطر ممکن است بیش تر از یکبار مقدار 1 دیده شود.
در ادامه با استفاده از فانکشن zip در پایتون یکی از این سطر هارا به صورت معنا دار پیرینت کرده ایم تا بدانیم هر ستون مربوط به کدام دیتا است.
1 |
plt.imshow(data.val_ds.denorm(to_np(x))[0]*1.4); |
عکس را نماش میدهیم و برای اینکه روشن تر و واضح تر شود قبل از نمایش در ۱.۴ ضرب میکنیم.
اینکه دیتاست عکس های ماهواره ای فاصله ی زیادی با عکس های image net دارد میتواند یک نکته ثبت تلقی شود زیرا در دنیای واقعی ما نیاز داریم مسئله های زیادی که دیتایشان با دیتای image net فاصله دارد را با شبکه های CNN حل کنیم مانند مسائل پزشکی، ماهواره ای و …
و به همین دلیل که دیتای ما فصله ی زیادی با Image netدارد میتوانیم سایز ورودی مدل را تغییر بدهیم بدون آنکه مشکل خاصی ایجاد شود اما اگر میخواستیم این کار را برروی چالشی مانند catVsDog که تمامی دیتاهایش در Image net دیده شده و بسیار نزدیک به هم هستند انجام دهیم احتمالا نتیجه ی بدتری میگرفتیم.
بنابراین اینجا میتوانیم سایز مدل را کوچیکتر یا بزرگتر از سایز اصلی قرار دهیم.
1 2 3 |
sz=64 data = get_data(sz) data = data.resize(int(sz*1.3), 'tmp') |
و سپس دیتا را یکبار با یک سایز بزرگتر کش میکند تا بتواند rotation ها و crop های مختلفی را هر بار از آن در سایز ۶۴*۶۴ لود کند.
1 2 3 |
learn = ConvLearner.pretrained(f_model, data, metrics=metrics) lrf=learn.lr_find() learn.sched.plot() |
برای پیدا کردن learning rate بهینه نمودار آن را رسم کرده است که طبیعتا جون مسئله ی ما از مسئله ای image net دور است مقدار نسبتا بزرگی خواهد بود.
1 2 |
lr = 0.2 learn.fit(lr, 3, cycle_len=1, cycle_mult=2) |
learning rate=0.2 را اتخاب کرده و با سه ایپاک به دقت 88 درصد رسیده است. سپس از differential learning rates با ضرایب lrs = lr/9 , lr/3 , lr استفاده کرده است.
1 2 3 |
lrs = np.array([lr/9,lr/3,lr]) learn.unfreeze() learn.fit(lrs, 3, cycle_len=1, cycle_mult=2) |
و سه ایپاک دیگر هم با learning rate جدید انجام داده و به دقت 92 درصد رسیده است.
مدل را سیو کرده و نمودار Loss آن را رسم میکنیم. که تقریبا در انتهای هر سایکل مدل تقریبا over fit شده و در شروع سایکل جدید دوباره ریست میشود.
1 2 |
learn.save(f'{sz}') learn.sched.plot_loss() |
اکنون سایز مدل را تغییر داده و اینبار با سایز ۱۲۸ آموزش میدهیم.(در ابتدا لایه های اولیه را قفل میکنیم).
1 2 3 4 |
sz=128 learn.set_data(get_data(sz)) learn.freeze() learn.fit(lr, 3, cycle_len=1, cycle_mult=2) |
تفاوت چشمگیری حاصل نشده و دقت همان 92 درصد باقی می ماند
اکنون قفل لایه های بالایی را باز کرده و اجازه میدهیم کمی آموزش ببینند.
1 2 3 |
learn.unfreeze() learn.fit(lrs, 3, cycle_len=1, cycle_mult=2) learn.save(f'{sz}') |
دقت مدل اندکی بهتر میشود.
1 |
sz=256 |
همین روند را یکبار دیگر با سایز 256 تکرار میکنیم.
1 2 3 |
learn.set_data(get_data(sz)) learn.freeze() learn.fit(lr, 3, cycle_len=1, cycle_mult=2) |
دقت حتی مقداری از حالت قبل کمتر هم میشود.
1 2 3 |
learn.unfreeze() learn.fit(lrs, 3, cycle_len=1, cycle_mult=2) learn.save(f'{sz}') |
پس از باز کردن قفل لایه های اولیه وضعیت بهتر شده و دقت به 93.5 میرسد.
شبکه های اجتماعی