一、手寫體分類
1. 數據集
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' os.environ["CUDA_VISIBLE_DEVICES"] = "0" config = tf.ConfigProto(allow_soft_placement = True) gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction = 0.33) config.gpu_options.allow_growth = True max_steps = 20 # 最大迭代次數 learning_rate = 0.001 # 學習率 dropout = 0.9 # dropout時隨機保留神經元的比例 data_dir = '.\MNIST_DATA' # 樣本數據存儲的路徑 log_dir = 'E:\MNIST_LOG' # 輸出日志保存的路徑 # 獲取數據集,並采用采用one_hot熱編碼 mnist = input_data.read_data_sets(data_dir,one_hot = True) # mnist=input_data.read_data_sets('MNIST_data',one_hot=True) '''下載數據是直接調用了tensorflow提供的函數read_data_sets,輸入兩個參數, 第一個是下載到數據存儲的路徑,第二個one_hot表示是否要將類別標簽進行獨熱編碼。 它首先回去找制定目錄下有沒有這個數據文件,沒有的話才去下載,有的話就直接讀取。 所以第一次執行這個命令,速度會比較慢。''' # sess = tf.InteractiveSession(config = config)
下面的圖是通過調用 tf.summary.image('input', image_shaped_input, 10)得到的,具體見2.初始化參數
2. 初始化參數
with tf.name_scope('input'): x = tf.placeholder(tf.float32, [None, 784], name='x-input') y_ = tf.placeholder(tf.float32, [None, 10], name='y-input') # 保存圖像信息 with tf.name_scope('input_reshape'): image_shaped_input = tf.reshape(x, [-1, 28, 28, 1]) tf.summary.image('input', image_shaped_input, 10) # 初始化權重參數 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 variable_summaries(var): with tf.name_scope('summaries'): # 計算參數的均值,並使用tf.summary.scaler記錄 mean = tf.reduce_mean(var) tf.summary.scalar('mean', mean) # 計算參數的標准差 with tf.name_scope('stddev'): stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean))) # 使用tf.summary.scaler記錄下標准差,最大值,最小值 tf.summary.scalar('stddev', stddev) tf.summary.scalar('max', tf.reduce_max(var)) tf.summary.scalar('min', tf.reduce_min(var)) # 用直方圖記錄參數的分布 tf.summary.histogram('histogram', var)
第一層的weight和biases的變化情況
第二層的weight和biases的變化情況
3. 構建神經網絡
下面是單層神經網絡模型
# 構建神經網絡 def nn_layer(input_tensor, input_dim, output_dim, layer_name, act=tf.nn.relu): # 設置命名空間 with tf.name_scope(layer_name): # 調用之前的方法初始化權重w,並且調用參數信息的記錄方法,記錄w的信息 with tf.name_scope('weights'): weights = weight_variable([input_dim, output_dim]) variable_summaries(weights) # 調用之前的方法初始化權重b,並且調用參數信息的記錄方法,記錄b的信息 with tf.name_scope('biases'): biases = bias_variable([output_dim]) variable_summaries(biases) # 執行wx+b的線性計算,並且用直方圖記錄下來 with tf.name_scope('linear_compute'): preactivate = tf.matmul(input_tensor, weights) + biases tf.summary.histogram('linear', preactivate) # 將線性輸出經過激勵函數,並將輸出也用直方圖記錄下來 activations = act(preactivate, name='activation') tf.summary.histogram('activations', activations) # 返回激勵層的最終輸出 return activations
下面要建雙層神經網絡,第一層加dropout
hidden1 = nn_layer(x, 784, 500, 'layer1')
# 創建dropout層
with tf.name_scope('dropout'):
keep_prob = tf.placeholder(tf.float32)
tf.summary.scalar('dropout_keep_probability', keep_prob)
dropped = tf.nn.dropout(hidden1, keep_prob)
y = nn_layer(dropped, 500, 10, 'layer2', act=tf.identity)
模型圖如下:
dropout變化如下:
第一層和第二層的weights、bias、未激活前,激活后的值分布如下:
tf.summary.histogram接受任意大小和形狀的張量,並將該張量壓縮成一個由許多分箱組成的直方圖數據結構,這些分箱有各種寬度和計數。例如,假設我們要將數字 [0.5, 1.1, 1.3, 2.2, 2.9, 2.99]
整理到不同的分箱中,我們可以創建三個分箱: * 一個分箱包含 0 到 1 之間的所有數字(會包含一個元素:0.5), * 一個分箱包含 1 到 2 之間的所有數字(會包含兩個元素:1.1 和 1.3), * 一個分箱包含 2 到 3 之間的所有數字(會包含三個元素:2.2、2.9 和 2.99)。TensorFlow 使用類似的方法創建分箱,但與我們的示例不同,它不創建整數分箱。對於大型稀疏數據集,可能會導致數千個分箱。相反,這些分箱呈指數分布,許多分箱接近 0,有較少的分箱的數值較大。 然而,將指數分布的分箱可視化是非常艱難的。如果將高度用於為計數編碼,那么即使元素數量相同,較寬的分箱所占的空間也越大。反過來推理,如果用面積為計數編碼,則使高度無法比較。因此,直方圖會將數據重新采樣並分配到統一的分箱。很不幸,在某些情況下,這可能會造成假象。
直方圖可視化工具中的每個切片顯示單個直方圖。切片是按步驟整理的;較早的切片(如,步驟 0)位於較“靠后”的位置,顏色也較深,而較晚的切片則靠近前景,顏色也較淺。右側的 y 軸顯示步驟編號。
舉例:下圖表示時間步驟76,對應的直方圖的分箱位於0.0695附近,分箱中有712個元素
切換historm到覆蓋模式
信息中心左側有一個控件,可以將直方圖模式從“偏移”切換到“覆蓋”:在“偏移”模式下,可視化旋轉 45 度,以便各個直方圖切片不再按時間展開,而是全部繪制在相同的 y 軸上。
現在,每個切片都是圖表上的一條單獨線條,y 軸顯示的是每個分箱內的項目數。顏色較深的線條表示較早的步,而顏色較淺的線條表示較晚的步。同樣,可以將鼠標懸停在圖表上以查看其他一些信息。
4. 損失函數+優化器
# 創建損失函數 with tf.name_scope('loss'): # 計算交叉熵損失(每個樣本都會有一個損失) diff = tf.nn.softmax_cross_entropy_with_logits_v2(labels=y_, logits=y) with tf.name_scope('total'): # 計算所有樣本交叉熵損失的均值 cross_entropy = tf.reduce_mean(diff) tf.summary.scalar('loss', cross_entropy) # 使用AdamOptimizer優化器訓練模型,最小化交叉熵損失 with tf.name_scope('train'): train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy) # 計算准確率 with tf.name_scope('accuracy'): with tf.name_scope('correct_prediction'): # 分別將預測和真實的標簽中取出最大值的索引,弱相同則返回1(true),不同則返回0(false) correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1)) with tf.name_scope('accuracy'): # 求均值即為准確率 accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) tf.summary.scalar('accuracy', accuracy)
5. 訓練
sess = tf.Session() #summaries合並 merged = tf.summary.merge_all() # 寫到指定的磁盤路徑中 train_writer = tf.summary.FileWriter(log_dir + '/train', sess.graph) test_writer = tf.summary.FileWriter(log_dir + '/test',sess.graph) #運行初始化所有變量 # global_variables_initializer().run() sess.run(tf.global_variables_initializer()) def feed_dict(train): """Make a TensorFlow feed_dict: maps data onto Tensor placeholders.""" if train: xs, ys = mnist.train.next_batch(10) k = dropout else: xs, ys = mnist.test.images[:100], mnist.test.labels[:100] k = 1.0 return {x: xs, y_: ys, keep_prob: k} for i in range(100): if i % 10 == 0: #記錄測試集的summary與accuracy summary, acc = sess.run([merged, accuracy], feed_dict=feed_dict(False)) test_writer.add_summary(summary, i) print('Accuracy at step %s: %s' % (i, acc)) else: # 記錄訓練集的summary summary, _ = sess.run([merged, train_step], feed_dict=feed_dict(True)) train_writer.add_summary(summary, i) train_writer.close() test_writer.close()
准確率:
損失函數:
參考文獻:
【1】莫煩Python