کلاسه بندی تصاویر با شبکه عصبی پیشخور در Keras

در این مقاله ، به نحوه ی پیاده سازی یک شبکه عصبی پیشخور ( Feed Forward ) در Keras خواهیم پرداخت. ما از کلاسه بندی ارقام دست نویس به عنوان مثالی برای نشان دادن اثربخشی شبکه پیشخور استفاده می کنیم. همچنین خواهیم دید که چگونه می توان بیش برازش ( Overfitting ) را در طول آموزش تشخیص داد و بر آن غلبه کرد.

MNIST یک مجموعه داده ارقام دست نویس است که معمولاً مورد استفاده قرار می گیرد و متشکل از ۶۰۰۰۰ تصویر در مجموعه آموزشی و ۱۰۰۰۰ مورد در مجموعه آزمایشی است. بنابراین در مجموعه آموزشی ، هر رقم دارای ۶۰۰۰ تصویر است. ابعاد این تصاویر با اندازه ثابت (۲۸ × ۲۸) نرمال شده اند. هدف ، آموزش یک الگوریتم یادگیری ماشین است که بتواند یک نمونه جدید از مجموعه آزمایشی را به درستی شناسایی کند.

۱- شبکه

شماتیک پرسپترون چند لایه روی MNIST
شماتیک پرسپترون چند لایه روی MNIST

برای درک و مرور سریع شبکه عصبی پیشخور ، می توانید نگاهی به مقاله قبلی ما بیندازید. ما از مقادیر پیکسل خام به عنوان ورودی به شبکه استفاده خواهیم کرد. تصاویر ، ماتریس هایی با اندازه ۲۸ × ۲۸ هستند. بنابراین ، ماتریس تصویر را به آرایه ای به اندازه ۲۸ × ۲۸ = ۷۸۴ تغییر شکل می دهیم و این آرایه را به شبکه وارد می کنیم. ما از شبکه ای با ۲ لایه پنهان که هر کدام ۵۱۲ نورون دارند استفاده خواهیم کرد. لایه خروجی برای ۱۰ رقم دارای ۱۰ واحد خواهد بود. نمودار شماتیک در زیر نشان داده شده است.

اگر هنوز Keras را نصب نکرده اید ، این پست را بررسی کنید! همچنین ، کد را از لینک زیر دانلود کنید تا همراه با پست دنبال شود.

بیایید نگاهی به کد داشته باشیم!

۲- بارگذاری داده ها

با استفاده از تابع ()mnist.load_data ، داده هایMNIST را در Keras بارگذاری می کنیم. اگر این مجموعه داده در رایانه شما وجود نداشته باشد، به کمک این تابع می توانید داده ها را از سرور دانلود کنید. داده های بارگیری شده با استفاده از این تابع ، به زیرمجموعه های آموزشی و آزمایشی تقسیم می شود :

from tensorflow.keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

۳- وارسی داده ها

بیایید ببینیم چه داده هایی را در اختیار داریم. داده ها شامل اعداد دست نویس از ۰ تا ۹ به همراه نسخه حقیقی ( Ground Truth ) شان هستند. متشکل از ۶۰۰۰۰ نمونه آموزشی و ۱۰۰۰۰ نمونه آزمایشی. هر نمونه ، تصویری با ابعاد ۲۸ × ۲۸ در مقیاس خاکستری است.

from tensorflow.keras.utils import to_categorical

print('Training data shape : ', train_images.shape, train_labels.shape)

print('Testing data shape : ', test_images.shape, test_labels.shape)

# Find the unique numbers from the train labels
classes = np.unique(train_labels)
classes_num = len(classes)
print('Total number of outputs : ', classes_num)
print('Output classes : ', classes)

plt.figure(figsize=[10,5])

# Display the first image in training data
plt.subplot(121)
plt.imshow(train_images[0,:,:], cmap='gray')
plt.title("Ground Truth : {}".format(train_labels[0]))

# Display the first image in testing data
plt.subplot(122)
plt.imshow(test_images[0,:,:], cmap='gray')
plt.title("Ground Truth : {}".format(test_labels[0]))

خروجی:

نمونه داده های MNIST
نمونه داده های MNIST

۴- پردازش داده ها

تصاویر در مقیاس خاکستری هستند و مقادیر پیکسل ها بین ۰ تا ۲۵۵ است. ما داده ها را قبل از ورود به شبکه به صورت زیر پیش پردازش خواهیم کرد.

۱- هر ماتریس تصویر (۲۸ × ۲۸) را به یک آرایه (۲۸ × ۲۸ = ۷۸۴ بعدی) تبدیل کرده تا به عنوان یک ویژگی واحد به شبکه منتقل شود.

# Change from matrix to array of dimension 28x28 to array of dimension 784
dim_data = np.prod(train_images.shape[1:])
train_data = train_images.reshape(train_images.shape[0], dim_data)
test_data = test_images.reshape(test_images.shape[0], dim_data)

۲- داده ها را به float تبدیل کرده و مقادیر را از ۰ به ۱ مقیاس بندی می کنیم.

