天天看點

超基礎的機器學習入門-原理篇

随着前端智能化的火熱,AI機器學習進入前端開發者們的視野。AI能夠解決程式設計領域不能直接通過規則和運算解決的問題,通過自動推理産出最佳政策,成為了前端工程師們解決問題的又一大利器。

可能很多同學都躍躍欲試過,打開 <code>TensorFlow</code> 或者 <code>Pytorch</code> 官網,然後按照文檔想要寫一個機器學習的 <code>Hello World</code> ,然後就會遇到一些不知道是什麼的函數,跑完例子卻一頭霧水,這是因為 <code>TensorFlow</code> 和 <code>Pytorch</code> 是使用機器學習的工具,而沒有說明什麼是機器學習。是以這篇文章以實踐為最終目的出發,介紹一些機器學習入門的基本原理,加上一丢丢圖像處理的卷積,希望可以幫助你了解。

首先,什麼是機器學習?機器學習約等于找這樣一個函數,比如在語音識别中,輸入一段語音,輸出文字内容

超基礎的機器學習入門-原理篇

在圖像識别中,輸入一張圖像,輸出圖中的對象,

超基礎的機器學習入門-原理篇

在圍棋中,輸入棋盤資料,輸出下一步怎麼走,

超基礎的機器學習入門-原理篇

在對話系統中,輸入一句 hi ,輸出一句回應,

超基礎的機器學習入門-原理篇

而這個函數,是由你寫的程式加上大量的資料,然後由機器自己學習到的。

超基礎的機器學習入門-原理篇

怎麼找這樣一個函數呢,讓我們從線性模型入手。線性模型形式簡單,易于模組化,但是蘊含着機器學習中一些重要的基本思想,許多功能更為強大的非線性模型都可線上性模型的基礎上通過引入層級結構或高維映射而得到。

我們以一個貓和狗的分類來看,我們在教一個小朋友區分貓和狗的時候,并不會給到一個維奇百科的定義,而是不斷的讓小朋友看到貓和狗,讓他判斷,然後告訴他正确答案,糾結錯誤認知。機器學習也是同理,不斷告知計算機怎樣是正确的,糾正計算機的認知,不同的是,小朋友的認知是人腦自動處理完成的,而計算機并不能自動的建構貓和狗的記憶,計算機隻認識數字。

是以我們需要提取出代表貓和狗的特征,然後用數字來表示。為了簡化例子,我們這裡隻用到兩個特征,鼻子的大小以及耳朵的形狀,一般來說貓貓的鼻子更小,耳朵更尖,而狗狗鼻子比較大,耳朵比較圓。

超基礎的機器學習入門-原理篇

我們對多張圖檔,統計圖檔中耳朵以及鼻子特征,在一個二維坐标中表現出來,可以看到貓貓和狗狗會分布在坐标系的不同區域。

超基礎的機器學習入門-原理篇

肉眼可見,我們可以用一條直線來區分,但是,計算機并看不到哪裡可以畫條線。如何将資訊傳遞給計算機呢,讓我們定義兩個變量,<code>x1</code> 表示鼻子大小,<code>x2</code> 表示耳朵形狀,再定義這樣一個直線方程 <code>W1 · X1 + W2 · X2 - b = 0</code>,也就相當于,令<code>y=W1 · X1 + W2 · X2 - b</code> ,當 <code>y</code> 大于<code>0</code>,判斷是貓,當 <code>y</code> 小于 <code>0</code> ,判斷是狗。

超基礎的機器學習入門-原理篇

現在,從計算機的角度來看,它擁有了一堆資料,

超基礎的機器學習入門-原理篇

以及一個線性模型,

超基礎的機器學習入門-原理篇

還差一個目标/任務,我們的期望是,當給一個沒有見過的 <code>x</code> ,通過 <code>f(x)</code> ,可以得到一個預測值 <code>y</code> ,這個 <code>y</code> 要能夠盡可能的貼近真實的值,這樣,就有了一台有用的萌寵分類機了!這樣的目标如何用數字來表示呢,這就要引入一個概念損失函數(<code>Loss function</code>)了,損失函數計算的是預測值與真實值之間的差距。

