آموزش کلاسیفیکیشن تصویر با دیتاست MNIST به کمک Keras در پایتون
طبقه بندی ارقام دست نویس به کمک Kersas
اگر مبحث «یادگیری عمیق (Deep Learning)» را با «شبکهی عصبی (Neural Network)» شروع کرده باشید، به این نکته پی بردهاید که یکی از تکنیکهای قدرتمند «یادگیری عمیق»، «شبکههای عصبی کانولوشنال (Convolutional Neural Networks یا CNN یا شبکه عصبی پیچشی)» هستند.
ساختار نهایی CNN، در واقع بسیار شبیه به «شبکههای عصبی منظم (Regular Neural Networks یا RegularNets)» میباشد؛ که در آن، نرونها با وزن و بایاسها وجود دارند. علاوهبر این و همانند شبکههای عصبی منظم، ما از یک تابع ضررر یا زیان (Loss Function)، مثلا: بیشینه هموار یا corrs entropy در مقابل softmax؛ و همچنین از یک بهینهساز (optimizer)، مثلا: adam optimizer؛ در CNNها استفاده میکنیم. بهعلاوه، در CNNها، لایههای Convolutional، لایههای Pooling و لایههای Flatten نیز وجود دارند. CNNها عمدتا جهت طبقهبندی (کلاسیفیکیشن یا Classification) تصویر مورد استفاده قرار میگیرند؛ اگرچه ممکن است در زمینههای کاربردی دیگری از جمله «پردازش زبان طبیعی (Natural Language Processing)» نیز یافت شوند.
چرا شبکههای عصبی کانولوشنال؟
ویژگی اصلی ساختار RegularNets، متصل بودن تمامی نورونها به یکدیگر است. بهعنوان مثال، هنگامی که ما تصاویر 28 x 28 پیکسلی با مقیاس خاکستری در اختیار داشته باشیم، در نهایت، 784 یا (28 x 28 x 1) نورون در یک لایه خواهیم داشت؛ که به نظر قابل کنترل (manageable) میآید. با اینحال، اکثر تصاویر شامل پیکسهای بیشتری بوده و همچنین خاکستری نیستند. لذا با فرض اینکه ما مجموعهای از تصاویر رنگی با ابعاد 4K Ultra HD در اختیار داشته باشیم، در نهایت ما 26,542,080 یا (4096 x 2160 x 3) نورن متفاوتی خواهیم داشت؛ که در لایهی اول به یکدیگر متصل بوده و در واقع قابل کنترل نیست. بنابراین میتوان گفت که RegularNets برای کلاسیفیکیشن تصویر، مقیاسپذیر (scalable) نیست. با این حال و به ویژه وقتی صحبت از تصاویر میشود، بهنظر ارتباط یا رابطه کمی بین دو پیکسل مجزا وجود دارد، مگر آنکه نزدیک یکدیگر باشند. این منجر به ایدهی لایههای Convolutional یا لایههای Pooling میشود.
لایهها در CNN
ما قادر به استفاده از لایههای مختلفی در یک CNN هستیم. با این حال، کانولوشن (convolution)، پولینگ (pooling) و لایههای تماما متصل (fully connect layers)، مهمترین آنها هستند. لذا در ادامه و بهطور مختصر، این لایههای را قبل از پیادهسازیشان معرفی خواهم کرد.
لایههای کانولوشن (Convolution: پیچشی یا حلقوی)
این لایه، اولین لایه بوده و جاییست که ما ویژگیها را از تصاویر موجود در دیتاستهایمان استخراح میکنیم. با توجه به این واقعیت که پیکسلها فقط با پیکسلهای مجاور و نزدیکشان ارتباط دارند، کانولوشن به ما این اجازه را میدهد تا ارتباط بین بخشهای مختلف یک تصویر را حفظ کنیم.
کانولوشن، در اصل، فیلتر کردن تصویر با یک فیلتر پیکسلی کوچکتر است؛ تا اندازهی تصویر، بدون از دست دادن ارتباط بین پیکسلها کاهش یابد. هنگامی که ما با استفاده از یک فیلتر 3×3، با گام 1×1 (یک تغییر پیکسل در هر مرحله)، کانولشن را به تصویر 5×5 اعمال میکنیم. ما در نهایت به یک خروجی 3×3 خواهیم رسید (64٪ کاهش در پیچیدگی).
لایههای Pooling
بههنگام ساخت و ایجاد CNNها، درج لایههای پولینگ بعد از هر لایهی کانولوشن، به منظور کاهش بار محاسباتی (پیچدگی محاسباتی)، حافظه و تعداد پارامترها، مرسوم بوده و در واقع هدف اصلی این لایه، کوچک کردن (subsample) تصویر ورودی است. علاوه بر این، لایههای پولینگ باعث کاهش ریسک overfitting میشوند. همچنین کاهش اندازهی تصویر ورودی باعث میشود تا شبکه عصبی، نسبت به جابجایی تصویر (مستقل از موقعیت) حساسیت کمتری داشته باشد.
اساسا در این لایه و جهت کم کردن میزان پارامترها، ماکسیمم، میانگین و یا مجموع مقادیر درون پیکسلها را به عنوان انداژهای برای پولینگ (Pooling Size) انتخاب میکنیم. پولینگ ماکسیمم (Max Pooling)، یکی از رایج ترین تکنیک های پولینگ، میتواند به صورت زیر نمایش داده شود:
مجموعه ای از لایه های کاملاً متصل
یک شبکهی کاملا متصل، همانند RegularNet است؛ که هر پارامتر به یکدیگر متصل میشود؛ تا رابطهی صحیح و تأثیر هر پارامتر روی برچسبها را تعیین کند.
از آنجایی که پیچیدگی زمانی و مکانی، به لطف کانولوشن و پولینگ کاهش مییابد؛ میتوانیم در پایان، یک شبکهی کاملا متصل ایجاد کنیم تا تصاویر تصاویر خود را طبقهبندی (کلاسیفیکیشن) نماییم. مجموعهای از لایههای کاملا متصل، شبیه به این است:
اکنون که در مورد لایههای منحصر به فردی که از آنها استفاده خواهیم کرد، آگاهی پیدا کردهاید، فکر میکنم وقت آن رسیده است تا مروری از یک شبکههای عصبی کاملا کانولوشنال را با شما به اشتراک بگذاریم.
و حال که شما ایده و تصوری از شبکه عصبی کانولوشنال جهت کلاسیفیکیشن تصاویر دارید، میتوانیم دیتاستهای کلیشهای زیادی را دریافت کنیم؛ مثلا دیتاست MNIST، که مخفف … بوده و یک پایگاه داده یا دیتابیس بزرگی از ارقام دستنویس میباشد؛ که معمولاً برای تعلیم سیستمهای مختلف پردازش تصویر استفاده میشود.
دانلود دیتاست MINIST
دیتاست MNSIT، یکی از متداولترین مجموعه دادههاییست که برای طبقهبندی (کلاسیفیکیشن) تصاویر، استفاده شده و از بسیاری منابع مختلف، قابل دسترسی است. در حقیقت، Tensorflow و Keras نیز این اجازه را به ما میدهند تا دیتاست MNIST را مستقیما از طریق API آنها وارد کرده و دانلود کنیم. بنابراین، من با وارد کردن دو خط کد زیر، مراحل کار را شروع میکنم:
tensorflow و دیتاست MNIST (تحت Kares) وارد پروژه میشوند.
1 2 |
import tensorflow as tf (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data() |
دیتاست MNIST، شامل 60,000 تصویر جهت تعلیم (Training) و 10,000 تصویر تست شده از کارمندان اداره سرشماری و دانش آموزان دبیرستانی آمریکایی میباشد. بنابراین من در خط دوم، این دو گروه تعلیم و تست را از هم جدا کردم. همچنین تصاویر و برچسب تصاویر نیز از یکدیگر جدا شدهاند.
x_train و y_train، حاوی کدهای RGB خاکستری از 0 تا 255 هستند؛ درحالی که y_train و y_test، حاوی برچسبهای 0 تا 9 میباشند؛ و این برچسبها، نشاندهندهی این هستند که تصویر مرتبط با آنها، مربوط به کدام رقم (عدد) میباشد. برای بصری سازی این تصاویر و نمایش دادنشان، میتوان از matplotlib استفاده کرد:
1 2 3 4 5 6 |
import matplotlib.pyplot as plt %matplotlib inline # در صورت استفاده از آی پایتون، این خط را بنویسید image_index = 7777 # شما میتوانید هرچیزی را تا 60,000 تصویر انتخاب کنید print(y_train[image_index]) # برچسب 8 است plt.imshow(x_train[image_index], cmap='Greys') |
وقتی ما کد بالا را اجرا میکنیم، همانطور که در تصویر زیر مشاهده می شود، تجسم یا حالت بصری خاکستری از کدهای RGB را دریافت خواهیم کرد:
همچنین جهت انتقال این دیتاست به کانال آن در شبکه عصبی پیچشی، بایستی ابعاد آن را نیز بدانیم. لذا از صفت shape آرایههای numpy و بهصورت زیر استفاده میکنیم:
1 |
x_train.shape |
شما با چاپ این ویژگی، مقدار (60000, 28, 28) را دریافت خواهید کرد. همانطور که احتمالا حدس زدهاید، عدد 60000، تعداد دادهی موجود در دیتاست تعلیم را نشان میدهد و (28, 28) نیز بیانگر اندازهی تصویر است: 28 در 28 پیکسل.
تغییر ابعاد و نرمالسازی تصاویر
برای آنکه بتوانیم از دیتاست موجود در API پکیج Keras استفاده کنیم، به آرایههای 4 بعدی نامپی نیاز داریم. با این حال و همانطور که در بالا میبینید، آرایههای ما سه بعدی هستند. علاوهبر این، ما بایستی دادههای خود را نرمال کنیم؛ چرا که همواره در مدلهای شبکه عصبی، مورد نیاز است.
ما میتوانیم با تقسیم کدهای RGB به 255 (که حداکثر کد RGB منهای حداقل کد RGB است)، به این هدف گفته شده دست یابیم. این کار، با کد زیر قابل انجام است:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# تغییر شکل آرایه به 4 بعدی تا بتواند با ای پی آی پکیج کراس کار کند x_train = x_train.reshape(x_train.shape[0], 28, 28, 1) x_test = x_test.reshape(x_test.shape[0], 28, 28, 1) input_shape = (28, 28, 1) # ایجاد اطمینان از اینکه نوع مقادیر فلوت میباشند؛ تا بتوانیم بعد از تقسیم، بخش اعشاری را نیز بدست آوریم x_train = x_train.astype('float32') x_test = x_test.astype('float32') # نرمالسازی کدهای آر جی بی، با تقسیم آنها با حداکثر مقدا آر جی بی x_train /= 255 x_test /= 255 print('x_train shape:', x_train.shape) print('Number of images in x_train', x_train.shape[0]) print('Number of images in x_test', x_test.shape[0]) |
ساخت شبکهی عصبی پیچشی
ما مدل خود را با استفاده از API سطح بالای Keras که از TensorFlow یا Theano در وَرای خود استفاده میکند، میسازیم. البته لازم به ذکر است که APIهای سطح بالای مختلفی برای TensorFlow وجود دارند؛ مانند Layers و Keras و Estimators که به ما در ایجاد شبکههای عصبی با دانش سطح بالا کمک میکنند. اما با این حال، همهی آنها، به یک شکل پیادهسازی نمیشوند و ساختار آنها نسبت به یکدیگر متفاوت است.
من از سادهترین API که Keras است، استفاده خواهم کرد. لذا، مدل Sequential را از Keras وارد کرده و لایههای Conv2D، MaxPooling، Flatten، Dropout و Dense را اضافه میکنم. قبلا در مورد لایههای Maxpooling، Conv2D و Dense صحبت کردم. علاوهبراین، لایههای Dropout، با نادیده گرفتن برخی از نورونها در هنگام تعلیم با مشکل overfitting مقابله میکنند؛ در حالی که لایههای Flatten، آرایههای 2بعدی را قبل از ایجاد لایههای تماما متصل، به آرایههای تکبعدی تبدیل میکنند (مثل صاف کردن).
1 2 3 4 5 6 7 8 9 10 11 |
# وارد کردن ماژول های مورد نیاز کراس که حاوی مدل و لایهها هستند from keras.models import Sequential from keras.layers import Dense, Conv2D, Dropout, Flatten, MaxPooling2D # ایجاد یک مدل سکوئنشیال و افزودن لایهها model = Sequential() model.add(Conv2D(28, kernel_size=(3,3), input_shape=input_shape)) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Flatten()) # تبدیل آرایههای دوبعدی به تکبعدی قبل برای لایههای تماما متصل model.add(Dense(128, activation=tf.nn.relu)) model.add(Dropout(0.2)) model.add(Dense(10,activation=tf.nn.softmax)) |
میتوان با هر شمارهای، اولین لایهی Dense را آزمایش یا امتحان کنیم؛ با این حال، آخرین لایهی Dense، بایستی 10 نورون داشته باشد؛ چراکه ما 10 کلاس شماره داریم (0، 1، 2، …، 9). همچنسن میتوانید همیشه با تغییر مقادیر اندازهی کرنل، اندازهی pool، توابع فعالسازی، نرخ dropout و تعداد نورونها در اولین لایهی Dense، آن را آزمایش کنید.
کامپایل و متناسبسازی مدل
با کد بالا، ما یک CNN خالیِ بهینهنشدهای را ایجاد کردیم. (که نه داده داشته و نه عملیات تعلیمی انجام گرفته است!)
اکنون زمان تنظیم یک بهینه ساز با دادن تابع ضرری (لاس یا Loss) که از یک معیارهایی (متریک) استفاده میکند، فرا رسیده است. سپس میتوانیم با استفاده از دادههای تعلیم خود، مدل را متناسب کنیم. از کدهای زیر، برای انجام این وظایف استفاده میکنیم:
1 2 3 4 |
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) model.fit(x=x_train,y=y_train, epochs=10) |
شما میتوانید با تغییر بهینهساز، تابع زیان (لاس)، معیارها و دورهها (epochs)، کامپایل را آزمایش کنید. به هر حال، بهینهساز Adam، معمولا عملکرد بهینهسازهای دیگر را ندارد.
عدد دورهها (epochs)، ممکن است کمی کوچک به نظر برسد. با این حال، شما به دقت 98 تا 99 درصدی خواهید رسید.
از آنجایی که دیتاست MNIST به محاسبات سنگینی نیاز ندارد، میتوانید به راحتی و با تغییر مقدار epchos، شبکه را آزمایش کنید.
ارزیابی مدل
در انتها، میتوانید مدلهای آموزش دیده (تعلیم یافته) را با x_test و y_test بیازمایید. با استفاده از یک خط کد زیر:
1 |
model.evaluate(x_test, y_test) |
نتایج برای 10 دوره و برای چنین مدل سادهای، بسیار خوب است:
ما با چنین مدل پایهای، دقت 98.5 درصدی را بهدست آوردیم. صراحتا، در بسیاری از موارد کلاسیفیکیشن تصویر (به عنوا مثال برای اتومبیلهای خودران)، حتی نمیتوانیم خطای 0.1 درصدی را هم تحمل کنیم؛ چرا که با یک قیاس، این خطا موجب اتفاق دادن 1 تصادف از 1000 مورد میشود. با این حال، برای اولین مدل ما، میگویم نتیجه هنوز هم بسیار خوب است.ما همچنین میتوانیم پیشبینی تکی را با کد زیر انجام دهیم:
1 2 3 4 |
image_index = 4444 plt.imshow(x_test[image_index].reshape(28, 28),cmap='Greys') pred = model.predict(x_test[image_index].reshape(1, 28, 28, 1)) print(pred.argmax()) |
مدل ما، تصویری که در ایندکس 4444 تصاویر تستی وجود دارد را به عنوان «9» کلاسیفای میکند؛ و در پایین، حالت بصری تصویر را مشاهده میکنید:
اگرچه واقعاً دست نویس خوبی برای شماره 9 نیست ، اما مدل ما توانست آن را به عنوان 9 طبقهبندی کند.
تبریک
شما با موفقیت یک شبکه عصبی پیچشی را برای طبقه بندی ارقام نوشته شده دستی با API Keras Tensorflow ایجاد کردید. شما به دقت بیش از 98٪ دست یافتهاید و اکنون میتوانید این مدل را نیز ذخیره کرده و یک برنامهی طبقهبندی رقمی ایجاد کنید! همچنین اگر در مورد نحوهی ذخیرهی مدل خود کنجکاو هستید، میتوانید «مستندان Keras» را مطاطعه کنید.
منابع
- KataKoda
- CS231n Convolutional Neural Networks for Visual Recognition
- MathWorks, Introducing Deep Learning with MATLAB
- Wikipedia, MNIST database
منابع: towardsdatascience
نظرات ثبت شده بدون دیدگاه