Pytorch使用

视频链接:PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】_哔哩哔哩_bilibili

Pytorch的配置

有时候会使用不同版本的python,这时进入

image-20220703143951998

创建你需要的环境,并指定python的安装版本

conda create -n pytorch python=3.6   	# 一个名为pytorch环境的,版本为3.6

激活你需要的python版本

conda activate pytorch					# 激活名为pytorch这个环境

下载pytorch

链接:Start Locally | PyTorch

image-20220703144407204

复制Run this Command里面的代码,回到最开始进入的命令行里,粘贴,然后开始下载

判断当前电脑的显卡能否用

import torch
torch.cuda.is_available()			
# true表示没问题,false的话,可以参考链接
# https://blog.csdn.net/qq_41997920/article/details/105090212

PyCharm和Jupyter的配置

为pycharm指定conda的环境

image-20220703153835368

环境导入成功

image-20220703154248397

创建好的工程

image-20220805102909592

打开Anaconda的命令行,切换到pytorch环境,pytorch环境里面是没有jupyter book的,需要自己安装

conda install nb_conda  # 安装jupyter
jupyter notebook		# 打开jupyter notebook

创建新的代码文件

image-20220703155132851

测试是否成功,True为成功

image-20220703155241017

python中的两大法宝函数

dir()

作用:打开,看见

dir(torch)				# 查看torch这个工具箱里面的所有工具
dir(torch.cuda)			# 查看torch工具箱里的cuda工具

help()

help(torch.cuda)		# 查看torch的cuda用法

Pytorch加载数据初认识

如何读取数据?

答:主要涉及到两个类,Dataset和Dataloader

Dataset

dataset能够从一些垃圾数据中,提取到我们需要的数据,并给这些数据编号。作用就是提供一种方式去获取数据及其label。

所需要实现的功能

  • 如何获取每一个数据及其label
  • 告诉我们总共有多少的数据

Dataset实战

from torch.utils.data import Dataset						# 导入工具
from PIL import Image           							# 导入处理图片
import os


# 定义一个数据类
class MyData(Dataset):
    # root_dir:数据类的目录
    # label_dir:标签
    def __init__(self,root_dir,label_dir):
        self.root_dir = root_dir
        self.label_dir = label_dir
        self.path = os.path.join(root_dir,label_dir)        # 获取图片的路径地址
        self.img_path = os.listdir(self.path)               # 获取该目录下所有图片的地址

    # 读取指定的图片
    def __getattr__(self, idx):
        img_name = self.img_path[idx]                       # 获取指定的图片名称
        img_item_path = os.path.join(self.root_dir,self.label_dir,img_name)     # 获取图片的绝对路径
        img = Image.open(img_item_path)                     # 打开图片
        label = self.label_dir
        return img,label

    # 数据集的长度
    def __len__(self):
        return len(self.img_path)


root_dir = "data_set/train"

ants_label_dir = "ants"

bees_label_dir = "bees"
# 指定蚂蚁的数据集
ants_dataset = MyData(root_dir,ants_label_dir)
# 指定蜜蜂的数据集
bees_dataset = MyData(root_dir,bees_label_dir)

# 两个数据集的结合
train_dataset = ants_dataset + bees_dataset

Dataloader

为后面的网络提供不同数据形式

TensorBoard的使用

参考链接:

对大部分人而言,深度神经网络就像一个黑盒子,其内部的组织、结构、以及其训练过程很难理清楚,这给深度神经网络原理的理解和工程化带来了很大的挑战。为了解决这个问题,tensorboard应运而生。Tensorboard是tensorflow内置的一个可视化工具,它通过将tensorflow程序输出的日志文件的信息可视化使得tensorflow程序的理解、调试和优化更加简单高效。Tensorboard的可视化依赖于tensorflow程序运行输出的日志文件,因而tensorboard和tensorflow程序在不同的进程中运行

安装tensorboard

pip install tensorboard

add_scalar作用

函数是用来保存程序中的数据,然后利用tensorboard工具来进行可视化的

  • 第一个参数:生成图像的名称
  • 第二个参数:X轴的值
  • 第三个参数:Y轴的值

打开logs下的文件

需要在命令行打开

tensorboard --logdir=logs   				# logdir=事件文件所在文件夹名
tensorboard --logdir=logs --port=6007		# 更换端口
# 记得切换到安装tensorboard的环境,本地的切换命名 1.进入项目目录,2.conda activate 环境名(我的是pytorch)

然后打开网址

image-20220703180054188

增加一个y=2x函数,为什么y=x注释了,还可以显示?

答:因为y=x的值已经写入到logs下面了,程序每执行一次,就会在logs目录下面创建一个文件,下图就是执行三次以后的情况。

image-20220703180734483

from torch.utils.tensorboard import SummaryWriter
# 这里的SummaryWriter的作用就是,将数据以特定的格式存储到刚刚提到的那个文件夹中。

# 首先我们将其实例化
writer = SummaryWriter("logs")

# writer.add_image()
# y = x
for i in range(100):
   writer.add_scalar("y=x",i,i)

# for i in range(100):
#     writer.add_scalar("y=2x",2*i,i)


writer.close()

add_image()的使用

add_image(self, tag, img_tensor, global_step=None, walltime=None, dataformats=‘CHW’):绘制图片,可用于检查模型的输入,监测 feature map 的变化,或是观察 weight。
tag:就是保存图的名称
img_tensor:图片的类型要是torch.Tensor, numpy.array, or string这三种
global_step:记录的步骤
dataformats=‘CHW’,默认CHW,tensor是CHW,numpy是HWC

如果只写入一张图片信息,可以使用writer.add_image。

如果要写入多张图片信息,可以使用writer.add_images。

from torch.utils.tensorboard import SummaryWriter	#   tensorboard可视化库
import numpy as np				# numpy库,用于展示图像的数据信息的一个库
from PIL import Image           # 处理图片可以用cv2,PIL,但是后面的时候记得转换格式
# 导入图片的方式
# https://blog.csdn.net/qq_43075383/article/details/114376363

writer = SummaryWriter("logs")

image_path = "E:\\Study\\learn_torch\\data_set\\train\\ants\\0013035.jpg"
img_PIL = Image.open(image_path)	# 使用Image.open()函数打开图片,并将其图片数据类型通过np.array()转换为numpy类型

print(type(img_PIL))                # img_PIL是PIL类型,而add_image需要numpy.array类型,所以需要转换

img_array = np.array(img_PIL)
print(type(img_array))              # numpy类型
print(img_array.shape)              # (512, 768, 3) (h,w,c) (高度,宽度,通道)

writer.add_image("test",img_array,1,dataformats='HWC')      # 因为图片的是(高度,宽度,通道),所以这里dataformats要等于HWC,C是chaneel的意思
# 此处的“1”代表的是步骤,在前文中的add_image()函数中设置,我们可以设置1.2.3…等将其全部放置在同一个tag下,通过滑动窗口上的滑轮即可查看不同的图片。

for i in range(100):
    writer.add_scalar("y=x",i,i)

writer.close()

点击image显示图片,

step为1

image-20220703190725022

step为2

image-20220703191712773

如果想再展示一个图片的话,只需要更改add_image里面的名称

writer.add_image("train",img_array,1,dataformats='HWC')  

image-20220703191959809

transforms

对图片进行变换

