深度学习代码篇(pytorch实现)

文章目录

  • 一、Dataset加载数据——获取数据及其label
  • 二、tensorboard的使用
    • 1. add_image()函数——显示image
    • 2. add_scalar()函数——显示函数
  • 三、torchvision中transform.py的使用
  • 三、torchvision中数据集的使用
  • 四、Dataloader打包压缩——为网络提供不同的数据形式
  • 五、神经网络
    • 1. 神经网络基本骨架(简单使用)
    • 2. 卷积层(convolution layers)
    • 3. 最大池化的使用(pooling layers)
    • 4. 非线性激活(Non-linear Activations)
      • relu
      • sigmoid
    • 5. 线性层(Linear Layers)(全连接层)
    • 6. 搭建网络
    • 7. 通过 torch.nn.Sequential 使得网络搭建代码更加简洁
    • 8. loss function
    • 9. 优化器
    • 10. 在pytorch提供的网络模型的基础上进行修改
    • 10. 网络模型的保存和读取
      • 保存方式1
      • 保存方式2
  • 六、完整模型的训练套路
    • GPU加速训练
  • 七、完整模型的验证套路


一、Dataset加载数据——获取数据及其label

from torch.utils.data import Dataset
from PIL import Image
import os
class MyData(Dataset):
    def __init__(self,root_dir,label_dir):
        self.root_dir = root_dir
        self.label_dir = label_dir
        self.path = os.path.join(self.root_dir,self.label_dir) #传递的是图片的相对地址
        self.img_path_list = os.listdir(self.path) #将相对地址变为列表形式(可按index找指定的图像)
    def __getitem__(self, idx):
        img_name = self.img_path_list[idx] #图像名字
        img_item = os.path.join(self.root_dir,self.label_dir,img_name) #图像全貌(相对地址要具体到单张图片)
        img = Image.open(img_item)
        label = self.label_dir
        return img,label
    def __len__(self):
        return len(self.img_path_list)

root_dir = "dataset1/train"
ants_dir = "ants"
bees_dir = "bees"
ant_dataset = MyData(root_dir,ants_dir)
bees_dataset = MyData(root_dir,bees_dir)
train_dataset = ant_dataset + bees_dataset

print(train_dataset[124])
img,label = train_dataset[124]
print(label)
img.show()

结果:(, 'bees')
'bees'

class MyData(Dataset):表示这个类继承括号中的类,并且要求重写类内函数。
在类的函数中含self. 的都是新建的属性

二、tensorboard的使用

1. add_image()函数——显示image

要想在tensor中查看图像,要在虚拟环境中输入:tensorboard --logdir=logs打开网址即可

from torch.utils.tensorboard import SummaryWriter #载入SummaryWriter类
import numpy as np
from PIL import Image
#调用该类中函数 生成的事件文件 存到logs这个文件夹(和py文件在同一目录下)中
writer = SummaryWriter("logs") #用SummaryWriter类初始化对象
image_path = "dataset1/train/ants/0013035.jpg" #相对地址
image_PIV= Image.open(image_path) #打开图像(PIV(pic)类型)

img_numpy = np.array(image_PIV) #转换为numpy类型
#或者这么获得numpy类型
# import cv2
# image_path = "dataset/train/ants/0013035.jpg"
# img_numpy = cv2.imread(img_path) #生成numpy类型的img,传递的参数为相对地址
#或者利用totensor转换为tensor型再传参

#在tensorboard中显示图像
#图像名,numpy型或者tensor型,step(前为numpy型才加上dataformats='HWC')
writer.add_image("image",img_numpy,1,dataformats='HWC') 
writer.close()

SummaryWriter类中的add_image()函数中传的参数是numpy类型或者tensor类型,不能是pil类型。

2. add_scalar()函数——显示函数

from torch.utils.tensorboard import SummaryWriter #载入SummaryWriter类
writer = SummaryWriter("logs") 
for i in range(100):
    #在tensorboard中显示函数
    writer.add_scalar("y=x",2*i,i)  #函数名,y,x
writer.close()

三、torchvision中transform.py的使用

transfrom对象中传的参数可以是 tensor类型,也可以是pil类型

头文件及初始定义:

