【深度学习】绘制模型训练曲线(epoch-accuracy),寻找最佳epoch值

一、什么是epoch

在卷积神经网络(CNN)训练中,"epoch"(周期)是一个重要的概念,表示模型训练过程中数据集的完整遍历次数。通过多次"epoch",模型逐渐适应训练数据,提高性能,并通过验证性能监测模型的泛化能力。在深度学习中,正确设置"epoch"的数量是一个重要的超参数,需要根据具体问题和数据集来调整。

Epoch设置过大

  • 过拟合风险:如果 epoch 设置过大,模型有可能过度拟合训练数据,即模型在训练数据上表现得非常好,但在新数据上表现较差。这是因为模型会记住噪声和细节,而不是学习通用规律。

  • 浪费时间和计算资源:较大的 epoch 会占用更多的时间和计算资源,而结果可能不会有显著的提升。这对于大规模深度学习模型来说特别昂贵。

Epoch设置过小

  • 欠拟合风险:如果 epoch 设置得太小,模型可能无法捕获训练数据中的模式和特征,导致训练不足,模型性能较差。

  • 性能下降:模型需要一定的训练时间才能逐渐学习数据的特征。如果 epoch 设置太小,模型可能没有足够的时间来收敛,导致性能下降。

二、绘制epoch-accuracy(准确性)与epoch-loss(损失值)曲线图,寻找最佳epoch

所用数据文件:点击下载

import torch
from torch.utils.data import Dataset, DataLoader
import numpy as np
from PIL import Image
from torchvision import transforms

# 定义数据预处理的转换
data_transforms = {
    'train':
        transforms.Compose([
            transforms.Resize([256,256]),
            transforms.ToTensor()
        ]),
    'valid':
        transforms.Compose([
            transforms.Resize([256, 256]),
            transforms.ToTensor()
        ]),
}

# 定义自定义的数据集类
class food_dataset(Dataset):  # food_dataset是自己创建的类名称,可以改为你需要的名称
    def __init__(self, file_path, transform=None):  # 类的初始化
        self.file_path = file_path
        self.imgs = []
        self.labels = []
        self.transform = transform
        with open(self.file_path) as f:
            samples = [x.strip().split(' ') for x in f.readlines()]
            for img_path, label in samples:
                self.imgs.append(img_path)
                self.labels.append(label)

    def __len__(self):  # 类实例化对象后,可以使用len函数测量对象的个数
        return len(self.imgs)

    def __getitem__(self, idx):  # 关键,可通过索引的形式获取每一个图片数据及标签
        image = Image.open(self.imgs[idx])  #
        if self.transform:
            image = self.transform(image)

        label = self.labels[idx]
        label = torch.from_numpy(np.array(label, dtype=np.int64))
        return image, label

# 创建训练集和测试集的实例
training_data = food_dataset(file_path='./train.txt', transform=data_transforms['train'])
test_data = food_dataset(file_path='./test.txt', transform=data_transforms['valid'])

# 创建训练集和测试集的数据加载器
train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)  # 64张图片为一个包,
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)


'''-------------cnn卷积神经网络部分----------------------'''
#此行根据支持 CUDA 的 GPU 和 MPS 的可用性设置变量。
# 它使用 检查启用了 CUDA 的 GPU 是否可用。如果为 true,则设置为“cuda”。
# 否则,它将使用 检查 MPS 是否可用。如果为 true,则设置为“mps”。如果两个条件都为 false,则设置为“cpu”。
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
print(f"Using {device} device")

''' 定义神经网络  '''
from torch import nn

class CNN(nn.Module):
    def __init__(self):  # 输入大小 (3, 256, 256)
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(  # 将多个层组合成一起。
            nn.Conv2d(  # 2d一般用于图像,3d用于视频数据(多一个时间维度),1d一般用于结构化的序列数据
                in_channels=3,  # 图像通道个数,1表示灰度图(确定了卷积核 组中的个数),
                out_channels=16,  # 要得到几多少个特征图,卷积核的个数
                kernel_size=5,  # 卷积核大小,5*5
                stride=1,  # 步长
                padding=2,  # 一般希望卷积核处理后的结果大小与处理前的数据大小相同,效果会比较好。那padding改如何设计呢?建议stride为1,kernel_size = 2*padding+1
            ),  # 输出的特征图为 (16, 256, 256)
            nn.ReLU(),  # relu层
            nn.MaxPool2d(kernel_size=2),  # 进行池化操作(2x2 区域), 输出结果为: (16, 128, 128)
        )
        self.conv2 = nn.Sequential(  # 输入 (16, 128, 128)
            nn.Conv2d(16, 32, 5, 1, 2),  # 输出 (32, 128, 128)
            nn.ReLU(),  # relu层
            nn.Conv2d(32, 32, 5, 1, 2),  # 输出 (32, 128, 128)
            nn.ReLU(),
            nn.MaxPool2d(2),  # 输出 (32, 64, 64)
        )

        self.conv3 = nn.Sequential(  # 输入 (32, 64, 64)
            nn.Conv2d(32, 64, 5, 1, 2),
            nn.ReLU(),  # 输出 (64, 64, 64)
        )

        self.out = nn.Linear(64 * 64 * 64, 20)  # 全连接层得到的结果

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)  # 输出 (64,64, 32, 32)
        x = x.view(x.size(0), -1)  # flatten操作,结果为:(batch_size, 64 * 32 * 32)
        output = self.out(x)
        return output

