學習筆記 | 吳恩達之神經網路和深度學習

機器學習

機器學習研究的是計算機怎樣模擬人類的學習行為,以獲取新的知識或技能,並重新組織已有的知識結構使之不斷改善自身。簡單的說,就是計算機從數據中學習規律和模式,以應用在新數據上做預測的任務。

深度學習概念

深度學習指的是訓練神經網路,有時候規模很大。

線性回歸

回歸函數,例如在最簡單的房價預測中,我們有幾套房屋的面積以及最後的價格,根據這些數據來預測另外的面積的房屋的價格,根據回歸預測,在以房屋面積為輸入x,輸出為價格的坐標軸上,做一條直線最符合這幾個點的函數,將它作為根據面積預測價格的根據,這條線就是回歸線,對應回歸函數。當然也應該有限制條件,價格總不能為0,所以直線不合適,在最初的階段,應該是為0的橫線,這樣的經過修改的更符合實際的就可以作為判斷依據了。這是一個最簡單的神經網路了,只經過了一層也就是一個神經元,從輸入面積,經過神經元到輸出價格。

線性回歸-回歸線

在很多神經網路的文獻中,都會看到這樣的函數,函數一開始是0,然後就是一條直線,這個函數就被稱為ReLU函數:修正線性單元函數(rectified linear unit)。「修正」指的是取不小於0的值,這是一個單神經元網路,規模很小的神經網路,大一點的神經網路是把這些單個神經元堆疊起來形成的,形成一個更大的神經網路。

假設現在預測房價不只是考慮大小,還有卧室數量,郵編,以及富裕程度,根據這是個條件來預測價格呢,那麼就會形成一個更大的神經網路,如圖:

房價預測神經網路

在圖中,x為4個特徵輸入,y為輸出結果房價,中間的為隱藏層,其中第一層挨著x輸入的為輸入層,每個神經元都與4個輸入特徵有聯繫,把這些獨立的神經單元堆疊起來,簡單的預測器(神經元)形成一個更大的。神經網路的一部分神奇之處在於,當你實現了它之後,你要做的只是輸入x,就能得到輸出,不管訓練集有多大,所有的中間過程,都會自己完成。要做的就是,這有四個輸入的神經網路,輸入的特徵可能是面積,大小等等,已知這些輸入的特徵,神經網路的工作就是預測對應的價格。

輸入層的連接數最高,因為每個輸入都連接到了中間的每個圓圈。神經網路只要你給足夠多的數據關於x和y的數據,給到足夠多的訓練數據,神經網路非常擅長於計算從x到y的精準映射函數。

神經網路給了輸入以及輸出的訓練數據,是一種監督學習。

幾乎所有由神經網路創造的經濟價值,都基於其中一種機器學習,我們稱之為監督學習(supervisor learning)。在監督學習中,輸入x,習得一個函數,映射到輸出y。這裡有一些其他例子,這些例子中神經網路效果超群。

也許通過深度學習獲利最大的就是在線廣告,給網站輸入廣告信息,網站會考慮是否給你看這個廣告,有時還需要輸入一些用戶信息。神經網路在預測你是否會點擊這個廣告方面,已經表現的很好,通過向你展示,向用戶展示最有可能點開的廣告。

計算機視覺,你輸入一個圖像,然後想輸出一個指數,可以是從1到1000來表明這些照片是1000個不同的圖像中的某一個,可以用來給照片打標籤。

語音識別,你可以把一段音頻輸入神經網路,可以輸出文本。機器翻譯進步也很大,輸入英語句子,直接輸出一個中文句子。在無人駕駛技術中,你輸入一副圖像,汽車前方的一個快照,還有一些雷達信息,基於這個,訓練過的神經網路能告訴你路上其他汽車的位置,這是無人駕駛系統的關鍵組件。要機智的選擇x和y,才能解決特定問題,然後把這個監督學習過的組件嵌入到更大型的系統中,比如無人駕駛。

可以看出稍微不同的神經網路應用到不同的地方,都行之有效。圖像領域裡:我們經常用的是卷積神經網路(cnn)。對於序列數據,例如音頻中含有時間成分,音頻是隨著時間播放的,所以音頻很自然的被表示為以為時間序列。對於序列數據,經常使用循環神經網路(RNN)- recurrent neural network。

語言最自然的表示方式也是序列數據,語言,漢語,英語,單詞和字母都是按序列出現的,更複雜的RNN經常會用於這些應用。對於更複雜的無人駕駛,輸入一張快照,需要使用卷積神經網路架構去處理,雷達信息更不一樣,可能需要更複雜的混合的神經網路結構。

結構化數據和非結構化數據

所以,為了更好的說明標準的CNN和RNN結構,所以在文獻中,你可以看到標準神經網路和卷積神經網路,卷積網路通常用於圖像處理。循環神經網路很好的處理一維時間序列數據,其中包含時間成分。機器學習還可能運用於結構化數據和非結構化數據。

  • 結構化數據:數據的資料庫,比如在房價預測中,你可能有一個資料庫或者數據列,告訴你房間的大小和卧室的數量,這就是結構化數據,意味著每個特徵都有著清晰的定義。
  • 非結構化數據:比如音頻,原始音頻,圖像。你要表示圖像或文本中的內容,這裡的特徵,可能是圖像中的像素值,或者文本中的每個單詞。非結構化數據相對於結構化數據,計算機理解起來更難。

