自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)之圖像分類應(yīng)用實戰(zhàn)

譯文 精選
人工智能 深度學(xué)習(xí)
深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)是魔法,還是線性代數(shù)和微積分更有魔力?本文將通過一個圖像分類深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)的實戰(zhàn)案例來回答這個問題。

譯者 | 朱先忠?

審校 | 孫淑娟?

深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)最近受到了大量關(guān)注,原因在于它是當(dāng)今語音識別、人臉檢測、語音控制、自動駕駛汽車、腦腫瘤檢測技術(shù)背后的技術(shù),而這些在20年前并不屬于我們生活的內(nèi)容。盡管這些神經(jīng)網(wǎng)絡(luò)看起來很復(fù)雜,但它們也像人類一樣地學(xué)習(xí)——通過各種案例來進(jìn)行。只不過,神經(jīng)網(wǎng)絡(luò)是使用大量數(shù)據(jù)集進(jìn)行訓(xùn)練,并通過多個網(wǎng)絡(luò)層和多次迭代進(jìn)行優(yōu)化,以便獲得最佳的運(yùn)算結(jié)果而已。?

在過去20年中,計算能力和數(shù)據(jù)量的指數(shù)級增長為深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)創(chuàng)造了完美的發(fā)展條件。盡管我們在機(jī)器學(xué)習(xí)和人工智能等華而不實的術(shù)語上磕磕絆絆;但其實,這些技術(shù)只不過是線性代數(shù)和微積分與計算的結(jié)合結(jié)果罷了。?

Keras、PyTorch和TensorFlow等框架有助于定制深度神經(jīng)網(wǎng)絡(luò)的艱難構(gòu)建、訓(xùn)練、驗證和部署過程。在現(xiàn)實生活中,創(chuàng)建深度學(xué)習(xí)應(yīng)用程序時,這幾款框架顯然成為首選。?

盡管如此,有時后退一步繼續(xù)前進(jìn)是至關(guān)重要的,我的意思是真正理解框架幕后發(fā)生的事情。在本文中,我們將通過僅使用NumPy這個基礎(chǔ)框架來創(chuàng)建一個深度神經(jīng)網(wǎng)絡(luò)并將其應(yīng)用于圖像分類問題來實現(xiàn)這一點。在計算過程中,你可能會迷失在某個地方,特別是在與微積分相關(guān)的反向傳播環(huán)節(jié),但不要擔(dān)心。在框架處理過程中,對過程的直覺比計算更重要。?

在本文中,我們將構(gòu)建一個圖像分類(貓或無貓)神經(jīng)網(wǎng)絡(luò),該網(wǎng)絡(luò)將使用兩組共1652張圖像進(jìn)行訓(xùn)練。其中,852張圖像來自??狗和貓圖像數(shù)據(jù)集??的貓圖像,另外800張來自Unsplash隨機(jī)圖像集的??隨機(jī)圖像??。開始時,首先需要將圖像轉(zhuǎn)換為數(shù)組,我們將通過將原始尺寸減小到128x128像素來加快計算速度,因為如果我們保持原始形狀,則需要很長時間來訓(xùn)練模型。所有這些128x128圖像都有三個顏色層(紅色、綠色和藍(lán)色);當(dāng)混合時,這些顏色會達(dá)到圖像的原始顏色。每幅圖像上的128x128像素中的每一個像素都具有從0到255的紅色、綠色和藍(lán)色值范圍,這些值是我們圖像矢量中的值。因此,在我們的計算中,我們將處理1652幅圖像的共計128x128x3個矢量。

要在網(wǎng)絡(luò)中運(yùn)行上述矢量,需要通過將三層顏色堆疊成單個數(shù)組來重構(gòu)它,如下圖所示。然后,我們將獲得一個(49152,1652)大小的向量,該向量將通過使用1323個圖像向量來訓(xùn)練模型,并通過使用訓(xùn)練后的模型預(yù)測圖像分類(貓或無貓)來對其進(jìn)行測試。在將這些預(yù)測與圖像的真實分類標(biāo)簽進(jìn)行比較之后,將有可能估計模型的準(zhǔn)確性。?

圖像1將圖像轉(zhuǎn)換為矢量的過程?

隨著訓(xùn)練向量的解釋,現(xiàn)在是討論網(wǎng)絡(luò)架構(gòu)的時候了,如圖2所示。由于訓(xùn)練向量中使用了49152個值,模型的輸入層必須具有相同數(shù)量的節(jié)點(或神經(jīng)元)。然后,在輸出層之前有三個隱藏層,這將是該圖片中貓的概率。在現(xiàn)實生活中的模型中,通常有3個以上的隱藏層,因為網(wǎng)絡(luò)需要更深入才能在大數(shù)據(jù)環(huán)境中表現(xiàn)良好。?