image-20220704095353396

transforms的使用

from PIL import Image                       # 处理图片的包
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms          # 导入包
import cv2

# python的用法---》tensor数据类型
# 通过transform.ToTensor去看两个问题

# 2、为什么需要Tensor数据类型
# 答:神经网络中,进行训练时,需要需要tensor类型


# 绝对路径  E:\Study\learn_torch\data_set\train\ants\0013035.jpg
# python中绝对路径需要写双斜杠

# 相对路径  data_set/train/ants/0013035.jpg

img_path = "data_set/train/ants/0013035.jpg"

img_path_abs = "E:\\Study\\learn_torch\\data_set\\train\\ants\\0013035.jpg"

img = Image.open(img_path)


writer = SummaryWriter("logs")


# 1、transform该如何使用
tensor_trans = transforms.ToTensor()        # 定义一个toTensor的对象
tensor_img = tensor_trans(img)              # 为什么传img? ToTensor的对象__call__()的作用是使实例能够像函数一样被调用,同时不影响实例本身的生命周期

print(type(tensor_img))

writer.add_image("Tensor",tensor_img)

writer.close()

常见的transforms

  • tensor
  • normalize
  • resize
  • compose

Normalize的计算方法

image-20220704104558452

参考链接:(20条消息) Python numpy 归一化和标准化 代码实现_Kenn7的博客-CSDN博客_numpy 归一化

Compose的用法

一般用Compose把多个步骤整合到一起

image-20220704112658282

from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
import numpy as np

writer = SummaryWriter("logs")

img = Image.open("data_set/train/ants/0013035.jpg")

# 通过PIL导入的图片,不能直接用到add_image里面,需要进行numpy,或者tensor转换,还需要设置hwc
img_array = np.array(img)
writer.add_image("test",img_array,dataformats="HWC")

print(img)

# ToTensor
trans_tensor = transforms.ToTensor()
img_tensor = trans_tensor(img)


writer.add_image("tensor",img_tensor)

# Normalize
print(img_tensor[0][0][0])      # 第一行,第一列,第一个像素点

trans_norm = transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])
img_norm = trans_norm(img_tensor)

print(img_norm[0][0][0])        # 第一行,第一列,第一个像素点

writer.add_image("Normalize",img_norm)

# Resize
print(img.size)

trans_resize = transforms.Resize((512,512))
# img PIL -> resize -> img.resize PIL
img_resize = trans_resize(img)

# img_resize PIL -> totensor -> img_resize
img_resize = trans_tensor(img_resize)

writer.add_image("Resize",img_resize,0)
print(img_resize)

# Compose - resize - 2
# 进行等比缩放,不改变高和宽的比例
trans_resize_2 = transforms.Resize(512)
trans_compose = transforms.Compose([trans_resize_2,trans_tensor])
img_resize_2 = trans_compose(img)
writer.add_image("Resize",img_resize_2,1)

writer.close()

运行结果

image-20220704104317054

torchvision中的数据集使用

import torchvision
# compose 第一个参数的输出,是第二个参数的输入
from torch.utils.tensorboard import SummaryWriter

dataset_transform = torchvision.transforms.Compose([
    torchvision.transforms.ToTensor()
])


# 使用cifar数据集
train_set = torchvision.datasets.CIFAR10(root="./dataset",train=True,transform=dataset_transform,download=True)
test_set = torchvision.datasets.CIFAR10(root="./dataset",train=False,transform=dataset_transform,download=True)

writer = SummaryWriter("p10")
for i in range(10):
    img,target = test_set[i]
    print(type(img))    	# tensor类型
    writer.add_image("test_set",img,i)

writer.close()

dataloader

参考链接:两文读懂PyTorch中Dataset与DataLoader(二)理解DataLoader源码 - 知乎 (zhihu.com)

dataloader里面batch_size设置为4,那么就会到dataset取个四个数据,然后将img0到img4进行打包,将target0到target4也进行打包,然后返回。

image-20220704150227708

import torchvision

# 准备的测试数据集
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor())

# num_workers:进程数,shuffle:打乱
# test_loader = DataLoader(dataset=test_data,batch_size=4,shuffle=True,num_workers=0,drop_last=False)

# 64张图片叠在一起,drop_last为true时,剩下不满足64张的图片将会舍去
test_loader = DataLoader(dataset=test_data,batch_size=64,shuffle=False,num_workers=0,drop_last=False)

# 测试数据集中第一张图片及target,target就是label
img,target = test_data[0]
print(img.shape)        # torch.Size([3, 32, 32])       3通道,32×32像素
print(target)           # 3                             target 就是label

writer = SummaryWriter("dataloader")
for epoch in range(2):
    step = 0
    for data in test_loader:
        imgs,targets = data
        #  print(imgs.shape)           # torch.Size([4, 3, 32, 32])  这个4是四张图片的意思(batch)
        #  print(targets)              # 4张图片的target,融合在一起
        writer.add_images("Epoch{}".format(epoch),imgs,step)        # 注意这里是add_images,加了个s
        step = step+1

writer.close()

设置drop_last为True时,不足64(自己设的)将会丢弃

image-20220704153511263

shuffle设置False时,两次训练的图片顺序一样

image-20220704154107088

神经网络的基本骨架-nn.Module的使用

打开pytorch官网,进入以下界面

image-20220704154959112

  • container

    骨架,往骨架中添加不同的内容,就可以组成不同的神经网络

  • Convolution Layers

    卷积层

  • Pooling Layers

    池化层

  • Non-linear Activations

    非线性激活

    image-20220704160430881

进入Container里面,Module是神经网络的基础,很重要

image-20220704161025498

我们大部分定义的神经网络模板就如下图的代码一样,写一个class,定义一个自己神经网络的名字,然后继承nn.Module,主要写两个函数,一个是__init__函数进行初始化,然后要调用父类的初始化函数。

image-20220704161537510

这个forward函数可以理解为有一个输入叫input,输入到神经网络中,神经网络会一个输出,神经网络经过的一个运算步骤就是forward函数,这叫前向传播

image-20220704162056280

forward函数的执行流程如下,首先输入x,卷积之后进行非线性,然后卷积,接着非线性,然后输出

image-20220704162415120

调试过程

image-20220704164010881

import torch
from torch import nn


# 定义一个名为Bzw的神经网络模板
class Bzw(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self,input):
        output = input + 1
        return output

bzw = Bzw()
# 输入x
x = torch.tensor(1.0)
print(type(x))      # tensor 类型
output=bzw(x)
print(output)       # tensor(2.)

卷积

卷积操作

将输入图像与卷积核对应位置的值进行相乘,然后相加

image-20220704165014207

然后开始移动,可以向右移一步,两步,这取决于stride

image-20220704165304848

image-20220704165407851

右移不了,开始往下走

image-20220704165552474

继续移动

image-20220704165612441

image-20220704165621995

剩下步骤省略,下图是最后一步结果,最右边的那个3×3的矩阵就是卷积后的输出

image-20220704165723641

stride=2时,右移2步

image-20220704165942878

下移2步

image-20220704170014322

流程如下

image-20220704170324300

函数的用法看下图

image-20220704171220734

conv2d的参数

现在准备使用卷积函数,这里是二维,使用conv2d函数,查看conv2d的用法,input需要有batch,通道,高,宽

