使用numpy手寫一個神經網絡

本文主要包含以下內容:

  1. 推導神經網絡的誤差反向傳播過程
  2. 使用numpy編寫簡單的神經網絡,并使用iris數據集和california_housing數據集分別進行分類和回歸任務,最終將訓練過程可視化。

1. BP算法的推導過程

1.1 導入

img
前向傳播和反向傳播的總體過程。

img

神經網絡的直接輸出記為 Z [ l ] Z^{[l]} Z[l],表示激活前的輸出,激活后的輸出記為 A A A
img

第一個圖像是神經網絡的前向傳遞和反向傳播的過程,第二個圖像用于解釋中間的變量關系,第三個圖像是前向和后向過程的計算圖,方便進行推導,但是第三個圖左下角的 A [ l ? 2 ] A^{[l-2]} A[l?2]有錯誤,應該是 A [ l ? 1 ] A^{[l-1]} A[l?1]

1.2 符號表

為了方便進行推導,有必要對各個符號進行介紹

符號表

記號含義
n l n_l nl? l l l層神經元個數
f l ( ? ) f_l(\cdot) fl?(?) l l l層神經元的激活函數
W l ∈ R n l ? 1 × n l \mathbf{W}^l\in\R^{n_{l-1}\times n_{l}} WlRnl?1?×nl? l ? 1 l-1 l?1層到第 l l l層的權重矩陣
b l ∈ R n l \mathbf{b}^l \in \R^{n_l} blRnl? l ? 1 l-1 l?1層到第 l l l層的偏置
Z l ∈ R n l \mathbf{Z}^l \in \R^{n_l} ZlRnl? l l l層的凈輸出,沒有經過激活的輸出
A l ∈ R n l \mathbf{A}^l \in \R^{n_l} AlRnl? l l l層經過激活函數的輸出, A 0 = X A^0=X A0=X

深層的神經網絡都是由一個一個單層網絡堆疊起來的,于是我們可以寫出神經網絡最基本的結構,然后進行堆疊得到深層的神經網絡。

于是,我們可以開始編寫代碼,通過一個類Layer來描述單個神經網絡層

class Layer:def __init__(self, input_dim, output_dim):# 初始化參數self.W = np.random.randn(input_dim, output_dim) * 0.01self.b = np.zeros((1, output_dim))def forward(self, X):# 前向計算self.Z = np.dot(X, self.W) + self.bself.A = self.activation(self.Z)return self.Adef backward(self, dA, A_prev, activation_derivative):# 反向傳播# 計算公式推導見下方m = A_prev.shape[0]self.dZ = dA * activation_derivative(self.Z)self.dW = np.dot(A_prev.T, self.dZ) / mself.db = np.sum(self.dZ, axis=0, keepdims=True) / mdA_prev = np.dot(self.dZ, self.W.T)return dA_prevdef update_parameters(self, learning_rate):# 參數更新self.W -= learning_rate * self.dWself.b -= learning_rate * self.db# 帶有ReLU激活函數的Layer
class ReLULayer(Layer):def activation(self, Z):return np.maximum(0, Z)def activation_derivative(self, Z):return (Z > 0).astype(float)# 帶有Softmax激活函數(主要用于分類)的Layer
class SoftmaxLayer(Layer):def activation(self, Z):exp_z = np.exp(Z - np.max(Z, axis=1, keepdims=True))return exp_z / np.sum(exp_z, axis=1, keepdims=True)def activation_derivative(self, Z):# Softmax derivative is more complex, not directly used in this form.return np.ones_like(Z)

1.3 推導過程

權重更新的核心在于計算得到self.dWself.db,同時,為了將梯度信息不斷回傳,需要backward函數返回梯度信息dA_prev

需要用到的公式
Z l = W l A l ? 1 + b l A l = f ( Z l ) d Z d W = ( A l ? 1 ) T d Z d b = 1 Z^l = W^l A^{l-1} +b^l \\A^l = f(Z^l)\\\frac{dZ}{dW} = (A^{l-1})^T \\\frac{dZ}{db} = 1 Zl=WlAl?1+blAl=f(Zl)dWdZ?=(Al?1)TdbdZ?=1
解釋:

