یادگیری عمیق با کراس - بخش دوم (چطور با شبکه های عصبی ارقام دست نویس فارسی را بخوانیم)


به صورت سنتی Hello World یادگیری عمیق، آموزش تشخیص ارقام دست نویس انگلیسی دیتاست معروف MNIST هست. مثلا اگر شما به اکثر مثال های آموزشی فریمورک های یادگیری عمیق مثل تنسورفلو و کراس نگاه کنید می بینید که اولین مثالی که زده شده نحوه تشخیص این ارقام دست نویس است. از اونجا که من دوست دارم مثال هایی که می زنم با خودمون مرتبط تر باشه تصمیم گرفتم که برای آموزش شبکه های عصبی در کراس از دیتاست اعداد دست نویس فارسی استفاده کنم. خوشبختانه دیتاست فارسی متناظر با MNIST هم به اسم «دیتاست هدی» وجود داره که اینجا از اون استفاده می کنم.
از این جا به بعد به احترام حفظ زبان فارسی پست را با زبان فارسی رسمی می نویسم :)

توجه: فایل ها و کدهای استفاده شده در این پست همگی در گیت هاب من موجود هستند.

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

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

نمونه ای از تصاویر ارقام دست نویس فارسی دیتاست هدی
نمونه ای از تصاویر ارقام دست نویس فارسی دیتاست هدی


برای این کار فرآیندی که طی می‌کنیم این است که اول یک شبکه عصبی را با استفاده از فریمورک کراس می‌سازیم. سپس داده ها را تقسیم می کنیم و مجموعه ای از داده‌ها (تصویر به اضافه برچسب عدد متناظر با آن تصویر) که به آن‌ها مجموعه آموزشی می گوییم را به شبکه می‌دهیم و مجموعه دیگری از داده‌ها که یک گوشه نگه می‌داریم تا بعدا با آن عملکرد شبکه عصبی را بسنجیم و به آن مجموعه تست می گوییم. در نهایت شبکه را با مجموعه داده های آموزش، آموزش می‌دهیم تا یاد بگیرد که چطور تصاویر و برچسب‌های متناظر با آن‌ها را به هم نگاشت کند. در نهایت هم تصاویر مجموعه تست را به شبکه می‌دهیم تا شبکه برچسب آن‌ها پیشبینی کند و ما هم برای این که ببینیم مدل ما خوب بوده است آن‌ها را با برچسب واقعی‌شان مقایسه کنیم.خواندن داده‌هاپیش از این که وارد مدل سازی شبکه عصبی به وسیله کراس شویم لازم است که اول دیتاست تصاویر را بخوانیم و یک سری پیش پردازش بر روی داده‌ها انجام دهیم. برای این کار ابتدا کتابخانه‌های opencv، numpy، matplotlib، scipy را که برای ادامه کار لازم داریم به محیط پایتون اضافه می‌کنیم. کتابخانه opencv که یک کتابخانه پردازش تصویر برای پایتون است که طریقه نصب آن را می‌توانید در این لینک ببینید.

import scipy.io
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import cv2

داده‌های مربوط به تصاویر که می‌توانید در گیت هاب من دانلود کنید به صورت یک فایل با فرمت.mat (فرمت مخصوص ذخیره سازی داده‌های متلب) قرار دارند. برای این که این داده‌ها را بخوانیم از تابع ()loadmat ماژول scipy.io استفاده می‌کنیم و داده‌ها را در یک متغیر به نام hoda می‌ریزیم.

hoda = scipy.io.loadmat('data\\Data_hoda_full.mat')

اگر نوع داده متغیر hoda را بررسی کنیم می‌بینیم که این متغیر در اصل یک دیکشنری پایتون است.

print(type(hoda))

dict

حالا اگر نگاهی به کلید های این دیکشنری بیاندازیم می‌بینیم که این دیکشنری دارای دو کلید به نام‌های Data و labels است که ما هم فقط با همین دو کار داریم.

print(hoda.keys())

dict_keys(['__header__', '__version__', '__globals__', 'Data', 'labels'])

مقدار متناظر با کلید Data در حقیقت یک آرایه نامپای است که هر مؤلفه آن آرایه‌های پیکسل‌های تصویر هستند که اعدادی بین 0 تا 255 را می‌گیرند. اگر ابعاد این آرایه را هم بررسی کنیم می‌بینیم که در مجموع داده 60000 تصویر در این آرایه ذخیره شده‌اند.