神經網路應用

神經網路的發展,就是讓計算機更好的能理解非結構化數據。語音識別,圖像識別,自然語言文本處理。神經網路在非結構化數據上的成功,主要是媒體領域。神經網路在很多短期經濟價值的創造是基於結構化數據的,比如更好的廣告系統,更好的獲利建議,有更好的能力去處理很多公司擁有的海量資料庫,並且這些數據有準確的預測未來的能力。

監督學習神經網路應用領域:

監督學習神經網路應用領域

三種神經網路:標準神經網路 卷積神經網路 循環神經網路

三種神經網路:標準神經網路 卷積神經網路 循環神經網路

神經網路流行原因

神經網路發展的原因

如上圖: 在前面數據量很小的時候,演算法之間性能差異不是很明顯。到了大數據段,神經網路的作用就很明顯了。

訓練出一個很好的神經網路,重要的條件是:

  • 規模大的數據量m(橫軸)
  • 以及很好的計算訓練能力。

規模促進了機器學習

關於機器學習發展,重要的原因,海量數據,計算速度,演算法改進。

關於演算法改進,許多演算法方面的創新都是為了讓神經網路運行的更快,舉一個例子,神經網路方面一個巨大的突破是,從sigmoid函數轉換到這樣的ReLU函數,使用sigmoid函數機器學習的問題是,在這個函數後部分,sigmoid函數的斜率梯度會接近0,所以學習會變得非常緩慢,因為用梯度下降法時,梯度接近0時,參數會變化的很慢,學習也會變的很慢,而通過改變激活函數,神經網路用ReLU函數(修正線性單元函數),它的梯度對於所有為正值的輸入輸出都是1,因此梯度不會逐漸趨近於0。為這裡的梯度,這條線的斜率,在這左邊是0,我們發現,只需將sigmoid函數轉換為ReLU函數便能夠使得"梯度下降法"運行的更快,這就是一個例子關於演算法創新。其目的就是增加計算速度。

2、神經網路基礎

2.1 二分分類

當你要構建一個神經網路,有些技巧是相當重要的。例如,m個樣本的訓練集,你可能會習慣性的去用一個for循環來遍歷這m個樣本,但事實上,實現一個神經網路,如果你要遍歷整個數據集,並不需要直接使用for循環。還有就是,神經網路的計算過程中中,通常有一個正向過程,或者正向傳播步驟,接著會有一個反向過程,也叫反向傳播步驟。

(1)二分分類案例:

使用logistic回歸來闡述,以便能更好的理解。

logistic回歸是一個用於二分分類的演算法。例如舉個例子,輸入一張圖像,我們要輸入y,是貓(1)或者不為貓(0)。計算機保存一張圖片,要保存三個獨立矩陣,分別對應圖片中的紅綠藍三個顏色通道,如果輸入圖片是64*64像素的,就有三個64*64的矩陣,分別對應圖片中的紅綠藍三個像素的亮度。為了方便表示,這裡用三個小矩陣5*4的來表示,要把這些像素亮度值放進一個特徵向量中,就要把這些像素值都提出來,放入一個特徵向量x。

為了把這些像素值取出來放入特徵向量,就要像下面一樣定義一個特徵向量x以表示這張圖片,我們把所有的像素值都取出來,把三層矩陣上的所有數都放入一個向量中,最後得到一個很長的特徵向量,把圖片中所有的紅綠藍像素強度值都列出來。

如果圖片是64*64的,那麼向量x的總維度為64*64*3,因為這是三個矩陣的元素數量,乘出來結果為12288,我們用Nx=12288來表示輸入特徵向量x的維度,有時候為了簡潔,我們直接用小寫的n,來表示特徵向量的維度。

案例,判斷圖片是否有貓

輸入特徵x的表示

在二分分類問題中,目標就是訓練出一個分類器,它以圖片的特徵向量x作為輸入,預計輸出的結果標籤y是1還是0,也就是說,預測圖片中是否有貓。

(2)相關符號:

後面課程中需要用到的符號。

用一對(x,y)來表示一個獨立的樣本,x是Nx維的特徵向量,標籤y: 值為1或0。訓練集由m個訓練樣本構成,(x^(1),y^(1))表示樣本一的輸入和輸出,(x^(2),y^(2))表示樣本二的輸入和輸出,(x^(m),y^(m))表示最後一個樣本m的輸入和輸出,{(x^(1),y^(1))....(x^(m),y^(m))}這些一起就表示整個訓練集。m表示整個訓練集的樣本數,有時候為了強調這是訓練樣本的個數,可以寫作m=m_train,當說到測試集時,可以用m_test來表示測試集的樣本數。最後用更緊湊的符號表示訓練集,定義一個矩陣,用大寫X來表示,它由訓練集中的x1,x2這些組成,像這樣寫成矩陣的列。

每個訓練集x^(1)..x^(m)則分別為這個矩陣的1到m列,所以這個矩陣有m列,m是訓練集的樣本數,這個矩陣的高度記為Nx。要注意的是,有時候X的定義,訓練數據作為行向量堆疊,而不是這樣的列向量堆疊。但是構建神經網路時,用列向量堆疊這個約定形式,會讓構建過程簡單的多。

