我主要用pytorch,但是導師和師兄都用TensorFlow,並且許多論文也是用TensorFlow來復現,出於交流和學習需要看了《TensorFlow實戰》的部分章節,以對TensorFlow有基礎的認識,記錄於此。
目錄:
1 TensorFlow基礎
2 TensorFlow和其他深度學習框架的對比
3 TensorFlow第一步
3.2 TensorFlow實現Softmax Regression識別手寫數字
4 TensorFlow實現自編碼器及多層感知機
5 TensorFlow實現卷積神經網絡
6 TensorFlow實現經典卷積神經網絡
6.3 TensorFlow實現Google Inception Net
1 TensorFlow基礎
1.1 TensorFlow概要
- TensorFlow使用數據流式圖規划計算流程,它可以將計算映射到不同的硬件和操作系統平台。
1.2 TensorFlow編程模型簡介
- TensorFlow中的計算可表示為一個有向圖(計算圖),其中每個運算操作為一個節點,每個節點可有任意多個輸入和輸出;
- 在計算圖的邊中流動(flow)的數據被稱為張量(tensor),tensor在計算圖中flow,故命名為TensorFlow;
- Session是用戶使用TensorFlow的交互式接口,可通過Session的Extend方法添加新的節點和邊;
- 反復執行計算圖時一般的tensor不會被持續保留,Variable中的數據tensor會被保存。
2 TensorFlow和其他深度學習框架的對比
3 TensorFlow第一步
3.1 TensorFlow的編譯及安裝
3.2 TensorFlow實現Softmax Regression識別手寫數字
- 載入MNIST數據集
from tensorflow.examples.tutorials.mnist import input_data import tensorflow as tf mnist = input_data.read_data_sets("Mnist_data/", one_hot=True)
- 創建placeholder用以輸入數據
x = tf.placeholder(tf.float32, [None, 784]) y_ = tf.placeholder(tf.float32, [None, 784]) #None代表不限條數的輸入
- 給模型中的weights和biases創建Variable對象,以存儲模型參數(其他的tensor將不保存)
W = tf.Variable(tf.zeros([784, 10])) b = tf.Variable(tf.zeros([10]))
- 構建計算公式
y = softmax(Wx + b)
- 定義Loss,確定優化器
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1])) train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
- 初始化變量,執行計算
tf.global_variables_initializer().run() for i in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) train_step.run({x: batch_xs, y_: batch_ys})
4 TensorFlow實現自編碼器及多層感知機
4.1 自編碼器簡介
- 自編碼器,即可以使用自身的高階特征編碼自己;
- 特征的稀疏表達:使用少量的基本特征組合拼裝得到更高層抽象的特征;
- 為防止簡單的映射,加入幾種限制:1. 讓中間隱含層節點數量小於輸入、輸出節點數量,相當於降維 2. 給輸入數據加入噪聲
4.2 TensorFlow實現自編碼器
- 用到xavier參數初始化方法
def xavier_init(fan_in, fan_out, constant = 1): low = - constant * np.sqrt(6.0 / (fan_in + fan_out)) high = constant * np.sqrt(6.0 / (fan_in + fan_out)) return tf.random_uniform((fan_in, fan_out), minval=;ow, maxval=high, dtype=tf.float32)
- 參數初始化函數_initial_weights(創建一個名為all_weights的字典dict,將w1、b1、w2、b2全部存入其中)
def _initialize_weights(self): all_weights = dict() all_weights['w1'] = tf.Variable(xavier_init(self.n_input, self.n_hidden)) all_weights['b1'] = tf.Variable(tf.zeros([self.n_hidden], dtype=tf.float32)) all_weights['w2'] = tf.Variable(xavier_init([self.n_hidden, self.n_input], dtype=tf.float32)) all_weights['b2'] = tf.Variable(tf.zeros([self.n_input], dtype=tf.float32)) return all_weights
4.3 多層感知機簡介
- Dropout是在訓練時將神經網絡某一層的輸出節點數據隨機丟棄一部分(訓練時小於1,預測時等於1),這等於創造出了很多新的隨機樣本,也算是一種bagging
- Dropout是在訓練時將神經網絡某一層的輸出節點數據隨機丟棄一部分(訓練時小於1,預測時等於1),這等於創造出了很多新的隨機樣本,也算是一種bagging
1. 單側抑制
2. 相對寬闊的興奮邊界
3. 稀疏激活性
4.4 TensorFlow實現多層感知機
- ReLU層的權重可以用截斷正態分布進行初始化,偏置可以用一些小的非零值來初始化以避免dead neuron.Sigmoid層的權重和偏置則都初始化為0即可
- 使用ReLU作為隱藏層,並添加dropout,就實現了多層感知機
5 TensorFlow實現卷積神經網絡
5.1 卷積神經網絡簡介
- CNN最初是為解決圖像識別問題設計,現在也可用於時間序列信號如音頻、文本;
- CNN要點:局部連接、權值共享、池化層降采樣
1. 局部連接和權值共享降低了參數量,減輕過擬合
2. 池化層降采樣進一步降低輸出參數量
- 只要卷積核數量足夠多,能提取出各種方向的邊或各種形態的點,就可以讓采卷積層抽象出有效而豐富的高階特征;
- CNN參數量只與卷積核尺寸和個數有關
5.2 TensorFlow實現簡單的卷積網絡
- 首先載入MNIST數據集
- 這個CNN會有很多權重和偏置需要創建,因此先定義初始化函數以便后續重復使用
def weight_variable(shape): initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial) def bias_variable(shape): initial = tf.constant(0.1, shape=shape) return tf.Variable(initial)
- 卷積層、池化層也需重復使用,這里分別定義創建函數
def conv2d(x, W): return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME') def max_pool_2x2(x): return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
- 定義輸入:x是特征,y_是真實的label
x = tf.placeholder(tf.float32, [None, 784]) y_ = tf.placeholder(tf.float32, [NOne, 10]) x_image = tf.reshape(x, [-1, 28, 28, 1])
- 第一個卷積層
W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32]) h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) h_pool1 = max_pool_2x2(h_conv1)
- 第二個卷積層
W_conv2 = weight_variable([5, 5, 32, 64]) b_conv2 = bias_variable([64]) h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) h_pool2 = max_pool_2x2(h_conv2)
- 第一個全連接層,后接dropout
W_fc1 = weight_variable([7*7*64, 1024]) b_fc1 = bias_variable([1024]) h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) keep_prob = tf.placeholder(tf.float32) h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
- 第二個全連接層,后接softmax
W_fc2 = weight_variable([1024, 10]) b_fc2 = bias_variable([10]) y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
- 定義損失函數cross entropy,優化器使用Adam,學習速率1e-4
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv), reductioin_indices=[1])) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
- inference完成后,開始訓練
tf.global_variables_initializer().run() for i in range(20000): batch = mnist.train.next_batch(50) train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
5.3 TensorFlow實現進階的卷積網絡
- 定義初始化weight函數
1. 這里給weight加一個L2的loss,相當於做了一個L2正則化處理
2. tf.multiply讓L2 loss與wl(weight loss)相乘,即用wl控制L2 loss的大小(tf.mutmul矩陣乘法、tf.multiply點乘)
3. 使用tf.add_to_collection把weight loss同意存到名為“losses"的列表中(tf.get_colletion("name")獲取列表)
def variable_with_weight_loss(shape, stddev, wl): var = tf.Variable(tf.truncated_normal(shape, stddev=stddev)) if wl is not None: weight_loss = tf.multiply(tf.nn.l2_loss(var), wl, name='weight_loss') tf.add_to_collection('losses', weight_loss) return var
- 第一個卷積層
weight1 = variable_with_weight_loss(shape=[5, 5, 3, 64], stddev=5e-2, wl=0.0) kernel1 = tf.nn.conv2d(image_input, weight1, [1, 1, 1, 1], padding='SAME') bias = tf.Variable(tf.constant(0.0, shape[64])) conv1 = tf.nn.relu(tf.nn.bias_add(kernel1, bias1)) pool1 = tf.nn.max_pool(conv1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME') norm1 = tf.nn.lrn(pool1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
- 數據增強(Data Augmentation):對圖片進行翻轉、隨機剪切,制造更多樣本
6 TensorFLow實現經典卷積神經網絡
6.1 TensorFlow實現AlexNet
- 使用ReLu,在較深的網絡中效果好於Sigmoid,解決了Sigmoid在深層網絡中梯度彌散問題
- 使用Dropout,避免模型過擬合
- 提出LRN
- 使用CUDA加速訓練
- 數據增強
def inference(images): parameters = [] with tf.nn.name_scope('conv1') as scope: kernel = tf.Variable(tf.truncated_normal([11, 11, 3, 64], dtype=tf.float32, stddev=1e-1), name='weights') conv = tf.nn.con2d(images, kernel, [1, 4, 4, 1], padding='SAME') biases = tf.Variable(tf.constant(0.0, shape=[64], dtype=tf.float32), trainable=True, name='biases') bias = tf.nn.bias_add(conv, biases) conv1 = tf.nn.relu(bias, name=scope) print_activation(conv1) parameters += [kernel, biases] lrn1 = tf.nn.lrn(conv1, 4, bias=1.0, alpha=0.001/9, beta=0.75, name='lrn1') pool1 = tf.nn.max_pool(lrn1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool1') print_activations(pool1)
6.2 TensorFlow實現VGGNet
- 卷積核都為3x3,池化層都為2x2
- 3個3x3卷積層串聯相當於1個7x7卷積層(感受野相同),參數為后者55%
- 使用數據增強
- vgg16包含很多層,先創建函數conv_op、fc_op和mpool_op
def conv_op(input_op, name, kh, kw, n_out, dh, dw, p): n_in = input_op.get_shape()[-1].value with tf.name_scope(name) as scope: kernel = tf.get_variable(scope+"w", shape=[kh, kw, n_in, n_out], dtype=tf.float32, initializer=tf.contrib.layers.xavier_initializer_conv2d()) conv = tf.nn.conv2d(input_op, kernel, (1, dh, dw, 1), padding='SAME') bias_init_val = tf.constant(0.0, shape=[n_out], dtype=tf.float32) biases = tf.Variable(bias_init_val, trainable=True, name='b') z = tf.nn.bias_add(conv, biases) activation = tf.nn.relu(z, name=scope) p += [kernel, biases] return activation def fc_op(input_op, name, n_out, p): n_in = input_op.get_shape()[-1].value with tf.name_scope(name) as scope: kernel = tf.get_variable(scope+"w", shape=[n_in, n_out], dtype=tf.float32, initializer=tf.contrib.layers.xavier_initializer()) biases = tf.Variable(tf.constant(0.1, shape=[n_out], dtype=tf.float32), name='b') activation = tf.nn.relu_layer(input_op, kernel, biases, name=scope) p += [kernel, biases] return activation def mpool_op(input_op, name, kh, kw, dh, dw): return tf.nn.max_pool(input_op, ksize=[1, kh, kw, 1], strides=[1, dh, dw, 1], padding='SAME', name=name)
- 完成創建函數,開始構建VGG16網絡結構
def inference_op(input_op, keep_prob): p = [] conv1_1 = conv_op(input_op, name="conv1_1", kh=3, kw=3, n_out=64, dh=1, dw=1, p=p) conv1_2 = conv_op(conv1_1, name="conv1_2", kh=3,pool1",w=3, n_out=64, dh=1, dw=1, p=p) pool1 = mpool_op(conv1_2, name="pool1", kh=2, kw=2, dw=2, dh=2) ...
6.3 TensorFlow實現Google Inception Net
- Inception V1去除最后的全連接層,用全局平均池化層代替。(模型訓練更快且減輕了過擬合)
- 1x1卷積可以跨通道組織信息,提高網絡表達能力,且可以對輸出通道升維、降維
- 1x1卷積的性價比很高,用很小的計算量就能增加一層特征變化和非線性化
- 1x1卷積可很自然地把相關性很高的、在同一個空間位置但是不同通道的特征連接在一起
- 靠后的Inception Module中大面積的卷積核占比更多,以捕捉更高階的抽象特征
- 用到輔助分類節點(auxiliary classifiers),在Inception Net中間一層的輸出用作分類,並按一個較小權重(0.3)加到最終分類結果中
- Inception V2提出了Batch Normalization
- 定義inception_v3_arg_scope用來生成網絡中經常用到的函數的默認參數
def inception_v3_arg_scope(weight_decay=0.00004, stddev=0.1, batch_norm_var_collectioin='moving_vars'): batch_norm_params = { 'decay': 0.9997, 'epsilon': 0.001, 'updates_collections': tf.GraphKeys.UPDATE_OPS, 'variables_collections': { 'beta': None, 'gamma': None, 'moving_mean': [batch_norm_var_collectioin], 'moving_variance': [batch_norm_var_collection], } } with slim.arg_scope([slim.conv2d, slim.fully_connected], weights_regularizer=slim.l2_regularizer(weight_decay)): with slim.arg_scope([slim.conv2d], weights_initializer=tf.truncated_normal_initializer(stddev=stddev), activation_fn=tf.nn.relu, normalizer_fn=slim.batch_norm, normalizer_params=batch_norm_params) as sc: return sc
- 定義inception_v3_base用來生成Inception V3網絡的卷積部分
def inception_v3_base(inputs, scope=None): end_points = {} with tf.variable_scope(scope, 'InceptionV3', [inputs]): #前面的卷積層 with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d], stride=1, padding='VALID'): net = slim.conv2d(inputs, 32, [3, 3], stride=2, scope='Conv2d_1a_3x3') net = slim.conv2d(net, 32, [3, 3], scope='Conv2d_2a_3x3') ... #接着是三個Inception #inception 1 with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d], stride=1, padding='SAME'): #inception 1中的第一個inception module with tf.variable_scope('Mixed_5b'): with tf.variable_scope('Branch_0'): branch_0 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1') with tf.variable_scope('Branch_1'): branch_1 = slim.conv2d(net, 48, [1, 1], scope='Conv2d_0a_1x1') branch_1 = slim.conv2d(branch_1, 64, [5, 5], scope='Conv2d_0b_5x5') ...
6.4 TensorFlow實現ResNet
- ResNet的殘差學習單元(Residual Unit)不再學習一個完整的輸出H(x),而是學習殘差H(x)-x.ResNet有很多支線將輸入直接連到后面的層,使得后面的層可以直接學習殘差,這種結構被稱為shortcut
- 兩層的殘差學習單元包含兩個相同輸出通道數的3x3卷積,三層的殘差學習單元則是1x1卷積,3x3卷積,1x1卷積,並且先降維再升維