print(type(hoda['Data']))

<class 'numpy.ndarray'>

print(hoda['Data'].shape)

(60000, 1)

مقدار ذخیره شده در کلید labels هم یک آرایه نامپای دوبعدی شامل برچسب متناظر با عدد هر کدام از تصاویر است.

print(type(hoda['labels']))

<class 'numpy.ndarray'>

print(hoda['labels'].shape)

(60000, 1)

برای ساده تر شدن ادامه کار آرایه نامپای متناظر با Data و labels تصاویر را به صورت یک آرایه یک بعدی (ستونی) در می‌آوریم.

data= hoda['Data'].reshape(-1)
print(data.shape)

(60000,)

labels = hoda['labels'].reshape(-1)
print(labels.shape)

(60000,)

حالا به وسیله کتابخانه matplotlib و تابع ()plt.show سعی می‌کنیم به صورت نمونه یکی از تصاویر این دیتاست را رسم کنیم و برچسب متناظر با آن را با آن چک کنیم.

pic1_data = data[1]
pic1_label = labels[1]
plt.figure(figsize = (5,5))
plt.axis('off')
plt.imshow(pic1_data,cmap= matplotlib.cm.Greys)
plt.show()

همانطور که می‌بینیم این تصویر عدد 5 را نشان می‌دهد. حالا با چاپ برچسب آن صحت 5 بودن این تصویر را هم چک می‌کنیم!

print(pic1_label)

5

پیش پردازش داده‌هاپیش از این که شبکه را آموزش دهیم لازم است که داده‌ها را پیش پردازش کنیم تا به صورت مناسبی که شبکه عصبی از داده های ورودی انتظار دارد تبدیل شوند. در این جا ما با چند چالش روبرو هستیم. اول از همه اندازه تصاویر درون دیتاست با هم متفاوت است و این برای شبکه عصبی ما مشکل ایجاد می‌کند. دلیل این مشکل این است تعداد پیکسل‌های تصاویر نیز در این حالت متفاوت است و در نتیجه ابعاد ورودی‌های ما هم متفاوت است ولی برای استفاده از شبکه‌های عصبی نیاز است که ابعاد ورودی‌های ما با هم برابر باشند و در غیر ما نمی‌توانیم از شبکه عصبی برای آموزش مدلی برای تشخیص عدد تصاویر استفاده کنیم. به طور مثال می‌توانیم ببنیم که اندازه چند تصویر اول دیتاست با هم متفاوت است:

for i in range(1,6):
    print(data[i].shape)

(20, 21) (10, 15) (36, 17) (36, 28) (12, 14)

برای حل این مشکل از کتابخانه opencv و تابع ()resize آن به همراه قابلیت List Comprehension پایتون استفاده می‌کنیم تا همه تصاویر موجود در دیتاست را به یک اندازه 5 در 5 دربیاوریم.

data_resized = np.array([cv2.resize(img, dsize=(5, 5)) for img in data])

حالا اگر دوباره چک کنیم می‌بینیم که هم تصاویر ما به یک اندازه درآمده‌اند.

for i in range(1,6):
print(data_resized[i].shape)

(5, 5) (5, 5) (5, 5) (5, 5) (5, 5)

حالا می‌توانیم به عنوان مثال یکی از این تصاویری که اندازه‌اش تغییر پیدا کرده است را رسم کنیم.

plt.figure(figsize = (2,2))
plt.axis('off')
plt.imshow(data_resized[1],cmap= matplotlib.cm.Greys)
plt.show()

خوب این چالش هم برطرف شد ولی چالش دیگر این است که مقادیر ذخیره شده مربوط به پیکسل های هر تصویر باید به یک مقیاس در بیایند چون شبکه های عصبی دوست دارند مقادیر ورودی به آن ها در یک محدوده ( مثلا 0 تا 1) باشد و در غیر این صورت آموزش شبکه عصبی ما به خوبی انجام نخواهد شد. همانطور که گفتیم مقدار ذخیره شده در هر پیکسل عددی بین 0 تا 255 است. برای همین می‌توانیم با تقسیم آرایه هر تصویر بر 255 مقادیر ذخیره شده هر پیکسل آن را به صورت عددی بین 0 تا 1 در بیاوریم.

