成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

深度圖解神經網絡架構技術

譯文 精選
人工智能
本文將通過一個基本實例并結合大量圖示來深入探索現代人工智能中最基本的神經網絡架構技術。

譯者 | 朱先忠

審校 | 重樓

在本文中,我們將全面了解神經網絡,這是幾乎所有尖端人工智能系統的基礎技術。我們將首先探索人類大腦中的神經元,然后探索它們如何形成人工智能神經網絡的基本靈感。然后,我們將探索反向傳播,即用于訓練神經網絡執行酷炫操作的算法。最后,在形成徹底的概念理解之后,我們將從頭開始自己實現一個神經網絡,并訓練它解決一個玩具問題。

來自大腦的靈感

神經網絡直接從人類大腦中獲取靈感,人類大腦由數十億個極其復雜的細胞(稱為神經元)組成。

神經元圖神經元圖

人類大腦中的思考過程是神經元之間交流的結果。可能會以所見事物的形式接收刺激,然后該信息通過電化學信號傳播到大腦中的神經元。

使用Midjourney生成的眼睛圖像使用Midjourney生成的眼睛圖像

大腦中的第一個神經元接收某種刺激,然后每個神經元可以根據其接收到的刺激量選擇是否“激發”。在這種情況下,“激發”是神經元決定向其連接的神經元發送信號。

來自眼睛的信號直接輸入到三個神經元中;其中,兩個決定激發來自眼睛的信號直接輸入到三個神經元中;其中,兩個決定激發

然后,這些神經元所連接的神經元可能會或可能不會選擇激發。

神經元從先前的神經元接收刺激,然后根據刺激的強度選擇是否激發神經元從先前的神經元接收刺激,然后根據刺激的強度選擇是否激發

因此,“想法”可以概念化為大量神經元根據來自其他神經元的刺激選擇激發或不激發。

當一個人環游世界時,他可能會比其他人有更多特定的想法。例如,大提琴手可能比數學家更多地使用某些神經元。

不同的任務需要使用不同的神經元(使用Midjourney生成的圖像)不同的任務需要使用不同的神經元(使用Midjourney生成的圖像)

當我們更頻繁地使用某些神經元時,它們的連接會變得更強,從而增加這些連接的強度。當我們不使用某些神經元時,這些連接就會減弱。這個一般規則啟發了“一起激發的神經元會連接在一起”這句話,它是大腦負責學習過程的高級品質。

使用某些神經元的過程會加強它們的連接使用某些神經元的過程會加強它們的連接

我不是神經學家所以這是對大腦的一個極其簡化的描述。然而,這足以幫助我們來理解神經網絡的基本概念。

神經網絡的直覺

神經網絡本質上是大腦中的神經元數學上方便且簡化的版本。神經網絡由稱為“感知器”的元素組成,這些元素直接受到神經元的啟發。

左側是感知器,右側是神經元左側是感知器,右側是神經元

感知器像神經元一樣接收數據

人工智能中的感知器處理數字,而大腦中的神經元處理電化學信號

像神經元一樣聚合數據

感知器聚合數字以產生輸出,而神經元聚合電化學信號以產生輸出感知器聚合數字以產生輸出,而神經元聚合電化學信號以產生輸出

然后根據輸入輸出信號,就像神經元一樣

感知器輸出數字,而神經元輸出電化學信號感知器輸出數字,而神經元輸出電化學信號

神經網絡可以概念化為這些感知器的大型網絡,就像大腦是一個巨大的神經元網絡一樣。

神經網絡(左)與大腦(右)神經網絡(左)與大腦(右)

當大腦中的神經元激發時,它會以二元決策的方式進行。或者換句話說,神經元要么激發,要么不激發。另一方面,感知器本身并不“激發”,而是根據感知器的輸入輸出一系列數字。

感知器輸出一系列連續的數字,而神經元要么激發,要么不激發感知器輸出一系列連續的數字,而神經元要么激發,要么不激發

大腦內的神經元可以使用相對簡單的二進制輸入和輸出,因為思想會隨著時間而存在。神經元本質上以不同的速率脈動,較慢和較快的脈沖傳達不同的信息。

因此,神經元以開或關脈沖的形式具有簡單的輸入和輸出,但它們脈動的速率可以傳達復雜的信息。感知器每通過網絡只能看到一次輸入,但它們的輸入和輸出可以是一系列連續的值。如果熟悉電子學,可能會思考這與數字信號和模擬信號之間的關系有何相似之處。

感知器的數學計算方式其實非常簡單。標準神經網絡由一組權重組成,這些權重將不同層的感知器連接在一起。

神經網絡,其中突出顯示了進入和離開特定感知器的權重神經網絡,其中突出顯示了進入和離開特定感知器的權重

可以通過將所有輸入相加并乘以各自的權重來計算特定感知器的值。

