数学建模学习-人工神经网络(Artificial Neural Network)教程(26)

数学建模学习-人工神经网络(Artificial Neural Network)教程(26)

写在最前

注意本文的相关代码及例子为同学们提供参考,借鉴相关结构,在这里举一些通俗易懂的例子,方便同学们根据实际情况修改代码,很多同学私信反映能否添加一些可视化,这里每篇教程都尽可能增加一些可视化方便同学理解,但具体使用时,同学们要根据实际情况选择是否在论文中添加可视化图片。

系列教程计划持续更新,同学们可以免费订阅专栏,内容充足后专栏可能付费,提前订阅的同学可以免费阅读,同时相关代码获取可以关注博主评论或私信。

目录

  1. 算法简介
  2. 算法特点
  3. 环境准备
  4. 算法实现
  5. 实例应用
  6. 结果分析
  7. 优化技巧
  8. 注意事项

算法简介

人工神经网络(Artificial Neural Network,ANN)是一种受生物神经网络启发的计算模型,它模仿了人类大脑的基本工作原理。神经网络由大量的人工神经元相互连接而成,每个神经元接收来自其他神经元的输入信号,经过处理后产生输出信号。通过调整神经元之间的连接权重,神经网络可以学习复杂的非线性映射关系,从而解决各种复杂的问题。

算法特点

  1. 非线性映射能力:神经网络可以逼近任意复杂的非线性函数,这使它能够处理现实世界中的复杂问题。

  2. 自适应学习:通过反向传播算法,神经网络能够自动调整网络参数,适应不同的问题和数据。

  3. 并行处理:神经网络的结构天然支持并行计算,可以高效处理大规模数据。

  4. 容错能力:即使部分神经元或连接出现问题,整个网络仍然可以正常工作。

  5. 分布式存储:知识以分布式的方式存储在网络的连接权重中,而不是集中存储。

环境准备

首先需要安装必要的Python库:

pip install -r requirements.txt

requirements.txt文件内容:

numpy>=1.19.2
matplotlib>=3.3.2
scikit-learn>=0.23.2
seaborn>=0.11.0

算法实现

神经网络类的实现

class NeuralNetwork:
    def __init__(self, layers):
        self.layers = layers
        self.weights = []
        self.biases = []
        
        # Xavier/Glorot初始化
        for i in range(len(layers)-1):
            limit = np.sqrt(6 / (layers[i] + layers[i+1]))
            self.weights.append(np.random.uniform(-limit, limit, (layers[i], layers[i+1])))
            self.biases.append(np.zeros((1, layers[i+1])))

这里我们使用了Xavier/Glorot初始化方法,这种方法可以帮助解决深度神经网络中的梯度消失和爆炸问题。初始化的范围与输入和输出神经元的数量相关,这样可以保持各层信号的方差相对稳定。

激活函数

我们在网络中使用了两种激活函数:

  1. tanh函数(隐藏层):
def tanh(self, x):
    return np.tanh(x)

def tanh_derivative(self, x):
    return 1 - x**2
  1. sigmoid函数(输出层):
def sigmoid(self, x):
    return 1 / (1 + np.exp(-np.clip(x, -500, 500)))

def sigmoid_derivative(self, x):
    return x * (1 - x)

注意在sigmoid函数中使用了clip操作来防止数值溢出。

前向传播

def forward(self, X):
    self.activations = [X]
    
    # 隐藏层使用tanh
    for i in range(len(self.weights)-1):
        z = np.dot(self.activations[-1], self.weights[i]) + self.biases[i]
        self.activations.append(self.tanh(z))
    
    # 输出层使用sigmoid
    z = np.dot(self.activations[-1], self.weights[-1]) + self.biases[-1]
    self.activations.append(self.sigmoid(z))
    
    return self.activations[-1]

前向传播过程中,数据依次通过每一层,每层的计算包括:

  1. 线性变换: z = W x + b z = Wx + b z=Wx+b
  2. 非线性激活: a = a c t i v a t i o n ( z ) a = activation(z) a=activation(z)

反向传播