data_norm = data_resized/255
data_norm[1]

array([[0., 0., 1., 0., 0.], [0., 1., 0.50196078, 1., 0.], [0.8, 0.2, 0., 0.2, 0.8], [1., 0., 1., 0., 1.], [0.2, 0.89803922, 1., 0.6, 0.8]])

حالا ابعاد آرایه داده‌های ورودی را بررسی می‌کنیم.

data_norm.shape

(60000, 5, 5)

همانطور که می‌بینیم ابعاد این آرایه به صورت (5,5,60000) است. برای استفاده از این داده‌ها به عنوان ورودی به شبکه عصبی در کراس لازم است که ابعاد بردار پیسکل ها را تبدیل به (25,60000) کنیم یا اصطلاحاً بردار را به صورت تخت شده (flattened) در بیاوریم تا هر پیکسل از تصویر متناظر با یک نورون ورودی باشد.

data_norm = data_norm.reshape(60000,25)
data_norm.shape

(60000, 25)

حالا نوبت به تقسیم داده‌ها به مجموعه تست و آموزش می‌رسد تا با استفاده از مجموعه آموزش مدل را آموزش دهیم و با استفاده از مجموعه تست عملکرد مدل را بر روی داده‌های دیده نشده بررسی کنیم. برای این کار از کتابخانه scikit-learn و تابع train_test_split آن استفاده می‌کنیم.

from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(data_norm,labels)

برای روشن شدن بیشتر ابعاد هر کدام از این مجموعه را هم بررسی می‌کنیم.

print("size of training dataset is: " + str(X_train.shape))
print("size of test dataset is: " + str(X_test.shape))

size of training dataset is: (45000, 25)

size of test dataset is: (15000, 25)

برای مدل سازی مسائل دسته بندی در کراس باید به ازای هر کدام کلاس باید یک ستون هدف ایجاد کنیم که این ستون‌های هدف شامل مقادیر 0 و 1 هستند. اصطلاحاً به این کار one-hot encoding گفته می‌شود که در شکل زیر هم یک مثال از آن را می توانید ببینید.

روش one-hot-encoding
روش one-hot-encoding

هم در مسئله ما 10 ستون دیگر به ازای 10 کلاس عدد بین 0 تا 9 ساخته می‌شود. اگر مقدار برچسب یک مشاهده برابر با 5 بود مقدار متناظر با ستون کلاس 5 ساخته شده آن برابر با 1 و برای بقیه ستون‌ها برابر با 0 خواهد شد. انجام این کار در کراس بسیار ساده است و با استفاده از تابع ()np_utils.to_categorical می‌توانیم هر کدام از کلاس‌ها را به یک ستون 0 و 1 تبدیل می تماییم.

n_classes = 10
y_train_cat = keras.utils.to_categorical(y_train, n_classes)
y_test_cat = keras.utils.to_categorical(y_train,n_classes)
y_train[1]

array([0., 0., 1., 0., 0., 0., 0., 0., 0., 0.])

ساخت مدل با کراستمام گام‌هایی که تا به الان طی کردیم گام‌های پیش پردازش داده‌ها بود. حالا نوبت این استف که از کراس برای ساخت شبکه عصبی و مدل سازی استفاده کنیم. پیش از شروع باید بگوییم که انواع مختلفی از شبکه‌های عصبی وجود دارند ولی در این بخش ما از شبکه‌های تماماً متصل (fully connected) برای دسته بندی تصاویر اعداد دست نویس فارسی استفاده می‌کنیم. این نوع شبکه‌ها از مجموعه ای پشت از سرهم از لایه‌ها تشکیل شده‌اند که در آن‌ها هر نورون به تمامی نورون‌های لایه بعدی خودش متصل است. برای ساخت این شبکه‌ها در کراس از ماژول model که اصلی‌ترین ساختار در کراس است و لایه‌ها را سازماندهی می‌کند استفاده می‌کنیم. اصلی‌ترین و ساده‌ترین نوع مدل هم در این ماژول مدل Sequential (ترتیبی) است که به صورت یک مجموعه ترتیبی و خطی از لایه‌ها که هر لایه تنها با لایه بعد از خود ارتباط دارد تعریف می‌شود.

