【机器学习】二分类神经网络

news/2024/11/1 13:03:27/

本教程旨在帮助初学者理解神经网络的基本原理和实现,特别针对二分类任务,深入解析其正向传播和反向传播的数学推导,并逐步用 numpy 实现完整的神经网络模型,最终使用 PyTorch 简化实现。

神经网络基本概念

神经网络是一种**基于神经元(节点)构建的机器学习模型。每个节点接收输入、执行计算并输出结果。通过多个层(layer)**的堆叠和复杂的计算结构,神经网络可以逼近任意的非线性关系。

数据准备

我们首先生成一个简单的二分类数据集。每个数据点有两个特征,我们希望训练一个神经网络模型预测其类别(0 或 1)。

import numpy as np
import matplotlib.pyplot as plt# 生成数据
np.random.seed(0)
num_points = 200
X = np.random.randn(num_points, 2)
y = (X[:, 0] * X[:, 1] > 0).astype(int)  # 创建一个简单的二分类数据集# 数据可视化
plt.scatter(X[y == 0, 0], X[y == 0, 1], color='red', label='Class 0')
plt.scatter(X[y == 1, 0], X[y == 1, 1], color='blue', label='Class 1')
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.title("Binary Classification Data")
plt.legend()
plt.show()

构建神经网络的基本结构

我们构建一个简单的神经网络结构,包含:

  • 输入层:接受数据输入。
  • 隐藏层:使用ReLU激活函数进行非线性变换。
  • 输出层:通过sigmoid激活函数输出0到1之间的概率,用于二分类

正向传播(Forward Propagation)

正向传播是指数据从输入层流向输出层的计算过程。

  1. 输入层到隐藏层:计算 Z1 = X * W1 + b1
  2. 激活函数:在隐藏层使用 ReLU 函数。
  3. 隐藏层到输出层:计算 Z2 = A1 * W2 + b2,然后使用 sigmoid 激活函数获得输出概率。
# 初始化参数函数
def initialize_parameters(input_dim, hidden_dim, output_dim):"""初始化神经网络的权重和偏置参数。参数:- input_dim: 输入层神经元数量(即特征数量)- hidden_dim: 隐藏层神经元数量- output_dim: 输出层神经元数量(对于二分类输出为1)返回:- W1: 输入层到隐藏层的权重矩阵,形状为 (input_dim, hidden_dim)- b1: 隐藏层的偏置项,形状为 (1, hidden_dim)- W2: 隐藏层到输出层的权重矩阵,形状为 (hidden_dim, output_dim)- b2: 输出层的偏置项,形状为 (1, output_dim)"""np.random.seed(1)  # 固定随机种子,保证每次初始化相同# 初始化 W1,权重较小以防止初始值过大影响训练W1 = np.random.randn(input_dim, hidden_dim) * 0.01b1 = np.zeros((1, hidden_dim))  # 初始化 b1 为零,避免对初始输出的影响W2 = np.random.randn(hidden_dim, output_dim) * 0.01b2 = np.zeros((1, output_dim))return W1, b1, W2, b2# ReLU 激活函数
def relu(Z):"""ReLU(线性整流)激活函数,返回输入中每个值的最大值和 0 的较大值。参数:- Z: 输入数组,可以是任意形状返回:- A: ReLU 激活后的输出,与 Z 形状相同"""return np.maximum(0, Z)# Sigmoid 激活函数
def sigmoid(Z):"""Sigmoid 激活函数,将输入值映射到 0 到 1 之间的范围,用于二分类问题。参数:- Z: 输入数组,可以是任意形状返回:- A: Sigmoid 激活后的输出,与 Z 形状相同"""return 1 / (1 + np.exp(-Z))# 正向传播函数
def forward_propagation(X, W1, b1, W2, b2):"""执行神经网络的正向传播过程。参数:- X: 输入数据矩阵,形状为 (样本数, 输入层神经元数)- W1, b1: 输入层到隐藏层的权重和偏置- W2, b2: 隐藏层到输出层的权重和偏置返回:- A2: 输出层激活值,形状为 (样本数, 输出层神经元数)- cache: 包含正向传播中间计算结果的字典(供反向传播使用)"""# 计算隐藏层的线性组合 Z1 = X * W1 + b1Z1 = np.dot(X, W1) + b1# 通过 ReLU 激活函数得到 A1A1 = relu(Z1)# 计算输出层的线性组合 Z2 = A1 * W2 + b2Z2 = np.dot(A1, W2) + b2# 通过 Sigmoid 激活函数得到最终输出 A2A2 = sigmoid(Z2)# 将所有计算结果缓存,以便反向传播时使用cache = (Z1, A1, W1, b1, Z2, A2, W2, b2)return A2, cache

