当前位置: 首页 > article >正文

2024-11-29 学习人工智能的Day33 BP算法和神经网络小实验

七、BP 算法(误差反向传播)

BP(Back Propagation)算法是用于训练多层神经网络的核心方法。它结合前向传播和反向传播,通过链式求导计算每个权重和偏置的梯度,并更新这些参数以最小化损失函数。

1. 算法基本步骤

  1. 前向传播

    • 数据从输入层传到隐藏层,再到输出层,计算预测值。

    • 公式:
      z ( l ) = W ( l ) ⋅ a ( l − 1 ) + b ( l ) z ( l ) = W ( l ) ⋅ a ( l − 1 ) + b ( l ) z ( l ) = W ( l ) ⋅ a ( l − 1 ) + b ( l ) a ( l ) = σ ( z ( l ) ) a ( l ) = σ ( z ( l ) ) a ( l ) = σ ( z ( l ) ) z(l)=W(l)⋅a(l−1)+b(l)z^{(l)} = W^{(l)} \cdot a^{(l-1)} + b^{(l)}z(l)=W(l)⋅a(l−1)+b(l) a(l)=σ(z(l))a^{(l)} = \sigma(z^{(l)})a(l)=σ(z(l)) z(l)=W(l)a(l1)+b(l)z(l)=W(l)a(l1)+b(l)z(l)=W(l)a(l1)+b(l)a(l)=σ(z(l))a(l)=σ(z(l))a(l)=σ(z(l))

  2. 计算损失

    • 使用损失函数计算预测值和真实值的误差(如均方误差、交叉熵损失)。
  3. 反向传播

    • 通过链式法则计算损失函数相对于每个权重和偏置的梯度。

    • 梯度计算公式:
      δ ( l ) = ( δ ( l + 1 ) W ( l + 1 ) ) ⊙ σ ′ ( z ( l ) ) δ ( l ) = ( δ ( l + 1 ) W ( l + 1 ) ) ⊙ σ ′ ( z ( l ) ) δ ( l ) = ( δ ( l + 1 ) W ( l + 1 ) ) ⊙ σ ′ ( z ( l ) ) δ(l)=(δ(l+1)W(l+1))⊙σ′(z(l))\delta^{(l)} = (\delta^{(l+1)} W^{(l+1)}) \odot \sigma'(z^{(l)})δ(l)=(δ(l+1)W(l+1))⊙σ′(z(l)) δ(l)=(δ(l+1)W(l+1))σ(z(l))δ(l)=(δ(l+1)W(l+1))σ(z(l))δ(l)=(δ(l+1)W(l+1))σ(z(l))

  4. 更新参数

    • 使用梯度下降或其他优化算法更新参数:
      W ( l ) ← W ( l ) − η ∂ L ∂ W ( l ) , b ( l ) ← b ( l ) − η ∂ L ∂ b ( l ) W ( l ) ← W ( l ) − η ∂ L ∂ W ( l ) , b ( l ) ← b ( l ) − η ∂ L ∂ b ( l ) W ( l ) ← W ( l ) − η ∂ W ( l ) ∂ L , b ( l ) ← b ( l ) − η ∂ b ( l ) ∂ L W(l)←W(l)−η∂L∂W(l),b(l)←b(l)−η∂L∂b(l)W^{(l)} \gets W^{(l)} - \eta \frac{\partial L}{\partial W^{(l)}}, \quad b^{(l)} \gets b^{(l)} - \eta \frac{\partial L}{\partial b^{(l)}}W(l)←W(l)−η∂W(l)∂L,b(l)←b(l)−η∂b(l)∂L W(l)W(l)ηLW(l),b(l)b(l)ηLb(l)W(l)W(l)ηW(l)L,b(l)b(l)ηb(l)LW(l)W(l)ηW(l)L,b(l)b(l)ηb(l)L

    需要关注的是,当进行循环反向传播时,优化器中的梯度会不断的叠加,所以需要对梯度进行清空。


