人工智能
人工智能(Artificial Intelligence,簡稱AI)一詞最初是在1956年Dartmouth學會上提出的,從那以后,研究者們發展了眾多理論和原理,人工智能的概念也隨之擴展。由於人工智能的研究是高度技術性和專業的,各分支領域都是深入且各不相通的,因而涉及范圍極廣 。 人工智能的核心問題包括建構能夠跟人類似甚至超越人類的推理、知識、學習、交流、感知、使用工具和操控機械的能力等,當前人工智能已經有了初步成果,甚至在一些影像識別、語言分析、棋類游戲等等單方面的能力達到了超越人類的水平 。
人工智能的分支領域非常多,主要有演繹推理、知識表示、規划、學習、自然語言處理……等十多個分支領域,而以機器學習為代表的“學習”領域,是目前研究最廣泛的分支之一。
機器學習
機器學習(Machine Learning)是人工智能的一個分支,它是實現人工智能的一個途徑,即以機器學習為手段解決人工智能中的問題。機器學習在近30多年已發展為一門多領域交叉性的學科,涉及概率論、統計學、逼近論、凸分析、計算復雜性理論等多門學科。
機器學習理論主要是設計和分析一些讓計算機可以自動“學習”的算法,該算法是一類從數據中自動分析獲得規律,並利用規律對未知數據進行預測的算法。
深度學習
深度學習(Deep Learning)是機器學習的分支,是一種以人工神經網絡為架構,對數據進行表征學習的算法。表征學習的目標是尋求更好的表示方法並創建更好的模型來從大規模未標記數據中學習這些表示方法。表示方法來自神經科學,並松散地創建在類似神經系統中的信息處理和對通信模式的理解上,如神經編碼,試圖定義拉動神經元的反應之間的關系以及大腦中的神經元的電活動之間的關系。
因此,人工智能、機器學習、深度學習的關系如下圖所示。
至今已有數種深度學習模型,如深度神經網絡、卷積神經網絡和深度置信網絡和遞歸神經網絡已被應用在計算機視覺、語音識別、自然語言處理、音頻識別與生物信息學等領域並獲取了極好的效果。
目前,業內也已經產生了多種優秀的深度學習框架,例如TensorFlow、PyTorch、Caffe、Mxnet等等。但這些都不是本文討論的重點,本文主要以機器學習初學者的身份,使用最基本的機器學習算法,加以微積分、線性代數、概率統計等基礎數學知識,來解決手寫數字的識別的問題。
問題背景
為什么要去研究數字的識別問題呢?因為最近剛過雙11,又看了到許多曝光快遞行業野蠻分揀的新聞。據某快遞公司負責人回應稱,之所以會出現野蠻分揀的問題,主要是雙11期間快遞數量巨增,為了盡快派發收到的快遞,他們不得不請了許多“臨時工”,而這些“臨時工”缺乏培訓,缺乏規范操作,所以出現了暴力分揀快遞的問題。
問題分析
針對分揀快遞這種簡單、重復的工作,可以交給機器去做嗎?我們來分析一下“臨時工”所做的工作。“臨時工”拿到一個快遞,找到快遞上的快遞單,然后再找到目的省(城市),如果是華北城市,則將快遞扔進“北京”的筐里;如果是華東城市,則扔進“上海”的筐里;如果是西南城市,則扔進“成都”的筐里。那么機器可以完成嗎?
如上圖所示,快遞通過傳送帶進入“目的地識別系統”,識別后將該快遞分配到對應地點的傳送帶即可。那么,該問題的關鍵就是“目的地識別系統”如何將快遞單上的目的地識別出來,並作出正確的判斷。
以順豐速運的快遞單為例,快遞單上已將目的地翻譯為了城市代碼,通過識別該代碼即可讓機器“知道”快遞的目的地,然后配置對應傳送帶接收的具體城市代碼即可。
識別數字技術在深度學習領域已經非常成熟,常見的解決方案是OpenCV+Keras+TensorFlow,例如Github上有比較完善的車牌識別項目,但本文並不打算使用這些庫,而是采用最底層、最基礎的機器學習方法來實現。
數學建模
數字照片通過掃描后,以像素點的方式進行存儲,因此輸入數據即是像素點,通過機器學習算法后,結果則是識別出來的0-9的數字。根據機器學習理論,每個樣本都有對應的標簽,因此屬於“監督式學習”的范疇。而樣本的輸出值為0-9固定的10種情況,因此可以采用邏輯回歸的機器學習模型來解決,分別計算結果為0-9的概率,建模就是找到一個假設函數(Hypothesis Function),函數值是數據通過假設函數后獲得對應的輸出結果,即概率。
邏輯回歸的假設函數是由S型函數(Sigmoid Function)演變而來,S型函數的表達式及曲線如下圖所示:
從曲線中可以看到,當變量z趨近於正無窮時,函數值趨近於1,當變量z趨近於負無窮時,函數值趨近於0。這樣就能夠很好的匹配邏輯回歸,因為邏輯回歸的輸出為0或1,當輸出值為0.7時,則表示結果為1的概率是70%,為0的概率是30%,正好可以進行概率的預測。
受線性回歸所啟發,邏輯回歸的假設函數公式為(其中θ為模型的參數矩陣,x為輸入變量矩陣,變量z變成了θ的轉置乘以x):
如果要讓機器來識別數字,那么首先就要先用樣本去教會機器,即用樣本“訓練”模型。為了獲得“最好”的模型,我們需要計算樣本在模型下的代價函數(Cost Function,也有資料稱為“損失函數”)。所謂代價函數,就是在該模型下產生的輸出與實際結果間產生的偏差,偏差越小,則可以在一定程度上表明模型越好(也不是絕對的,可能會出現模型過度擬合(Overfit)的情況,需要一些手段來避免)。
通過概率統計理論中的“最大似然估計”,可以得到如下的邏輯回歸的代價函數:
該函數看起來很復雜,可以將其拆開來看,log(h(x))是y=1時的代價函數,log(1-h(x))是y=0時的代價函數,最右邊的一項為正則化參數,可以減小出現過度擬合的幾率。為了找到最好的模型(假設函數),我們需要找到該代價函數的最小值。找到最小值后,自變量θ即為我們要找的邏輯回歸的模型參數。
根據高等數學中的“拉格朗日中值定理”,可以得知該函數為凹函數,存在最小值。證明過程比較復雜,不在此闡述。
模型訓練
為了得到代價函數J(θ)的最小值,我們可以采用機器學習中最常用的“梯度下降”算法(Gradient Decent)來求得函數在區間內的極小值。所謂梯度下降算法,就是對於任一函數,首先取任一點(x1或者x2均可),在這一點減去這一點對應的梯度(即該點的導數),那么這一點就會向該函數的某一極小值運動,反復進行梯度下降,則可以得到區間內的極小值x0。如果函數為凹函數,那么該極小值就是函數的最小值。
因此,執行梯度下降的公式為:
這里需要對J(θ)求“偏導數”,求得后的結果為:
至此,理論工作准備完畢,可以進行編碼實戰。
在Matlab/Octave中訓練
輸入樣本為手寫數字,以20 * 20像素點的形式存儲,將像素點數據攤開作為一行,每行就有400個像素點信息。訓練樣本中搜集了5000個手寫數字的照片,因此樣本X為5000 * 400的矩陣,樣本結果y為5000 * 1的列向量。
% 計算代價函數
J = 1 / m * (-y' * log(sigmoid(X * theta)) - (1 - y)' * log(1 - sigmoid(X * theta)));
% 計算梯度
grad = 1 / m * X' * (sigmoid(X * theta) - y);
% 代價函數正則化
J = J + lambda / (2 * m) * (sum(theta(2:end) .^ 2));
% 梯度正則化
theta_temp = theta;
theta_temp(1) = 0;
grad = grad + lambda / m * theta_temp;
通過以上代碼,就可以實現一次代價函數的計算,並返回當前點的梯度。根據之前的分析,只要重復進行梯度下降即可。而Matlab提供了一種更加簡便的方式“fmincg”函數,它能采用類似梯度下降的方式,來自動優化參數θ。
% 初始化theta
initial_theta = zeros(n + 1, 1);
% 參數
options = optimset('GradObj', 'on', 'MaxIter', 50);
% 循環所有數字
for c = 1:num_labels
% 訓練出最優theta
theta = fmincg(@(t)(lrCostFunction(t, X, (y == c), lambda)), initial_theta, options);
all_theta(c, :) = theta;
endfor
可以看到,上述代碼進行了1-10總共10個模型的訓練,每個模型就是識別0-9這10個數字的概率。通過以上訓練后,就可以得到最后的theta,將其帶入假設函數hθ(x),於是就得到了我們訓練后的10個模型,可以用該模型來進行手寫數字的識別。
數字識別
利用已經訓練好的10個模型,我們就可以將機器從未見過的手寫數字通過10個模型,讓每個模型計算出他是對應數字的概率,然后我們取最高的概率,就可以得到機器識別出的數字。我們來舉個例子:
如圖紅框中的數字,可能有的人會認成“4”,而有的人卻會認成“6”。到底是4還是6呢?可能眾說紛紜,因為有的人習慣這樣寫4,而有的人卻不習慣這樣寫。在機器學習中,機器會學習之前樣本中的數據,學習到作者寫數字的習慣,將該測試樣本分別輸入到10個模型后,得到如下的概率輸出(均保留5位有效數字):
數字 | 概率 | 數字 | 概率 |
---|---|---|---|
0 | 0.0000021328% | 5 | 0.0000015184% |
1 | 0.0000021719% | 6 | 99.987% |
2 | 2.3224% | 7 | 0.000033508% |
3 | 0.0000012768% | 8 | 0.0011023% |
4 | 0.013391% | 9 | 0.032251% |
通過如上數據可以看到,數字6的匹配度高達99.987%占據了絕對領先,第二則是數字2的2.3224%。而數字4只有0.013391的概率,看來在機器學習看來,這個數字基本可以判定為“6”,只是稍微有一丁點像“2”,跟其他數字都特別不像。我們取概率最大值,得出了正確的結果為數字“6”。
我們用測試樣本的真實值對模型進行校驗,最終獲得訓練的正確率為94.9%。那么取100個測試樣本的識別結果如何呢?
可以看到,100個測試樣本的識別結果有5個數字識別錯誤,測試識別率為95%。那么有什么方法可以提高識別率呢?
提高梯度下降次數
通過之前的理論分析我們知道,梯度下降次數越多,代價函數就越接近最小值,於是我把次數從50提高到100時,測試樣本准確率達到了95.98,提升了約1%。然后又提高到200時,達到了96.4%,提升了約0.5%。最后提高到500時,仍為96.4%,沒有提升。
看來在邏輯回歸模型下,手寫數字的識別率最高僅可以提升到96.4%,已經達到了最高。還有其他辦法可以提升識別率嗎?
神經網絡
人工神經網絡(Artificial Neural Network),簡稱神經網絡(Neural Network,NN),在機器學習和認知科學領域,是一種模仿生物神經網絡(動物的中樞神經系統,特別是大腦)的結構和功能的數學模型或計算模型,用於對函數進行估計或近似。
神經網絡由大量的人工神經元聯結進行計算,大多數情況下人工神經網絡能在外界信息的基礎上改變內部結構,是一種自適應系統,通俗的講就是具備學習功能,並且是一種非線性統計性數據建模工具。
不得不說,人類是真的聰明,居然可以想到建立類似於生物大腦神經的模型來模擬大腦,從而實現部分人類的能力。神經網絡模型如下:
可以看到,基本的神經網絡模型有輸入層、隱藏層、輸出層。輸入層用於接受輸入信號,類似於人類感知視覺信號、聲音信號、觸覺信號等等。隱藏層可以是多層,可以讓數據在不同層之間傳遞與處理,類似於人類的神經元,可以逐級傳遞。輸出層用於輸出處理后的數據。如果有非常多的隱藏層,又可以稱為深度神經網絡,在這種模型下的機器學習又稱作深度學習。
由於神經網絡是一種非線性模型,屬於邏輯結構,因此沒有簡單的“假設函數”。要計算數據通過輸入層、隱藏層后到輸出層的數據,可以通過“正向傳播算法”(Forward Propagation)。
正向傳播
神經網絡模型看似復雜,如果只看一層的話,就可以用邏輯回歸的模型來推導,因為每一層都是邏輯回歸問題。若θ1與θ2已知(圖中標識),那么就可以用邏輯回歸來計算每一層的輸出,然后逐漸從左到右,正向傳遞,所以稱為正向傳播,最終得出輸出值hθ(x)。
正向傳播的步驟如下:
假設函數有了,如果我們能找到模型的θ1與θ2,那么模型就有了,就可以用這個模型來進行數字識別了。那么怎么才能找到合適的θ1與θ2呢?與之前講的邏輯回歸類似,我們也可以先找到該模型的代價函數,然后通過梯度下降找到代價函數的最小值,就可以找到神經網絡的參數了。
代價函數
前面已經提到,神經網絡模型其實就是有很多層的邏輯回歸模型,那么代價函數也可以采用邏輯回歸的代價函數,然后將每一層網絡疊加起來就可以了,所以神經網絡的代價函數如下:
公式看起來比較嚇人,實際上只是多了網絡層數K,並且參數θ從向量變成了矩陣而已。如果把這個公式轉化為矩陣形式,其實非常的簡單(不含最右邊的正則化):
% m為樣本數,y為樣本結果矩陣,h為由正向傳播計算出的輸出矩陣。
J = - 1 / m * (sum(sum(y .* log(h))) + sum(sum((1 - y) .* log(1-h))));
代價函數有了,梯度該怎么算呢?與邏輯回歸不同,因為增加了層數的概念,所以梯度計算也會變得比較復雜,神經網絡里稱為“反向傳播算法”(Backward Propagation)。
反向傳播
求解梯度,最終還是對代價函數進行求偏導數,但由於模型是非線性的,無法直接求偏導數。所以,反向傳播的基本思路就是將最終的計算偏差分攤到每一層,逐漸從右向左,反向傳遞,所以稱為反向傳播。
反向傳播的步驟如下:
通過第4步,我們就得到了J(θ)的偏導數,即梯度。
隨機初始化
在神經網絡模型中,θ的初始化非常重要。我在訓練神經網絡的實踐中,就因為忘了隨機初始化θ,而導致模型非常糟糕,無論怎么訓練,識別率都只有40%。吃一塹,長一智。神經網絡不像邏輯回歸中的θ,邏輯回歸中的θ初始為0或者其他任何數都可以,神經網絡中θ的初始化值直接影響了模型的好壞。
θ的隨機初始化的要求如下:
在Matlab/Octave中訓練
正向傳播:
% 計算h(x)
a1 = [ones(m, 1) X]';
z2 = Theta1 * a1;
a2 = [ones(1, m); sigmoid(z2)];
z3 = Theta2 * a2;
a3 = sigmoid(z3);
h = a3';
計算代價函數:
J = - 1 / m * (sum(sum(y2 .* log(h))) + sum(sum((1 - y2) .* log(1-h))));
% 計算正則化后的J
% 去掉theta的第一列,即去掉theta0
Theta1_new = Theta1(:, 2:end);
Theta2_new = Theta2(:, 2:end);
J = J + (sum(sum(Theta1_new .^ 2)) + sum(sum(Theta2_new .^ 2))) * lambda / (2 * m);
反向傳播:
% 將y2轉化為每一列為一個樣本
delta3 = a3 - y2';
Theta2_grad = delta3 * a2' / m;
% Theta2需要使用去掉了第一列針對偏置的權重
delta2 = Theta2_new' * delta3 .* sigmoidGradient(z2);
Theta1_grad = delta2 * a1' / m;
% 正則化,需要將theta的第一列設置為0
Theta1(:, 1) = 0;
Theta2(:, 1) = 0;
Theta2_grad = Theta2_grad + lambda / m * Theta2;
Theta1_grad = Theta1_grad + lambda / m * Theta1;
梯度下降:
options = optimset('MaxIter', 50);
lambda = 1;
costFunction = @(p) nnCostFunction(p, ...
input_layer_size, ...
hidden_layer_size, ...
num_labels, X, y, lambda);
[nn_params, cost] = fmincg(costFunction, initial_nn_params, options);
然后用同樣的訓練樣本對神經網絡模型訓練后,循環50次,測試樣本識別率達到了95.82%。提高到100次,達到98.14%。提高到200次,達到了98.94%。最后提高到500次,最終達到了99.36%。可以看到,神經網絡模型對於手寫數字識別率高於邏輯回歸模型。
總結
- 對於手寫數字,神經網絡模型一般比邏輯回歸模型的准確率更高。
- 邏輯回歸模型只能處理二維的輸出,如果是高維的輸出,需要用多個模型來降維,而神經網絡可以直接處理多維輸出。
- 使用簡單的(非深度)神經網絡,就可以實現較高的手寫數字識別率。
- 神經網絡模型的初始化參數非常重要。
- 在Maltab/Octave中,矩陣的運算效率要遠遠高於循環的運算效率,因此數據處理盡量采用矩陣形式。
(由於水平有限,本文如有分析得不對之處,還請指正。)