厦门网站推广步骤机构,自己做自己的私人网站,京津冀协同发展的基础,中国广东手机网站建设文章目录Day 40 早停策略与模型权重的保存1. 基线训练流程2. 同步监控测试集2.1 过拟合的典型特征3. 模型权重的保存与加载3.1 仅保存模型参数#xff08;推荐#xff09;3.2 加载已保存的参数3.3 保存整个模型3.4 保存训练状态#xff08;Checkpoint#xff09;4. 早停策…文章目录Day 40 · 早停策略与模型权重的保存1. 基线训练流程2. 同步监控测试集2.1 过拟合的典型特征3. 模型权重的保存与加载3.1 仅保存模型参数推荐3.2 加载已保存的参数3.3 保存整个模型3.4 保存训练状态Checkpoint4. 早停策略Early Stopping4.1 逻辑流程4.2 小结Day 40 · 早停策略与模型权重的保存示例使用 Iris 数据集 简单 MLP默认在 CPU 运行如使用 GPU 请修改device。1. 基线训练流程复习数据预处理、模型定义、训练循环、损失可视化以及测试集评估的完整套路。importtorchimporttorch.nnasnnimporttorch.optimasoptimfromsklearn.datasetsimportload_irisfromsklearn.model_selectionimporttrain_test_splitfromsklearn.preprocessingimportMinMaxScalerimporttimeimportmatplotlib.pyplotaspltfromtqdmimporttqdm# 导入tqdm库用于进度条显示importwarnings warnings.filterwarnings(ignore)# 忽略警告信息# 设置设备devicecpuprint(f使用设备:{device})# 加载鸢尾花数据集irisload_iris()Xiris.data# 特征数据yiris.target# 标签数据# 划分训练集和测试集X_train,X_test,y_train,y_testtrain_test_split(X,y,test_size0.2,random_state42)# 归一化数据scalerMinMaxScaler()X_trainscaler.fit_transform(X_train)X_testscaler.transform(X_test)# 将数据转换为PyTorch张量并移至GPUX_traintorch.FloatTensor(X_train).to(device)y_traintorch.LongTensor(y_train).to(device)X_testtorch.FloatTensor(X_test).to(device)y_testtorch.LongTensor(y_test).to(device)classMLP(nn.Module):def__init__(self):super(MLP,self).__init__()self.fc1nn.Linear(4,10)# 输入层到隐藏层self.relunn.ReLU()self.fc2nn.Linear(10,3)# 隐藏层到输出层defforward(self,x):outself.fc1(x)outself.relu(out)outself.fc2(out)returnout# 实例化模型并移至GPUmodelMLP().to(device)# 分类问题使用交叉熵损失函数criterionnn.CrossEntropyLoss()# 使用随机梯度下降优化器optimizeroptim.SGD(model.parameters(),lr0.01)# 训练模型num_epochs20000# 训练的轮数# 用于存储每100个epoch的损失值和对应的epoch数losses[]epochs[]start_timetime.time()# 记录开始时间# 创建tqdm进度条withtqdm(totalnum_epochs,desc训练进度,unitepoch)aspbar:# 训练模型forepochinrange(num_epochs):# 前向传播outputsmodel(X_train)# 隐式调用forward函数losscriterion(outputs,y_train)# 反向传播和优化optimizer.zero_grad()loss.backward()optimizer.step()# 记录损失值并更新进度条if(epoch1)%2000:losses.append(loss.item())epochs.append(epoch1)# 更新进度条的描述信息pbar.set_postfix({Loss:f{loss.item():.4f}})# 每1000个epoch更新一次进度条if(epoch1)%10000:pbar.update(1000)# 更新进度条# 确保进度条达到100%ifpbar.nnum_epochs:pbar.update(num_epochs-pbar.n)# 计算剩余的进度并更新time_alltime.time()-start_time# 计算训练时间print(fTraining time:{time_all:.2f}seconds)# 可视化损失曲线plt.figure(figsize(10,6))plt.plot(epochs,losses)plt.xlabel(Epoch)plt.ylabel(Loss)plt.title(Training Loss over Epochs)plt.grid(True)plt.show()# 在测试集上评估模型此时model内部已经是训练好的参数了# 评估模型model.eval()# 设置模型为评估模式withtorch.no_grad():# torch.no_grad()的作用是禁用梯度计算可以提高模型推理速度outputsmodel(X_test)# 对测试数据进行前向传播获得预测结果_,predictedtorch.max(outputs,1)# torch.max(outputs, 1)返回每行的最大值和对应的索引#这个函数返回2个值分别是最大值和对应索引参数1是在第1维度行上找最大值_ 是Python的约定表示忽略这个返回值所以这个写法是找到每一行最大值的下标# 此时outputs是一个tensorp每一行是一个样本每一行有3个值分别是属于3个类别的概率取最大值的下标就是预测的类别# predicted y_test判断预测值和真实值是否相等返回一个tensor1表示相等0表示不等然后求和再除以y_test.size(0)得到准确率# 因为这个时候数据是tensor所以需要用item()方法将tensor转化为Python的标量# 之所以不用sklearn的accuracy_score函数是因为这个函数是在CPU上运行的需要将数据转移到CPU上这样会慢一些# size(0)获取第0维的长度即样本数量correct(predictedy_test).sum().item()# 计算预测正确的样本数accuracycorrect/y_test.size(0)print(f测试集准确率:{accuracy*100:.2f}%)使用设备: cpu 训练进度: 100%|██████████| 20000/20000 [00:0400:00, 4001.73epoch/s, Loss0.0666] Training time: 5.00 seconds测试集准确率: 96.67%2. 同步监控测试集训练集 loss 持续下降 ≠ 模型泛化良好测试集可能在增大说明过拟合。实战中最好同步记录测试集 loss/指标并用一张图观察双方走势。2.1 过拟合的典型特征正常训练/测试损失一起下降直至稳定。过拟合训练损失继续下降而测试损失上升或震荡不再下降。importtorchimporttorch.nnasnnimporttorch.optimasoptimfromsklearn.datasetsimportload_irisfromsklearn.model_selectionimporttrain_test_splitfromsklearn.preprocessingimportMinMaxScalerimporttimeimportmatplotlib.pyplotaspltfromtqdmimporttqdm# 导入tqdm库用于进度条显示importwarnings warnings.filterwarnings(ignore)# 忽略警告信息# 设置设备devicecpuprint(f使用设备:{device})# 加载鸢尾花数据集irisload_iris()Xiris.data# 特征数据yiris.target# 标签数据# 划分训练集和测试集X_train,X_test,y_train,y_testtrain_test_split(X,y,test_size0.2,random_state42)# 归一化数据scalerMinMaxScaler()X_trainscaler.fit_transform(X_train)X_testscaler.transform(X_test)# 将数据转换为PyTorch张量并移至GPUX_traintorch.FloatTensor(X_train).to(device)y_traintorch.LongTensor(y_train).to(device)X_testtorch.FloatTensor(X_test).to(device)y_testtorch.LongTensor(y_test).to(device)classMLP(nn.Module):def__init__(self):super(MLP,self).__init__()self.fc1nn.Linear(4,10)# 输入层到隐藏层self.relunn.ReLU()self.fc2nn.Linear(10,3)# 隐藏层到输出层defforward(self,x):outself.fc1(x)outself.relu(out)outself.fc2(out)returnout# 实例化模型并移至GPUmodelMLP().to(device)# 分类问题使用交叉熵损失函数criterionnn.CrossEntropyLoss()# 使用随机梯度下降优化器optimizeroptim.SGD(model.parameters(),lr0.01)# 训练模型num_epochs20000# 训练的轮数# 用于存储每200个epoch的损失值和对应的epoch数train_losses[]# 存储训练集损失test_losses[]# 新增存储测试集损失epochs[]start_timetime.time()# 记录开始时间# 创建tqdm进度条withtqdm(totalnum_epochs,desc训练进度,unitepoch)aspbar:# 训练模型forepochinrange(num_epochs):# 前向传播outputsmodel(X_train)# 隐式调用forward函数train_losscriterion(outputs,y_train)# 反向传播和优化optimizer.zero_grad()train_loss.backward()optimizer.step()# 记录损失值并更新进度条if(epoch1)%2000:# 计算测试集损失新增代码model.eval()withtorch.no_grad():test_outputsmodel(X_test)test_losscriterion(test_outputs,y_test)model.train()train_losses.append(train_loss.item())test_losses.append(test_loss.item())epochs.append(epoch1)# 更新进度条的描述信息pbar.set_postfix({Train Loss:f{train_loss.item():.4f},Test Loss:f{test_loss.item():.4f}})# 每1000个epoch更新一次进度条if(epoch1)%10000:pbar.update(1000)# 更新进度条# 确保进度条达到100%ifpbar.nnum_epochs:pbar.update(num_epochs-pbar.n)# 计算剩余的进度并更新time_alltime.time()-start_time# 计算训练时间print(fTraining time:{time_all:.2f}seconds)# 可视化损失曲线plt.figure(figsize(10,6))plt.plot(epochs,train_losses,labelTrain Loss)# 原始代码已有plt.plot(epochs,test_losses,labelTest Loss)# 新增测试集损失曲线plt.xlabel(Epoch)plt.ylabel(Loss)plt.title(Training and Test Loss over Epochs)plt.legend()# 新增显示图例plt.grid(True)plt.show()# 在测试集上评估模型此时model内部已经是训练好的参数了# 评估模型model.eval()# 设置模型为评估模式withtorch.no_grad():# torch.no_grad()的作用是禁用梯度计算可以提高模型推理速度outputsmodel(X_test)# 对测试数据进行前向传播获得预测结果_,predictedtorch.max(outputs,1)# torch.max(outputs, 1)返回每行的最大值和对应的索引correct(predictedy_test).sum().item()# 计算预测正确的样本数accuracycorrect/y_test.size(0)print(f测试集准确率:{accuracy*100:.2f}%)使用设备: cpu 训练进度: 100%|██████████| 20000/20000 [00:0800:00, 2344.32epoch/s, Train Loss0.0635, Test Loss0.0599] Training time: 8.53 seconds测试集准确率: 96.67%3. 模型权重的保存与加载深度学习训练通常需要定期持久化模型或训练状态常见颗粒度如下。3.1 仅保存模型参数推荐保存内容state_dict权重参数。特点文件小、最常用但加载前需重新定义与训练一致的模型结构。# 保存模型参数torch.save(model.state_dict(),model_weights.pth)3.2 加载已保存的参数步骤先构造同一模型再load_state_dict。适用推理或继续训练配合model.eval()进入推理模式。# 加载参数需先定义模型结构modelMLP()# 初始化与训练时相同的模型结构model.load_state_dict(torch.load(model_weights.pth))# model.eval() # 切换至推理模式可选All keys matched successfully3.3 保存整个模型保存内容结构 参数。优点加载时无需重新定义类缺点是文件大且依赖原始代码环境自定义层可能报错。# 保存整个模型torch.save(model,full_model.pth)# 加载模型无需提前定义类但需确保环境一致modeltorch.load(full_model.pth,weights_onlyFalse)model.eval()# 切换至推理模式可选MLP( (fc1): Linear(in_features4, out_features10, biasTrue) (relu): ReLU() (fc2): Linear(in_features10, out_features3, biasTrue) )3.4 保存训练状态Checkpoint保存内容模型参数、优化器状态、当前 epoch、loss 等可用于断点续训。适合长时间训练或需要频繁中断的场景也方便配合早停策略回退到最佳点。# # 保存训练状态# checkpoint {# model_state_dict: model.state_dict(),# optimizer_state_dict: optimizer.state_dict(),# epoch: epoch,# loss: best_loss,# }# torch.save(checkpoint, checkpoint.pth)# # 加载并续训# model MLP()# optimizer torch.optim.Adam(model.parameters())# checkpoint torch.load(checkpoint.pth)# model.load_state_dict(checkpoint[model_state_dict])# optimizer.load_state_dict(checkpoint[optimizer_state_dict])# start_epoch checkpoint[epoch] 1 # 从下一轮开始训练# best_loss checkpoint[loss]# # 继续训练循环# for epoch in range(start_epoch, num_epochs):# train(model, optimizer, ...)4. 早停策略Early Stopping目标当验证集表现连续多次无提升时提前结束训练避免对训练集过拟合。核心持续监控验证损失或指标记录最佳结果并保存模型。4.1 逻辑流程维护best_loss与counter每隔固定 epoch 评估一次验证集。若当前 loss 更低则更新best_loss、清零counter、保存模型。否则counter 1当counter patience时触发早停。importtorchimporttorch.nnasnnimporttorch.optimasoptimfromsklearn.datasetsimportload_irisfromsklearn.model_selectionimporttrain_test_splitfromsklearn.preprocessingimportMinMaxScalerimporttimeimportmatplotlib.pyplotaspltfromtqdmimporttqdm# 导入tqdm库用于进度条显示importwarnings warnings.filterwarnings(ignore)# 忽略警告信息# 设置GPU设备devicetorch.device(cuda:0iftorch.cuda.is_available()elsecpu)print(f使用设备:{device})# 加载鸢尾花数据集irisload_iris()Xiris.data# 特征数据yiris.target# 标签数据# 划分训练集和测试集X_train,X_test,y_train,y_testtrain_test_split(X,y,test_size0.2,random_state42)# 归一化数据scalerMinMaxScaler()X_trainscaler.fit_transform(X_train)X_testscaler.transform(X_test)# 将数据转换为PyTorch张量并移至GPUX_traintorch.FloatTensor(X_train).to(device)y_traintorch.LongTensor(y_train).to(device)X_testtorch.FloatTensor(X_test).to(device)y_testtorch.LongTensor(y_test).to(device)classMLP(nn.Module):def__init__(self):super(MLP,self).__init__()self.fc1nn.Linear(4,10)# 输入层到隐藏层self.relunn.ReLU()self.fc2nn.Linear(10,3)# 隐藏层到输出层defforward(self,x):outself.fc1(x)outself.relu(out)outself.fc2(out)returnout# 实例化模型并移至GPUmodelMLP().to(device)# 分类问题使用交叉熵损失函数criterionnn.CrossEntropyLoss()# 使用随机梯度下降优化器optimizeroptim.SGD(model.parameters(),lr0.01)# 训练模型num_epochs20000# 训练的轮数# 用于存储每200个epoch的损失值和对应的epoch数train_losses[]# 存储训练集损失test_losses[]# 存储测试集损失epochs[]# 新增早停相关参数 best_test_lossfloat(inf)# 记录最佳测试集损失best_epoch0# 记录最佳epochpatience50# 早停耐心值连续多少轮测试集损失未改善时停止训练counter0# 早停计数器early_stoppedFalse# 是否早停标志# start_timetime.time()# 记录开始时间# 创建tqdm进度条withtqdm(totalnum_epochs,desc训练进度,unitepoch)aspbar:# 训练模型forepochinrange(num_epochs):# 前向传播outputsmodel(X_train)# 隐式调用forward函数train_losscriterion(outputs,y_train)# 反向传播和优化optimizer.zero_grad()train_loss.backward()optimizer.step()# 记录损失值并更新进度条if(epoch1)%2000:# 计算测试集损失model.eval()withtorch.no_grad():test_outputsmodel(X_test)test_losscriterion(test_outputs,y_test)model.train()train_losses.append(train_loss.item())test_losses.append(test_loss.item())epochs.append(epoch1)# 更新进度条的描述信息pbar.set_postfix({Train Loss:f{train_loss.item():.4f},Test Loss:f{test_loss.item():.4f}})# 新增早停逻辑 iftest_loss.item()best_test_loss:# 如果当前测试集损失小于最佳损失best_test_losstest_loss.item()# 更新最佳损失best_epochepoch1# 更新最佳epochcounter0# 重置计数器# 保存最佳模型torch.save(model.state_dict(),best_model.pth)else:counter1ifcounterpatience:print(f早停触发在第{epoch1}轮测试集损失已有{patience}轮未改善。)print(f最佳测试集损失出现在第{best_epoch}轮损失值为{best_test_loss:.4f})early_stoppedTruebreak# 终止训练循环# # 每1000个epoch更新一次进度条if(epoch1)%10000:pbar.update(1000)# 更新进度条# 确保进度条达到100%ifpbar.nnum_epochs:pbar.update(num_epochs-pbar.n)# 计算剩余的进度并更新time_alltime.time()-start_time# 计算训练时间print(fTraining time:{time_all:.2f}seconds)# 新增加载最佳模型用于最终评估 ifearly_stopped:print(f加载第{best_epoch}轮的最佳模型进行最终评估...)model.load_state_dict(torch.load(best_model.pth))# # 可视化损失曲线plt.figure(figsize(10,6))plt.plot(epochs,train_losses,labelTrain Loss)plt.plot(epochs,test_losses,labelTest Loss)plt.xlabel(Epoch)plt.ylabel(Loss)plt.title(Training and Test Loss over Epochs)plt.legend()plt.grid(True)plt.show()# 在测试集上评估模型model.eval()withtorch.no_grad():outputsmodel(X_test)_,predictedtorch.max(outputs,1)correct(predictedy_test).sum().item()accuracycorrect/y_test.size(0)print(f测试集准确率:{accuracy*100:.2f}%)使用设备: cuda:0 训练进度: 100%|██████████| 20000/20000 [00:1300:00, 1461.66epoch/s, Train Loss0.0604, Test Loss0.0509] Training time: 13.68 seconds测试集准确率: 96.67%4.2 小结patience过大时可能观察不到早停适当调小可更快触发。若训练结束未早停说明测试集损失尚未出现连续恶化。最终部署/复训建议加载保存的 checkpoint而非最后一次参数。浙大疏锦行