在本文中,我們僅使用了三個隱藏層,這是因為它們對于簡單的分類模型來說已經(jīng)足夠好。盡管該架構(gòu)只有4層(輸出層不計算在內(nèi)),但該代碼可以通過使用層的維度作為訓(xùn)練函數(shù)的參數(shù)來創(chuàng)建更深層的神經(jīng)網(wǎng)絡(luò)。?

圖2網(wǎng)絡(luò)架構(gòu)?

到現(xiàn)在為止,我們已經(jīng)解釋了圖像向量和所采用的網(wǎng)絡(luò)架構(gòu);接下來,我們將使用優(yōu)化算法在圖3所示的梯度下降算法中進(jìn)行描述。同樣,如果您不能立即完成所有步驟,請不要擔(dān)心,因為本文稍后將在編碼部分詳細(xì)介紹其圖中所示的每個步驟。?

圖3訓(xùn)練過程?

首先,我們啟動網(wǎng)絡(luò)的參數(shù)。這些參數(shù)是圖像2中顯示的節(jié)點的每個連接的權(quán)重(w)和偏差(b)。在代碼中,更容易理解每個權(quán)重和偏差參數(shù)的工作方式以及它們的初始化方式。稍后,當(dāng)這些參數(shù)初始化后,是時候運(yùn)行正向傳播塊并在最后一次激活中應(yīng)用sigmoid函數(shù)以獲得概率預(yù)測。?

在我們的例子中,這是一只貓出現(xiàn)在照片中的概率。隨后,我們通過交叉熵成本(一種廣泛用于優(yōu)化分類模型的損失函數(shù))將我們的預(yù)測與圖像的真實標(biāo)簽(貓或非貓)進(jìn)行比較。最后,在計算出成本的情況下,我們通過反向傳播模塊將其返回,以計算其相對于參數(shù)w和b的梯度。隨著損失函數(shù)相對于w和b所具有的梯度已經(jīng)為我們所掌握,可以通過對各個梯度求和來更新參數(shù),因為它們指向使損失函數(shù)最小化的w和b值的方向。?

由于目標(biāo)是使損失函數(shù)最小化,所以該循環(huán)應(yīng)該經(jīng)過預(yù)定義的迭代次數(shù),朝著損失函數(shù)的最小值邁出一小步。在某一點上,參數(shù)將停止改變,因為當(dāng)最小值接近時,梯度會趨于零。?

1. 加載數(shù)據(jù)?

import numpy as np
import pandas as pd
import os

from os.path import join
from tensorflow.keras.preprocessing.image import load_img, img_to_array
from sklearn.model_selection import train_test_split

首先,需要加載庫。除了使用keras.preprrocessing.image將圖像轉(zhuǎn)換為向量之外,只需要導(dǎo)入Numpy、Pandas和OS三個庫模塊,而另一方面我們使用sklearn.model_selection將圖像向量拆分為訓(xùn)練向量和測試向量兩部分。

cats_dir = "data\\cats"
all_cats_path = [join(cats_dir,filename) for filename in os.listdir(cats_dir)]

images_dir = "data\\random_images"
images_path = [join(images_dir,filename) for filename in os.listdir(images_dir)]

all_paths = all_cats_path + images_path

df = pd.DataFrame({
'path': all_paths,
'is_cat': [1 if path in all_cats_path else 0 for path in all_paths] })

數(shù)據(jù)必須從兩個文件夾下加載:cats和random_images。這可以通過獲取所有文件名并構(gòu)建每個文件的路徑來完成。然后,只需合并數(shù)據(jù)幀中的所有文件路徑,并創(chuàng)建一個條件列“is_cat”。如果該路徑位于cats文件夾中,則值為1;否則值為0。?

X = df.path
Y = df.is_cat

X_train, X_test, y_train, y_test = train_test_split(X,Y, test_size=.2 , shuffle= True)

X_train = [load_img(img_path,target_size=(128,128)) for img_path in X_train]
X_train = np.array([img_to_array(img) for img in X_train])

X_test = [load_img(img_path,target_size=(128,128)) for img_path in X_test]
X_test = np.array([img_to_array(img) for img in X_test])

(X_train.shape,X_test.shape)