從上方計算圖右側的反向傳播過程可以看到,來自于上一層的梯度信息dA經過dZ之后直接傳遞到db,也經過dU之后傳遞到dW,于是我們可以得到dWdb的梯度計算公式如下:
d W = d A ? d A d Z ? d Z d W = d A ? f ′ ( d Z ) ? A p r e v T \begin{align}dW &= dA \cdot \frac{dA}{dZ} \cdot \frac{dZ}{dW}\\ &= dA \cdot f'(dZ) \cdot A_{prev}^T \\ \end{align} dW?=dA?dZdA??dWdZ?=dA?f(dZ)?AprevT???
其中, f ( ? ) f(\cdot) f(?)是激活函數, f ′ ( ? ) f'(\cdot) f(?)是激活函數的導數, A p r e v T A_{prev}^T AprevT?是當前層上一層激活輸出的轉置。

同理,可以得到
d b = d A ? d A d Z ? d Z d b = d A ? f ′ ( d Z ) \begin{align}db &= dA \cdot \frac{dA}{dZ} \cdot \frac{dZ}{db}\\ &= dA \cdot f'(dZ) \\ \end{align} db?=dA?dZdA??dbdZ?=dA?f(dZ)??
需要僅需往前傳遞的梯度信息:
d A p r e v = d A ? d A d Z ? d Z A p r e v = d A ? f ′ ( d Z ) ? W T \begin{align}dA_{prev} &= dA \cdot \frac{dA}{dZ} \cdot \frac{dZ}{A_{prev}}\\ &= dA \cdot f'(dZ) \cdot W^T \\ \end{align} dAprev??=dA?dZdA??Aprev?dZ?=dA?f(dZ)?WT??
所以,經過上述推導,我們可以將梯度信息從后向前傳遞。

分類損失函數

分類過程的損失函數最常見的就是交叉熵損失了,用來計算模型輸出分布和真實值之間的差異,其公式如下:
L = ? 1 N ∑ i = 1 N ∑ j = 1 C y i j l o g ( y i j ^ ) L = -\frac{1}{N}\sum_{i=1}^N \sum_{j=1}^C{y_{ij} log(\hat{y_{ij}})} L=?N1?i=1N?j=1C?yij?log(yij?^?)
其中, N N N表示樣本個數, C C C表示類別個數, y i j y_{ij} yij?表示第i個樣本的第j個位置的值,由于使用了獨熱編碼,因此每一行僅有1個數字是1,其余全部是0,所以,交叉熵損失每次需要對第 i i i個樣本不為0的位置的概率計算對數,然后將所有所有概率取平均值的負數。

交叉熵損失函數的梯度可以簡潔地使用如下符號表示:
? z L = y ^ ? y \nabla_zL = \mathbf{\hat{y}} - \mathbf{{y}} ?z?L=y^??y

回歸損失函數

均方差損失函數由于良好的性能被回歸問題廣泛采用,其公式如下:
L = 1 N ∑ i = 1 N ( y i ? y i ^ ) 2 L = \frac{1}{N} \sum_{i=1}^N(y_i - \hat{y_i})^2 L=N1?i=1N?(yi??yi?^?)2
向量形式:
L = 1 N ∣ ∣ y ? y ^ ∣ ∣ 2 2 L = \frac{1}{N} ||\mathbf{y} - \mathbf{\hat{y}}||^2_2 L=N1?∣∣y?y^?22?
梯度計算:
? y ^ L = 2 N ( y ^ ? y ) \nabla_{\hat{y}}L = \frac{2}{N}(\mathbf{\hat{y}} - \mathbf{y}) ?y^??L=N2?(y^??y)

2 代碼

2.1 分類代碼

