- 🍨 本文为🔗365天深度学习训练营中的学习记录博客
- 🍖 原作者:K同学啊
1.导入数据
原代码在苹果系统里依旧会报错,不能直接下载,这里我依旧采用的是手动下载以后,通过代码导入数据。
import os
import numpy as np
import torch
import torchvision.transforms as transforms
from torch.utils.data import Dataset, DataLoader
import pickle
import matplotlib.pyplot as plt
import torch.nn as nn
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
class CIFAR10Manual(Dataset):
def __init__(self, root, train=True, transform=None):
"""
手动加载 CIFAR-10 数据集
:param root: 数据集文件路径
:param train: 是否加载训练集(True)还是测试集(False)
:param transform: 可选的图像预处理方法
"""
self.root = root
self.train = train
self.transform = transform
# 训练数据文件列表
if self.train:
self.data_files = ['data_batch_1', 'data_batch_2', 'data_batch_3', 'data_batch_4', 'data_batch_5']
else:
self.data_files = ['test_batch']
# 加载数据和标签
self.images = []
self.labels = []
for file_name in self.data_files:
file_path = os.path.join(self.root, file_name)
with open(file_path, 'rb') as f:
data_dict = pickle.load(f, encoding='bytes')
self.images.append(data_dict[b'data']) # 获取图片数据
self.labels.extend(data_dict[b'labels']) # 获取标签
# 将所有图片数据合并为一个大的数组
self.images = np.concatenate(self.images)
self.labels = np.array(self.labels)
# 将图片数据的形状转换为 [N, 3, 32, 32]
self.images = self.images.reshape(-1, 3, 32, 32)
# 转换为 PyTorch tensor
self.images = torch.tensor(self.images, dtype=torch.float32)
self.labels = torch.tensor(self.labels, dtype=torch.long)
def __len__(self):
return len(self.labels)
def __getitem__(self, idx):
# 获取图片和标签
image = self.images[idx]
label = self.labels[idx]
# 如果定义了transform,则对图片进行处理
if self.transform:
image = self.transform(image)
return image, label
# 数据预处理:将数据转换为 tensor 并进行标准化(归一化)
transform = transforms.Compose([
transforms.ToPILImage(), # 将 Tensor 转为 PIL Image
transforms.ToTensor(), # 然后转换为 Tensor
# 使用适合 CIFAR-10 的归一化
transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]), # 归一化
transforms.ColorJitter(brightness=0.2, contrast=0.2) # 增加亮度和对比度
])
这里定义了一个类来进行手动导入数据,其中的方法包括导入路径,训练集测试集,以及转换,对数据进行处理转换格式,设置图片以及标签,这里归一化以后又设置了对比度,让彩色图片更清晰一点。
2.加载数据以及显示图像
train_ds = CIFAR10Manual(root='/Users/qiang/PycharmProjects/PythonProject2/cifar-10-batches-py', train=True,
transform=transform)
test_ds = CIFAR10Manual(root='/Users/qiang/PycharmProjects/PythonProject2/cifar-10-batches-py', train=False,
transform=transform)
# 创建数据加载器
train_dl = DataLoader(train_ds, batch_size=32, shuffle=True)
test_dl = DataLoader(test_ds, batch_size=32, shuffle=False)
# 获取一个批次的数据
imgs, labels = next(iter(train_dl))
# 创建绘图
plt.figure(figsize=(20, 5))
# 循环显示前 20 张图片
for i, img in enumerate(imgs[:20]):
# 将图像数据从 Tensor 转换为 numpy 数组,并调整轴顺序
np_img = img.numpy().transpose((1, 2, 0)) # (C, H, W) 转为 (H, W, C)
# 反归一化:将 [-1, 1] 转回 [0, 1]
np_img = (np_img + 1) / 2
# 打印图像的数据范围,帮助调试
print(np_img.min(), np_img.max()) # 输出图像数据的最小值和最大值
# 对数据进行裁剪,确保数据范围在 [0, 1] 之间
np_img = np.clip(np_img, 0, 1)
# 显示第 i+1 张图像
plt.subplot(2, 10, i + 1)
plt.imshow(np_img) # 直接显示图像
plt.axis('off') # 关闭坐标轴
# 显示图像
plt.show()
3.卷积层设置前向传播
num_classes = 10 # 图片的类别数
class Model(nn.Module):
def __init__(self):
super().__init__()
# 特征提取网络
self.conv1 = nn.Conv2d(3, 64, kernel_size=3) # 第一层卷积,卷积核大小为3*3
self.pool1 = nn.MaxPool2d(kernel_size=2) # 设置池化层,池化核大小为2*2
self.conv2 = nn.Conv2d(64, 64, kernel_size=3) # 第二层卷积,卷积核大小为3*3
self.pool2 = nn.MaxPool2d(kernel_size=2)
self.conv3 = nn.Conv2d(64, 128, kernel_size=3) # 第二层卷积,卷积核大小为3*3
self.pool3 = nn.MaxPool2d(kernel_size=2)
# 分类网络
self.fc1 = nn.Linear(512, 256)
self.fc2 = nn.Linear(256, num_classes)
# 前向传播
def forward(self, x):
x = self.pool1(F.relu(self.conv1(x)))
x = self.pool2(F.relu(self.conv2(x)))
x = self.pool3(F.relu(self.conv3(x)))
x = torch.flatten(x, start_dim=1)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
from torchinfo import summary
# 将模型转移到GPU中(我们模型运行均在GPU中进行)
model = Model().to(device)
summary(model)
loss_fn = nn.CrossEntropyLoss() # 创建损失函数
learn_rate = 1e-2 # 学习率
opt = torch.optim.SGD(model.parameters(),lr=learn_rate)
这块代码和手写识别数字基本没有区别,多建了一层卷积池化层。
4.训练测试循环
def train(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset) # 训练集的大小,一共60000张图片
num_batches = len(dataloader) # 批次数目,1875(60000/32)
train_loss, train_acc = 0, 0 # 初始化训练损失和正确率
for X, y in dataloader: # 获取图片及其标签
X, y = X.to(device), y.to(device)
# 计算预测误差
pred = model(X) # 网络输出
loss = loss_fn(pred, y) # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失
# 反向传播
optimizer.zero_grad() # grad属性归零
loss.backward() # 反向传播
optimizer.step() # 每一步自动更新
# 记录acc与loss
train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
train_loss += loss.item()
train_acc /= size
train_loss /= num_batches
return train_acc, train_loss
def test(dataloader, model, loss_fn):
size = len(dataloader.dataset) # 测试集的大小,一共10000张图片
num_batches = len(dataloader) # 批次数目,313(10000/32=312.5,向上取整)
test_loss, test_acc = 0, 0
# 当不进行训练时,停止梯度更新,节省计算内存消耗
with torch.no_grad():
for imgs, target in dataloader:
imgs, target = imgs.to(device), target.to(device)
# 计算loss
target_pred = model(imgs)
loss = loss_fn(target_pred, target)
test_loss += loss.item()
test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item()
test_acc /= size
test_loss /= num_batches
return test_acc, test_loss
5.正式训练以及图像绘制
epochs = 10
train_loss = []
train_acc = []
test_loss = []
test_acc = []
for epoch in range(epochs):
model.train()
epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)
model.eval()
epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
train_acc.append(epoch_train_acc)
train_loss.append(epoch_train_loss)
test_acc.append(epoch_test_acc)
test_loss.append(epoch_test_loss)
template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}')
print(template.format(epoch + 1, epoch_train_acc * 100, epoch_train_loss, epoch_test_acc * 100, epoch_test_loss))
print('Done')
import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore") #忽略警告信息
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
plt.rcParams['figure.dpi'] = 100 #分辨率
epochs_range = range(epochs)
plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
这里训练次数设置的10次,代码第一次运行时建议设置一次,运行的快一点,确定代码没有错误再换成10或者更多次
6.学习遇到的问题(mac系统):
导入数据时依旧需要手动下载然后导入,不能直接下载,需要用更复杂的代码手动导入。(见第一章)

809



被折叠的 条评论
为什么被折叠?