损失函数(Loss Function)

对于二分类任务,常用的损失函数是二元交叉熵损失(binary cross-entropy loss),其定义为:
Loss = − 1 m ∑ i = 1 m ( y ( i ) log ⁡ ( y ^ ( i ) ) + ( 1 − y ( i ) ) log ⁡ ( 1 − y ^ ( i ) ) ) \text{Loss} = -\frac{1}{m} \sum_{i=1}^m (y^{(i)} \log(\hat{y}^{(i)}) + (1 - y^{(i)}) \log(1 - \hat{y}^{(i)})) Loss=m1i=1m(y(i)log(y^(i))+(1y(i))log(1y^(i)))

# 定义损失函数
def compute_loss(A2, Y):"""计算二分类问题的交叉熵损失函数。参数:- A2: 模型的预测输出,形状为 (样本数, 1),值在 0 到 1 之间- Y: 实际标签,形状为 (样本数, 1),值为 0 或 1返回:- loss: 交叉熵损失的平均值,标量"""m = Y.shape[0]  # 获取样本数# 计算交叉熵损失,每个样本的损失为 -[y*log(a) + (1-y)*log(1-a)]loss = -np.mean(Y * np.log(A2) + (1 - Y) * np.log(1 - A2))return loss

反向传播(Backward Propagation)

反向传播通过链式法则计算损失函数对每个参数的梯度,从而更新参数以最小化损失。具体步骤如下:

  1. 计算输出层的梯度。
  2. 通过输出层的梯度,进一步计算隐藏层的梯度。
  3. 使用梯度下降算法更新参数。
# 反向传播函数
def backward_propagation(X, Y, cache):"""执行神经网络的反向传播计算梯度,以用于更新参数。参数:- X: 输入数据矩阵,形状为 (样本数, 输入层神经元数)- Y: 实际标签,形状为 (样本数, 1)- cache: 包含正向传播中间结果的字典,供反向传播计算使用返回:- gradients: 包含各层参数梯度的字典,供梯度下降法更新参数"""# 解包缓存的中间结果Z1, A1, W1, b1, Z2, A2, W2, b2 = cachem = X.shape[0]  # 获取样本数量# 计算输出层的梯度dZ2 = A2 - Y  # A2 是模型预测值,Y 是实际值,计算损失对 Z2 的梯度dW2 = (1 / m) * np.dot(A1.T, dZ2)  # 损失对 W2 的梯度db2 = (1 / m) * np.sum(dZ2, axis=0, keepdims=True)  # 损失对 b2 的梯度# 计算隐藏层的梯度dA1 = np.dot(dZ2, W2.T)  # 反向传播 dZ2,通过 W2 获得 dA1dZ1 = dA1 * (Z1 > 0)  # ReLU 导数,当 Z1 > 0 时导数为 1,否则为 0dW1 = (1 / m) * np.dot(X.T, dZ1)  # 损失对 W1 的梯度db1 = (1 / m) * np.sum(dZ1, axis=0, keepdims=True)  # 损失对 b1 的梯度# 将各梯度存入字典,便于更新gradients = {"dW1": dW1, "db1": db1, "dW2": dW2, "db2": db2}return gradients

参数更新

通过反向传播获得梯度后,我们使用梯度下降算法更新参数。更新公式如下:

W = W − α ⋅ d W b = b − α ⋅ d b W = W - \alpha \cdot dW\\ b = b - \alpha \cdot db W=WαdWb=bαdb

# 参数更新函数
def update_parameters(W1, b1, W2, b2, gradients, learning_rate=0.01):"""使用梯度下降法更新模型参数。参数:- W1, b1, W2, b2: 当前神经网络的权重和偏置参数- gradients: 包含各参数梯度的字典(从反向传播计算得出)- learning_rate: 学习率,控制更新步长,默认为 0.01返回:- W1, b1, W2, b2: 更新后的参数"""# 使用学习率调整各参数的梯度,更新 W1W1 -= learning_rate * gradients["dW1"]# 更新 b1b1 -= learning_rate * gradients["db1"]# 更新 W2W2 -= learning_rate * gradients["dW2"]# 更新 b2b2 -= learning_rate * gradients["db2"]return W1, b1, W2, b2

