【人工智能概論】 K折交叉驗證
一. 簡單驗證及其缺點
1.1 簡單驗證簡介
- 簡單驗證: 將原始數(shù)據(jù)集隨機劃分成訓練集和驗證集兩部分,例,將數(shù)據(jù)按照7:3的比例分成兩部分,70%的樣本用于訓練模型;30%的樣本用于模型驗證,如下圖。
文章來源:http://www.zghlxwxcb.cn/news/detail-720070.html
1.2 簡單驗證的缺點
- 數(shù)據(jù)都只被用了一次;
- 驗證集上計算出來的評估指標與原始分組有很大關系;
- 對于時序序列,要保存時序信息,往往不能打亂數(shù)據(jù)的順序對數(shù)據(jù)進行隨機截取,這就帶來了問題,比如總用春、夏、秋的數(shù)據(jù)做訓練,用冬的數(shù)據(jù)做測試,這顯然是有問題的,是不能容忍的。
二. K折交叉驗證
- 為了解決簡單交叉驗證的不足,引出K折交叉驗證,其既可以解決數(shù)據(jù)集的數(shù)據(jù)量不夠大的問題,也可以解決參數(shù)調優(yōu)的問題。。
2.1 K折交叉驗證的思路
- 首先,將全部樣本劃分成k個大小相等的樣本子集;
- 依次遍歷這k個子集,每次把當前子集作為驗證集,其余所有樣本作為訓練集,進行模型的訓練和評估;
- 最后把k次評估指標的平均值作為最終的評估指標。在實際實驗中,k通常取10,如下圖。
文章來源地址http://www.zghlxwxcb.cn/news/detail-720070.html
2.2 小細節(jié)
- K折交叉驗證中有這樣一個細節(jié),下一折的訓練不是在上一折的基礎上進行的,即每訓練新的一折都要重新初始化模型參數(shù)。
- K折交叉驗證只能做驗證使用,因此不能根據(jù)它的結果做為模型參數(shù)的保存判斷依據(jù),但可以基于它做超參組合的確定與模型結構的調整,然后再重新初始化模型,進行訓練得到較好的模型參數(shù)。
- 對于有時序信息的數(shù)據(jù),要看看不同折之間性能表現(xiàn)會不會有明顯差距。
2.3 K折交叉驗證的缺點
- 因為K折交叉驗證執(zhí)行一次訓練的總輪數(shù)是每一折的訓練輪數(shù)(epochs)與總折數(shù)(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
# 返回第 i+1 折(i取 0 ~ k-1)的訓練集(train)與驗證集(valid)
def get_Kfold_data(k, i, x): # k是折數(shù),取第i+1折,x是特征數(shù)據(jù)
fold_size = x.size(0) // k # 計算每一折中的數(shù)據(jù)數(shù)量
val_start = i * fold_size # 第 i+1折 數(shù)據(jù)的測試集初始數(shù)據(jù)編號
if i != k - 1: # 不是最后一折的話,數(shù)據(jù)的分配策略
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: # 如果是最后一折,數(shù)據(jù)的分配策略,主要涉及到不能K整除時,多出的數(shù)據(jù)如何處理
valid_data = x[val_start:] # 實際上,多出來的樣本,都放在最后一折里了
train_data = x[0: val_start]
return train_data, valid_data
# k折交叉驗證,某一折的訓練
def train(model, train_data, valid_data, batch_size, lr,epochs):
# 數(shù)據(jù)準備
train_loader = DataLoader(TensorDataset(train_data), batch_size, shuffle=True)
valid_loader = DataLoader(TensorDataset(valid_data), batch_size, shuffle=True)
# 損失函數(shù),優(yōu)化函數(shù)的準備
criterion = Loss_function()
optimizer = torch.optim.Adam(params=model.parameters(), lr=lr)
# 記錄每一個epoch的平均損失
train_loss = []
valid_loss = []
for epoch in range(epochs):
tra_loss = 0
val_loss = 0
for i , data in enumerate(train_loader):
# 假設數(shù)據(jù)的處理 此時的data是list類型的數(shù)據(jù),轉化成Tensor,并且把多出來的第0維去掉
data = torch.stack(data)
data = data.squeeze(0)
optimizer.zero_grad() # 梯度清零
recon, mu, log_std = model(data, if_train=True) # if_train不能少
# 計算損失
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):
# 假設數(shù)據(jù)的處理 此時的data是list類型的數(shù)據(jù),轉化成Tensor,并且把多出來的第0維去掉
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
# k折交叉驗證
def k_test(config, datas): # k是總折數(shù),
valid_loss_sum = 0
for i in range(config.k):
model = ReconsModel(config) # 細節(jié),每一折,并不是在上一折訓練好的模型基礎上繼續(xù)訓練,而是重新訓練
print('-'*25,'第',i+1,'折','-'*25)
train_data , valid_data = get_Kfold_data(config.k, i, datas) # 獲取某一折的訓練數(shù)據(jù)、測試數(shù)據(jù)
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 # 基于K折交叉驗證的驗證損失
print('*' * 60, )
print('基于K折交叉驗證的驗證損失為%.4f'%valid_loss_k_ave)
if __name__ == "__main__":
# 創(chuàng)建數(shù)據(jù)集,或者說數(shù)據(jù)集只要是這樣的形式即可
X = torch.rand(5000, 16, 38) # 5000條數(shù)據(jù),,每條有16個時間步,每步38個特征,時序數(shù)據(jù)
# 隨機打亂
index = [i for i in range(len(X))]
random.shuffle(index)
X = X[index] # 要是有標簽的話,index要對得上
config = ModelConfig()
config.load('./Model/config.json')
k_test(config, X)
到了這里,關于【人工智能概論】 K折交叉驗證的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!