شبکه های تماما متصل
شبکه های تماما متصل


این مدل به این خاطر ترتیبی نامیده می‌شود چون ابتدا یک شی خالی از این مدل می‌سازیم و به آن به صورت ترتیبی (Sequential) لایه‌های مختلف اضافه می‌کنیم و به عبارتی دیگر یک شبکه با انتشار رو به جلو می‌سازیم. برای استفاده از مدل باید آن را از ماژول keras.models به برنامه اضافه کنیم.بعد از این که یک شی خالی از مدل را ساختیم باید به تعدادی که مدنظر داریم به آن لایه اضافه کنیم. اضافه کردن لایه به یک شبکه عصبی در کراس بسیار راحت است کافی است که ابتدا از ماژول keras.layers نوع لایه مدنظر را انتخاب و به برنامه اضافه کنیم.. سپس با استفاده از متد add شی مدل ساخته شده استفاده کنیم تا آن لایه به شبکه اضافه اضافه شود.

همانطور که گفته شد برای حل مسئله دسته بندی تصاویر اعداد فارسی در این مرحله ما از معماری شبکه عصبی تماماً متصل بهره می‌بریم. برای همین وقتی می‌خواهیم به مدل ترتیبی لایه اضافه کنیم از لایه‌های استاندارد Dense کراس استفاده می‌کنیم. به این لایه‌ها Dense می گوییم چون تمامی گره‌ها در یک لایه به تمام گره‌ها در لایه بعدی متصل می‌شوند.

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

به علاوه، باید توجه کنیم که پیش از این که مقادیر خروجی شبکه از یک لایه به لایه دیگر منتقل شوند باید از یک تابع فعالسازی غیرخطی عبور کنند چون در غیر این صورت شبکه ما قادر به یادگیری الگوهای غیرخطی موجود در داده ها نخواهد بود. کراس هم تقریباً از همه توابع فعالسازی استانداری که به صورت رایج هم در پژوهش‌ها و هم در کاربردهای عملی مورد استفاده قرار می‌گیرند پشتیبانی می‌کند و هم به ما این امکان را می‌دهد تا برای هر لایه یک تابع فعال سازی متفاوت را انتخاب کنیم. یکی از توابع فعالسازی مشهور که ما در اینجا از آن استفاده می کنیم تابع فعال سازی Relu است که اگر مقدار ورودی آن کوچکتر از 0 باشد(منفی باشد) عدد 0 را بازمی گرداند و اگر بزرگتر از 0 باشد خود آن مقدار را باز می گرداند. در شکل زیر می توانید ساختار تابع فعال سازی Relu را ببینید.

تابع فعالسازی Relu
تابع فعالسازی Relu


در نهایت، در لایه اول هم باید ابعاد داده‌های ورودی‌ها را مشخص کنیم. لایه آخر هم معمولاً بسته به نوع مسئله تعداد نورون‌های متفاوتی دارد مثلاً برای مسئله رگرسیون تعداد نورون‌های لایه آخر برابر با 1 است چون می‌خواهیم تنها یک مقدار را پیشبینی کنیم ولی برای مسئله دسته بندی تصاویر اعداد فارسی تعداد نورون‌ها برابر با 10 است چون می‌خواهیم 10 عدد (0 تا 9) را پیشبینی کنیم.فراتر از توابع فعال سازی، تعداد نورون‌ها و ابعاد ورودی، کراس از اعمال تغییرات بیشتر دیگری مانند تعریف کردن تابع برای وزن گره‌ها و توابع تنظیم سازی برای وزن‌های گره در هر لایه پشتیبانی می‌کند؛ اما یک اصل نانوشته در کراس وجود دارد که بهتر است از همان گزینه‌های پیشفرض استفاده کرد چون این پیشفرض ها بر اساس نتایج بهترین رویه‌های ممکن تعیین شده‌اند.برای این که شبکه را بر روی این دیتاست آموزش دهیم باید سه چیز دیگر را نیز مشخص کنیم:

تابع زیان: تابع زیان عملکرد شبکه را بر روی داده‌های آموزش اندازه گیری می‌کند تا ببینیم که آیا جهتی که طی می‌کنیم جهت درستی است یا خیر؟·

