手寫體識別與Tensorflow
如同所有語言的hello world一樣,手寫體識別就相當於深度學習里的hello world。
TensorFlow是當前最流行的機器學習框架,有了它,開發人工智能程序就像Java編程一樣簡單。
MNIST
MNIST 數據集已經是一個被”嚼爛”了的數據集, 很多教程都會對它”下手”, 幾乎成為一個 “典范”. 不過有些人可能對它還不是很了解, 下面來介紹一下.
MNIST 數據集可在 http://yann.lecun.com/exdb/mnist/ 獲取, 它包含了四個部分:
Training set images: train-images-idx3-ubyte.gz (9.9 MB, 解壓后 47 MB, 包含 60,000 個樣本)
Training set labels: train-labels-idx1-ubyte.gz (29 KB, 解壓后 60 KB, 包含 60,000 個標簽)
Test set images: t10k-images-idx3-ubyte.gz (1.6 MB, 解壓后 7.8 MB, 包含 10,000 個樣本)
Test set labels: t10k-labels-idx1-ubyte.gz (5KB, 解壓后 10 KB, 包含 10,000 個標簽)
MNIST 數據集來自美國國家標准與技術研究所, National Institute of Standards and Technology (NIST). 訓練集 (training set) 由來自 250 個不同人手寫的數字構成, 其中 50% 是高中學生, 50% 來自人口普查局 (the Census Bureau) 的工作人員. 測試集(test set) 也是同樣比例的手寫數字數據.
tensorflow提供一個input_data.py文件,專門用於下載mnist數據,我們直接調用就可以了,代碼如下:
import tensorflow.examples.tutorials.mnist.input_data mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
執行完成后,會在當前目錄下新建一個文件夾MNIST_data
input_data文件會調用一個maybe_download函數,確保數據下載成功。這個函數還會判斷數據是否已經下載,如果已經下載好了,就不再重復下載。
思路
把圖片當成一枚枚像素來看,下圖為手寫體數字1的圖片,它在計算機中的存儲其實是一個二維矩陣,每個元素都是0~1之間的數字,0代表白色,1代表黑色,小數代表某種程度的灰色。
現在,對於MNIST數據集中的圖片來說,我們只要把它當成長度為784的向量就可以了(忽略它的二維結構,28×28=784)。我們的任務就是讓這個向量經過一個函數后輸出一個類別。就是下邊這個函數,稱為Softmax分類器。
這個式子里的圖片向量的長度只有3,用x表示。乘上一個系數矩陣W,再加上一個列向量b,然后輸入softmax函數,輸出就是分類結果y。W是一個權重矩陣,W的每一行與整個圖片像素相乘的結果是一個分數score,分數越高表示圖片越接近該行代表的類別。因此,W x + b 的結果其實是一個列向量,每一行代表圖片屬於該類的評分。通常分類的結果並非評分,而是概率,表示有多大的概率屬於此類別。因此,Softmax函數的作用就是把評分轉換成概率,並使總的概率為1。
CNN
卷積神經網絡(Convolutional Neural Networks / CNNs / ConvNets)與普通神經網絡非常相似,它們都由具有可學習的權重和偏置常量(biases)的神經元組成。每個神經元都接收一些輸入,並做一些點積計算,輸出是每個分類的分數,普通神經網絡里的一些計算技巧到這里依舊適用。
卷積神經網絡利用輸入是圖片的特點,把神經元設計成三個維度 : width, height, depth(注意這個depth不是神經網絡的深度,而是用來描述神經元的) 。比如輸入的圖片大小是 32 × 32 × 3 (rgb),那么輸入神經元就也具有 32×32×3 的維度。下面是圖解:
一個卷積神經網絡由很多層組成,它們的輸入是三維的,輸出也是三維的,有的層有參數,有的層不需要參數。
卷積神經網絡通常包含以下幾種層:
數據輸入層:
該層要做的處理主要是對原始圖像數據進行預處理,其中包括:
• 去均值:把輸入數據各個維度都中心化為0,如下圖所示,其目的就是把樣本的中心拉回到坐標系原點上。
• 歸一化:幅度歸一化到同樣的范圍,如下所示,即減少各維度數據取值范圍的差異而帶來的干擾,比如,我們有兩個維度的特征A和B,A范圍是0到10,而B范圍是0到10000,如果直接使用這兩個特征是有問題的,好的做法就是歸一化,即A和B的數據都變為0到1的范圍。
• PCA/白化:用PCA降維;白化是對數據各個特征軸上的幅度歸一化
卷積層
卷積神經網路中每層卷積層由若干卷積單元組成,每個卷積單元的參數都是通過反向傳播算法優化得到的。卷積運算的目的是提取輸入的不同特征,第一層卷積層可能只能提取一些低級的特征如邊緣、線條和角等層級,更多層的網絡能從低級特征中迭代提取更復雜的特征。
下面的動態圖形象地展示了卷積層的計算過程:
線性整流層(Rectified Linear Units layer, ReLU layer),這一層神經的活性化函數(Activation function)使用線性整流(Rectified Linear Units, ReLU)f(x)=max(0,x)
把卷積層輸出結果做非線性映射。
池化層(Pooling layer),通常在卷積層之后會得到維度很大的特征,將特征切成幾個區域,取其最大值或平均值,得到新的、維度較小的特征。
池化層的具體作用。
1.特征不變性,也就是我們在圖像處理中經常提到的特征的尺度不變性,池化操作就是圖像的resize,平時一張狗的圖像被縮小了一倍我們還能認出這是一張狗的照片,這說明這張圖像中仍保留着狗最重要的特征,我們一看就能判斷圖像中畫的是一只狗,圖像壓縮時去掉的信息只是一些無關緊要的信息,而留下的信息則是具有尺度不變性的特征,是最能表達圖像的特征。
2.特征降維,我們知道一幅圖像含有的信息是很大的,特征也很多,但是有些信息對於我們做圖像任務時沒有太多用途或者有重復,我們可以把這類冗余信息去除,把最重要的特征抽取出來,這也是池化操作的一大作用。
3.在一定程度上防止過擬合,更方便優化。
![]()
池化層用的方法有Max pooling 和 average pooling,而實際用的較多的是Max pooling。
全連接層( Fully-Connected layer), 把所有局部特征結合變成全局特征,用來計算最后每一類的得分。CNN的常用框架
Caffe
• 源於Berkeley的主流CV工具包,支持C++,python,matlab
• Model Zoo中有大量預訓練好的模型供使用
Torch
• Facebook用的卷積神經網絡工具包
• 通過時域卷積的本地接口,使用非常直觀
• 定義新網絡層簡單
TensorFlow
• Google的深度學習框架
• TensorBoard可視化很方便
• 數據和模型並行化好,速度快
實現代碼
代碼如下:
# -*- coding: utf-8 -*- """ Spyder Editor This is a temporary script file. """ # -*- coding:utf-8 -*- import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data #number from 0 to 9: mnist=input_data.read_data_sets('MNIST_data/',one_hot=True) def add_layer(inputs,in_size,out_size,activation_function=None): Weights=tf.Variable(tf.random_normal([in_size,out_size])) bises=tf.Variable(tf.zeros([1,out_size])+0.1) Wx_plus_b=tf.matmul(inputs,Weights)+bises if activation_function is None: outputs=Wx_plus_b else: outputs=activation_function(Wx_plus_b) return outputs #計算准確度 def compute_accuracy(x,y): global prediction y_pre=sess.run(prediction,feed_dict={xs:x}) correct_prediction=tf.equal(tf.argmax(y_pre,1),tf.argmax(y,1)) accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32)) result=sess.run(accuracy,feed_dict={xs:x,ys:y}) return result #def placeholder for inputs xs=tf.placeholder(tf.float32,[None,784]) #28*28 ys=tf.placeholder(tf.float32,[None,10]) #10個輸出 #add output layer prediction=add_layer(xs,784,10,tf.nn.softmax) #softmax常用於分類 cross_entropy=tf.reduce_mean(-tf.reduce_sum(ys*tf.log(prediction),reduction_indices=[1])) train=tf.train.GradientDescentOptimizer(0.3).minimize(cross_entropy) sess=tf.Session() sess.run(tf.initialize_all_variables()) for i in range(2000): batch_xs,batch_ys=mnist.train.next_batch(100) sess.run(train,feed_dict={xs:batch_xs,ys:batch_ys}) if i%100==0: print(compute_accuracy(mnist.test.images,mnist.test.labels))
執行看輸出,准確度為:
Extracting MNIST_data/train-images-idx3-ubyte.gz Extracting MNIST_data/train-labels-idx1-ubyte.gz Extracting MNIST_data/t10k-images-idx3-ubyte.gz Extracting MNIST_data/t10k-labels-idx1-ubyte.gz 0.1007 0.6668 0.7603 0.7946 0.8198 0.8324 0.8416 0.8473 0.8544 0.8565 0.8634 0.8661 0.8654 0.8692 0.8725 0.8727 0.8748 0.8753 0.8771 0.8781
准確率為87%。
總結
上面的例子使用的是TensorFlow提供的數據集,我們可以自己手寫一個數字,然后通過opencv對數字進行剪裁,然后輸入模型看識別的結果。
深度學習和nlp的可以加微信群交流,目前,我們正在參加nlp方面的比賽。