image-20220704171330173

这个shape只有2个参数,而上图中给的shape的参数是四个

image-20220704171515859

in_channel和reshape

所以,我们需要进行尺寸变换,调节它的shape,这里我们需要指定in_channel数量,in_channel由输入矩阵的通道数决定,那么输入矩阵的通道数怎么算了?

参考以下链接:

1.(20条消息) 【深度学习笔记】卷积的输入输出的通道、维度或尺寸变化过程_月满星沉的博客-CSDN博客_卷积神经网络的通道数和维度数

2.(20条消息) OpenCV 学习笔记之矩阵的维数和通道数之间的关系_火锅丸子23333的博客-CSDN博客_维度和通道数

我们得知5×5的矩阵,每一个坐标上只有一个值,那么通道数就是1

那么minibatch是多少?目前不知道,up主说的是1

reshape之后的input和kernel满足要求

image-20220704180036204

进行卷积,因为是二维数组,所以这里需要调用conv2d函数,记得引用torch.nn.functional,结果如下,up主12哪里算错了,应该是13

image-20220704180605640

stride=2时的结果

image-20220704181024653

padding用法

padding作用就是扩大一圈输入矩阵,padding为1时,扩大的格子为1,padding为2时,扩大的格子为2,默认填充值为0

image-20220704181720884

image-20220704185238962

import torch
import torch.nn.functional as F
# 输入图像
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]])

# 卷积核
kernel = torch.tensor([[1,2,1],
                       [0,1,0],
                       [2,1,0]])


print(input.shape)
print(kernel.shape)

input = torch.reshape(input,(1,1,5,5))
kernel = torch.reshape(kernel,(1,1,3,3))

print(input.shape)
print(kernel.shape)

# 进行卷积操作,记得引入torch.nn.functional
output = F.conv2d(input,kernel,stride=1)     # stride 是步长

output2 = F.conv2d(input,kernel,stride=1,padding=1)
print(output)
print(output2)

神经网络

卷积层

进入如下界面

image-20220704190058514

out_channel含义

当out_channel为1时,系统会拿一个卷积核与输入图像进行计算

当out_channel为2时,系统会拿两个卷积核(不一定相等)与输入图像进行计算,然后会有两个输出结果,然后将这两个输出结果合成一个输出

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("../data", train=False, transform=torchvision.transforms.ToTensor(),download=True)

dataloader = DataLoader(dataset,batch_size=64)


class BzwNN(nn.Module):
    def __init__(self):
        super(BzwNN, self).__init__()
        # 彩色图像,所以channel数是3层,6为输出通道数
        self.conv1 = Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0)

    # 实例化会调用此函数
    def forward(self,x):
        x = self.conv1(x)   # 将x放入到卷积层中
        return x

bzwnn = BzwNN()
print(bzwnn)

writer = SummaryWriter("./logs")
step=0
for data in dataloader:
    imgs,targets = data
    output = bzwnn(imgs)    # output就是将dataloader里面的图片放到神经网络里面,然后执行forward函数,进行卷积操作
    # print(imgs.shape)       # 未经过卷积操作   torch.Size([64, 3, 32, 32]) channel为3,卷积之后变为6
    # print(output.shape)     # torch.Size([64, 6, 30, 30]) batchsize为64,卷积之后,图像变小变为30×30

    writer.add_images("input",imgs,step)            # 写入多张图片,使用add_images()
    # torch.Size([64, 6, 30, 30]) -> [xxx,3,30,30]

    # [xxx,3,30,30] 第一个数,不知道是多少的时候,直接写-1,这里有多个channel,如果想看图片,需要指定channel,所以这里设定为3
    output = torch.reshape(output,(-1,3,30,30))
    writer.add_images("output",output,step)         # 写入多张图片,使用add_images()
    print(imgs.shape)
    print(output.shape)
    step = step+1

writer.close()

运行结果

image-20220704210831619

最大池化的使用

image-20220704211202884

dilation

空洞卷积

image-20220704211827915

原理

image-20220704213313525

首先初始化一个池化核,然后拿这个池化核与输入图像进行计算,取这一个区域的里的最大值,移动的大小默认为kernel_size,ceil_mode决定池化核没覆盖满时,是否保留最大值

image-20220704213151060

ceil_mode为True时,结果为

image-20220704213232398

ceil_mode为False时,结果为

image-20220704213251286

输入要求是4个参数,而创建的矩阵的shape只有两个参数,需要reshape

image-20220704213604262

import torch
from torch import nn
from torch.nn import MaxPool2d

# RuntimeError: "max_pool2d" not implemented for 'Long' 报这种错误时,需要指定输入类型
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)

# -1 让系统计算batch_size
input = torch.reshape(input,(-1,1,5,5)) # 这个参数分别为:batch_size,channel ,height,width
print(input.shape)                      # torch.Size([1, 1, 5, 5]) 满足对输入的要求

class BzwNN(nn.Module):
    def __init__(self):
        super(BzwNN, self).__init__()
        self.maxpool1 = MaxPool2d(kernel_size=3,ceil_mode=True)

    def forward(self,input):
        output = self.maxpool1(input)
        return output

bzwnn = BzwNN()
output = bzwnn(input)
print(output)       

ceil_mode为True时

image-20220704214846542

ceil_mode为False时

image-20220704214914962

最大池化的作用

一个简单的例子,平时我们看视频是1080p,这里1080p作为输入,720p作为输出,虽然也能看,但是大小却减少了。

import torch
import torchvision.datasets
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("./data",train=False,download=True,transform=torchvision.transforms.ToTensor())

# 不知道输入什么参数时,按住ctrl+p
dataloader = DataLoader(dataset,batch_size=64)

class BzwNN(nn.Module):
    def __init__(self):
        super(BzwNN, self).__init__()
        self.maxpool1 = MaxPool2d(kernel_size=3,ceil_mode=False)

    def forward(self,input):
        output = self.maxpool1(input)
        return output

bzwnn = BzwNN()

writer = SummaryWriter("logs_maxpool")
step = 0
for data in dataloader:
    imgs,targets = data
    writer.add_images("input",imgs,step)

    # 最大池化不会有多的channel,所以这里不需要reshape,原先是3纬的,池化后还是3纬的
    output = bzwnn(imgs)
    writer.add_images("output",output,step)

    step = step+1
writer.close()

可以看出最大池化的图片,就像压缩了一样

image-20220704220635582

通过最大池化,神经网络的数据量大大的减少,提高了训练速度

非线性激活

进入下面界面

image-20220705103604264

ReLU

ReLU的参数inplace表示是否替换原来的数据,一般建议inplace为false,这样防止原始数据丢失

image-20220705104403406

import torch
from torch import nn
from torch.nn import ReLU

input = torch.tensor([[1,-0.5],
                     [-1,3]])
# 这里需要指定batch_size,-1让系统自己算
input = torch.reshape(input,(-1,1,2,2))
print(input.shape)  # torch.Size([1, 1, 2, 2])

# 搭建网络
class BzwNN(nn.Module):
    def __init__(self):
        super(BzwNN, self).__init__()
        self.relu1 = ReLU()

    def forward(self,input):
        output = self.relu1(input)
        return output

# 创建网络
bzwnn = BzwNN()
output = bzwnn(input)
print(output)

运行结果

image-20220705104833026

sigmoid