model = CNN().to(device)

def train(dataloader, model, loss_fn, optimizer):
    model.train()
    # pytorch提供2种方式来切换训练和测试的模式,分别是:model.train() 和 model.eval()。
    # 一般用法是:在训练开始之前写上model.trian(),在测试时写上 model.eval() 。
    for X, y in dataloader:  # 其中batch为每一个数据的编号
        X, y = X.to(device), y.to(device)  # 把训练数据集和标签传入cpu或GPU
        pred = model.forward(X)  # 自动初始化 w权值
        loss = loss_fn(pred, y)  # 通过交叉熵损失函数计算损失值loss
        # Backpropagation 进来一个batch的数据,计算一次梯度,更新一次网络
        optimizer.zero_grad()  # 梯度值清零
        loss.backward()  # 反向传播计算得到每个参数的梯度值
        optimizer.step()  # 根据梯度更新网络参数

def test(dataloader,model,loss_fn):
    size= len (dataloader.dataset) #计算测试数据集的总样本数。
    num_batches = len(dataloader) #计算测试数据集的总样本数。
    model.eval()#通过这个方法将神经网络模型设置为评估模式,以关闭一些在训练时启用的操作,例如丢弃。
    test_loss,correct=0,0 #初始化两个变量 test_loss 和 correct,分别用于累积测试损失和正确分类的样本数量。
    with torch.no_grad(): #使用 torch.no_grad() 上下文管理器,将其包裹的代码块中的梯度计算禁用,以减少内存使用和加速计算。
        for X,y in dataloader: #开始遍历测试数据加载器中的每个批次,每个批次包括输入数据 X 和相应的标签 y。
            X,y=X.to(device),y.to(device) #将测试数据移动到指定的设备,以确保与模型的设备匹配。
            pred=model.forward(X) #使用神经网络模型进行前向传播,得到预测结果 pred。
            test_loss += loss_fn(pred,y).item()  #计算并累积测试损失,通过使用损失函数 loss_fn 计算模型的预测结果 pred 与真实标签 y 之间的损失。
# 计算并累积正确分类的样本数量。这里使用了 argmax 函数来找到预测结果中的最大值对应的类别,然后检查是否与真实标签匹配,并将匹配的结果转换为浮点数。
            correct += (pred.argmax(1)==y).type(torch.float).sum().item()
            a = (pred.argmax(1) == y)  # dim=1表示每一行中的最大值对应的索引号,dim=0表示每一列中的最大值对应的索引号
            b = (pred.argmax(1) == y).type(torch.float)
    test_loss /=num_batches
    #计算测试的平均损失,将累积的损失值除以批次数目。
    correct /= size
    #计算准确率,将正确分类的样本数量除以总样本数,然后将其乘以 100 得到百分比形式。
    print(f'Test result:\n Accuracy:{(100*correct)}%,Avg loss: {test_loss}')
    #打印测试结果,包括准确率和平均损失。
    acc_s.append(correct)
    loss_s.append(test_loss)


loss_fn = nn.CrossEntropyLoss()  # 创建交叉熵损失函数对象,因为手写字识别中一共有10个数字,输出会有10个结果
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)  # 创建一个优化器,SGD为随机梯度下降算法??

'''训练模型'''
epochs = 50  # 训练次数
acc_s = []  # 存储每个 epoch 的准确率
loss_s = []  # 存储每个 epoch 的损失值

for t in range(epochs):
    print(f"Epoch {t + 1}\n-------------------------------")
    # 训练模型
    train(train_dataloader, model, loss_fn, optimizer)
    # 在测试集上评估模型
    test(test_dataloader, model, loss_fn)
'''绘制训练效果曲线'''
from matplotlib import pyplot as plt

