深度学习(DL)与卷积神经网络(CNN)学习笔记随笔-03-基于Python的LeNet之LR
原地址可以查看更多信息
本文主要參考于:Classifying MNIST digits using Logistic Regression?
python源代碼(GitHub下載 CSDN免費下載)
0階張量叫標量(scarlar);1階張量叫向量(vector);2階張量叫矩陣(matrix)?
?
本文主要內容:如何用python中的theano包實現最基礎的分類器–LR(Logistic Regression)。?
一、模型?
?
由概率論知識總結出模型,二分類用公式(1),多分類用公式(2);為了求解公式(2)中的最優參數(W和b),推導出目標函數公式(3)?
?
邏輯回歸是一種線性分類器。它的參數包括權重矩陣W和偏置b。通過將輸入向量映射到一組超平面進行多分類(一個超平面可以分兩類,多個超平面可以進行多分類)。輸入向量到超平面的距離作為輸入樣本屬于對應類別的概率。?
輸入向量x屬于第Y=i類的概率模型表示如下:?
其中, W 和 b 是參數, P(Y=i|x) 是條件概率,意思是在變量 x 的條件下, Y=i 的概率。 舉例:P(Y=0|x)表示輸入的樣本x被識別為數字0的概率。 這個并不難理解,只要學過概率論的話,不是問題。?
?
softmaxi(Wx+b) 可以理解為 x 屬于 i 的概率,具體含義看下邊內容。這個表達式更清楚地說明了 x 的運算過程,即: x與W點乘,再與b?矩陣/向量 相加,然后把結果傳入softmax分類器,得到分類結果為i 。那么, softmax 是如何工作的呢(具體含義是什么呢)?就是公式(1)中最終的結果表達式。下面分析這個表達式。?
?
eWix+bi 可以理解為表示樣本 x 屬于第 i 類的概率。那么 ∑Nj=0eWjx+bj 顯然是表示 x 屬于每一類的概率之和。為什么兩者要做除法呢?答案是: 歸一化 。這樣,最終 P(Y=i) 的累加和就是1。?
?
首先看看 eWix+bi 是怎么來的。因為邏輯回歸的假設函數是 sigmoid 函數,即 h(x)=11+e?(Wx+b) ,而 softmax 是多分類,所以其假設函數就是?
h(x)=???????p(Y=0|x)p(Y=1|x)?p(Y=N|x)???????=1∑Nj=0eWjx+bj???????eW0x+b0eW1x+b1?eWNx+bN???????(2)
從公式(2)很容易看出是如何歸一化的了。找到這個列向量中最大值的下標 k ,就代表樣本x屬于第 k 類的概率最大。因此,模型的最終預測公式為: ypred=argmaxP(Y=i|x)(3)
argmax 函數的作用是返回矩陣中每一行或每一列最大數的下標。在這里,矩陣 Pm?n 的元素構成是每一行代表一個樣本(即共有 m 個樣本),每一列代表當前樣本屬于該列下標(0-n)類別的概率。 舉例:第0行第0列元素是第0行中最大數下標。則表示:第0行所代表的樣本被分類為0的概率是最大的。 那么以此類推,最終的結果就表示每一個樣本所分 類別 的最大概率。
至此,概率模型已經分析完畢。下邊就是如何用python實現計算輸入樣本的概率值了。代碼如下(代碼中會涉及到Theano的使用,因此如果有解釋不到的函數,請參考我的Theano官方教程翻譯及學習筆記系列博文,以上博文因正在編寫,臨時可參考Theano實現卷積運算代碼詳解):
<code class="language-python hljs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 初始化權值W,shared是用來為了GPU運算的。</span> self.W = theano.shared(value=numpy.zeros((n_in, n_out),dtype=theano.config.floatX),name=<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'W'</span>,borrow=<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">True</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 初始化偏置b</span> self.b = theano.shared(value=numpy.zeros((n_out,),dtype=theano.config.floatX),name=<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'b'</span>,borrow=<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">True</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 下面就是概率公式(1)的代碼實現,其中dot是點乘運算;input是輸入向量x;</span> self.p_y_given_x = T.nnet.softmax(T.dot(input, self.W) + self.b)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 對輸入樣本的預測,是公式(3)的代碼實現,axis表示函數argmax要按照行返回最大數</span> self.y_pred = T.argmax(self.p_y_given_x, axis=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li></ul>由于水平有限,思路可能會有一些亂,但是對照公式(1-3)慢慢整理一下還是可以理解的。現在所寫的代碼只是實現了公式的計算,相當于定義了一個函數,輸入樣本矩陣x,輸出預測矩陣y。但是,如果能預測的準呢?下面且看第二部分。
二、定義代價函數?
?
要最優化公式(3)的參數,那么根據經驗編寫出代價函數,即公式(4)?
?
由機器學習的知識知道,要想使得模型預測結果最佳,即W和b取得最佳參數,那么就要根據假設函數定義一個代價函數,當代價函數最小化時,預測結果最優。(為什么要根據假設函數?因為模型公式就是基于假設函數編寫的)
在多分類的邏輯回歸中,經常用負對數似然函數作為代價函數,記為A。最小化函數A就等價于最大化A中的似然函數。似然函數L和代價函數?定義如下:?
D 是數據集(輸入樣本集); |D| 表示樣本總數; θ 是模型參數(由 W 和 b 構成);?
公式 L 表示,先對每一個輸入樣本進行公式(1)操作,然后對結果取log對數,最后將所有樣本的概率對數求和。?
公式 ? 表示取 L 的負數。?
?
那么,怎么最小化那一堆的非線性函數呢? 梯度下降法 。梯度下降法是到目前為止最簡單的用來最小化任意非線性函數的方法。因此,這里也同樣采取該方法,不過是經過改進的,即 批量隨機梯度下降法(MSGD-stochastic gradient method with mini-batches) 聽起來很炫,其實很簡單。梯度下降是更新整體樣本;隨機梯度下降是更新一個樣本;而批量隨機梯度則是介于兩者之間,更新一部分樣本。具體解釋 看這里 。?
?
現在基本介紹完了代價函數了,下面來看一下代價函數的代碼,注意是傳入一塊(minibatch)樣本數據,而不是一個或整個樣本。?
<code class="language-python hljs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># y.shape返回y的行數和列數,則y.shape[0]返回y的行數,即樣本的總個數,因為一行是一個樣本。</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># T.arange(n),則是產生一組包含[0,1,...,n-1]的向量。</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># T.log(x),則是對x求對數。記為LP</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># LP[T.arange(y.shape[0]),y]是一組向量,其元素是[ LP[0,y[0]], LP[1,y[1]], </span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># LP[2,y[2]], ...,LP[n-1,y[n-1]] ]</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># T.mean(x),則是求向量x中元素的均值。</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> -T.mean(T.log(self.p_y_given_x)[T.arange(y.shape[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>]), y])</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul>
對照公式(4)看一下,?
P(Y=yi|xi)就是傳入的self.p_y_given_x;?
log(P)就是T.log(p);?
∑求和就是代碼中的 LP[T.arange(y.shape[0]),y];?
?=?L就是return中的取反。?
還有一點就是公式中沒有求均值,而代碼中加入了T.mean的均值運算。這是因為公式(4)還欠缺一部分就是除以|D|,因此其完整的公式為:?
為什么要用均值,若不是和值呢?這里是因為用的批量隨機梯度下降法來最小化代價函數,不同的樣本輸入塊可能對學習速率產生不同的影響。因此用均值是為了降低學習速率對輸入樣本塊的依賴性。?
三、創建LR的python類 ?
?
1. 創建類。
<code class="language-python hljs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">LR</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(object)</span>:</span><span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"""邏輯歸回的實現是基于博文中給出的公式,需要預先設定好參數W和b。最小化方法用的批量隨機梯度下降法MSGD。因此傳入數據是一塊一塊的。"""</span><span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">__init__</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(self, input, n_in, n_out)</span>:</span><span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"""初始化函數!此類實例化時調用該函數 按照Python定義類的格式給出如下定義,需要傳入的參數分別為:input的類型為 TensorType,類似于形參,起象征性的作用,并不包含真實的數據;input傳入值為 minibatch樣本數據,該數據是一個m*n的矩陣。m表示此minibatch塊共有m個樣本;n表示每一個樣本的實際數據。在mnist實驗中,n=784=28*28,因為每一張圖片是28*28像素的。n_in 的類型為 int;n_in 傳入值為 每個輸入樣本的單元數(應該是圖片的高*寬(28*28=784),但是在我們的實驗數據中,已經把圖片數據矩陣存儲為了行向量(784*1),因此這個地方傳入的就是數據域中的data列的長度,即n_in=784,具體的樣本數據是傳入input里面)n_out的類型為 intn_out傳入值為 輸出結果的類別數,就是數據域中的標簽的范圍。此處就是0-9共10個數字。所以n_out=10。就是10分類。"""</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 初始化權值矩陣</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># numpy.zeros((m,n),dtype='float32') 是產生一組 m行n列的全0矩陣,每個矩陣元素存儲為float32類型。</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># shared()函數是將生成的矩陣封裝為shared類型,該類型可以用于GPU加速運算,沒有其他用途。</span>self.W = theano.shared(value = numpy.zeros((n_in, n_out),dtype = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'float32'</span> ), name = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'W'</span>,borrow = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">True</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 初始化偏置值</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># b是一個向量,長度為n_out,就是每一種分類都有一個偏置值</span>self.b = theano.shared(value = numpy.zeros((n_out,),dtype = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'float32'</span> ),name = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'b'</span>,borrow = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">True</span> )<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 計算公式(1),具體解釋見博文 http://blog.csdn.net/niuwei22007/article/details/47705081</span>self.p_y_given_x = T.nnet.softmax(T.dot(input, self.W) + self.b)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 計算公式(3)</span>self.y_pred = T.argmax(self.p_y_given_x, aixs = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 組織模型用到的參數,即把W和b組裝成list,便于在類外引用。</span>self.param = [self.W, self.b]<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 記錄模型的具體輸入數據,便于在類外引用</span>self.input = input<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">negative_log_likelihood</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(self, y)</span>:</span><span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"""負對數似然函數,即代價函數。 需要傳入的參數為:y 的類型為 TensorType,類似于形參,起象征性的作用,并不包含真實的數據;y 傳入值為 input對應的標簽向量,如果input的樣本數為m,則input的行數就是m,那么y就是一個m行的列向量。"""</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 計算完整的公式(4),具體解釋見博文 http://blog.csdn.net/niuwei22007/article/details/47705081 </span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> -T.mean(T.log(self.p_y_given_x)[T.arange(y.shape[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>]), y])<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">errors</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(self, y)</span>:</span><span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"""誤差計算函數。傳入的參數參考negative_log_likelihood.其作用就是統計預測正確的樣本數占本批次總樣本數的比例。 """</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 檢查 傳入正確標簽向量y和前面做出的預測向量y_pred是否是具有相同的維度。如果不相同怎么去判斷某個樣本預測的對還是不對?</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># y.ndim返回y的維數</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># raise是拋出異常</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> y.ndim != self.y_pred.ndim:<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">raise</span> TypeError(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"y doesn't have the same shape as self.y_pred"</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 繼續檢查y是否是有效數據。依據就是本實驗中正確標簽數據的存儲類型是int</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 如果數據有效,則計算:</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># T.neq(y1, y2)是計算y1與y2對應元素是否相同,如果相同便是0,否則是1。</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 舉例:如果y1=[1,2,3,4,5,6,7,8,9,0] y2=[1,1,3,3,5,6,7,8,9,0]</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 則,err = T.neq(y1,y2) = [0,1,0,1,0,0,0,0,0,0],其中有3個1,即3個元素不同</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># T.mean()的作用就是求均值。那么T.mean(err) = (0+1+0+1+0+0+0+0+0+0)/10 = 0.3,即誤差率為30%</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> y.dtype.startswith(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'int'</span>):<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> T.mean(T.neq(self.y_pred, y))<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>:<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">raise</span> NotImplementedError()</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li><li style="box-sizing: border-box; padding: 0px 5px;">75</li><li style="box-sizing: border-box; padding: 0px 5px;">76</li><li style="box-sizing: border-box; padding: 0px 5px;">77</li><li style="box-sizing: border-box; padding: 0px 5px;">78</li><li style="box-sizing: border-box; padding: 0px 5px;">79</li><li style="box-sizing: border-box; padding: 0px 5px;">80</li><li style="box-sizing: border-box; padding: 0px 5px;">81</li><li style="box-sizing: border-box; padding: 0px 5px;">82</li><li style="box-sizing: border-box; padding: 0px 5px;">83</li><li style="box-sizing: border-box; padding: 0px 5px;">84</li><li style="box-sizing: border-box; padding: 0px 5px;">85</li><li style="box-sizing: border-box; padding: 0px 5px;">86</li><li style="box-sizing: border-box; padding: 0px 5px;">87</li><li style="box-sizing: border-box; padding: 0px 5px;">88</li><li style="box-sizing: border-box; padding: 0px 5px;">89</li><li style="box-sizing: border-box; padding: 0px 5px;">90</li><li style="box-sizing: border-box; padding: 0px 5px;">91</li><li style="box-sizing: border-box; padding: 0px 5px;">92</li><li style="box-sizing: border-box; padding: 0px 5px;">93</li><li style="box-sizing: border-box; padding: 0px 5px;">94</li><li style="box-sizing: border-box; padding: 0px 5px;">95</li></ul>
2.實例化LR類的方式:
<code class="language-python hljs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 因為LR中的input是TensorType類型,因此引用時,也需要定義一個TensorType類型</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># x表示樣本的具體數據</span> x = T.matrix(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'x'</span>) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 同樣y也應該是一個TensorType類型,是一個向量,而且數據類型還是int,因此定義一個T.ivector。</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 其中i表示int,vector表示向量。詳細可以參考Theano教程。</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># y表示樣本的標簽。</span> y = T.ivector(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'y'</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># x就是input樣本,是一個矩陣,因此定義一個T.matrix</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># n_in,n_out的取值在此不再贅述,可以翻看上邊的博文。</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 在實例化時,會自動調用LR中的__init__函數</span> classifier = LR(input=x, n_in=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">28</span>*<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">28</span>, n_out=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">10</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 代價函數,依據公式(3)計算生成。這是一個符號變量,cost并不是一個具體的數值。當傳入具體的數據后,</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 其才會有具體的數據產生。</span> cost = classifier.negative_log_likelihood(y)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li></ul> 四、訓練模型?
我們先回顧一下如何進行模型訓練。?
【1】根據概率論的知識總結出一個概率模型,二分類用公式(1);多分類用公式(2);?
【2】求解出模型中的參數(即公式(2)中的W和b),因為參數沒有確定值,所以我們的目標是使概率模型產出的概率(即公式(3)的結果)距離正確結果越接近越好。?
【3】根據經驗以及步驟2中的目標編寫出代價函數,即公式(4);通過最小化公式(4)獲得最優參數W和b。?
【4】采用批量隨機梯度下降法最小化公式(4)。本節主要講述如何用梯度下降法最小化代價函數。先說一下思路,就是先根據上邊計算出的cost(代價函數)對W和b分別求偏導,然后根據梯度更新W和b的值。計算誤差;再根據新的W和b求偏導,如此迭代下去,直到誤差符合要求或者迭代達到一定次數結束循環,此時的W和b即可以認為是目前最優的。?
?
若要在大多數的編程語言中實現梯度下降算法,需要手動的推導出梯度表達式???W和???b(?就是公式(4)),這是一個非常麻煩的推導,而且最終結果也很復雜,特別是考慮到數值穩定性的問題的時候。?
?
然而,在Theano這個工具中,這個變得異常簡單。因為它已經把求梯度這種運算給封裝好了,不需要手動推導公式,只需要按照格式傳入數據即可。下面來看一下代碼。
計算完了梯度,就要根據梯度進行權值偏置值的更新。操作如下:
<code class="language-python hljs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># updates相當于一個更新器,說明了哪個參數需要更新,以及更新公式</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 下面代碼指明更新需要參數W,更新公式是(原值-學習速率*梯度值)</span> updates = [(classifier.W, classifier.W - learning_rate * g_W), <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 參數b的更新類似于W </span>(classifier.b, classifier.b - learning_rate * g_b)]</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>現在我們就可以編寫模型訓練函數了。代碼就兩句話,但是解釋一大堆,希望能幫助初學者了解function的工作原理。
<code class="language-python hljs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 上邊所提到的TensorType都是符號變量,符號變量只有傳入具體數值時才會生成新的數據。</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># theano.function也是一個特色函數。在本實驗中,它會生成一個叫train_model的函數。</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 該函數的參數傳遞入口是inputs,就是將需要傳遞的參數index賦值給inputs</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 該函數的返回值是通過outputs指定的,也就是返回經過計算后的cost變量。</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 更新器updates是用剛剛定義的update</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># givens是一個很實用的功能。它的作用是:在計算cost時會用到符號變量x和y(x并沒有顯示的表達出來,</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 函數negative_log_likehood用到了p_y_given_x,而計算p_y_given_x時用到了input,input就是x)。</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 符號變量經過計算之后始終會有一個自身值,而此處計算cost不用x和y的自身值,那就可以通過givens里邊的表達式</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 重新指定計算cost表達式中的x和y所用的值,而且不會改變x和y原來的值。</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">##舉個簡單的例子:</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># state = shared(0)</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># inc = T.iscalar('inc')</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># accumulator = function([inc], state, updates=[(state, state+inc)])</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># state.get_value() #結果是array(0),因為初始值就是0</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># accumulator(1) #會輸出結果array(0),即原來的state是0,但是繼續往下看</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># state.get_value() #結果是array(1),根據updates得知,state=state+inc=0+1=1</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># accumulator(300) #會輸出結果array(1),即原來的state是1,但是繼續往下看</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># state.get_value() #結果是array(301),根據updates得知,state=state+inc=1+300=301</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">##此時state=301,繼續做實驗</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># fn_of_state = state * 2 + inc</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">##foo用來代替更新表達式中的state,即不用state原來的值,而用新的foo值,但是fn_of_state表達式不變</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># foo = T.scalar(dtype=state.dtype)</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">##skip_shared函數是輸入inc和foo,輸出fn_of_state,通過givens修改foo代替fn_of_state表達式中的state</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># skip_shared = function([inc, foo], fn_of_state, givens=[(state, foo)]) </span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># skip_shared(1, 3) #會輸出結果array(7),即fn_of_state=foo * 2 + inc = 3*2+1 = 7</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">##再來看看state的原值是多少呢?</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># state.get_value() #會輸出結果array(301),而不是foo的值3</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">##因為每一次都需要用新的x和y去計算cost值,而不是用原來的上一次的x和y去計算,因此需要用到givens</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">##希望通過這個小例子能說清楚givens的作用。</span> train_model = theano.function(inputs = [index],outputs = cost,updates = update,givens = {<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># x:僅僅是表示第一個數據用來代替x,而不去重新聲明一個和x結構類型相同的符號變量了;y同理</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># trian_set_x是訓練數據集中的x分量,就是樣本的數據部分,trian_set_x[a:b]代表取數組中下標從a開始,到下標b之前的數據。</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># train_set_y是訓練數據集中的y分量,就是樣本的標簽部分。</span>x: trian_set_x[index * batch_size:(index + <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>) * batch_size],y: trian_set_y[index * batch_size:(index + <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>) * batch_size]} ) </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li></ul> 每一次調用train_model(index),都會計算并返回輸入樣本塊的cost,然后執行一次MSGD,并更新W和b。整個學習算法的一次迭代這樣循環調用train_model (總樣本數/樣本塊數)次。假設總樣本60000個,一個樣本塊600個,那么一次迭代就需要調用100次train_model。而模型的訓練又需要進行多次迭代,直到達到迭代次數或者誤差率達到要求。?
?
五、測試模型?
?
模型測試需要用到LR中的errors函數。下面來看一下測試模型函數test_model和驗證模型函數validate_model。有了上面訓練模型的基礎,相信這個測試模型會很容易理解。
一塊一塊的講了這么多代碼,或許都看暈了,下面就看一下整合之后的代碼會更清晰。?
六、整合代碼?
python源代碼帶注釋下載?
只有一個lr.py,直接用python命令執行即可。?
七、參考目錄?
總結
以上是生活随笔為你收集整理的深度学习(DL)与卷积神经网络(CNN)学习笔记随笔-03-基于Python的LeNet之LR的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度学习(DL)与卷积神经网络(CNN)
- 下一篇: 深度学习(DL)与卷积神经网络(CNN)