有了路徑數(shù)據(jù)集,是時候通過分割圖像來構(gòu)建我們的訓(xùn)練和測試向量了;其中80%用于訓(xùn)練,20%用于測試。Y表示特征的真實標(biāo)簽,而X表示圖像的RGB值,因此X被定義為數(shù)據(jù)幀中具有圖像文件路徑的列,然后使用load_img函數(shù)加載它們,target_size設(shè)置為128x128像素,以便實現(xiàn)更快的計算。最后,使用img_to_array函數(shù)將圖像轉(zhuǎn)換為數(shù)組。這些是X_train和X_test向量的形狀:?

4X_train和X_test向量的形狀?

2. 初始化參數(shù)?

def initialize(layers_dimensions):

parameters = {}
L = len(layers_dimensions)

for l in range (1,L):
parameters['w' + str(l)] = np.random.randn(layers_dimensions[l],layers_dimensions[l-1]) / np.sqrt(layers_dimensions[l-1])
parameters['b' + str(l)] = np.zeros((layers_dimensions[l],1))

return parameters

由于線性函數(shù)是z=w*x+b并且網(wǎng)絡(luò)具有4個層,所以要初始化的參數(shù)向量是w1、w2、w3、w4、b1、b2、b3和b4。在代碼中,這是通過在層維度列表的長度上循環(huán)來完成的——稍后將定義;但是在這里它是一個硬編碼列表,其中包含網(wǎng)絡(luò)中每個層中的神經(jīng)元數(shù)量。?

參數(shù)w和b必須使用不同的初始化方式:w必須初始化為隨機(jī)小數(shù)字矩陣,b必須初始化為零矩陣。這是因為如果我們將權(quán)重初始化為零,則權(quán)重wrt(相對于)損失函數(shù)的導(dǎo)數(shù)將全部相同,因此后續(xù)迭代中的值將始終相同,隱藏層將全部對稱,導(dǎo)致神經(jīng)元只學(xué)習(xí)相同的幾個特征。因此,我們把權(quán)重初始化為隨機(jī)數(shù),以打破這種對稱性,從而允許神經(jīng)元學(xué)習(xí)不同的特征。需要注意的是,偏置可以初始化為零,因為對稱性已經(jīng)被權(quán)重打破,并且神經(jīng)元中的值都將不同。?

最后,為了理解參數(shù)向量初始化時定義的形狀,必須知道權(quán)重參與矩陣乘法,而偏差參與矩陣和運(yùn)算(還記得z1=w1*x+b1嗎?)。得益于Python廣播技術(shù),可以使用不同大小的數(shù)組進(jìn)行矩陣加法運(yùn)算。另一方面,矩陣乘法只有在形狀兼容時才可能進(jìn)行運(yùn)算,如(m,n)x(n,k)=(m,k)。這意味著,第一個陣列上的列數(shù)需要與第二個陣列上行數(shù)匹配,最終矩陣將具有陣列1的行數(shù)和陣列2的列數(shù)。圖5顯示了神經(jīng)網(wǎng)絡(luò)上使用的所有參數(shù)向量的形狀。?

圖5參數(shù)向量的形狀?

在第一層中,當(dāng)我們將w1參數(shù)向量乘以原始49152個輸入值時,我們需要w1形狀為(20,49152)*(49152,1323)=(20,1323),這是第一個隱藏層激活的形狀。b1參數(shù)與矩陣乘法的結(jié)果相加(記住z1=w1*x+b1),因此我們可以將(20,1)數(shù)組添加到乘法的(20,1323)結(jié)果中,因為廣播會自動考慮不匹配的形狀。這一邏輯繼續(xù)到下一層,因此我們可以假設(shè)w(l)形狀的公式是(節(jié)點數(shù)量層l+1,節(jié)點數(shù)量層l),而b(l)的公式為(節(jié)點數(shù)量,層l+1)。?

最后,我們對權(quán)重向量初始化進(jìn)行重要分析。我們應(yīng)該將隨機(jī)初始化值除以正在初始化w參數(shù)向量的各個層上節(jié)點數(shù)量的平方根。例如,輸入層有49152個節(jié)點,因此我們將隨機(jī)初始化的參數(shù)除以√49152,即222,而第一個隱藏層有20個節(jié)點;所以,我們將隨機(jī)初始的w2參數(shù)除以√20,即結(jié)果值為45。初始化必須保持較小,因為這是隨機(jī)梯度下降的要求。?

3. 正向傳播?

