斯坦福TensorFlow課CS20第七講筆記:使用TensorFlow實現CNN

原始課程講義地址

原始課程膠片地址

無特殊說明,筆記內容均來自老師提供的講義和膠片

完整帶圖講義戳我

在MNIST上使用TensorFlow

TensorFlow使用tf.nn.conv2d來做二維的卷積操作(此時需要輸入是三維)。不同維度輸入的卷積操作可以參考Runhani的StackOverflow回答。函數簽名為

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

其中

  • input是輸入,默認是四維,每個維度按順序是batch大小(N)、高度(H)、寬度(W)、信道數(C),即shape(input) = (N, H, W, C)(對應於data_format參數值NHWC
  • filter對應CNN的卷積核(也就是前面理論部分所講的濾波器),其維度也是四維,不過是高度 x 寬度 x 輸入信道數 x 輸出信道數
  • stride是長度為4的一維數組,代表在input四個維度上的步長。通常設為[1, 1, 1, 1][1, 2, 2, 1]。一般情況下,stride[0] == stride[-1] == 1,因為不想跳過任何樣本和任何信道
  • padding是補零的方式而非個數,有兩個選項。
    • 如果設置為VALID,那麼當卷積核和步幅的設置使得卷積過程不能完整覆蓋輸入時,捨棄輸入最右側(或最下側)的內容。根據相關StackOverflow問題的講解,考慮最簡單的1維卷積的情況,假設輸入長度為13,卷積核大小為6,步幅為5,那麼第一次卷積操作所「看」的範圍是1-6,第二次是6-11,此時再移動卷積核不能覆蓋剩下的數據,到此為止,輸入的最後兩個元素被丟棄
    • 如果設置為SAME,那麼會對輸入補0,使得輸入可以被卷積核按照設定的步幅完整覆蓋。補0的原則是左邊和右邊補的0盡量一樣——如果要補的0是奇數個0,在右側多補一個
  • dilations是理論講義里提到的膨脹係數。1.4版本里尚未引入這個參數

其它卷積函數,例如depthwise_conv2dseparable_conv2d,可以參考文檔

課程講義中給出了一個使用預定義卷積核做計算的例子,不過這種場景在實際應用中估計很難遇到,大部分卷積核都是訓練得出來的(不要忘記卷積核的本質,它就是一組權重)

因此,為了讓例子更加貼近實際,這裡講解對MNIST數據集使用TF構建CNN的方法。這裡使用的體系結構是兩個(卷積+ReLU+最大池)的組合,然後跟兩個全連接。具體的體系結構如下圖所示

由於很多事情(例如最大池、conv)都要做多次,因此需要設計可復用的代碼,而且需要使用變數域來讓我們可以在不同的層使用名字相同的變數。通常是每層都創建自己的變數域

定義卷積層

具體實現時,通常把conv和隨後的非線性變化ReLU實現在一起

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)

個中維度計算可以參考前面的理論講解

定義池化層

可以直接使用TF中的tf.nn.max_pool來完成

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

構成最終代碼

將上面的各個函數組合起來可以得到完整的代碼。講義里給出的是預測的圖,訓練的圖應該是類似的,只不過加了定義loss和優化器的部分(然而GitHub上的代碼並沒有像上面一樣良好地組織)

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)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))

tf.layers

前面的各層定義還是有些麻煩,實際上,TensorFlow提供了一種更簡單的手段,就是直接使用在tf.layers里定義的各種層

conv1 = tf.layers.conv2d(inputs=self.img, filters=32, kernel_size=[5, 5], padding=SAME, activation=tf.nn.relu, name=conv1)pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2, name=pool1)fc = tf.layers.dense(pool2, 1024, activation=tf.nn.relu, name=fc)

注意在使用tf.layers.dropout做dropout時,需要指明現在是在訓練還是預測。回憶dropout的核心思想,其僅在訓練時會隨機丟棄一部分神經元,而預測時不會

dropout = tf.layers.dropout(fc, self.keep_prob, training=self.training, name=dropout)

推薦閱讀:

TensorFlow 到底慢在哪裡?
手把手教你用TensorFlow實現看圖說話|教程+代碼
cs20si: tensorflow for research學習筆記4
TensorFlow官方教程翻譯:導入數據
TF Boys (TensorFlow Boys ) 養成記(二): TensorFlow 數據讀取

TAG:卷積神經網路CNN | TensorFlow |