MindSpore框架学习项目-ResNet药物分类-模型训练

发布于:2025-05-10 ⋅ 阅读:(10) ⋅ 点赞:(0)

目录

3.模型训练

3.1模型训练

3.1.1 定义优化器和损失函数

定义优化器和损失函数代码解析

3.1.2定义训练、推理函数

定义训练、推理函数代码解释

3.2模型保存

模型保存代码说明

3.3绘制acc和loss的曲线


参考内容: 昇思MindSpore | 全场景AI框架 | 昇思MindSpore社区官网 华为自研的国产AI框架,训推一体,支持动态图、静态图,全场景适用,有着不错的生态

本项目可以在华为云modelart上租一个实例进行,也可以在配置至少为单卡3060的设备上进行

https://console.huaweicloud.com/modelarts/

Ascend环境也适用,但是注意修改device_target参数

需要本地编译器的一些代码传输、修改等可以勾上ssh远程开发

说明:项目使用的数据集来自华为云的数据资源。项目以深度学习任务构建的一般流程展开(数据导入、处理 > 模型选择、构建 > 模型训练 > 模型评估 > 模型优化)。

主线为‘一般流程’,同时代码中会标注出一些要点(# 要点1-1-1:设置使用的设备

)作为支线,帮助学习mindspore框架在进行深度学习任务时一些与pytorch的差异。

可以只看目录中带数字标签的部分来快速查阅代码。

 本系列

MindSpore框架学习项目-ResNet药物分类-数据增强-CSDN博客
MindSpore框架学习项目-ResNet药物分类-构建模型-CSDN博客
MindSpore框架学习项目-ResNet药物分类-模型训练-CSDN博客
MindSpore框架学习项目-ResNet药物分类-模型评估-CSDN博客
MindSpore框架学习项目-ResNet药物分类-模型优化-CSDN博客

参考内容:https://www.mindspore.cn/华为自研的国产AI框架,训推一体,支持动态图、静态图,全场景适用,有着不错的生态

本项目可以在华为云modelart上租一个实例进行,也可以在配置至少为单卡3060的设备上进行

https://console.huaweicloud.com/modelarts/

Ascend环境也适用,但是注意修改device_target参数

需要本地编译器的一些代码传输、修改等可以勾上ssh远程开发

说明:项目使用的数据集来自华为云的数据资源。项目以深度学习任务构建的一般流程展开(数据导入、处理 > 模型选择、构建 > 模型训练 > 模型评估 > 模型优化)。

主线为‘一般流程’,同时代码中会标注出一些要点(# 要点1-1-1:设置使用的设备

)作为支线,帮助学习mindspore框架在进行深度学习任务时一些与pytorch的差异。

可以只看目录中带数字标签的部分来快速查阅代码。

3.模型训练

3.1模型训练

要求:

补充如下代码的空白处

主要完成:

1. 定义优化器为Momentum优化器

2. 定义损失函数为SoftmaxCrossEntropyWithLogits损失函数,sparse=True, reduction='mean'

3. 模型编译:利用函数式编程实现loss的计算,并返回loss和模型预测值logits

4. 利用value_and_grad API定义反向传播函数

5. 设置模型为预测模式

3.1.1 定义优化器和损失函数

定义优化器为Momentum优化器,定义损失函数为SoftmaxCrossEntropyWithLogits损失函数,sparse=True, reduction='mean'

num_epochs = 3
patience = 5 #用于早停的阈值,如果模在5轮都没有提高性能(精度)就结束训练
lr = nn.cosine_decay_lr(min_lr=0.00001, max_lr=0.001, total_step=step_size_train * num_epochs,
                        step_per_epoch=step_size_train, decay_epoch=num_epochs)
                        
# 要点3-1-1:定义优化器为Momentum优化器, 动量因子设置为0.9
opt = nn.Momentum(params=network.trainable_params(), learning_rate=lr, momentum=0.9)

# 要点3-1-2:定义损失函数为SoftmaxCrossEntropyWithLogits损失函数,sparse=True, reduction='mean'
loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')
model = ms.Model(network, loss_fn, opt, metrics={'acc'})

best_acc = 0
best_ckpt_dir = "./BestCheckpoint"
best_ckpt_path = "./BestCheckpoint/resnet50-best.ckpt"

定义优化器和损失函数代码解析

1. 学习率调度(余弦衰减)

lr = nn.cosine_decay_lr(...) 生成余弦衰减学习率序列:

参数:初始学习率 max_lr=0.001,最终学习率 min_lr=0.00001,总步数 step_size_train*num_epochs(总训练步数 = 每轮步数 × 轮数)。

作用:训练初期使用较大学习率快速收敛,后期逐步降低学习率精细调整,避免震荡,提升模型收敛稳定性。

2. 优化器定义(Momentum)

opt = nn.Momentum(...) 定义动量优化器:

参数:params=network.trainable_params():优化网络的可训练参数。

learning_rate=lr:使用上述余弦衰减学习率。

momentum=0.9:动量因子(历史梯度的加权系数),0.9 表示保留 90% 的历史梯度方向,10% 当前梯度,加速收敛并抑制噪声。

