شبکه های عصبی کانوولوشن (Convolution Neural Networks- CNN) در Tensorflow

شبکه عصبی کانوولوشن (Convolution Neural Networks- CNN)
شبکه عصبی کانوولوشن (Convolution Neural Networks- CNN)

در این پست می خواهیم ابتدا مقدمه ای بر شبکه های عصبی کانوولوشن بیان کنیم. در این مقدمه فرض شده است شما با واژه هایی مانند يادگيري ماشين، يادگيري عميق، پردازش تصوير آشنا هستید. سپس برای یک پیاده سازی واقعی، مسئله Classification مجموعه داده آزمایشی MNIST را به زبان پایتون شرح دهیم. برای حل این مسئله از ابزار Tensorflow و امکاناتی که برای پیاده سازی شبکه های CNN بر روی GPU در اختیارمون گذاشته استفاده خواهیم کرد. CNNها از رايجترين روش­هاي يادگيري عميق در حل مسائل يادگيري ماشين مي­باشند و بویژه بطور گسترده در پردازش تصوير و ویدئو بکارگرفته شده­اند. CNN با ارتباطات محلي بين نورون­هاي لايه­هاي مجاور، همبستگي مکاني محلي ورودي را ا ستخراج مي­کند. معماري سه سطحي آن در شکل 1 نشان داده شده است

ساختار کلي شبکه عصبی کانولوشن   [1]
ساختار کلي شبکه عصبی کانولوشن [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 استفاده شده را هم توضیح خواهیم داد.

123456# 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 را بر روی برچسب کلاس ها اعمال میکند.

12345678910111213141516def 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
1234567891011def 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 خود از توابع زیر استفاده می کنیم. بنابراین ابتدا آن ها را شرح می دهیم.

1def weight_variable(shape):

1    """
1    Create a weight variable with appropriate initialization
1    :param name: weight name
1    :param shape: weight shape
1    :return: initialized weight variable
1    """
1    initer = tf.truncated_normal_initializer(stddev=0.01)
1    return tf.get_variable('W', shape=shape, dtype=tf.float32, initializer=initer)
12
def bias_variable(shape):
1    """
1    Create a bias variable with appropriate initialization
1    :param name: bias variable name
1    :param shape: bias variable shape
1    :return: initialized bias variable
1    """
1    initial = tf.constant(0., shape=shape, dtype=tf.float32)
1    return tf.get_variable('b', dtype=tf.float32, initializer=initial)
1
1def conv_layer(x, filter_size, num_filters, stride, phase_train, name):
1    """
1    Create a 2D convolution layer
1    :param x: input from previous layerphase_train
1    :param filter_size: size of each filter
1    :param num_filters: number of filters (or output feature maps)
1    :param stride: filter stride
1    :param name: layer name
1    :return: The output array
1    """
1    with tf.variable_scope(name):
1        num_in_channel = x.get_shape().as_list()[-1]
1        shape = [filter_size, filter_size, num_in_channel, num_filters]
1        W = weight_variable(shape)
1        tf.summary.histogram('weight', W)
1        b = bias_variable(shape=[num_filters])
1        tf.summary.histogram('bias', b)
1        layer = tf.nn.conv2d(x, W, strides=[1, stride, stride, 1], padding="SAME")
1        layer += b
1        bn_layer = tf.layers.batch_normalization(inputs=layer, training=phase_train)
1        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 در اینجا توضیح داده شده است.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950def 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.

نویسنده مطلب: Amin Zare

منبع مطلب

به فکر سرمایه‌گذاری هستی؟

با هر سطحی از دانش در سریع‌ترین زمان با آموزش گام به گام، سرمایه گذاری را تجربه کن. همین الان میتونی با لینک زیر ثبت نام کنی و ۱۰ درصد تخفیف در کارمزد معاملاتی داشته باشی

ثبت نام و دریافت جایزه
ممکن است شما بپسندید
نظر شما درباره این مطلب

آدرس ایمیل شما منتشر نخواهد شد.