總結一下,X是一個Nx*m的矩陣,當用python實現時,會看到X.shape,這是一條python命令,用來輸出矩陣的維度,即(nx,m),表示X是一個nx*m的矩陣,這就是如何將訓練樣本,即輸入x用矩陣表示,那輸出標籤y呢,同樣為了簡單的構建一個神經網路,將y標籤也放入列中,Y = [y^(1),y^(2),...y^(m)],這裡的Y是一個1*m的矩陣,同樣的,在python裡面,Y.shape等於(1,m)。

後面的課程中你會發現,好的習慣符號能夠將不同訓練樣本的數據聯繫起來,這裡說的數據,不僅有x和y還會有其他的量。將不同的訓練樣本數據取出來,放到不同的列上,就像剛才處理x和y一樣。在logistic回歸和神經網路,要用到的符號就是這些了。

符號表示

2.2 logistc回歸

這是一個學習演算法,用在監督學習中,輸出y標籤是0或1時,這是一個二分分類的問題。已知輸入特徵向量是x,可能是一張圖,你希望把識別出這是不是貓圖,你需要一個演算法,可以給出一個預測值,y hat(y帽子),就是你對y的預測。

更正式的說,你希望y hat是一個概率,當輸入特徵滿足條件時,y就是1,所以換句話說,如果x是圖片,例如之前判讀圖片是否有貓的例子,你希望y是一張圖片是貓圖的概率,x是一個nx維的向量,已知logistic回歸的參數是w,也是一個nx維向量,而b就是一個實數,所以已知輸入x和參數w和b,如何計算y hat。

也許可以嘗試,y = w^T * x + b, 一個輸入x的線性函數,事實上,如果你做線性回歸,就是這麼算的,但這並不是一個非常好的二元分類演算法,因為你希望y hat 是y=1的概率,而不是計算y的值,所以y hat應該介於0到1之間,但實際上很難實現,因為w^T*x+b可能比1大得多,甚至是負值,這樣的概率是沒有意義的。所以在logistc回歸中,我們把輸出變成y hat等於sigmoid函數作用到這個量y上,這就是sigmoid函數的圖形,橫軸是Z,函數是從0到1的光滑函數,與數軸的交點在0.5處,這就是sigmoid(z)的圖形,用z來表示w^T * x + b這個量。

sigmoid函數公式

sigmoid函數圖像

要注意的是,如果z非常大,那麼e^(-z)就很接近0,那麼sigmoid(z)就是1/(1+某個接近0的量),所以這就接近1。相反如果z非常小,或者是非常大的負數,那麼sigmoid(z)就很接近於0。

所以當你要實現logistic回歸時,你要做的是學習參數w和b,這樣y hat 就變成了比較好的估計。對y=1的比較好的估計。當我們對神經網路編程時,我們通常會把w和b分開,這裡b對應一個攔截器,在其他課程中,可能看到過不同的表示。

在一些符號約定中,定義一個額外的特徵向量x0並且等於1,所以出現x就是R^(nx+1)維向量,然後將y hat定義為σ(θ^T*X),在這種符號定義中,出現了一個向量參數θ,這個θ是由向量[θ0,θ1,θ2...θnx]組成的列向量。所以θ0就是扮演的b的角色,這是一個實數,而θ1直到θnx的作用和w一樣。事實上,當你實現你的神經網路時,將b和w看成獨立的參數可能更好。

logistic回歸模型與參數,以及sigmoid函數

參考資料1

參考資料2

2.3 logistc 回歸損失函數

為了訓練logistic回歸模型的參數w和b,需要定義一個成本函數。為了讓模型通過學習調整參數要給一個m個樣本的訓練集,通過訓練集,找到參數b和w,來得到你的輸出,在訓練集上得到預測值,是預測值y hat(i) 更接近於訓練集得到的y^(i)。

為了讓上面的方程更詳細一點,需要說明上面這裡定義的y hat(即y頭上有個^這個符號),是對一個訓練樣本x來說的,當然是對每個訓練樣本,我們使用這些帶有圓角的符號方便引用說明,來區分樣本。你的訓練樣本(i),對應的預測值為通過sigmoid函數獲得的y hat^(i),通過函數作用到W^T*x(i)+b得到的,所以符號約定就是這個上標(i)來指明數據。

loss function: 損失函數,也叫作誤差函數,他們可以用來衡量演算法的運行情況,你可以定義損失為y hat和真的類標y的差平方的一半,但通常在logistic回歸中,大家都不這麼做,因為當你學習這些參數時候,會發現優化問題會變成非凸的,最後會得到很多個局部最優解,所以梯度下降法(gradient descent)可能找不到全局最優值。我們通過定義損失函數L來衡量你的預測輸出值y hat和y的實際值有多接近。誤差平方看起來似乎是一個合理的選擇,但是用這個的話,梯度下降法就不會很有效。在logistic回歸中,我們會定義一個不同的損失函數,它有著與誤差平方相似的作用,這會給我們一個凸的優化問題,就很容易去做優化。在logistic回歸中,我們用到的損失函數如下:

logstic回歸中用到的損失函數L

直觀地看看為什麼這個損失函數能起作用,假設我們使用誤差平方越小越好,對於這個logistic回歸的損失函數,同樣的我們也讓它儘可能的小。

損失函數L

  • 當y=1時,我們要使y hat儘可能大,但這是由sigmoid函數估計出來的值,不會超過1,所以要儘可能接近1
  • 當y=0時,要使y hat儘可能小,但是不會小於0,所以儘可能接近0。

