在彩色图像数据集上的应用#
import torchvision
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
CIFAR-10 数据集介绍#
在本次应用中,我们将使用 CIFAR-10 数据集。该数据集包含 \(60,000\) 张彩色图像,每张图像的大小为 \(3 \times 32 \times 32\),分为 \(10\) 个类别。
类别包括:飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船和卡车。
我们将使用 torchvision 加载该数据集:
classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# Transformation des données, normalisation et transformation en tensor pytorch
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# Téléchargement et chargement du dataset
dataset = torchvision.datasets.CIFAR10(root='./../data', train=True,download=True, transform=transform)
testdataset = torchvision.datasets.CIFAR10(root='./../data', train=False,download=True, transform=transform)
print("taille d'une image : ",dataset[0][0].shape)
#Création des dataloaders pour le train, validation et test
train_dataset, val_dataset=torch.utils.data.random_split(dataset, [0.8,0.2])
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16,shuffle=True, num_workers=2)
val_loader= torch.utils.data.DataLoader(val_dataset, batch_size=16,shuffle=True, num_workers=2)
test_loader = torch.utils.data.DataLoader(testdataset, batch_size=16,shuffle=False, num_workers=2)
Files already downloaded and verified
Files already downloaded and verified
taille d'une image : torch.Size([3, 32, 32])
我们可以可视化图像及其对应的类别:
def imshow_with_labels(img, labels):
img = img / 2 + 0.5 # Dénormaliser
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.xticks([]) # Supprimer les graduations sur l'axe des x
plt.yticks([]) # Supprimer les graduations sur l'axe des y
for i in range(len(labels)):
plt.text(i * 35, -2, classes[labels[i]], color='black', fontsize=8, ha='left')
# Récupération d'un batch d'images
images, labels = next(iter(train_loader))
# Affichage des images avec leurs classes
imshow_with_labels(torchvision.utils.make_grid(images[:8]), labels[:8])
plt.show()
设计用于该问题的卷积神经网络#
在卷积的相关课程中,我们学习了 步长(stride) 参数,它表示卷积操作的步幅。当 步长 设置为 \(2\) 时,图像的分辨率在操作后会减半(前提是 填充(padding) 能够补偿滤波器的大小)。因此,可以通过增加 步长 来替代 池化(pooling) 操作。 使用 步长 为 \(1\) 的卷积层后接一个 池化 层,其输出维度与使用 步长 为 \(2\) 的单独卷积层的输出维度相同。
在本次应用中,我们将用增加卷积层的 步长 来替代 池化 层。 更多详情,请参阅此 博客文章 或 论文。
class cnn(nn.Module):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.conv1=nn.Conv2d(3,8,kernel_size=3,stride=2,padding=1)
self.conv2=nn.Conv2d(8,16,kernel_size=3,stride=2,padding=1)
self.conv3=nn.Conv2d(16,32,kernel_size=3,stride=2,padding=1)
self.fc=nn.Linear(4*4*32,10)
def forward(self,x):
x=F.relu(self.conv1(x))
x=F.relu(self.conv2(x))
x=F.relu(self.conv3(x))
x=x.view(-1,4*4*32)
output=self.fc(x)
return output
model = cnn() # Couches d'entrée de taille 2, deux couches cachées de 16 neurones et un neurone de sortie
print(model)
print("Nombre de paramètres", sum(p.numel() for p in model.parameters()))
cnn(
(conv1): Conv2d(3, 8, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
(conv2): Conv2d(8, 16, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
(conv3): Conv2d(16, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
(fc): Linear(in_features=512, out_features=10, bias=True)
)
Nombre de paramètres 11162
模型训练#
criterion = nn.CrossEntropyLoss()
epochs=5
learning_rate=0.001
optimizer=torch.optim.Adam(model.parameters(),lr=learning_rate)
for i in range(epochs):
loss_train=0
for images, labels in train_loader:
preds=model(images)
loss=criterion(preds,labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss_train+=loss
if i % 1 == 0:
print(f"step {i} train loss {loss_train/len(train_loader)}")
loss_val=0
for images, labels in val_loader:
with torch.no_grad(): # permet de ne pas calculer les gradients
preds=model(images)
loss=criterion(preds,labels)
loss_val+=loss
if i % 1 == 0:
print(f"step {i} val loss {loss_val/len(val_loader)}")
step 0 train loss 1.5988761186599731
step 0 val loss 1.4532517194747925
step 1 train loss 1.3778905868530273
step 1 val loss 1.3579093217849731
step 2 train loss 1.2898519039154053
step 2 val loss 1.2919617891311646
step 3 train loss 1.2295998334884644
step 3 val loss 1.256637692451477
step 4 train loss 1.186734914779663
step 4 val loss 1.240902304649353
现在我们可以验证模型在测试数据上的准确率:
correct = 0
total = 0
for images,labels in test_loader:
with torch.no_grad():
preds=model(images)
_, predicted = torch.max(preds.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
test_acc = 100 * correct / total
print("Précision du modèle en phase de test : ",test_acc)
Précision du modèle en phase de test : 55.92
使用 GPU#
如果您的电脑配备了 GPU,或能够使用带有 GPU 的云服务,则可以加速模型的训练和推理过程。 以下是在 PyTorch 中使用 GPU 的方法:
model = cnn().to('cuda') # Ajouter le .to('cuda') pour charger le modèle sur GPU
criterion = nn.CrossEntropyLoss()
epochs=5
learning_rate=0.001
optimizer=torch.optim.Adam(model.parameters(),lr=learning_rate)
for i in range(epochs):
loss_train=0
for images, labels in train_loader:
images=images.to('cuda') # Ajouter le .to('cuda') pour charger les images sur GPU
labels=labels.to('cuda') # Ajouter le .to('cuda') pour charger les labels sur GPU
preds=model(images)
loss=criterion(preds,labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss_train+=loss
if i % 1 == 0:
print(f"step {i} train loss {loss_train/len(train_loader)}")
loss_val=0
for images, labels in val_loader:
with torch.no_grad():
images=images.to('cuda')
labels=labels.to('cuda')
preds=model(images)
loss=criterion(preds,labels)
loss_val+=loss
if i % 1 == 0:
print(f"step {i} val loss {loss_val/len(val_loader)}")
/home/aquilae/anaconda3/envs/dev/lib/python3.11/site-packages/torch/autograd/graph.py:744: UserWarning: Plan failed with a cudnnException: CUDNN_BACKEND_EXECUTION_PLAN_DESCRIPTOR: cudnnFinalize Descriptor Failed cudnn_status: CUDNN_STATUS_NOT_SUPPORTED (Triggered internally at ../aten/src/ATen/native/cudnn/Conv_v8.cpp:919.)
return Variable._execution_engine.run_backward( # Calls into the C++ engine to run the backward pass
step 0 train loss 1.6380540132522583
step 0 val loss 1.4465171098709106
step 1 train loss 1.369913101196289
step 1 val loss 1.3735681772232056
step 2 train loss 1.2817057371139526
step 2 val loss 1.2942956686019897
step 3 train loss 1.2238909006118774
step 3 val loss 1.2601954936981201
step 4 train loss 1.186323881149292
step 4 val loss 1.2583365440368652
correct = 0
total = 0
for images,labels in test_loader:
images=images.to('cuda')
labels=labels.to('cuda')
with torch.no_grad():
preds=model(images)
_, predicted = torch.max(preds.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
test_acc = 100 * correct / total
print("Précision du modèle en phase de test : ",test_acc)
Précision du modèle en phase de test : 55.76
根据您的 GPU 类型,您可能已经注意到模型的训练和推理速度有所提升。对于性能强大的 GPU 以及深度且可并行化的模型,这种速度差异会更加显著。
实践练习#
为了提升实践能力,您可以尝试改进模型在 CIFAR-10 数据集上的性能。以下是一些建议:
增加网络层数
调整每层的滤波器数量
添加 Dropout 层
使用 Batch Normalization
增加训练轮数(epochs)
调整学习率(learning rate)
尝试将模型的准确率提升至至少 \(70\%\)。