import torch
import torchvision.datasets
from torch import nn
from torch.nn import ReLU, Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

input = torch.tensor([[1,-0.5],
                     [-1,3]])
# 这里需要指定batch_size,-1让系统自己算
input = torch.reshape(input,(-1,1,2,2))
print(input.shape)  # torch.Size([1, 1, 2, 2])

dataset = torchvision.datasets.CIFAR10("./data",train=False,download=True,transform=torchvision.transforms.ToTensor())

dataloader = DataLoader(dataset,batch_size=64)

# 搭建网络
class BzwNN(nn.Module):
    def __init__(self):
        super(BzwNN, self).__init__()
        self.relu1 = ReLU()
        self.sigmoid1 = Sigmoid()

    def forward(self,input):
        output = self.sigmoid1(input)

        return output

# 创建网络
bzwnn = BzwNN()

writer = SummaryWriter("logs_relu")
step=0
for data in dataloader:
    imgs,targets = data
    writer.add_images("input",imgs,global_step=step)
    output = bzwnn(imgs)
    writer.add_images("sigmoid",output,global_step=step)
    step += 1

writer.close()

结果

image-20220705105949258

线性层机器其他层介绍

Linears

进入下面界面

image-20220705110333031

将1×1×4096通过这个层变成1×1×1000,其实是将input_feature设为4096,output_feature设置为1000

image-20220705111426840

怎样将一个5×5的矩阵,转成1×25,通过线性层变成1×3,如下图

image-20220705111901003

见下面代码

import torch
import torchvision		# 和图片相关
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("./data",train=False,transform=torchvision.transforms.ToTensor(),download=True)

dataloader = DataLoader(dataset,batch_size=64,drop_last=True)

class BzwNN(nn.Module):
    def __init__(self):
        super(BzwNN, self).__init__()
        # in_feature 是196608,out_feature是10
        self.linear1 = Linear(196608,10)

    def forward(self,input):
        output = self.linear1(input)
        return output

bzwNN = BzwNN()


# 将图片从[64, 3, 32, 32] reshape成 [1,1,1,196608] 然后拉成 [1,1,1,10]
for data in dataloader:
    imgs,targets = data
    print(imgs.shape)   # torch.Size([64, 3, 32, 32])

    # 这里的1,1,1,-1,就是把原来的张量拉成长条,-1是让系统帮你算 (batchsize,channel,height,width),这里让系统自动算宽度
    output = torch.reshape(imgs,(1,1,1,-1))
    output2 = torch.flatten(imgs)  # 降维,flatten和上面的reshape代码作用一样,只不过输出有区别


    print(output.shape) # reshape的结果  torch.Size([1, 1, 1, 196608])
    print(output2.shape) # flatten的结果   torch.Size([196608])


    output = bzwNN(output)
    output2 = bzwNN(output2)

    print(output.shape) # reshape的结果 torch.Size([1, 1, 1, 10])
    print(output2.shape) # flatten的结果 torch.Size([10])

搭建小实战和Sequential的使用

进入以下界面

image-20220705115454295

CIFAR 10 model结构

将input(in_channel,通道为3,32×32),经过一个卷积(5×5的kernel),然后变成32通道,32×32。接着经过最大池化,尺寸减半,通道数没变还是32,再经过一次卷积,发现通道数还是32,16×16结构,继续最大池化,尺寸减半变为8×8,通道没变,再卷积,通道数变为64,再最大池化,尺寸减半变为4×4,再经过flatten,进行展平,通过一个线性层,最后output为10

image-20220705115917983

第一次卷积后尺寸还是32×32,我们需要看下卷积后真实的尺寸是多少

image-20220705121411463

我们需要根据Conv2d公式来计算卷积后的尺寸

image-20220705121554076

这里dialtion默认是1,只有padding不知道,如果stride不是的1的话,那么分子的就扩大很多,所以这里stride就设1,32-27=4,4÷2=2,所以padding=2

image-20220705142525545

回过来设置第一个卷积函数

# 经过卷积后,还是32×32,我们需要注意看下padding和stride需不需要修改
self.conv1 = Conv2d(in_channels=3,out_channels=32,kernel_size=5,padding=2)  

对应就是下图

image-20220705142814568

第一个卷积设置完,设置第二个部分最大池化,池化过后,尺寸变小

image-20220705142904486

#第二个部分,最大池化,2是kernel_size
self.maxpool1 = MaxPool2d(2)

池化后,接着卷积,这里前面最大池化后的out_channel是卷积操作的in_channel,这里是32,out_channel也是32,还有这里的padding,如果按照stride=1的话,padding=2的话,结果还是16

image-20220705143423765

 # 第三个部分,卷积,
        self.conv2 = Conv2d(in_channels=32,out_channels=32,kernel_size=5,padding=2)

卷积后,开始最大池化,尺寸减半,从16×16变为8×8

image-20220705143932645

# 第四个部分,最大池化,2是kernel_size
self.pool2 = MaxPool2d(2)

池化后开始卷积,in_channel为输入图像的通道数(32),out_channel为输出图像的通道数(64)

image-20220705144227610

# 第五个部分,卷积,in_channel是32,out_channel是64
self.conv3 = Conv2d(in_channels=32,out_channels=64,kernel_size=5,padding=2)

接着是最后一个最大池化,尺寸从8×8变为4×4

image-20220705144730988

# 第六个部分,最大池化
self.pool3 = MaxPool2d(2)

展平

image-20220705144905323

# 第七个部分,展平
self.flatten =  Flatten()

线性层(这里图中省略了),为什么in_featrue参数填1024,因为64×4×4。out_feature填64

image-20220705145227471

# 第八个部分,线性层
self.linear1 = Linear(1024,64)

再一个线性层,in_feature填64,out_feature填10

image-20220705145606284

# 第九个部分,线性层
self.linear2 = Linear(64,10)

普通写法

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


class BZWNN(nn.Module):
    def __init__(self):
        super(BZWNN, self).__init__()
        # 第一个部分,卷积
        self.conv1 = Conv2d(in_channels=3,out_channels=32,kernel_size=5,padding=2)
        # 经过卷积后,还是32×32,我们需要注意看下padding和stride需不需要修改

        #第二个部分,最大池化,2是kernel_size
        self.maxpool1 = MaxPool2d(2)

        # 第三个部分,卷积,
        self.conv2 = Conv2d(in_channels=32,out_channels=32,kernel_size=5,padding=2)

        # 第四个部分,最大池化,2是kernel_size
        self.maxpool2 = MaxPool2d(2)

        # 第五个部分,卷积,in_channel是32,out_channel是64
        self.conv3 = Conv2d(in_channels=32,out_channels=64,kernel_size=5,padding=2)

        # 第六个部分,最大池化
        self.maxpool3 = MaxPool2d(2)

        # 第七个部分,展平
        self.flatten =  Flatten()

        # 第八个部分,线性层
        self.linear1 = Linear(1024,64)

        # 第九个部分,线性层
        self.linear2 = Linear(64,10)

    def forward(self,x):
        x = self.conv1(x)
        x = self.maxpool1(x)
        x = self.conv2(x)
        x = self.maxpool2(x)
        x = self.conv3(x)
       	x = self.maxpool3(x)
        x = self.flatten(x)
        x = self.linear1(x)
        x = self.linear2(x)

        return x

