آموزش Keras : یادگیری انتقالی با استفاده از مدل های از پیش آموزش دیده

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

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

یادگیری انتقالی در مقابل تنظیم دقیق

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

شماتیک شبکه عصبی کانولوشنی
شماتیک شبکه عصبی کانولوشنی

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

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

تصاویر گوجه فرنگی در ImageNet
تصاویر گوجه فرنگی در ImageNet

ساختار ImageNet بر اساس WordNet است که کلمات را به مجموعه های مترادف (synsets) گروه بندی می کند. در واقع به هر synset یک “wnid” یا (Wordnet ID) اختصاص داده شده است. توجه داشته باشید که در یک دسته بندی کلی، می تواند زیر مجموعه های زیادی وجود داشته باشد و هر یک از آن ها متعلق به یک synset متفاوت هستند. به عنوان مثال سگ شاغل (sysnet = n02103406) ، سگ راهنما (sysnet = n02109150) و سگ پلیس (synset = n02106854) سه مجموعه با synset مختلف هستند.

wnid های ۳ کلاس مد نظر ما در زیر آورده شده است

n07734017 -> گوجه فرنگی (Tomato)

n07735510 -> کدو تنبل (Pumpkin)

n07756951 ->  هندوانه (WaterMelon)

دانلود و آماده سازی داده ها

برای دانلود تصاویر Imagenet از wnid ، یک کد مرجع خوب نوشته شده توسط Tzuta Lin وجود دارد که در GitHub موجود است. برای دانلود تصاویر یک “wnid” خاص می توانید از این روش استفاده کنید. برای دانلود تصاویر هر wnid می توانید به این صفحه GitHub مراجعه کرده و دستورالعمل ها را دنبال کنید.

با این حال ، اگر شما در ابتدای کار هستید و نمی خواهید تصاویر با اندازه کامل را دانلود کنید ، می توانید از کتابخانه دیگری از پایتون که از طریق pip – imagenetscraper در دسترس است استفاده کنید. استفاده از آن آسان است و دارای گزینه های تغییر اندازه نیز می باشد. دستورالعمل نصب و استفاده در زیر آورده شده است. توجه داشته باشید که فقط با python3 کار می کند.

# Install imagenetscraper
pip3 install imagenetscraper

# Download the images for the three wnids and keep them in separate folders.
imagenetscraper n07756951 watermelon
imagenetscraper n07734017 tomato
imagenetscraper n07735510 pumpkin

ما دریافتم که داده ها بسیار نویزی هستند ، به عنوان مثال ، بهم ریختگی زیادی وجود دارد ، اشیاء در تصاویر واضح نیستند و غیره. بنابراین ، حدود ۲۵۰ تصویر را برای هر کلاس انتخاب کردیم. ما ابتدا باید دو گروه به نام های” آموزش ” و ” اعتبارسنجی ” ایجاد کنیم تا بتوانیم از توابع Keras برای بارگذاری تصاویر در دسته ها استفاده کنیم.

بارگذاری مدل از پیش آموزش دیده

from tensorflow.keras.applications import vgg16

vgg_conv = vgg16.VGG16(weights='imagenet',
                  include_top=False,
                  input_shape=(224, 224, 3))

در کد بالا ، ما مدل VGG را به همراه وزن های ImageNet مشابه آموزش های قبلی بارگذاری می کنیم. با این حال ، یک تغییر وجود دارد : include_top=False. ما دو لایه آخر کاملاً متصل را که به عنوان کلاسه بند عمل می کنند بارگذاری نکرده ایم. در واقع فقط در حال بارگذاری لایه های کانولوشنی هستیم. لازم به ذکر است که ابعاد لایه آخر ۷x7x512 است.

استخراج ویژگی ها

داده ها به نسبت ۸۰:۲۰ تقسیم شده و در پوشه های جداگانه آموزش و اعتبار سنجی نگهداری می شوند. هر پوشه باید دارای ۳ پوشه متعلق به کلاس های مربوطه باشد. با توجه به سیستم خود می توانید این چیدمان را تغییر دهید.

# each folder contains three subfolders in accordance with the number of classes
train_dir = './clean-dataset/train'
validation_dir = './clean-dataset/validation'

# the number of images for train and test is divided into 80:20 ratio
nTrain = 600
nVal = 150

ما از کلاس ImageDataGenerator برای بارگذاری تصاویر و تابع flow_from_directory برای تولید دسته هایی از تصاویر و برچسب ها استفاده خواهیم کرد.

# load the normalized images
datagen = ImageDataGenerator(rescale=1./255)

# define the batch size
batch_size = 20

