代碼已經發布到了github:https://github.com/roadwide/AI-Homework
如果幫到你了,希望給個star鼓勵一下
1 BP神經網絡
1.1算法介紹
反向傳播(英語:Backpropagation,縮寫為BP)是"誤差反向傳播"的簡稱,是一種與最優化方法(如梯度下降法)結合使用的,用來訓練人工神經網絡的常見方法。該方法對網絡中所有權重計算損失函數的梯度。這個梯度會反饋給最優化方法,用來更新權值以最小化損失函數。反向傳播要求有對每個輸入值想得到的已知輸出,來計算損失函數梯度。因此,它通常被認為是一種監督式學習方法。反向傳播要求人工神經元(或"節點")的激勵函數可微。
下面舉一個簡單的例子來介紹什么是梯度下降法以及如何訓練神經網絡。
假設有這樣一個函數(ε是噪聲)
但我們不知道參數的值,也就是w和b的值,但是我們直到很多(x,y)。那么我們可以通過這些值來預測原函數。
構造如下的損失函數
其中的x和y都是真實值,而w和b是我們要預測的值。我們預測的w和b應該使得損失函數越小越好,這點不難看出,損失函數越小證明我們預測出來的函數越接近真實情況。
假設我們得到的loss函數圖像如上圖所示,那么我們得目的就是找到一組w和b使得loss函數值處於一個極小值。
約定w和b按照上面得式子更新自己的值。其中w和b是原來的值,w'和b'是新值,lr是learning rate的縮寫,直觀理解就是橫坐標移動的程度。根據高等數學的相關知識,我們知道函數梯度的方向指向極大值,所以上面式子用減號,也就達到了梯度下降的目的。梯度下降可以使得loss函數處於極小值。當然處於極小值不一定是處於最小值,可能會造成局部最優解,但這些問題超出了本篇實驗報告的討論范圍,所以不作考慮。
經過若干次的更新w和b,我們就會找到一個較小的loss值,也就是說我們找到的w和b就很接近真實值了。
梯度下降是一種常用的優化loss函數的方法,還有其他的方法也可以對loss函數進行優化,例如隨機梯度下降、Adagrad、Adam等。
下面介紹應用BP算法完成手寫體識別問題。MNIST數據集的每一張圖片是一個28x28的矩陣,每個元素是其位置的灰度信息。
我們首先將二維的矩陣打平成一維的,也就是說變成一個784x1的矩陣。上面介紹的簡單的例子是單個數字,這里成了矩陣,所以我們的參數也應該是矩陣。
假設我們先將784x1的矩陣乘以一個512x784的矩陣參數,將得到一個512x1的矩陣
再將512x1的矩陣乘以一個256x512的矩陣參數,將得到一個256x1的矩陣
再將256x1的矩陣乘以一個10x256的矩陣參數,將得到一個10x1的矩陣
經過上述操作,我們將一個784x1的矩陣轉換成了一個10x1的矩陣。為什么最后要轉換成10個元素的矩陣?因為我們的手寫體識別問題中有10個數字,最后我們得到的矩陣的每個元素代表的是可能是相應數字的概率。根據得到的概率和實際情況來構造損失函數,再利用梯度下降的方法來更新參數。
上述的好幾步的矩陣轉換操作實際上就是神經網絡中的層,最后一步的10個元素屬於輸出層,第一次的784x1是輸入層,中間的則是隱藏層。
1.2實驗代碼
#下載、導入數據用到的函數包 import input_data #tensorflow 2.x沒有placeholder所以要用1.x的API import tensorflow.compat.v1 as tf tf.disable_v2_behavior() #讀取數據 mnist = input_data.read_data_sets("MNIST_data", one_hot=True) #不是一個特定的值,而是一個占位符placeholder # 我們在TensorFlow運行計算時輸入這個值。我們希望能夠輸入任意數量的MNIST圖像,每一張圖展平成784維的向量。 x = tf.placeholder("float", [None, 784]) #W的維度是[784,10],因為我們想要用784維的圖片向量乘以它以得到一個10維的證據值向量 W = tf.Variable(tf.zeros([784,10])) #b的形狀是[10],所以我們可以直接把它加到輸出上面 b = tf.Variable(tf.zeros([10])) #softmax模型 y = tf.nn.softmax(tf.matmul(x,W) + b) #為了計算交叉熵,我們首先需要添加一個新的占位符用於輸入正確值 y_ = tf.placeholder("float", [None,10]) #計算交叉熵 cross_entropy = -tf.reduce_sum(y_*tf.log(y)) #使用反向傳播算法(backpropagation algorithm)進行訓練 train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy) #初始化tensorflow init = tf.initialize_all_variables() sess = tf.Session() sess.run(init) #讓模型循環訓練1000次 for i in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys}) correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
1.3實驗結果
可以看出經過1000次的訓練,模型識別手寫體的准確度達到了0.9166
1.4實驗總結
BP神經網絡的原理並不難,可以自行手動實現,但使用TensorFlow不僅方便,而且有更高的效率。
Python的數值計算效率並不高,要實現高效的數值運算一般會使用NumPy這樣的庫,將運算放到外部其他語言封裝好的程序中,以此來完成高效運算。但是從外部計算切換回Python的每一個操作,仍然是一個很大的開銷。
TensorFlow也把復雜的計算放在Python之外完成,但是為了避免前面說的那些開銷,它做了進一步完善。Tensorflow不單獨地運行單一的復雜計算,而是讓我們可以先用圖描述一系列可交互的計算操作,然后全部一起在Python之外運行。
2 卷積神經網絡
2.1算法介紹
卷積神經網絡(Convolutional Neural Network, CNN)是一種前饋神經網絡,它的人工神經元可以響應一部分覆蓋范圍內的周圍單元,對於大型圖像處理有出色表現。卷積神經網絡由一個或多個卷積層和頂端的全連通層(對應經典的神經網絡)組成,同時也包括關聯權重和池化層(pooling layer)。這一結構使得卷積神經網絡能夠利用輸入數據的二維結構。
卷積神經網絡中比較重要的一個概念是卷積操作,如下圖所示,根據learned weights將若干塊映射到一個新的矩陣上。
其實卷積操作並不陌生,在日常生活中p圖時就經常會用到,例如對圖片的銳化、模糊等操作,就是用一個特定的矩陣(圖中的learned weights矩陣)對原圖像進行卷積操作。下圖就是一張圖片卷積后的樣子。
不難看出卷積操作后圖片會變小(2021.05.11更新,這里之前說的不對,其實卷積操作不一定會變小,padding之后就可以不變小。圖片里這個變小是因為下采樣),經過若干次的操作(相當於隱藏層),最后輸出一個和上面介紹BP神經網絡時一樣的具有10個元素的向量,分別代表是某個數字的概率。如下圖
不斷優化中間參數的值,利用梯度下降或其他優化方法,就可以完成模型的訓練。
2.2實驗代碼
#下載、導入數據用到的函數包 import input_data #讀取數據 mnist = input_data.read_data_sets('MNIST_data', one_hot=True) #tensorflow 2.x沒有placeholder所以要用1.x的API import tensorflow.compat.v1 as tf tf.disable_v2_behavior() sess = tf.InteractiveSession() # 我們在TensorFlow運行計算時輸入這個值。我們希望能夠輸入任意數量的MNIST圖像,每一張圖展平成784維的向量。 x = tf.placeholder("float", shape=[None, 784]) #為了計算交叉熵,我們首先需要添加一個新的占位符用於輸入正確值 y_ = tf.placeholder("float", shape=[None, 10]) #W的維度是[784,10],因為我們想要用784維的圖片向量乘以它以得到一個10維的證據值向量 W = tf.Variable(tf.zeros([784,10])) #b的形狀是[10],所以我們可以直接把它加到輸出上面 b = tf.Variable(tf.zeros([10])) #初始化 sess.run(tf.initialize_all_variables()) #計算每個分類的softmax概率值 y = tf.nn.softmax(tf.matmul(x,W) + b) #損失函數是目標類別和預測類別之間的交叉熵。 cross_entropy = -tf.reduce_sum(y_*tf.log(y)) #用最速下降法讓交叉熵下降,步長為0.01 train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy) #整個模型的訓練可以通過反復地運行train_step來完成 for i in range(1000): batch = mnist.train.next_batch(50) train_step.run(feed_dict={x: batch[0], y_: batch[1]}) #評估模型 correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) print (accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels})) #上面的代碼是應用BP神經網絡 #下面將構建一個多層卷積網絡 #權重初始化 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') #第一層卷積 W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32]) x_image = tf.reshape(x, [-1,28,28,1]) 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) #密集連接層 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) #Dropout keep_prob = tf.placeholder("float") h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) #輸出層 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 = -tf.reduce_sum(y_*tf.log(y_conv)) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) sess.run(tf.initialize_all_variables()) for i in range(20000): batch = mnist.train.next_batch(50) if i%100 == 0: train_accuracy = accuracy.eval(feed_dict={ x:batch[0], y_: batch[1], keep_prob: 1.0}) print ("step %d, training accuracy %g"%(i, train_accuracy)) train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5}) print ("test accuracy %g"%accuracy.eval(feed_dict={ x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
2.3實驗結果
可以看出使用卷積神經網絡訓練的准確率要比BP神經網絡高。
2.4實驗總結
1、我們看到一個的東西時,其實眼睛並沒有注意到整個物體,而是把注意力集中在一小部分。比如說我們看到一個人時,首先注意到的是他的臉。卷積操作就相當於提取圖片的特征信息,相當於將計算機的注意力放在圖片的某一部分。我們將中間的隱藏層作為圖片輸出時,看到的東西會很奇怪,雖然我們看不懂,但這些相當於計算機對這張圖片的一種抽象認識。
2、完成本篇實驗報告后對卷積神經網絡有了初步的認識。但對其中的激勵層、池化層等概念還沒有深入徹底的理解,只是模糊的知道大概是干什么的,對深度學習中的數學原理也沒有很透徹的理解,還需要今后不斷地學習。