الگوریتم بهینه ساز: الگوریتم بهینه سازی بر اساس تابع زیان و داده‌ها جهتی که وزن‌های شبکه باید به روزرسانی شوند تا شبکه به بهینگی برسد را مشخص می‌کند. در این مثال ما از الگوریتم بهینه ساز ADAM (گرادیان نزولی تصادفی) استفاده می کنیم.

· معیار ارزیابی: معمولاً برای مسائل دسته بندی از معیار صحت استفاده می‌کنیم که نسبت مشاهدات درست پیشبینی شده به کل داده‌ها است.خوب حالا به بحث ساخت و پیاده سازی مدل و اضافه کردن لایه‌های شبکه عصبی به آن در کراس می‌رسیم تا تمامی مواردی که در بخش قبلی گفتیم را در عمل هم اجرا کنیم. برای دسته بندی ارقام ما می‌خواهیم یک شبکه با دو لایه پنهان و یک لایه ورودی بسازیم که در هر لایه 50 نورون قرار دارد. برای شروع ابتدا کلاس‌های مربوط به مدل Sequential و لایه Dense را از ماژول‌های مختلف کراس به برنامه اضافه می‌کنیم.

import keras
from keras.layers import Dense
from keras.models import Sequential

حالا یک نمونه خالی از مدل Sequential را به صورت زیر ایجاد می‌کنیم.

model = Sequential()

حالا گام به گام شروع به اضافه کردن لایه‌ها به مدل می‌کنیم. اولین چیزی که باید مشخص کنیم این است که ابعاد داده‌های ورودی و تعداد نورون‌های این لایه چقدر است. از آنجا که ابعاد بردار تصویر ورودی ما برابر با 25 است و نمی‌خواهیم محدودیتی بر روی تعداد تصاویر ورودی به شبکه داشته باشیم، آرگومان ابعاد ورودی را به صورت input_shape = (25,) قرار می‌دهیم که مولفه اول ابعاد تصاویر و مولفه دوم آن تعداد تصاویر ورودی است. همچنین باید در هر لایه تابع فعال سازی را هم مشخص کنیم که در کراس این کار با استفاده از آرگومان ورودی activation انجام می‌شود. در دو لایه اول از تابع فعال سازی Relu استفاده می‌کنیم ک. برای لایه آخر شبکه از تابع فعالسازی سافت مکس (softmax) استفاده می‌کنیم چون این تابع یک عدد بین 0 و 1 یا به عبارتی دیگر یک خروجی احتمالی به ما می‌دهد که با آن می‌توانیم احتمال تخصیص هر کدام از مشاهدات به کلاس‌ها را محاسبه کنیم.


تابع فعالسازی softmax
تابع فعالسازی softmax


model = Sequential()
model.add(Dense(50,activation = 'relu', input_shape = (25,)))
model.add(Dense(50,activation = 'relu'))
model.add(Dense(50,activation = 'relu'))
model.add(Dense(10, activation='softmax'))

بعد از مشخص کردن نوع مدل، نوع لایه و تعداد نورون‌ها در هر لایه گام بعدی کامپایل کردن مدل ساخته شده به وسیله متد ()compile است. در اصل در این جا است که کراس در پشت صحنه تنسرفلو را اجرا می‌کند. وقتی یک مدل را کامپایل می‌کنیم باید دو ورودی اصلی را به آن بدهیم. اولین ورودی این است که بگوییم که چطور می‌خواهیم صحت مدل را در طی فرآیند آموزش بسنجیم (تابع زیان مدل چه باشد) که این با پارامتر ورودی loss تعیین می‌شود و ما برای معمولاً برای مسائل دسته بندی چندکلاسه مثل مساله ما از Categorical crossentropy استفاده می کنیم. دومین چیز این است که می‌خواهیم از چه الگوریتم بهینه سازی استفاده کنیم که این الگوریتم بهینه ساز با پارامتر ورودی optimizer مشخص می‌شود و فرآیند و نرخ یادگیری را کنترل می‌کند. الگوریتم‌های بهینه سازی مختلفی در کراس وجود دارند که الگوریتم Adam یک الگوریتم مناسب است که خودش در حین فرآیند آموزش نرخ یادگیری را تنظیم می‌کند. برای آشنایی با بقیه بهینه سازهای کراس می‌توان به این آدرس مراجعه کرد. علاوه بر این دو ورودی می‌توانیم با استفاده از ورودی metrics مشخص کنیم که در حین فرآیند آموزش چه معیار ارزیابی نمایش داده شود.

