[Einführung in die künstliche Intelligenz] K-fache Kreuzvalidierung

[Einführung in die künstliche Intelligenz] K-fache Kreuzvalidierung


1. Einfache Überprüfung und ihre Mängel

1.1 Einführung in die einfache Verifizierung

  • Einfache Überprüfung: Teilen Sie den Originaldatensatz nach dem Zufallsprinzip in einen Trainingssatz und einen Überprüfungssatz auf. Teilen Sie die Daten beispielsweise entsprechend dem Verhältnis 7:3 in zwei Teile auf. 70 % der Stichproben werden zum Trainieren des Modells verwendet, 30 % der Proben werden zur Modellverifizierung verwendet. Wie unten gezeigt.

1.2 Nachteile der einfachen Verifizierung

  • Die Daten werden nur einmal verwendet;
  • Die anhand des Validierungssatzes berechneten Bewertungsmetriken weisen eine starke Beziehung zur ursprünglichen Gruppierung auf.
  • Bei Zeitreihensequenzen ist es zum Speichern von Zeitreiheninformationen oft nicht möglich, die Reihenfolge der Daten zu stören und die Daten zufällig abzufangen. Dies bringt Probleme mit sich. Beispielsweise werden Frühlings-, Sommer- und Herbstdaten immer für das Training verwendet Für die Tests werden Winterdaten verwendet. Dies ist offensichtlich problematisch und kann nicht toleriert werden.

2. K-fache Kreuzvalidierung

  • Um die Mängel der einfachen Kreuzvalidierung zu beheben, wird die K-fache Kreuzvalidierung eingeführt, die nicht nur das Problem unzureichender Datenmenge im Datensatz, sondern auch das Problem der Parameteroptimierung lösen kann. .

2.1 Die Idee der K-fachen Kreuzvalidierung

  1. Teilen Sie zunächst alle Stichproben in k Stichprobenteilmengen gleicher Größe auf.
  2. Die k Teilmengen werden nacheinander durchlaufen, und jedes Mal wird die aktuelle Teilmenge als Verifizierungssatz und alle verbleibenden Stichproben als Trainingssatz zum Trainieren und Bewerten des Modells verwendet.
  3. Schließlich wird der Durchschnitt von k Bewertungsindikatoren als endgültiger Bewertungsindikator verwendet. In tatsächlichen Experimenten beträgt k normalerweise 10, wie in der folgenden Abbildung dargestellt.

Fügen Sie hier eine Bildbeschreibung ein

2.2 Kleine Details

  • Bei der K-Falten-Kreuzvalidierung gibt es ein solches Detail, dass das Training der nächsten Falte nicht auf der vorherigen Falte basiert, d. h. die Modellparameter müssen für jede neue Falte neu initialisiert werden.
  • Die K-fache Kreuzvalidierung kann nur zur Verifizierung verwendet werden, sodass ihre Ergebnisse nicht als Grundlage zum Speichern und Beurteilen von Modellparametern verwendet werden können. Sie kann jedoch verwendet werden, um die Kombination von Superparametern zu bestimmen und die Modellstruktur anzupassen Initialisieren Sie das Modell für das Training neu. Bessere Modellparameter.
  • Bei Daten mit sequentiellen Informationen muss geprüft werden, ob zwischen verschiedenen Faltungen ein erheblicher Leistungsunterschied besteht.

2.3 Nachteile der K-fachen Kreuzvalidierung

  • Da die Gesamtzahl der Epochen, die für die K-fache Kreuzvalidierung zur Durchführung einer Trainingssitzung erforderlich sind, das Produkt aus der Anzahl der Trainingsepochen für jede Falte und der Gesamtzahl der Falten (K) ist, werden die Trainingskosten verdoppelt.

2.4 K-facher Kreuzvalidierungscode

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是折数,取第i+1折,x是特征数据
    fold_size = x.size(0) // k  # 计算每一折中的数据数量
    val_start = i * fold_size  # 第 i+1折 数据的测试集初始数据编号
    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:  # 如果是最后一折,数据的分配策略,主要涉及到不能K整除时,多出的数据如何处理
        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):
    # 数据准备
    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)

    # 记录每一个epoch的平均损失
    train_loss = []
    valid_loss = []


    for epoch in range(epochs):
        tra_loss = 0
        val_loss = 0
        for i , data in enumerate(train_loader):

            # 假设数据的处理 此时的data是list类型的数据,转化成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):

                # 假设数据的处理 此时的data是list类型的数据,转化成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是总折数,
    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  # 基于K折交叉验证的验证损失
    print('*' * 60, )
    print('基于K折交叉验证的验证损失为%.4f'%valid_loss_k_ave)




if __name__ == "__main__":
    # 创建数据集,或者说数据集只要是这样的形式即可
    X = torch.rand(5000, 16, 38)  # 5000条数据,,每条有16个时间步,每步38个特征,时序数据

    # 随机打乱
    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)

Guess you like

Origin blog.csdn.net/qq_44928822/article/details/130515839