# 创建子图,分别用于绘制准确率和损失值
plt.subplot(1, 2, 1)
plt.plot(range(0, epochs), acc_s)
plt.xlabel('epoch')
plt.ylabel('accuracy')

plt.subplot(1, 2, 2)
plt.plot(range(0, epochs), loss_s)
plt.xlabel('epoch')
plt.ylabel('loss')

plt.show()  # 显示绘制的图形
print("Done!")  # 训练结束

三、运行结果及分析

1、epoch=20:

2、epoch=20(添加数据增强):(后面的都是添加数据增强后的)

注意数据增强详见:数据增强的原理及实现(超详细)

3、epoch=50:

4、epoch=100:

5、epoch=150:

 综上所述:基本上可以确定epoch的值在 60~80 为最佳!

  • 34
    点赞
  • 246
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 可以使用 Matplotlib 库在 Python绘制 loss 和 epoch 曲线。首先,需要在代码中导入 Matplotlib,然后通过记录 loss 和 epoch 的数并将其作为 Matplotlib 的输入数据,最后调用 plot 函数绘制曲线。例如: ``` import matplotlib.pyplot as plt epochs = [1, 2, 3, 4, 5] loss = [0.2, 0.15, 0.1, 0.05, 0.01] plt.plot(epochs, loss, 'r') plt.xlabel('Epochs') plt.ylabel('Loss') plt.title('Loss vs. Epochs') plt.show() ``` 这将绘制红色的 loss 和 epoch 曲线。 ### 回答2: Python是一种高级的编程语言,适用于各种领域,包括机器学习和深度学习。当我们训练模型时,我们通常需要了解模型的性能如何随时间推移而变化。为了实现这一目标,我们在代码中添加了可视化损失和epoch曲线的功能。下面将详细介绍如何使用Python绘制loss和epoch曲线。 首先,我们需要从训练模型的程序中获取损失和epoch数据。我们可以使用Python的numpy库来存储这些数据。在训练期间,我们可以将损失和epoch数据发送到numpy数组中。通常,我们将损失和epoch数据保存在csv文件中,以备将来使用。 接下来,我们需要将损失和epoch数据可视化。我们可以使用Python的matplotlib库来实现可视化。对于简单的可视化,我们可以使用plt.plot()函数将损失和epoch数据传递给Matplotlib。在图表上,将显示损失和epoch的变化趋势。 如果我们想更加详细地研究模型的性能,并将损失和epoch图表细化到数据点级别,我们可以使用Seaborn库。Seaborn库建立在Matplotlib库之上,并提供了更加高效和美观的可视化效果。 一些其他的Python库,如Plotly和Bokeh,提供了交互式的数据可视化,使用户能够从数据中获取更多信息。 总的来说,Python的numpy,Matplotlib,Seaborn等开源库可以帮助我们对机器学习和深度学习模型进行可视化,以进一步优化模型性能。可视化数据可以帮助我们更好地理解模型的运行和优化,以便在竞争激烈的数据科学和机器学习领域中保持竞争优势。 ### 回答3: Python是一种高级编程语言,其简单易学、易于阅读和有效地处理数据,使其成为机器学习和深度学习领域的流行语言。在训练深度学习模型时,监控模型表现的指标非常重要。通常,人们使用loss和accuracy来监测模型的表现,其中loss表示在一次迭代中预测与实际之间的误差。 在机器学习中,模型的loss通常会在每个epoch中被计算并记录下来。这些数据需要可视化来更好地理解模型表现。Python提供了一种简单而有效的方法来绘制loss和epoch曲线,这是使用matplotlib库。 首先,我们需要在训练过程中记录每个epoch中的loss。 这可以通过以下方式实现: history = model.fit(X_train, y_train, epochs=10, validation_data=(X_val, y_val)) loss = history.history['loss'] val_loss = history.history['val_loss'] 其中,history是模型训练的历史记录,而loss和val_loss分别是训练集和验证集中的loss。 接下来,我们可以使用matplotlib库来绘制loss和epoch曲线,并添加必要的标签和标题。示例代码如下所示: import matplotlib.pyplot as plt epochs = range(1, len(loss) + 1) plt.plot(epochs, loss, 'bo', label='Training loss') plt.plot(epochs, val_loss, 'b', label='Validation loss') plt.title('Training and Validation Loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show() 此代码将绘制一个图表,其中包含两个曲线:蓝色圆点表示训练集的loss,而蓝色线表示验证集的loss。通过图表,我们可以看出模型是否过度拟合,以及我们需要对其进行调整。 总之,通过Python和matplotlib库,绘制loss和epoch曲线是一项简单而重要的任务,它可以帮助我们监视和优化机器学习模型的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值