import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder
import matplotlib.pyplot as pltclass Layer:def __init__(self, input_dim, output_dim):self.W = np.random.randn(input_dim, output_dim) * 0.01self.b = np.zeros((1, output_dim))def forward(self, X):self.Z = np.dot(X, self.W) + self.b     # 激活前的輸出self.A = self.activation(self.Z)        # 激活后的輸出return self.Adef backward(self, dA, A_prev, activation_derivative):# 注意:梯度信息是反向傳遞的: l+1 --> l --> l-1# A_prev是第l-1層的輸出,也即A^{l-1}# dA是第l+1的層反向傳遞的梯度信息# activation_derivative是激活函數的導數# dA_prev是傳遞給第l-1層的梯度信息m = A_prev.shape[0]self.dZ = dA * activation_derivative(self.Z)self.dW = np.dot(A_prev.T, self.dZ) / mself.db = np.sum(self.dZ, axis=0, keepdims=True) / mdA_prev = np.dot(self.dZ, self.W.T) # 反向傳遞給下一層的梯度信息return dA_prevdef update_parameters(self, learning_rate):self.W -= learning_rate * self.dWself.b -= learning_rate * self.dbclass ReLULayer(Layer):def activation(self, Z):return np.maximum(0, Z)def activation_derivative(self, Z):return (Z > 0).astype(float)class SoftmaxLayer(Layer):def activation(self, Z):exp_z = np.exp(Z - np.max(Z, axis=1, keepdims=True))return exp_z / np.sum(exp_z, axis=1, keepdims=True)def activation_derivative(self, Z):# Softmax derivative is more complex, not directly used in this form.return np.ones_like(Z)class NeuralNetwork:def __init__(self, layer_dims, learning_rate=0.01):self.layers = []self.learning_rate = learning_ratefor i in range(len(layer_dims) - 2):self.layers.append(ReLULayer(layer_dims[i], layer_dims[i + 1]))self.layers.append(SoftmaxLayer(layer_dims[-2], layer_dims[-1]))def cross_entropy_loss(self, y_true, y_pred):n_samples = y_true.shape[0]y_pred_clipped = np.clip(y_pred, 1e-12, 1 - 1e-12)return -np.sum(y_true * np.log(y_pred_clipped)) / n_samplesdef accuracy(self, y_true, y_pred):y_true_labels = np.argmax(y_true, axis=1)y_pred_labels = np.argmax(y_pred, axis=1)return np.mean(y_true_labels == y_pred_labels)def train(self, X, y, epochs):loss_history = []for epoch in range(epochs):A = X# Forward propagationcache = [A]for layer in self.layers:A = layer.forward(A)cache.append(A)loss = self.cross_entropy_loss(y, A)loss_history.append(loss)# Backward propagation# 損失函數求導dA = A - yfor i in reversed(range(len(self.layers))):layer = self.layers[i]A_prev = cache[i]dA = layer.backward(dA, A_prev, layer.activation_derivative)# Update parametersfor layer in self.layers:layer.update_parameters(self.learning_rate)if (epoch + 1) % 100 == 0:print(f'Epoch {epoch + 1}/{epochs}, Loss: {loss:.4f}')return loss_historydef predict(self, X):A = Xfor layer in self.layers:A = layer.forward(A)return A# 導入數據
iris = load_iris()
X = iris.data
y = iris.target.reshape(-1, 1)# One hot encoding
encoder = OneHotEncoder(sparse_output=False)
y = encoder.fit_transform(y)# 分割數據
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 定義并訓練神經網絡
layer_dims = [X_train.shape[1], 100, 20, y_train.shape[1]]  # Example with 2 hidden layers
learning_rate = 0.01
epochs = 5000nn = NeuralNetwork(layer_dims, learning_rate)
loss_history = nn.train(X_train, y_train, epochs)# 預測和評估
train_predictions = nn.predict(X_train)
test_predictions = nn.predict(X_test)train_acc = nn.accuracy(y_train, train_predictions)
test_acc = nn.accuracy(y_test, test_predictions)print(f'Training Accuracy: {train_acc:.4f}')
print(f'Test Accuracy: {test_acc:.4f}')# 繪制損失曲線
plt.plot(loss_history)
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Loss Curve')
plt.show()
輸出
Epoch 100/1000, Loss: 1.0983
Epoch 200/1000, Loss: 1.0980
Epoch 300/1000, Loss: 1.0975
Epoch 400/1000, Loss: 1.0960
Epoch 500/1000, Loss: 1.0891
Epoch 600/1000, Loss: 1.0119
Epoch 700/1000, Loss: 0.6284
Epoch 800/1000, Loss: 0.3711
Epoch 900/1000, Loss: 0.2117
Epoch 1000/1000, Loss: 0.1290
Training Accuracy: 0.9833
Test Accuracy: 1.0000

