Kaggle猫狗分类Pytorch CNN

介绍

猫狗分类来源于Kaggle上的一个入门竞赛。

https://www.kaggle.com/competitions/dogs-vs-cats-redux-kernels-edition/overview

代码及解释

首先,导入一系列的库。

import numpy as np
from PIL import Image
from pathlib import Path
import torch
from torch import nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader, random_split
from torchvision import transforms
import matplotlib.pyplot as plt

这段代码主要是导入了一些Python库,包括:

  1. numpy:Python中常用的科学计算库,用于处理数组、矩阵等数值数据。
  2. PIL(Python Imaging Library):Python图像处理库,用于处理各种图像格式。
  3. pathlib:Python处理文件和目录路径的标准库,支持多平台。
  4. torch:PyTorch深度学习框架的核心库。
  5. nn:PyTorch中用于构建神经网络的模块。
  6. F(functional):PyTorch中用于创建自定义卷积层、激活函数等的函数。
  7. DataLoader:PyTorch中用于加载和批量处理数据的工具。
  8. transforms:PyTorch中对图像和数据进行预处理的工具。
  9. matplotlib:Python绘图库,用于绘制数据和图像。

这些库的导入是PyTorch实践项目中经常用到的基础操作,其中PIL、numpy和matplotlib主要用于读取和展示图像、transforms用于对图像进行数据增强,torch和nn则是构建和训练深度神经网络的核心。
而后,启用GPU加速。

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print("device: ", device)
get_label = lambda x: x.name.split('.')[0]

class get_dataset(Dataset):
    def __init__(self, root, transform=None):
        self.images = list(Path(root).glob('*.jpg'))
        self.transform = transform
    def __len__(self):
        return len(self.images)
    def __getitem__(self, idx):
        img = self.images[idx]
        label = get_label(img)
        label = 1 if label == 'dog' else 0
        if self.transform:
            img = self.transform(Image.open(img))
        return img, torch.tensor(label, dtype=torch.int64)

这段代码定义了一个类get_dataset,用于加载和预处理数据集。
在类的初始化函数中,root为数据集路径,transform为数据预处理函数。通过list和glob函数获取符合条件的文件名,即所有后缀为jpg的图片文件名,并将其转为列表self.images。同时记录transform函数,即数据预处理函数。
__len__函数返回数据集中的图片数量,__getitem__函数根据索引idx获取对应图片和标签。首先获取索引对应的图片img,并通过get_label函数获取该图片对应的标签。该函数将图片文件名以’.‘分割,并将第一个分割出来的字符串作为标签。如果标签等于’dog’,则将其转为数字1,否则转为数字0。
接着如果有定义transform函数,就将img通过transform函数进行数据预处理。最后返回处理后的图片和标签,其中标签用torch.tensor转为整型。

🤚注意:glob 是 Python 标准库中 pathlib 模块下的一个函数,用于查找指定路径下符合特定规则的文件或文件夹路径,返回一个生成器(generator)。
在这个实现中,glob(‘*.jpg’) 表示查找 root 目录下所有后缀为 .jpg 的文件。通配符 * 表示匹配任意字符,因此 *.jpg 表示匹配任意字符开头、.jpg 结尾的文件名。
举个例子,如果 root 目录下有这些文件:

  • cat.1.jpg
  • cat.2.jpg
  • dog.1.jpg
  • dog.2.jpg
  • rabbit.1.jpg

那么 list(Path(root).glob(‘*.jpg’)) 将返回这些文件的路径:

Copy Code[
    'root/cat.1.jpg',
    'root/cat.2.jpg',
    'root/dog.1.jpg',
    'root/dog.2.jpg',
    'root/rabbit.1.jpg'
]

这里需要注意,glob 函数返回的是一个生成器,需要通过 list() 转换成列表才能进行遍历或其他操作。

transforms = transforms.Compose([
    transforms.Resize([224, 224]),  # resize the input image to a uniform size
    transforms.ToTensor(),  # convert PIL Image or numpy.ndarray to tensor and normalize to somewhere between [0,1]
    transforms.Normalize(   # standardized processing
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225])
])