感知器值的計算方法示例:(0.3×0.3) + (0.7×0.1) +(-0.5×0.5)=-0.0感知器值的計算方法示例:(0.3×0.3) + (0.7×0.1) +(-0.5×0.5)=-0.0

許多神經網絡還具有與每個感知器相關的“偏差”,該偏差被添加到輸入的總和中以計算感知器的值。

當模型中包含偏差項時,感知器的值可能計算方法示例(0.3×0.3) + (0.7×0.1) +(-0.5×0.5) + 0.01 =-0.08

因此,計算神經網絡的輸出只是進行一系列加法和乘法來計算所有感知器的值。

有時數據科學家將這種一般操作稱為“線性投影”,因為我們通過線性運算(加法和乘法)將輸入映射到輸出。這種方法的一個問題是,即使你將十億個這樣的層連接在一起,得到的模型仍然只是輸入和輸出之間的線性關系,因為它們只是加法和乘法。

這是一個嚴重的問題,因為輸入和輸出之間的關系并非都是線性的。為了解決這個問題,數據科學家采用了一種叫做“激活函數”的概念。這些是非線性函數,可以注入整個模型中,本質上是加入一些非線性。

給定一些輸入,產生一些輸出的各種函數的例子。前三個是線性的,而后三個是非線性的給定一些輸入,產生一些輸出的各種函數的例子。前三個是線性的,而后三個是非線性的

通過在線性投影之間交織非線性激活函數,神經網絡能夠學習非常復雜的函數

通過在神經網絡中放置非線性激活函數,神經網絡能夠對復雜關系進行建模通過在神經網絡中放置非線性激活函數,神經網絡能夠對復雜關系進行建模

在人工智能中,有許多流行的激活函數,但業界已基本集中在三種流行的激活函數上:ReLU、Sigmoid和Softmax,它們分別適用于各種不同的應用場景。在所有這些函數中,ReLU是最常見的,因為它簡單且能夠泛化以模仿幾乎任何其他函數。

ReLU激活函數:如果輸入小于零,則輸出等于零;如果輸入大于零,則輸出等于輸入ReLU激活函數:如果輸入小于零,則輸出等于零;如果輸入大于零,則輸出等于輸入

所以,這就是人工智能模型進行預測的本質。它是一堆加法和乘法,中間夾雜一些非線性函數。

神經網絡的另一個定義特征是,它們可以通過訓練更好地解決某個問題,我們將在下一節中探討這一點。

反向傳播

人工智能的基本思想之一是你可以“訓練”一個模型。這是通過要求神經網絡(它最初是一大堆隨機數據)執行某些任務來實現的。然后,你以某種方式根據模型輸出與已知良好答案的比較情況更新模型。

訓練神經網絡的基本思想示意圖你給它一些你知道你想要輸出的數據,將神經網絡輸出與你想要的結果進行比較,然后使用神經網絡的錯誤程度來更新參數,使其錯誤更少

在本節中,我們設想一個具有輸入層、隱藏層和輸出層的神經網絡。

一個具有兩個輸入和一個輸出的神經網絡(中間有一個隱藏層,允許模型進行更復雜的預測)一個具有兩個輸入和一個輸出的神經網絡(中間有一個隱藏層,允許模型進行更復雜的預測)

這些層中的每一個都連接在一起,最初具有完全隨機的權重。

神經網絡(具有隨機定義的權重和偏差)神經網絡(具有隨機定義的權重和偏差)

我們將在隱藏層上使用ReLU激活函數。

我們將ReLU激活函數應用于隱藏感知器的值我們將ReLU激活函數應用于隱藏感知器的值

假設我們有一些訓練數據,其中期望的輸出是輸入的平均值。

我們將要用來訓練的數據示例我們將要用來訓練的數據示例

我們將訓練數據的一個示例傳遞給模型,生成預測。

根據輸入計算隱藏層和輸出的值,包括所有主要的中間步驟根據輸入計算隱藏層和輸出的值,包括所有主要的中間步驟

為了使我們的神經網絡更好地完成計算輸入平均值的任務,我們首先將預測輸出與期望輸出進行比較。

訓練數據的輸入為0.1和0.3,期望輸出(輸入的平均值)為0.2。模型的預測為-0.1。因此,輸出和期望輸出之間的差異為0.3

現在我們知道輸出的大小應該增加,我們可以回顧模型來計算我們的權重和偏差如何變化以促進這種變化。

首先,讓我們看看直接導致輸出的權重:w?、w?、w?。由于第三個隱藏感知器的輸出為-0.46,因此ReLU的激活為0.00。

第三個感知器的最終激活輸出為0.00第三個感知器的最終激活輸出為0.00

因此,w?沒有任何變化可以使我們更接近期望的輸出,因為在這個特定示例中,w?的每個值都會導致零的變化。

然而,第二個隱藏神經元確實有一個大于零的激活輸出,因此調整w?將對本例的輸出產生影響。