在這里插入圖片描述
可以看到經過1000輪迭代,最終的準確率到達100%。

回歸代碼

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_california_housingclass Layer:def __init__(self, input_dim, output_dim):self.W = np.random.randn(input_dim, output_dim) * 0.01self.b = np.zeros((1, output_dim))def forward(self, X):self.Z = np.dot(X, self.W) + self.bself.A = self.activation(self.Z)return self.Adef backward(self, dA, X, activation_derivative):m = X.shape[0]self.dZ = dA * activation_derivative(self.Z)self.dW = np.dot(X.T, self.dZ) / mself.db = np.sum(self.dZ, axis=0, keepdims=True) / mdA_prev = np.dot(self.dZ, self.W.T)return dA_prevdef update_parameters(self, learning_rate):self.W -= learning_rate * self.dWself.b -= learning_rate * self.dbclass ReLULayer(Layer):def activation(self, Z):return np.maximum(0, Z)def activation_derivative(self, Z):return (Z > 0).astype(float)class LinearLayer(Layer):def activation(self, Z):return Zdef activation_derivative(self, Z):return np.ones_like(Z)class NeuralNetwork:def __init__(self, layer_dims, learning_rate=0.01):self.layers = []self.learning_rate = learning_ratefor i in range(len(layer_dims) - 2):self.layers.append(ReLULayer(layer_dims[i], layer_dims[i + 1]))self.layers.append(LinearLayer(layer_dims[-2], layer_dims[-1]))def mean_squared_error(self, y_true, y_pred):return np.mean((y_true - y_pred) ** 2)def train(self, X, y, epochs):loss_history = []for epoch in range(epochs):A = X# Forward propagationcache = [A]for layer in self.layers:A = layer.forward(A)cache.append(A)loss = self.mean_squared_error(y, A)loss_history.append(loss)# Backward propagation# 損失函數求導dA = -(y - A)for i in reversed(range(len(self.layers))):layer = self.layers[i]A_prev = cache[i]dA = layer.backward(dA, A_prev, layer.activation_derivative)# Update parametersfor layer in self.layers:layer.update_parameters(self.learning_rate)if (epoch + 1) % 100 == 0:print(f'Epoch {epoch + 1}/{epochs}, Loss: {loss:.4f}')return loss_historydef predict(self, X):A = Xfor layer in self.layers:A = layer.forward(A)return Ahousing = fetch_california_housing()# 導入數據
X = housing.data
y = housing.target.reshape(-1, 1)# 標準化
scaler_X = StandardScaler()
scaler_y = StandardScaler()
X = scaler_X.fit_transform(X)
y = scaler_y.fit_transform(y)# 分割數據
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 定義并訓練神經網絡
layer_dims = [X_train.shape[1], 50, 5, 1]  # Example with 2 hidden layers
learning_rate = 0.8
epochs = 1000nn = NeuralNetwork(layer_dims, learning_rate)
loss_history = nn.train(X_train, y_train, epochs)# 預測和評估
train_predictions = nn.predict(X_train)
test_predictions = nn.predict(X_test)train_mse = nn.mean_squared_error(y_train, train_predictions)
test_mse = nn.mean_squared_error(y_test, test_predictions)print(f'Training MSE: {train_mse:.4f}')
print(f'Test MSE: {test_mse:.4f}')# 繪制損失曲線
plt.plot(loss_history)
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Loss Curve')
plt.show()
輸出
Epoch 100/1000, Loss: 1.0038
Epoch 200/1000, Loss: 0.9943
Epoch 300/1000, Loss: 0.3497
Epoch 400/1000, Loss: 0.3306
Epoch 500/1000, Loss: 0.3326
Epoch 600/1000, Loss: 0.3206
Epoch 700/1000, Loss: 0.3125
Epoch 800/1000, Loss: 0.3057
Epoch 900/1000, Loss: 0.2999
Epoch 1000/1000, Loss: 0.2958
Training MSE: 0.2992
Test MSE: 0.3071

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/17289.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/17289.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/17289.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Three.js——相機