損失函數是在單個訓練樣本中定義的,它衡量了在單個訓練樣本中的表現。接下來要定義一個成本函數cost function,它衡量的是在全體訓練樣本上的表現,這個成本函數J根據之前得到的兩個參數w和b,J(w,b)

即所有訓練樣本的損失函數和。而y hat是用一組特定的參數w,b通過logistc回歸演算法得出的預測輸出值。從而成本函數把L帶入得到以下形式:

成本函數J(w,b)最終形式

損失函數只適用於像這樣的單個訓練樣本,而成本函數基於參數的總成本,所以在訓練logistic回歸模型時,我們要找到合適的參數w和b,讓這裡的成本函數J儘可能的小。

綜上所述,可以得到logistic回歸可以被看成一個非常小的神經網路。

2.4 梯度下降法

如何使用梯度下降法來訓練或學習訓練集上的參數w和b,成本函數只能用來說明你選擇的w和b作用效果好不好,最後誤差大不大,參數w和b在訓練集上的效果,但是我們不知道一個w和b的關係,如何取值,發現一組不好後如何確定下一組取值,這就需要梯度下降法來研究如何訓練w和b使得成本函數最小。

梯度下降法

這個圖中的橫軸表示空間參數w和b,在實踐中,w可以是更高維的,但為了方便繪圖,我們讓w是一個實數,b也是一個實數,成本函數J(w,b)是在水平軸上w和b上的曲面,曲面的高度J(w,b)在某一點的值,我們想要做的就是找到這樣的w和b使其對應的成本函數J值,是最小值。

可以看出,成本函數J是一個凸函數,和非凸的函數不一樣,非凸函數有很多的局部最優,因此,我們使用為凸函數的J(w,b),凸函數的這個性質是我們為啥使用這個特定的成本函數J做logistic回歸一個很重要的原因。為了找到更好的參數值,我們要做的就是用某初始值,初始化w和b。對於logistic回歸而言,幾乎是任意的初始化方法都有效,通常用0來初始化,但人們一般不這麼做,但是因為函數是凸的,無論在哪裡初始化,都應該到達到同一點,或大致相同的點。

梯度下降法所做的就是,從初始點開始,朝最抖的下坡方向走一步,在梯度下降一步後,也許就會停在碗的最低端,因為它試著沿著最快下降的方向往下走,這是梯度下降的一次迭代。兩次迭代可能就到了最低點,或者需要更多次,隱藏在圖上的曲線中,很有希望收斂到這個全局最優解,或接近全局最優解,這張圖片闡述了梯度下降法。

梯度下降法中參數更新規則

為了更好的說明如何學習w和b,我們將圖像的橫軸只有w,變成二維坐標軸,圖像如左圖,右圖的公式代表w如何變化的公式。

α表示學習率,學習率可以控制每一次迭代,或者梯度下降法中的步長,後面會討論,如何選擇學習率α;其次d(J(w))/dw這個數是導數(derivative),這就是對參數w的更新或者說變化量。

當我們開始編寫代碼,來實現梯度下降,我們會使用到代碼中變數名的約定,dw用來表示導數,作為導數的變數名,那麼w:=w-α*dw(:=代表變化取值),現在我們確保梯度下降法中更新是有用的。

記住導數的定義,是函數在這個點的斜率。對於一開始就很大的參數w來說,每更新一次就會向左移動,向最小值點更靠近,同樣的,假設w很小,在最小值的左邊,那麼斜率為負值,每次迭代就是w加上一個數,也會逐步的向最小值的w0靠近。

我們想知道,用目前參數的情況下函數的斜率朝下降速度最快的方向走。我們知道,為了讓成本函數J走下坡路,下一步更新的方向在哪。當前J(w)的梯度下降法只有參數w,在logistic回歸中,你的成本函數是一個含有w和b的函數,在這種情況下,梯度下降的內循環就是這裡的這個東西,你必須重複的計算,通過w:=w-α*dw【d(j(w,b)/dw)】更新w,通過b:=b-α*d(J(w,b))/db更新b。

這兩個公式是實際更新w和b時所作的操作。當然這裡的符號d微分也可以是偏導數花哨的α,表示的是函數在w方向的斜率是多小,當函數有兩個以上的變數時,應該使用偏導數符號,計算函數關於其中一個變數的在對應點所對應的斜率。

2.5 導數2.6 更多關於導數的例子

這兩節主要就是關於微積分,導數的內容,通過舉例f(x)=3x, f(x)=x^2, f(x)=x^3來說明函數上某一點的導數其實就是在該點的斜率,直線上點的斜率處處相等,但是對於其他非直線的函數,每個點的斜率即導數可能都不相同。

對於f(x)=x^2, f(x)=2x, 當x=2時,f(2)=4, 將x向後移動一點點,比如x=2.001,對應的f(x)=8.004002 約為8.004,發現f(x)增量與x的增量相比,結果為8.004/2.001=4=2*2,這裡f(x)我們只是近似的等於4倍,實際上,按照導數的定義,是在x點出,增加一個無窮小量,0.001的增量很明顯不能表示無窮小,因此在該點處的切線的斜率就為2x。

2.7 計算圖

可以說,一個神經網路的計算都是按照前向或者反向傳播過程來實現的,首先計算出,神經網路的輸出,緊接著進行一個反向傳播過程,後者我們用來計算出對應的梯度或者導數,以下流程圖解釋了,為什麼要用這樣的方式實現。

