NNDL 实验五 前馈神经网络(2)自动梯度计算 & 优化问题

目录

​编辑

4.3 自动梯度计算

  4.3.1 利用预定义算子重新实现前馈神经网络

  4.3.2 完善Runner类

  4.3.3 模型训练

  4.3.4 性能评价

 

4.4 优化问题

  4.4.1 参数初始化

  4.4.2 梯度消失问题

  4.4.3 死亡ReLU问题

实验感悟


37cf3ea4bffe47eb9101d2ed504b25d9.png

4.3 自动梯度计算

  虽然我们能够通过模块化的方式比较好地对神经网络进行组装,但是每个模块的梯度计算过程仍然十分繁琐且容易出错。在深度学习框架中,已经封装了自动梯度计算的功能,我们只需要聚焦模型架构,不再需要耗费精力进行计算梯度。

pytorch中的相应内容是什么?请简要介绍。

torch.nn是专门为神经网络设计的模块化接口,nn.Module是nn中十分重要的类。在介绍该模块前,我们先看下pytorch官方对该模块的注释:

 

  4.3.1 利用预定义算子重新实现前馈神经网络

1. 使用pytorch的预定义算子来重新实现二分类任务。(必做)

import torch.nn as nn
import torch.nn.functional as F
#from paddle.nn.initializer import Constant, Normal, Uniform
import torch
from torch.nn.parameter import Parameter
 
class Model_MLP_L2_V2(torch.nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(Model_MLP_L2_V2, self).__init__()
        # 使用'paddle.nn.Linear'定义线性层。
        # 其中第一个参数(in_features)为线性层输入维度;第二个参数(out_features)为线性层输出维度
        # weight_attr为权重参数属性,这里使用'paddle.nn.initializer.Normal'进行随机高斯分布初始化
        # bias_attr为偏置参数属性,这里使用'paddle.nn.initializer.Constant'进行常量初始化
        self.fc1 = nn.Linear(input_size, hidden_size,)
        nn.init.normal_(self.fc1.weight, mean=0, std=1)
        nn.init.constant_(self.fc1.bias,0)
 
        self.fc2 = nn.Linear(hidden_size, output_size,)
        nn.init.normal_(self.fc2.weight, mean=0, std=1)
        nn.init.constant_(self.fc2.bias, 0)
        # 使用'paddle.nn.functional.sigmoid'定义 Logistic 激活函数
        self.act_fn = torch.sigmoid
 
    # 前向计算
    def forward(self, inputs):
        z1 = self.fc1(inputs)
        a1 = self.act_fn(z1)
        z2 = self.fc2(a1)
        a2 = self.act_fn(z2)
        return a2
 
    class RunnerV2_2(object):
        def __init__(self, model, optimizer, metric, loss_fn, **kwargs):
            self.model = model
            self.optimizer = optimizer
            self.loss_fn = loss_fn
            self.metric = metric
 
            # 记录训练过程中的评估指标变化情况
            self.train_scores = []
            self.dev_scores = []
 
            # 记录训练过程中的评价指标变化情况
            self.train_loss = []
            self.dev_loss = []
 
        def train(self, train_set, dev_set, **kwargs):
            # 将模型切换为训练模式
            self.model.train()
 
            # 传入训练轮数,如果没有传入值则默认为0
            num_epochs = kwargs.get("num_epochs", 0)
            # 传入log打印频率,如果没有传入值则默认为100
            log_epochs = kwargs.get("log_epochs", 100)
            # 传入模型保存路径,如果没有传入值则默认为"best_model.pdparams"
            save_path = kwargs.get("save_path", "best_model.pdparams")
 
            # log打印函数,如果没有传入则默认为"None"
            custom_print_log = kwargs.get("custom_print_log", None)
 
            # 记录全局最优指标
            best_score = 0
            # 进行num_epochs轮训练
            for epoch in range(num_epochs):
                X, y = train_set
                # 获取模型预测
                logits = self.model(X)
                # 计算交叉熵损失
                trn_loss = self.loss_fn(logits, y)
                self.train_loss.append(trn_loss.item())
                # 计算评估指标
                trn_score = self.metric(logits, y).item()
                self.train_scores.append(trn_score)
 
                # 自动计算参数梯度
                trn_loss.backward()
                if custom_print_log is not None:
                    # 打印每一层的梯度
                    custom_print_log(self)
 
                # 参数更新
                self.optimizer.step()
                # 清空梯度
                self.optimizer.clear_grad()
 
                dev_score, dev_loss = self.evaluate(dev_set)
                # 如果当前指标为最优指标,保存该模型
                if dev_score > best_score:
                    self.save_model(save_path)
                    print(
                        f"[Evaluate] best accuracy performence has been updated: {best_score:.5f} --> {dev_score:.5f}")
                    best_score = dev_score
 
                if log_epochs and epoch % log_epochs == 0:
                    print(f"[Train] epoch: {epoch}/{num_epochs}, loss: {trn_loss.item()}")
 
        # 模型评估阶段,使用'paddle.no_grad()'控制不计算和存储梯度
        def evaluate(self, data_set):
            # 将模型切换为评估模式
            self.model.eval()
 
            X, y = data_set
            # 计算模型输出
            logits = self.model(X)
            # 计算损失函数
            loss = self.loss_fn(logits, y).item()
            self.dev_loss.append(loss)
            # 计算评估指标
            score = self.metric(logits, y).item()
            self.dev_scores.append(score)
            return score, loss
 
        # 模型测试阶段,使用'paddle.no_grad()'控制不计算和存储梯度
        def predict(self, X):
            # 将模型切换为评估模式
            self.model.eval()
            return self.model(X)
 
        # 使用'model.state_dict()'获取模型参数,并进行保存
        def save_model(self, saved_path):
            torch.save(self.model.state_dict(), saved_path)
 
        # 使用'model.set_state_dict'加载模型参数
        def load_model(self, model_path):
            state_dict = torch.load(model_path)
            self.model.set_state_dict(state_dict)
 

2. 增加一个3个神经元的隐藏层,再次实现二分类,并与1做对比。(必做)

class Model_MLP_L2_V2(torch.nn.Module):
    def __init__(self, input_size, hidden_size,hidden_size2, output_size):
        super(Model_MLP_L2_V2, self).__init__()
        # 使用'paddle.nn.Linear'定义线性层。
        # 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值