这段代码定义了一个数据预处理过程,用于将读入的图像进行标准化处理,从而方便模型训练。
数据预处理使用了 transforms 模块中的 Compose 函数,它可以将多种图像变换串联起来,形成一个可复用的数据处理流程。这里串联了三个变换:

  1. Resize 变换:将图像调整为指定的大小,这里将图像大小调整为 224×224224×224 像素。这一步是为了将所有图像都调整到同样的大小,方便后续数据输入模型。
  2. ToTensor 变换:将 PIL Image 类型或者 numpy.ndarray 类型的图像数据转换成 PyTorch 中的 Tensor 类型。从而使图像可以被输入到神经网络中进行计算。
  3. Normalize 变换:通过对数据进行归一化处理,将每一个像素在 RGB 颜色通道上的取值压缩到 [0,1][0,1] 区间内。这里使用 imagenet 数据集的平均值和标准差作为归一化的标准,将每个像素减去平均值再除以标准差,达到对数据进行标准化的目的。

最终 transforms 变量定义了一个复合的预处理过程,可以方便地应用于训练集和测试集中的图像数据。

注意:transforms.Normalize() 是一个数据标准化操作,通过对每个像素在 RGB 颜色通道上的取值进行归一化,将图像的统计性质进行规范化,从而方便模型的使用和训练。
具体地,该函数接受两个参数:mean 和 std,分别表示要减去的均值和要除以的标准差。在这里,mean 和 std 的取值是根据 Imagenet 数据集的统计特征来计算的,这些统计特征对于大多数视觉任务都是通用的:

  • mean:表示在 Imagenet 训练集上所有图片在 RGB 颜色通道上的平均取值。对于彩色图像,mean 是一个长度为 3 的列表,分别表示 RGB 三个颜色通道上的平均值。在这里,mean 的取值为 [0.485, 0.456, 0.406]。
  • std:表示在 Imagenet 训练集上所有图片在 RGB 颜色通道上的标准差。同样地,std 也是一个长度为 3 的列表,在这里,std 的取值为 [0.229, 0.224, 0.225]。

假设输入图像为 x,则 Normalize 操作的具体计算为:(x−mean)/std,即将输入图像减去均值再除以标准差。最终得到的图像每个像素在每个颜色通道上的取值都在 [0,1][0,1] 区间内。

这样的标准化操作可以帮助模型更好地训练和收敛,提高了模型的鲁棒性,同时加快了模型训练的速度。

train_data, valid_data = random_split(dataset, 
        lengths=[int(len(dataset)*0.8),int(len(dataset)*0.2)], 
        generator=torch.Generator().manual_seed(7))
# print("train: ", len(train_data))
# print("valid: ", len(valid_data))
train_loader = DataLoader(train_data, batch_size=64, shuffle=True)
valid_loader = DataLoader(valid_data, batch_size=64)

这段代码定义了数据集的划分方式以及相应的数据加载器。
首先,数据集(即 dataset)被随机划分为训练集和验证集,划分的方式是使用 PyTorch 中的 random_split() 函数。其中,lengths 参数指定了划分后训练集和验证集的长度比例,即训练集长度为总数据集的 80%,验证集长度为总数据集的 20%。同时,这里也设置了随机数生成器的种子为 7,保证每次划分结果的一致性。
接着,使用 PyTorch 中的 DataLoader() 函数对训练集和验证集进行批量数据的加载,具体来说:

  • train_loader:表示训练集的数据加载器,它从 train_data 中按照指定大小(batch_size=64)将数据读取到内存中,并将数据打乱(shuffle=True)以提高模型鲁棒性。这样,每次训练时可以从 train_data 中随机取出一个大小为 64 的批次进行训练。
  • valid_loader:表示验证集的数据加载器,它从 valid_data 中按照指定大小(batch_size=64)将数据读取到内存中。与训练集不同的是,验证集一般不需要 shuffle 操作,因为在模型训练时是无法使用验证集进行反向传播和梯度更新的。