我們實際計算w?應該改變多少的方法是將輸出應該改變的量乘以w?的輸入。

計算權重應該如何變化的計算方法展示:這里的符號Δ(delta)表示“變化”,因此Δw?表示“w?的變化”

我們這樣做的原因最簡單的解釋是“因為微積分”,但如果我們看看最后一層的所有權重是如何更新的,我們就可以形成一有趣的直覺。

計算導致輸出的權重應該如何變化計算導致輸出的權重應該如何變化

注意兩個“激發”(輸出大于零)的感知器是如何一起更新的。另外,注意感知器的輸出越強,其對應的權重更新就越多。這有點類似于人腦中“一起激發的神經元會連接在一起”的想法。

計算輸出偏差的變化非常簡單。事實上,我們已經做到了。因為偏差是感知器輸出應該改變的程度,所以偏差的變化就是期望輸出的變化。所以,Δb?=0.3

輸出的偏差應該如何更新輸出的偏差應該如何更新

現在我們已經計算出輸出感知器的權重和偏差應該如何變化,我們可以通過模型“反向傳播”我們期望的輸出變化。讓我們從反向傳播開始,這樣我們就可以計算出我們應該如何更新w?。

首先,我們計算第一個隱藏神經元的激活輸出應該如何變化。我們通過將輸出變化乘以w?來實現這一點。

通過將輸出的期望變化乘以w?來計算第一個隱藏神經元的激活輸出應該如何變化通過將輸出的期望變化乘以w?來計算第一個隱藏神經元的激活輸出應該如何變化

對于大于零的值,ReLU只需將這些值乘以1。因此,對于此示例,我們希望第一個隱藏神經元的未激活值的變化等于激活輸出的期望變化

基于從輸出反向傳播,我們想要改變第一個隱藏感知器的未激活值基于從輸出反向傳播,我們想要改變第一個隱藏感知器的未激活值

回想一下,我們計算了如何根據將其輸入乘以其期望輸出的變化來更新w?。我們可以做同樣的事情來計算w?的變化。

現在我們已經計算出第一個隱藏神經元應該如何變化,我們可以計算應該如何更新w?,就像我們之前計算w?應該如何更新一樣。

需要注意的是,我們實際上并沒有在整個過程中更新任何權重或偏差。相反,我們正在計算應該如何更新每個參數,假設沒有其他參數更新。

因此,我們可以進行這些計算來計算所有參數變化。

通過反向傳播模型,使用來自前向傳播的值和來自模型各個點的反向傳播的期望變化的組合,我們可以計算出所有參數應該如何變化

反向傳播的一個基本思想稱為“學習率”,它涉及我們根據特定數據批次對神經網絡所做的更改的大小。為了解釋為什么這很重要,我想打個比方。

想象一下,有一天你出門,每個戴帽子的人都用奇怪的眼神看著你。你可能不想倉促得出結論說戴帽子=奇怪的眼神,但你可能會對戴帽子的人有點懷疑。三、四、五天、一個月甚至一年后,如果看起來絕大多數戴帽子的人都用奇怪的眼神看著你,你可能會開始認為這是一種強烈的趨勢。

同樣,當我們訓練神經網絡時,我們不想根據單個訓練示例完全改變神經網絡的思維方式。相反,我們希望每個批次僅逐步改變模型的思維方式。當我們將模型暴露給許多示例時,我們希望模型能夠學習數據中的重要趨勢。

在我們計算出每個參數應該如何變化(就好像它是唯一要更新的參數)之后,我們可以將所有這些變化乘以在將這些更改應用于參數之前,我們先將其設置為一個小數,例如0.001。這個小數通常稱為“學習率”,其確切值取決于我們正在訓練的模型。這有效地縮小了我們的調整范圍,然后再將它們應用于模型。

到目前為止,我們幾乎涵蓋了實現神經網絡所需了解的所有內容。讓我們試一試吧!

從頭開始實現神經網絡

通常,數據科學家只需使用PyTorch之類的庫,用幾行代碼即可實現神經網絡是,我們現在打算使用數值計算庫NumPy從頭開始定義一個神經網絡。

首先,讓我們從定義神經網絡結構的方法開始。

""" 構建神經網絡結構。
"""

import numpy as np

class SimpleNN:
 def __init__(self, architecture):
 self.architecture = architecture
 self.weights = []
 self.biases = []

 #初始化權重和偏差
 np.random.seed(99)
 for i in range(len(architecture) - 1):
 self.weights.append(np.random.uniform(
 low=-1, high=1,
 size=(architecture[i], architecture[i+1])
 ))
 self.biases.append(np.zeros((1, architecture[i+1])))

architecture = [2, 64, 64, 64, 1] # 兩個輸入,兩個隱藏層,一個輸出
model = SimpleNN(architecture)

print('weight dimensions:')
for w in model.weights:
 print(w.shape)

