当前位置: 首页 > 科技观察

机器学习中的训练和验证指标图能告诉我们什么?

时间:2023-03-16 19:11:20 科技观察

在本文中,我将总结训练和验证可以产生什么,并介绍这些图可以为我们提供什么信息。让我们从一些简单的代码开始。以下代码设置了一个基本的训练管道框架。从sklearn.model_selectionimporttrain_test_splitfromsklearn.datasetsimportmake_classificationimporttorchfromtorch.utils.dataimportDataset,DataLoaderimporttorch.optimastorch_optimporttorch.nnasnnimporttorch.nn.functionalasFimportnumpyasnpimportmatplotlib.pyplotaspltclassMyCustomDataset(数据集):def__init__(self,X,Y,scale=False):self.X=torch.from_numpy(X.astype(np.float32))self.y=torch.from_numpy(Y.astype(np.int64))def__len__(self):返回len(self.y)def__getitem__(self,idx):返回self.X[idx],self.y[idx]defget_optimizer(model,lr=0.001,wd=0.0):参数=filter(lambdap:p.requires_grad,model.parameters())optim=torch_optim.Adam(parameters,lr=lr,weight_decay=wd)returnoptimdeftrain_model(model,optim,train_dl,loss_func):#确保模型在训练模式model.train()total=0sum_loss=0forx,yintrain_dl:batch=y.shape[0]#为这批数据训练模型logits=model(x)#Run损失函数。我们将在调用我们的训练Looploss=loss_func(logits,y)#接下来的3行执行所有PyTorch反向传播goodnessoptim.zero_grad()loss.backward()optim.step()#保持运行检查我们在这个epochtotal+=batch#中的样本总数#并保持我们的losssum_loss+=batch*(loss.item())returnsum_loss/totaldeftrain_loop(model,train_dl,valid_dl,epochs,loss_func,lr=0.1,wd=0):optim=get_optimizer(model,lr=lr,wd=wd)train_loss_list=[]val_loss_list=[]acc_list=[]foriinrange(epochs):loss=train_model(model,optim,train_dl,loss_func)#在训练这个epoch之后,保留一个进度列表#每个epochtrain_loss_list.append(loss)val,acc=val_loss(model,valid_dl,loss_func)#同样对于验证损失和accuracyval_loss_list.append(val)acc_list.append(acc)print("trainingloss:%.5fvalidloss:%.5faccuracy:%.5f"%(loss,val,acc))returntrain_loss_list,val_loss_list,acc_listdefval_loss(model,valid_dl,loss_func):#将模型置于评估模式,而不是训练模式model.eval()total=0sum_loss=0correct=0batch_count=0forx,yinvalid_dl:batch_count+=1current_batch_size=y.shape[0]logits=model(x)loss=loss_func(logits,y)sum_loss+=current_batch_size*(loss.item())total+=current_batch_size#上面的所有代码本质上都是一样的#训练,所以看看那里的评论#找出所有返回的预测中哪个是最响亮的#,这就是我们的预测(s)preds=logits.sigmoid().argmax(1)#看看我们的预测是否正确正确+=(preds==y).float().mean().item()返回sum_loss/total,correct/batch_countdefview_results(train_loss_list,val_loss_list,acc_list):plt.rcParams["figure.figsize"]=(15,5)plt.figure()epochs=np.arange(0,len(train_loss_list))plt.subplot(1,2,1)plt.plot(epochs-0.5,train_loss_list)plt.plot(epochs,val_loss_list)plt.title('modelloss')plt.ylabel('loss')plt.xlabel('epoch')plt.legend(['train','val','acc'],loc='左上')plt.subplot(1,2,2)plt.plot(acc_list)plt.title('accuracy')plt.ylabel('accuracy')plt.xlabel('epoch')plt.legend(['train','val','acc'],loc='左上角')plt.show()defget_data_train_and_show(model,batch_size=128,n_samples=10000,n_classes=2,n_features=30,val_size=0.2,epochs=20,lr=0.1,wd=0,break_it=False):#我们将制作一个虚构的数据集,假设所有相关的#EDA/特征工程已经完成,这是我们的#结果数据X,y=make_classification(n_samples=n_samples,n_classes=n_classes,n_features=n_features,n_informative=n_features,n_redundant=0,random_state=1972)ifbreak_it:#特别搞乱数据X=np.random.rand(n_samples,n_features)X_train,X_val,y_train,y_val=train_test_split(X,y,test_size=val_size,random_state=1972)train_ds=MyCustomDataset(X_train,y_train)valid_ds=MyCustomDataset(X_val,y_val)train_dl=DataLoader(train_ds,batch_size=batch_size,shuffle=True)valid_dl=DataLoader(valid_ds,batch_size=batch_size,shuffle=True)train_loss_list,val_loss_list,acc_list=train_loop(模型,train_dlepochs,loss_func=F.cross_entropy,lr=lr,wd=wd)view_results(train_loss_list,val_loss_list,acc_list)上面的代码很简单,就是一个获取数据、训练、验证的基本过程。下面开始进入主题场景1——模型看似学习了,但是在验证或者准确率上表现不佳。不管超参数如何,模型Trainloss在慢慢减小,但是Valloss却没有,它的Accuracy也不代表它在学习什么。例如,在这种情况下,二进制分类准确率徘徊在50%左右。类Scenario_1_Model_1(nn.Module):def__init__(self,in_features=30,out_features=2):super().__init__()self.lin1=nn.Linear(in_features,out_features)defforward(self,x):x=self.lin1(x)returnxget_data_train_and_show(Scenario_1_Model_1(),lr=0.001,break_it=True)Thereisnotenoughinformationinthedatatoallow'learning',训练数据可能没有包含足够的信息让模型“学习”'.在这种情况下(随机数据作为代码中的训练数据),这意味着它无法学到任何实质性的东西。数据必须有足够的信息才能从中学习。EDA和特征工程是关键!模型学习可以学到的东西,而不是弥补不存在的东西。场景2——训练、验证和准确度曲线非常不稳定。例如下面的代码:lr=0.1,bs=128classScenario_2_Model_1(nn.Module):def__init__(self,in_features=30,out_features=2):super()。__init__()self.lin1=nn.Linear(in_features,out_features)defforward(self,x):x=self.lin1(x)returnxget_data_train_and_show(Scenario_2_Model_1(),lr=0.1)“学习率太高”或“Batchesaretoosmall”尝试将学习率从0.1降低到0.001,这意味着它不会“反弹”,而是平稳下降。get_data_train_and_show(Scenario_1_Model_1(),lr=0.001)除了降低学习率外,增加batchsize也会使其更平滑。get_data_train_and_show(Scenario_1_Model_1(),lr=0.001,batch_size=256)场景3——训练损失接近于零,准确率看起来不错,但是验证并没有下降,还增加了类Scenario_3_Model_1(nn.Module):def__init__(self,in_features=30,out_features=2):super().__init__()self.lin1=nn.Linear(in_features,50)self.lin2=nn.Linear(50,150)self.lin3=嗯。Linear(150,50)self.lin4=nn.Linear(50,out_features)defforward(self,x):x=F.relu(self.lin1(x))x=F.relu(self.lin2(x))x=F.relu(self.lin3(x))x=self.lin4(x)returnxget_data_train_and_show(Scenario_3_Model_1(),lr=0.001)这绝对是过拟合:低训练损失和高准确性,验证损失和训练损失越来越大,这是经典的过拟合指标。从根本上说,您的模型学习得太多了。它对训练数据的记忆太好,这意味着它也不能泛化到新数据。我们可以尝试的第一件事是降低模型的复杂性。类Scenario_3_Model_2(nn.Module):def__init__(self,in_features=30,out_features=2):super().__init__()self.lin1=nn.Linear(in_features,50)self.lin2=nn.Linear(50,out_features)defforward(self,x):x=F.relu(self.lin1(x))x=self.lin2(x)returnxget_data_train_and_show(Scenario_3_Model_2(),lr=0.001)这样就更好了您还可以引入L2权重衰减正则化以使其再次变得更好(对于较浅的模型)。get_data_train_and_show(Scenario_3_Model_2(),lr=0.001,wd=0.02)如果我们想保持模型的深度和大小,我们可以尝试使用dropout(对于更深的模型)。类Scenario_3_Model_3(nn.Module):def__init__(self,in_features=30,out_features=2):super().__init__()self.lin1=nn.Linear(in_features,50)self.lin2=nn.Linear(50,150)self.lin3=nn.Linear(150,50)self.lin4=nn.Linear(50,out_features)self.drops=nn.Dropout(0.4)defforward(self,x):x=F.relu(self.lin1(x))x=self.drops(x)x=F.relu(self.lin2(x))x=self.drops(x)x=F.relu(self.lin3(x))x=self.drops(x)x=self.lin4(x)returnxget_data_train_and_show(Scenario_3_Model_3(),lr=0.001)场景4-训练和验证表现良好,但精度没有提高lr=0.001,bs=128(默认,分类类别=5classScenario_4_Model_1(nn.Module):def__init__(self,in_features=30,out_features=2):super().__init__()self.lin1=nn.Linear(in_features,2)self.lin2=nn.Linear(2,out_features)defforward(self,x):x=F.relu(self.lin1(x))x=self.lin2(x)returnxget_data_train_and_show(Scenario_4_Model_1(out_features=5),lr=0.001,n_classes=5)学习不足capacity:模型中某一层的参数少于模型可能的输出类。在这种情况下,当有5个可能的输出类时,中间层只有2个参数。这意味着模型会丢失信息,因为它必须通过一个较小的层来填充它,一旦层的参数再次扩大,就很难恢复这些信息。所以记录层的参数永远不能小于模型的输出大小。类Scenario_4_Model_2(nn.Module):def__init__(self,in_features=30,out_features=2):super().__init__()self.lin1=nn.Linear(in_features,50)self.lin2=nn.Linear(50,out_features)defforward(self,x):x=F.relu(self.lin1(x))x=self.lin2(x)returnxget_data_train_and_show(Scenario_4_Model_2(out_features=5),lr=0.001,n_classes=5)综上所述,以上是一些常见的训练和验证曲线的例子。希望大家在遇到相同情况时能够迅速定位并改进。