bzwnn = BZWNN()
print(bzwnn)

# 验证网络是否正确,这里的64你可以理解为batch_size,就是一个batch里面,有64个3通道,32×32的输入图像
input = torch.ones((64,3,32,32))
# 产生网络
output = bzwnn(input)
print(output.shape)

Sequential写法(有点像compose)

import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter


class BZWNN(nn.Module):
    def __init__(self):
        super(BZWNN, self).__init__()
        # 第一个部分,卷积
        # self.conv1 = Conv2d(in_channels=3,out_channels=32,kernel_size=5,padding=2)
        # 经过卷积后,还是32×32,我们需要注意看下padding和stride需不需要修改

        #第二个部分,最大池化,2是kernel_size
        # self.maxpool1 = MaxPool2d(2)

        # 第三个部分,卷积,
        # self.conv2 = Conv2d(in_channels=32,out_channels=32,kernel_size=5,padding=2)

        # 第四个部分,最大池化,2是kernel_size
        # self.maxpool2 = MaxPool2d(2)

        # 第五个部分,卷积,in_channel是32,out_channel是64
        # self.conv3 = Conv2d(in_channels=32,out_channels=64,kernel_size=5,padding=2)

        # 第六个部分,最大池化
        # self.maxpool3 = MaxPool2d(2)

        # 第七个部分,展平
        #   self.flatten =  Flatten()

        # 第八个部分,线性层
        # self.linear1 = Linear(1024,64)

        # 第九个部分,线性层
        # self.linear2 = Linear(64,10)


        # 下面的方法,等同于上面的,九个部分
        self.model1 = Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,64,5,padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )


    def forward(self,x):
        # x = self.conv1(x)
        # x = self.maxpool1(x)
        # x = self.conv2(x)
        # x = self.maxpool2(x)
        # x = self.conv3(x)
        # x = self.maxpool3(x)
        # x = self.flatten(x)
        # x = self.linear1(x)
        # x = self.linear2(x)

        # 使用sequential的方法
        x = self.model1(x)
        return x

bzwnn = BZWNN()
print(bzwnn)

# 验证网络是否正确,这里的64你可以理解为batch_size,就是一个batch里面,有64个3通道,32×32的输入图像
input = torch.ones((64,3,32,32))
# 产生网络
output = bzwnn(input)
print(output.shape)

# 使用summarywriter展示流程
writer = SummaryWriter("logs_seq")
writer.add_graph(bzwnn,input)
writer.close()

SummrayWriter的add_graph效果

image-20220705152611432

双击BZWNN效果如下

image-20220705152741934

损失函数与反向传播

损失函数的作用

1.计算实际输出和目标之间的差距(越小越好)

2.为我们更新输出提供一定的依据(反向传播)

进入下面界面

image-20220705162619550

L1loss和MSE的计算方法如下

image-20220705163508757

import torch
from torch.nn import L1Loss, MSELoss

# 不写dtype=float会报错
inputs = torch.tensor([1,2,3],dtype=float)
targets = torch.tensor([1,2,5],dtype=float)

inputs = torch.reshape(inputs,(1,1,1,3))
targets = torch.reshape(targets,(1,1,1,3))

loss = L1Loss(reduction='sum')
result = loss(inputs,targets)

loss_mse = MSELoss()
result_mse = loss_mse(inputs,targets)


print(result)
print(result_mse)

CrossEntropyLoss

交叉熵(cross entropy)适用于计算分类问题

image-20220705164106315

计算过程:x[1] = 0.2

Loss(x,class) = -x[1]+log[exp(0.1)+exp(0.2)+exp(0.3)]

​ = -0.2 + log[exp(0.1)+exp(0.2)+exp(0.3)]

如果想要这个Loss值越来越小的话,那么这个log[exp(0.1)+exp(0.2)+exp(0.3)]就要有点小,同时这个x[class]要比较大,因为前有负号,这output是预测每个物体的概率,人的概率为0.1,狗的概率为0.2,猫的概率为0.3。只有当target和output完全命中的时候,这时候x[class]才会很大。

交叉熵的输入要求,这里的C是分类问题,代表分类的类别,像cifar10,分成10类,那么C就是10,N是batch_size,target这里的N是要求有多少个batch_size

image-20220705165737106

input这是1×3,这个3就是它的类别,这个1就是1batch_size,然后这个target也是1,因为只有一个batch_size。这个input一定要是一个你没有处理过,对每一个的得分

image-20220705170355768

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

dataset = torchvision.datasets.CIFAR10("./data",train=False,transform=torchvision.transforms.ToTensor(),download=True)

dataloader = DataLoader(dataset,batch_size=1)

class BZWNN(nn.Module):
    def __init__(self):
        super(BZWNN, self).__init__()
        # 下面的方法,等同于上面的,九个部分
        self.model1 = Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,64,5,padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self,x):
        # 使用sequential的方法
        x = self.model1(x)
        return x

loss = nn.CrossEntropyLoss()

bzwnn = BZWNN()
for data in dataloader:
    imgs,targets = data
    outputs = bzwnn(imgs)
    result_loss = loss(outputs,targets)
    print(result_loss)

反向传播

反向传播意思就是,尝试如何调整网络过程中的参数才会导致最终的loss变小(因为是从loss开始推导参数,和网络的顺序相反,所以叫反向传播),以及梯度的理解可以直接当成“斜率”。

当我们采用反向传播的时候,每一个结点或者更新的参数,它都会求出一个对应的梯度(grad),然后我们在优化的过程中,可以根据这个梯度对参数进行优化,最终让loss降低。

优化器

进入以下界面

image-20220706154128547

这里使用的是SGD,注意SGB(随机梯度下降)的输入要求

image-20220706154912658

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

dataset = torchvision.datasets.CIFAR10("./data",train=False,transform=torchvision.transforms.ToTensor(),download=True)

dataloader = DataLoader(dataset,batch_size=1)

class BZWNN(nn.Module):
    def __init__(self):
        super(BZWNN, self).__init__()
        # 下面的方法,等同于上面的,九个部分
        self.model1 = Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,64,5,padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self,x):
        # 使用sequential的方法
        x = self.model1(x)
        return x

loss = nn.CrossEntropyLoss()

bzwnn = BZWNN()

# SGD
optim = torch.optim.SGD(bzwnn.parameters(),lr=0.01)


for epoch in range(20):
    running_loss = 0.0
    for data in dataloader:
        imgs,targets = data
        outputs = bzwnn(imgs)
        # 计算输出与真实的差距
        result_loss = loss(outputs,targets)

        # 我们要做的第一步,去把每一个网络模型可以调节参数的对应梯度调为0
        optim.zero_grad()

        # 设置为0后,调用优化器对其中的参数进行优化,优化器需要每一个参数的一个梯度,所以我们需要反向传播
        result_loss.backward()  # 现在得到了每一个需要调节参数的梯度

        # 调用优化器,对每一个参数进行调优
        optim.step()

        running_loss = running_loss + result_loss
    print(running_loss)

网络模型的保存与读取

保存

import torchvision
import torch
# 对应的加载文件在model_load文件中


# pretrained 为预训练
from torch import nn

vgg16= torchvision.models.vgg16(pretrained=False)

# 保存方式1,pth是一种后缀格式     保存模型结构+模型参数
torch.save(vgg16, 'vgg16_method1.pth')