print('nbias dimensions:')
for b in model.biases:
 print(b.shape)

示例神經網絡中定義的權重和偏差矩陣示例神經網絡中定義的權重和偏差矩陣

雖然我們通常將神經網絡繪制為密集網絡,但實際上我們將其連接之間的權重表示為矩陣。這很方便,因為矩陣乘法相當于通過神經網絡傳遞數據。

將密集網絡視為左側的加權連接,對應右側的矩陣乘法。在右側圖中,左側的向量表示輸入,中的矩陣表示權重矩陣,右側的向量表示輸出。

我們可以通過將輸入傳遞到每一層,讓我們的模型根據某些輸入做出預測。

"""實現前向傳播
"""

import numpy as np

class SimpleNN:
 def __init__(self, architecture):
 self.architecture = architecture
 self.weights = []
 self.biases = []

 # 初始化權重和偏差
 np.random.seed(99)
 for i in range(len(architecture) - 1):
 self.weights.append(np.random.uniform(
 low=-1, high=1,
 size=(architecture[i], architecture[i+1])
 ))
 self.biases.append(np.zeros((1, architecture[i+1])))

 @staticmethod
 def relu(x):
 #實現relu激活函數
 return np.maximum(0, x)

 def forward(self, X):
 #遍歷所有層
 for W, b in zip(self.weights, self.biases):

 #應用該層的權重和偏差
 X = np.dot(X, W) + b

 #為除最后一層之外的所有層進行ReLU激活
 if W is not self.weights[-1]:
 X = self.relu(X)

 #返回結果
 return X

 def predict(self, X):
 y = self.forward(X)
 return y.flatten()

#定義模型
architecture = [2, 64, 64, 64, 1] # 兩個輸入,兩個隱藏層,一個輸出
model = SimpleNN(architecture)

# 生成預測
prediction = model.predict(np.array([0.1,0.2]))
print(prediction)

將數據傳遞給模型的打印結果我們的模型是隨機定義的,因此這不是一個有用的預測,但它證實了模型正在發揮作用

我們需要能夠訓練這個模型為此,我們首先需要一個問題來訓練模型。我定義了一個隨機函數,它接受兩個輸入并產生一個輸出:

"""定義我們希望模型要學習的內容
"""
import numpy as np
import matplotlib.pyplot as plt

# 定義一個具有兩個輸入的隨機函數
def random_function(x, y):
 return (np.sin(x) + x * np.cos(y) + y + 3**(x/3))

# 生成一個包含x和y值對的網格
x = np.linspace(-10, 10, 100)
y = np.linspace(-10, 10, 100)
X, Y = np.meshgrid(x, y)

#計算隨機函數的輸出
Z = random_function(X, Y)

#創建二維圖
plt.figure(figsize=(8, 6))
contour = plt.contourf(X, Y, Z, cmap='viridis')
plt.colorbar(contour, label='Function Value')
plt.title('2D Plot of Objective Function')
plt.xlabel('X-axis')
plt.ylabel('Y-axis')
plt.show()

建模目標給定兩個輸入(此處繪制為x和y),模型需要預測輸出(此處表示為顏色)。這里給出的是一個完全任意的函數

在現實世界中,我們不知道底層函數。我們可以通過創建由隨機點組成的數據集來模擬現實:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 定義一個具有兩個輸入的隨機函數
def random_function(x, y):
 return (np.sin(x) + x * np.cos(y) + y + 3**(x/3))

# 定義要生成的隨機樣本數
n_samples = 1000

#生成指定范圍內的隨機X和Y值
x_min, x_max = -10, 10
y_min, y_max = -10, 10

# 生成X和Y生成隨機值
X_random = np.random.uniform(x_min, x_max, n_samples)
Y_random = np.random.uniform(y_min, y_max, n_samples)

# 在生成的X和Y值上計算隨機函數
Z_random = random_function(X_random, Y_random)

#創建數據集
dataset = pd.DataFrame({
 'X': X_random,
 'Y': Y_random,
 'Z': Z_random
})

#顯示數據集
print(dataset.head())

#創建采樣數據的二維散點圖
plt.figure(figsize=(8, 6))
scatter = plt.scatter(dataset['X'], dataset['Y'], c=dataset['Z'], cmap='viridis', s=10)
plt.colorbar(scatter, label='Function Value')
plt.title('Scatter Plot of Randomly Sampled Data')
plt.xlabel('X-axis')
plt.ylabel('Y-axis')
plt.show()

這是我們將用來訓練以嘗試學習函數的數據這是我們將用來訓練以嘗試學習函數的數據

回想一下,反向傳播算法根據前向傳播中發生的情況更新參數。因此,在實現反向傳播本身之前,讓我們跟蹤前向傳播中的幾個重要值:整個模型中每個感知器的輸入和輸出。

import numpy as np