現(xiàn)在,參數(shù)向量已經(jīng)被初始化,現(xiàn)在我們可以進(jìn)行正向傳播了。該正向傳播將線性操作z=w*x+b與ReLU激活相結(jié)合,直到最后一層,當(dāng)sigmoid激活函數(shù)替代ReLU激活函數(shù)時,我們得到最后一次激活的概率。線性運(yùn)算的輸出通常用字母“z”表示,稱為預(yù)激活參數(shù)。因此,預(yù)激活參數(shù)z將是ReLU和sigmoid激活的輸入。?

在輸入層之后,給定層L上的線性操作將是z[L]=w[L]*a[L-1]+b[L],使用前一層的激活值而不是數(shù)據(jù)輸入x。線性操作和激活函數(shù)的參數(shù)都將存儲在緩存列表中,用作稍后在反向傳播塊上計算梯度的輸入。?

因此,首先定義線性正向函數(shù):?

def linear_forward(activation, weight, bias):

Z = np.dot(weight,activation) + bias

cache = (activation, weight, bias)

return Z, cache

接下來,我們來定義Sigmoid和ReLU兩個激活函數(shù)。圖6顯示了這兩個函數(shù)的圖形示意。其中,Sigmoid激活函數(shù)通常用于二類分類問題,以預(yù)測二元變量的概率。這是因為S形曲線使大多數(shù)值接近0或1。因此,我們將僅在網(wǎng)絡(luò)的最后一層使用Sigmoid激活函數(shù)來預(yù)測貓出現(xiàn)在圖片中的概率。?

另一方面,如果輸入為正,ReLU函數(shù)將直接輸出;否則,將輸出零。這是一個非常簡單的操作,因為它沒有任何指數(shù)運(yùn)算,有助于加快內(nèi)層的計算速度。此外,與tanh和sigmoid函數(shù)不同,使用ReLU作為激活函數(shù)降低了消失梯度問題的可能性。?

值得注意的是,ReLU激活函數(shù)不會同時激活所有節(jié)點,因為激活后所有負(fù)值將變?yōu)榱?。在整個網(wǎng)絡(luò)中設(shè)置一些0值很重要,因為它增加了神經(jīng)網(wǎng)絡(luò)的一個理想特性——稀疏性;這意味著網(wǎng)絡(luò)具有更好的預(yù)測能力和更少的過度擬合。畢竟,神經(jīng)元正在處理有意義的信息部分。像我們的例子中一樣,可能存在一個特定的神經(jīng)元可以識別貓耳朵;但是,如果圖像是人或風(fēng)景的話,顯然應(yīng)該將其設(shè)置為0。?

圖6Sigmoid和ReLU激活函數(shù)圖形示意?

def sigmoid(Z):
activation = 1/ (1+ np.exp(-Z))
cache = Z
return activation, cache

def relu(Z):
activation = np.maximum(0,Z)
cache = Z
return activation, cache

現(xiàn)在可以實現(xiàn)全部的激活函數(shù)了。?

def sigmoid_activation(previous_activation, weight, bias):

Z, linear_cache = linear_forward(previous_activation,weight, bias)

activation, activation_cache = sigmoid(Z)

cache = (linear_cache,activation_cache)

return activation, cache

def relu_activation(previous_activation, weight, bias):

Z, linear_cache = linear_forward(previous_activation,weight, bias)

activation, activation_cache = relu(Z)

cache = (linear_cache,activation_cache)

return activation, cache

最后,是時候根據(jù)前面預(yù)先計劃的網(wǎng)絡(luò)架構(gòu)在一個完整的函數(shù)中整合上面的激活函數(shù)了。首先,創(chuàng)建緩存列表,將第一次激活函數(shù)設(shè)置為數(shù)據(jù)輸入(訓(xùn)練向量)。由于網(wǎng)絡(luò)中存在兩個參數(shù)(w和b),因此可以將層的數(shù)量定義為參數(shù)字典長度的一半。然后,該函數(shù)在除最后一層外的所有層上循環(huán);在最后一層應(yīng)用線性前向函數(shù),隨后應(yīng)用的是ReLU激活函數(shù)。?

def l_layer_model_forward(data, parameters):

caches = []
activation = data
n_layers = len(parameters)//2

for layer in range (1,n_layers):
previous_activation = activation

activation, cache = relu_activation(previous_activation,
weight = parameters['w' + str(layer)],
bias = parameters['b' + str(layer)])
caches.append(cache)

last_activation, cache = sigmoid_activation(activation,
weight = parameters['w' + str(layer+1)],
bias = parameters['b' + str(layer+1)])
caches.append(cache)

return last_activation, caches

4. 交叉熵?fù)p失函數(shù)?

