CS20si 第7課: TensorFlow中的卷積

第7課: TensorFlow中的卷積

CS20si課程資料和代碼Github地址

沒(méi)有訓(xùn)練的卷積

你有可能已經(jīng)熟悉數(shù)學(xué)或物理中的“卷積”,牛津詞典對(duì)“卷積”在數(shù)學(xué)領(lǐng)域的定義如下:

一個(gè)由兩個(gè)給定的函數(shù)集成的函數(shù),表達(dá)一個(gè)函數(shù)的形狀是如何被另一個(gè)函數(shù)修改的。

這就是卷積在機(jī)器學(xué)習(xí)中的含義,卷積是一個(gè)原始輸入Input被一個(gè)核Kernel(或者叫濾波器Filter/特征圖Feature Map)修改。更多的細(xì)節(jié)參照CS231n課程。

事實(shí)上,我們可以不用訓(xùn)練而直接使用卷積。比如我們可以用一個(gè)3x3的卷積來(lái)模糊一張圖片。

image

在TensorFlow中去做卷積,我們有很多內(nèi)建的層可以使用。你可以輸入2維數(shù)據(jù)做1維卷積,輸入3維數(shù)據(jù)做2維卷積,輸入4維數(shù)據(jù)做3維卷積,最常用的是2維卷積。

tf.nn.conv2d(
    input,
    filter,
    strides,
    padding,
    use_cudnn_on_gpu=True,
    data_format='NHWC',
    dilations=[1, 1, 1, 1],
    name=None
)

Input: Batch size (N) x Height (H) x Width (W) x Channels (C)
Filter: Height x Width x Input Channels x Output Channels
(e.g. [5, 5, 3, 64])
Strides: 4 element 1-D tensor, strides in each direction
(often [1, 1, 1, 1] or [1, 2, 2, 1])
Padding: 'SAME' or 'VALID'
Dilations: The dilation factor. If set to k > 1, there will be k-1 skipped cells between each filter element on that dimension.
Data_format: default to NHWC

這兒還有一些其它的內(nèi)建卷積:

depthwise_conv2d: 單獨(dú)處理每個(gè)channel的數(shù)據(jù)。
separable_conv2d: 一個(gè)depthwise的空間濾波器后面跟一個(gè)逐點(diǎn)濾波器。

作為一個(gè)有趣的練習(xí),你可以在課程的GitHub中的kernes.py文件中看到一些著名的核的值,在07_basic_kernels.py中看到它們的用法。

image

在練習(xí)中,我們硬編碼這些核的值,但是在訓(xùn)練一個(gè)CNN時(shí),我們不知道核的最優(yōu)值而是學(xué)習(xí)出它們。我們會(huì)用老朋友MNIST來(lái)做學(xué)習(xí)核的練習(xí)。

用CNN處理MNIST

我們?cè)?jīng)用含有一個(gè)全連接層的邏輯回歸處理MNIST,結(jié)果是糟糕的?,F(xiàn)在讓我們看看用局部連接的CNN是否會(huì)好一些。

我們將會(huì)用兩個(gè)步長(zhǎng)為1的卷積層,每個(gè)跟隨一個(gè)relu激活和最大池化層maxpool,最后加上兩個(gè)全連接層。

image

卷積層

def conv_relu(inputs, filters, k_size, stride, padding, scope_name):
    with tf.variable_scope(scope_name, reuse=tf.AUTO_REUSE) as scope:
        in_channels = inputs.shape[-1]
        kernel = tf.get_variable('kernel', [k_size, k_size, in_channels, filters], 
                                initializer=tf.truncated_normal_initializer())
        biases = tf.get_variable('biases', [filters],
                            initializer=tf.random_normal_initializer())
        conv = tf.nn.conv2d(inputs, kernel, strides=[1, stride, stride, 1], padding=padding)
    return tf.nn.relu(conv + biases, name=scope.name)

池化層

def maxpool(inputs, ksize, stride, padding='VALID', scope_name='pool'):
    with tf.variable_scope(scope_name, reuse=tf.AUTO_REUSE) as scope:
        pool = tf.nn.max_pool(inputs, 
                            ksize=[1, ksize, ksize, 1], 
                            strides=[1, stride, stride, 1],
                            padding=padding)
    return pool

全連接層

def fully_connected(inputs, out_dim, scope_name='fc'):
    with tf.variable_scope(scope_name, reuse=tf.AUTO_REUSE) as scope:
        in_dim = inputs.shape[-1]
        w = tf.get_variable('weights', [in_dim, out_dim],
                            initializer=tf.truncated_normal_initializer())
        b = tf.get_variable('biases', [out_dim],
                            initializer=tf.constant_initializer(0.0))
        out = tf.matmul(inputs, w) + b
    return out

放在一起

有了上面這些層,我們可以簡(jiǎn)單地建立我們的模型

def inference(self):
        conv1 = conv_relu(inputs=self.img,
                        filters=32,
                        k_size=5,
                        stride=1,
                        padding='SAME',
                        scope_name='conv1')
        pool1 = maxpool(conv1, 2, 2, 'VALID', 'pool1')
        conv2 = conv_relu(inputs=pool1,
                        filters=64,
                        k_size=5,
                        stride=1,
                        padding='SAME',
                        scope_name='conv2')
        pool2 = maxpool(conv2, 2, 2, 'VALID', 'pool2')
        feature_dim = pool2.shape[1] * pool2.shape[2] * pool2.shape[3]
        pool2 = tf.reshape(pool2, [-1, feature_dim])
        fc = tf.nn.relu(fully_connected(pool2, 1024, 'fc'))
        dropout = tf.layers.dropout(fc, self.keep_prob, training=self.training, name='dropout')
        
        self.logits = fully_connected(dropout, self.n_classes, 'logits')

在訓(xùn)練時(shí),需要評(píng)估每個(gè)epoch的準(zhǔn)確率。

def eval(self):
        '''
        Count the number of right predictions in a batch
        '''
        with tf.name_scope('predict'):
            preds = tf.nn.softmax(self.logits)
            correct_preds = tf.equal(tf.argmax(preds, 1), tf.argmax(self.label, 1))
            self.accuracy = tf.reduce_sum(tf.cast(correct_preds, tf.float32))

可以在課程Github的07_convnet_mnist.py中查看完整代碼。

譯者注:這篇略過(guò)了很多和CS231n重復(fù)的東西,CS20si中的CNN相關(guān)課程主要看看Tensorflow的代碼組織,CNN的知識(shí)還是推薦看CS231n系列課程。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容