最终,train_loader 和 valid_loader 可以作为模型的输入数据来源,在模型的训练和验证过程中起到很重要的作用。

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 32, 3),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(32, 64, 3),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )
        self.conv3 = nn.Sequential(
            nn.Conv2d(64, 128, 3),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )
        self.fc1 = nn.Sequential(
            nn.Linear(128*26*26, 512),
            nn.BatchNorm1d(512),
            nn.ReLU()
        )
        self.fc2 = nn.Linear(512, 2)
        
    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = x.view(x.size(0), -1)
        x = self.fc1(x)
        x = self.fc2(x)
        x = F.log_softmax(x, dim=1)
        return x

这段代码定义了一个卷积神经网络模型,包含三个卷积层和两个全连接层。
首先,在 init 方法中,对网络的各个层次进行了定义。其中,

  • conv1:表示第一个卷积层,它包含一个 Conv2d 模块(输入通道为 3,输出通道为 32,卷积核大小为 3),一个 BatchNorm2d 模块(对通道维度进行归一化),一个 ReLU 激活函数模块,以及一个最大池化模块(kernel_size=2)。
  • conv2:表示第二个卷积层,它与 conv1 类似,但是输入通道变为了 32,输出通道变为了 64。
  • conv3:表示第三个卷积层,输入通道为 64,输出通道为 128,最后同样进行了一个最大池化操作。
  • fc1:表示第一个全连接层,它包含一个 Linear 模块(输入大小为 1282626,输出大小为 512),一个 BatchNorm1d 模块和一个 ReLU 激活函数模块。
  • fc2:表示第二个全连接层,包含一个 Linear 模块(输入大小为 512,输出大小为 2)。

接着,在 forward 方法中定义了前向传播过程,即按照 conv1、conv2、conv3、fc1、fc2 的顺序依次处理输入数据,并最终经过一个 log_softmax 函数进行输出。其中, view() 方法将卷积层输出的二维张量展平成一维张量,用于作为全连接层的输入。

在卷积神经网络中,每一层卷积和池化操作都会缩小输入特征图的尺寸,同时增加通道数。在这个模型中,第一个卷积层对输入的大小进行了卷积操作,将其从224x224的RGB图像转换成了(3, 32, 32)的特征图。接着,通过使用步幅为2的2x2最大池化层,将特征图的尺寸减半,得到(32, 112, 112)大小的特征图。后续的卷积层和最大池化层同理可以得到(64, 56, 56)和(128, 26, 26)大小的特征图。
因此,在全连接层之前,需要将最后一层卷积输出的(128, 26, 26)的特征图展开成一个一维向量,作为全连接层的输入。由于其中包含128个26x26大小的特征图,每个特征图又有128个通道,因此展开后的一维向量大小为128x26x26=86528。该大小即为nn.Linear(1282626, 512)中输入的大小。