# 保存方式2,将vgg16网络的参数,保存成字典,不保存结构(官方推荐)
torch.save(vgg16.state_dict(),"vgg16_method2.pth")


# 陷阱
class BzwNN(nn.Module):
    def __init__(self):
        super(BzwNN, self).__init__()
        self.covn1 = nn.Conv2d(3,64,kernel_size=3)

    def forward(self,x):
        x = self.conv1(x)
        return x

bzwnn = BzwNN()
torch.save(bzwnn,"bzw_method1.pth")

读取

import torch
import torchvision

# 对应的是mode_save.py文件

# 方式1:保存方式1,加载模型
from torch import nn

model = torch.load("vgg16_method1.pth")
print(model)

# 方式2:加载方式,以这种方式保存的,需要先创建模型,然后加载
vgg16 = torchvision.models.vgg16(pretrained=False)
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))

print(vgg16)


# 陷阱1,自己定义的网络,保存后,在这面导入时,需要把网络复制过来

model = torch.load("bzw_method1.pth")
print(model)

# 需要把自己定义的网络复制过来
class BzwNN(nn.Module):
    def __init__(self):
        super(BzwNN, self).__init__()
        self.covn1 = nn.Conv2d(3,64,kernel_size=3)

    def forward(self,x):
        x = self.conv1(x)
        return x

完整的模型训练套路(一)

import torchvision

from torch.utils.data import DataLoader
from model import *   # 为了规范,第五步,改为从这里引用

# 第一步,准备数据集
train_data = torchvision.datasets.CIFAR10(root="./data",train=True,transform=torchvision.transforms.ToTensor(),download=True)

# 第二步,准备测试数据集
test_data = torchvision.datasets.CIFAR10(root="./data",train=False,transform=torchvision.transforms.ToTensor(),download=True)

# 第三步,查看数据集的大小
train_data_size = len(train_data)
test_data_size = len(test_data)
print("训练数据集的长度为:{0}".format(train_data_size))
print("测试数据集的长度为:{0}".format(test_data_size))

# 第四步,利用dataloader进行数据集加载
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)

# 第五步,搭建神经网络
# 这里改为引用了

# 第六步,创建网络模型
bzwnn = BzwNN()

# 第七步,创建损失函数
loss_fn = nn.CrossEntropyLoss()

# 第八步,优化器
# learning_rate = 0.01
learning_rate = 1e-2    # 1e-2 = 1 × (10)^ -2 = 1 / 100 = 0.01
optimizer = torch.optim.SGD(bzwnn.parameters(),lr=learning_rate)

# 第九步,设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10

# 第十步,开始训练
for i in range(epoch):
    print("---------------第 {} 轮训练开始------------".format(i+1))

    # 第十一步,从dataloader中取数据,训练步骤开始
    for data in train_dataloader:
        imgs,targets = data

        # 得到一个训练后的输出
        outputs = bzwnn(imgs)

        # 计算损失值,将输出的结果和真实值放入损失函数中
        loss = loss_fn(outputs,targets)

        # 开始优化,首先需要将梯度清零
        optimizer.zero_grad()

        #  根据损失值,进行反向传播,得到每一个参数结点的梯度
        loss.backward()

        #  进行优化
        optimizer.step()

        # 记录训练次数
        total_train_step += 1
        print("训练次数:{},Loss:{}".format(total_train_step,loss))

训练结果

image-20220706174854844

完整的模型训练套路(二)

import torchvision


from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

from model import *   # 为了规范,第五步,改为从这里引用

# 第一步,准备数据集
train_data = torchvision.datasets.CIFAR10(root="./data",train=True,transform=torchvision.transforms.ToTensor(),download=True)

# 第二步,准备测试数据集
test_data = torchvision.datasets.CIFAR10(root="./data",train=False,transform=torchvision.transforms.ToTensor(),download=True)

# 第三步,查看数据集的大小
train_data_size = len(train_data)
test_data_size = len(test_data)
print("训练数据集的长度为:{0}".format(train_data_size))
print("测试数据集的长度为:{0}".format(test_data_size))

# 第四步,利用dataloader进行数据集加载
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)

# 第五步,搭建神经网络
# 这里改为引用了

# 第六步,创建网络模型
bzwnn = BzwNN()

# 第七步,创建损失函数
loss_fn = nn.CrossEntropyLoss()

# 第八步,优化器
# learning_rate = 0.01
learning_rate = 1e-2    # 1e-2 = 1 × (10)^ -2 = 1 / 100 = 0.01
optimizer = torch.optim.SGD(bzwnn.parameters(),lr=learning_rate)

# 第九步,设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10

# 添加tensorboard
writer = SummaryWriter("./logs_train")



# 第十步,开始训练
for i in range(epoch):
    print("---------------第 {} 轮训练开始------------".format(i+1))

    # 第十一步,从dataloader中取数据,训练步骤开始
    for data in train_dataloader:
        imgs,targets = data

        # 得到一个训练后的输出
        outputs = bzwnn(imgs)

        # 计算损失值,将输出的结果和真实值放入损失函数中
        loss = loss_fn(outputs,targets)

        # 开始优化,首先需要将梯度清零
        optimizer.zero_grad()

        #  根据损失值,进行反向传播,得到每一个参数结点的梯度
        loss.backward()

        #  进行优化
        optimizer.step()

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

    # 第十二步,让训练完的在测试数据上跑一遍,以测试数据的损失,来评估这个模型有没有训练好
    total_test_loss = 0
    with torch.no_grad():   # no_grad,没有梯度,可以保证不会调优
        for data in test_dataloader:
            imgs,targets = data
            outputs = bzwnn(imgs)
            loss = loss_fn(outputs,targets)
            total_test_loss += loss.item()
    print("整体测试集上的Loss:{}".format(total_test_loss))
    writer.add_scalar("test_loss",total_test_loss,total_test_step)
    total_test_step += 1

    # 保存每一轮的训练结果
    torch.save(bzwnn,"bzwnn_{}.pth".format(i))
    print("模型已保存")

writer.close()

训练结果,可以看到loss在变小

image-20220706184215504

有两个输入,放到模型中,第一个输入,会得到一个输出[0.1,0.2],第二个输入,会得到一个输出[0.3,0.4],有两个类别0和1,0.1代表第一个输入预测为0的概率是0.1,0.2代表第一个输入预测1的概率是0.2,0.3代表第二个输入预测为0的概率是0.3,0.4代表第二个输入预测1的概率是0.4,preds=[1][1],因为第一个最大是0.2是1的类别,第二个最大的是0.4也是1的类别。Argmax能求出横向的最大值处于那个位置。inputs target=[0][1],第一个是0类别,第二个是1类别。通过preds == inputs target 判断正确率,也是[1][1]和[0][1]比,比的过程是第一个的1与第二个的0相比,第一个的1与第二个的1相比,结果为[false,true],然后把结果相加false是0,true是1,结果为1,然后结果再除以个数就是正确率

image-20220706205657104

import torch

outputs = torch.Tensor([[0.1,0.2],
                       [0.3,0.4]])

output2s = torch.Tensor([[0.5,0.2],
                       [0.3,0.4]])