3. 损失函数定义(交叉熵)

loss_fn = nn.SoftmaxCrossEntropyWithLogits(...) 定义分类任务损失函数:

特性:sparse=True:标签为稀疏整数(如[0, 1, 2]),无需独热编码,减少内存占用。

reduction='mean':对批量样本的损失取平均,输出标量值,便于反向传播计算梯度。

4. 模型封装与评估指标

model = ms.Model(...) 封装训练组件:

参数:网络network、损失函数loss_fn、优化器opt、评估指标{'acc'}(准确率)。

作用:整合训练所需的所有组件,支持调用model.train()和model.eval()完成训练与评估流程。

5. 最佳模型保存配置

best_acc、best_ckpt_dir等变量记录训练过程中验证集最高准确率及对应模型路径,用于训练结束后保存最优模型参数(如resnet50-best.ckpt),避免过拟合,保留泛化能力最佳的模型。

3.1.2定义训练、推理函数

模型编译:利用函数式编程实现loss的计算,并返回loss和模型预测值logits;利用value_and_grad API定义反向传播函数

训练函数train_loop

def train_loop(model, dataset, loss_fn, optimizer):
    # 要点3-1-3:模型编译:利用函数式编程实现loss的计算,并返回loss和模型预测值logits
    def forward_fn(data, label):
        logits = model(data)
        loss = loss_fn(logits,label)
        return loss, logits

    # 要点3-1-4:利用value_and_grad API定义反向传播函数
    grad_fn = ms.ops.value_and_grad(forward_fn, None, opt.parameters, has_aux=True)

    def train_step(data, label):
        (loss, _), grads = grad_fn(data, label)
        loss = ops.depend(loss, optimizer(grads))
        return loss
    size = dataset.get_dataset_size()
    model.set_train()
    for batch, (data, label) in enumerate(dataset.create_tuple_iterator()):
        loss = train_step(data, label)

        if batch % 100 == 0 or batch == step_size_train - 1:
            loss, current = loss.asnumpy(), batch
            print(f"loss: {loss:>7f}  [{current:>3d}/{size:>3d}]")
            

测试函数test_loop

from sklearn.metrics import classification_report

