使用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[l2]有错误,应该是 A [ l − 1 ] A^{[l-1]} A[l1]

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}} WlRnl1×nl l − 1 l-1 l1层到第 l l l层的权重矩阵
b l ∈ R n l \mathbf{b}^l \in \R^{n_l} blRnl l − 1 l-1 l1层到第 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=WlAl1+blAl=f(Zl)dWdZ=(Al1)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=dAdZdAdWdZ=dAf(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=dAdZdAdbdZ=dAf(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=dAdZdAAprevdZ=dAf(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=N1i=1Nj=1Cyijlog(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}} zL=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=N1i=1N(yiyi^)2
向量形式:
L = 1 N ∣ ∣ y − y ^ ∣ ∣ 2 2 L = \frac{1}{N} ||\mathbf{y} - \mathbf{\hat{y}}||^2_2 L=N1∣∣yy^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.mzph.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…