本次猫狗识别参考的是余~意大佬写的详解pytorch实现猫狗识别来实现的,主要是通过代码去学习损失函数、数据加载等知识点的实际应用,和权重,学习率等对最后准确率的影响。

期望实现的目标

  • 实现随机抽取9张测试集中的图片,通过模型来预测出结果 (已实现)

使用的数据集

数据集采用两万五千张图片(未经筛选前),来源于kaggle官网,下载来源dogsVScats | Kaggle

代码的主要流程

  1. 定义数据预处理的方法(包括图像增强、缩放、裁剪、翻转、标准化等)。
  2. 构建数据集(包括训练集和测试集)。
  3. 定义模型(使用预训练的 ResNet-50 网络结构,并替换最后一层的全连接层)。
  4. 定义损失函数和优化器。
  5. 进行模型训练,设置训练轮数和训练阶段(训练或测试),计算每个阶段的损失率和正确率。
  6. 保存模型参数。
  7. 从测试集中随机抽取9张图片,并用训练好的模型对这9张图片进行预测。
  8. Matplotlib 中显示这9张图片和它们的预测结果。

定义数据处理方式:

通过transforms定义了针对训练集和测试集的数据处理方式,包括随机裁剪、缩放、随机水平翻转、标准化等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
data_trainsforms = {
"train": transforms.Compose([
transforms.RandomResizedCrop(300),
transforms.Resize((224, 224)),
transforms.RandomCrop((224, 224)),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]),
]),
"test": transforms.Compose([
transforms.RandomResizedCrop(300),
transforms.RandomHorizontalFlip(),
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]),
]),
}

加载数据:

通过datasets模块中的ImageFolder方法加载数据集,并使用DataLoader进行批量处理和加载内存中,并且每次迭代返回一批样本。

1
data_loader = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=20, shuffle=True) for x in ["train", "test"]}

定义模型:

使用models模块中的resnet50方法加载预训练好的ResNet-50模型,并修改最后一层全连接层的输出节点数为2,以适应猫狗二分类任务。

1
model = models.resnet50(pretrained=True)

训练模型:

采用交叉熵损失函数和Adam优化器进行模型训练,并在训练过程中计算损失率和正确率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
epoch_n = 1
for epoch in range(epoch_n):
print("Epoch {}/{}".format(epoch + 1, epoch_n))
print("-" * 10)

for phase in ["train", "test"]:
if phase == "train":
print("训练中。。。")
model.train(True)
else:
print("测试中。。。")
model.train(False)
running_loss = 0.0
running_corrects = 0

for batch, data in enumerate(data_loader[phase], 1):
X, y = data
if Use_gpu:
X, y = Variable(X.cuda()), Variable(y.cuda())
else:
X

测试模型:

在模型训练完成后,使用随机采样的方式从测试集中选取9张图片进行测试,并将预测结果可视化显示在Matplotlib中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
model.load_state_dict(torch.load('model.pth'))

model.eval()

test_images = image_datasets['test'].imgs
random.shuffle(test_images)
test_images = test_images[:9]

fig, axs = plt.subplots(nrows=3, ncols=3, figsize=(8, 8))
fig.suptitle('Model Predictions')

for i, (image_path, label) in enumerate(test_images):
image = Image.open(image_path)
image = image.resize((224, 224))
image = data_trainsforms['test'](image)
image = image.unsqueeze(0)

if Use_gpu:
image = image.cuda()
output = model(image)
_, predicted = torch.max(output.data, 1)
predicted_label = example_classees[predicted.item()]

image = image.cpu().numpy()

axs[i//3, i%3].imshow(np.transpose(image.squeeze(), (1, 2, 0)))
axs[i//3, i%3].axis('off')
axs[i//3, i%3].set_title(predicted_label)

plt.show()

代码展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
from torchvision import datasets, models, transforms
import os
from torch.autograd import Variable
import torch.utils.data
import random
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np

data_dir = "E:/trains/CatsVSDogs"
# 图片处理
data_trainsforms = {
"train": transforms.Compose([
transforms.RandomResizedCrop(300),
transforms.Resize((224, 224)),
transforms.RandomCrop((224, 224)),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]),
]),

"test": transforms.Compose([
transforms.RandomResizedCrop(300),
transforms.RandomHorizontalFlip(),
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]),
]), }