損失函數(shù)通過將預(yù)測的概率(最后一次激活的結(jié)果)與圖像的真實標(biāo)簽進(jìn)行比較來量化模型對給定數(shù)據(jù)的性能。如果網(wǎng)絡(luò)使用數(shù)據(jù)進(jìn)行學(xué)習(xí),則每次迭代后成本(損失函數(shù)的結(jié)果)必須降低。在分類問題中,交叉熵?fù)p失函數(shù)通常用于優(yōu)化目的,其公式如下圖7所示:?

圖7神經(jīng)網(wǎng)絡(luò)的成本(損失函數(shù)的輸出結(jié)果)示意圖?

在本例中,我們使用NumPy定義交叉熵成本函數(shù):?

def cross_entropy_cost(last_activation,true_label):

m = true_label.shape[1]

cost = -1/m * np.sum(np.dot(true_label,np.log(last_activation).T) + np.dot(1-true_label, np.log(1-last_activation).T))

cost = np.squeeze(cost)

return cost

5. 反向傳播?

在反向傳播模塊中,我們應(yīng)該在網(wǎng)絡(luò)上從右向左移動,計算與損失函數(shù)相關(guān)的參數(shù)梯度,以便稍后更新。就像在前向傳播模塊中一樣的順序,接下來,我們首先介紹一下線性反向傳播,然后是sigmoid和relu,最后通過一個函數(shù)整合網(wǎng)絡(luò)架構(gòu)上的所有功能。?

對于給定的層L,線性部分為z[L]=w[L]*a[L-1]+b[L]。假設(shè)我們已經(jīng)計算了導(dǎo)數(shù)dZ[L],即線性輸出的成本導(dǎo)數(shù),對應(yīng)的公式稍后很快就會給出。但首先讓我們看看下圖8中dW[L]、dA[L-1]和db[L]的導(dǎo)數(shù)公式,以便首先實現(xiàn)線性后向函數(shù)。?

圖8成本相關(guān)權(quán)重、偏差和先前激活函數(shù)的導(dǎo)數(shù)?

這些公式是交叉熵成本函數(shù)相對于權(quán)重、偏差和先前激活(a[L-1])的導(dǎo)數(shù)。本文不打算進(jìn)行導(dǎo)數(shù)計算,但它們已經(jīng)在我的另一篇文章??《走向數(shù)據(jù)科學(xué)》??一文中進(jìn)行了介紹。

定義線性向后函數(shù)需要使用dZ作為輸入,因為在反向傳播中線性部分位于sigmoid或relu向后激活函數(shù)之后。在下一段代碼中,將計算dZ,但為了在正向傳播上遵循相同的函數(shù)實現(xiàn)邏輯,首先應(yīng)用線性反向函數(shù)。?

在執(zhí)行梯度計算之前,必須從前一層加載參數(shù)權(quán)重、偏置和激活,所有這些都在線性傳播期間存儲在緩存中。參數(shù)m最初來自交叉熵成本公式,是先前激活函數(shù)的向量大小,可以通過previous_activation.shape[1]獲得。然后,可以使用NumPy實現(xiàn)梯度公式的矢量化計算。在偏置梯度中,keepdims=True和axis=1參數(shù)是必要的,因為求和需要在向量的行中進(jìn)行,并且必須保持向量的原始維度,這意味著dB將具有與dZ相同的維度。?

def linear_backward(dZ, cache):

previous_activation, weight, bias = cache
m = previous_activation.shape[1]

dw = 1/m * np.dot(dZ, previous_activation.T)
db = 1/m * np.sum(dZ, keepdims = True, axis = 1)
dpreviousactivation = np.dot(weight.T,dZ)

return dpreviousactivation, dw, db

成本wrt對線性輸出(dZ)公式的導(dǎo)數(shù)如圖9所示,其中g(shù)’(Z[L])代表激活函數(shù)的導(dǎo)數(shù)。?

圖9——線性輸出成本的導(dǎo)數(shù)。?

因此,必須首先計算Sigmoid函數(shù)和ReLU函數(shù)的導(dǎo)數(shù)。在ReLU中,如果該值為正,則導(dǎo)數(shù)為1;否則,未定義。但是,為了計算ReLU后向激活函數(shù)中的dZ,有可能只復(fù)制去激活向量(因為dactivation * 1 = dactivation),并在z為負(fù)時將dZ設(shè)置為0。對于Sigmoid函數(shù)s,其導(dǎo)數(shù)為s*(1-s),將該導(dǎo)數(shù)乘以去激活,矢量dZ在Sigmoid向后函數(shù)中實現(xiàn)。?

def relu_backward(dactivation, cache):