在Three.js中,相機(Camera)是用于定義視圖和渲染場景的一個關鍵組件。相機決定了你從哪個角度和位置觀察場景中的物體,以及如何呈現這些物體。Three.js 提供了幾種不同類型的相機,每種相機都有其特定的用途和特性。以下…

Unity OutLine 模型外描邊效果

效果展示: 下載鏈接

【Rust日報】ratatui版本更新

[new ver] ratatui v0.26.3 一個構建終端用戶界面的庫。新版本包括: 修復Unicode 截斷 bug對顏色更好地序列化更快的渲染棄用assert_buffer_eq宏暴露錯誤類型常量函數和類型 官網: https://ratatui.rs/ 鏈接: https://ratatui.rs/highlights/v0263/ [new lib] ansi2…

618電商選品爆款攻略,誰掌握誰爆單

618電商節是各大電商平臺和品牌商家的重要促銷節點,選品和營銷策略對于銷售成績至關重要。以下是一些選品和營銷攻略的要點: 一、市場分析與目標定位 1、分析當前經營類目市場的流行趨勢、熱門品類以及消費者需求的變化。 目前市場上商品繁多&#xf…

Java 命令執行某一個特定類

在Java中,要執行一個特定的類(通常是包含main方法的類),你需要使用java命令,并指定類的完全限定名(包括包名)。通常,這還需要你設置正確的類路徑(classpath)&…

Apache Cassandra和Java:介紹如何在Java中連接和使用Apache Cassandra這款數據庫

1. Apache Cassandra簡介 Apache Cassandra是一個開源的分布式NoSQL數據庫系統,最初由Facebook開發,用來處理大量的結構化數據 across many commodity servers. Cassandra在高可用性和無單點故障的同時,提供了出色的數據分布策略。 Apache Cassandra的主要特點: 分布式…

超詳細避坑指南!OrangpiAIPro轉換部署模型全流程!