# 1是横向看
print(outputs.argmax(1))   # tensor([1, 1]) 第一行最大是0.2,处于1的位置,第二行最大的是0.4,处于1的位置
# 0是纵向看
print(output2s.argmax(0))   # tensor([0, 1]) 第一列最大是0.5,处于0的位置,第二列最大的是0.4,处于1的位置

preds = outputs.argmax(1)
targets = torch.tensor([0,1])
print(targets == preds)     # tensor([False,  True])
# 计算相等的个数,最后除以总个数就是正确率
print((targets == preds).sum()) # tensor(1)

测试

import torchvision


from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

from model import *   # 为了规范,第五步,改为从这里引用

# 第一步,准备数据集
train_data = torchvision.datasets.CIFAR10(root="./data",train=True,transform=torchvision.transforms.ToTensor(),download=True)

# 第二步,准备测试数据集
test_data = torchvision.datasets.CIFAR10(root="./data",train=False,transform=torchvision.transforms.ToTensor(),download=True)

# 第三步,查看数据集的大小
train_data_size = len(train_data)
test_data_size = len(test_data)
print("训练数据集的长度为:{0}".format(train_data_size))  #   50000
print("测试数据集的长度为:{0}".format(test_data_size))   # 10000

# 第四步,利用dataloader进行数据集加载
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)

# 第五步,搭建神经网络
# 这里改为引用了

# 第六步,创建网络模型
bzwnn = BzwNN()

# 第七步,创建损失函数
loss_fn = nn.CrossEntropyLoss()

# 第八步,优化器
# learning_rate = 0.01
learning_rate = 1e-2    # 1e-2 = 1 × (10)^ -2 = 1 / 100 = 0.01
optimizer = torch.optim.SGD(bzwnn.parameters(),lr=learning_rate)

# 第九步,设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10

# 添加tensorboard
writer = SummaryWriter("./logs_train")



# 第十步,开始训练
for i in range(epoch):
    print("---------------第 {} 轮训练开始------------".format(i+1))

    # 第十一步,从dataloader中取数据,训练步骤开始
    for data in train_dataloader:
        imgs,targets = data

        # 得到一个训练后的输出
        outputs = bzwnn(imgs)

        # 计算损失值,将输出的结果和真实值放入损失函数中
        loss = loss_fn(outputs,targets)

        # 开始优化,首先需要将梯度清零
        optimizer.zero_grad()

        #  根据损失值,进行反向传播,得到每一个参数结点的梯度
        loss.backward()

        #  进行优化
        optimizer.step()

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

    # 第十二步,让训练完的在测试数据上跑一遍,以测试数据的损失,来评估这个模型有没有训练好
    # 总损失
    total_test_loss = 0
    # 准确率
    total_accuracy = 0
    with torch.no_grad():   # no_grad,没有梯度,可以保证不会调优
        for data in test_dataloader:
            imgs,targets = data
            outputs = bzwnn(imgs)
            loss = loss_fn(outputs,targets)
            total_test_loss += loss.item()
            accuracy = (outputs.argmax(1) == targets).sum()
            total_accuracy += accuracy
    print("整体测试集上的Loss:{}".format(total_test_loss))
    print("整体测试集的正确率:{}".format(total_accuracy / test_data_size))
    writer.add_scalar("test_loss",total_test_loss,total_test_step)
    writer.add_scalar("test_accuracy",total_accuracy,total_test_step)
    total_test_step += 1

    # 保存每一轮的训练结果
    torch.save(bzwnn,"bzwnn_{}.pth".format(i))
    print("模型已保存")

writer.close()

运行结果

image-20220706210800568

利用GPU进行训练(一)

找到一个网络模型,找一个数据,这个数据包括输入,标注,找到损失,对这些调用.cuda(),然后返回

image-20220707143257492

对网络模型调用cuda

if torch.cuda.is_available():
    bzwnn = bzwnn.cuda()

对数据调用cuda

if torch.cuda.is_available():
    imgs = imgs.cuda()
    targets = targets.cuda()

对损失函数调用cuda

if torch.cuda.is_available():
    loss_fn = loss_fn.cuda()

对测试数据调用cuda

if torch.cuda.is_available():
    imgs = imgs.cuda()
    targets = targets.cuda()

colab教程

首先新建一个文件

image-20220707150050518

文件创建后,进去,点击edit,然后点击notebook settings

image-20220707150142996

点击启用GPU,然后保存

image-20220707150224714

复制代码,点击运行

import time

import torch
import torchvision
from torch import nn

from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

# from model import *   # 为了规范,第五步,改为从这里引用

# 第一步,准备数据集
train_data = torchvision.datasets.CIFAR10(root="./data",train=True,transform=torchvision.transforms.ToTensor(),download=True)

# 第二步,准备测试数据集
test_data = torchvision.datasets.CIFAR10(root="./data",train=False,transform=torchvision.transforms.ToTensor(),download=True)

# 第三步,查看数据集的大小
train_data_size = len(train_data)
test_data_size = len(test_data)
print("训练数据集的长度为:{0}".format(train_data_size))  #   50000
print("测试数据集的长度为:{0}".format(test_data_size))   # 10000

# 第四步,利用dataloader进行数据集加载
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)

# 第五步,搭建神经网络
# 这里改为引用了

# 第六步,创建网络模型
class BzwNN(nn.Module):
    def __init__(self):
        super(BzwNN, self).__init__()
        self.module = nn.Sequential(
            # in_channel,out_channel,kernel_size,stride,padding
            nn.Conv2d(3,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,64,5,1,2),
            nn.MaxPool2d(2),
            nn.Flatten(),
            nn.Linear(1024,64),
            nn.Linear(64,10)
        )

    def forward(self,x):
        x = self.module(x)
        return x

bzwnn = BzwNN()
# 对网络模型调用cuda
if torch.cuda.is_available():
    bzwnn = bzwnn.cuda()



# 第七步,创建损失函数
loss_fn = nn.CrossEntropyLoss()
# 对损失函数调用cuda
if torch.cuda.is_available():
    loss_fn = loss_fn.cuda()

# 第八步,优化器
# learning_rate = 0.01
learning_rate = 1e-2    # 1e-2 = 1 × (10)^ -2 = 1 / 100 = 0.01
optimizer = torch.optim.SGD(bzwnn.parameters(),lr=learning_rate)

# 第九步,设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10

# 添加tensorboard
writer = SummaryWriter("./logs_train")