from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
img_path = "dataset1/train/bees/16838648_415acd9e3f.jpg"
img_PIV = Image.open(img_path)#(PIV(pic)类型)
  1. totensor,作用是使得pic或者numpy型 转换为tensor型
#实例化对象(transforms.py中的ToTensor类)
totensor_ob = transforms.ToTensor() 
#隐含方式 调用内置函数(可按citrl+p查看参数类型)(参数为piv类型)
img_tensor = totensor_ob(img_PIV)
  1. Normalize 归一化
#每个通道的均值,每个通道的标准差(彩色图一般为3个通道数)
norm_ob = transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5]) 
#参数为tensor类型
img_norm = norm_ob.forward(img_tensor) 
#在tensor中显示
# writer = SummaryWriter("logs")
# writer.add_image("normalize",img_tensor,0)
# writer.close()
  1. resize用法1
print(img_PIV.size) #原先的 图片尺寸
resize1_ob = transforms.Resize((512,512)) #(高度,宽度)
img_resize1 = resize1_ob(img_PIV) #参数为pil类型(也可以为tensor)
print(img_resize1) #输出重置尺寸之后的 图片参数

结果:

(500, 450)# W H (宽度,高度)
<PIL.Image.Image image mode=RGB size=512x512 at 0x14F0DB1C7C0>
  1. resize用法2在Compose混合操作函数(之所以要用compose,是为了方便加入totensor函数,以便于在tensorboard中显示图像)
#在之前尺寸的基础上等比缩放
resize2_ob = transforms.Resize(256) #256匹配w和h较小的那个数,然后另外一个数根据比例缩放
#参数为transforms类型(对象)的列表
compose_ob1 = transforms.Compose([resize2_ob,totensor_ob])
img_resize2 = compose_ob1(img_PIV) #参数为pil类型(也可以为tensor)
print(img_resize2.size()) #输出等比缩放后的图片size,图片为tensor类型

结果:torch.Size([3, 256, 284]) #CHW

  1. randomcrop用在compose混合操作函数
random_ob = transforms.RandomCrop(256) #将原图随机裁剪为(256*256)的尺寸或者传参(m,n)
compose_ob2 = transforms.Compose([random_ob,totensor_ob]) #实例化对象
img_random = compose_ob2(img_PIV) #参数为pil类型(也可以为tensor)
print(img_random.size()) #输出随即裁剪之后的图片size,图片为tensor类型

结果:torch.Size([3, 256, 256])

三、torchvision中数据集的使用

import torchvision
from torch.utils.tensorboard import SummaryWriter

data_transform_ob = torchvision.transforms.Compose([torchvision.transforms.ToTensor()]) #等号左边为transforms类型的对象
# 数据集本身是piv类型,经过transform=data_transform_ob后,转换为tensor类型
train_set = torchvision.datasets.CIFAR10(root="./dataset2",train=True,transform=data_transform_ob,download=True) #训练集
test_set = torchvision.datasets.CIFAR10(root="./dataset2",train=False,transform=data_transform_ob,download=True) #测试集

#生成的事件文件存到pictures这个文件夹(和py文件在同一目录下)中
writer = SummaryWriter("pictures")
for i in range(10):
    img,label = test_set[i] #将test_set[i]分离,得到tensor类型的img
    writer.add_image('test_set',img,i) #一个图象共10步
writer.close()

img,label = test_set[i]:
比如test_set[5]表示cifar10测试集中按顺序数第六个图片,这个图片可能是10种类型label图片中的任意一种,图片类型不同,label也就不同。

test_set[5]为列表形式: [img的信息,label(为数字,一个数字表示一个类型)]

四、Dataloader打包压缩——为网络提供不同的数据形式

import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
#1. 载入数据集
test_data = torchvision.datasets.CIFAR10("./dataset2",train=False,transform=torchvision.transforms.ToTensor())
#2. 打包数据集
#batch_size=64表示一次性抓取几个数据,shuffle=True表示第二轮抓取是否打乱,drop_last=False表示到最后不足batch_size的图片是否丢掉
test_loader = DataLoader(dataset=test_data,batch_size=64,shuffle=True,num_workers=0,drop_last=False)
#3. tensorboard显示
writer = SummaryWriter("loaderdata")
#第一轮抓取,每一次都随机抓取batch_size张图片进行打包。
step = 0
for data in test_loader:
    imgs,targets = data
    writer.add_images("test1",imgs,step)
    step = step+1