目錄 OrangepiPro初體驗 前述: 一、硬件準備 二、安裝CANN工具鏈(虛擬機) 三、配置模型轉換環境(虛擬機) 1.安裝miniconda 。 2.創建環境。 3.安裝依賴包 四、轉換模型 1. 查看設備號(開發板&…

一步一腳印:輕松掌握服務器硬件的奧秘

在這個信息化飛速發展的時代,無論是企業還是個人,對數據處理和存儲的需求日益增長。服務器,作為互聯網的基石,其重要性不言而喻。但對于大多數人來說,服務器的內部世界似乎既復雜又遙遠。不過,不用擔心&…

在Anaconda中修改查找和安裝軟件包的存儲庫的來源channels

以下是一些關鍵的步驟和命令&#xff0c;用于修改Anaconda的channels&#xff1a; 查看當前channels 使用命令 conda config --show channels 可以查看當前配置的channels。 添加新的channel 使用命令 conda config --add channels <channel_url> 來添加一個新的channel…

TIM定時器PWM輸出

tim.c #include "tim.h" #include "stm32mp1xx_tim.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_rcc.h"//tim4初始化(蜂鳴器) void tim4_init(){//1.使能GPIOB的外設時鐘RCC->MP_AHB4ENSETR | (0x1<<1);//使能TI…

辦公必備!一鍵拆分文件,效率翻倍的秘密

需求介紹 1、我有一張數據表“測試數據.xlsx” 2、我要根據A1“COUNTY_CODE”分類拆分成幾張數據表&#xff08;這里從9657到9658共12類&#xff0c;就是拆分成12張數據表&#xff09; 3、根據12個分類&#xff0c;發送數據郵件給對應的收件人 4、收件人及抄送人、共同抄送人…

Appium系列(2)元素定位工具appium-inspector

背景 如實現移動端自動化&#xff0c;依賴任何工具時&#xff0c;都需要針對于頁面中的元素進行識別&#xff0c;通過識別到指定的元素&#xff0c;對元素進行事件操作。 識別元素的工具為appium官網提供的appium-inspector。 appium-inspector下載地址 我這里是mac電腦需要下…

基于Cloudflare/CloudDNS/GitHub使用免費域名部署NewBing的AI服務

部署前準備&#xff1a; Cloudflare 賬號 https://dash.cloudflare.com/login CloudDNS 賬號 https://www.cloudns.net/ GitHub 賬號 https://github.com/Harry-zklcdc/go-proxy-bingai Cloudflare 部署 Worker CloudDNS 獲取免費二級域名 GitHub New Bing Ai 項目 https://git…

揭秘黃金分割數列:斐波那契數列的奇妙之旅

新書上架~&#x1f447;全國包郵奧~ python實用小工具開發教程http://pythontoolsteach.com/3 歡迎關注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目錄 一、黃金分割數列——斐波那契數列的簡介 二、實現斐波那契數列的函數 三、斐波那契數列在…

前端開發之xlsx的使用和實例,并導出多個sheet

前端開發之xlsx的使用和實例 前言效果圖1、安裝2、在頁面中引用3、封裝工具類&#xff08;excel.js&#xff09;4、在vue中使用 前言 在實現業務功能中導出是必不可少的功能&#xff0c;接下來為大家演示在導出xlsx的時候的操作 效果圖 1、安裝 npm install xlsx -S npm inst…

Discuz!X3.4論壇網站公安備案號怎樣放到網站底部?

Discuz&#xff01;網站的工信部備案號都知道在后臺——全局——站點信息——網站備案信息代碼填寫&#xff0c;那公安備案號要添加在哪里呢&#xff1f;并沒有看到公安備案號填寫欄&#xff0c;今天馳網飛飛和你分享 1&#xff09;工信部備案號和公安備案號統一填寫到網站備案…

數據預處理

數據預處理 引入一.配置java , hadoop , maven的window本機環境變量1.配置2.測試是否配置成功 二.創建一個Maven項目三.導入hadoop依賴四.數據清洗1.數據清洗的java代碼2.查看數據清洗后的輸出結果 引入 做數據預處理 需要具備的條件 : java,hadoop,maven環境以及idea軟件 一…

斯坦福2024人工智能指數報告 2

《人工智能指數報告》由斯坦福大學、AI指數指導委員會及業內眾多大佬Raymond Perrault、Erik Brynjolfsson 、James Manyika、Jack Clark等人員和組織合著&#xff0c;旨在追蹤、整理、提煉并可視化與人工智能&#xff08;AI&#xff09;相關各類數據&#xff0c;該報告已被大多…

靜態網站部署指南

一、資源準備 1.1 服務器 # 當前的服務器,公網ip:127.0.0.1 # 通過ssh協議連接訪問服務器1.2 域名 目前個人擁有的域名有: 域名所有者有效期wujinet.top個人2029-04-151.3 網站代碼 純靜態網站,網站源碼由筆者自行開發并提供發布部署的技術支持。 二、技術棧 2.0 源碼…

linux部署rustdesk

1.拉取RustDesk鏡像 sudo docker image pull rustdesk/rustdesk-server2.啟動hbbs服務 sudo docker run --name hbbs -p 21115:21115 -p 21116:21116 -p 21116:21116/udp -p 21118:21118 -v pwd:/root -td --nethost rustdesk/rustdesk-server hbbs3.啟動hbbr服務 sudo dock…