class SimpleNN:
 def __init__(self, architecture):
 self.architecture = architecture
 self.weights = []
 self.biases = []

 #在此代碼塊中跟蹤這些值
 #以便我們可以觀察它們
 self.perceptron_inputs = None
 self.perceptron_outputs = None

 #初始化權重和偏差
 np.random.seed(99)
 for i in range(len(architecture) - 1):
 self.weights.append(np.random.uniform(
 low=-1, high=1,
 size=(architecture[i], architecture[i+1])
 ))
 self.biases.append(np.zeros((1, architecture[i+1])))

 @staticmethod
 def relu(x):
 return np.maximum(0, x)

 def forward(self, X):
 self.perceptron_inputs = [X]
 self.perceptron_outputs = []

 for W, b in zip(self.weights, self.biases):
 Z = np.dot(self.perceptron_inputs[-1], W) + b
 self.perceptron_outputs.append(Z)

 if W is self.weights[-1]: # Last layer (output)
 A = Z # 回歸線性輸出
 else:
 A = self.relu(Z)
 self.perceptron_inputs.append(A)

 return self.perceptron_inputs, self.perceptron_outputs

 def predict(self, X):
 perceptron_inputs, _ = self.forward(X)
 return perceptron_inputs[-1].flatten()

#定義模型
architecture = [2, 64, 64, 64, 1] # 兩個輸入,兩個隱藏層,一個輸出
model = SimpleNN(architecture)

#生成預測
prediction = model.predict(np.array([0.1,0.2]))

#查看臨界優化值
for i, (inpt, outpt) in enumerate(zip(model.perceptron_inputs, model.perceptron_outputs[:-1])):
 print(f'layer {i}')
 print(f'input: {inpt.shape}')
 print(f'output: {outpt.shape}')
 print('')

print('Final Output:')
print(model.perceptron_outputs[-1].shape)

由于前向傳播,模型各個層中的值都會發生變化,這將使我們能夠計算更新模型所需的更改由于前向傳播,模型各個層中的值都會發生變化,這將使我們能夠計算更新模型所需的更改

現在,我們已經在網絡中存儲了關鍵中間值的記錄,我們可以使用這些值以及模型對特定預測的誤差來計算我們應該對模型進行的更改。

import numpy as np

class SimpleNN:
 def __init__(self, architecture):
 self.architecture = architecture
 self.weights = []
 self.biases = []

 #初始化權重和偏差
 np.random.seed(99)
 for i in range(len(architecture) - 1):
 self.weights.append(np.random.uniform(
 low=-1, high=1,
 size=(architecture[i], architecture[i+1])
 ))
 self.biases.append(np.zeros((1, architecture[i+1])))

 @staticmethod
 def relu(x):
 return np.maximum(0, x)

 @staticmethod
 def relu_as_weights(x):
 return (x > 0).astype(float)

 def forward(self, X):
 perceptron_inputs = [X]
 perceptron_outputs = []

 for W, b in zip(self.weights, self.biases):
 Z = np.dot(perceptron_inputs[-1], W) + b
 perceptron_outputs.append(Z)

 if W is self.weights[-1]: #最后一層(輸出)
 A = Z # 回歸線性輸出
 else:
 A = self.relu(Z)
 perceptron_inputs.append(A)

 return perceptron_inputs, perceptron_outputs

 def backward(self, perceptron_inputs, perceptron_outputs, target):
 weight_changes = []
 bias_changes = []

 m = len(target)
 dA = perceptron_inputs[-1] - target.reshape(-1, 1) # 輸出層梯度

 for i in reversed(range(len(self.weights))):
 dZ = dA if i == len(self.weights) - 1 else dA * self.relu_as_weights(perceptron_outputs[i])
 dW = np.dot(perceptron_inputs[i].T, dZ) / m
 db = np.sum(dZ, axis=0, keepdims=True) / m
 weight_changes.append(dW)
 bias_changes.append(db)

 if i > 0:
 dA = np.dot(dZ, self.weights[i].T)

 return list(reversed(weight_changes)), list(reversed(bias_changes))

 def predict(self, X):
 perceptron_inputs, _ = self.forward(X)
 return perceptron_inputs[-1].flatten()

#定義模型
architecture = [2, 64, 64, 64, 1] #兩個輸入,兩個隱藏層,一個輸出
model = SimpleNN(architecture)

#定義樣本輸入和目標輸出
input = np.array([[0.1,0.2]])
desired_output = np.array([0.5])

#進行正向和反向傳播來計算變化
perceptron_inputs, perceptron_outputs = model.forward(input)
weight_changes, bias_changes = model.backward(perceptron_inputs, perceptron_outputs, desired_output)

#用于打印的較小數字
np.set_printoptions(precisinotallow=2)