CNN(
(conv1): Sequential(
(0): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
(3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(conv2): Sequential(
(0): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
(3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(conv3): Sequential(
(0): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1))
(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
(3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(fc1): Sequential(
(0): Linear(in_features=86528, out_features=512, bias=True)
(1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
(fc2): Linear(in_features=512, out_features=2, bias=True)
)

model = CNN()
model.to(device)
print(model)
loss_function = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

epochs = 25
train_loss_list = []
train_acc_list = []

for epoch in range(epochs):
    print("Epoch {} / {}".format(epoch + 1, epochs))
            
    t_loss, t_corr = 0.0, 0.0
    
    model.train() 
    for inputs, labels in train_loader:
        inputs = inputs.to(device)
        labels = labels.to(device)
        
        preds = model(inputs)
        loss = loss_function(preds, labels)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        t_loss += loss.item() * inputs.size(0)
        t_corr += torch.sum(preds.argmax(1) == labels) 
        # preds.argmax(1)返回预测结果中概率最大的类别标签,即预测的类别
        
    train_loss = t_loss / len(train_loader.dataset)
    train_acc = t_corr.cpu().numpy() / len(train_loader.dataset)
    train_loss_list.append(train_loss)
    train_acc_list.append(train_acc)  
    print('Train Loss: {:.4f} Accuracy: {:.4f}%'.format(train_loss, train_acc * 100))

这段代码实现了使用交叉熵损失函数和Adam优化器对一个卷积神经网络模型进行训练,并记录每个epoch的训练损失和训练准确率。
具体地,该代码首先定义了一种损失函数(loss_function)nn.CrossEntropyLoss(),用于计算模型预测结果与真实标签之间的交叉熵损失。接着,定义了一个优化器(optimizer)torch.optim.Adam(),用于对模型参数进行Adam梯度下降优化,其中初始学习率为1e-3。
接下来,在25个epoch的循环中,对训练数据集(train_loader)中的每个批次数据进行模型训练。在每个epoch开始时,打印当前epoch的编号。然后,将模型设置为训练模式(model.train()),并初始化该epoch的总训练损失(t_loss)和正确预测的样本数量(t_corr)为0。
在每个批次中,将输入数据(inputs)和标签(labels)转移到设备(device)上(例如GPU),通过前向传播得到模型对该批次输入数据的预测结果(preds)。接着,通过loss_function计算该批次预测结果的交叉熵损失(loss),通过optimizer.zero_grad()将优化器的梯度清零,再通过loss.backward()自动计算参数的梯度,并通过optimizer.step()更新参数值。同时,累加该批次的训练损失(t_loss)和正确预测的样本数量(t_corr)。
在完成该epoch的所有批次训练后,计算该epoch的平均训练损失(train_loss)和训练准确率(train_acc),并将其分别存储在train_loss_list和train_acc_list列表中。最后打印该epoch的平均训练损失和训练准确率。
:::tips
Epoch 1 / 25
Train Loss: 0.5553 Accuracy: 72.3900%
Epoch 2 / 25
Train Loss: 0.4048 Accuracy: 81.5550%
Epoch 3 / 25
Train Loss: 0.3101 Accuracy: 86.6150%
Epoch 4 / 25
Train Loss: 0.2128 Accuracy: 91.3250%
Epoch 5 / 25
Train Loss: 0.1293 Accuracy: 95.0350%
Epoch 6 / 25
Train Loss: 0.0817 Accuracy: 96.9900%
Epoch 7 / 25
Train Loss: 0.0529 Accuracy: 98.0900%
Epoch 8 / 25
Train Loss: 0.0535 Accuracy: 98.0000%
Epoch 9 / 25
Train Loss: 0.0389 Accuracy: 98.6350%
Epoch 10 / 25
Train Loss: 0.0234 Accuracy: 99.2100%
Epoch 11 / 25
Train Loss: 0.0387 Accuracy: 98.6900%
Epoch 12 / 25
Train Loss: 0.0370 Accuracy: 98.7700%
Epoch 13 / 25
Train Loss: 0.0265 Accuracy: 99.1250%
Epoch 14 / 25
Train Loss: 0.0229 Accuracy: 99.1650%
Epoch 15 / 25
Train Loss: 0.0181 Accuracy: 99.4150%
Epoch 16 / 25
Train Loss: 0.0171 Accuracy: 99.3800%
Epoch 17 / 25
Train Loss: 0.0287 Accuracy: 99.0500%
Epoch 18 / 25
Train Loss: 0.0246 Accuracy: 99.1350%
Epoch 19 / 25
Train Loss: 0.0177 Accuracy: 99.3900%
Epoch 20 / 25
Train Loss: 0.0172 Accuracy: 99.4250%
Epoch 21 / 25
Train Loss: 0.0097 Accuracy: 99.7200%
Epoch 22 / 25
Train Loss: 0.0173 Accuracy: 99.4150%
Epoch 23 / 25
Train Loss: 0.0138 Accuracy: 99.5250%
Epoch 24 / 25
Train Loss: 0.0076 Accuracy: 99.7050%
Epoch 25 / 25
Train Loss: 0.0057 Accuracy: 99.8250%
:::

plt.figure()
plt.title('Train Loss and Accuracy')
plt.xlabel('Epoch')
plt.ylabel('')
plt.plot(range(1, epochs+1), np.array(train_loss_list), color='blue',
         linestyle='-', label='Train_Loss')
plt.plot(range(1, epochs+1), np.array(train_acc_list), color='red',
         linestyle='-', label='Train_Accuracy')
plt.legend()  # 凡例
plt.show()  # 表示

这是一个用于展示模型训练过程中训练损失和准确率的函数。主要功能包括:

  1. 创建一个新的图形窗口

plt.figure() 用于创建一个新的窗口,并将其设置为当前活动窗口,以便在其中进行数据可视化操作。

  1. 设置图形标题、横轴标签和纵轴标签

plt.title()、plt.xlabel() 和 plt.ylabel() 用于设置图形标题、横轴标签和纵轴标签的文本内容。

  1. 绘制训练损失和准确率曲线

plt.plot() 用于绘制训练损失和准确率曲线,第一个参数表示 x 轴的取值范围(从 1 到 epochs+1),第二个参数则表示 y 轴的取值范围(train_loss_list 或 train_acc_list)。颜色和线条样式则可通过 color 和 linestyle 参数进行设置。

  1. 添加图例

plt.legend() 用于添加图例,其中包含每个曲线的标签(即 Train_Loss 和 Train_Accuracy)。

  1. 显示图形

plt.show() 用于显示绘制出来的图形。

test_path = './kaggle/inputs/test'
# get dataset
test_data = get_dataset(test_path, transform=transforms)
# print(len(test_data))
test_loader = DataLoader(test_data, batch_size=64)
_loss, _corr = 0.0, 0.0
model.eval()
with torch.no_grad():
    for inputs, labels in test_loader:
        inputs = inputs.to(device)
        labels = labels.to(device)
        y = model(inputs)
        preds = y.argmax(1)
        _loss += loss.item() * inputs.size(0)
        _corr += torch.sum(preds== labels)

    print('Test Loss: {:.4f} Accuracy: {:.4f}%'.format(_loss / len(test_loader.dataset),
                                                       (_corr / len(test_loader.dataset)) * 100))

这段代码用于在测试集上评估经过训练的模型的性能。

  1. 设定测试集数据路径

首先,test_path 是测试数据集所在的路径。

  1. 加载测试数据集

get_dataset() 是一个自定义函数,用于加载测试集的图像和标签,并将其存储在test_data中。此外,transform 是一个可选参数,用于数据增强操作。

  1. 创建测试数据加载器

然后,创建一个 DataLoader 对象 test_loader,该对象用于从测试集中为模型提供数据。batch_size 参数指定了每个批次的大小。在此处,batch_size 设定为 64。

  1. 初始化变量

接着,_loss 和 _corr 都被初始化为零。这两个变量分别用于累计模型在测试集上的损失和分类正确率。

  1. 设置模型为评估模式

使用 model.eval() 将模型设置为评估模式,以便禁用一些训练时的特性(如 dropout 层),并确保在每个前向传播时保留所有权重。

  1. 禁用梯度计算

为了节省内存,使用 torch.no_grad() 上下文管理器禁用梯度计算,避免在测试期间不必要地计算梯度。

  1. 循环遍历测试集

对于测试集中的每个批次(即 inputs 和 labels),将其转移到 GPU 上(如果有的话),然后利用模型预测输入数据的标签,并计算该批次的交叉熵损失。具体来说,preds 是预测标签的最大值索引,而 labels 是实际标签。因此,preds == labels 返回一个布尔张量,其中每个元素指示对应的预测标签是否与真实标签匹配,torch.sum() 函数返回 True 值的数量。

  1. 计算最终损失和正确率

最后,通过将 _loss 和 _corr 分别除以测试集样本数目,得到模型在测试集上的平均损失和准确率,并使用 print() 函数将这些信息打印出来。特别地,_loss / len(test_loader.dataset) 表示平均损失,(_corr / len(test_loader.dataset)) * 100 表示准确率,乘以 100 是为了将其转换为百分比。

完整代码

import numpy as np
from PIL import Image
from pathlib import Path
import torch
from torch import nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader, random_split
from torchvision import transforms
import matplotlib.pyplot as plt

transforms = transforms.Compose([
    transforms.Resize([224, 224]),  # resize the input image to a uniform size
    transforms.ToTensor(),  # convert PIL Image or numpy.ndarray to tensor and normalize to somewhere between [0,1]
    transforms.Normalize(   # standardized processing
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225])
])

get_label = lambda x: x.name.split('.')[0]

class get_dataset(Dataset):
    def __init__(self, root, transform=None):
        self.images = list(Path(root).glob('*.jpg'))
        self.transform = transform
    def __len__(self):
        return len(self.images)
    def __getitem__(self, idx):
        img = self.images[idx]
        label = get_label(img)
        label = 1 if label == 'dog' else 0
        if self.transform:
            img = self.transform(Image.open(img))
        return img, torch.tensor(label, dtype=torch.int64)


class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 32, 3),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(32, 64, 3),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )
        self.conv3 = nn.Sequential(
            nn.Conv2d(64, 128, 3),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )
        self.fc1 = nn.Sequential(
            nn.Linear(128 * 26 * 26, 512),
            nn.BatchNorm1d(512),
            nn.ReLU()
        )
        self.fc2 = nn.Linear(512, 2)

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = x.view(x.size(0), -1)
        x = self.fc1(x)
        x = self.fc2(x)
        x = F.log_softmax(x, dim=1)
        return x

if __name__ == '__main__':
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    train_path = './train'
    dataset = get_dataset(train_path, transform=transforms)
    train_data, valid_data = random_split(dataset,
                                          lengths=[int(len(dataset) * 0.8), int(len(dataset) * 0.2)],
                                          generator=torch.Generator().manual_seed(7))
    train_loader = DataLoader(train_data, batch_size=64, shuffle=True)
    valid_loader = DataLoader(valid_data, batch_size=64)

    model = CNN()
    model.to(device)
    # print(model)

    loss_function = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

    epochs = 25
    train_loss_list = []
    train_acc_list = []

    for epoch in range(epochs):
        print("Epoch {} / {}".format(epoch + 1, epochs))

        t_loss, t_corr = 0.0, 0.0

        model.train()
        for inputs, labels in train_loader:
            inputs = inputs.to(device)
            labels = labels.to(device)

            preds = model(inputs)
            loss = loss_function(preds, labels)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            t_loss += loss.item() * inputs.size(0)
            t_corr += torch.sum(preds.argmax(1) == labels)

        train_loss = t_loss / len(train_loader.dataset)
        train_acc = t_corr.cpu().numpy() / len(train_loader.dataset)
        train_loss_list.append(train_loss)
        train_acc_list.append(train_acc)
        print('Train Loss: {:.4f} Accuracy: {:.4f}%'.format(train_loss, train_acc * 100))

    v_loss, v_corr = 0.0, 0.0
    model.eval()
    with torch.no_grad():
        for inputs, labels in valid_loader:
            inputs = inputs.to(device)
            labels = labels.to(device)
            preds = model(inputs)
            v_loss += loss.item() * inputs.size(0)
            v_corr += torch.sum(preds.argmax(1) == labels)

        print('Valid Loss: {:.4f} Accuracy: {:.4f}%'.format(v_loss / len(valid_loader.dataset),
                                                            (v_corr / len(valid_loader.dataset)) * 100))

    test_path = './test'
    # get dataset
    test_data = get_dataset(test_path, transform=transforms)
    # print(len(test_data))
    test_loader = DataLoader(test_data, batch_size=64)
    _loss, _corr = 0.0, 0.0
    model.eval()
    with torch.no_grad():
        for inputs, labels in test_loader:
            inputs = inputs.to(device)
            labels = labels.to(device)
            y = model(inputs)
            preds = y.argmax(1)
            _loss += loss.item() * inputs.size(0)
            _corr += torch.sum(preds == labels)

        print('Test Loss: {:.4f} Accuracy: {:.4f}%'.format(_loss / len(test_loader.dataset),
                                                           (_corr / len(test_loader.dataset)) * 100))
  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值