数据集处理
在这个项目中,我们将使用 MNIST Fashion 数据集,这是一个众所周知的数据集,它碰巧作为 PyTorch 库中的一个玩具样本集合在一起。
对于 MNIST 来说,Fashion-MNIST 数据集是一个更具挑战性的替代数据集。这是一个由60,000个小方块28 × 28像素灰度图像组成的数据集,这些图像包含了10种类型的衣服,比如鞋子、 T 恤、裙子等等。
作为一个简短的评论,数据集图像将不会重新缩放,因为我们希望以更高的训练率为代价来提高预测性能。因此,唯一发生的转换将是处理作为张量对象(矩阵)的图像所需的转换。
构建模型
众所周知,卷积神经网络(CNN)是计算机视觉最常用的体系结构之一。这种体系结构通常可以在90% 的准确率范围内取得令人印象深刻的结果。不仅如此,这些模型往往能够很好地推广。
一个由卷积变换和激活变换组成的细胞神经网络。几个层可以通过管道连接在一起,以增强特征提取(是的,我知道您在想什么,我们用原始数据填充模型)。一般来说,我们使用卷积作为一种方法来减少处理的信息量,同时保持特征的完整性。这有助于我们减少最后一层的输入(和神经元)数量。于这种特殊的情况,我们将使用一个卷积,其中 kerner size 为5,Max Pool size为2。
设计模型时最难的部分之一是确定矩阵的维数,这需要作为卷积和最后一个完全连接的线性层的输入参数。最后一层帮助我们确定预测的类别或标签,在本文这种情况下,这些是不同的服装类别。
我们将创建一个2层的 CNN 与 Max Pool 激活函数。由于我们不想失去图像边缘的特征,我们将在进行卷积操作之前对图像进行填充。在整个项目中,我们将使用 m = n (行等于列)的方阵。我们将矩阵输入维度称为 i,在这种特殊情况下,原始图像的维度为 i = 28。同样,输出矩阵的维数也用字母 o 来表示。
卷积参数
kernel = 5
padding = 2
stride = 1
dilation = 1
基于给定的这些参数,卷积过程后的新矩阵维度为:
O = I + 2 p - k + 1
O = I
其中:
p: padding
k: kernel size
I: Input matrix size
O: Output matrix size
MaxPool 激活参数
对于 MaxPool,在默认情况下,stride 是内核的大小:
kernel = 2
padding = 0
stride = 0
dilation = 1
在这种情况下,Max Pool 之后的新矩阵维度如下:
O = (I - k)/s + 1
O = (I - 2)/2 + 1
O = I/2
实际项目矩阵维度
在前面的讨论之后,在这种特殊情况下,项目矩阵维如下:
在第一次卷积之后,将创建16个输出矩阵,其大小为28x28 px
最大池激活后的矩阵维数是14x14像素
第二次卷积之后的32个结果矩阵具有与第一次卷积相同的内核和填充,其维数为14x14 px
最后,在最后一次激活 Max Pool 之后,结果矩阵的维数为7x7 px
最后一次激活 Max Pool 之后的32个通道,每个有7x7像素,最后一共有1568个输入到全连接的最终层。
下面的类显示了 forward 方法,我们将在其中定义如何在模型内部组织操作。这就是我们设计神经网络结构的地方。PyTorch 提供了另一种方式,称为 Sequential 模式。正如您可能注意到的,第一个转换是卷积,接着是 Relu 激活,然后是 MaxPool 激活/转换。正如前面提到的,卷积作为一个特征提取过程,其中预测器被保留,信息有一个压缩。通过这种方式,我们可以在不丢失输入数据的情况下更快地训练网络。
class CNN(nn.Module):
# Constructor
def __init__(self, out_1=16, out_2=32, out_mp2=7*7):
super(CNN, self).__init__()
self.cnn1 = nn.Conv2d(in_channels=1, out_channels=out_1,
kernel_size=5, padding=2)
self.maxpool1=nn.MaxPool2d(kernel_size=2)
self.cnn2 = nn.Conv2d(in_channels=out_1, out_channels=out_2,
kernel_size=5, stride=1, padding=2)
self.maxpool2=nn.MaxPool2d(kernel_size=2)
self.fc1 = nn.Linear(out_2 * out_mp2, 10)
# Prediction
def forward(self, x):
x = self.cnn1(x)
x = torch.relu(x)
x = self.maxpool1(x)
x = self.cnn2(x)
x = torch.relu(x)
x = self.maxpool2(x)
# Flatten the matrices
x = x.view(x.size(0), -1)
x = self.fc1(x)
return x
确定优化器和数据加载器
在构建我们的神经网络之前,我们必须确定的损失函数和优化参数。因此,我们将选择一个交叉熵策略作为损失函数。这个函数通常是用非二元分类变量来选择的。为了确定最小损失,我们将使用随机梯度下降策略,在数据不适合放入内存的情况下,这几乎是大家普遍使用的方法。利用SGD,运行损失函数求至少一个局部极小值,分批进行,分步进行。为此,我们将创建 train_loader 和 validation_loader 迭代器。
训练模型
如前所述,我们将通过数据运行一些训练迭代(epoch) ,这将分批完成。然后,我们将用验证数据检查模型的准确性,最后我们将重复这个过程。需要注意的是,optimizer.step()调整了下一次迭代的模型权重,这是为了用真正的函数 y 最小化误差。
最后,我们将添加每个epoch的成本和准确度值,并绘制最终结果。我们将看到,随着模型调整权重和从训练数据中“学习”,损失如何下降,准确性如何提高。
n_epochs=6
cost_list=[]
accuracy_list=[]
N_test=len(validation_dataset)
COST=0
def train_model(n_epochs):
for epoch in range(n_epochs):
COST=0
for x, y in train_loader:
optimizer.zero_grad()
z = model(x)
loss = criterion(z, y)
loss.backward()
optimizer.step()
COST+=loss.data
cost_list.append(COST)
correct=0
#perform a prediction on the validation data
for x_test, y_test in validation_loader:
z = model(x_test)
_, yhat = torch.max(z.data, 1)
correct += (yhat == y_test).sum().item()
accuracy = correct / N_test
accuracy_list.append(accuracy)
train_model(n_epochs)
分析结果
下面你将看到这个模型的损失和准确性。正如预期的那样,该算法在对核函数和全连接层权值进行微调的同时,降低了损失,提高了精度。换句话说,模型通过迭代学习。
检查分类
这里有一张图片描绘了 Fashion-MNIST 数据集中的不同类别。
最后,我们将检查一些模型没有正确分类的样本。正如你所看到的,有时即使对于人眼来说,也很难区分这样低分辨率的图片是凉鞋还是运动鞋。还要注意第一张图片,模型预测的是一个包,但它是一双运动鞋。它看起来有点像一个袋子。这种款式也很难区分套头衫和外套,但是说实话,这种形象很难区分。
总结
一个2层的 CNN 在预测来自时尚 MNIST 数据集的图像方面做了一个出色的工作,在6个训练周期之后的总体准确度几乎达到90% 。这并不奇怪,因为这种类型的神经网络架构取得了巨大的成果。
当然,准确性可以提高,减少卷积核的大小,以减少每次迭代的数据丢失,以更高的训练时间为代价。此外,在每个卷积和最终全连接层之后进行归一化处理,这有助于在更短的时间内达到更高的精度。
· END ·
HAPPY LIFE