Z = cache
dZ = np.array(dactivation, copy=True)
dZ[Z <= 0] = 0

return dZ

def sigmoid_backward(dactivation, cache):
Z = cache

s = 1/(1+np.exp(-Z))
dZ = dactivation * s * (1-s)

return dZ

現(xiàn)在可以實現(xiàn)linear_activation_backward函數(shù)。?

def linear_activation_backward(dactivation, cache, activation):

linear_cache, activation_cache = cache

if activation == 'relu':

dZ = relu_backward(dactivation, activation_cache)
dprevious_activation, dw, db = linear_backward(dZ,linear_cache)

elif activation == 'sigmoid':

dZ = sigmoid_backward(dactivation, activation_cache)
dprevious_activation, dw, db = linear_backward(dZ,linear_cache)

return dprevious_activation, dw, db

首先,必須從緩存列表中檢索線性緩存和激活緩存。然后,對于每一次激活,首先運(yùn)行activation_backward函數(shù),獲得dZ,然后將其作為輸入,與線性緩存結(jié)合,用于linear_backward函數(shù)。最后,函數(shù)返回dW、dB和dprevious_activation梯度。請記住,這是正向傳播的逆序,因為我們在網(wǎng)絡(luò)上從右向左傳播。?

現(xiàn)在,我們可以為整個網(wǎng)絡(luò)實現(xiàn)后向傳播函數(shù)了。該函數(shù)將從最后一層L開始向后迭代所有隱藏層。因此,代碼需要計算dAL;dAL是上次激活時成本函數(shù)的導(dǎo)數(shù),以便將其用作sigmoid激活函數(shù)的linear_activation_backward函數(shù)的輸入。dAL的公式如下圖10所示?

圖10最后激活函數(shù)的成本導(dǎo)數(shù)?

現(xiàn)在,實現(xiàn)后向傳播函數(shù)的一切都設(shè)置到位。?

def l_layer_model_backward(last_activation, true_labels, caches):

gradients = {}
n_layers = len(caches)
true_labels = true_labels.reshape(last_activation.shape)

dlast_activation = -(np.divide(true_labels, last_activation) - np.divide(1 - true_labels, 1 - last_activation))

current_cache = caches[n_layers-1]
dprevious_activation, dw_temp, db_temp = linear_activation_backward(dlast_activation,current_cache,'sigmoid')
gradients["da" + str(n_layers-1)] = dprevious_activation
gradients["dw" + str(n_layers)] = dw_temp
gradients["db" + str(n_layers)] = db_temp

for layer in reversed(range(n_layers-1)):
current_cache = caches[layer]
dprevious_activation, dw_temp, db_temp = linear_activation_backward(gradients["da" + str(layer + 1)],current_cache,'relu')
gradients["da" + str(layer)] = dprevious_activation
gradients["dw" + str(layer+1)] = dw_temp
gradients["db" + str(layer+1)] = db_temp

return gradients

首先,創(chuàng)建梯度字典。網(wǎng)絡(luò)的層數(shù)是通過獲取緩存字典的長度來定義的,因為每個層在前向傳播塊期間都存儲了其線性緩存和激活緩存,因此緩存列表長度與層數(shù)相同。稍后,該函數(shù)將遍歷這些層的緩存,以檢索線性激活反向函數(shù)的輸入值。此外,真正的標(biāo)簽向量(Y_train)被重構(gòu)為與上一次激活的形狀相匹配的維度,因為這是dAL計算中一個除以另一個的要求,即代碼的下一行。?

創(chuàng)建current_cache對象并將其設(shè)置為檢索最后一層的線性緩存和激活緩存(請記住,python索引從0開始,因此最后一層是n_layers-1)。然后,到最后一層,在linear_activation_backward函數(shù)上,激活緩存將用于sigmoid_backward函數(shù),而線性緩存將作為linear_backward的輸入。最后,該函數(shù)收集函數(shù)的返回值并將它們分配給梯度字典。在dA的情況下,因為計算的梯度公式來自于先前的激活,所以有必要在索引上使用n_layer-1來分配它。在該代碼塊之后,計算網(wǎng)絡(luò)的最后一層的梯度。?

按照網(wǎng)絡(luò)的反向順序,下一步是在線性層向relu層上反向循環(huán)并計算其梯度。但是,在反向循環(huán)期間,linear_activation_backward函數(shù)必須使用“relu”參數(shù)而不是“sigmoid”,因為需要為其余層調(diào)用relu_backward函數(shù)。最后,該函數(shù)返回計算的所有層的dA、dW和dB梯度,并完成反向傳播。?