2. 链式法则的应用

  • 反向传播利用链式法则逐层计算梯度,从输出层逐层传播到输入层。

  • 例子:复合函数
    f ( x ) = 11 + e − ( w x + b ) f ( x ) = 1 1 + e − ( w x + b ) f ( x ) = 1 + e − ( w x + b ) 1 f(x)=11+e−(wx+b)f(x) = \frac{1}{1 + e^{-(wx + b)}}f(x)=1+e−(wx+b)1 f(x)=11+e(wx+b)f(x)=1+e(wx+b)1f(x)=1+e(wx+b)1
    的梯度可分解为每层的导数相乘。


3. BP 算法的优势

  • 高效计算梯度,适合深度神经网络。
  • 结合优化算法(如 SGD、Adam),能有效降低损失,提升模型性能。

总结:BP 算法是深度学习的基础,通过前向传播和反向传播实现参数的自动优化,适用于多种类型的神经网络。

实例代码:

import torch
import torch.nn as nn
import torch.optim as optim


class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        self.linear1 = nn.Linear(2, 2)
        self.linear2 = nn.Linear(2, 2)

        # 网络参数初始化
        self.linear1.weight.data = torch.tensor([[0.15, 0.20], [0.25, 0.30]])
        self.linear2.weight.data = torch.tensor([[0.40, 0.45], [0.50, 0.55]])
        self.linear1.bias.data = torch.tensor([0.35, 0.35])
        self.linear2.bias.data = torch.tensor([0.60, 0.60])

    def forward(self, x):

        x = self.linear1(x)
        x = torch.sigmoid(x)
        x = self.linear2(x)
        x = torch.sigmoid(x)

        return x


if __name__ == "__main__":

    inputs = torch.tensor([[0.05, 0.10]])
    target = torch.tensor([[0.01, 0.99]])

    # 获得网络输出值
    net = Net()
    output = net(inputs)

    # 计算误差
    loss = torch.sum((output - target) ** 2) / 2

    # 优化方法
    optimizer = optim.SGD(net.parameters(), lr=0.5)

    # 梯度清零
    optimizer.zero_grad()

    # 反向传播
    loss.backward()

    # 打印(w1-w8)观察w5、w7、w1 的梯度值是否与手动计算一致
    print(net.linear1.weight.grad.data)
    print(net.linear2.weight.grad.data)

    #更新梯度
    optimizer.step()
   
    # 打印更新后的网络参数
    print(net.state_dict())

八、优化算法

优化算法用于调整网络参数(权重和偏置),以最小化损失函数。

1. 梯度下降法(Gradient Descent)

  • 核心思想:沿着损失函数梯度下降的方向更新参数。

  • 参数更新公式:
    θ = θ − η ∂ L ∂ θ θ = θ − η ∂ L ∂ θ θ = θ − η ∂ θ ∂ L θ=θ−η∂L∂θ\theta = \theta - \eta \frac{\partial L}{\partial \theta}θ=θ−η∂θ∂L θ=θηLθθ=θηθLθ=θηθL

    • η:学习率,控制步长。
2. 改进的梯度下降方法
  • 指数加权平均:参考各数值,并且各数值的权重都不同,距离越远的数字对平均数计算的贡献就越小(权重较小),距离越近则对平均数的计算贡献就越大(权重越大)。

示例:

import torch
import matplotlib.pyplot as plt


ELEMENT_NUMBER = 30


# 1. 实际平均温度
def test01():

    # 固定随机数种子
    torch.manual_seed(0)

    # 产生30天的随机温度
    temperature = torch.randn(size=[ELEMENT_NUMBER,]) * 10
    print(temperature)

    # 绘制平均温度
    days = torch.arange(1, ELEMENT_NUMBER + 1, 1)
    plt.plot(days, temperature, color='r')
    plt.scatter(days, temperature)
    plt.show()


# 2. 指数加权平均温度
def test02(beta=0.9):

    # 固定随机数种子
    torch.manual_seed(0)

    # 产生30天的随机温度
    temperature = torch.randn(size=[ELEMENT_NUMBER,]) * 10
    print(temperature)

    exp_weight_avg = []
    for idx, temp in enumerate(temperature):

        # 第一个元素的的 EWA 值等于自身
        if idx == 0:
            exp_weight_avg.append(temp)
            continue

        # 第二个元素的 EWA 值等于上一个 EWA 乘以 β + 当前气温乘以 (1-β)
        new_temp = exp_weight_avg[-1] * beta + (1 - beta) * temp
        exp_weight_avg.append(new_temp)


    days = torch.arange(1, ELEMENT_NUMBER + 1, 1)
    plt.plot(days, exp_weight_avg, color='r')
    plt.scatter(days, temperature)
    plt.show()