為了闡明這個計算過程,舉一個比logistc回歸更加簡單的、不那麼正式的神經網路的例子。我們嘗試計算函數J,它是關於三個變數a,b,c的函數

J(a,b,c) = 3(a+b*c)

計算這個函數,實際上有三個不同的步驟:

  1. 計算b*c,存在在變數u中,u=b*c
  2. 計算a+u,存在變數v中,v=a+u
  3. 最後輸出J,J=3*v

    我們可以把這個計算過程劃成如下的流程圖:

`J(a,b,c)=3(a+b*c)` 計算流程圖

這種流程圖用起來很方便,有不同的或者一些特殊的輸出變數時,比如我們想要優化的J。在logistc回歸中,J是想要最小化的成本函數,可以看出,通過一個從左到右的過程,你可以計算出J的值,計算導數就是一個從右到左的過程,剛好與從左到右傳播的過程相反。

2.8 計算圖的導數計算

在使用計算圖計算出J的值後,我們研究如果通過計算圖計算出函數J的導數,在上個例子中,要計算J對v的導數,怎麼做?

在反向傳播術中,我們看到如果你想計算最後輸出變數的導數,使用你最關心的變數對v的導數,dJ/dv=3, 那麼我們就做完了一步反向傳播。

那麼關於dJ/da呢,也就是說改變a,對J的數值有什麼影響呢

a=5時,v為11,J為33 a=5.001時,v為11.001,J為33.003

那麼增加a,如果你把這個5換成某個新值,那麼a的改變就會傳播到流程圖的最右,所以J最後是33.001。所以J的增量是3乘以a的增量,意味著這個導數是3。換句話說,如果你改變了a,那麼就改變了v,通過改變v,又會改變J,所以J值的凈變化量,當你提升這個值,當你把a值提高一點點,這就是J的凈變化量。關於a影響v,v影響J,這在微積分里叫做鏈式法則,所以

dJ/da = dJ/dv * dv/da

這樣我們就完成了另一步反向傳播。

介紹一個新的符號約定,當你編程實現反向傳播時,通常會有一個最終輸出值是你要關心的,最終的輸出變數,你真正想要關心的或者說優化的。在例子中,最終的輸出變數是J,就是流程圖的最後一個符號,所以有很多的計算嘗試計算輸出變數的導數,所以d輸出變數對於某個變數的導數,我們就用d var命名。

因為在python代碼里,可以用d(finalvar)/dvar,例如dJ/dvar,但是在這個反向傳播過程中,我們都是在對最終變數求它的導數,因此就用dvar來表示這個整體,所以在編程時候,我們就用d var來表示J關於對代碼中各種中間量的導數。

2.9 logistic回歸中的梯度下降法

在本節我們將討論如何計算偏導數來實現logistic回歸的梯度下降法,它的核心關鍵點,其中有幾個重要的公式用來實現logistc回歸的梯度下降法,將使用導數流程圖來計算梯度。使用計算圖來計算logistc回歸的梯度有點大材小用了。

回想一下logistc回歸的公式,y hat或者說a的定義是預測出來的值,是logostic回歸的輸出;y是樣本的基本真值即標籤值,並且a=sigmoid(z),而z=w^T+b,現在只考慮單個樣本的情況,關於該樣本的損失函數如第三個公式,L(a,y)是關於y和y hat的函數,實質是w和b。現在寫出該樣本的偏導數流程圖如圖2。

logistc回歸關於單個樣本的公式集 圖1

假設樣本只有兩個,特徵值x1,x2,為了計算z,我們需要輸入參數w1,w2,b,還有樣本特徵值x1,x2,這幾個都是用來計算z的,z=x1*w1+x2*w2+b,接下來再計算a即y hat,a=sigmoid(z),最後計算L(a,y)。

logistc回歸中的計算圖 圖2

因此我們在logistc回歸中,需要做的就是變換參數w和b的值來最小化損失函數,在前面我們已經經過前向傳播步驟在單個訓練樣本上,計算損失函數,接下來討論,如何向後傳播來計算偏導數,其實就是根據鏈式求導法則,把每一個變數的偏導數求出來。

偏導數求出來如下:

da = dL/da = -y/a + (1-y)/(1-a)

dz = dL/dz = dL/da * da/dz = a - y

這裡需要說明,關於 a=sigmoid(z),如何求da/dz,按正常求導法則得到,求導結果為

e^(-z) * 1/(1+e^(-z)), 發現與原始函數相同,將a帶入,因此最後結果,關於sigmid函數求導結果為a(1-a),即da/dz=a(1-a) 向後傳播的最後一步,就是看w和b怎麼變化

dw1 = dL/dw1 = dL/dz * dz/dw1 = x1 * dz

dw2 = dL/dw2 = dL/dz * dz/dw2 = x2 * dz db = dz

單個樣本logistc回歸反向傳播手寫圖

因此這就是關於單個樣本的梯度下降法,你所需要做的就是這些事情,使用這個公式計算dz,dw1,dw2,db,然後更新w1為 w1=w1-dw1*α(學習率),類似的,更新w2,更新b為b=b-db*α, 這就是單個樣本實例的一次梯度更新步驟。

現在你知道了怎麼計算導數,並且實現了單個訓練樣本的logistc回歸的梯度下降法,但是訓練logistc回歸模型,不僅僅只有一個訓練樣本,而是有m個訓練樣本的整個訓練集。下一節將講這些想法如何應用到整個訓練樣本集中,而不僅僅只是單個樣本。