# Change to float datatype
train_data = train_data.astype('float32')
test_data = test_data.astype('float32')

۳- برچسب ها را از عدد صحیح به صورت رمزگذاری وان هات دسته ای ( categorical one-hot encoding ) تبدیل می کنیم زیرا این قالب مورد نیاز Keras برای انجام کلاسه بندی چند کلاسه است. رمزگذاری وان هات دسته ای ، یک نوع نمایش بولین از داده های عدد صحیح است. به این صورت که این عدد صحیح را به آرایه ای از صفرها تبدیل می کند به جز شماره آن عدد صحیح که باید ۱ باشد.

به عنوان مثال ، برای رمزگذاری وان هات دسته ای برای ۱۰ کلاس ، عدد ۵ به صورت ۰۰۰۰۰۱۰۰۰۰ کدگذاری می شود.

# Change the labels from integer to categorical data
train_labels_one_hot = to_categorical(train_labels)
test_labels_one_hot = to_categorical(test_labels)

خروجی:

Original label 0 : 5
After conversion to categorical ( one-hot ) : [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]

۵- چرخه کاری Keras برای آموزش شبکه

چرخه کاری Keras
چرخه کاری Keras

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

۱-۵-  ساخت شبکه

ما گفته بودیم که از یک شبکه با ۲ لایه پنهان و یک لایه خروجی با ۱۰ واحد استفاده خواهیم کرد. تعداد واحدهای موجود در لایه های مخفی ۵۱۲ عدد نگه داشته می شود. ورودی شبکه آرایه ۷۸۴ بعدی است که از تصویر ۲۸ × ۲۸ تبدیل شده است.

ما برای ساخت شبکه از مدل ترتیبی ( Sequential Model ) استفاده خواهیم کرد. در مدل ترتیبی ، فقط می توانیم لایه ها را یکی یکی اضافه کنیم. ما از لایه متراکم ( Dense ) استفاده می کنیم که به آن لایه کاملاً متصل ( Fully Connected ) نیز گفته می شود زیرا ما در حال ساخت یک شبکه پیشخور هستیم که در آن تمام نورون های عصبی از یک لایه به نورون های عصبی موجود در لایه قبلی متصل می شوند. جدا از لایه متراکم ، تابع فعالسازی ReLU را اضافه می کنیم که برای معرفی غیر خطی بودن مدل مورد نیاز است. این به شبکه کمک می کند تا مرزهای تصمیم گیری غیر خطی را بیاموزد. آخرین لایه یک لایه softmax است زیرا این یک مسئله کلاسه بندی چند کلاسه است. برای کلاسه بندی باینری ، می توانیم از sigmoid استفاده کنیم.

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

model = Sequential()
model.add(Dense(512, activation='relu', input_shape=(dim_data,)))
model.add(Dense(512, activation='relu'))
model.add(Dense(classes_num, activation='softmax'))

۲-۵- پیکربندی شبکه

در این مرحله ، بهینه ساز را به صورت rmsprop پیکربندی می کنیم. ما همچنین نوع زیان را تعیین می کنیم که categorical cross entropy است و برای کلاسه بندی چند کلاسه مورد استفاده قرار می گیرد. معیارهایی که تعیین می کنیم باید نیاز ما در طول فرآیند آموزش را برآورده کنند (در این مورد، دقت شبکه حائز اهمیت است). همچنین می توانید از هر بهینه ساز دیگری مانند adam یا SGD نیز استفاده کرد.

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

۳-۵- آموزش مدل

حالا شبکه آماده آموزش است. این کار با استفاده از تابع ()fit در Keras انجام می شود. ما تعداد دوره ها را ۲۰ تعیین می کنیم. این بدان معنی است که کل مجموعه داده ۲۰ بار به شبکه وارد می شود. ما از داده های آزمایشی برای اعتبار سنجی استفاده خواهیم کرد.

history = model.fit(train_data, train_labels_one_hot, batch_size=256, epochs=20, verbose=1, 
                   validation_data=(test_data, test_labels_one_hot))

۴-۵- ارزیابی مدل آموزش دیده

ما با استفاده از تابع ()evaluate ، عملکرد شبکه را بر روی کل داده های آزمایشی بررسی می کنیم.

[test_loss, test_acc] = model.evaluate(test_data, test_labels_one_hot)
print("Evaluation result on Test Data : Loss = {}, accuracy = {}".format(test_loss, test_acc))

خروجی:

Evaluation result on Test Data : Loss = 0.15887983631565528, accuracy = 0.9800999760627747

نتایج خوب به نظر می رسند. با این حال ، ما می خواهیم نگاهی دوباره به نتایج داشته باشیم.

۶- بررسی بیش برازش

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

می توانید از تابع ()history.history.keys برای بررسی معیارهای موجود در تاریخچه استفاده کرد. به صورت زیر :

['accuracy', 'loss', 'val_accuracy', 'val_loss']

اجازه دهید نمودار های زیان و دقت را ترسیم کنیم.

#Plot the Loss Curves
plt.figure(figsize=[8,6])
plt.plot(history.history['loss'],'r',linewidth=3.0)
plt.plot(history.history['val_loss'],'b',linewidth=3.0)
plt.legend(['Training loss', 'Validation Loss'],fontsize=18)
plt.xlabel('Epochs ',fontsize=16)
plt.ylabel('Loss',fontsize=16)
plt.title('Loss Curves',fontsize=16)

#Plot the Accuracy Curves
plt.figure(figsize=[8,6]) plt.plot(history.history['accuracy'],'r',linewidth=3.0) plt.plot(history.history['val_accuracy'],'b',linewidth=3.0) plt.legend(['Training Accuracy', 'Validation Accuracy'],fontsize=18) plt.xlabel('Epochs ',fontsize=16) plt.ylabel('Accuracy',fontsize=16) plt.title('Accuracy Curves',fontsize=16)

نمودار ضرر
نمودار ضرر
نمودار دقت
نمودار دقت

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

۷- اضافه کردن منظم سازی به مدل

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

from tensorflow.keras.layers import Dropout

model_reg = Sequential()
model_reg.add(Dense(512, activation='relu', input_shape=(dim_data,)))
model_reg.add(Dropout(0.5))
model_reg.add(Dense(512, activation='relu'))
model_reg.add(Dropout(0.5))
model_reg.add(Dense(classes_num, activation='softmax'))

۸- بررسی عملکرد بعد از منظم سازی

ما دوباره شبکه را به همان روشی که قبلاً آموزش دادیم آموزش خواهیم داد و منحنی های زیان و دقت را بررسی می کنیم.

model_reg.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])

history_reg = model_reg.fit(train_data, train_labels_one_hot, batch_size=256, epochs=20, verbose=1, 
                            validation_data=(test_data, test_labels_one_hot))

#Plot the Loss Curves
plt.figure(figsize=[8,6])
plt.plot(history_reg.history['loss'],'r',linewidth=3.0)
plt.plot(history_reg.history['val_loss'],'b',linewidth=3.0)
plt.legend(['Training loss', 'Validation Loss'],fontsize=18)
plt.xlabel('Epochs ',fontsize=16)
plt.ylabel('Loss',fontsize=16)
plt.title('Loss Curves',fontsize=16)

#Plot the Accuracy Curves
plt.figure(figsize=[8,6])
plt.plot(history_reg.history['accuracy'],'r',linewidth=3.0)
plt.plot(history_reg.history['val_accuracy'],'b',linewidth=3.0)
plt.legend(['Training Accuracy', 'Validation Accuracy'],fontsize=18)
plt.xlabel('Epochs ',fontsize=16)
plt.ylabel('Accuracy',fontsize=16)
plt.title('Accuracy Curves',fontsize=16)

نمودار ضرر پس از منظم سازی
نمودار ضرر پس از منظم سازی
نمودار دقت پس از منظم سازی
نمودار دقت پس از منظم سازی

از منحنی های زیان و دقت فوق ، می توانیم مشاهده کنیم که :

  • زیان اعتبارسنجی در حال افزایش نیست
  • تفاوت چندانی بین دقت آموزش و اعتبارسنجی وجود ندارد

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

۹- استنباط روی یک تصویر واحد

دیدیم که اولین تصویر در مجموعه آزمایشی عدد ۷ است. بگذارید ببینیم مدل چه چیزی را پیش بینی می کند :

۱-۹- بدست آوردن کلاس پیش بینی شده

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

# Predict the most likely class
print("Model prediction: {}".format(model_reg.predict_classes(test_data[[1],:])[0]))

# Display the predicted image
plt.imshow(test_images[1], cmap='gray')
plt.title("Ground Truth : {}".format(test_labels[1]))

خروجی:

پیش بینی مدل: عدد ۲

عدد 2 از داده های MNIST
عدد ۲ از داده های MNIST

۲-۹- بدست آوردن احتمالات

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

# Predict the probabilities for each class 
model_reg.predict(test_data[[0],:])

خروجی:

array([[1.29905996e-18, 1.10607663e-14, 1.40711756e-11, 1.03725896e-10, 7.9663463e-19, 1.92765065e-14, 4.65404149e-28, 1.00000000e+00, 6.88956165e-16, 3.75895357e-11]], dtype=float32)

این امتیازات ، احتمال را برای هر کلاس نشان می دهد. می توانیم ببینیم که امتیاز شاخص هشتم تقریباً ۱ است که نشان می دهد شبکه توانسته عدد ۷ را با نمره اطمینان ۱ پیش بینی کند.

۱۰- تمرین

ما از ۲ لایه پنهان و فعالسازی relu استفاده کرده بودیم. حال شما سعی کنید تعداد لایه پنهان و فعالسازی را به tanh یا sigmoid تغییر دهید و ببینید چه تغییراتی در خروجی رخ می دهد. همچنین نسبت dropout را تغییر داده و عملکرد مدل را بررسی کنید.

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

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

منبع Learn OpenCV

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

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

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

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

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

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

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