for i, (layer_weights, layer_biases, layer_weight_changes, layer_bias_changes)
in enumerate(zip(model.weights, model.biases, weight_changes, bias_changes)):
 print(f'layer {i}')
 print(f'weight matrix: {layer_weights.shape}')
 print(f'weight matrix changes: {layer_weight_changes.shape}')
 print(f'bias matrix: {layer_biases.shape}')
 print(f'bias matrix changes: {layer_bias_changes.shape}')
 print('')

print('The weight and weight change matrix of the second layer:')
print('weight matrix:')
print(model.weights[1])
print('change matrix:')
print(weight_changes[1])

這可能是最復雜的實施步驟,所以我想花點時間深入了解一些細節。基本思想正如我們在前面幾節中描述的一樣我們從后到前迭代所有層,并計算每個權重和偏差的哪些變化會產生更好的輸出。

# 計算輸出誤差
dA = perceptron_inputs[-1] - target.reshape(-1, 1)

#一個批處理大小的縮放因子。
#希望更改是所有批次的平均值,所以一旦聚合了所有更改,我們就除以m。

m = len(target)

for i in reversed(range(len(self.weights))):
 dZ = dA #現已簡化

 # 計算權重變化
 dW = np.dot(perceptron_inputs[i].T, dZ) / m
 #計算偏差的變化
 db = np.sum(dZ, axis=0, keepdims=True) / m

 # 跟蹤所需的變更
 weight_changes.append(dW)
 bias_changes.append(db)
 ...

計算偏差的變化非常簡單。如果你看看給定神經元的輸出應該如何影響所有未來的神經元,那么可以將所有這些值(正值和負值)相加,以了解神經元是否應該偏向正方向或負方向。

我們使用矩陣乘法來計算權重的變化,這在數學上有點復雜。

dW = np.dot(perceptron_inputs[i].T, dZ) / m

基本上來說,這代碼表示權重的變化應該等于進入感知器的值乘以輸出應該改變的量。如果感知器有一個大輸入值,其輸出權重的變化應該很大相反,如果感知器有一個小輸入值,其輸出權重的變化將很小。此外,如果權重指向應該發生很大變化的輸出,則權重本身也應該發生很大變化。

在我們的反向傳播實現中,還有如下所示的另一行代碼值得討論

dZ = dA if i == len(self.weights) - 1 else dA * self.relu_as_weights(perceptron_outputs[i])

在這個特定的網絡中,整個網絡應用了激活函數,除了最終輸出外。當我們進行反向傳播時,我們需要通過這些激活函數進行反向傳播,以便更新它們之前的神經元。我們對除最后一層之外的所有層都執行此操作,最后一層沒有應用激活函數,這就是為什么上面使用了條件判斷dZ = dA if i == len(self.weights) - 1。

用數學術語來說,我們將其稱為導數,但因為我不想涉及微積分,所以我將該函數稱為relu_as_weights。基本上,我們可以將每個ReLU激活視為一個微型神經網絡,其權重是輸入的函數。如果ReLU激活函數的輸入小于零,那么這就像將該輸入通過權重為0的神經網絡如果ReLU的輸入大于零,那么這就像將輸入通過權重為1的神經網絡。

回想一下ReLU激活函數回想一下ReLU激活函數

這正是relu_as_weights函數的作用。

def relu_as_weights(x):
 return (x > 0).astype(float)

使用這種邏輯,我們可以將通過ReLU的反向傳播視為我們通過神經網絡的其余部分反向傳播一樣。

同樣,我將很快從更強大的數學角度介紹這個概念,但這是從概念角度來看的基本思想。

現在我們已經實現了前向和后向傳播接下來,我們可以實現對模型的訓練。

import numpy as np

class SimpleNN:
 def __init__(self, architecture):
 self.architecture = architecture
 self.weights = []
 self.biases = []

 #初始化權重和偏差
 np.random.seed(99)
 for i in range(len(architecture) - 1):
 self.weights.append(np.random.uniform(
 low=-1, high=1,
 size=(architecture[i], architecture[i+1])
 ))
 self.biases.append(np.zeros((1, architecture[i+1])))

 @staticmethod
 def relu(x):
 return np.maximum(0, x)

 @staticmethod
 def relu_as_weights(x):
 return (x > 0).astype(float)

 def forward(self, X):
 perceptron_inputs = [X]
 perceptron_outputs = []

 for W, b in zip(self.weights, self.biases):
 Z = np.dot(perceptron_inputs[-1], W) + b
 perceptron_outputs.append(Z)

 if W is self.weights[-1]: # 最后一層(輸出)
 A = Z # 回歸線性輸出
 else:
 A = self.relu(Z)
 perceptron_inputs.append(A)

 return perceptron_inputs, perceptron_outputs

 def backward(self, perceptron_inputs, perceptron_outputs, y_true):
 weight_changes = []
 bias_changes = []

 m = len(y_true)
 dA = perceptron_inputs[-1] - y_true.reshape(-1, 1) # 回歸線性梯度

 for i in reversed(range(len(self.weights))):
 dZ = dA if i == len(self.weights) - 1 else dA * self.relu_as_weights(perceptron_outputs[i])
 dW = np.dot(perceptron_inputs[i].T, dZ) / m
 db = np.sum(dZ, axis=0, keepdims=True) / m
 weight_changes.append(dW)
 bias_changes.append(db)

 if i > 0:
 dA = np.dot(dZ, self.weights[i].T)

 return list(reversed(weight_changes)), list(reversed(bias_changes))

 def update_weights(self, weight_changes, bias_changes, lr):
 for i in range(len(self.weights)):
 self.weights[i] -= lr * weight_changes[i]
 self.biases[i] -= lr * bias_changes[i]

 def train(self, X, y, epochs, lr=0.01):
 for epoch in range(epochs):
 perceptron_inputs, perceptron_outputs = self.forward(X)
 weight_changes, bias_changes = self.backward(perceptron_inputs, perceptron_outputs, y)
 self.update_weights(weight_changes, bias_changes, lr)

 if epoch % 20 == 0 or epoch == epochs - 1:
 loss = np.mean((perceptron_inputs[-1].flatten() - y) ** 2) # MSE
 print(f"EPOCH {epoch}: Loss = {loss:.4f}")

 def predict(self, X):
 perceptron_inputs, _ = self.forward(X)
 return perceptron_inputs[-1].flatten()