训练神经网络

我们将上述步骤整合到一个完整的训练过程中,循环执行正向传播、损失计算、反向传播和参数更新。

# 定义训练神经网络的函数
def train_neural_network(X, Y, input_dim, hidden_dim, output_dim, epochs=1000, learning_rate=0.01):"""训练神经网络模型,使用梯度下降法优化参数。参数:- X: 输入数据矩阵,形状为 (样本数, 输入层神经元数)- Y: 实际标签矩阵,形状为 (样本数, 1)- input_dim: 输入层的神经元数量- hidden_dim: 隐藏层的神经元数量- output_dim: 输出层的神经元数量- epochs: 训练迭代次数,默认为 1000- learning_rate: 学习率,控制每次参数更新的步长,默认为 0.01返回:- W1, b1, W2, b2: 训练好的参数- losses: 每个 epoch 的损失值列表,用于可视化"""# 初始化参数W1, b1, W2, b2 = initialize_parameters(input_dim, hidden_dim, output_dim)losses = []  # 存储每个 epoch 的损失值,用于后续绘图# 迭代训练for epoch in range(epochs):# 正向传播A2, cache = forward_propagation(X, W1, b1, W2, b2)# 计算损失loss = compute_loss(A2, Y)losses.append(loss)  # 保存当前损失值# 反向传播gradients = backward_propagation(X, Y, cache)# 更新参数W1, b1, W2, b2 = update_parameters(W1, b1, W2, b2, gradients, learning_rate)# 每 100 个 epoch 打印一次损失if epoch % 100 == 0:print(f"Epoch {epoch}, Loss: {loss}")return W1, b1, W2, b2, losses# 使用数据训练神经网络
Y = y.reshape(-1, 1)  # 确保标签是列向量,符合网络输入要求
W1, b1, W2, b2, losses = train_neural_network(X, Y, input_dim=2, hidden_dim=4, output_dim=1, epochs=1000, learning_rate=0.1)# 可视化训练过程中的损失变化
import matplotlib.pyplot as pltplt.plot(losses)
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.title("Training Loss")
plt.show()

模型预测

模型训练后,我们可以使用训练得到的参数进行预测。

# 定义预测函数
def predict(X, W1, b1, W2, b2):A2, _ = forward_propagation(X, W1, b1, W2, b2)predictions = (A2 > 0.5).astype(int)return predictions# 预测并可视化分类结果
predictions = predict(X, W1, b1, W2, b2)# 绘制预测结果
plt.scatter(X[predictions.flatten() == 0, 0], X[predictions.flatten() == 0, 1], color='red', label='Class 0')
plt.scatter(X[predictions.flatten() == 1, 0], X[predictions.flatten() == 1, 1], color='blue', label='Class 1')
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.title("Prediction Results")
plt.legend()
plt.show()

使用PyTorch简化实现

在实际应用中,可以使用深度学习库如 PyTorch 快速实现同样的网络结构。

# 导入必要库
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt# 将数据转换为 PyTorch tensor
X_tensor = torch.FloatTensor(X)  # 将数据 X 转换为浮点数 tensor
y_tensor = torch.FloatTensor(Y)  # 将标签 y 转换为浮点数 tensor# 定义简单的二分类神经网络
class SimpleNN(nn.Module):def __init__(self):super(SimpleNN, self).__init__()self.hidden = nn.Linear(2, 4)   # 隐藏层self.output = nn.Linear(4, 1)   # 输出层def forward(self, x):x = torch.relu(self.hidden(x))  # 使用 ReLU 激活函数x = torch.sigmoid(self.output(x))  # 使用 Sigmoid 激活函数return x# 创建模型实例、定义损失函数和优化器
model = SimpleNN()
criterion = nn.BCELoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)# 训练模型并记录损失
epochs = 1000
losses = []
for epoch in range(epochs):optimizer.zero_grad()output = model(X_tensor)loss = criterion(output, y_tensor)loss.backward()optimizer.step()losses.append(loss.item())if epoch % 100 == 0:print(f"Epoch {epoch}, Loss: {loss.item()}")# 绘制训练过程中损失值变化
plt.plot(losses)
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.title("Training Loss over Epochs")
plt.show()# 使用训练好的模型进行预测
with torch.no_grad():  # 禁用梯度计算,提高效率predictions = model(X_tensor)  # 获取预测值predicted_labels = (predictions > 0.5).float()  # 0.5 阈值将预测值转为二分类# 绘制预测结果
plt.figure(figsize=(8, 6))
# 绘制预测为类别 1 的点
plt.scatter(X[predicted_labels.squeeze() == 1, 0], X[predicted_labels.squeeze() == 1, 1], color="blue", label="Predicted Class 1")
# 绘制预测为类别 0 的点
plt.scatter(X[predicted_labels.squeeze() == 0, 0], X[predicted_labels.squeeze() == 0, 1], color="red", label="Predicted Class 0")
# 绘制原始数据的分布
plt.scatter(X[Y.squeeze() == 1, 0], X[Y.squeeze() == 1, 1], color="cyan", marker="x", label="Actual Class 1")
plt.scatter(X[Y.squeeze() == 0, 0], X[Y.squeeze() == 0, 1], color="orange", marker="x", label="Actual Class 0")# 添加图例和标签
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.title("Prediction Results vs Actual Data")
plt.legend()
plt.show()