6. 參數(shù)更新?

隨著梯度的計算,我們將通過用梯度更新原始參數(shù)以向成本函數(shù)的最小值移動來結(jié)束梯度下降。?

def update_parameters(parameters, gradients, learning_rate):

parameters = parameters.copy()
n_layers = len(parameters) // 2

for layer in range (n_layers):

parameters["w" + str(layer+1)] = parameters["w" + str(layer+1)] - learning_rate * gradients["dw" + str(layer+1)]
parameters["b" + str(layer+1)] = parameters["b" + str(layer+1)] - learning_rate * gradients["db" + str(layer+1)]

return parameters

該函數(shù)通過在層上循環(huán)并將w和b參數(shù)的原始值減去學(xué)習(xí)率輸入乘以相應(yīng)的梯度來實現(xiàn)。乘以學(xué)習(xí)率是控制每次更新模型權(quán)重時響應(yīng)于估計誤差改變網(wǎng)絡(luò)參數(shù)w和b的程度的方法。?

7. 預(yù)處理矢量?

最后,我們實現(xiàn)了計算梯度下降優(yōu)化所需的所有函數(shù),從而可以對訓(xùn)練和測試向量進(jìn)行預(yù)處理,為訓(xùn)練做好準(zhǔn)備。?

初始化函數(shù)的layers_dimensions輸入必須進(jìn)行硬編碼,這是通過創(chuàng)建一個包含每個層中神經(jīng)元數(shù)量的列表來實現(xiàn)的。隨后,必須將X_train和X_test向量展平,以作為網(wǎng)絡(luò)的輸入,如圖11所示。這可以通過使用NumPy函數(shù)重構(gòu)來完成。此外,有必要將X_train和X_test值除以255,因為它們是以像素為單位的(范圍從0到255),并且將值標(biāo)準(zhǔn)化為0到1是一個很好的做法。這樣,數(shù)字會更小,計算速度更快。最后,Y_train和Y_test被轉(zhuǎn)換為數(shù)組,并被展平。?

layers_dimensions = [49152, 20, 7, 5, 1]

X_train_flatten = X_train.reshape(X_train.shape[0], -1).T
X_test_flatten = X_test.reshape(X_test.shape[0], -1).T

X_train = X_train_flatten/255.
X_test = X_test_flatten/255.

y_train = np.array(y_train)
y_test = np.array(y_test)

Y_train = y_train.reshape(-1,1).T
Y_test = y_test.reshape(-1,1).T

print(f'X train shape: {X_train.shape}')
print(f'Y train shape: {Y_train.shape}')
print(f'X test shape: {X_test.shape}')
print(f'Y test shape: {Y_test.shape}')

這些是訓(xùn)練和測試向量的最終形狀打印結(jié)果:?

圖11訓(xùn)練和測試向量的形狀大小?

8. 訓(xùn)練?

有了所有的函數(shù),只需要將它們組織成一個循環(huán)來創(chuàng)建訓(xùn)練迭代即可。?

def l_layer_model(X, Y, layers_dimensions, learning_rate = 0.0075, iterations = 3000, print_cost=False):

costs = []

parameters = initialize(layers_dimensions)

for i in range(0, iterations):

last_activation, caches = l_layer_model_forward(X, parameters)

cost = cross_entropy_cost(last_activation, Y)

gradients = l_layer_model_backward(last_activation, Y, caches)

parameters = update_parameters(parameters, gradients, learning_rate)

if print_cost and i % 50 == 0 or i == iterations - 1:
print(f"Cost after iteration {i}: {np.squeeze(cost)}")
if i % 100 == 0 or i == iterations:
costs.append(cost)

return parameters, costs

但首先,創(chuàng)建一個空列表來存儲cross_entropy_cost函數(shù)的成本輸出,并初始化參數(shù),因為這必須在迭代之前完成一次,因為這些參數(shù)將由梯度更新。?

現(xiàn)在,在輸入的迭代次數(shù)上創(chuàng)建循環(huán),以正確的順序調(diào)用實現(xiàn)的函數(shù):l_layer_model_forward、cross_entropy_cost、l_layer_mmodel_backward和update_parameters。最后,是一個每50次迭代或最后一次迭代打印一次成本的條件語句。?

調(diào)用函數(shù)2500次迭代的形式如下:?

parameters, costs = l_layer_model(X_train, Y_train, layers_dimensions, iterations = 2500, print_cost = True)?

調(diào)用訓(xùn)練函數(shù)的代碼?

下面輸出展示了成本從第一次迭代的0.69下降到最后一次迭代的0.09。?