2.10 m個樣本的梯度下降

在之前的視頻中,已經看到如何計算導數和把梯度下降法應用到logistc回歸的一個訓練樣本上,現在我們想要把他應用在m個訓練樣本上 。首先,時刻記住有關於成本函數J(w,b)的定義,它是這樣的一個平均值,m個樣本的損失求和的平均值。

成本函數 & 輸出預測值y冒

上述公式說明

全局梯度值是樣本梯度值的平均數

總演算法

總演算法-2

這裡就是循環的結束了,最終對所有的m個樣本都進行了這個計算,還需要除以m,因為我們計算平均值。

沒有上標-全局 & 有上標-單個樣本

梯度下降更新參數

logistc回歸 導數計算

這些公式只應用了一次梯度下降法,因此你需要重複以上的內容很多次,以應用多次梯度下降,因為整個過程執行一次,說明在梯度下降J圖中,參數得到了一次更新,但是要到達最低點,還要繼續更新,而更新的內容來自於,將更新後的帶回原公式中進行計算,z的值,a的值都會改變,此時J的值也會更新,直到最後參數不再更新,J到達最低點。

雖然細節看起來有點複雜,但是在編程的過程中,思路都會變得很清晰,但它表明計算中有兩個缺點:

  • 當應用在這裡的時候,就是說引用此方法到logistc回歸你需要編寫兩個for循環,第一個for循環是遍歷m個訓練樣本的小循環,第二個for循環是遍歷所有特徵的for循環。

for循環

當你應用深度學習演算法,你會發現在代碼中顯示的使用for循環會使演算法的效率很低,同時在深度學習領域,會有越來越大的數據集,所以能夠應用你的演算法完全不顯示for循環的話是很有用的,可以幫你處理更大的數據集,有一門向量化技術幫助你的代碼,擺脫這些顯示的for循環。

向量化技術有時用來加速運算,但有時候也未必能夠,但是在深度學習時代,用向量化來擺脫for循環已經變得相當重要,因為我們開始處理越來越大的數據集,你的代碼需要變得非高效。接下來將了解向量化技術,使得在logistc回歸中應用梯度下降法而不需要for循環。

2.11 向量化vectorization

什麼是向量化:

非向量化方法

所以這是一個非向量的實現方法,你會發現這是真的很慢,作為對比,

向量化方法

# _*_ coding: utf-8 _*_import nnumpy as npna = np.array([1,2,3,4])nprint ann# 完成向量化的例子 引用time模塊是為了計算兩次不同的操作花費了多少時間nimport timen#它能創建一個數組a,通過rand函數隨機得到n#用隨機值創建了一個百萬維度的數組,b為另外一個百萬大小的數組na = np.random.rand(1000000)nb = np.random.rand(1000000)n#現在tic記錄一下當前時間ntic = time.time()nc=np.dot(a,b)ntoc = time.time()print cprint (這是向量化的版本+str(1000*(toc-tic))+"ms")nnc=0tic = time.time()for i in range(1000000):n c+=a[i]*b[i]ntoc = time.time()print cprint (這是for loop的版本+str(1000*(toc-tic))+"ms")n#可以發現向量化和非向量化計算出來的結果是一樣的,但是最終花費的時間卻相差很大n

向量化效果截圖

當你正在實現深度學習演算法,使用向量化技術真的可以更快得到結果,向量化之後,運行速度會大幅提升,你可能聽說過,可擴展深度學習實現是在GPU上做的,GPU也叫圖像處理單元(graphics processing unit)。我們普通寫的代碼都是在cpu上運行的,cpu和gpu都有並行化的指令,有時候叫做SIMD指令,意思是指單指令流多數據流,這個詞的意思是如果你使用了這樣的內置函數,如np.function或者其他能讓你顯示去掉for循環的函數,這樣python的numpy能夠充分利用並行化去更快的計算,這點對gpu和cpu上面計算都是成立的。Gpu更加擅長SIMD計算,但是cpu事實上也不是太差,只是沒有gpu那麼擅長。

經驗法則是:只要有其他可能,就不要顯式使用for循環。

2.12 向量化vectorization的一些其它例子

例子

非向量化版計算:

u = np.zeros((n,1)) nnp.zeros這個函數意思是構造相應數據類型的數據,這裡是一個n維一列的向量,用0填充nfor i in ... nfor j in ...nu[i] += A[i][j]*v[j]n#這是一個雙重for循環,對指標i和jn#而向量化實現:nu = np.dot(A,v)n

(2)假設你內存中已經有一個向量v,如果你想做指數運算,作用到向量v的每一個元素,那麼非向量的做法: 先初始化一個全0向量u,然後再用for循環,對v中每一個元素做指數運算再放入u中,一次計算一個元素。但事實上numpy模塊中有很多內置函數,可以完成運算,只需要調用一個函數,u=np.exp(v)則完成了。

Numpy庫有很多向量值函數,例如np.log(v)會逐個元素計算log值,np.Abs(v)會計算絕對值,np.maximum(v,0)計算所有元素中的最大值,求出v中所有元素和0相比的最大值,v**2就是計算v中每個元素的平方,1/v就是每個元素求倒數等等。

所以每當你想寫一個for循環時,應該看看可不可以調用numpy,用內置函數計算,而不是用for循環,