start_time = time.time()
# 第十步,开始训练
for i in range(epoch):
    print("---------------第 {} 轮训练开始------------".format(i+1))

    # 第十一步,从dataloader中取数据,训练步骤开始
    for data in train_dataloader:
        imgs,targets = data

        # 对数据调用cuda
        if torch.cuda.is_available():
            imgs = imgs.cuda()
            targets = targets.cuda()

        # 得到一个训练后的输出
        outputs = bzwnn(imgs)

        # 计算损失值,将输出的结果和真实值放入损失函数中
        loss = loss_fn(outputs,targets)

        # 开始优化,首先需要将梯度清零
        optimizer.zero_grad()

        #  根据损失值,进行反向传播,得到每一个参数结点的梯度
        loss.backward()

        #  进行优化
        optimizer.step()

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

    end_time = time.time()
    print("花费时间:{}".format(end_time-start_time))
    # 第十二步,让训练完的在测试数据上跑一遍,以测试数据的损失,来评估这个模型有没有训练好
    # 总损失
    total_test_loss = 0
    # 准确率
    total_accuracy = 0
    with torch.no_grad():   # no_grad,没有梯度,可以保证不会调优
        for data in test_dataloader:
            imgs,targets = data

            # 对测试数据调用cuda
            if torch.cuda.is_available():
                imgs = imgs.cuda()
                targets = targets.cuda()

            outputs = bzwnn(imgs)
            loss = loss_fn(outputs,targets)
            total_test_loss += loss.item()
            accuracy = (outputs.argmax(1) == targets).sum()
            total_accuracy += accuracy
    print("整体测试集上的Loss:{}".format(total_test_loss))
    print("整体测试集的正确率:{}".format(total_accuracy / test_data_size))
    writer.add_scalar("test_loss",total_test_loss,total_test_step)
    writer.add_scalar("test_accuracy",total_accuracy,total_test_step)
    total_test_step += 1

    # 保存每一轮的训练结果
    torch.save(bzwnn,"bzwnn_{}.pth".format(i))
    print("模型已保存")

writer.close()

利用GPU进行训练(二)

对网络模型,数据(输入,标注),损失函数调用.to(device)到设备上去

image-20220707151342532

定义训练设备

device = torch.device("cuda")

将网络模型放到设备上去

bzwnn = bzwnn.to(device)

将损失函数放到设备上去

loss_fn = loss_fn.to(device)

将数据放到设备上去

imgs = imgs.to(device)
targets = targets.to(device)

将测试数据放到设备上去

imgs = imgs.to(device)
targets = targets.to(device)

运行代码

import time

import torch
import torchvision
from torch import nn

from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter


# 定义训练的设备
device = torch.device("cuda")


# 第一步,准备数据集
train_data = torchvision.datasets.CIFAR10(root="./data",train=True,transform=torchvision.transforms.ToTensor(),download=True)

# 第二步,准备测试数据集
test_data = torchvision.datasets.CIFAR10(root="./data",train=False,transform=torchvision.transforms.ToTensor(),download=True)

# 第三步,查看数据集的大小
train_data_size = len(train_data)
test_data_size = len(test_data)
print("训练数据集的长度为:{0}".format(train_data_size))  #   50000
print("测试数据集的长度为:{0}".format(test_data_size))   # 10000

# 第四步,利用dataloader进行数据集加载
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)

# 第五步,搭建神经网络
# 这里改为引用了

# 第六步,创建网络模型
class BzwNN(nn.Module):
    def __init__(self):
        super(BzwNN, self).__init__()
        self.module = nn.Sequential(
            # in_channel,out_channel,kernel_size,stride,padding
            nn.Conv2d(3,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,64,5,1,2),
            nn.MaxPool2d(2),
            nn.Flatten(),
            nn.Linear(1024,64),
            nn.Linear(64,10)
        )

    def forward(self,x):
        x = self.module(x)
        return x

bzwnn = BzwNN()

# 网络模型放到设备上去
bzwnn = bzwnn.to(device)




# 第七步,创建损失函数
loss_fn = nn.CrossEntropyLoss()
# 将损失函数放到设备上去
loss_fn = loss_fn.to(device)

# 第八步,优化器
# learning_rate = 0.01
learning_rate = 1e-2    # 1e-2 = 1 × (10)^ -2 = 1 / 100 = 0.01
optimizer = torch.optim.SGD(bzwnn.parameters(),lr=learning_rate)

# 第九步,设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10

# 添加tensorboard
writer = SummaryWriter("./logs_train")


start_time = time.time()
# 第十步,开始训练
for i in range(epoch):
    print("---------------第 {} 轮训练开始------------".format(i+1))

    # 第十一步,从dataloader中取数据,训练步骤开始
    for data in train_dataloader:
        imgs,targets = data

        imgs = imgs.to(device)
        targets = targets.to(device)

        # 得到一个训练后的输出
        outputs = bzwnn(imgs)

        # 计算损失值,将输出的结果和真实值放入损失函数中
        loss = loss_fn(outputs,targets)

        # 开始优化,首先需要将梯度清零
        optimizer.zero_grad()

        #  根据损失值,进行反向传播,得到每一个参数结点的梯度
        loss.backward()

        #  进行优化
        optimizer.step()

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

    end_time = time.time()
    print("花费时间:{}".format(end_time-start_time))
    # 第十二步,让训练完的在测试数据上跑一遍,以测试数据的损失,来评估这个模型有没有训练好
    # 总损失
    total_test_loss = 0
    # 准确率
    total_accuracy = 0
    with torch.no_grad():   # no_grad,没有梯度,可以保证不会调优
        for data in test_dataloader:
            imgs,targets = data

            imgs = imgs.to(device)
            targets = targets.to(device)

            outputs = bzwnn(imgs)
            loss = loss_fn(outputs,targets)
            total_test_loss += loss.item()
            accuracy = (outputs.argmax(1) == targets).sum()
            total_accuracy += accuracy
    print("整体测试集上的Loss:{}".format(total_test_loss))
    print("整体测试集的正确率:{}".format(total_accuracy / test_data_size))
    writer.add_scalar("test_loss",total_test_loss,total_test_step)
    writer.add_scalar("test_accuracy",total_accuracy,total_test_step)
    total_test_step += 1

    # 保存每一轮的训练结果
    torch.save(bzwnn,"bzwnn_{}.pth".format(i))
    print("模型已保存")

writer.close()

完整的模型验证套路

import torch
import torchvision.transforms
from PIL import Image
from torch import nn

image_path = "./imgs/airplane.png"

image = Image.open(image_path)


# 因为png格式是四个通道,除了RGB三通道外,还有一个透明度通道。
# 所以,我们调用image = image.convert('RGB),保留其颜色通道。
# 当然,如果图片本来就是三个颜色通道,经过此操作,不变。
# 加上这一步后,可以适应png jpg各种格式的图片。

image = image.convert('RGB')

# 将图片首先进行resize,然后转换为ToTensor
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)),torchvision.transforms.ToTensor()])

image = transform(image)
print(image.shape)

class BzwNN(nn.Module):
    def __init__(self):
        super(BzwNN, self).__init__()
        self.module = nn.Sequential(
            # in_channel,out_channel,kernel_size,stride,padding
            nn.Conv2d(3,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,32,5,1,2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,64,5,1,2),
            nn.MaxPool2d(2),
            nn.Flatten(),
            nn.Linear(1024,64),
            nn.Linear(64,10)
        )

    def forward(self,x):
        x = self.module(x)
        return x


# 加载模型时,需要把自己的模型导入过来

model = torch.load("bzwnn_29_gpu.pth")
print(model)

# Expected 4-dimensional input for 4-dimensional weight [32, 3, 5, 5], but got 3-dimensional input o
# 要求是4个纬度,输入只有3个纬度,需要reshape
image = torch.reshape(image,(1,3,32,32))

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
image = image.to(device)

# 将模型转换为测试类型
model.eval()

with torch.no_grad():       # 可以节约内存和时间
    output = model(image)

print(output.argmax(1))     
#  照片为狗时 tensor([5], device='cuda:0') 5是狗
#  照片为飞机时 tensor([0], device='cuda:0') 0是飞机

posted @ 2022-10-19 12:06  放学别跑啊  阅读(480)  评论(0编辑  收藏  举报