#第二轮抓取
step = 0
for data in test_loader:
    imgs,targets = data
    writer.add_images("test2", imgs, step)
    step = step + 1

writer.close()

将数据进行打包之后,以batch_size=64为例,相当于数据集中的test_data[0[~test_data[63](或64—127)为一组进行显示,

五、神经网络

1. 神经网络基本骨架(简单使用)

import torch
from torch import nn
#神经网络的输入和输出,以及负责处理的类
class PXD (nn.Module): #表示继承括号中的类
    def __init__(self) -> None:
        super().__init__()
    def forward(self,input):
        output = input+1
        return output
pxd = PXD() #初始化对象
input = torch.tensor(1.0) #将1.0转换为tesor类型
output = pxd.forward(input)
print(output)

结果:tensor(2.)

2. 卷积层(convolution layers)

  1. torch.Size中的各个参数:
    torch.Size(batch_size,通道数,尺寸,尺寸)(NCHW)
  2. 只有三通道及以下的图片才可以被显示出来。
  3. 多通道转为少通道时,batch_size会增多
  4. 卷积之后,图像尺寸会变小(因为padding设置为0即不填充),若填充,则保持原尺寸
  5. 卷积层的stride默认为1
  6. 卷积核 通道数 = 卷积 输入层 的 通道数
    卷积核 个数 = 卷积 输出层 通道数(深度)

    在卷积层的计算中,假设输入是H x W x C, C是输入的深度(即通道数),那么卷积核(滤波器)的通道数需要和输入的通道数相同,所以也为C,假设卷积核的大小为K x K,一个卷积核就为K x K x C。
    计算时卷积核的对应通道应用于输入的对应通道,这样一个卷积核应用于输入就得到一个输出通道,假设有P个K x K x C的卷积核,这样每个卷积核应用于输入都会得到一个通道,所以会输出P个通道。
    深度学习代码篇(pytorch实现)_第1张图片
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset2",train=False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset,batch_size=64)

class PXD(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        #输入通道数,输出通道数,卷积核大小,步长,是否在周围进行数据填充
        self.cov1 = Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0) #根据条件可知,卷积核的通道数为3,个数为6
    def forward(self,input):
        output = self.cov1(input)
        return output

pxd = PXD() #实例化对象
writer = SummaryWriter("cov")
step = 0
for data in dataloader:
    imgs,targets = data
    output = pxd.forward(imgs)
    # print(imgs.shape) #torch.Size([64,3,32,32])
    # print(output.shape) #torch.Size([64,6,30,30])
    writer.add_images("input",imgs,step)
    output = torch.reshape(output,(-1,3,30,30)) # batch_size自己算
    writer.add_images("output",output,step)
    step = step+1
writer.close()

tensorboard中的显示:

深度学习代码篇(pytorch实现)_第2张图片
卷积:可以通过改变 卷积核的个数 改变输出图片的通道数,还可以使得输出图片的尺寸变小

3. 最大池化的使用(pooling layers)

  1. ceil_mode有两种取整方式:floor向下取整(不保留),ceiling向上取整(保留)
    ceil_mode默认为False,即为floor取整模式,ceil_mode为True时,为ceiling取整模式
    所谓保留和不保留指的是:池化核在input上移动时,如果覆盖不上kernel_size池化核大小的数字,那么就涉及到了保留还是不保留的问题。
  2. 池化层的stride默认不是1,而是kernel_size的值(池化核的大小)
  3. 池化后,图像尺寸减小(也可以通过设置padding使得尺寸保持不变),但是输出通道数不变,而卷积之后输出的通道数取决于卷积核的个数。

代码示例1:

import torch
from torch import nn
from torch.nn import MaxPool2d
input = torch.tensor([[1,2,0,3,1],
                      [0,1,2,3,1],
                      [1,2,1,0,0],
                      [5,2,3,1,1],
                      [2,1,0,1,1]],dtype=torch.float32)
input = torch.reshape(input,(-1,1,5,5))
class PXD(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.maxpool1 = MaxPool2d(kernel_size=3,ceil_mode=True) #stride默认为kernel_size,ceil_mode默认为False
    def forward(self,input):
        output = self.maxpool1(input)
        return output

pxd = PXD()
output = pxd.forward(input)
print(output)

结果:

tensor([[[[2.]]]]) # ceil_mode=False
tensor([[[[2., 3.],
          [5., 1.]]]]) #ceil_mode=True

代码示例2:

import torchvision
from torch import nn
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset = torchvision.datasets.CIFAR10("./dataset2",train=False,transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset,batch_size=64)

class PXD(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.maxpool1 = MaxPool2d(kernel_size=3,ceil_mode=True) #stride默认为kernel_size
    def forward(self,input):
        output = self.maxpool1(input)
        return output
pxd = PXD()
writer = SummaryWriter("maxpool")
step = 0
for data in dataloader:
    imgs,targets = data
    writer.add_images("input",imgs,step)
    output = pxd(imgs)
    writer.add_images("output",output,step) #不用调整通道数
    step = step+1

writer.close()

结果:
深度学习代码篇(pytorch实现)_第3张图片

  1. 最大池化后,不改变输出通道数,但图像尺寸变小。
    input_size:(64,3,32,32),3个pool_size:(-1,3,3,3),output_size:(64,3,11,11)

4. 非线性激活(Non-linear Activations)

relu

relu激活函数:小于0的返回0,大于0的返回input

import torch
from torch import nn
from torch.nn import ReLU #引入截断非线性激活函数
input = torch.tensor([[1,-0.5],
                      [-1,3]],dtype=torch.float32)
input = torch.reshape(input,(-1,1,2,2)) #NCHW,-1表示自己计算batch_size

class PXD(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.relu = ReLU(inplace=False) #默认为False,表示不就地处理,而是赋值给output
    def forward(self,input):
        output = self.relu(input)
        return output
pxd = PXD()
output = pxd.forward(input)
print(output)
print(output.shape)

结果:

tensor([[[[1., 0.],
          [0., 3.]]]])
torch.Size([1, 1, 2, 2])

sigmoid

sigmoid激活函数:output为0到1,为较平滑的范围。
可以使得被处理后的图片亮度降低,失去高光。
代码使用模式与relu相同

5. 线性层(Linear Layers)(全连接层)

import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
#线性层可以使得1
dataset = torchvision.datasets.CIFAR10("./dataset2",train=False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset,batch_size=64,drop_last=True)

class PXD(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.linear = nn.Linear(in_features=32,out_features=10) #size of each input sample,size of each output sample
    def forward(self,input):
        output = self.linear(input)
        return output
pxd = PXD()

for data in dataloader:
    imgs,targets = data
    print(imgs.shape)   #torch.Size([64, 3, 32, 32])
    output = pxd.forward(imgs)
    print(output.shape) #torch.Size([64, 3, 32, 10]) NCHW

线性层的目的是:
改变图像前后的宽度width,高度height不变
矩阵中来看:改变的是列数(维数),行数不变
in_features为原图像的宽度(列数)
线性层实质:
将展平的特征行向量,经过全连接层后,改变了宽度(列数)(维度)

6. 搭建网络

要通过搭建网络完成如下操作:
深度学习代码篇(pytorch实现)_第4张图片
计算padding要用到的公式:
深度学习代码篇(pytorch实现)_第5张图片
图片input在神经网络最后阶段,最后经过flatten()后,会被平展成为1行1024列的特征矩阵,高度H为1,宽度W为1024,在经过两个线性层,变为1行10列的特征矩阵。
注意:
输入input为图片,也具体化为x,图片的参数有像素,通道数等等。
神经网络中有权重参数,为w,与input的像素点相乘,会被不断更新。

import torch
from torch import nn
from torch.nn import Conv2d,MaxPool2d,Flatten,Linear

class PXD(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.conv1 = Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2)
        self.maxpool1 = MaxPool2d(kernel_size=2)
        self.cov2 = Conv2d(32,32,5,1,2)
        self.maxpool2 = MaxPool2d(2)
        self.conv3 = Conv2d(32,64,5,1,2)
        self.maxpool3 = MaxPool2d(2)
        self.flatten = Flatten() #平展
        self.linear1 = Linear(in_features=1024,out_features=64)
        self.linear2 = Linear(64,10)
    def forward(self,input):
        input = self.conv1(input)
        input = self.maxpool1(input)
        input = self.cov2(input)
        input = self.maxpool2(input)
        input = self.conv3(input)
        input = self.maxpool3(input)
        input = self.flatten(input)
        input = self.linear1(input)
        output = self.linear2(input)
        return output
pxd = PXD()
print(pxd)
#检验网络的正确性(不报错则证明正确):
input = torch.ones((64,3,32,32))
output = pxd.forward(input)
print(output.shape)

结果:

PXD(
  (model1): Sequential(
    (0): Conv2d(3, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (2): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (4): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (6): Flatten(start_dim=1, end_dim=-1)
    (7): Linear(in_features=1024, out_features=64, bias=True)
    (8): Linear(in_features=64, out_features=10, bias=True)
  )
)
torch.Size([64, 10]) 

7. 通过 torch.nn.Sequential 使得网络搭建代码更加简洁

import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential

class PXD(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.model1 = Sequential(Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2),
                                 MaxPool2d(kernel_size=2),
                                 Conv2d(32,32,5,1,2),
                                 MaxPool2d(2),
                                 Conv2d(32,64,5,1,2),
                                 MaxPool2d(2),
                                 Flatten(),
                                 Linear(in_features=1024,out_features=64),
                                 Linear(64,10))
    def forward(self,input):
        output = self.model1(input)
        return output
pxd = PXD()
print(pxd)
#检验网络的正确性(不报错则证明正确):
input = torch.ones((64,3,32,32))
output = pxd(input)
print(output.shape)

8. loss function

作用1 :计算实际输出和目标之间的差距
作用2 :为我们更新输出提供一定的依据(反向传播)

import torch
from torch.nn import L1Loss,MSELoss,CrossEntropyLoss

inputs = torch.tensor([1.,2.,3.]) #输入
targets = torch.tensor([1.,2.,5.]) #目标

inputs = torch.reshape(inputs,(-1,1,1,3))
targets = torch.reshape(targets,(-1,1,1,3))
#1. L1Loss
loss1 = L1Loss() #计算输入和目标差值的和的平均值
result1 = loss1(inputs,targets)
print(result1)
loss2 = L1Loss(reduction='sum') #计算输入和目标差值的和
result2 = loss2(inputs,targets)
print(result2)

#2. MSELoss
loss3 = MSELoss() #计算输入和目标方差的平均值
result3 = loss3(inputs,targets)
print(result3) 

#3. CrossEntropyLoss 交叉熵
x = torch.tensor([0.1,0.2,0.3])
y = torch.tensor([1])
x = torch.reshape(x,(1,3)) #1个batch_size,3类 (N,W)
loss_cross = CrossEntropyLoss()
result4 = loss_cross(x,y)
print(result4)

结果:

tensor(0.6667)
tensor(2.)
tensor(1.3333)
tensor(1.1019)

9. 优化器

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader

class PXD(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.model1 = Sequential(Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2),
                                 MaxPool2d(kernel_size=2),
                                 Conv2d(32,32,5,1,2),
                                 MaxPool2d(2),
                                 Conv2d(32,64,5,1,2),
                                 MaxPool2d(2),
                                 Flatten(),
                                 Linear(in_features=1024,out_features=64),
                                 Linear(64,10))
    def forward(self,input):
        output = self.model1(input)
        return output
pxd = PXD()
#这一步的目的是加载图片数据集,得到targets方便计算损失,得到imgs以便得到输出
dataset = torchvision.datasets.CIFAR10("./dataset2",train=False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset,batch_size=64)

loss = nn.CrossEntropyLoss() #初始化损失
optim = torch.optim.SGD(pxd.parameters(),lr=0.01) #选择优化器,参数:模型,学习率,不同优化器后面的参数不同
for epoch in range(20):#训练20轮
    running_loss = 0
    for data in dataloader:
        imgs,targets = data
        outputs = pxd.forward(imgs)
        result_loss = loss(outputs,targets) #计算损失
        optim.zero_grad() #梯度清零
        result_loss.backward() #反向传播,更新参数
        optim.step() #优化器进行优化
        running_loss = running_loss + result_loss
    print(running_loss) #计算每一轮损失函数的值

结果:

tensor(360.3538, grad_fn=<AddBackward0>)
tensor(354.5248, grad_fn=<AddBackward0>)
tensor(334.0772, grad_fn=<AddBackward0>)
tensor(319.1064, grad_fn=<AddBackward0>)
...........#可见,通过训练学习,损失越来越小,

10. 在pytorch提供的网络模型的基础上进行修改

import torchvision
from torch import nn
vgg16 = torchvision.models.vgg16()
print(vgg16)
# 结果:
# VGG(
#   (features): Sequential(
#     (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
#     (1): ReLU(inplace=True)
#     (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
#     (3): ReLU(inplace=True)
#     (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
#     (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
#     (6): ReLU(inplace=True)
#     (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
#     (8): ReLU(inplace=True)
#     (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
#     (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
#     (11): ReLU(inplace=True)
#     (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
#     (13): ReLU(inplace=True)
#     (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
#     (15): ReLU(inplace=True)
#     (16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
#     (17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
#     (18): ReLU(inplace=True)
#     (19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
#     (20): ReLU(inplace=True)
#     (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
#     (22): ReLU(inplace=True)
#     (23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
#     (24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
#     (25): ReLU(inplace=True)
#     (26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
#     (27): ReLU(inplace=True)
#     (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
#     (29): ReLU(inplace=True)
#     (30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
#   )
#   (avgpool): AdaptiveAvgPool2d(output_size=(7, 7))
#   (classifier): Sequential(
#     (0): Linear(in_features=25088, out_features=4096, bias=True)
#     (1): ReLU(inplace=True)
#     (2): Dropout(p=0.5, inplace=False)
#     (3): Linear(in_features=4096, out_features=4096, bias=True)
#     (4): ReLU(inplace=True)
#     (5): Dropout(p=0.5, inplace=False)
#     (6): Linear(in_features=4096, out_features=1000, bias=True)
#   )
# )
  1. vgg16.classifier.add_module('add_linear',nn.Linear(1000,10)) #通过添加线性层,使得分类减少(维数减少)
  2. vgg16.classifier.add_module('add_linear',nn.Linear(1000,10)) #通过添加线性层,使得分类减少(维数减少)

10. 网络模型的保存和读取

保存方式1

保存:

vgg16 = torchvision.models.vgg16()#如果是自己写的网络模型,此步不写
torch.save(vgg16,"vgg16_method1.pth") #网络模型,名字

加载:

#如果是自己写的模型,需要在这一行写上神经网络或者头文件中from model import*
#但是不用再实例化了
model = torch.load("vgg16_method1.pth") #相对地址  model为实例化的网络模型 对象
print(model)

保存方式2

保存:

vgg16 = torchvision.models.vgg16()
torch.save(vgg16.state_dict(),"vgg16_method2.pth") #网络模型,名字

加载:

vgg16 = torchvision.models.vgg16()
vgg16.load_state_dict(torch.load("vgg16_method2.pth")) #相对地址
print(vgg16)

六、完整模型的训练套路

print("-------第 {}轮训练开始-------".format(i+1)) 字符串函数,用 i+1 替换 {}
accuracy = (outputs.argmax(1) == targets).sum() 用于计算每一次训练的正确率,1表示按行看寻最大值

训练的基本思路:
我们要训练的对象是神经网络,我们先初步构建神经网络,如卷积,池化等,然后第一轮让训练集通过创建好的神经网络的这些层,使得训练集的图片变为特征图(类似于高亮变灰白)(使得容易训练神经网络),然后让特征图(outputs)targets相比来通过损失函数获得损失,然后再经过优化器优化、损失反向传播 使得神经网络中的层的参数逐渐被优化更新,然后经过n轮训练,直到更加接近target。

如何知道神经网络被训练的效果怎么样呢?(是否适应模型)
通过测试集。 让测试集经过神经网络,看看每一轮的损失或者正确率(分类问题常用)如何,进而判断训练效果。

为什么要在每一轮过后要保存网络模型呢?
因为神经网络在每一轮训练过后参数都会被优化更新,网络模型实质上发生了变化。

import torch
import torchvision
from torch.utils.tensorboard import SummaryWriter
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
#device = torch.device("cuda")   也表示在gpu中训练

#dataset 准备数据集
train_dataset = torchvision.datasets.CIFAR10(root="./dataset2",train=True,transform=torchvision.transforms.ToTensor(),download=True)
test_dataset = torchvision.datasets.CIFAR10(root="./dataset2",train=False,transform=torchvision.transforms.ToTensor(),download=True)

#length 长度
train_dataset_size = len(train_dataset)
test_dataset_size = len(test_dataset)
print("训练数据集的长度为{}".format(train_dataset_size))
print("测试数据集的长度为{}".format(test_dataset_size))

#dataloader 加载数据集
train_dataloader = DataLoader(train_dataset,batch_size=64)
test_dataloader = DataLoader(test_dataset,batch_size=64)

#实例化网络模型
class PXD(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.model1 = Sequential(Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2),
                                 MaxPool2d(kernel_size=2),
                                 Conv2d(32,32,5,1,2),
                                 MaxPool2d(2),
                                 Conv2d(32,64,5,1,2),
                                 MaxPool2d(2),
                                 Flatten(),
                                 Linear(in_features=1024,out_features=64),
                                 Linear(64,10))
    def forward(self,input):
        output = self.model1(input)
        return output
pxd = PXD()
pxd = pxd.cuda() #GPU训练 #pxd = pxd.to(device)

#损失函数
loss_fn = nn.CrossEntropyLoss() #交叉熵
loss_fn = loss_fn.cuda() #GPU训练 #loss_fn = loss_fn.to(device)

#添加tensorboard便于观察损失函数变化
writer = SummaryWriter("loss")

#优化器
learning_rate = 1e-2 #0.01
optimizer = torch.optim.SGD(pxd.parameters(),lr=learning_rate) #选用sgd优化器

#设置神经网络的一些参数
total_train_step = 0 #训练的次数
total_test_step = 0 #测试的次数
epoch = 10 #训练的轮数

for i in range(epoch):
    print("-------第 {}轮训练开始-------".format(i+1))
    #训练步骤开始
    pxd.train()
    for data in train_dataloader:
        imgs,targets = data
        imgs = imgs.cuda() #GPU           #imgs = imgs.to(device)
        targets = targets.cuda() #GPU     #targets = targets.to(device)
        outputs = pxd.forward(imgs)  #让输入通过层层特征提取网络(前向传播)
        #特征提取:输入的像素点矩阵x * 权重参数矩阵w的过程,像素矩阵x的行列(形状)会发生变化,权重矩阵w的元素值将来会被不断更新。
        loss = loss_fn(outputs,targets) #计算在dataloader中一次训练的损失(每一轮输出的得分和真实值作比较的过程)

        #优化器优化
        optimizer.zero_grad() #梯度清零
        loss.backward() #反向传播,求解损失函数梯度
        optimizer.step() #更新权重参数

        total_train_step = total_train_step + 1
        if(total_train_step % 100 == 0):
            print("训练次数:{},此次训练的Loss:{}".format(total_train_step,loss.item()))
            writer.add_scalar("train_loss",loss.item(),total_train_step)

    #验证步骤开始(验证训练结果怎么样)
    pxd.eval()
    total_test_loss = 0
    total_test_accuracy = 0
    	#无梯度的目的是:正处在验证阶段,所以不用对梯度进行调整,无需优化神经网络参数,也可以节省内存
    with torch.no_grad():
        for data in test_dataloader:
            imgs,targets = data
        	imgs = imgs.cuda() #GPU          #imgs = imgs.to(device)
        	targets = targets.cuda() #GPU    #targets = targets.to(device)
            outputs = pxd.forward(imgs)
            #特征提取网络经过该轮训练,神经网络参数w被更新,将输入图片放入该网络后的到的得分值被记录下来
            loss = loss_fn(outputs,targets) #计算损失
            total_test_loss = total_test_loss + loss
            accuracy = (outputs.argmax(1) == targets).sum() #正确率的分子
            total_test_accuracy = total_test_accuracy + accuracy

    print("整体测试集上的loss:{}".format(total_test_loss))
    print("整体测试集上的正确率:{}".format(total_test_accuracy/test_dataset_size))
    writer.add_scalar("test_loss",total_test_loss,total_test_step)
    writer.add_scalar("test_accuracy", total_test_accuracy/test_dataset_size, total_test_step)
    total_test_step = total_test_step + 1

    torch.save(pxd,"tudui_{}.pth".format(i))
    print("模型已保存")

writer.close()

前三轮次的结果:

Files already downloaded and verified
Files already downloaded and verified
训练数据集的长度为50000
测试数据集的长度为10000
-------1轮训练开始-------
训练次数:100,此次训练的Loss:2.287644147872925
训练次数:200,此次训练的Loss:2.281564712524414
训练次数:300,此次训练的Loss:2.258236885070801
训练次数:400,此次训练的Loss:2.173457145690918
训练次数:500,此次训练的Loss:2.0533664226531982
训练次数:600,此次训练的Loss:2.0135819911956787
训练次数:700,此次训练的Loss:1.9880279302597046
整体测试集上的loss:314.1552734375
整体测试集上的正确率:0.28039997816085815
模型已保存
-------2轮训练开始-------
训练次数:800,此次训练的Loss:1.8499757051467896
训练次数:900,此次训练的Loss:1.823794960975647
训练次数:1000,此次训练的Loss:1.880897879600525
训练次数:1100,此次训练的Loss:1.9815046787261963
训练次数:1200,此次训练的Loss:1.7078672647476196
训练次数:1300,此次训练的Loss:1.6430387496948242
训练次数:1400,此次训练的Loss:1.7231780290603638
训练次数:1500,此次训练的Loss:1.7968997955322266
整体测试集上的loss:301.3468017578125
整体测试集上的正确率:0.3149999976158142
模型已保存
-------3轮训练开始-------
训练次数:1600,此次训练的Loss:1.7480535507202148
训练次数:1700,此次训练的Loss:1.638698697090149
训练次数:1800,此次训练的Loss:1.9216716289520264
训练次数:1900,此次训练的Loss:1.7019065618515015
训练次数:2000,此次训练的Loss:1.8428000211715698
训练次数:2100,此次训练的Loss:1.5035483837127686
训练次数:2200,此次训练的Loss:1.5163278579711914
训练次数:2300,此次训练的Loss:1.7925814390182495
整体测试集上的loss:267.4210205078125
整体测试集上的正确率:0.3807999789714813
模型已保存

通过每一轮训练后的结果,我们可以观察到测试集上的整体loss在逐轮次下降、正确率逐轮次提高,表明神经网络经过每一轮的训练,神经网络的参数在不断被优化,损失越来越小,神经网络越来越适应模型。

GPU加速训练

只能在以下环节中利用GPU加速:

  1. 网络模型
  2. 数据(imgs,targets)(输入,标注(eg:‘dogs’))
  3. 损失函数

七、完整模型的验证套路

类似于训练套路中的测试集那里

import torch
import torchvision
from PIL import  Image
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear

img_path = "./pictures/dog.png"
image = Image.open(img_path) #以pil类型打开
print(image)
transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor(),torchvision.transforms.Resize((32,32))])
image = transform(image)
image = image.cuda() #因为网络模型是用gpu训练的,所以测试的输入图片应该.cuda()
print(image.shape)

#使用第一种方式加载网络
class PXD(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.model1 = Sequential(Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2),
                                 MaxPool2d(kernel_size=2),
                                 Conv2d(32,32,5,1,2),
                                 MaxPool2d(2),
                                 Conv2d(32,64,5,1,2),
                                 MaxPool2d(2),
                                 Flatten(),
                                 Linear(in_features=1024,out_features=64),
                                 Linear(64,10))
    def forward(self,input):
        output = self.model1(input)
        return output
model = torch.load("tudui_9.pth") #传参为神经网络的相对地址,实例化

image = torch.reshape(image,(1,3,32,32)) #此步使得batch_size变为1,很重要,容易忽略!!!
model.eval()
with torch.no_grad():
    output = model.forward(image)
print(output)
print(output.argmax(1)) #输出tensor最大的标签

你可能感兴趣的:(深度学习,pytorch,python)