【DeepLearning學習筆記】Coursera課程《Neural Networks and Deep Learning》——Week2 Neural Networks Basics課堂筆記
Coursera課程《Neural Networks and Deep Learning》 deeplearning.ai
Week2 Neural Networks Basics
2.1 Logistic Regression as a Neutral Network
2.1.1 Binary Classification 二分類
邏輯回歸是一個用於二分類(binary classification)的算法。首先我們從一個問題開始說起,這裏有一個二分類問題的例子,假如你有一張圖片作為輸入,比如這只貓,如果識別這張圖片為貓,則輸出標簽1作為結果;如果識別出不是貓,那麽輸出標簽0作為結果。現在我們可以用字母 \(y\)
計算機存儲圖片,是存成RGB三個顏色通道的三個矩陣。也就是說如果圖片大小是64*64像素,那麽就有三個64*64的矩陣。
為了把這些像素值放到一個特征向量中,我們需要把這些像素值提取出來,然後放入一個特征向量\(x\)。所以最後我們得到的這個向量,將會是\(64\times64\times3=12,288\)長度的向量。我們可以使用\(n_x=12,288\)來表示特征向量的維度。
所以在二分類問題中,我們的目標就是習得一個分類器,它以圖片的特征向量作為輸入,然後預測輸出結果\(y\)為1還是0,也就是預測圖片中是否有貓。
符號定義 :
\(x\):表示一個\(n_x\)
\(y\):表示輸出結果,取值為\((0,1)\);
\((x^{(i)},y^{(i)})\):表示第\(i\)組數據,可能是訓練數據,也可能是測試數據,此處默認為訓練數據;
\(X=[x^{(1)},x^{(2)},...,x^{(m)}]\):表示所有的訓練數據集的輸入值,放在一個 \(n_x×m\)的矩陣中,其中\(m\)表示樣本數目;
\(Y=[y^{(1)},y^{(2)},...,y^{(m)}]\):對應表示所有訓練數據集的輸出值,維度為\(1×m\)。
用一對\((x,y)\)來表示一個單獨的樣本,\(x\)代表\(n_x\)
為了能把訓練集表示得更緊湊一點,我們會定義一個矩陣用大寫\(X\)的表示,它由輸入向量\(x^{(1)}\)、\(x^{(2)}\)等組成,如下圖放在矩陣的列中,所以現在我們把\(x^{(1)}\)作為第一列放在矩陣中,\(x^{(2)}\)作為第二列,\(x^{(m)}\)放到第\(m\)列,然後我們就得到了訓練集矩陣\(X\)。所以這個矩陣有\(m\)列,\(m\)是訓練集的樣本數量,然後這個矩陣的高度記為\(n_x\),註意有時候可能因為其他某些原因,矩陣\(X\)會由訓練樣本按照行堆疊起來而不是列,如下圖所示:\(x^{(1)}\)的轉置直到\(x^{(m)}\)的轉置,但是在實現神經網絡的時候,使用左邊的這種形式,會讓整個實現的過程變得更加簡單。
輸出標簽\(y\)同樣的道理,為了能更加容易地實現一個神經網絡,將標簽\(y\)放在列中將會使得後續計算非常方便,所以我們定義大寫的\(Y\)等於\({{y}^{\left( 1 \right)}},{{y}^{\left( m \right)}},...,{{y}^{\left( m \right)}}\),所以在這裏是一個規模為1乘以\(m\)的矩陣。
2.2.2 Logistic Regression 邏輯回歸
對於二元分類問題來講,給定一個輸入特征向量\(X\),它可能對應一張圖片,你想識別這張圖片識別看它是否是一只貓或者不是一只貓的圖片,你想要一個算法能夠輸出預測,你只能稱之為\(\hat{y}\),也就是你對實際值 \(y\) 的估計。更正式地來說,你想讓 \(\hat{y}\) 表示 \(y\) 等於1的一種可能性或者是機會,前提條件是給定了輸入特征\(X\)。換句話來說,如果\(X\)是我們在上個視頻看到的圖片,你想讓 \(\hat{y}\) 來告訴你這是一只貓的圖片的機率有多大。
\(X\)是一個\(n_x\)維的向量(相當於有\(n_x\)個特征的特征向量)。我們用\(w\)來表示邏輯回歸的參數,這也是一個\(n_x\)維向量(因為\(w\)實際上是特征權重,維度與特征向量相同),參數裏面還有\(b\),這是一個實數(表示偏差)。所以給出輸入\(x\)以及參數\(w\)和\(b\)之後,如果讓\(\hat{y}={{w}^{T}}x+b\),那麽我們得到的是關於\(x\)的線性函數。但是這對於二分類問題來說不是一個很好的算法,因為我們想要讓\(\hat{y}\)表示實際值\(y\)等於1的幾率的話,\(\hat{y}\)應該在0到1之間。
因此在邏輯回歸裏,我們的輸出應該是\(\hat{y}\)等於由上面得到的線性函數式子作為自變量的sigmoid函數,將線性函數轉換為非線性函數。該函數的公式如下:
\(\sigma\left(z\right)=\frac{1}{1+e^{-z}}\)
因此當你實現邏輯回歸時,你的工作就是去讓機器學習參數\(w\)以及\(b\),這樣才使得\(\hat{y}\)成為對\(y=1\)這一情況的概率的一個很好的估計。
在繼續進行下一步之前,介紹一種符號慣例,可以讓參數\(w\)和參數\(b\)分開。在符號上要註意的一點是當我們對神經網絡進行編程時經常會讓參數\(w\)和參數\(b\)分開,在這裏參數\(b\)對應的是一種偏置。在之前的機器學習課程裏,你可能已經見過處理這個問題時的其他符號表示。比如在某些例子裏,你定義一個額外的特征稱之為\({{x}_{0}}\),並且使它等於1,那麽現在\(X\)就是一個\(n_x+1\)維的變量,然後你定義\(\hat{y}=\sigma \left( {{\theta }^{T}}x \right)\)的sigmoid函數。在這個備選的符號慣例裏,你有一個參數向量\({{\theta }_{0}},{{\theta }_{1}},{{\theta }_{2}},...,{{\theta }_{{{n}_{x}}}}\),這樣\({{\theta }_{0}}\)就充當了\(b\),這是一個實數,而剩下的\({{\theta }_{1}}\) 直到\({{\theta }_{{{n}_{x}}}}\)充當了\(w\),結果就是當你實現你的神經網絡時,有一個比較簡單的方法是保持\(b\)和\(w\)分開。
2.2.3 Logistic Regression Cost Function 邏輯回歸的代價函數
對訓練集的預測值,我們將它寫成\(\hat{y}\),我們更希望它會接近於訓練集中的\(y\)值。為了讓模型通過學習調整參數,你需要給予一個\(m\)樣本的訓練集,這會讓你在訓練集上找到參數\(w\)和參數\(b\),來得到你的輸出。
我們使用這些帶有圓括號的上標來區分索引和樣本,訓練樣本\(i\)所對應的預測值是\({{y}^{(i)}}\),是用訓練樣本的\({{w}^{T}}{{x}^{(i)}}+b\)然後通過sigmoid函數來得到,也可以把\(z\)定義為\({{z}^{(i)}}={{w}^{T}}{{x}^{(i)}}+b\),我們將使用這個符號\((i)\)註解,上標\((i)\)來指明數據表示\(x\)或者\(y\)或者\(z\)或者其他數據的第\(i\)個訓練樣本,這就是上標\((i)\)的含義。
損失函數:
損失函數又叫做誤差函數,用來衡量算法的運行情況,Loss function:\(L\left( \hat{y},y \right)\).
一般我們用預測值和實際值的平方差或者它們平方差的一半,但是通常在邏輯回歸中我們不這麽做,因為當我們在學習邏輯回歸參數的時候,會發現我們的優化目標不是凸優化,只能找到多個局部最優值,梯度下降法很可能找不到全局最優值,雖然平方差是一個不錯的損失函數,但是我們在邏輯回歸模型中會定義另外一個損失函數。
我們在邏輯回歸中用到的損失函數是:\(L\left( \hat{y},y \right)=-y\log(\hat{y})-(1-y)\log (1-\hat{y})\)
當\(y=1\)時損失函數\(L=-\log (\hat{y})\),如果想要損失函數\(L\)盡可能得小,那麽\(\hat{y}\)就要盡可能大,因為sigmoid函數取值\([0,1]\),所以\(\hat{y}\)會無限接近於1。
損失函數是在單個訓練樣本中定義的,它衡量的是算法在單個訓練樣本中表現如何,為了衡量算法在全部訓練樣本上的表現如何,我們需要定義一個算法的代價函數,算法的代價函數是對\(m\)個樣本的損失函數求和然後除以\(m\):
\(J\left( w,b \right)=\frac{1}{m}\sum\limits_{i=1}^{m}{L\left( {{{\hat{y}}}^{(i)}},{{y}^{(i)}} \right)}=\frac{1}{m}\sum\limits_{i=1}^{m}{\left( -{{y}^{(i)}}\log {{{\hat{y}}}^{(i)}}-(1-{{y}^{(i)}})\log (1-{{{\hat{y}}}^{(i)}}) \right)}\)
損失函數只適用於像這樣的單個訓練樣本,而代價函數是參數的總代價,所以在訓練邏輯回歸模型時候,我們需要找到合適的\(w\)和\(b\),來讓代價函數 \(J\) 的總代價降到最低。
2.2.4 Gradient Descent 梯度下降
梯度下降法可以在你測試集上,通過最小化代價函數(成本函數)\(J(w,b)\)來訓練的參數\(w\)和\(b\)。
在這個圖中,橫軸表示你的空間參數\(w\)和\(b\),在實踐中,\(w\)可以是更高的維度,但是為了更好地繪圖,我們定義\(w\)和\(b\),都是單一實數,代價函數(成本函數)\(J(w,b)\)是在水平軸\(w\)和\(b\)上的曲面,因此曲面的高度就是\(J(w,b)\)在某一點的函數值。我們所做的就是找到使得代價函數(成本函數)\(J(w,b)\)函數值是最小值,對應的參數\(w\)和\(b\)。
如圖,代價函數(成本函數)\(J(w,b)\)是一個凸函數(convex function),像一個大碗一樣。
如圖,這就與剛才的圖有些相反,因為它是非凸的並且有很多不同的局部最小值。由於邏輯回歸的代價函數(成本函數)\(J(w,b)\)特性,我們必須定義代價函數(成本函數)\(J(w,b)\)為凸函數。
可以用如圖那個小紅點來初始化參數\(w\)和\(b\),也可以采用隨機初始化的方法,對於邏輯回歸幾乎所有的初始化方法都有效,因為函數是凸函數,無論在哪裏初始化,應該達到同一點或大致相同的點。
假定代價函數(成本函數)\(J(w)\) 只有一個參數\(w\),即用一維曲線代替多維曲線,這樣可以更好畫出圖像。
叠代就是不斷重復做如圖的公式:
\(:=\)表示更新參數,
$a $ 表示學習率(learning rate),用來控制步長(step),即向下走一步的長度\(\frac{dJ(w)}{dw}\) 就是函數\(J(w)\)對\(w\) 求導(derivative),在代碼中我們會使用\(dw\)表示這個結果。
整個梯度下降法的叠代過程就是不斷地向左走,直至逼近最小值點。
邏輯回歸的代價函數(成本函數)\(J(w,b)\)是含有兩個參數的。
$\partial $ 表示求偏導符號,可以讀作round,
\(\frac{\partial J(w,b)}{\partial w}\) 就是函數\(J(w,b)\) 對\(w\) 求偏導,在代碼中我們會使用\(dw\) 表示這個結果,
\(\frac{\partial J(w,b)}{\partial b}\) 就是函數\(J(w,b)\)對\(b\) 求偏導,在代碼中我們會使用\(db\) 表示這個結果,
小寫字母\(d\) 用在求導數(derivative),即函數只有一個參數,
偏導數符號$\partial $ 用在求偏導(partial derivative),即函數含有兩個以上的參數。
2.2.5 導數(Derivatives)
跳過,高數知識足夠。
2.2.6 更多的導數例子(More Derivative Examples)
跳過,高數知識足夠。
2.2.7 計算圖(Computation Graph)
跳過,好像其實就是高數中的復合函數情況。
2.2.8 使用計算圖求導數(Derivatives with a Computation Graph)
跳過,其實就是復合函數求導情況。
2.2.9 邏輯回歸中的梯度下降(Logistic Regression Gradient Descent)
\[z=w^{T}x+b\]
\[\hat{y}=a=\sigma\left(z\right)\]
\[L\left(a,y\right)=-\left(ylog\left(a\right)+\left(1-y\right)log\left(1-a\right)\right)\]
所以通過上面這個過程,我們就能求導每個變量的偏導數。
最後一步反向推導,也就是計算\(w\)和\(b\)變化對代價函數\(L\)的影響,特別地,可以用:
\(d{{w}_{1}}=\frac{1}{m}\sum\limits_{i}^{m}{x_{1}^{(i)}}({{a}^{(i)}}-{{y}^{(i)}})\)
\(d{{w}_{2}}=\frac{1}{m}\sum\limits_{i}^{m}{x_{2}^{(i)}}({{a}^{(i)}}-{{y}^{(i)}})\)
\(db=\frac{1}{m}\sum\limits_{i}^{m}{({{a}^{(i)}}-{{y}^{(i)}})}\)
因此,關於單個樣本的梯度下降算法,你所需要做的就是如下的事情:
使用公式\(dz=(a-y)\)計算\(dz\),
使用\(d{{w}_{1}}={{x}_{1}}\cdot dz\) 計算\(d{{w}_{1}}\), \(d{{w}_{2}}={{x}_{2}}\cdot dz\)計算\(d{{w}_{2}}\),
\(db=dz\) 來計算\(db\),
然後:
更新\({{w}_{1}}={{w}_{1}}-a d{{w}_{1}}\),
更新\({{w}_{2}}={{w}_{2}}-a d{{w}_{2}}\),
更新\(b=b-\alpha db\)。
這就是關於單個樣本實例的梯度下降算法中參數更新一次的步驟。
2.2.10 m 個樣本的梯度下降(Gradient Descent on m Examples)
帶有求和的全局代價函數,實際上是1到\(m\)項各個損失的平均。 所以它表明全局代價函數對\({{w}_{1}}\)的微分,對\({{w}_{1}}\)的微分也同樣是各項損失對\({{w}_{1}}\)微分的平均。
我們把這些裝進一個具體的算法。同時你需要一起應用的就是邏輯回歸和梯度下降。
我們初始化\(J=0,d{{w}_{1}}=0,d{{w}_{2}}=0,db=0\)
代碼流程:
J=0;dw1=0;dw2=0;db=0;
for i = 1 to m
z(i) = wx(i)+b;
a(i) = sigmoid(z(i));
J += -[y(i)log(a(i))+(1-y(i))log(1-a(i));
dz(i) = a(i)-y(i);
dw1 += x1(i)dz(i);
dw2 += x2(i)dz(i);
db += dz(i);
J/= m;
dw1/= m;
dw2/= m;
db/= m;
w=w-alpha*dw
b=b-alpha*db
幻燈片上只應用了一步梯度下降。因此你需要重復以上內容很多次,以應用多次梯度下降。
2.2.11 向量化(Vectorization)
都是在說和for循環相比,向量化可以快速得到結果。
2.2.12 向量化的更多例子(More Examples of Vectorization)
同上。
2.2.13 向量化的邏輯回歸(Vectorizing Logistic Regression)
向量化邏輯回歸那些公式。
2.2.14 向量化 logistic 回歸的梯度輸出(Vectorizing Logistic Regression‘s Gradient)
現在,讓我們回顧一下,看看我們之前怎麽實現的邏輯回歸,可以發現,沒有向量化是非常低效的,如下圖所示代碼:
我們的目標是不使用for循環,而是向量,我們可以這麽做:
\(Z = w^{T}X + b = np.dot( w.T,X)+b\)
\(A = \sigma( Z )\)
\(dZ = A - Y\)
\({{dw} = \frac{1}{m}*X*dz^{T}\ }\)
\(db= \frac{1}{m}*np.sum( dZ)\)
\(w: = w - a*dw\)
\(b: = b - a*db\)
現在我們利用前五個公式完成了前向和後向傳播,也實現了對所有訓練樣本進行預測和求導,再利用後兩個公式,梯度下降更新參數
2.2.15 Python 中的廣播(Broadcasting in Python)
這是一個不同食物(每100g)中不同營養成分的卡路裏含量表格,表格為3行4列,列表示不同的食物種類,從左至右依次為蘋果,牛肉,雞蛋,土豆。行表示不同的營養成分,從上到下依次為碳水化合物,蛋白質,脂肪。
那麽,我們現在想要計算不同食物中不同營養成分中的卡路裏百分比。
現在計算蘋果中的碳水化合物卡路裏百分比含量,首先計算蘋果(100g)中三種營養成分卡路裏總和56+1.2+1.8
= 59,然後用56/59 = 94.9%算出結果。
可以看出蘋果中的卡路裏大部分來自於碳水化合物,而牛肉則不同。
對於其他食物,計算方法類似。首先,按列求和,計算每種食物中(100g)三種營養成分總和,然後分別用不用營養成分的卡路裏數量除以總和,計算百分比。
那麽,能否不使用for循環完成這樣的一個計算過程呢?
假設上圖的表格是一個4行3列的矩陣\(A\),記為 \(A_{3\times 4}\),接下來我們要使用Python的numpy庫完成這樣的計算。我們打算使用兩行代碼完成,第一行代碼對每一列進行求和,第二行代碼分別計算每種食物每種營養成分的百分比。
在jupyter notebook中輸入如下代碼,按shift+Enter運行,輸出如下。
下面使用如下代碼計算每列的和,可以看到輸出是每種食物(100g)的卡路裏總和。
其中sum
的參數axis=0
表示求和運算按列執行,之後會詳細解釋。
接下來計算百分比,這條指令將 \(3\times 4\)的矩陣\(A\)除以一個\(1 \times 4\)的矩陣,得到了一個 \(3 \times 4\)的結果矩陣,這個結果矩陣就是我們要求的百分比含量。
下面再來解釋一下A.sum(axis = 0)
中的參數axis
。axis用來指明將要進行的運算是沿著哪個軸執行,在numpy中,0軸是垂直的,也就是列,而1軸是水平的,也就是行。
而第二個A/cal.reshape(1,4)
指令則調用了numpy中的廣播機制。這裏使用 \(3 \times 4\)的矩陣\(A\)除以 \(1 \times 4\)的矩陣\(cal\)。技術上來講,其實並不需要再將矩陣\(cal\) reshape
(重塑)成 \(1 \times 4\),因為矩陣\(cal\)本身已經是 \(1 \times 4\)了。但是當我們寫代碼時不確定矩陣維度的時候,通常會對矩陣進行重塑來確保得到我們想要的列向量或行向量。重塑操作reshape
是一個常量時間的操作,時間復雜度是\(O(1)\),它的調用代價極低。
那麽一個 \(3 \times 4\) 的矩陣是怎麽和 \(1 \times 4\)的矩陣做除法的呢?讓我們來看一些更多的廣播的例子。
在numpy中,當一個 \(4 \times 1\)的列向量與一個常數做加法時,實際上會將常數擴展為一個 \(4 \times 1\)的列向量,然後兩者做逐元素加法。結果就是右邊的這個向量。這種廣播機制對於行向量和列向量均可以使用。
再看下一個例子。
用一個 \(2 \times 3\)的矩陣和一個 \(1 \times 3\) 的矩陣相加,其泛化形式是 \(m \times n\) 的矩陣和 \(1 \times n\)的矩陣相加。在執行加法操作時,其實是將 \(1 \times n\) 的矩陣復制成為 \(m \times n\) 的矩陣,然後兩者做逐元素加法得到結果。針對這個具體例子,相當於在矩陣的第一列加100,第二列加200,第三列加300。這就是在前一張幻燈片中計算卡路裏百分比的廣播機制,只不過這裏是除法操作(廣播機制與執行的運算種類無關)。
下面是最後一個例子
這裏相當於是一個 \(m \times n\) 的矩陣加上一個 \(m \times 1\) 的矩陣。在進行運算時,會先將 \(m \times 1\) 矩陣水平復制 \(n\) 次,變成一個 \(m \times n\) 的矩陣,然後再執行逐元素加法。
對於Matlab/Octave 有類似功能的函數bsxfun
。
總結一下broadcasting
,可以看看下面的圖:
2.2.16 關於 python_numpy 向量的說明(A note on python or numpy vectors)
Python的特性允許我們使用廣播(broadcasting)功能,這是Python的numpy程序語言庫中最靈活的地方。而這是程序語言的優點,也是缺點。有時候可能會產生很細微或者看起來很奇怪的bug。比如,將一個列向量添加到一個行向量中,你會以為它報出維度不匹配或類型錯誤之類的錯誤,但是實際上你會得到一個行向量和列向量的求和。
所以我們盡量不要使用這些一維數組。相反,如果你每次創建一個數組,你都得讓它成為一個列向量,產生一個\((5,1)\)向量或者你讓它成為一個行向量,那麽你的向量的行為可能會更容易被理解。
# don‘t use
a = np.random.randn(5)
# recommand
a = np.random,randn(5,1)
a = np.random.randn(1,5)
2.2.17 Jupyter/iPython Notebooks快速入門(Quick tour of Jupyter/iPython Notebooks)
介紹Jupyter。
【參考】
[1]Coursera深度學習教程中文筆記
【DeepLearning學習筆記】Coursera課程《Neural Networks and Deep Learning》——Week2 Neural Networks Basics課堂筆記