接下來看看如何把這些技巧應用到logistc回歸梯度下降演算法實現中來,看看是否可以去掉兩個for循環中的一個.

logistc回歸,兩個循環簡化為一個循環過程

向量化處理方法

2.13 向量化logistc回歸

這一節我們將談及向量化是如何實現在logistc回歸上面的,這樣就能同時處理整個訓練集來實現梯度下降法的一步迭代,針對整個訓練集的一步迭代不需要使用任何顯式for循環。

省掉外部m個樣本的循環

m個樣本計算預測值

訓練輸入矩陣

z為行向量,每個值為z(i)

X是把所有的訓練樣本堆疊起來得到的,一個挨著一個,橫向堆疊,Z則是一個一維行向量,使用每個計算出來的橫向排在一起,最後為了計算Z,直接使用numpy的指令:

計算z的指令

在這裡b是一個實數,或者說是一個1*1的矩陣,就是一個普通的實數,但是當向量加上這個實數時,pyhton會自動的把b這個實數擴展成一個1*m的向量,在python中這叫做廣播。

激活函數將z轉換為A

總的來說,不需要for循環就可以從m個訓練樣本一次性計算出所有樣本的z值和a預測值,只需要運行兩行代碼就可以高效計算出結果,以上就是正向傳播一步迭代的向量化實現,同時處理m個訓練樣本,接下來你會發現使用向量化也可以高效的計算反向傳播過程,計算出梯度。

2.14 向量化logistic回歸的梯度輸出

如何使用向量化計算m個訓練數據的梯度,注意是同時計算,最後得到一個非常高效的logistc回歸的實現。

在計算梯度時,我們需要計算:

dz的計算

dz的向量化計算

特徵分量使用轉換為向量

python下·實現

使用向量計算

現在回顧之前的計算logistc回歸的整個過程,沒有向量化非常低效,而沒有使用任何for循環,最後代碼計算只需要幾步就完成了:

沒有for循環的代碼完成

dz = dL/dz = dL/da * da/dz = a - y

向後傳播的最後一步,就是看w和b怎麼變化

dw1 = dL/dw1 = dL/dz * dz/dw1 = x1 * dz db = dz

這就完成了正向傳播和反向傳播,確實實現了對所有樣本進行預測和求導,而且沒有使用任何一個for循環,然後梯度下降更新參數:

梯度下降更新參數

有了這些我們就實現了logistc回歸的梯度下降一次迭代,雖然說過盡量不要使用for循環,但是如果想要實現多次迭代,仍然需要使用for循環,在上面整個一次迭代過程中加上循環次數,應該沒有方式能把這個for循環去掉。

高度向量化的非常高效的logistc回歸的梯度下降法。

2.15 python中的廣播

廣播是一種手段,可以讓你的python代碼段執行的更快,我們將繼續深入研究python中的廣播是如何實際運作的。

# _*_ coding: utf-8 _*_import numpy as npnnA = np.array([[56.0,0.0,4.4,68.0],n [1.2,104.0,52.0,8.0],n [1.8,135.0,99.0,0.9]])print Anncal = A.sum(axis=0)# axis=0意味著豎直相加,水平軸是axis=1print calnncal2 = cal.reshape(2,2)print cal2nnpercentage = 100* A/cal.reshape(1,4)#使用矩陣A除以這個1*4的矩陣,然後得到了百分比矩陣,print percentage#這個例子就是一個廣播的例子.#在求percentage時,用3*4的矩陣除以一個1*4的矩陣,這裡在技術上使用reshape是多餘的.#但是通常使用reshape來確保參與運算的是我們想要的形狀的矩陣。#這裡是o(1)操作,成本很低。所以不要害怕使用reshape命令來確保你的矩陣形狀是你想要的。#這種運算是怎麼執行的?3*4矩陣是如何除1*4矩陣的呢。n

運行結果:

C:Anaconda2python.exe C:/Users/mtianyan/Desktop/PyCode/haha/non_fortest.pyn[[ 56. 0. 4.4 68. ]n [ 1.2 104. 52. 8. ]n [ 1.8 135. 99. 0.9]]n[ 59. 239. 155.4 76.9]n[[ 59. 239. ]n [ 155.4 76.9]]n[[ 94.91525424 0. 2.83140283 88.42652796]n [ 2.03389831 43.51464435 33.46203346 10.40312094]n [ 3.05084746 56.48535565 63.70656371 1.17035111]]n[[ 1 4 9]n [ 4 10 18]n [ 7 16 27]]nnProcess finished with exit code 0n

關於廣播,一個(m,n)矩陣和一個(1,n)矩陣相加,首先pyhton會把(1,n)的矩陣複製m次,變成(m,n)矩陣,然後兩個矩陣相加。同理,若一個(m,n)與一個(m,1),則會複製n次列向量。

廣播通用規則,對於一個(m,n)矩陣,加減乘除一個(1,n)的矩陣,後者都會複製m次變成(m,n)矩陣,同理,若為(m,1)的矩陣,則會複製n次列。注意這裡的加減乘除都是逐個元素對應操作,不是根據矩陣的乘法原則。 還有另一種廣播操作,就是一維向量與實數的操作,同理。以上是在神經網路中用到的廣播形式,還有更多參看numpy文檔。

2.16 關於python/numpy向量的說明