if __name__ == '__main__':

    test01()
    test02(0.5)
    test02(0.9)

  • Momentum(动量法):加速收敛,避免震荡。
    v t = β v t − 1 + ( 1 − β ) ∇ θ L , θ = θ − η v t v t = β v t − 1 + ( 1 − β ) ∇ θ L , θ = θ − η v t v t = β v t − 1 + ( 1 − β ) ∇ θ L , θ = θ − η v t vt=βvt−1+(1−β)∇θL,θ=θ−ηvtv_t = \beta v_{t-1} + (1-\beta)\nabla_\theta L, \quad \theta = \theta - \eta v_tvt=βvt−1+(1−β)∇θL,θ=θ−ηvt vt=βvt1+(1β)θL,θ=θηvtvt=βvt1+(1β)θL,θ=θηvtvt=βvt1+(1β)θL,θ=θηvt

API:

optimizer = optim.SGD(model.parameters(), lr=0.6, momentum=0.9)  # 学习率和动量值可以根据实际情况调整,momentum 参数指定了动量系数,默认为0。动量系数通常设置为 0 到0.5 之间的一个值,但也可以根据具体的应用场景调整
  • Adam:

    • 结合动量法和自适应学习率,参数更新平稳。

    • 更新公式:
      m t = β 1 m t − 1 + ( 1 − β 1 ) ∇ θ L m t = β 1 m t − 1 + ( 1 − β 1 ) ∇ θ L m t = β 1 m t − 1 + ( 1 − β 1 ) ∇ θ L v t = β 2 v t − 1 + ( 1 − β 2 ) ( ∇ θ L ) 2 v t = β 2 v t − 1 + ( 1 − β 2 ) ( ∇ θ L ) 2 v t = β 2 v t − 1 + ( 1 − β 2 ) ( ∇ θ L ) 2 θ = θ − η m t v t + ϵ θ = θ − η m t v t + ϵ θ = θ − v t + ϵ η m t mt=β1mt−1+(1−β1)∇θLm_t = \beta_1 m_{t-1} + (1-\beta_1)\nabla_\theta Lmt=β1mt−1+(1−β1)∇θL vt=β2vt−1+(1−β2)(∇θL)2v_t = \beta_2 v_{t-1} + (1-\beta_2)(\nabla_\theta L)^2vt=β2vt−1+(1−β2)(∇θL)2 θ=θ−ηmtvt+ϵ\theta = \theta - \frac{\eta m_t}{\sqrt{v_t} + \epsilon}θ=θ−vt+ϵηmt mt=β1mt1+(1β1)θLmt=β1mt1+(1β1)θLmt=β1mt1+(1β1)θLvt=β2vt1+(1β2)(θL)2vt=β2vt1+(1β2)(θL)2vt=β2vt1+(1β2)(θL)2θ=θηmtvt+ϵθ=θvt +ϵηmtθ=θvt+ϵηmt

API:

optimizer = optim.Adagrad(model.parameters(), lr=0.9)  # 设置学习率

总结:优化算法选择需视任务和模型而定,SGD 适合小数据集,Adam 是通用选择。


九、正则化(Regularization)

正则化方法用于防止模型过拟合,提高泛化能力。