訓練函數train實現了

  • 對所有數據進行一定次數的迭代(由變量epoch定義)
  • 將數據進行前向傳播
  • 計算權重和偏差應如何變化
  • 通過按學習率lr縮放其變化來更新權重和偏差

這樣我們就實現了一個神經網絡!接下來,讓我們開始訓練它。

訓練和評估神經網絡

首先,我們來回想一下,我們定義了一個我們想要學習如何模擬的任意2D函數

我們用一些點對該空間進行采樣,我們用這些點來訓練模型。

在將這些數據輸入我們的模型之前,首先“規范化”數據至關重要。數據集的某些值非常小或非常大,這會使訓練神經網絡變得非常困難。神經網絡中的值可以快速增長到非常大的值,或者減小到零,這可能會抑制訓練。規范化將我們所有的輸入和期望的輸出壓縮到一個更合理的范圍內,平均在零附近,標準化分布稱為“正態”分布。

# 數據扁平化處理
X_flat = X.flatten()
Y_flat = Y.flatten()
Z_flat = Z.flatten()

# 把X和Y入棧,作為輸入特性
inputs = np.column_stack((X_flat, Y_flat))
outputs = Z_flat

#規范化輸入和輸出
inputs_mean = np.mean(inputs, axis=0)
inputs_std = np.std(inputs, axis=0)
outputs_mean = np.mean(outputs)
outputs_std = np.std(outputs)

inputs = (inputs - inputs_mean) / inputs_std
outputs = (outputs - outputs_mean) / outputs_std

如果我們想從原始數據集中獲取實際數據范圍內的預測,我們可以使用這些值來“取消壓縮”數據。

完成此操作后,我們就可以定義和訓練我們的模型。

# 定義體系結構:[input_dim, hidden1, ..., output_dim]
architecture = [2, 64, 64, 64, 1] #兩個輸入,兩個隱藏層,一個輸出
model = SimpleNN(architecture)

# 訓練模型
model.train(inputs, outputs, epochs=2000, lr=0.001)

可以看出,損失值一直在下降,這意味著模型正在改進可以看出,損失值一直在下降,這意味著模型正在改進

然后我們可以將神經網絡的預測輸出與實際函數進行可視化。

import matplotlib.pyplot as plt

# 將預測重新調整為網格格式,以進行可視化
Z_pred = model.predict(inputs) * outputs_std + outputs_mean
Z_pred = Z_pred.reshape(X.shape)

#True函數圖和模型預測圖比較
fig, axes = plt.subplots(1, 2, figsize=(14, 6))

# 繪制True函數
axes[0].contourf(X, Y, Z, cmap='viridis')
axes[0].set_title("True Function")
axes[0].set_xlabel("X-axis")
axes[0].set_ylabel("Y-axis")
axes[0].colorbar = plt.colorbar(axes[0].contourf(X, Y, Z, cmap='viridis'), ax=axes[0], label="Function Value")

# 繪制預測函數
axes[1].contourf(X, Y, Z_pred, cmap='plasma')
axes[1].set_title("NN Predicted Function")
axes[1].set_xlabel("X-axis")
axes[1].set_ylabel("Y-axis")
axes[1].colorbar = plt.colorbar(axes[1].contourf(X, Y, Z_pred, cmap='plasma'), ax=axes[1], label="Function Value")

plt.tight_layout()
plt.show()

這個方法還不錯,但不如我們所想的那么好。很多數據科學家都在這方面投入了時間,而且有很多方法可以讓神經網絡更好地適應某個問題。其他一些顯而易見的方法包括:

  • 使用更多數據
  • 調整學習率
  • 訓練更多輪次
  • 改變模型結構