12成本輸出值越來越小?

這意味著,在NumPy中開發(fā)的梯度下降函數(shù)優(yōu)化了訓(xùn)練過程中的參數(shù),這必將導(dǎo)致更好的預(yù)測結(jié)果,從而降低了成本。?

訓(xùn)練結(jié)束以后,接下來我們可以檢查訓(xùn)練后的模型是如何預(yù)測測試圖像標(biāo)簽的。?

9. 預(yù)測?

通過使用訓(xùn)練的參數(shù),該函數(shù)運(yùn)行X_test向量的正向傳播以獲得預(yù)測,然后將其與真標(biāo)簽向量Y_test進(jìn)行比較以返回精度。?

def predict(X, y, parameters):

m = X.shape[1]
p = np.zeros((1,m))

probs, _ = l_layer_model_forward(X, parameters)

for i in range(0, probs.shape[1]):
if probs[0,i] > 0.5:
p[0,i] = 1
else:
p[0,i] = 0

print("Accuracy: " + str(np.sum((p == y)/m)))
return p

pred_test = predict(X_test, Y_test , parameters)

13調(diào)用預(yù)測函數(shù)?

該模型在測試圖像上檢測貓的準(zhǔn)確率達(dá)到了77%??紤]到僅使用NumPy構(gòu)建網(wǎng)絡(luò),這已經(jīng)是一個相當(dāng)不錯的準(zhǔn)確性了。將新圖像添加到訓(xùn)練數(shù)據(jù)集、增加網(wǎng)絡(luò)的復(fù)雜性或使用數(shù)據(jù)增強(qiáng)技術(shù)將現(xiàn)有訓(xùn)練圖像轉(zhuǎn)換為新圖像都是提高準(zhǔn)確性的可能方案。?

最后,值得再次強(qiáng)調(diào)的是,當(dāng)我們深入數(shù)學(xué)基礎(chǔ)時,準(zhǔn)確性并不是重點。這正是本文所強(qiáng)調(diào)的。努力學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)的基礎(chǔ)知識將為以后深入學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)應(yīng)用的迷人世界奠定扎實的基礎(chǔ)。真誠希望您繼續(xù)深入下去!?

譯者介紹?

朱先忠,51CTO社區(qū)編輯,51CTO專家博客、講師,濰坊一所高校計算機(jī)教師,自由編程界老兵一枚。?

原文標(biāo)題:??Behind the Scenes of a Deep Learning Neural Network for Image Classification??,作者:Bruno Caraffa?



責(zé)任編輯:華軒 來源: 51CTO
相關(guān)推薦

2022-06-16 10:29:33

神經(jīng)網(wǎng)絡(luò)圖像分類算法

2016-12-27 14:24:57

課程筆記神經(jīng)網(wǎng)絡(luò)

2018-02-05 08:58:36

Python神經(jīng)網(wǎng)絡(luò)識別圖像

2023-04-19 10:17:35

機(jī)器學(xué)習(xí)深度學(xué)習(xí)

2021-03-29 09:02:24

深度學(xué)習(xí)預(yù)測間隔

2017-06-11 23:38:43

進(jìn)化圖像神經(jīng)網(wǎng)絡(luò)

2020-11-02 08:00:00

深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)人工智能

2022-10-11 23:35:28

神經(jīng)網(wǎng)絡(luò)VGGNetAlexNet

2018-04-08 11:20:43

深度學(xué)習(xí)

2017-12-22 08:47:41

神經(jīng)網(wǎng)絡(luò)AND運(yùn)算

2020-03-05 15:59:10

神經(jīng)網(wǎng)絡(luò)數(shù)據(jù)圖形

2017-08-04 14:23:04

機(jī)器學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)TensorFlow

2020-08-06 10:11:13

神經(jīng)網(wǎng)絡(luò)機(jī)器學(xué)習(xí)算法

2022-10-19 07:42:41

圖像識別神經(jīng)網(wǎng)絡(luò)

2017-03-22 11:59:40

深度神經(jīng)網(wǎng)絡(luò)

2017-03-07 13:55:30

自動駕駛神經(jīng)網(wǎng)絡(luò)深度學(xué)習(xí)

2017-05-23 18:54:18

深度學(xué)習(xí)人工智能

2021-07-07 10:57:08

神經(jīng)網(wǎng)絡(luò)AI算法

2022-10-17 15:43:14

深度學(xué)習(xí)回歸模型函數(shù)

2019-11-06 17:00:51

深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)人工智能
點贊
收藏

51CTO技術(shù)棧公眾號