# 拼接路径
image_datasets = {
x: datasets.ImageFolder(root=os.path.join(data_dir, x),
transform=data_trainsforms[x])
for x in ["train", "test"]
}
# 数据加载器
data_loader = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=20, shuffle=True) for x in
["train", "test"]}

X_example, y_example = next(iter(data_loader["train"]))
example_classees = image_datasets["train"].classes
index_classes = image_datasets["train"].class_to_idx

# 迁移学习模型
model = models.resnet50(pretrained=True)

Use_gpu = torch.cuda.is_available()

for parma in model.parameters():
parma.requires_grad = False # 屏蔽预训练模型的权重,只训练最后一层的全连接的权重
model.fc = torch.nn.Linear(2048, 2)

if Use_gpu:
model = model.cuda()

# 损失函数和优化器
loss_f = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.fc.parameters(), lr=0.00001)

epoch_n = 5

for epoch in range(epoch_n):
print("Epoch {}/{}".format(epoch + 1, epoch_n))
print("-" * 10)

for phase in ["train", "test"]:
if phase == "train":
print("训练中。。。")
model.train(True)
else:
print("测试中。。。")
model.train(False)
running_loss = 0.0
running_corrects = 0

for batch, data in enumerate(data_loader[phase], 1):
X, y = data
if Use_gpu:
X, y = Variable(X.cuda()), Variable(y.cuda())
else:
X, y = Variable(X), Variable(y)

y_pred = model(X)

_, pred = torch.max(y_pred.data, 1)
optimizer.zero_grad()
loss = loss_f(y_pred, y)
if phase == "train":
loss.backward() # 反向传播计算当前梯度# 误差反向传播,采用求导的方式,计算网络中每个节点参数的梯度,显然梯度越大说明参数设置不合理,需要调整
optimizer.step() # 优化采用设定的优化方法对网络中的各个参数进行调整
running_loss += loss.item()
running_corrects += torch.sum(pred == y.data)
if batch % 500 == 0 and phase == "train":
print("Batch{},训练损失率:{:.4f},训练正确率:{:.4f}".format(batch, running_loss / batch,
100 * running_corrects / (20 * batch)))
epoch_loss = running_loss * 20 / len(image_datasets[phase])
epoch_acc = 100 * running_corrects / len(image_datasets[phase])

print("{} 当前损失率:{:.4f} 当前正确率:{:.4f}%".format(phase, epoch_loss, epoch_acc))
torch.save(model.state_dict(),'model.ckpt1')
torch.save(model.state_dict(),'model.pth')
print("over")

model.load_state_dict(torch.load('model.pth'))

model.eval()

test_images = image_datasets['test'].imgs
random.shuffle(test_images)
test_images = test_images[:9]

fig, axs = plt.subplots(nrows=3, ncols=3, figsize=(8, 8))
fig.suptitle('Model Predictions')

for i, (image_path, label) in enumerate(test_images):
image = Image.open(image_path)
image = image.resize((224, 224))
image = data_trainsforms['test'](image)
image = image.unsqueeze(0)

if Use_gpu:
image = image.cuda()
output = model(image)
_, predicted = torch.max(output.data, 1)
predicted_label = example_classees[predicted.item()]

image = image.cpu().numpy()

axs[i//3, i%3].imshow(np.transpose(image.squeeze(), (1, 2, 0)))
axs[i//3, i%3].axis('off')
axs[i//3, i%3].set_title(predicted_label)

plt.show()

结果展示

可视化展示

测试损失率和成功率展示

小结

本次实现的猫狗识别有以下不足:

  • 正确率并没有预期一样高,需要在原有清理过的训练集上,再次筛选。

但是在本次的学习中,学会了如何使用可视化进行图片展示,和训练结果输出。同时再次巩固了深度学习神经网络的基本实现方式。以及上次在手写数字识别中没能实现的可视化展示、和模型的保存与迭代测试,在本次也得以解决。