# the defined shape is equal to the network output tensor shape
train_features = np.zeros(shape=(nTrain, 7, 7, 512))
train_labels = np.zeros(shape=(nTrain,3))

# generate batches of train images and labels
train_generator = datagen.flow_from_directory(
    train_dir,
    target_size=(224, 224),
    batch_size=batch_size,
    class_mode='categorical',
    shuffle=True)

سپس از تابع ()model.predict برای عبور تصویر از شبکه استفاده می کنیم که به ما یک تنسور با ابعاد ۷x7x512 می دهد. ما تنسور را به یک بردار تغییر شکل می دهیم. به طور مشابه ، ما ویژگی های اعتبارسنجی ( validation_features ) را پیدا می کنیم.

# iterate through the batches of train images and labels
for i, (inputs_batch, labels_batch) in enumerate(train_generator):
    if i * batch_size >= nTrain:
        break    
    # pass the images through the network
    features_batch = vgg_conv.predict(inputs_batch)
    train_features[i * batch_size : (i + 1) * batch_size] = features_batch
    train_labels[i * batch_size : (i + 1) * batch_size] = labels_batch

# reshape train_features into vector        
train_features_vec = np.reshape(train_features, (nTrain, 7 * 7 * 512))
print("Train features: {}".format(train_features_vec.shape))

خروجی :

Train features: (600, 25088)

مدل خود را بسازید

ما یک شبکه پیشخور ساده با لایه خروجی softmax داریم که دارای ۳ کلاس است.

from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras import Sequential, optimizers

model = Sequential()
model.add(Dense(512, activation='relu', input_dim=7 * 7 * 512))
model.add(Dropout(0.5))
model.add(Dense(3, activation='softmax'))

مدل را آموزش دهید

با فراخوانی تابع ()model.fit به سادگی می توان یک شبکه را در Keras آموزش داد ، همانطور که در آموزش های قبلی خود دیده بودیم.

# configure the model for training
model.compile(optimizer=optimizers.RMSprop(lr=2e-4),
              loss='categorical_crossentropy',
              metrics=['acc'])

# use the train and validation feature vectors 
history = model.fit(train_features_vec,
                    train_labels,
                    epochs=20,
                    batch_size=batch_size,
                    validation_data=(validation_features_vec, 
                    validation_labels)
)

بررسی عملکرد

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

# get the list of all validation file names
fnames = validation_generator.filenames

# get the list of the corresponding classes
ground_truth = validation_generator.classes

# get the dictionary of classes
label2index = validation_generator.class_indices

# obtain the list of classes
idx2label = list(label2index.keys())
print("The list of classes: ", idx2label)

خروجی :

The list of classes:  ['pumpkin', 'tomato', 'watermelon']

بیایید تعداد پیش بینی های نادرست را بررسی کنیم :

predictions = model.predict_classes(validation_features_vec)
prob = model.predict(validation_features_vec)

errors = np.where(predictions != ground_truth)[0]
print("Number of errors = {}/{}".format(len(errors),nVal))

از ۱۵۰ تصویر اعتبارسنجی ، ۱۴ کلاس پیش بینی شده اشتباه دریافت می کنیم :

تعداد خطا ها = ۱۴/۱۵۰

بگذارید ببینیم کدام تصاویر اشتباه پیش بینی شده اند :

for i in range(len(errors)):
    pred_class = np.argmax(prob[errors[i]])
    pred_label = idx2label[pred_class]
    
    print('Original label:{}, Prediction :{}, confidence : {:.3f}'.format(
        fnames[errors[i]].split('/')[0],
        pred_label,
        prob[errors[i]][pred_class]))
    
    original = load_img('{}/{}'.format(validation_dir,fnames[errors[i]]))
    plt.axis('off')
    plt.imshow(original)
    plt.show()

در ادامه می توانید چندین مثال از نتیجه اجرای کد بالا را مشاهده کنید :

نتایج نادرست از تشخیص تصویر با یادگیری انتقالی
نتایج نادرست از تشخیص تصویر با یادگیری انتقالی

در پست بعدی سعی خواهیم کرد با استفاده از روش دیگری به نام تنظیم دقیق (Fine-tuning) ، محدودیت های یادگیری انتقالی را بهبود ببخشیم. با ما همراه باشید.

بیشتر بخوانید :

منبع Learn OpenCV

درباره‌ی امیر اقتدائی

همچنین ببینید

آیا می خواهید در زمینه یادگیری ماشین استخدام شوید؟

آیا می خواهید در زمینه یادگیری ماشین استخدام شوید؟

مسیر های شغلی زیادی در حوزه یادگیری ماشین وجود دارد، اما از کجا بفهمیم که …

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *