در مقالۀ حاضر نگاهی به یک مثال از شبکههای عصبی ژرف خواهیم داشت که از لایبرری Keras برای یادگیری و طبقهبندی ارقام دستنویس استفاده میکند.
مسئله این است که تصاویر سیاهوسفید از ارقام دستنویس به اندازۀ 28 پیکسل * 28 پیکسل را به 10 دسته از اعداد 0 تا 9 و با استفاده از زبان برنامهنویسی پایتون طبقهبندی کنیم. به این منظور تعداد 60،000 تصویر برای Train و 10،000 تصویر برای Test شبکه عصبی استفاده خواهیم کرد.
برای شروع، دیتاست MNIST را که یک دیتاست از پیشتعریفشده در لایبرری کراس است، بهصورت مجموعهای از چهار آرایۀ Numpy ایمپورت میکنیم:
import keras
from keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
آرایۀ train_images، مجموعۀ تصاویر ارقام دستنویس و train_labels لیبل اعداد 0 تا 9 را شامل میشود؛ این دو آرایه مجموعۀ آموزشی را تشکیل میدهند و از آنها برای فرآیند Train شبکه استفاده میکنیم. سپس مدل بر روی مجموعه تست، test_images و test_labels تست خواهد شد.
مراحل زیر را طی خواهیم کرد:
- ابتدا شبکۀ عصبی را با دادههای آموزشی، train_images و train_labels آموزش میدهیم.
- سپس شبکه یاد میگیرد که چطور به هر تصویر عدد، لیبل مربوط به آن را اختصاص دهد.
- درنهایت، از شبکه خواهیم خواست برای مجموعۀ تصاویر test_images پیشبینیهایی را انجام دهد و بررسی میکنیم که آیا این پیشبینیها با لیبلهای مجموعه test_labels مطابقت دارند یا خیر؛ به عبارت دیگر تا چه حد مجموعۀ test_images خوب جواب میدهد!
در ادامه شبکه عصبی مدنظر را میسازیم:
from keras import models
from keras import layers
network = models.Sequential()
network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,)))
network.add(layers.Dense(10, activation='softmax'))
یک شبکه Sequential (ترتیبی) که از دو لایۀ اصطلاحاً Dense تشکیل شده است و به صورت fully-connected به هم متصل هستند (تمام نورونهای هر دو شبکه عصبی به هم متصل هستند). لایه دوم یک لایه softmax با 10 خروجی است. بدین معنی که در خروجی یک آرایه دَهتایی از احتمالهایی خواهیم داشت که میزان تعلق آن عدد به یکی از کلاسهای ارقام 0 تا 9 را نشان میدهد.
به منظور آمادهسازی شبکه برای Training سه مورد را برای مرحله «کامپایل» تعریف میکنیم:
network.compile (optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
- Loss Function: مشخص میکند که چگونه شبکه عصبی قادر خواهد بود که بر اساس دادههای آموزشی بهدرستی کار کند و چگونه بتواند خود را در مسیر درست هدایت کند تا فرآیند آموزش بهخوبی انجام شود؛ در این پیادهسازی از تابع خطای categorical_crossentropy استفاده کردیم. این تابع، صحت خروجی شبکه عصبی را میسنجد و فاصلهی آن با جواب درست را نشان میدهد. در ادامه با استفاده از تصاویر و لیبل مربوط به هر تصویر، وزنها و bias شبکه طوری تعیین میشود که این تابع خطا به حداقل برسد.
- Optimizer: مکانیزمی است که از طریق آن شبکۀ عصبی بر اساس دیتای جدید و عملکرد تابع Loss وزنهای خود را آپدیت میکند.
- Metrics: معیارهایی برای نظارت بر فرآیند ترِین و تست هستند؛ در این مسئله ما فقط معیار Accuracy را در نظر گرفتیم که کسری از تصاویر را که بهدرستی طبقهبندی شدهاند، نشان میدهد.
قبل از شروع فرآیند Training، دادههای ما ابتدا باید پیشپردازش شده و مطابق با آنچه که شبکه به عنوان ورودی از ما انتظار دارد تغییر شکل و اندازه دهند؛ ما ورودی شبکه را دوبعدی تعریف کردهایم بنابراین تصاویر آموزشی که قبلاً در آرایهای به شکل (28 و 28 و 60000) از نوع uint8 ذخیره شده بودند بایستی آنها را به یک آرایه از جنس float32 و به شکل (28*28 و 60000) تبدیل کنیم.
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype('float32') / 255
test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype('float32') / 255
همچنین تمام مقادیر بایستی در بازۀ [1 و ۰] قرار گیرند؛ برای این کار از نرمالسازی استفاده میکنیم و از آنجایی که دادههای ما در بازه [255 و 0] قرار دارند و اگر بخواهیم این اعداد را به بازۀ [1 و ۰] نگاشت کنیم، کافی است تا تمام اعداد را بر 255 تقسیم کنیم!
و برای نرمالسازی لیبلهای مجموعۀ آموزش و همچنین تست نیز از تابع to_categorical استفاده میکنیم. به این صورت که ابتدا کل لیبلهای مجموعههای تست و ترین را به آن پاس میدهیم؛ سپس این تابع تمام لیبلها را به آرایههای دَهتایی صفر و یک نگاشت میکند. مثلاً برای لیبل عدد 5، ایندکس مربوط به خانه پنجم از این آرایه برابر یک و باقی خانهها را صفر در نظر میگیرد و به همین ترتیب.
from keras.utils import to_categorical
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)
train_labels
در ادامه با استفاده از متد fit شبکه را آموزش میدهیم؛ بدین معنی که شبکه را روی دادههای ترِین اصطلاحاً فیت میکنیم!
network.fit(train_images, train_labels, epochs=5, batch_size=128)
فرآیند آموزش به اندازه پنج گام ادامه خواهد داشت و در هر گام کل شبکۀ عصبی ترِین میشود. مجموعه آموزشی را به دستههای 128 تایی تقسیمبندی میکنیم؛ بدین معنی که برای هر گام آموزش از 60000 دادۀ ترِین، هر بار به تعداد 128 داده را برداشته و برای آموزش به شبکه میدهیم و در اِپُک بعدی به تعداد 128 دادۀ بعدی را به شبکه میدهیم. در پایان هر اِپُک نیز میانگین Loss رو محاسبه کرده و بر اساس آن تصمیم گرفته میشود که برای اِپُک بعدی وزنهای شبکه به چه شکل آپدیت شوند!
حال که در گام پنجم به دقت 0.989 (یعنی 98.9٪) در دادههای آموزشی دست یافتیم؛ اجازه دهید که مدل را بر روی مجموعه تست نیز ارزیابی کنیم:
test_loss, test_acc = network.evaluate(test_images, test_labels)
print('test_acc:', test_acc)
خواهیم دید که با این شرایط دقت مجموعۀ تست 97.8٪ به دست میآید که کمی پایینتر از دقت مجموعۀ آموزش است. این فاصله بین دقت مجموعه آموزش و مجموعه تست یک مثال از Overfitting است و این واقعیت را نشان میدهد که مدلهای یادگیری ماشینی در اکثر مواقع روی مجموعه آموزشی فیت شده و تمایل کمتری به کار روی دادههای جدید دارند؛ به عبارت دیگر شبکه عصبی ما در مواجهۀ با دادههایی بهغیر از دادههای آموزشی، نمیتواند آنچه یاد گرفته را به دادههای جدید نیز تعمیم دهد و در نتیجه دقتی کمتر را ارائه خواهد داد!