def test_loop(model, dataset, loss_fn):
    num_batches = dataset.get_dataset_size()
    # 要点3-1-5:设置模型为预测模式
    model.set_train(False)
    total, test_loss, correct = 0, 0, 0
    y_true = []
    y_pred = []
    for data, label in dataset.create_tuple_iterator():
        y_true.extend(label.asnumpy().tolist())
        pred = model(data)
        total += len(data)
        test_loss += loss_fn(pred, label).asnumpy()
        y_pred.extend(pred.argmax(1).asnumpy().tolist())
        correct += (pred.argmax(1) == label).asnumpy().sum()
    test_loss /= num_batches
    correct /= total
    print(f"Test: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
    print(classification_report(y_true,y_pred,target_names= list(index_label_dict.values()),digits=3))
    return correct,test_loss

定义训练、推理函数代码解释

1. 训练循环 train_loop

前向传播与损失计算(要点 3-1-3)

def forward_fn(data, label):
    logits = model(data)          # 模型前向传播,输出未归一化的预测值(logits)
    loss = loss_fn(logits, label) # 计算交叉熵损失(含Softmax操作)
    return loss, logits           # 返回损失值和预测logits(用于后续梯度计算)

作用:定义单次前向传播流程,分离模型推理与损失计算,便于后续反向传播梯度获取。

反向传播与梯度更新(要点 3-1-4)

grad_fn = ms.ops.value_and_grad(forward_fn, None, opt.parameters, has_aux=True)
def train_step(data, label):
    (loss, _), grads = grad_fn(data, label)    # 同时获取损失值和参数梯度
    loss = ops.depend(loss, optimizer(grads))  # 确保梯度更新完成后再返回损失(依赖控制)
    return loss

value_and_grad:MindSpore 内置函数,自动计算forward_fn的输出值和参数梯度,提升反向传播效率。

梯度更新:optimizer(grads)使用 Momentum 优化器更新网络参数,ops.depend保证梯度更新与损失计算的顺序性(避免异步执行错误)。

批量训练与日志输出

size = dataset.get_dataset_size()           # 训练集总样本数(用于进度显示)
for batch, (data, label) in enumerate(dataset.create_tuple_iterator()):
    loss = train_step(data, label)           # 执行单步训练(前向+反向+梯度更新)
    if batch % 100 == 0 or batch == step_size_train - 1:
        print(f"loss: {loss:>7f}  [{current:>3d}/{size:>3d}]")  # 打印训练损失和进度

核心逻辑:按批次迭代训练数据,通过step_size_train控制打印频率,实时监控训练过程。

2. 测试循环 test_loop

模型预测模式设置(要点 3-1-5)

model.set_train(False)  # 关闭训练模式(冻结BN层均值/方差,停用Dropout等)

作用:确保测试时使用稳定的统计量(如 BN 的全局均值 / 方差),而非当前批次的统计量,避免预测结果波动。

批量推理与指标计算

for data, label in dataset.create_tuple_iterator():
    pred = model(data)                          # 模型推理,输出logits
    test_loss += loss_fn(pred, label).asnumpy()  # 累加批次损失
    correct += (pred.argmax(1) == label).sum()   # 统计正确分类样本数
    y_true.extend(label.asnumpy())               # 收集真实标签(用于分类报告)
    y_pred.extend(pred.argmax(1).asnumpy())      # 收集预测标签(用于分类报告)

指标计算:准确率:correct / total,反映模型整体分类能力。

平均损失:test_loss / num_batches,衡量预测值与真实标签的平均差异。

分类报告:使用sklearn.classification_report输出各分类的精确率、召回率、F1 值,便于细粒度评估模型性能。

3.2模型保存

要求:

将训练后的权重保存到代码指定路径下即可

1. 设置条件:得到训练中得到的最优模型权重

2. 利用save_checkpoint API对模型进行保存

3. 模型训练

开始训练,定义收集精确率和损失的承载对象--列表对象(acc、loss都是连续型的,并且有对应关系)

no_improvement_count = 0
acc_list = []
loss_list = []
stop_epoch = num_epochs

for t in range(num_epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train_loop(network, dataset_train, loss_fn, opt)
    acc,loss = test_loop(network, dataset_val, loss_fn)
    acc_list.append(acc)
    loss_list.append(loss)
    # 要点3-2-1:设置条件:利用计算的acc指标,得到训练中得到的最优模型权重
    if best_acc < acc:
        best_acc = acc
        if not os.path.exists(best_ckpt_dir):
            os.mkdir(best_ckpt_dir)
        # 要点3-2-2:利用save_checkpoint API对模型进行保存, 保存的路径为best_ckpt_path
        ms.save_checkpoint(network,best_ckpt_path)
        no_improvement_count = 0
    else:
        no_improvement_count += 1
        if no_improvement_count > patience:
            print('Early stopping triggered. Restoring best weights...')
            stop_epoch = t
            break 
print("Done!")

从训练过程的结果来看,每一轮的损失值都在下降,acc也在相应提高,后续增加训练轮次或者每轮训练的批次(让模型更好的拟合数据),应该可以得到更好的模型分类效果

模型保存代码说明

1. 初始化与变量定义

no_improvement_count:记录验证集准确率连续未提升的轮次数(用于早停判断)。

acc_list/loss_list:保存每轮验证集的准确率和损失(用于后续分析训练趋势)。

stop_epoch:记录提前停止时的轮次(默认等于总轮次num_epochs)。

2. 训练 - 验证主循环

for t in range(num_epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train_loop(network, dataset_train, loss_fn, opt)  # 执行第t+1轮训练
    acc, loss = test_loop(network, dataset_val, loss_fn)  # 验证集评估,获取准确率和损失
    acc_list.append(acc)  # 记录验证准确率
    loss_list.append(loss)  # 记录验证损失

逻辑:按总轮次num_epochs循环,每轮先调用train_loop完成训练,再调用test_loop评估验证集性能,记录指标。

3. 最佳模型保存(要点 3-2-1/3-2-2)

if best_acc < acc:  # 当前轮验证准确率优于历史最佳
    best_acc = acc  # 更新最佳准确率
    if not os.path.exists(best_ckpt_dir):  # 创建保存目录(若不存在)
        os.mkdir(best_ckpt_dir)
    ms.save_checkpoint(network, best_ckpt_path)  # 保存当前模型权重到指定路径
    no_improvement_count = 0  # 重置未提升计数

作用:仅当验证集准确率超过历史最佳时,保存模型权重(resnet50-best.ckpt),确保最终保留的是验证集表现最优的模型。

4. 早停机制(防止过拟合)

else:  # 当前轮验证准确率未提升
    no_improvement_count += 1  # 未提升计数+1
    if no_improvement_count > patience:  # 连续未提升次数超过耐心值(如5轮)
        print('Early stopping triggered. Restoring best weights...')
        stop_epoch = t  # 记录提前停止的轮次
        break  # 终止训练循环

逻辑:若验证集准确率连续patience轮未提升,提前终止训练,避免过拟合(模型在训练集上继续优化但验证集性能下降)。

3.3绘制acc和loss的曲线

import matplotlib.pyplot as plt

def plot_training_process(acc_list, loss_list):
    epochs = range(1, len(acc_list) + 1)
    plt.figure(figsize=(10, 7))
    
    # 绘制训练精度曲线
    plt.subplot(121)
    plt.plot(epochs, acc_list, 'b-', label='Training Accuracy')
    plt.title('Training Accuracy')
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy')
    plt.legend()

    # 绘制损失函数曲线
    plt.subplot(122)
    plt.plot(epochs, loss_list, 'r-', label='Training Loss')
    plt.title('Training Loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()

    plt.subplots_adjust(wspace=0.4)

    plt.show()

plot_training_process(acc_list, loss_list)

acc_list, loss_list 训练过程中记录下来的连续型数值列表


网站公告

今日签到

点亮在社区的每一天
去签到