我們很容易就能增加訓練數據量。讓我們看看這會給我們帶來什么。在這里,我對數據集進行了10,000次采樣,這比我們之前的數據集多10倍訓練樣本。

然后我像以前一樣訓練模型,只是這次花費的時間更長,因為現在每個輪次分析10,000個樣本,而不是1,000個。

# 定義體系結構: [input_dim, hidden1, ..., output_dim]
architecture = [2, 64, 64, 64, 1] # 兩個輸入,兩個隱藏層,一個輸出
model = SimpleNN(architecture)

# 訓練模型
model.train(inputs, outputs, epochs=2000, lr=0.001)

然后,我之前一樣渲染了這個模型的輸出,但看起來輸出并沒有好多少。

回顧訓練的損失輸出,似乎損失仍在穩步下降。也許我只需要訓練更長時間。我們試試吧。

# 定義體系結構: [input_dim, hidden1, ..., output_dim]
architecture = [2, 64, 64, 64, 1] # Two inputs, two hidden layers, one output
model = SimpleNN(architecture)

# 訓練模型
model.train(inputs, outputs, epochs=4000, lr=0.001)

結果似乎好一點,但并不令人吃驚

我就不多說細節了。我運行了幾次,得到了一些不錯的結果,但從來沒有1比1的結果。我將在以后的文章中介紹數據科學家使用的一些更高級的方法,如退火和Dropout,這將產生更一致、更好的輸出。不過,本文中我們從頭開始創建了一個神經網絡,并訓練它做一些事情,它做得很好!

結論

在本文中,我們避免了提及微積分,同時加深了對神經網絡的理解。我們探索了它們的理論,加上一點數學知識,還有反向傳播的概念,然后從頭開始實現了一個神經網絡。然后,我們將神經網絡應用于一個玩具問題,并探索了數據科學家用來實際訓練神經網絡以擅長某些事情的一些簡單想法。

在未來的文章中,我們將探索一些更高級的神經網絡方法,敬請期待!現在,可能會對梯度的更徹底分析(反向傳播背后的基本數學知識)感興趣

譯者介紹

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

原文標題:Neural Networks – Intuitively and Exhaustively Explained,作者:Daniel Warfield

責任編輯:華軒 來源: 51CTO
相關推薦

2017-12-22 08:47:41

神經網絡AND運算

2017-03-22 11:59:40

深度神經網絡

2016-12-27 14:24:57

課程筆記神經網絡

2023-05-29 08:31:48

Redis散列表

2018-07-03 16:10:04

神經網絡生物神經網絡人工神經網絡

2021-03-29 09:02:24

深度學習預測間隔

2019-07-25 08:20:37

代碼開發神經網絡

2022-04-22 12:36:11

RNN神經網絡)機器學習

2018-04-08 11:20:43

深度學習

2023-02-28 08:00:00

深度學習神經網絡人工智能

2023-04-19 10:17:35

機器學習深度學習

2019-07-20 11:00:00

神經網絡數據圖形

2017-01-10 17:25:59

深度學習框架神經網絡

2018-02-05 08:58:36

Python神經網絡識別圖像

2018-08-03 16:00:09

人工智能神經網絡高級算法

2017-08-04 14:23:04

機器學習神經網絡TensorFlow

2025-02-19 15:12:17

神經網絡PyTorch大模型

2023-09-14 10:42:46

SQL數據庫

2017-04-25 15:12:45

神經網絡SSD檢測

2019-12-20 09:15:48

神經網絡數據圖形
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 蜜桃视频在线观看免费视频网站www | 粉嫩一区二区三区性色av | 欧美一区二区三区四区在线 | 91视视频在线观看入口直接观看 | 国产精品一区网站 | a在线视频| 日韩精品一区二 | av片毛片| 黄色av网站在线观看 | 久久久蜜桃 | 爱综合 | 国产综合在线视频 | 欧美三级网站 | 亚洲a网 | 中文日韩字幕 | www.色53色.com | 亚洲精品欧美 | 91精品在线播放 | 在线播放国产一区二区三区 | 久久精品一区二区视频 | www.久久99| 国产永久免费 | 特级做a爰片毛片免费看108 | 久久久亚洲 | 伊人久操| 欧美精品在线免费 | 免费看一区二区三区 | 日本一区精品 | 少妇一级淫片aaaaaaaaa | 国产清纯白嫩初高生在线播放视频 | 色婷婷一区二区三区四区 | 亚洲欧美国产精品一区二区 | 二区不卡 | 日韩成人精品在线 | 成人网av | 久久99精品国产自在现线小黄鸭 | 久久久久久成人 | 欧美久久一级特黄毛片 | 国产一区二区在线视频 | 亚洲欧美国产毛片在线 | 日韩欧美网 |