跨年博客/轻松向Pytorch卷积神经网络图像识别

Posted 千鱼干

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了跨年博客/轻松向Pytorch卷积神经网络图像识别相关的知识,希望对你有一定的参考价值。

【跨年博客/轻松向】Pytorch卷积神经网络图像识别

写于2021-12-31 23:04,2021年最后一篇博客了,也是2022年第一篇博客。


今年的格言:有一分热,发一分光,就令萤火一般,也可以在黑暗里发一点光,不必等候炬火,此后如竟没有炬火,我便是唯一的光。


导语

前几天不是发了一个MNIST图像识别的文章吗,但那篇还是有一些东西没讲的很好。MNIST图像是灰度图像,很多小伙伴想看看RGB的写法。这次拿RGB图做一个细胞分类吧~

注意

此篇博客是对上篇博客的修正、补充以及拓展。
请先参阅上期博客:【Pytorch】MNIST 图像分类代码 - 超详细解读_CSDN_千鱼干的博客
有地方看不懂没事,看完后看这篇的补充内容。

程序

需要导入的库

import torch
import torch.nn as nn
from torch.nn import Sequential
from matplotlib import pyplot as plt
import torchvision.datasets as datasets
from torch.utils.data import DataLoader
import torchvision.transforms as transforms

torch库就不解释了。
torch.nn库是一个包含了神经网络的Modules和用来继承的包以及一些函数方法,比如nn.functional。因为我们之后定义网络时是从nn.Module继承的,还用了nn里面的卷积、激活函数等等,所以这个库必不可少。
Sequential(中文:序列)是torch.nn里面“整合”层用的。就相当于饼干盒,把饼干“像序列一样”装进去。
matplotlib在这里是一个显示图像的库。我们引入了pyplot,用来显示图像。
torchvision包含一些数据集、模型、图像处理方法。这里用datasets来处理数据集(我们自己的图片)。
torch.utils.data里面的DataLoader是用来将数据集装载用的,以便训练。
torchvision.transforms在这里用于定义数据集处理形式。往下看就知道。

设置数据集

这里假设我们数据集的路径是当前你的python文件所在文件夹下data/CELLS/。这个文件夹下有两个子文件夹,一个叫“linba”,另一个叫“xianxingli”

插播一句,现在是2022-1-1 0:00,这篇博客写了一年(笑)

也就是说,我们的数据集分为淋巴细胞和线性粒细胞两种细胞的图片。
所以我们要设置两个类别:classes = [“linba”, “zhongxingli”]
我们这里设置一下批次大小位64,迭代250次,学习率0.001。
学习率选一个小小的数,这样有助于梯度下降。具体原因见上一篇博客。
这里还要写一个get_variable()函数以获取cuda加速后的自动求导结果。


这里 再次 详细解释一下epochs和batch_szie:
->batch_size表示每轮迭代训练时每次训练的数据量;
->epochs表示训练几轮。

每一次迭代(Iteration)都是一次权重更新,每一次权重更新需要batch_size个数据进行正向传递(Forward)运算得到损失函数,再通过反向传导(Backward)更新参数(注意,在这个过程中需要把梯度(Grad)设置为0,这个后面再讲)。1个迭代等于使用batch_size个样本训练一次。比如有256个样本数据,完整训练完这些样本数据需要:
->batch_size=64;
->迭代4次;
->epochs=1。


而通常会将epochs设为不仅1次,这就跟磨面一样,磨完一轮不够,磨多轮才能得到更加精细的面粉。

这时候因为我们处理的是图片,而我们处理的应该是张量(Pytorch处理的是张量,类似向量,矩阵)。我们怎么让数据集图片变成数据集矩阵呢?
这时就要设置transform(翻译:转换)了。
同时,我们的图片不能太大,否则会让训练很慢。
所以我们首先将图片转化为张量,然后将裁剪为 w ∗ h = 128 ∗ 128 w*h=128*128 wh=128128(w是width,宽;h是height,高;c是channel,通道,就是颜色通道,RGB是红绿蓝三通道)大小的张量。同时我们还要让每个像素数值服从标准为0.5的正态分布。
因为我们需要训练集和测试集两部分,所以将数据集分成两个处理内容,最后将训练集和测试集装载,数据集处理完毕。

最后,我们展示一下某张数据集图片转换为向量并处理之后的样子(可选)。

全部代码如下:

path = "./data/CELLS/"
classes = ["linba", "zhongxingli"]

def get_variable(x):
    x = torch.autograd.Variable(x)
    return x.cuda() if torch.cuda.is_available() else x

batch_size = 64
epochs = 250
lr = 0.001

test_path = "./data/CELLS/"

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Resize((128, 128)),
    transforms.CenterCrop(128),
    transforms.Normalize(
        mean=[0.5, 0.5, 0.5],
        std=[0.5, 0.5, 0.5]
    )
])

data_train = datasets.ImageFolder(root=path, transform=transform)
train_loader = DataLoader(data_train, batch_size=batch_size, shuffle=True)

data_test = datasets.ImageFolder(root=test_path, transform=transform)
test_loader = DataLoader(data_test, batch_size=batch_size, shuffle=True)

# -----------------------展示数据集---------------------------
images, labels = next(iter(train_loader))
img = images[0].numpy().transpose(1, 2, 0)
plt.imshow(img)
plt.title(labels[0])
plt.show()


# -----------------------展示数据集---------------------------

定义网络

网络结构如下图:

