
تشخیص سرطان پوست با یادگیری عمیق
ملانوما کشندهترین نوع سرطان پوست است و سالانه جان 60.000 نفر را میگیرد. اما اگر تشخیص سرطان پوست در مراحل ابتدایی صورت گرفته شود، میزان بهبودی تا 95 درصد افزایش مییابد. توسعهدهندگان میتوانند با مراجعه به آدرس https://challenge2018.isic-archive.com/ به تصاویر متعددی از سرطان پوست دسترسی پیدا کرده و مدلهای پیشبینیکنندۀ خود را برای تشخیص سرطان پوست ایجاد کنند. پست حاضر بر آن تا است چالشهای این کار را تبیین کند. کد به صورت منبع باز قابل دسترس است:
مرحله 0: نصب
در نوتبوک حاضر، فرض بر این است که سرور نصبشده و مراحل زیر به اجرا درمیآید:
(https://github.com/reshamas/fastai_deeplearn_part1/blob/master/tools/paperspace.md)
## Auto reload plots %reload_ext autoreload %autoreload 2 %matplotlib inline## Using Fast.ai library from Paperspace from fastai.imports import * from fastai.torch_imports import * from fastai.transforms import * from fastai.conv_learner import * from fastai.model import * from fastai.dataset import * from fastai.sgdr import * from fastai.plots import *## Set GPU torch.cuda.set_device(0)## Decrease batch size if running out of memory PATH = "data/lessionChallenge/" sz = 224 arch = resnext101_64 bs = 24## CSV format is image,disease label_csv = f'{PATH}labels.csv' n = len(list(open(label_csv))) - 1 # header is not counted (-1) val_idxs = get_cv_idxs(n) # random 20% data for validation set
مرحله 1: بررسی دادهها
مهمترین بخش بررسی داده، اجتناب از بررسی کاملاً دقیق دادههاست. عده زیادی به فرضیهسازی پرداخته و اطلاعات بیفایدهای را پیش از ساخت مدل ارائه میکنند.
زمانِ اختصاص داده شده: 5 دقیقه
کارهای انجامشده برای استفاده از دادهها در تشخیص سرطان پوست با یادگیری عمیق:
1. چند عکس بهصورت تصادفی پرینت کنید.
2. تعداد عکسها را در هر دسته محاسبه کنید تا ببینید دیتاست بهصورت متعادل توزیعشده است یا خیر.
3. ارتفاع و عرض هر عکس را ترسیم کنید تا محدودیتهای اندازه مشخص شود.

اندازه ستون و ردیفها و تعداد عکسها ## از کتابخانه Pandas و ساختار dataframe ها برای ساماندهی دادهها استفاده کنید. label_df = pd.read_csv(label_csv) label_df.pivot_table(index="disease", aggfunc=len).sort_values('images', ascending=False)## Create Transforms and Data Object ## More on this in the next section tfms = tfms_from_model(arch, sz, aug_tfms=transforms_side_on, max_zoom=1.1) data = ImageClassifierData.from_csv(PATH, 'train', f'{PATH}labels.csv',val_idxs=val_idxs, suffix='.jpg', tfms=tfms, bs=bs)## Plot Random Image fn = PATH + data.trn_ds.fnames[0]; fn img = PIL.Image.open(fn); img## Plot histogram size_d = {k: PIL.Image.open(PATH + k).size for k in data.trn_ds.fnames} row_sz, col_sz = list(zip(*size_d.values())) row_sz = np.array(row_sz); col_sz = np.array(col_sz) row_sz[:5] plt.hist(row_sz); plt.hist(col_sz);
مرحله 2: مدل پایه
کیفیت نتایج اهمیت چندانی ندارد، ضروری است که نتایج سریعاً به دست آیند. برخی از افراد به جای اینکه کارشان را از مدل کوچک و ساده شروع کنند؛ چند روز برای ساخت مدل پیچیده اختصاص میدهند. امیدواریم شما خود را با این موضوع گرفتار نکنید.
ما از Fast.ai برای ساخت مدل پایه در قالب سه کد استفاده کردیم.
Fast.ai به کتابخانه یادگیری عمیقی اطلاق میشود بر پایه pytorch ساخته شده است و زمینه را برای استفادۀ آسان از روشهای نوین فراهم میکند. هدف از این کار، توسعه و آموزش مدلهای یادگیری عمیق است. امکان شخصیسازی این کتابخانه با چندین لایه انتزاع نیز وجود دارد. بنابراین، متخصصان و افراد مبتدی که در حوزه یادگیری عمیق فعالیت میکنند، گزینه بسیار مناسبی برای پیشبرد اهدافشان در اختیار دارند.
در ابتدا، بگذارید ببینیم چگونه میتوان با استفاده از fast.ai یک data object ایجاد کرد.
## Helper function for creating data object def get_data(sz, bs): # sz: image size, bs: batch size ## Create transforms object which makes use of data augmentation tfms = tfms_from_model(arch, sz, aug_tfms=transforms_top_down, max_zoom=1.1) ## Data Object creates dataloaders which help with minibatching data = ImageClassifierData.from_csv(PATH, 'train', f'{PATH}labels.csv',val_idxs=val_idxs, suffix='.jpg', tfms=tfms, bs=bs) return data if sz > 300 else data.resize(340, 'tmp') # Reading the jpgs and resizing is slow for big images, so resizing them all to 340 first saves time

ما از راهبردی استفاده کردیم که جزئیات آن در fast.ai MOOC جِرِمی آموزش داده شده است. افزون بر این، استفاده از یک مدل از پیش آموزشدیده و مجموعهای از لایههای شبکه عصبی در دستور کار ما قرار داشت. درحالیکه بقیه بخشهای مدل در حالت بلوکه قرار دارد، مجموعه فوقالذکر آموزش داده میشود. فراموش نکنید که هدف از این بخش این است که بهسرعت یک مدل پایه بسازیم. ما از دستۀ ConvLearner متعلق به fast.ai برای افزودن مجموعه به مدل از پیش آموزشدیده استفاده کردیم.
این دسته نقش مهمی در انتقال data object و مدل resNext دارد و از این پارامترها برای افزودنِ مجموعهای از لایههای شبکه عصبی استفاده میکند. این لایهها فرصتِ پیشبینی دستهها را به مدل میدهند. دستۀ ConvLearner یکی از دستاوردهای بینظیر fast.ai است زیرا توسعه مدلهای پایه را آسان میکند. اکنون میتوان نتایج مدل پایه را مشاهده کرد.
این فرایند آموزشی جزئیات خوبی درباره لایههای اضافهشده به انتهای مدل عرضه میکند. بهطور خلاصه میتوان گفت که fast.ai توانست این موارد را برای هر بیماری در دادگان بیفزاید:
Adaptive Max Pool، Adaptive Average Pool، Adaptive Concat Pool، Flatten، BatchNorm، Dropout، Linear و Relu. data = get_data(sz, bs) learn = ConvLearner.pretrained(arch, data, precompute=True) learn.fit(1e-2, 3)

مرحله 3: درک متریک ارزیابی
دقت چند دستهای متعادل
این متریک نقش بسیار مهمی در هنگام کار کردن با دیتاست های نامتوازن ایفا میکند زیرا الگوریتمها را به خاطر روی آوردن به متداولترین دستهها جریمه میکند. برای نمونه، اگر الگوریتم با دیتاست نامتوازنی که از 99 تصویر گربه و یک تصویر سگ تشکیل یافته شروع به فعالیت کند، به 99 درصد دقت دست پیدا میکند؛ اگر همه چیز را بهعنوان گربه دستهبندی کند، تنها 50 درصد دقت حاصل میآید. میزان دقت با بررسی ماتریس درهمریختگی و گرفتنِ میانگین از دقت در هر ردیف قابلمحاسبه است.


from sklearn.metrics import confusion_matrixdef get_balanced_accuracy(probs,y,classes): # sz: image size, bs: batch size preds = np.argmax(probs, axis=1) probs = probs[:,1] cm = confusion_matrix(y, preds) plot_confusion_matrix(cm, classes) return (( cm[0][0]/(cm[0][0]+cm[0][1]+cm[0][2]+cm[0][3]+cm[0][4]+cm[0][5]+cm[0][6]) + cm[1][1]/(cm[1][0]+cm[1][1]+cm[1][2]+cm[1][3]+cm[1][4]+cm[1][5]+cm[1][6]) + cm[2][2]/(cm[2][0]+cm[2][1]+cm[2][2]+cm[2][3]+cm[2][4]+cm[2][5]+cm[2][6]) + cm[3][3]/(cm[3][0]+cm[3][1]+cm[3][2]+cm[3][3]+cm[3][4]+cm[3][5]+cm[3][6]) + cm[4][4]/(cm[4][0]+cm[4][1]+cm[4][2]+cm[4][3]+cm[4][4]+cm[4][5]+cm[4][6]) + cm[5][5]/(cm[5][0]+cm[5][1]+cm[5][2]+cm[5][3]+cm[5][4]+cm[5][5]+cm[5][6]) + cm[6][6]/(cm[6][0]+cm[6][1]+cm[6][2]+cm[6][3]+cm[6][4]+cm[6][5]+cm[6][6]) )/7)
سه کد برای ارزیابی Balanced multiclass accuracy:
## TTA stands for Test Time Augmentation log_preds,y = learn.TTA() probs = np.mean(np.exp(log_preds),0) get_balanced_accuracy(probs,y,data.classes)
مرحله 4: یافتن سرعت یادگیری بهینه

سرعت یادگیری یکی از مهمترین پارامترها در مدلهای یادگیری عمیق به شمار میرود و غالباً مستلزم زمان و آزمایش صحیح است تا بهدرستی پیش برود. کتابخانه fast.ai ازجمله نخستین کتابخانههایی است که روشی سریع و نظاممند برای یافتن سرعت یادگیری بهینه برای مدل عرضه میکند. این کتابخانه از روشی استفاده میکند که در مقاله سال 2015 تحت عنوان «سرعت یادگیری چرخهای برای آموزش شبکههای عصبی» معرفی شد. این روش بهتدریج سرعت یادگیری را افزایش میدهد.
## برای رسیدن بهسرعت یادگیری بهینه باید Learn Object جدیدی بسازیم. learn = ConvLearner.pretrained(arch, data, precompute=True) lrf=learn.lr_find() learn.sched.plot_lr() learn.sched.plot()
مرحله 5: دادهافزایی
بهترین راه برای ارتقای مدل این است که دادههای بیشتری جمعآوری کنیم. به لطف فرایند دادهافزایی میتوان تبدیل تصادفی را روی دادههای موجود اعمال کرد و دادههای جدیدی به دست آورد. در سه کد پیشین، شیءی به نام tfms و سپس یک شیء داده ایجاد کردیم . باوجوداین، پس از ساخت مدل، به آن دستور دادیم که از عمل دادهافزایی استفاده نکند.
learn = ConvLearner.pretrained(arch, data, precompute=True)
به خاطر داشته باشید که هدف پیشینمان این بود که هر چه سریعتر مدل را آموزش دهیم. این مؤلفه precompute = True به مدل دستور میدهد تا از عمل دادهافزایی اجتناب کند. این کار به تسریع زمان آموزش منجر میشود. برای اینکه بتوانید از عمل دادهافزایی استفاده کنید، باید این تنظیمات را اِعمال کنید: precompute = false و سپس اقدام به آموزش نمایید. پارامتر cycle_len تعداد دورههای هر چرخه را مشخص میکند. ازاینرو، تعداد دورهها تا زمان شروع مجدد SGDR تعیین میگردد.
learn.precompute = False learn.fit(1e-2, 3, cycle_len=1)

مرحله 6: ارتقای دقت و تقویت سرعت یادگیری متغیر
وزنهای مدل موجود تقریباً بیعیب و نقص هستند و شاید به تنظیم دقیق نیاز نداشته باشند. اما این مورد برای دیتاستی که با ImageNet فرق دارند، صِدق نمیکند. بنابراین، باید وزنها را در مدل موجود بهروزرسانی کنیم تا به نتایج دلخواه برسیم. Learn.unfreeze() به مدل دستور میدهد که وزنهای هر لایه را بهروزرسانی کند. ازآنجاکه نیاز داریم همه وزنهای لایه را بهروزرسانی کنیم، زمان آموزش بهطور چشمگیری افزایش پیدا میکند.
وقتی لایههای موجود در مدل را از حالت بلوکه در بیاوریم، میتوانیم از روشی به نام تقویت سرعت یادگیری متغیر استفاده کنیم. این کار بسیار حائز اهمیت است زیرا نخستین گروه از لایهها غالباً در شناسایی لبهها ایفای نقش میکنند و نیاز به تغییر گسترده ندارند. از سوی دیگر، احتمال دارد لایههای پیچشی بعدی ویژگیهای دیگری مثل مردمک چشم را جستجو کنند.
این ویژگی در دیتاست جدید ما موجود نیست. افزایش سرعت یادگیری در گروههای بعدی این فرصت را به لایهها میدهد تا تخصص لازم را در ویژگیهای پیچیده به دست آورند؛ البته ویژگیهایی که در دیتاست جدید وجود دارند. در عکس زیر، cycle_mult پارامتری است که تعداد بعدی چرخههای آموزش را تکثیر کرده و دورههای بیشتری را برای آموزشِ مدل در اختیارمان میگذارد. به مثال زیر توجه داشته باشید:
lrn.fit(lr,4,cycle_len=1,cycle_mult=2) results in 15epochs because 1+1*2+2*2 + 4*2= 15.


learn.unfreeze() lr=np.array([1e-2/9,1e-2/3,1e-2]) learn.fit(lr, 3, cycle_len=1, cycle_mult=2)log_preds,y = learn.TTA() probs = np.mean(np.exp(log_preds),0) get_balanced_accuracy(probs,y,data.classes)
مرحله 7: افزایش اندازه تصویر و آموزش مجدد
متأسفانه مرحله آخر نتایج خوبی را رقم نزد. بااینوجود، این روش در اغلب موارد مؤثر بوده و بیشبرازش را کاهش میدهد. این روش دربردارندۀ مدلهای آموزشی برای چند دوره در تصاویر کوچک است. اندازه تصاویر در اقدام بعدی بزرگتر شده و عمل آموزش مجدداً انجام میشود.
learn.set_data(get_data(299, bs)) learn.freeze() learn.fit(1e-3, 3, cycle_len=1) log_preds,y = learn.TTA() probs = np.mean(np.exp(log_preds),0) get_balanced_accuracy(probs,y,data.classes)
شما چه روشی را برای بهبود نتایج پیشنهاد میکنید؟