Pytorch使用
视频链接:PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】_哔哩哔哩_bilibili
Pytorch的配置
有时候会使用不同版本的python,这时进入
创建你需要的环境,并指定python的安装版本
conda create -n pytorch python=3.6 # 一个名为pytorch环境的,版本为3.6
激活你需要的python版本
conda activate pytorch # 激活名为pytorch这个环境
下载pytorch
复制Run this Command
里面的代码,回到最开始进入的命令行里,粘贴,然后开始下载
判断当前电脑的显卡能否用
import torch
torch.cuda.is_available()
# true表示没问题,false的话,可以参考链接
# https://blog.csdn.net/qq_41997920/article/details/105090212
PyCharm和Jupyter的配置
为pycharm指定conda的环境
环境导入成功
创建好的工程
打开Anaconda的命令行,切换到pytorch环境,pytorch环境里面是没有jupyter book的,需要自己安装
conda install nb_conda # 安装jupyter
jupyter notebook # 打开jupyter notebook
创建新的代码文件
测试是否成功,True为成功
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)
然后打开网址
增加一个y=2x函数,为什么y=x注释了,还可以显示?
答:因为y=x的值已经写入到logs下面了,程序每执行一次,就会在logs目录下面创建一个文件,下图就是执行三次以后的情况。
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
step为2
如果想再展示一个图片的话,只需要更改add_image里面的名称
writer.add_image("train",img_array,1,dataformats='HWC')
transforms
对图片进行变换
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的计算方法
参考链接:(20条消息) Python numpy 归一化和标准化 代码实现_Kenn7的博客-CSDN博客_numpy 归一化
Compose的用法
一般用Compose把多个步骤整合到一起
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()
运行结果
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也进行打包,然后返回。
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(自己设的)将会丢弃
shuffle设置False时,两次训练的图片顺序一样
神经网络的基本骨架-nn.Module的使用
打开pytorch官网,进入以下界面
-
container
骨架,往骨架中添加不同的内容,就可以组成不同的神经网络
-
Convolution Layers
卷积层
-
Pooling Layers
池化层
-
Non-linear Activations
非线性激活
进入Container里面,Module是神经网络的基础,很重要
我们大部分定义的神经网络模板就如下图的代码一样,写一个class,定义一个自己神经网络的名字,然后继承nn.Module,主要写两个函数,一个是__init__
函数进行初始化,然后要调用父类的初始化函数。
这个forward函数可以理解为有一个输入叫input,输入到神经网络中,神经网络会一个输出,神经网络经过的一个运算步骤就是forward
函数,这叫前向传播
forward函数的执行流程如下,首先输入x,卷积之后进行非线性,然后卷积,接着非线性,然后输出
调试过程
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.)
卷积
卷积操作
将输入图像与卷积核对应位置的值进行相乘,然后相加
然后开始移动,可以向右移一步,两步,这取决于stride,
右移不了,开始往下走
继续移动
剩下步骤省略,下图是最后一步结果,最右边的那个3×3的矩阵就是卷积后的输出
stride=2时,右移2步
下移2步
流程如下
函数的用法看下图
conv2d的参数
现在准备使用卷积函数,这里是二维,使用conv2d
函数,查看conv2d的用法,input需要有batch,通道,高,宽
这个shape只有2个参数,而上图中给的shape的参数是四个
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满足要求
进行卷积,因为是二维数组,所以这里需要调用conv2d
函数,记得引用torch.nn.functional
,结果如下,up主12哪里算错了,应该是13
stride=2时的结果
padding用法
padding作用就是扩大一圈输入矩阵,padding为1时,扩大的格子为1,padding为2时,扩大的格子为2,默认填充值为0
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)
神经网络
卷积层
进入如下界面
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()
运行结果
最大池化的使用
dilation
空洞卷积
原理
首先初始化一个池化核,然后拿这个池化核与输入图像进行计算,取这一个区域的里的最大值,移动的大小默认为kernel_size,ceil_mode决定池化核没覆盖满时,是否保留最大值
ceil_mode为True时,结果为
ceil_mode为False时,结果为
输入要求是4个参数,而创建的矩阵的shape只有两个参数,需要reshape
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时
ceil_mode为False时
最大池化的作用
一个简单的例子,平时我们看视频是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()
可以看出最大池化的图片,就像压缩了一样
通过最大池化,神经网络的数据量大大的减少,提高了训练速度
非线性激活
进入下面界面
ReLU
ReLU的参数inplace表示是否替换原来的数据,一般建议inplace为false,这样防止原始数据丢失
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)
运行结果
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()
结果
线性层机器其他层介绍
Linears
进入下面界面
将1×1×4096通过这个层变成1×1×1000,其实是将input_feature设为4096,output_feature设置为1000
怎样将一个5×5的矩阵,转成1×25,通过线性层变成1×3,如下图
见下面代码
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的使用
进入以下界面
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
第一次卷积后尺寸还是32×32,我们需要看下卷积后真实的尺寸是多少
我们需要根据Conv2d
公式来计算卷积后的尺寸
这里dialtion默认是1,只有padding不知道,如果stride不是的1的话,那么分子的就扩大很多,所以这里stride就设1,32-27=4,4÷2=2,所以padding=2
回过来设置第一个卷积函数
# 经过卷积后,还是32×32,我们需要注意看下padding和stride需不需要修改
self.conv1 = Conv2d(in_channels=3,out_channels=32,kernel_size=5,padding=2)
对应就是下图
第一个卷积设置完,设置第二个部分最大池化,池化过后,尺寸变小
#第二个部分,最大池化,2是kernel_size
self.maxpool1 = MaxPool2d(2)
池化后,接着卷积,这里前面最大池化后的out_channel是卷积操作的in_channel,这里是32,out_channel也是32,还有这里的padding,如果按照stride=1的话,padding=2的话,结果还是16
# 第三个部分,卷积,
self.conv2 = Conv2d(in_channels=32,out_channels=32,kernel_size=5,padding=2)
卷积后,开始最大池化,尺寸减半,从16×16变为8×8
# 第四个部分,最大池化,2是kernel_size
self.pool2 = MaxPool2d(2)
池化后开始卷积,in_channel为输入图像的通道数(32),out_channel为输出图像的通道数(64)
# 第五个部分,卷积,in_channel是32,out_channel是64
self.conv3 = Conv2d(in_channels=32,out_channels=64,kernel_size=5,padding=2)
接着是最后一个最大池化,尺寸从8×8变为4×4
# 第六个部分,最大池化
self.pool3 = MaxPool2d(2)
展平
# 第七个部分,展平
self.flatten = Flatten()
线性层(这里图中省略了),为什么in_featrue参数填1024,因为64×4×4。out_feature填64
# 第八个部分,线性层
self.linear1 = Linear(1024,64)
再一个线性层,in_feature填64,out_feature填10
# 第九个部分,线性层
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
效果
双击BZWNN效果如下
损失函数与反向传播
损失函数的作用
1.计算实际输出和目标之间的差距(越小越好)
2.为我们更新输出提供一定的依据(反向传播)
进入下面界面
L1loss和MSE的计算方法如下
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)适用于计算分类问题
计算过程: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
input这是1×3,这个3就是它的类别,这个1就是1batch_size,然后这个target也是1,因为只有一个batch_size。这个input一定要是一个你没有处理过,对每一个的得分
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降低。
优化器
进入以下界面
这里使用的是SGD,注意SGB(随机梯度下降)的输入要求
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))
训练结果
完整的模型训练套路(二)
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在变小
有两个输入,放到模型中,第一个输入,会得到一个输出[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,然后结果再除以个数就是正确率。
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()
运行结果
利用GPU进行训练(一)
找到一个网络模型,找一个数据,这个数据包括输入,标注,找到损失,对这些调用.cuda(),然后返回
对网络模型调用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教程
首先新建一个文件
文件创建后,进去,点击edit,然后点击notebook settings
点击启用GPU,然后保存
复制代码,点击运行
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)
到设备上去
定义训练设备
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是飞机