注意,输入出的“?x128x128x3”的意思是“?”张图片,“128x128x3”是 w ∗ h ∗ c = 128 ∗ 128 ∗ 3 w*h*c=128*128*3 whc=1281283

怎样定义网络呢?首先我们要从nn.Module继承,然后“装填”入我们的架构,最后在前向计算(forward)函数中进行前向计算。
这里注意一下:forward函数不需要显式调用,因为nn.Module类中有一个函数会自动调用forward。


上一篇博客没写明这里每层的参数是怎么计算的。这一部分相当重要,因为最后传入全连接层时必须要求输入和输出匹配,但这里我上篇没细讲。
这里贴出nn.Conv2d()在Pytorch官方文档里的图:

–>猛戳我 - 原文链接<–


注意这里的符号, H o u t H_out Hout是进行的向下取整
定义类的方式和上一篇相似。
代码:

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()

        self.conv1 = Sequential(
            nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )

        self.conv2 = Sequential(
            nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        self.conv3 = Sequential(
            nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )

        self.conv4 = Sequential(
            nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )

        self.dense = Sequential(
            nn.Linear(8 * 8 * 256, 256),
            nn.ReLU(),
            nn.Dropout(p=0.5),
            nn.Linear(256, 128),
            nn.ReLU(),
            nn.Dropout(p=0.5),
            nn.Linear(128, 2)
        )

    def forward(self, x):
        x1 = self.conv1(x)
        x2 = self.conv2(x1)
        x3 = self.conv3(x2)
        x4 = self.conv4(x3)
        x5 = x4.view(-1, 8 * 8 * 256)
        out = self.dense(x5)
        return out

训练

这一部分我上一篇讲的很详细了。我只贴出代码:

cnn = CNN()
if torch.cuda.is_available():
    cnn = cnn.cuda()

lossF = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(cnn.parameters(), lr=lr)

cnn.train()

loss_pth = 999999999.99
i_pth = 0

for epoch in range(epochs):
    running_loss = 0.0
    running_correct = 0.0
    print("Epochs [/]".format(epoch, epochs))

    for data in train_loader:
        X_train, y_train = data
        X_train, y_train = get_variable(X_train), get_variable(y_train)
        outputs = cnn(X_train)
        _, predict = torch.max(outputs.data, 1)
        # ----------------------------------
        optimizer.zero_grad()
        loss = lossF(outputs, y_train)
        loss.backward()
        optimizer.step()
        # ----------------------------------
        running_loss += loss.item()
        running_correct += torch.sum(predict == y_train.data)

    testing_correct = 0.0

    for data in test_loader:
        X_test, y_test = data
        X_test, y_test = get_variable(X_test), get_variable(y_test)
        outputs = cnn(X_test)
        _, predict = torch.max(outputs.data, 1)
        testing_correct += torch.sum(predict == y_test.data)

    print("Loss:     Training Accuracy: %    Testing Accuracy:%".format(
        running_loss,
        100 * running_correct / len(data_train),
        100 * testing_correct / len(data_test)
    ))

    if running_loss < loss_pth:
        loss_pth = running_loss
        torch.save(cnn, "./models/cell_classify_%d.pth" % i_pth)
        i_pth = i_pth + 1

torch.save(cnn, "cell_classify.pth")
print("训练完成!最小损失为:%f" % loss_pth)

预测

这一部分我在上一篇博客也是讲得比较详细。我只贴出代码。
这里设pics文件夹下还有一个文件夹,里面有待预测图片:

import torch
import torch.nn as nn
from torch.nn import Sequential
from matplotlib import pyplot as plt
import torchvision.datasets as datasets
from torch.utils.data import DataLoader
import torchvision.transforms as transforms

model_path = "./models/cell_classify.pth"
test_path = "./pics/"

classes = ["linbaxibao", "zhongxinglixibao"]

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Resize((128, 128)),
    transforms.CenterCrop(128),
    transforms.Normalize(
        mean=[0.5, 0.5, 0.5],
        std=[0.5, 0.5, 0.5]
    )
])

data_test = datasets.ImageFolder(root=test_path, transform=transform)
test_loader = DataLoader(data_test, batch_size=64, shuffle=True)


class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()

        self.conv1 = Sequential(
            nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )

        self.conv2 = Sequential(
            nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        self.conv3 = Sequential(
            nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )

        self.conv4 = Sequential(
            nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )

        self.dense = Sequential(
            nn.Linear(8 * 8 * 256, 256),
            nn.ReLU(),
            nn.Dropout(p=0.5),
            nn.Linear(256, 128),
            nn.ReLU(),
            nn.Dropout(p=0.5),
            nn.Linear(128, 2)
        )

    def forward<

以上是关于跨年博客/轻松向Pytorch卷积神经网络图像识别的主要内容,如果未能解决你的问题,请参考以下文章

3. 使用PyTorch深度学习库训练第一个卷积神经网络CNN

卷积神经网络 手写数字识别(包含Pytorch实现代码)

[Pytorch系列-34]:卷积神经网络 - 搭建LeNet-5网络与MNIST数据集手写数字识别

[Pytorch系列-40]:卷积神经网络 - 模型的恢复/加载 - 搭建LeNet-5网络与MNIST数据集手写数字识别

[Pytorch系列-41]:卷积神经网络 - 模型参数的恢复/加载 - 搭建LeNet-5网络与MNIST数据集手写数字识别

Python MINIST手写集的识别,卷积神经网络,CNN(最简单PyTorch的使用)