第7課: TensorFlow中的卷積
沒有訓(xùn)練的卷積
你有可能已經(jīng)熟悉數(shù)學(xué)或物理中的“卷積”,牛津詞典對“卷積”在數(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的卷積來模糊一張圖片。
在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中看到它們的用法。
在練習(xí)中弄跌,我們硬編碼這些核的值甲喝,但是在訓(xùn)練一個(gè)CNN時(shí),我們不知道核的最優(yōu)值而是學(xué)習(xí)出它們铛只。我們會用老朋友MNIST來做學(xué)習(xí)核的練習(xí)埠胖。
用CNN處理MNIST
我們曾經(jīng)用含有一個(gè)全連接層的邏輯回歸處理MNIST,結(jié)果是糟糕的〈就妫現(xiàn)在讓我們看看用局部連接的CNN是否會好一些直撤。
我們將會用兩個(gè)步長為1的卷積層,每個(gè)跟隨一個(gè)relu激活和最大池化層maxpool蜕着,最后加上兩個(gè)全連接層谋竖。
卷積層
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
放在一起
有了上面這些層,我們可以簡單地建立我們的模型
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í)承匣,需要評估每個(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中查看完整代碼。
譯者注:這篇略過了很多和CS231n重復(fù)的東西韧骗,CS20si中的CNN相關(guān)課程主要看看Tensorflow的代碼組織嘉抒,CNN的知識還是推薦看CS231n系列課程。