8.4.2 交叉验证优化
交叉验证(Cross-Validation)是一种用于评估模型性能的技术,它在有限的数据集上更准确地估计模型的性能,并帮助选择最佳的模型和超参数。交叉验证通过将数据集划分为多个子集,轮流使用其中一个子集作为验证集,其余子集作为训练集,从而多次训练和验证模型。
最常见的交叉验证方法是 K 折交叉验证(K-Fold Cross-Validation)。在 K-Fold 交叉验证中,数据集被均匀地划分为 K 个子集,每次使用其中一个子集作为验证集,其他 K-1 个子集作为训练集,重复进行 K 次。每次训练和验证都会得到一个性能评价指标,例如准确率或均方误差。最终,将 K 次评价指标的平均值作为模型在整个数据集上的性能估计。例如下面是一个使用 K-Fold 实现交叉验证的例子,演示了在 TensorFlow 中使用交叉验证创建和训练模型的过程。
实例8-3:TensorFlow使用交叉验证创建和训练模型(源码路径:daima/8/jiao.py)
实例文件jiao.py的主要实现代码如下所示。
import numpy as np
from sklearn.model_selection import KFold
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
# 生成示例数据
num_samples = 1000
input_dim = 10
output_dim = 1
X = np.random.rand(num_samples, input_dim)
y = np.random.randint(2, size=(num_samples, output_dim)) # 模拟二分类标签
# 设置 K-Fold 参数
num_folds = 5
kf = KFold(n_splits=num_folds, shuffle=True, random_state=42)
# 创建神经网络模型
def create_model():
model = Sequential([
Dense(32, activation='relu', input_shape=(input_dim,)),
Dense(16, activation='relu'),
Dense(output_dim, activation='sigmoid')
])
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
return model
# 进行 K-Fold 交叉验证
fold = 1
for train_idx, val_idx in kf.split(X):
print(f"Fold {fold}")
X_train, X_val = X[train_idx], X[val_idx]
y_train, y_val = y[train_idx], y[val_idx]
model = create_model()
model.fit(X_train, y_train, batch_size=32, epochs=50, validation_data=(X_val, y_val))
val_loss, val_accuracy = model.evaluate(X_val, y_val)
print(f"Validation Loss: {val_loss:.4f}, Validation Accuracy: {val_accuracy:.4f}")
fold += 1
对上述代码的具体说明如下:
- 生成示例数据。
- 使用 KFold 类进行 K-Fold 交叉验证的设置,其中 n_splits 参数表示将数据集分成几个子集。
- 创建一个函数 create_model 来构建神经网络模型。
- 使用交叉验证迭代进行训练和验证。在每个折叠中,我们根据训练索引和验证索引划分数据集,创建模型并进行训练和验证。同时,我们还计算了每个折叠的验证集性能指标。
注意:这个例子展示了如何使用 K-Fold 交叉验证来评估神经网络模型的性能,并且对每个折叠的性能进行了输出。这样的交叉验证可以更好地了解模型的性能稳定性和泛化能力。但是大家需要注意,虽然交叉验证可以更好地评估模型的性能,并提供对模型的稳定性和泛化能力的更准确估计。然而,交叉验证需要多次训练和验证模型,因此在计算资源有限的情况下,可能会消耗大量的时间和计算资源。另外,在大规模数据集上执行交叉验证可能会变得非常耗时,并且可能并不是必要的。在这些情况下,使用单独的验证集和测试集进行评估可能更实际。
在PyTorch中,可以使用库Scikit-learn中的类KFold来实现交叉验证功能。例如下面是一个例子,展示了使用交叉验证创建、训练和优化模型的过程。
实例8-4:PyTorch使用交叉验证创建、训练、优化模型(源码路径:daima/8/pyjiao1.py)
实例文件pyjiao1.py的主要实现代码如下所示。
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.model_selection import KFold
from torchvision import transforms, datasets
from torch.utils.data import DataLoader, Subset
# 数据预处理
transform = transforms.Compose([
transforms.Resize((32, 32)),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) # 图像归一化
])
# 加载CIFAR-10数据集
dataset = datasets.CIFAR10(root='./data', train=True, transform=transform, download=True)
# 定义模型
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, padding=1)
self.pool = nn.MaxPool2d(kernel_size=2, stride=2)
self.fc1 = nn.Linear(64 * 16 * 16, 256)
self.fc2 = nn.Linear(256, 10)
def forward(self, x):
x = self.pool(nn.functional.relu(self.conv1(x)))
x = x.view(x.size(0), -1)
x = nn.functional.relu(self.fc1(x))
x = self.fc2(x)
return x
# 定义交叉验证
num_splits = 5
kf = KFold(n_splits=num_splits, shuffle=True)
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数
# 执行交叉验证
for fold, (train_indices, val_indices) in enumerate(kf.split(dataset)):
print(f"Fold {fold+1}/{num_splits}")
# 划分训练集和验证集
train_subset = Subset(dataset, train_indices)
val_subset = Subset(dataset, val_indices)
# 定义数据加载器
batch_size = 32
train_loader = DataLoader(train_subset, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(val_subset, batch_size=batch_size, shuffle=False)
# 创建模型实例
model = Net()
# 定义优化器
optimizer = optim.Adam(model.parameters(), lr=0.001) # Adam优化算法
# 训练模型
num_epochs = 10
for epoch in range(num_epochs):
model.train() # 设置为训练模式
for inputs, labels in train_loader:
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# 在验证集上评估模型
model.eval() # 设置为评估模式
val_loss = 0.0
correct = 0
total = 0
with torch.no_grad():
for inputs, labels in val_loader:
outputs = model(inputs)
val_loss += criterion(outputs, labels).item()
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f"Validation Loss: {val_loss/len(val_loader):.4f} - Validation Accuracy: {100*correct/total:.2f}%")
在这个例子中,我们使用KFold类进行交叉验证。在每个交叉验证折叠中,分别划分了训练集和验证集,并在训练集上训练模型,然后在验证集上评估模型性能。在完成所有折叠后,得到了模型在不同验证集上的性能评估结果,从而更全面地了解模型的表现。