Python讓你能夠使用廣播運算,一般來說,pyhton numpy程序語言給你提供了很高的靈活性,這算是一門編程語言的優勢,同時也是劣勢,優勢是因為它讓語言的表現力更強,語言的靈活性很大,也就是說,你可以用一行代碼完成很多運算,弱點就是因為廣播和這麼大的靈活性,有時可能會引入非常細微的錯誤,非常奇怪的bug,如果你不熟悉所有複雜的廣播運作方式,比如你想用列向量把它加到一個橫向量上,你可能會預計它會報錯,說維度不匹配,或者類型錯誤之類的,但事實上你會得到一個行向量和一個列向量求和後的矩陣。

Python的這些奇怪的效果有其內在邏輯,但是如果你不熟悉的話,可能會產生很多奇怪的難以調試的錯誤。

# _*_ coding: utf-8 _*_nimport numpy as npnna = np.random.randn(5)n#生成5個隨機的高斯變數,存儲在數組a中,n#這創建了一個數據結構,a.shape=(5,)就可以查看到這個數據結構,n#叫做秩為1的數組,這個數據結構與行向量和列向量並不一樣n#nprint "============秩為1數組========"nprint anprint a.shapen#這就是所謂的pyhton秩為1的數組,它既不是行向量也不是列向量nprint a.Tnprint np.dot(a,a.T)nnprint "============標準列向量========"na=np.random.randn(5,1)nprint anprint a.Tnprint np.dot(a,a.T)n

秩為1數組和列向量區別:運行結果

當你進行編程聯繫時,或者實現神經網路的logistc回歸時,就不要使用這些秩為1的數組,相反每次創建數組時,你要把它定義成列向量,或者變成一個行向量,那麼你向量的行為就更容易理解一些。代碼如下:

a = np.random.randn(5) #創建了一個秩為1的數組,不使用這個na = np.random.randn(5,1) #列向量,5行1列 a.shape = (5,1)na = np.random.randn(1,5) #創建一個行向量,5行1列n

另外,當你在代碼中做了很多,不清楚一個向量具體的維度是多少,經常使用assert()這樣一個聲明,確保這是一個向量,這些assert()語句執行起來很快,也可以看成是代碼的文檔。最後,如果你因為一些原因,得到了秩為1 的數組,你可以使用reshape命令,a=a.reshape轉化成一個向量。那麼它的行為更好預測,就是列向量或者行向量的行為。

有時候會看到一些很難調試的錯誤,都來自秩為1的反直覺行為,通過消除代碼中秩為1的數組,可以讓代碼更簡單。所以在代碼中,盡量使用n1矩陣,基本上是列向量,或1n矩陣,基本是行向量,隨意插入assert()聲明,要仔細檢查你的矩陣和數組的維度。不要害怕調用reshape來保證你的數組或向量是你想要的維度。

2.17 jupyter notebook的基本使用

shift + enter 運行代碼

2.18 logistic回歸損失函數解釋

這節中,將給出關於成本函數的簡潔的證明。

公式集合:

公式集合

證明過程-1

證明過程-2

第一章的補充內容:

一點是,如果你想達到很高的性能水平,有兩個條件,一個是需要訓練一個規模足夠大的神經網路以發揮數據規模量巨大的優點,另外要達到橫軸的右端位置,需要很多的數據,因此我們經常說,規模一直在推動深度學習的進步,我們需要有一個有很多隱藏單元的神經網路。有許多的參數,許多的連接,而且還有數據規模,事實上,要在神經網路上獲得更好的表現,最可靠的手段要麼是訓練一個更大的神經網路,要麼投入更多的數據。但這只是在一定程度上,因為你的數據達到一定程度,神經網路規模太大,需要的訓練時間太久。

第二章作業筆記:

1.4標準化行

另一個在深度學習和機器學習的技巧是標準化數據,因為梯度下降收斂的更快在標準化之後,這使得有更好的性能。

標準化

1.4廣播以及softmax函數

sigmoid函數只能分兩類,而softmax能分多類,softmax是sigmoid的擴展。

Sigmoid函數對傳入的z進行運算,最後得到為0或者為1的概率。Softmax的演算法公式為

softmax函數

softmax函數公式詳細

numpy.dot()有兩種意思:

  • 參數為兩個列表時,為求兩個列表的點積,即對應相乘再加和
  • 參數為向量與矩陣或者矩陣時,則是做矩陣的乘法
  • 參數一個為m*n的矩陣,一個為n個元素的列表時,就結果為矩陣的每一行對應乘以列表,最後加和,每一行都是這樣,最後得到一個m行的列向量。

import numpy as np x1 = np.array([ [9, 2, 5], [7, 5, 0]]) x2 = [1,2,3]

toc = np.dot(x1,x2)print (str(toc))

運行結果:[28 17]

關於python的數組,列表,和矩陣之間的聯繫:

列錶轉換

x = [1,2,3,4]

x = np.array(x)

print (x.shape)

#結果則為:(4,)

但是此時np.dot(x,x)結果仍然是對應元素的乘積加和

abs()與np.abs()區別:這兩個函數的意思都是取絕對值,但是作用的對象不一樣.

  • abs()可以對實數取絕對值,數組也可以,但是對向量就不可以.
  • 而np.abs()還可以對列表進行處理。

推薦閱讀:

Python · 神經網路(二*)· 層
深度壓縮之蒸餾模型
「深度學習」和「多層神經網路」的區別?
關於深度神經網路壓縮(下)

TAG:吴恩达AndrewNg | 神经网络 |