1. L1 和 L2 正则化

  • L1 正则化:
    L = L l o s s + λ ∑ ∣ w i ∣ L = L loss + λ ∑ ∣ w i ∣ L = L l o s s + λ ∑ ∣ w i ∣ L=Lloss+λ∑∣wi∣L = L_{\text{loss}} + \lambda \sum |w_i|L=Lloss+λ∑∣wi∣ L=Lloss+λwiL=Lloss+λwiL=Lloss+λwi

    • 增强稀疏性,适合特征选择。

    示例代码:

    def train():
        # 模型构建  
        # 损失函数
        # 优化器
        # 输入数据
        # 预测
        # 计算损失:L1 正则化项并将其加入到总损失中
        l1_lambda = 0.001
    	l1_norm = sum(p.abs().sum() for p in model.parameters())
        loss = criterion(output, target)+l1_lambda*l1_norm
        print(loss)
        # 梯度清零
        # 反向传播
        # 进行权重参数更新
        # 打印更新之后的权重参数
        # 保存模型权重参数
    
  • L2 正则化(权重衰减):
    L = L l o s s + λ ∑ w i 2 L = L loss + λ ∑ w i 2 L = L l o s s + λ ∑ w i 2 L=Lloss+λ∑wi2L = L_{\text{loss}} + \lambda \sum w_i^2L=Lloss+λ∑wi2 L=Lloss+λwi2L=Lloss+λwi2L=Lloss+λwi2

    • 防止权重过大,适合连续值预测。

    实例代码如下:

    import torch.optim as optim
    optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=0.001)  # L2 正则化,weight_decay就是L2正则化前面的参数λ
    

2. Dropout

  • 随机丢弃部分神经元,避免过度依赖特定特征,提高模型鲁棒性。

实例代码:

import torch
import torch.nn as nn


def dropout():
    dropout = nn.Dropout(p=0.5)
    x = torch.randint(0, 10, (5, 6), dtype=torch.float)
    print(x)
    # 开始dropout
    print(dropout(x))


if __name__ == "__main__":
    dropout()

3. 数据增强

  • 对训练数据进行旋转、翻转、裁剪等操作,增加数据多样性,提升泛化能力。

总结:正则化是深度学习防止过拟合的重要工具,常与其他方法结合使用。


十、迁移学习

迁移学习是将预训练模型的知识应用于新任务。

1. 核心思想

  • 使用在大规模数据集(如 ImageNet)上训练的模型作为基础,在新数据集上微调。

2. 方法

  • 冻结层:固定预训练模型的部分权重,仅训练新加入的层。
  • 微调:对所有层进行训练,但设置较低学习率。

总结:迁移学习有效减少训练时间,适合小数据集和资源受限的场景。

学习示例:手机信息预测项目

import time
import torch
import pandas as pd
from sklearn.model_selection import train_test_split
from torch.utils.data import DataLoader,Dataset,TensorDataset
from sklearn.preprocessing import StandardScaler
# 1.数据
def phone_data_set(path):
    data = pd.read_csv(path)
    # 抽离特征和目标数据
    x = data.iloc[:,:-1]
    y = data.iloc[:,-1]


    transfer = StandardScaler()
    x = transfer.fit_transform(x.values)

    x = torch.tensor(x,dtype=torch.float32)
    y = torch.tensor(y.values,dtype=torch.int64)

    # 数据集划分
    x_train,x_test,y_train,y_test = train_test_split(x,y,train_size=0.8,random_state=66,shuffle=True,stratify=y)

    return x_train,x_test,y_train,y_test

class my_phone_data_loader(Dataset):

    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __len__(self):
        return len(self.x)

    def __getitem__(self, index):
        return self.x[index],self.y[index]


def data_loader(x_train,y_train):
    # data = my_phone_data_loader(x_train,y_train)
    data = TensorDataset(x_train,y_train)
    data_loader = DataLoader(data,batch_size=16,shuffle=True)
    return data_loader


# 2.模型(构建和初始化参数)
class Model(torch.nn.Module):

    def __init__(self,input_features,out_features):
        super(Model,self).__init__()
        self.linear1 = torch.nn.Linear(input_features,128)
        self.activation = torch.nn.LeakyReLU()
        self.linear2 = torch.nn.Linear(128,256)
        self.activation2 = torch.nn.LeakyReLU()
        self.out = torch.nn.Linear(256,out_features)
        self.activation3 = torch.nn.Softmax()

    def forward(self,input_data):
        l1 = self.linear1(input_data)
        o1 = self.activation(l1)
        l2 = self.linear2(o1)
        o2 = self.activation(l2)
        o3 = self.out(o2)
        y_pred = self.activation3(o3)
        return y_pred


# def forward(input_data):