def backward(self, X, y, learning_rate):
    m = X.shape[0]
    delta = self.activations[-1] - y
    
    weight_gradients = []
    bias_gradients = []
    
    for i in range(len(self.weights) - 1, -1, -1):
        dW = np.dot(self.activations[i].T, delta) / m
        db = np.sum(delta, axis=0, keepdims=True) / m
        
        weight_gradients.insert(0, dW)
        bias_gradients.insert(0, db)
        
        if i > 0:
            if i == len(self.weights) - 1:
                delta = np.dot(delta, self.weights[i].T) * self.sigmoid_derivative(self.activations[i])
            else:
                delta = np.dot(delta, self.weights[i].T) * self.tanh_derivative(self.activations[i])

反向传播使用链式法则计算损失函数对各参数的梯度。为了提高训练的稳定性,我们还实现了梯度裁剪:

def clip_gradients(self, gradients, threshold=1.0):
    norm = np.sqrt(sum([np.sum(g**2) for g in gradients]))
    if norm > threshold:
        scale = threshold / norm
        return [g * scale for g in gradients]
    return gradients

训练过程

训练过程包含了几个重要的优化技巧:

  1. 早停机制:当损失不再下降时,减小学习率
  2. 学习率衰减:根据训练进展动态调整学习率
  3. 梯度裁剪:防止梯度爆炸
  4. 数值稳定性处理:在计算中添加小的常数防止除零错误
def train(self, X, y, epochs, learning_rate, verbose=True):
    losses = []
    best_loss = float('inf')
    patience = 50
    patience_counter = 0
    min_learning_rate = 1e-6
    
    for epoch in range(epochs):
        output = self.forward(X)
        loss = np.mean(-y * np.log(output + 1e-8) - (1-y) * np.log(1-output + 1e-8))
        losses.append(loss)
        
        if loss < best_loss:
            best_loss = loss
            patience_counter = 0
        else:
            patience_counter += 1
            
        if patience_counter >= patience:
            learning_rate = max(learning_rate * 0.5, min_learning_rate)
            patience_counter = 0
        
        self.backward(X, y, learning_rate)

实例应用

数据准备

我们使用sklearn的make_moons函数生成一个月牙形的二分类数据集:

X, y = make_moons(n_samples=1000, noise=0.2, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 数据标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

模型训练

我们创建了一个具有[2, 64, 32, 1]结构的神经网络:

  • 输入层:2个特征
  • 第一隐藏层:64个神经元
  • 第二隐藏层:32个神经元
  • 输出层:1个神经元
nn = NeuralNetwork([2, 64, 32, 1])
losses = nn.train(X_train_scaled, y_train.reshape(-1, 1), epochs=1000, learning_rate=0.1)

结果可视化

  1. 训练损失曲线:
    在这里插入图片描述

  2. 训练集上的决策边界:
    在这里插入图片描述

  3. 测试集上的决策边界:
    在这里插入图片描述

  4. 混淆矩阵:
    在这里插入图片描述

结果分析

在月牙形数据集上,我们的模型取得了优秀的性能:

  1. 准确率: 90%
  2. 精确率: 89.11%
  3. 召回率: 90.00%
  4. F1分数: 89.55%

从决策边界图可以看出,模型成功学习到了数据的非线性分布特征,能够准确地区分两个类别。训练集和测试集上的表现相近,说明模型没有过拟合。

优化技巧

  1. 网络结构设计

    • 使用递减的神经元数量(64->32->1)
    • 隐藏层使用tanh激活函数
    • 输出层使用sigmoid激活函数
  2. 参数初始化

    • 使用Xavier/Glorot初始化
    • 偏置初始化为零
  3. 训练稳定性

    • 实现梯度裁剪
    • 使用自适应学习率
    • 添加早停机制
  4. 数值稳定性

    • 在计算中添加小的常数(1e-8)
    • 在sigmoid函数中使用clip操作

注意事项

  1. 数据预处理非常重要,一定要进行标准化。

  2. 网络结构的选择需要根据问题复杂度来确定,不是越大越好。

  3. 训练过程中要监控损失值的变化,如果出现震荡或者不收敛,可以:

    • 降低学习率
    • 增加梯度裁剪阈值
    • 调整网络结构
  4. 在实际应用中,建议:

    • 使用交叉验证来评估模型
    • 尝试不同的网络结构
    • 收集足够多的训练数据
    • 适当使用正则化技术

同学们如果有疑问可以私信答疑,如果有讲的不好的地方或可以改善的地方可以一起交流,谢谢大家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值