شبکه های عصبی کانوولوشن (Convolution Neural Networks- CNN) در Tensorflow
در این پست می خواهیم ابتدا مقدمه ای بر شبکه های عصبی کانوولوشن بیان کنیم. در این مقدمه فرض شده است شما با واژه هایی مانند يادگيري ماشين، يادگيري عميق، پردازش تصوير آشنا هستید. سپس برای یک پیاده سازی واقعی، مسئله Classification مجموعه داده آزمایشی MNIST را به زبان پایتون شرح دهیم. برای حل این مسئله از ابزار Tensorflow و امکاناتی که برای پیاده سازی شبکه های CNN بر روی GPU در اختیارمون گذاشته استفاده خواهیم کرد. CNNها از رايجترين روشهاي يادگيري عميق در حل مسائل يادگيري ماشين ميباشند و بویژه بطور گسترده در پردازش تصوير و ویدئو بکارگرفته شدهاند. CNN با ارتباطات محلي بين نورونهاي لايههاي مجاور، همبستگي مکاني محلي ورودي را ا ستخراج ميکند. معماري سه سطحي آن در شکل 1 نشان داده شده است
در سطح اول لايه کانولوشن قرار دارد که با استفاده از kernel های متنوع ميتواند ويژگيها جديدي را از تصوير استخراج کند. به دنبال آن عمليات Max pooling انجام ميشود که وظيفه کاهش ابعاد و تعداد پارامترهاي شبکه را انجام ميدهد. خروجي اين لايه بعد از تبديل به بردار يک بعدي به لايه شبکه اتصال کامل ارسال ميشود. در اين لايه از الگوريتمها رايج شبکههاي عصبي استفاده ميشود. بلاک convolution + max pooling که به عنوان لایهconvolution آن را میشناسیم می تواند به دفعات تکرار شود و شبکه ای عمیق تر ساخته شود. تعداد لایه های Fully connected نیز توسط کاربر تعیین می شود.
در سطح اول لايه کانولوشن قرار دارد که با استفاده از kernel های متنوع ميتواند ويژگيها جديدي را از تصوير استخراج کند. به دنبال آن عمليات Max pooling انجام ميشود که وظيفه کاهش ابعاد و تعداد پارامترهاي شبکه را انجام ميدهد. خروجي اين لايه بعد از تبديل به بردار يک بعدي به لايه شبکه اتصال کامل ارسال ميشود. در اين لايه از الگوريتمها رايج شبکههاي عصبي استفاده ميشود. بلاک convolution + max pooling که به عنوان لایهconvolution آن را میشناسیم می تواند به دفعات تکرار شود و شبکه ای عمیق تر ساخته شود. تعداد لایه های Fully connected نیز توسط کاربر تعیین می شود.
شبکه های عصبی کانوولوشن در کاربردهاي مختلف پردازش تصوير مانند طبقهبندي تصاوير، تشخيص اشياء و بازيابي محتوايي تصاوير کارايي خود را نشان داده است. در حل مسئله طبقهبندي تصاوير براي مجموعه دادههاي آزمايشي مانند Imagenet [2]، CIFAR [3] و MINST [4] به نتايج خوبي دست يافته است. در مسئله تشخيص اشياء نيز به عنوان مثال براي مجموعهPASCAL VOC به دقتهاي بالایي رسيده است [5]. در بازيابي محتوايي تصاوير نيز به کارايي خوبي نسبت به روشهاي کلاسيکي مانند ويژگيهاي رنگ، شکل و بافت دست يافته است [6], [7].
تعداد بسيار زياد پارامترهاي قابليادگيري در CNN يکي از چالشهاي آن ميباشد. امروزه پيادهسازي محاسباتي آن بر روي GPU توسط چارچوبهايي متن بازي مانند Tensorflow [8] و CNTK [9] که توسعهدهنگان قوي از آن حمايت ميکنند استفاده گسترده از CNN را امکانپذير نموده است و آن را به ابزار کاربردي و عملي در مطالعات پردازش تصوير و ويدئو تبديل کرده است.
بررسی یک کد واقعی بهترین روش برای تسلط بر برنامه نویسی و مفاهیم CNN می باشد. به این منظور من کد Classificationمجموعه داده آزمایشی MNIST به زبان پایتون و توسط Tensorflow را انتخاب کردم که در سایت ها و کتاب ها مختلف آموزشی شرح داده شده است. من چندین نوع از این کدها را بررسی کردم اما ساختار کد موجود در https://easy-tensorflow.com/tf-tutorials/convolutional-neural-nets-cnns/cnn1 را از همه بیشتر پسندیدم و می خواهم به بررسی آن بپردازم و آن را تکمیل کنم. البته لازم است شما کمی هم پایتون بلد باشید. ضمناً توابع Tensorflow استفاده شده را هم توضیح خواهیم داد.
# https://easy-tensorflow.com/tf-tutorials/convolutional-neural-nets-cnns/cnn1
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import input_data
خب برای شروع کار این چندتا import را اول برنامه قرار می دهیم. برای راحتی خواندن و باز کردن و کار کردن با دیتاست MINST کتابخانه input_data در اختیار شما قرار گرفته است. input_data را می توانید از اینجا دانلود و به پروژه تون اضافه کنید.
توابع load_data و reformat برای خواندن داده ها از دیتاست MNIST و تغییر شکل آن برای اعمال به ورودی شبکه CNN می باشند. اگر دیتاست MNIST را تاکنون دانلود نکرده اید تابع read_data_sets اینکار را برای شما انجام میدهد. این تابع در اولین بار پوشه "MNIST_data" را ایجاد می کند و داده ها را با اسکریپت پایتون دانلود می کند (حجم آن حدود 11 مگابایت است) البته به شرطی که اتصال اینترنت برقرار باشد و سایت 'http://yann.lecun.com/exdb/mnist/'بسته نباشد (تا این لحظه بسته نیست). در دفعه های بعدی داده ها در پوشه ذکر شده وجود دارند و دیگر عملیات دانلود انجام نمی شود. ورودی one_hot در این تابع کدینگ one_hot را بر روی برچسب کلاس ها اعمال میکند.
def load_data(mode="train"):
"""
Function to (download and) load the MNIST data
:param mode: train or test
:return: images and the corresponding labels
"""
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
if mode == "train":
x_train, lbl_train, x_valid, lbl_valid = mnist.train.images, mnist.train.labels,mnist.validation.images, mnist.validation.labels
x_train, _ = reformat(x_train, lbl_train)
x_valid, _ = reformat(x_valid, lbl_valid)
return x_train, lbl_train, x_valid, lbl_valid
elif mode == "test":
x_test, lbl_test = mnist.test.images, mnist.test.labels
x_test, _ = reformat(x_test, lbl_test)
return x_test, lbl_test
def reformat(x, lbl):
"""
Reformats the data to the format acceptable for convolutional layers
:param x: input array
:param lbl: corresponding labels
:return: reshaped input and labels
"""
img_size, num_ch, num_class = int(np.sqrt(x.shape[-1])), 1, len(np.unique(np.argmax(lbl, 1)))
dataset = x.reshape((-1, img_size, img_size, num_ch)).astype(np.float32)
labels = (np.arange(num_class) == lbl[:, None]).astype(np.float32)
return dataset, labels
این دیتا ست مجموعه تصاویر دستنویس 0 تا 9 به ابعاد 28 در 28 می باشد و از 55000 تصویر train، 5000 تصویر validation و 10000 تصویر تست تشکیل شده است. در داده های موجود، هر تصویر 28*28 به صورت یک آرایه یک بعدی به طول 784 ذخیره شده است. بنابراین لازم است توسط تابع reformat به صورت تنسور (-1, 28, 28, 1) تغییر شکل داده شود. خروجی labelsتابع reformatزمانی استفاده می شود که کدینگ one_hotمقدار Falseداشته باشد و در ادامه از این خروجی استفاده نمی کنیم.
فرمت ورودی یا همان data format در تابع covolution دو بعدی (conv2d) در تنسورفلو می تواند بر اساس فرمت های NHWC و NCHWباشد. N به معنی تعداد تنسورها ، H به معنی ارتفاع ، W به معنی پهنا و Cنشان دهنده تعداد کانال تنسورهای وروردی است. در صورت انتخاب NHWCتنسور ورودی باید به صورت [batch, height, width, channels]باشد که حالت پیش فرض تابع covolution دوبعدی می باشد. بعد از فرخوانی تابع reformat در تابع load_dataداده های x_train به شکل (55000, 28, 28, 1) در می آیند که اماده اعمال به تابع conv2d است. بدلیل اینکه تصاویر این دیتاست graylevel می باشند تعداد کانال ورودی، یک در نظر گرفته می شود.
برای تعریف شبکه CNN خود از توابع زیر استفاده می کنیم. بنابراین ابتدا آن ها را شرح می دهیم.
def weight_variable(shape):
"""
Create a weight variable with appropriate initialization
:param name: weight name
:param shape: weight shape
:return: initialized weight variable
"""
initer = tf.truncated_normal_initializer(stddev=0.01)
return tf.get_variable('W', shape=shape, dtype=tf.float32, initializer=initer)
def bias_variable(shape):
"""
Create a bias variable with appropriate initialization
:param name: bias variable name
:param shape: bias variable shape
:return: initialized bias variable
"""
initial = tf.constant(0., shape=shape, dtype=tf.float32)
return tf.get_variable('b', dtype=tf.float32, initializer=initial)
def conv_layer(x, filter_size, num_filters, stride, phase_train, name):
"""
Create a 2D convolution layer
:param x: input from previous layerphase_train
:param filter_size: size of each filter
:param num_filters: number of filters (or output feature maps)
:param stride: filter stride
:param name: layer name
:return: The output array
"""
with tf.variable_scope(name):
num_in_channel = x.get_shape().as_list()[-1]
shape = [filter_size, filter_size, num_in_channel, num_filters]
W = weight_variable(shape)
tf.summary.histogram('weight', W)
b = bias_variable(shape=[num_filters])
tf.summary.histogram('bias', b)
layer = tf.nn.conv2d(x, W, strides=[1, stride, stride, 1], padding="SAME")
layer += b
bn_layer = tf.layers.batch_normalization(inputs=layer, training=phase_train)
return tf.nn.relu(bn_layer)
تابع weight_variable(shape) : ماتریس وزن W را با مقداردهی تصادفی تعریف می کند. tf.get_variable در صورت عدم وجود متغیر W در scope خود، آن را تعریف می کند و در صورت وجود آن را بر میگرداند.
تابع bias_variable(shape): : ماتریس بایاس b را با مقداردهی تصادفی تعریف می کند.
تابع conv_layer(x, filter_size, num_filters, stride, phase_train, name) : عملیات conv2d با فراخوانی این تابع انجام می شود. پارامترهای این تابع به ترتیب: تنسور تصاویر ورودی، اندازه فیلتر کانولوشن یا همان اندازه ماتریس وزن W (یا همان کرنل کانولوشن)، گام کانولوشن، تعیین مرحله train یا تست و نام نود گراف تنسورفلو می باشد.
تنسور shape در این تابع ابعاد ماتریس وزن W (یا همان کرنل کانولوشن) را تعریف میکند. استاندار تنسورفلو برای تعریف آن به صورت [filter_height, filter_width, in_channels, out_channels] می باشد. یعنی بعد از عملیات کانولوشن تعداد کانال های خروجی out_channels می باشد. بعد از تعریف W و ماتریس b یا همان بایاس تابع conv2d را برای اجرای عملیات کانولوشن فراخوانی می کنیم. پارامتر stride، گام جابه جایی کرنل را در راستای افقی و عمودی را تعیین می کند. اگر data formatبرابر با NHWCباشد stride = [1, vertical stride, horizontal stride, 1] تعیین می شود. پارامتر padding می تواند مقادیر SAME یا VALID را بگیرد. مقدار SAME با اعمال zero paddingابعاد تصویر ورودی را بعد از عملیات کانولوشن تغییر نمی دهد. اما مقدار VALIDباعث down sample شدن تصویر ورودی می شود.
عملکرد تابع batch_normalization در اینجا توضیح داده شده است.
def max_pool(x, ksize, stride, name):
"""
Create a max pooling layer
:param x: input to max-pooling layer
:param ksize: size of the max-pooling filter
:param stride: stride of the max-pooling filter
:param name: layer name
:return: The output array
"""
return tf.nn.max_pool(x,
ksize=[1, ksize, ksize, 1],
strides=[1, stride, stride, 1],
padding="SAME",
name=name)
def flatten_layer(layer):
"""
Flattens the output of the convolutional layer to be fed into fully-connected layer
:param layer: input array
:return: flattened array
"""
with tf.variable_scope('Flatten_layer'):
layer_shape = layer.get_shape()
num_features = layer_shape[1:4].num_elements()
layer_flat = tf.reshape(layer, [-1, num_features])
return layer_flat
def fc_layer(x, num_units, name, use_relu=True):
"""
Create a fully-connected layer
:param x: input from previous layer
:param num_units: number of hidden units in the fully-connected layer
:param name: layer name
:param use_relu: boolean to add ReLU non-linearity (or not)
:return: The output array
"""
with tf.variable_scope(name):
in_dim = x.get_shape()[1]
W = weight_variable(shape=[in_dim, num_units])
tf.summary.histogram('weight', W)
b = bias_variable(shape=[num_units])
tf.summary.histogram('bias', b)
drop_x = tf.cond(phase_train, lambda: tf.nn.dropout(x, keep_prob=keep_prob), lambda: x) # tf.cond or by keep_prob=1
layer = tf.matmul(drop_x, W)
layer += b
if use_relu:
layer = tf.nn.relu(layer)
return layer
تابع max_pool(x, ksize, stride, name) : پارامترهای تابع max_poolشبیه پارامترهای تابع conv2d می باشد. با تغییر strideضریب downsampling را تنظیم میکنیم.
تابع flatten_layer(layer) : برای اتصال ویژگی های Spatialاستخراج شده توسط لایه های کانولوشن به لایه fully connectedلازم است ویژگی های Spatial بصورت خطی تغییر شکل یابند و به لایه fully connectedمتصل شوند. این عملیات توسط flatten_layerانجام می شود.
تابع fc_layer(x, num_units, name, use_relu=True) : برای تعریف لایه fully connected از آن استفاده می شود. عملیات dropout قبلا در اینجا توضیح داده شده است.
[1] Y. Guo, Y. Liu, A. Oerlemans, S. Lao, S. Wu, and M. S. Lew, “Deep learning for visual understanding: A review,” Neurocomputing, vol. 187, pp. 27–48, 2016.
[2] A. Krizhevsky, I. Sutskever, and G. E. Hinton, “ImageNet Classification with Deep Convolutional Neural Networks,” in Advances In Neural Information Processing Systems, 2012, pp. 1–9.
[3] K. He, X. Zhang, S. Ren, and J. Sun, “Deep Residual Learning for Image Recognition,” in 2016 IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 2016, pp. 770–778.
[4] L. Wan, M. Zeiler, S. Zhang, Y. LeCun, and R. Fergus, “Regularization of neural networks using dropconnect,” Icml, no. 1, pp. 109–111, 2013.
[5] X. Wang, L. Zhang, L. Lin, Z. Liang, and W. Zuo, “Deep joint task learning for generic object extraction,” in Advances in Neural Information Processing Systems, 2014, pp. 523–531.
[6] Y. Liu, Y. Guo, S. Wu, and M. S. Lew, “Deepindex for accurate and efficient image retrieval,” in Proceedings of the 5th ACM on International Conference on Multimedia Retrieval, 2015, pp. 43–50.
[7] J. Wan et al., “Deep learning for content-based image retrieval: A comprehensive study,” in Proceedings of the 22nd ACM international conference on Multimedia, 2014, pp. 157–166.
[8] Martín Abadi, Ashish Agarwal, Paul Barham, Eugene Brevdo et al., “TensorFlow: Large-Scale Machine Learning on Heterogeneous Systems.” 2015.
[9] D. Yu et al., “An Introduction to Computational Networks and the Computational Network Toolkit,” Microsoft Research, Oct. 2014.
مطلبی دیگر از این انتشارات
بهترین زبان برنامه نویسی برای طراحی سایت چیست؟
مطلبی دیگر از این انتشارات
شبکه همتا به همتا (Peer-to-Peer Network) چیست؟
مطلبی دیگر از این انتشارات
مفاهیم پایه جاوا اسکریپت - متغیرها و ثابت ها