x_train,x_test,y_train,y_test = phone_data_set('./data/手机价格预测.csv')
# 训练
def train():
# 加载数据

    data_loader_ = data_loader(x_train,y_train)

    y_feature = torch.unique(y_train).shape[0]
    model = Model(x_train.shape[1],y_feature)
    # 初始化模型参数
    # 默认是初始化过的
    torch.nn.init.kaiming_normal_(model.linear1.weight,nonlinearity='leaky_relu')
    torch.nn.init.kaiming_normal_(model.linear2.weight,nonlinearity='leaky_relu')
    # 3.损失函数
    loss_fn = torch.nn.CrossEntropyLoss()# 虽然分类的结果也可以用均方误差来计算,但一般用交叉熵(因为计算出来梯度更大)
    # 4.优化器
    optimizer = torch.optim.Adam(model.parameters(),lr=0.001)
    # 5.训练(batch)

    # 定义训练参数
    epochs = 100
    for epoch in range(epochs):
        loss_total = 0
        count = 0
        start_time = time.time()
        for x,y in data_loader_:
            count +=1
            # 生成预测值
            y_pred = model(x)
            # 损失函数
            loss = loss_fn(y_pred,y)
            # 梯度清零
            optimizer.zero_grad()
            # 迭代
            loss.backward()
            # 梯度更新
            optimizer.step()

            loss_total +=loss
        end_time = time.time()
        print(f'epoch{epoch},loss{loss_total/count},time{end_time-start_time}')
    torch.save(model.state_dict(),"./model/model.pth")
# 6.测试
# 7.保存
# 8.加载
# 9.前向传播

# 评估函数
def test():
    # 加载数据
    data = data_loader(x_test,y_test)
    # 加载模型
    y_feature = torch.unique(y_train).shape[0]
    model = Model(x_test.shape[1],y_feature)
    state_dict = torch.load('./model/model.pth',map_location='cpu')
    model.load_state_dict(state_dict)
    total = 0
    for x,y in data:
        y_pred = model(x)
        y_pred = torch.argmax(y_pred,dim=1)
        total += torch.sum(y_pred==y)
    print(f"精准度:{total/len(x_test)}")



if __name__ == '__main__':
    train()
    test()


总括性总结

从 BP 算法到正则化、优化和迁移学习,这些内容共同构成深度学习模型训练的重要组成部分:

  1. BP 提供了高效的梯度计算方法。
  2. 优化算法确保模型快速收敛。
  3. 正则化方法提高模型的泛化能力。
  4. 迁移学习为模型提供快速适配新任务的能力。

http://www.kler.cn/a/420564.html

相关文章:

  • 存储过程案例详解与应用示例
  • vue2+svg+elementui实现花瓣图自定义el-select回显色卡图片
  • 5.1 MySQL 锁机制
  • rest-assured multiPart上传中文名称文件,文件名乱码
  • 手机实时提取SIM卡打电话的信令声音-蓝牙电话如何适配eSIM卡的手机
  • bash命令缓存导致命令执行失败的问题
  • Python字符串对齐的几种方法、Python填充与对齐、Python中英文对齐
  • 软件测试常问面试问题及项目流程相关概念
  • 深入实践:从零开始掌握GPT的应用开发
  • 错误信息 Uncaught (in promise) TypeError: extender is not a function
  • spark中将json数据转成dataset
  • Ubuntu 20.04 程序运行导致“段错误 (核心已转储)”的原因分析及解决方案 ubuntu
  • droppath
  • Qt的定时器应用案例 || Qt的图片添加显示
  • 2017 NHOI小学(C++)
  • MySQL 单表练习
  • C#中的集合初始化器
  • TongRDS分布式内存数据缓存中间件
  • 《数据结构》学习系列——图(下)
  • flink学习(14)—— 双流join
  • Redis开发05:使用stackexchange.redis库对redis进行增删改查
  • 前端【9种前端常见的设计模式】
  • 详解Qt Pdf之QPdfBookmarkModel 读取pdf标签页并显示
  • 创建 EC2块存储磁盘并将其连接到 Linux 实例
  • Vue3.5新版本特性一览-数组操作10倍性能提升+响应式属性解构+自定义组件优化+ssr水合改善+teleport支持defer!
  • Maven、JAVAWeb、Servlet