【问题标题】:Resolve overfitting in a convolutional network解决卷积网络中的过拟合
【发布时间】:2022-01-05 18:28:23
【问题描述】:

我编写了一个 sn-p 来对 Omniglot 图像进行分类。我计算每个时期的训练和验证损失,后者是使用网络之前未见过的图像计算的。两幅图如下:

由于训练损失减少而验证损失增加,我得出的结论是我的模型过拟合。我尝试了几个建议(例如here)来克服这个问题,包括:

  • 增加训练集的大小。
  • 改组数据。
  • 添加 dropout 层(最高 p=0.9)。
  • 使用较小的模型。
  • 改变架构。
  • 更改学习率。
  • 减少批量大小。
  • 添加重量衰减。

但是,验证损失仍然增加。我想知道是否有任何其他建议可以改善这种行为,或者这是否不是过度拟合,但问题是别的。下面是本题使用的sn-p。

import torch
import torchvision
import torchvision.transforms as transforms

from torch import nn, optim
from torch.utils.data import DataLoader


class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()

        dim_out = 964

        # -- embedding params
        self.cn1 = nn.Conv2d(1, 16, 7)
        self.cn2 = nn.Conv2d(16, 32, 4)
        self.cn3 = nn.Conv2d(32, 64, 3)
        self.pool = nn.MaxPool2d(2)

        self.bn1 = nn.BatchNorm2d(16)
        self.bn2 = nn.BatchNorm2d(32)
        self.bn3 = nn.BatchNorm2d(64)

        # -- prediction params
        self.fc1 = nn.Linear(256, 170)
        self.fc2 = nn.Linear(170, 50)
        self.fc3 = nn.Linear(50, dim_out)

        # -- non-linearity
        self.relu = nn.ReLU()
        self.Beta = 10
        self.sopl = nn.Softplus(beta=self.Beta)

    def forward(self, x):

        y1 = self.pool(self.bn1(self.relu(self.cn1(x))))
        y2 = self.pool(self.bn2(self.relu(self.cn2(y1))))
        y3 = self.relu(self.bn3(self.cn3(y2)))


        y3 = y3.view(y3.size(0), -1)

        y5 = self.sopl(self.fc1(y3))
        y6 = self.sopl(self.fc2(y5))

        return self.fc3(y6)


class Train:
    def __init__(self):

        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

        # -- data
        dim = 28
        batch_size = 400
        my_transforms = transforms.Compose([transforms.Resize((dim, dim)), transforms.ToTensor()])
        trainset = torchvision.datasets.Omniglot(root="./data/omniglot_train/", download=False, transform=my_transforms)
        validset = torchvision.datasets.Omniglot(root="./data/omniglot_train/", background=False, download=False,
                                                     transform=my_transforms)
        self.TrainDataset = DataLoader(dataset=trainset, batch_size=batch_size, shuffle=True)
        self.ValidDataset = DataLoader(dataset=validset, batch_size=len(validset), shuffle=False)
        self.N_train = len(trainset)
        self.N_valid = len(validset)

        # -- model
        self.model = MyModel().to(self.device)

        # -- train
        self.epochs = 3000
        self.loss = nn.CrossEntropyLoss()
        self.optimizer = optim.Adam(self.model.parameters(), lr=1e-3)

    def train_epoch(self):

        self.model.train()
        train_loss = 0
        for batch_idx, data_batch in enumerate(self.TrainDataset):
            # -- predict
            predict = self.model(data_batch[0].to(self.device))

            # -- loss
            loss = self.loss(predict, data_batch[1].to(self.device))

            # -- optimize
            self.optimizer.zero_grad()
            loss.backward()
            self.optimizer.step()
            train_loss += loss.item()

        return train_loss/(batch_idx+1)

    def valid_epoch(self):

        with torch.no_grad():
            self.model.eval()
            for data_batch in self.ValidDataset:
                # -- predict
                predict = self.model(data_batch[0].to(self.device))

                # -- loss
                loss = self.loss(predict, data_batch[1].to(self.device))

        return loss.item()

    def __call__(self):

        for epoch in range(self.epochs):
            train_loss = self.train_epoch()
            valid_loss = self.valid_epoch()

            print('Epoch {}: Training loss = {:.5f}, Validation loss = {:.5f}.'.format(epoch, train_loss, valid_loss))

        torch.save(self.model.state_dict(), './model_stat.pth')


if __name__ == '__main__':
    my_train = Train()
    my_train()

【问题讨论】:

  • 过拟合总是一个棘手的问题。我在您提供的代码 sn-p 中没有看到任何会导致过度拟合的特定内容。有几件事要检查;你有三个相当大的全连接层,并且由于大量的参数,全连接层通常是过度拟合的原因。此外,在这个 sn-p 中,您实际上并没有报告 val 集的损失,只是上一个验证批次的损失。
  • 另外,您的验证集有标志 background=False,而不是训练集。我不确定这到底意味着什么,但如果它使您的验证集与您的训练集非常不同,则可能会导致过度拟合问题。您的模型可能会从训练集中的背景中学习更多信息,并且由于验证集中不存在背景,因此它会完全丢失。
  • @JackRickman 感谢 cmets。重新评论#1:我使用batch_size=len(validset) 作为验证集,所以我只有一个批次。重新评论#2:在omniglot数据集中,训练集称为背景,测试集称为评估。所以background=True 表示它是训练数据,background=False 表示它是测试数据。请参阅 Omniglot 文档:pytorch.org/vision/main/generated/…

标签: deep-learning pytorch image-classification overfitting-underfitting


【解决方案1】:

如果您的训练准确度很好,但测试(训练中未使用的数据)准确度很差,那么您就有过拟合问题。我对 CNN 模型也有同样的问题。您可以使用两种方法来克服过度拟合。首先是提前停车,其次是正规化。检查以下示例:

# L2 regularizers for layers
model = keras.Sequential([
    keras.layers.InputLayer(input_shape=(32, 32)),
    keras.layers.Reshape(target_shape=(32, 32, 1)),
    keras.layers.Conv2D(filters=12, kernel_size=(3, 3), activation=tf.nn.relu,  use_bias=True , kernel_regularizer =tf.keras.regularizers.l2( l=0.01)),
    keras.layers.MaxPooling2D(pool_size=(2, 2)),
    keras.layers.Flatten(),
    keras.layers.Dense(10, activation = 'softmax', use_bias=True)
])


model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(),
              metrics=['accuracy'])
#Early Stopping
history = model.fit(X_train, Y_train, 
                            validation_data=(X_dev, Y_dev), 
                            epochs=4000, 
                            callbacks=EarlyStopping(monitor='val_loss'))

不要忘记导入以提前停止。

from tensorflow.keras.callbacks import EarlyStopping

【讨论】:

    猜你喜欢
    • 2018-07-16
    • 2017-10-07
    • 2019-11-10
    • 1970-01-01
    • 1970-01-01
    • 2020-10-19
    • 2021-02-14
    • 1970-01-01
    • 2016-11-05
    相关资源
    最近更新 更多