model.compile(loss = 'categorical_crossentropy',optimizer='adam', metrics=['accuracy'])

بعد از کامپایل کردن مدل نوبت به برازش کردن و آموزش دادن آن با متد ()fit می‌رسد که در حقیقت همان فرآیند feed-forward، backpropagation و گرادیان نزولی را بر روی داده‌های آموزش برای به روز رسانی وزن‌ها انجام می دهد. ورودی این متد متغیرهای پیشبینی کننده (پیکسل تصاویر) و متغیر هدف (برچسب متناظر با عدد تصاویر) هستند.

چیز دیگری که می‌توانیم در این مرحله مشخص کنیم این است که چند بار آموزش را بر روی داده‌ها می‌خواهیم اعمال کنیم یعنی چند بار می خواهیم کل داده ها را به الگوریتم بدهیم که این تعداد epoch نامیده می‌شود. تعداد epoch ها در متد ()fit با ورودی epoch مشخص می‌شود. همچنین می‌توانیم در فرآیند آموزش داده‌ها را shuffle کنیم که این کار معمولاً در شبکه‌های عصبی باعث بهبود مدل می‌شود. توجه کنید که اگر تعداد epoch ها خیلی کم باشد ممکن است مدل شبکه عصبی نتواند پیشبینی صحیحی ارائه دهد از سوی دیگر اگر تعداد epoch ها خیلی زیاد باشد زمان آموزش بیشتر می‌شود و ممکن است دچار مسئله بیش برازش شویم. راه حل این است که مدل را تا زمانی که به یک میزان صحت مطلوب رسیدیم ادامه دهیم و سپس آن را متوقف کنیم. در خصوص ورودی batch_size هم در پست های بعدی و زمانی که در خصوص الگوریتم های یادگیری عمیق بحث کردم بیشتر توضیح می دهم.

model.fit(X_train,y_train_cat, batch_size = 512, epochs=100,verbose = 1)

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

خروجی چاپ شده در حین آموزش مدل
خروجی چاپ شده در حین آموزش مدل


همانطور که دیده می‌شود در هر epoch عملکرد مدل در دسته بندی بهتر می‌شود و زیان نیز کمتر می‌شود به طوری که در انتهای آموزش به صحت 95 درصد می‌رسیم. یعنی توانسته ایم عدد مربوط به رقم 95 درصد تصاویر را به درستی تشخیص دهیم.حالا بعد از کامپایل کردن و آموزش دادن مدل که به نتیجه نسبتاً خوبی بر روی داده‌های آموزش رسیدیم نوبت به تست کردن مدل بر روی داده‌های تست می‌رسد. برای تست کردن مدل از متد ()evaluate استفاده می‌کنیم و داده‌های تست را به عنوان ورودی به آن می‌دهیم

model.evaluate(X_test,y_test_cat)

15000/15000 [==============================] - 0s 31us/step [0.14266455941200257, 0.9506000000317891]

اینجا هم می بنیم که بر روی داده‌های تست هم به عملکرد نسبتاً خوب 95 درصد رسیده‌ایم و این یعنی مدل ما دچار مشکل بیش برازش نشده است. می‌توانیم حالا که مدل را آموزش دادیم در آخرین گام با استفاده از آن برچسب تصاویر جدید را هم پیشبینی کنیم. برای این کار از متد ()predict_classes استفاده می‌کنیم که داده‌های ورودی را می‌گیرد و برچسب متناظر با آن تصاویر را باز می‌گرداند.

preds = model.predict_classes(X_test)
preds

array([5, 8, 1, ..., 9, 1, 9], dtype=int64)

خب به انتهای این بخش از آموزش رسیدیم و ممنونم که این پست رو خوندید. در پست های بعدی نحوه پیاده سازی شبکه های کانولشنال (مدل معروف LeNet) و بهبود مدل رو برای بازشناسی ارقام رو هم توضیح می دهم.

https://www.aparat.com/4f41146f-8bc0-4d73-bc9e-ddc4b26a731c