(pytorch深度学习)利用三层网络实现数据分类

目录

写在前面

这一段时间上课在学习简单的神经网络,最后结课的时候要求搭建一个简单的网络模型,并训练识别几类交通标志牌。写下博客记录,供日后学习。这个简易模型可以根据不同的数据集实现对不同的类别的图片识别。但是层数只有三层,只能训练简易的数据集。
/ ————————————2021.8.7更新————————————————————–/
1.加了一些笔记,补充之前自己学习上面一些不理解的地方
————————————————————————————————————————

一些笔记

1. ‘.view()’函数相关笔记

.view()函数的用法
作用:改变Tensor的维度,使其变成你需要的维度
例如output.view(2,2) 将维度变成2*2
如果是在view里面加入了-1的参数,那么代表着这个维度会根据其他唯独算出来
即:一个size为16的tensor变量——output
经过了output.view(-1,8)就会变成(2,6)

2.卷积神经网络中图像大小的计算公式

在刚开始实现卷积神经网络的过程中,我经常由于没有算清楚图像的层数,而导致了最终程序出现报错的情况,搜索了资料之后发现,有固定的公式来计算我们所需要的图像
公式如下:
卷积层——输出=[(输入大小-卷积核大小+2*填充)/步长] +1

(pytorch深度学习)利用三层网络实现数据分类

池化层——输出=[(输入大小-卷积核大小)/步长] +1.

(pytorch深度学习)利用三层网络实现数据分类
拿一个例子来说明一下

        train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=2)

        self.conv1 = torch.nn.Conv2d(in_channels=3, out_channels = 32, kernel_size=5, stride=1, padding=2)

        self.pool1 = torch.nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv2 = torch.nn.Conv2d(in_channels=32, out_channels = 32, kernel_size=5, stride=1, padding=2)
        self.pool2 = torch.nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv3 = torch.nn.Conv2d(in_channels=32, out_channels = 32, kernel_size=5, stride=1, padding=2)
        self.pool3 = torch.nn.MaxPool2d(kernel_size=2, stride=2)

3.图像正规化处理

正规化:将数据按比例缩放,使之落入一个小的特定区间。

(pytorch深度学习)利用三层网络实现数据分类
因为图像一般具有三个维度,RGB,所以需要在三个维度上给均值和方差。

; 实现卷积神经网络的过程

这段时间的学习下来,我理解的深度学习的流程大致如下:

图像预处理化定义

导入数据

搭建网络模型

训练模型

保存模型或参数

预测图片

过程

一、图像预处理化函数定义