总结

通过本教程,我们从原理到实现全程了解了一个简单二分类神经网络的核心构造,并掌握了正向传播和反向传播的推导和实现。希望这为你深入学习神经网络和深度学习打下了坚实的基础。


http://www.ppmy.cn/news/1543585.html

相关文章

spring中bean的四种创建方式

本次分享一下spring中bean的四种创建方式 1. 方式一:普通配置 <bean id"myBean" class"cn.cjc.MyBean"> </bean>2. 方式二:集成静态工厂 // 准备静态工厂 public class CarFactory { //静态方法&#xff0c;返回一个对象 public static Car…

gem5运行简单RISC-V全系统模拟

简单记录gem5中运行最简单的RISC-V Full System Simulation的过程 首先是编译RISC-V和m5term&#xff0c;这部分不多写了&#xff0c;官网均有对应教程。 之后直接使用官方在configs/example/gem5_library目录下的riscv-fs.py 运行如下命令 ./build/RISCV/gem5.opt configs/…

java 代码实现sse客户端进行大模型流式推理协议转换

背景 使用 java 语言实现sse协议客户端消息接收&#xff0c;完成大模型流式推理的协议转换。 核心&#xff1a;基于 Spring 5 实现&#xff0c;关键类 WebClient&#xff0c;代码如下&#xff1a; /*** Author ouyangrongtao* Date 2024-05-30 13:54* Description SSE 客户…

.net core 读取 appsettings.json 值

namespace Utility { public class ConfigurationHelper { //先 NuGet:Microsoft.Extensions.Configuration //ConfigurationHelper.Configure(builder.Configuration);//在入口注册&#xff08;写在var app builder.Build();&#xff09;之前 …

【Python】【数据可视化】【商务智能方法与应用】课程 作业一 飞桨AI Studio

作业说明 程序运行和题目图形相同可得90分&#xff0c;图形显示有所变化&#xff0c;美观清晰可适当加分。 import matplotlib.pyplot as plt import numpy as npx np.linspace(0, 1, 100) y1 x**2 y2 x**4plt.figure(figsize(8, 6))# yx^2 plt.plot(x, y1, -., labelyx^2,…

工作流管理是什么?5款企业工作流管理工具推荐!

一、工作流管理 工作流管理是一个被业界广泛应用并迅速发展的技术。它主要是使处理过程自动化&#xff0c;使人以及各种应用工具相互之间协调工作&#xff0c;以完成某项工作。其目的是让合适的人或软件在恰当的时间执行正确的工作。通俗来说&#xff0c;工作流管理就是对业务…

【jvm】所有的线程都共享堆吗

目录 1. 说明 1. 说明 1.是的&#xff0c;JVM中所有的线程都共享堆内存。2.堆内存&#xff08;Heap&#xff09;是JVM管理的内存中最大的一块&#xff0c;用于存储对象实例和数组等动态分配的数据。3.它是Java内存管理中非常重要的一块区域&#xff0c;也是垃圾回收&#xff0…

深入了解 Three.js 中的材质与光照

开发领域&#xff1a;前端开发 | AI 应用 | Web3D | 元宇宙 技术栈&#xff1a;JavaScript、React、ThreeJs、WebGL、Go 经验经验&#xff1a;6年 前端开发经验&#xff0c;专注于图形渲染和AI技术 开源项目&#xff1a;github 晓智元宇宙、数字孪生引擎、前端面试题 大家好&am…