【人工智能概论】 K折交叉验证
文章目录
【人工智能概论】 K折交叉验证 一. 简单验证及其缺点
二. K折交叉验证 2.1 K折交叉验证的思路 2.2 小细节 2.3 K折交叉验证的缺点 2.4 K折交叉验证的代码
一. 简单验证及其缺点
1.1 简单验证简介
简单验证: 将原始数据集随机划分成训练集和验证集两部分,例,将数据按照7:3的比例分成两部分,70%的样本用于训练模型;30%的样本用于模型验证,如下图。
1.2 简单验证的缺点
数据都只被用了一次; 验证集上计算出来的评估指标与原始分组有很大关系; 对于时序序列,要保存时序信息,往往不能打乱数据的顺序对数据进行随机截取,这就带来了问题,比如总用春、夏、秋的数据做训练,用冬的数据做测试,这显然是有问题的,是不能容忍的。
二. K折交叉验证
为了解决简单交叉验证的不足,引出K折交叉验证,其既可以解决数据集的数据量不够大的问题,也可以解决参数调优的问题。。
2.1 K折交叉验证的思路
首先,将全部样本划分成k个大小相等的样本子集; 依次遍历这k个子集,每次把当前子集作为验证集,其余所有样本作为训练集,进行模型的训练和评估; 最后把k次评估指标的平均值作为最终的评估指标。在实际实验中,k通常取10,如下图。
2.2 小细节
K折交叉验证中有这样一个细节,下一折的训练不是在上一折的基础上进行的,即每训练新的一折都要重新初始化模型参数。
2.3 K折交叉验证的缺点
因为K折交叉验证执行一次训练的总轮数是每一折的训练轮数(epochs)与总折数(K)的乘积,因此训练的成本会翻倍。
2.4 K折交叉验证的代码
import torch
import random
from torch. utils. data import DataLoader, TensorDataset
from Model. ReconsModel. Recoder import ReconsModel, Loss_function
from Model. ModelConfig import ModelConfig
def get_Kfold_data ( k, i, x) :
fold_size = x. size( 0 ) // k
val_start = i * fold_size
if i != k - 1 :
val_end = ( i + 1 ) * fold_size
valid_data = x[ val_start: val_end]
train_data = torch. cat( ( x[ 0 : val_start] , x[ val_end: ] ) , dim= 0 )
else :
valid_data = x[ val_start: ]
train_data = x[ 0 : val_start]
return train_data, valid_data
def train ( model, train_data, valid_data, batch_size, lr, epochs) :
train_loader = DataLoader( TensorDataset( train_data) , batch_size, shuffle= True )
valid_loader = DataLoader( TensorDataset( valid_data) , batch_size, shuffle= True )
criterion = Loss_function( )
optimizer = torch. optim. Adam( params= model. parameters( ) , lr= lr)
train_loss = [ ]
valid_loss = [ ]
for epoch in range ( epochs) :
tra_loss = 0
val_loss = 0
for i , data in enumerate ( train_loader) :
data = torch. stack( data)
data = data. squeeze( 0 )
optimizer. zero_grad( )
recon, mu, log_std = model( data, if_train= True )
loss = criterion. loss_function( recon, data, mu, log_std)
loss. backward( )
optimizer. step( )
tra_loss = tra_loss + loss. item( )
tra_loss = tra_loss / len ( train_data)
train_loss. append( tra_loss)
with torch. no_grad( ) :
for i, data in enumerate ( valid_loader) :
data = torch. stack( data)
data = data. squeeze( 0 )
optimizer. zero_grad( )
recon, mu, log_std = model( data, if_train= False )
test_loss = criterion. loss_function( recon, data, mu, log_std) . item( )
val_loss = val_loss + test_loss
val_loss = val_loss / len ( valid_data)
valid_loss. append( val_loss)
print ( '第 %d 轮, 训练的平均误差为%.3f, 测试的平均误差为%.3f 。' % ( epoch+ 1 , tra_loss, val_loss) )
return train_loss, valid_loss
def k_test ( config, datas) :
valid_loss_sum = 0
for i in range ( config. k) :
model = ReconsModel( config)
print ( '-' * 25 , '第' , i+ 1 , '折' , '-' * 25 )
train_data , valid_data = get_Kfold_data( config. k, i, datas)
train_loss, valid_loss = train( model, train_data, valid_data, config. batch_size, config. lr, config. epochs)
train_loss_ave = sum ( train_loss) / len ( train_loss)
valid_loss_ave = sum ( valid_loss) / len ( valid_loss)
print ( '-*-*-*- 第 %d 折, 平均训练损失%.3f,平均检验损失%.3f -*-*-*-' % ( i+ 1 , train_loss_ave, valid_loss_ave) )
valid_loss_sum = valid_loss_sum + valid_loss_ave
valid_loss_k_ave = valid_loss_sum / config. k
print ( '*' * 60 , )
print ( '基于K折交叉验证的验证损失为%.4f' % valid_loss_k_ave)
if __name__ == "__main__" :
X = torch. rand( 5000 , 16 , 38 )
index = [ i for i in range ( len ( X) ) ]
random. shuffle( index)
X = X[ index]
config = ModelConfig( )
config. load( './Model/config.json' )
k_test( config, X)