'''
 Image preprocessing package in Python, Compose is used to integrate muiltiple steps
--图像预处理化的步骤,将所有的步骤全部写到这个里面
'''
myTransforms  = (
    [transforms.Resize((40,40)),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

图像预处理化是一步非常重要的步骤,尤其是需要将图像数据变成张量,因为pytorch里面的处理数据的基本操作是张量,即Tensor,具体可以搜索张量的含义。
博主这里用的预处理化操作只有三个,实际上还可以有很多其他的操作
例如:

ToPILImage: convert a tensor to PIL image

Resize:将图像拉伸或者收缩成自己需要的尺寸

CenterCrop:在图片的中间区域进行裁剪

RandomCrop:在一个随机的位置进行裁剪

RandomHorizontalFlip:以0.5的概率水平翻转图像

RandomVerticalFlip:以0.5的概率竖直翻转图像

RandomResizedCrop:将PIL图像裁剪成任意大小和纵横比

Grayscale:将图像转换为灰度图像

RandomGrayscale:将图像以一定的概率转换为灰度图像

FiceCrop:把图像裁剪为四个角和一个中心

Pad:填充

ColorJitter(色彩抖动):随机改变图像的亮度对比度和饱和度。

根据自己的需要添加或者删除操作。

下面的这一篇博客里面,博主讲了许多种图像处理和增强的方法,值得收藏
【pytorch】常用图像处理与数据增强方法合集(torchvision.transforms)

二、数据导入

我们用的数据集是按照不同的文件夹里面放入了不同的图片,所以可以直接利用torchvision.datasets.ImageFolder()这一个函数来进行数据读入,并将图像进行预处理化。
针对的数据集都需要如以下数据集类型:即按照文件夹的顺序把文件夹的名字当作标签,文件夹里面的图片作为该标签下面的数据。


root/dog/xxx.png
root/dog/xxy.png
root/dog/xxz.png

root/cat/123.png
root/cat/nsdf3.png
root/cat/asd932_.png

例2:

(pytorch深度学习)利用三层网络实现数据分类
用法:
dataset=torchvision.datasets.ImageFolder(
                       root, transform=None,
                       target_transform=None,
                       loader=<function default_loader>,
                       is_valid_file=None)

参数含义

  • root:图片存储的根目录,即各类别文件夹所在目录的上一级目录。例如/root/home/user
  • transform:对图片进行预处理的操作(函数),原始图片作为输入,返回一个转换后的图片。
  • target_transform:对图片类别进行预处理的操作,输入为 target,输出对其的转换。如果不传该参数,即对 target 不做任何转换,返回的顺序索引 0,1, 2…

  • loader:表示数据集加载方式,通常默认加载方式即可。

  • is_valid_file:获取图像文件的路径并检查该文件是否为有效文件的函数(用于检查损坏文件)

返回的dataset有以下属性

  • self.classes:用一个 list 保存类别名称
  • self.class_to_idx:类别对应的索引,与不做任何转换返回的 target 对应
  • self.imgs:保存图片的list
  • dataset[0] 表示取第一个训练样本,即(path, class_index)。可以自自行printf(dataset[0][0])和dataset[0][0]查看区别

使用了ImageFolder之后,还需要将返回的dataset变成lodaer一下,将数据转化成模型可以使用的数据类型。具体的实例见下方

train_dataset = torchvision.datasets.ImageFolder('/home/Desktop/dataset',transform=myTransforms)
'''
train: use train file:是否使用训练文件
download: if you have not downloaded the test file, program will download from web. if you have downloaded, do nothing
the method is myTransforms:训练方法是自己定义的Compose
'''
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=2)
'''
tran_dateset: take test_file form:训练的对象
batch_size: how many samples per batch to load (default:1):一次训练时候抓取的样本数量.或者一次检测图片的时候抓取的图片数量
shuffle: wheather reshuffle after every train:每次训练之前,是否要将训练集的数据打乱
num_workers: how many subprocesses use for data loading:使用多少个线程加载数据集
'''

三、模型搭建

这个模型中包含了三层

'''
in_channels: Number of channels in the input image
out_channels: Number of channels produced by the convolution
#注意:输出的通道数量代表了你有多少个卷积核,这里面有32个通道有默认的不同的卷积参数(其实也可以自己更改参数)
#我们训练的实质,其实就是训练这一个参数,对于不同类别的图片,参数会有kernel_size区别,这个参数就相当于是过滤器,通过了这一个过滤器之后我们就能快速得到是哪一类别
kernel_size: Size of the convolving kernel:卷积核的大小
stride:步幅,卷积核在图片上移动后遍历每一个像素点,每次移动的步长就是stride
padding:填充,在图像周围填充一圈像素点。
'''
class  myNetwork(torch.nn.Module):
    def  __init__(self) :
        '''
        初始化函数
        '''
        super(myNetwork, self).__init__()
        '''
        super(Net, self).init()是指首先找到Net的父类(比如是类NNet,然后把类Net的对象self转换为类NNet的对象,然后"被转换"的类NNet对象调用自己的init函数
        简单理解:子类把父类的__init__()放到自己的__init__()当中,这样子类就有了父类的__init__()的那些东西。
        '''
        self.convl1 = torch.nn.Conv2d(in_channels = 3, out_channels = 32, kernel_size = 5, stride = 1, padding = 2)
        self.pool11 = torch.nn.MaxPool2d(kernel_size = 2, stride = 2)
        '''
        # 初始化卷积层和池化层
        # 池化层的操作和卷积层的操作类似,但是最后是采用取最大值或者最小值来代表卷积之后那片区域的值
        '''
        self.convl2 = torch.nn.Conv2d(in_channels = 32, out_channels = 32, kernel_size = 5, stride = 1, padding = 2)
        self.pool12 = torch.nn.MaxPool2d(kernel_size = 2, stride = 2)
        self.convl3 = torch.nn.Conv2d(in_channels = 32, out_channels = 32, kernel_size = 5, stride = 1, padding = 2)
        self.pool13 = torch.nn.MaxPool2d(kernel_size = 2, stride = 2)

        '''
        输入图像的尺寸为32*32*40*3(RGB)
    图片变化尺寸的过程见后续的公式
        在这个定义的三层卷积网络中,卷积层和padding加起来不会影响图片的尺寸
        所以每一次池化,大小为2*2,步长为1,图片就会除以2
        40*40*32经过3次池化之后,就变成了40/2/2/2*40/2/2/2*32 = 32*5*5

        '''
        self.fc1 = torch.nn.Linear(in_features = 32*5*5, out_features = 64, bias=True)
        self.fc2 = torch.nn.Linear(in_features = 64, out_features = 64, bias=True)
        self.fc3 = torch.nn.Linear(in_features = 64, out_features = 10, bias=True)
        '''
        in_features指的是输入的二维张量的大小,即输入的[batch_size, size]中的size。
        out_features指的是输出的二维张量的大小,即输出的二维张量的形状为[batch_size,output_size],当然,它也代表了该全连接层的神经元个数。
        实际上全连接层也是做了一个类似于卷积的操作,将所有的特征点映射到了一个值
        '''

    def forward(self,train_data):
        output = self.pool11(torch.nn.functional.relu(self.convl1(train_data)))
        output = self.pool12(torch.nn.functional.relu(self.convl2(output)))
        output = self.pool13(torch.nn.functional.relu(self.convl3(output)))
        output = output.view(-1, 32*5*5)
        output = torch.nn.functional.relu(self.fc1(output))
        output = torch.nn.functional.relu(self.fc2(output))
        output = self.fc3(output)
        return output

图像尺寸变化的公式

  • height in:指输入的高
  • height kernel:卷积核的大小
  • padding:图像边缘增加的像素
  • stride:步长

(pytorch深度学习)利用三层网络实现数据分类

四、开始训练模型

训练模型的过程大致如下:

遍历数据集开始

抓取图片数据和标签

计算预测值和损失值

后向传递

更新所有参数

这一个简单的神经网络可以理解成一个分类器,可以分出图像属于哪一类的图片
训练模型其实可以简单的理解成不停的遍历数据集,后向传递参数,使得网络模型的分类器的参数针对你这几类图片越来越好。


myModel = myNetwork()
myOptimzier = optim.SGD(myModel.parameters(),lr = 0.01, momentum = 0.9)
myLoss = torch.nn.CrossEntropyLoss()
for _epoch in range(10):
    training_loss = 0.0
    '''
    step每次都是增加1,即遍历了整个图片
    而每次抓取图片的数量为64
    '''
    for _step, data in enumerate(train_loader):
        image,label = data
        predict_label = myModel.forward(image)
        loss = myLoss(predict_label, label)
        myOptimzier.zero_grad()
        loss.backward()
        myOptimzier.step()
        training_loss = training_loss + loss.item()
        ''''
         loss是张量,loss.item是对其取数值,但注意 item() 只适用于 tensor 只包含一个元素的时候。
        每10次把平均损失值计算出来
        '''
        if _step % 10 == 0:
            print('[iteration - %3d] training loss: %.3f' % (_epoch*len(train_loader) + _step, training_loss/10))
            training_loss = 0.0

五、保存模型

针对保存模型,我们可以使用torch.save来保存模型参数或者保存整个模型


'''保存模型'''

torch.save(myModel, 'model2.pkl')

之后需要使用模型的时候,或者需要继续训练模型参数的时候,我们可以使用load函数来进行加载模型和参数

torch.load(f, map_location=None, pickle_module=<module 'pickle' from '...'>)

torch.nn.Module.load_state_dict(state_dict, strict=True)

具体使用方法可以参考以下博客,写的特别清晰

Pytorch:模型的保存与加载 torch.save()、torch.load()、torch.nn.Module.load_state_dict()

六、使用训练好的模型测试训练集的准确率

correct = 0
total = 0
'''
关于with,with的作用是对于with后面任务进行事前设置,事后清理。对异常情况进行处理
(1)紧跟with后面的语句被求值后,返回对象的"–enter–()"方法被调用,这个方法的返回值将被赋值给as后面的变量;
(2)当with后面的代码块全部被执行完之后,将调用前面返回对象的"–exit–()"方法。
    比如文件需要打开,用完需要关闭,并且于对文件读取的数据发生异常的时候,进行处理

对于torch.no_grad()的理解,pytorch的张量变量有一个requires_grad参数,设置为ture的话,反向传播的时候tensor会自动求导
这里设置为no_grad。即不用构建计算图,因为我们只是进行模型测试,不需要进行更新参数,使得内存节约
'''
with torch.no_grad():
    for images,labels in train_loader:
        outputs = myModel(images)
        numbers,predicted = torch.max(outputs.data,1)

        total += labels.size(0)
        correct += (predicted==labels).sum().item()

print('Testing Accuracy : %d %%' % ( 100 * correct / total))

简单来说就是看模型输出的准确率和loss来判断你的模型训练的是否可以,如果准确率不够,可以试试增加卷积的层数和训练遍历的次数来加大准确率和减小loss

七、测试模型

当我们训练完了模型之后,我们需要将模型用测试集里面的图片进行测试,下面是一个简单的例子,实现了2w张图片遍历测试,并输出结果到txt文件中
其实最重要的就是torch.load和net(img)这两个函数

from matplotlib import image
import torch
import torchvision
import torch.nn.functional as F
import torchvision.transforms as transforms
import torch.optim as optim
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
from torchvision import transforms

class  myNetwork(torch.nn.Module):
    def  __init__(self) :
        super(myNetwork, self).__init__()
        self.convl1 = torch.nn.Conv2d(in_channels = 3, out_channels = 32, kernel_size = 5, stride = 1, padding = 2)
        self.pool11 = torch.nn.MaxPool2d(kernel_size = 2, stride = 2)
        self.convl2 = torch.nn.Conv2d(in_channels = 32, out_channels = 32, kernel_size = 5, stride = 1, padding = 2)
        self.pool12 = torch.nn.MaxPool2d(kernel_size = 2, stride = 2)

        self.convl3 = torch.nn.Conv2d(in_channels = 32, out_channels = 32, kernel_size = 5, stride = 1, padding = 2)
        self.pool13 = torch.nn.MaxPool2d(kernel_size = 2, stride = 2)
        self.fc1 = torch.nn.Linear(in_features = 32*5*5, out_features = 64, bias=True)
        self.fc2 = torch.nn.Linear(in_features = 64, out_features = 64, bias=True)
        self.fc3 = torch.nn.Linear(in_features = 64, out_features = 10, bias=True)
    def forward(self,train_data):
        output = self.pool11(torch.nn.functional.relu(self.convl1(train_data)))
        output = self.pool12(torch.nn.functional.relu(self.convl2(output)))
        output = self.pool13(torch.nn.functional.relu(self.convl3(output)))
        output = output.view(-1, 32*5*5)
        output = torch.nn.functional.relu(self.fc1(output))
        output = torch.nn.functional.relu(self.fc2(output))
        output = self.fc3(output)
        return output

myTransforms  = transforms.Compose(
    [transforms.Resize((40,40)),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

predicted = 1
if __name__ == '__main__':
    with open("result_lyx","w") as file:
        for i in range(20000):
            net=torch.load('model2.pkl')
            torch.no_grad()
            img=Image.open('/home/usr/Desktop/dataset/test/' + str(i) + ".png")
            img=myTransforms(img).unsqueeze(0)
            outputs = net(img)
            _, predicted = torch.max(outputs, 1)

            file.write(str(i) + ".png" + " " + str(predicted.item()))
            file.write("\n")

完整代码

训练模型代码

from matplotlib import image
import torch
import torchvision
import torch.nn.functional as F
import torchvision.transforms as transforms
import torch.optim as optim
import matplotlib.pyplot as plt
import numpy as np
from torchvision.datasets import ImageFolder

from torch.utils.tensorboard import SummaryWriter
from torchvision.transforms.transforms import RandomHorizontalFlip, Resize

myTransforms  = transforms.Compose(
    [transforms.Resize((40,40)),

    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
train_dataset = ImageFolder('/自己的路径',transform=myTransforms)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=2)
class  myNetwork(torch.nn.Module):
    def  __init__(self) :
        super(myNetwork, self).__init__()
        self.convl1 = torch.nn.Conv2d(in_channels = 3, out_channels = 32, kernel_size = 5, stride = 1, padding = 2)
        self.pool11 = torch.nn.MaxPool2d(kernel_size = 2, stride = 2)
        self.convl2 = torch.nn.Conv2d(in_channels = 32, out_channels = 32, kernel_size = 5, stride = 1, padding = 2)
        self.pool12 = torch.nn.MaxPool2d(kernel_size = 2, stride = 2)
        self.convl3 = torch.nn.Conv2d(in_channels = 32, out_channels = 32, kernel_size = 5, stride = 1, padding = 2)
        self.pool13 = torch.nn.MaxPool2d(kernel_size = 2, stride = 2)

        self.fc1 = torch.nn.Linear(in_features = 32*5*5, out_features = 64, bias=True)
        self.fc2 = torch.nn.Linear(in_features = 64, out_features = 64, bias=True)
        self.fc3 = torch.nn.Linear(in_features = 64, out_features = 10, bias=True)
    def forward(self,train_data):
        output = self.pool11(torch.nn.functional.relu(self.convl1(train_data)))
        output = self.pool12(torch.nn.functional.relu(self.convl2(output)))
        output = self.pool13(torch.nn.functional.relu(self.convl3(output)))
        output = output.view(-1, 32*5*5)
        output = torch.nn.functional.relu(self.fc1(output))
        output = torch.nn.functional.relu(self.fc2(output))
        output = self.fc3(output)
        return output

myModel = myNetwork()
myOptimzier = optim.SGD(myModel.parameters(),lr = 0.01, momentum = 0.9)
myLoss = torch.nn.CrossEntropyLoss()

for _epoch in range(10):
    training_loss = 0.0
    for _step, data in enumerate(train_loader):
        image,label = data
        predict_label = myModel.forward(image)
        loss = myLoss(predict_label, label)
        myOptimzier.zero_grad()
        loss.backward()
        myOptimzier.step()
        training_loss = training_loss + loss.item()
        if _step % 10 == 0:
            print('[iteration - %3d] training loss: %.3f' % (_epoch*len(train_loader) + _step, training_loss/10))
            training_loss = 0.0

'''保存模型参数'''

torch.save(myModel, 'model2.pkl')
correct = 0
total = 0
with torch.no_grad():
    for images,labels in train_loader:
        outputs = myModel(images)
        numbers,predicted = torch.max(outputs.data,1)

        total += labels.size(0)
        correct += (predicted==labels).sum().item()
print('Testing Accuracy : %d %%' % ( 100 * correct / total))

测试模型完整代码见上方七即可

Original: https://blog.csdn.net/scarecrow_sun/article/details/118422624
Author: 暮尘依旧
Title: (pytorch深度学习)利用三层网络实现数据分类

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/665902/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球