超基礎的機器學習入門-原理篇

常用的損失函數有絕對值損失函數(<code>Absolute value loss</code>),也就是兩個數值差的絕對值,就很直覺,距離目标差多少,加起來,就醬

超基礎的機器學習入門-原理篇

還有平方損失函數(最小二乘法, <code>Least squares loss</code>)

平方損失函數的目标是讓每個點到回歸直線的距離最小,這個距離算的是歐幾裡得距離。

現在,我們給計算機的目标就變成了求一個最小值,

為了求這個值,讓我們回憶一下久違的微積分,(同樣,為了簡化到二維坐标系,假設隻有一個需要求的 <code>w</code> ),導數為 <code>0</code> 的地方即是函數的極大值或者極小值。

對于圖中這樣一個簡單的一進制二次方程,我們可以直接對參數 <code>w</code> 求導,求得極小值。但是,如果是下圖中這樣一個函數呢,就..不好求了,而且對于不同的函數求導有不同的公式,那就..比較麻煩了,畢竟我們的目标是讓機器自己學習,是吧。

是以,我們需要一個更通用的計算方法,那就是梯度下降(<code>Gradient descent</code>,

梯度下降的基本流程如下,首先,我們随機取一個點作為初始值,計算這個點的斜率,也就是導數。

當斜率為負的時候,往右走一小步,

當斜率為正的時候,往左走一小步,

在每個點上重複,計算新的斜率,再适當的走一小步,就會逼近函數的某個局部最小值,就像一個小球從山上滾下來,不過初始位置不同,會到達不同的局部最小值,無法保證是全局最小,但是,其實,大部分情況我們根據問題抽象的函數基本都是凸函數,能夠得到一個極小值,在極小值不唯一的情況下,也可以加入随機數,來給到一個跳出目前極小值區域的機會。我們需要明确的是,機器學習的理論支撐是機率論與統計學,我們通過機器學習尋找的問題答案,往往不是最優解,而是一個極優解。

想象一個更複雜的有兩個輸入一個輸出的二進制函數,我們的 <code>loss function</code> 可以呈現為三維空間中的一個曲面,問題就變成了,曲面上某個點要往空間中哪個方向走,才能讓結果下降得最快。

步驟依舊是,計算梯度,更新,計算,更新....用公示來表示就是如下,

這時候,我們就遇到了第一個超參數 <code>η</code> ,即學習率(<code>Learning rate</code>),機器學習中的參數分為兩類,模型參數與超參數,模型參數是 <code>w</code> 這種,讓機器自己去學習的,超參數則是在模型訓練之前由開發人員指定的。

通過上面的公式,可以看到

是 <code>Loss function</code> 函數對于參數 <code>w</code> 的導數,決定了我們走的方向,那麼學習率則決定了在這個方向每一小部走的距離。

當 <code>η</code> 太小,到達極小值的過程會非常的緩慢,而如果 <code>η</code> 太大,則會因為步伐太大,直接越過最低點。那麼,<code>η</code> 的值要怎麼取呢,

比較正常的做法是,以從 <code>0.1</code> 這樣的值開始,然後再指數下降,取<code>0.01</code>,<code>0.001</code>,當我們用一個很大的學習率,會發現損失函數的值幾乎沒有下降,那可能就是在搖擺,當我們取到一個較小的值,能夠讓損失函數下降,那麼繼續往下取,不斷縮小範圍,這個過程也可以通過計算機自動來做,如果有計算資源的話。

了解了梯度下降、學習率後,我們已經可以使用線性模型解決比較簡單的問題了,

基本步驟:

提取特征

設定模型

計算梯度,更新

是不是想試一下了!

這裡有一個簡單的房價預測的栗子,可以本地跑跑看,試試調整不同的學習率,看 <code>loss function</code> 的變化。

https://github.com/xs7/MachineLearning-demo/blob/master/RegressionExperiment.ipynb

其中關鍵代碼如下:

我們剛剛說到的線性模型,實際上是一個單層的網絡,它包括了機器學習的基本要素,模型、訓練資料、損失函數和優化算法。但是受限于線性運算,并不能解決更加複雜的問題。

我們需要更為通用的模型來适應不同的資料。比如多加一層?加一層的效果約等于對坐标軸進行變換,可以做更複雜一丢丢的問題了。

但是依舊是線性模型,沒有辦法解決非線性問題,比如下圖中,沒有辦法用一條直線分開,但是用 <code>y= x2</code> 這樣一個二進制一次方程就可以輕輕松松,這就是非線性的好處了。

加一個非線性的結構,也就引入了神經網絡中另一個基本概念,激活函數(<code>Activation Function</code>),常見的激活函數如下

<code>Relu</code> 函數隻保留正數元素,清零負數元素,<code>sigmoid</code> 函數可以把元素的值變換到 <code>0~1</code> 之間,<code>tanh</code> 函數可以把元素的值變換到 <code>-1~1</code> 之間。其中用到最廣泛的是看上去最簡單的 <code>Relu</code> ,<code>Relu</code>函數就好比人腦神經元,達到神經元的刺激門檻值就輸出,達不到門檻值就置零。

激活函數的選擇要考慮到輸入輸出以及資料的變化,比如通常會用 <code>sigmoid</code> 作為輸出層的激活函數,比如做分類任務的時候,将結果映射到 <code>0~1</code> ,對于每個預設的類别給到一個 <code>0~1</code> 的預測機率值。

可以了解為,我們提供了非線性的函數,然後神經網絡通過自己學習,使用我們提供的非線性元素,可以逼近任意一個非線性函數,于是可以應用到衆多的非線性模型中。

加入激活函數後,我們就擁有了多層感覺機(<code>multi layer perceptron</code>),多層感覺機就是含有至少一個隐藏層的由全連接配接層組成的神經網絡,且每個隐藏層的輸出通過激活函數進行變換。

類似上圖這樣,就構成了一個簡單的多層感覺網絡,即深度神經網絡。網絡層級變複雜之後,依舊是使用梯度下降來進行疊代優化,但是梯度的計算卻變複雜了,網絡中的每條線上都有一個 <code>w</code> 權重參數,需要用 <code>loss function</code> 對每個 <code>w</code> 求梯度,大概估一下,假設輸入層有<code>10</code>個節點,有兩個隐藏層,每個隐藏層隐藏層那從輸入層到隐藏層 <code>1</code> 再到隐藏層 <code>2</code> 就有 <code>30000*3</code> 個參數,而且參數之間是存在函數關系的,最終輸出的 <code>loss</code> 對第一層隐藏層的 <code>w</code> 求導需要逐層求過來,計算量<code>++++++n</code>, 直接求導是萬萬不可能的,是以我們需要反向傳播算法(<code>Backpropagation</code>,<code>bp</code>算法)。

反向傳播算法是用來在多層網絡中快捷的計算梯度的,其推導相對而言要複雜一些,使用架構的時候..直接調用api即可,也沒有什麼開發者能調整的地方,大家應該..不想寫代碼計算偏導數吧..那就作為進階内容,先挖個坑..下次來填..

到現在我們應該對神經網絡的計算已經有了一個基本的印象,回顧一下,

就是給到一個多層網絡結構模型,然後輸入資料,不斷求梯度來更新模型的參數,不斷減少模型預測的誤差。其中使用梯度來更新參數的步伐由超參數學習率決定,用僞代碼表示就是:

到這裡,我們已經知道了一個深度神經網絡基本結構以及計算流程,可以看懂一些簡單的使用神經網絡的代碼了,繼續回去看 <code>Pytorch</code> 官方教程,結果 <code>demo</code> 裡面都是圖像的栗子,是以..那就再看看什麼是卷積神經網絡叭。

我們前面提到的網絡模型中,相鄰兩層之間任意兩個節點之間都有連接配接,稱之為全連接配接網絡(<code>Fully Connected Layer</code>)。當我們用一個深度網絡模型處理圖檔,可以把圖檔中每個像素的 <code>rgb</code> 值均作為輸入,一張 <code>100*100</code> 的圖檔,網絡的輸入層就有 <code>100*100*3</code> 個節點,哪怕隻給一個隐藏層,輸入層到隐藏層就已經有 <code>30000*100</code> 個參數了,再添加幾層或者換稍微大一點的圖檔,參數數量就更爆炸了。圖像需要處理的資料量太大,使用全連接配接網絡計算的成本太高,而且效率很低。直到卷積神經網絡(<code>Convolutional Neural Network</code>, <code>CNN</code>)出現,才解決了圖像處理的難題。

直接來看卷積神經網絡是什麼樣子的吧,如下

一個典型的卷積神經網絡包括了三部分

卷積層

池化層

全連接配接層

其中,卷積層用來提取圖檔的特征,池化層用來減少參數,全連接配接層用來輸出我們想要的結果。

先來看卷積。

輸入一張圖檔,再給到一個卷積核(<code>kernel</code>,又稱為<code>filter</code>濾波器)

超基礎的機器學習入門-原理篇

将濾波器在圖像上滑動,對應位置相乘求和,

超基礎的機器學習入門-原理篇
超基礎的機器學習入門-原理篇

滑完可以得到一個新的二維數組,這就是卷積運算了,是的..就是這樣簡單的加法。

超基礎的機器學習入門-原理篇

如果再加一個卷積核,運算完畢就得到了兩個通道的數組。

超基礎的機器學習入門-原理篇

二維卷積層輸出的二維數組可以看作是輸入在空間次元(寬和高)上某一級的表征,也叫特征圖(<code>feature map</code>)。

一個節點的輸入來源區域稱為其感受野(<code>receptive field</code>),比如特征圖中第一個節點 <code>3</code> 的輸入野就是輸入圖檔左上角 <code>3*3</code> 的區域。

超基礎的機器學習入門-原理篇

如果我們對結果再來一次卷積,最後得到的特征圖中的第一個節點 <code>17</code> ,其感受野就變成了其輸入節點的感受野的并集,即圖檔左上角 <code>4*4</code> 的區域。我們可以通過更深的卷積神經網絡使特征圖中單個元素的感受野變得更加廣闊,進而捕捉輸入上更大尺寸的特征。

超基礎的機器學習入門-原理篇

這其實是模拟人類視覺原理,當我們接收到視覺信号,大腦皮層的某些細胞會做初步處理,發現邊緣以及方向,然後再進行抽象,判定眼前物體的形狀是圓的還是方的,然後進一步抽象是什麼物體。通過多層的神經網絡,較低層的神經元識别初級的圖像特征,若幹底層特征組成更上一層特征,最終得到最高抽象的特征來得到分類結果。

超基礎的機器學習入門-原理篇

了解了多層卷積是從局部抽象到全局抽象這樣一個識别過程後,再回過頭來看一下卷積核本身。

從函數的角度來了解,卷積過程是在圖像每個位置進行線性變換映射成新值的過程, 在進行逐層映射,整體構成一個複雜函數。從模版比對的角度來說,卷積核定義了某種模式,卷積運算是在計算每個位置與該模式的相似程度,或者說每個位置具有該模式的分量有多少,目前位置與該模式越像,響應越強。

比如用邊緣檢測算子來做卷積,<code>sobel</code> 算子包含兩組 <code>3*3</code> 的矩陣,分别為橫向及縱向,将之與圖像作平面卷積,如果以A代表原始圖像,<code>G(x)</code> 及 <code>G(y)</code> 分别代表經橫向及縱向邊緣檢測的圖像:

超基礎的機器學習入門-原理篇

<code>sobel</code> 偏 <code>x</code> 方向的邊緣檢測計算結果如下所示:

超基礎的機器學習入門-原理篇

再看一些直覺表現不同卷積核算子效果的栗子,

超基礎的機器學習入門-原理篇

是不是感覺卷積大法好。當然,我們可以直接找一些有趣的卷積核來用,比如用卷積來檢測圖像邊緣,也可以通過資料來學習卷積核,讓神經網絡來學習到不同的算子。

剛剛在卷積計算的時候,每次滑動了一個小格,也就是 <code>stride</code> 步伐為 <code>1</code>,其實也可以把步伐加大,每次滑動 <code>2</code> 個小格,也可以跳着取值,來擴大感受野,也可以為了保持輸出數組的長寬與輸入一緻,在原圖邊緣加一圈 <code>padding</code> ,作為最最基礎的入門,這裡就不展開了。

回到我們的網絡結構,可以看到兩層神經元間隻有部分連接配接了,更少的連接配接,代表更少的參數。

超基礎的機器學習入門-原理篇

但是這樣還不夠,圖檔像素太多,哪怕我們隻對局部取特征,依舊需要許多許多的參數,是以還需要池化(<code>pooling</code>)。

池化層的作用其實就是下采樣,縮小圖檔,池化的計算也非常的簡單,對輸出資料的一個固定大小視窗的元素進行計算,然後輸出,最大池化(<code>Max Pooling</code>) 就是取池化視窗内元素的最大值,平均池化則是取輸入視窗的元素的平均值。

超基礎的機器學習入門-原理篇

除了下采樣,減小圖檔大小,池化還可以緩解卷積層對位置的過度敏感性,避免模型過拟合,舉一個極端的例子,一張圖檔隻有四個像素,如果某個位置像素為 <code>255</code> ,我們就判定是某個類型的物品,如果我們輸入的用來學習的訓練集圖檔中,每張圖檔都是左上角第一個像素為 <code>255</code> ,如果沒有池化,模型訓練的結果就是,當左上角第一個像素為 <code>255</code> ,那麼輸出判斷為該物品,當我們用這個模型去預測一張右上角像素為255的圖檔,模型會認為不是該物體,判斷錯誤。而如果有池化,不管 <code>255</code> 出現在哪一個位置,池化後都會取到 <code>255</code>,判斷為是該物品。

經過多個卷積層和池化層降維,資料就來到了全連接配接層,進行高層級抽象特征的分類啦。

到這裡,應該已經介紹完看懂 <code>Pytorch</code> / <code>Tensorflow</code> 官網入門教程所需要的絕大部分原理知識了,可以愉快的跑官網的圖檔分類示例然後寫自己的網絡了。

具體架構使用那就下篇《超基礎的機器學習入門-實踐篇》見。

Deco 智能代碼項目是凹凸實驗室在「前端智能化」方向上的探索,我們嘗試從設計稿生成代碼(DesignToCode)這個切入點入手,對現有的設計到研發這一環節進行能力補全,進而提升産研效率。其中使用到不少AI能力來實作設計稿的解析與識别,感興趣的童鞋歡迎關注我們的賬号「凹凸實驗室」(知乎、掘金)。

http://speech.ee.ntu.edu.tw/~tlkagk/courses_ML19.html

如何估算深度神經網絡的最優學習率

卷積神經網絡之卷積計算、作用與思想

數字圖像 - 邊緣檢測原理 - Sobel, Laplace, Canny算子

https://zh.wikipedia.org/wiki/%E7%B4%A2%E8%B2%9D%E7%88%BE%E7%AE%97%E5%AD%90

https://medium.com/@pkqiang49/%E4%B8%80%E6%96%87%E7%9C%8B%E6%87%82%E5%8D%B7%E7%A7%AF%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C-cnn-%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86-%E7%8B%AC%E7%89%B9%E4%BB%B7%E5%80%BC-%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8-6047fb2add35

http://cs231n.stanford.edu/

歡迎關注凹凸實驗室部落格:aotu.io

或者關注凹凸實驗室公衆号(AOTULabs),不定時推送文章:

超基礎的機器學習入門-原理篇