目录
(113条消息) PyTorch深度学习快速入门教程【小土堆】 学习笔记_阿尼亚要好好读书呀的博客-CSDN博客
P1pytorch的环境安装和配置
1、下载anaconda(包含大量的package)
在代码中,如果一个函数作为工具,一个package就是一个工具包
要记住anaconda的安装路径
如果在看到括号左边的(base)
那就是安装成功。
2、深度学习离不开显卡,pytorch和tensorflow仅支持英伟达的显卡。
有没有显卡对学习pytorch并没有影响,显卡主要起到训练加速的作用。
显卡的配置涉及:驱动+CUDA Toolkit(后者可以跟随pytorch一键安装)
在任务管理器看到显卡型号,说明驱动正确安装
3、管理环境
一个package相当于一个工具包,把环境理解为一个房子。里面本身拥有一个base房子,新建一个名为111的房子,里面有不同版本的pytorch.
1、新建虚拟环境 conda create -n env_name python=3.8(有哪些环境:conda env list)
2、conda activate env_name
3、安装pytorch(推荐1.1以上,有tensorboard)
测试有没有安装成功
P2python编辑器的选择和安装
pycharm、jupyter
jupyter默认安装在base中,在pytorch中安装jupyter
1、进入pytorch环境中,在pytorch中安装一个包 conda install nb_conda
2、输入jupyter notebook
3、
4、运行代码块是 shift+回车
p4 python中的两大法宝函数
dir():打开,看见有什么东西
help():像说明书
帮助文档
1、
2、
P5 pycharm和jupyter的对比
jupyter notebook
python、python控制台、和jupyter notebook区别
P6继承Dataset,读取自己创建的数据集
在pytorch中如何加载数据
读取数据涉及到的类:Dataset、Dataloader
Dataset:获取其中的数据、并编号
提供一种方式去获取数据及其label
Dataloader:对数据进行打包
为后面的网络提供不同的数据形式
实操:识别蚂蚁和蜜蜂
获取所有图片的索引
self.img_path = os.listdir(self.path) # 先获得那个文件夹的地址,然后把这个文件夹下的所有内容变成一个列表,这个列表有对应的索引号
os.path.join(self.root_dir,self.label_dir) #图片的路径地址 把上面两个地址相加
from torch.utils.data import Dataset
from PIL import Image # 读取图片
import os #获取所有图片的一个列表
class MyData(Dataset): #创建一个类 然后去继承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 = os.listdir(self.path) # 先获得那个文件夹的地址,然后把这个文件夹下的所有内容变成一个列表,这个列表有对应的索引号
def __getitem__(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 = "dataset/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) # 蜜蜂的实例
img, label = ants_dataset[1]# __getitem__是构造方法不用调用
img.show()
train_dataset =ants_dataset + bees_dataset # 蚂蚁数据集和蜜蜂数据集的集合, 数据集的拼接
p8tensorboard的使用
from torch.utils.tensorboard import SummaryWriter # 导入类 按住ctrl,点击想看的类,看他的说明文档 writer = SummaryWriter("logs") # 实例化类, 对应的事件文件保存在logs文件夹 #经常使用的方法有两个 writer.add_image() , writer.add_scalar()#添加标量数据 #最后关闭 writer.close() writer.add_scalar("名字",scalar_value对应的是y轴,global_value对应的是x轴) 在终端输入tensorboard --logdir=logs --port=6007 #可以改变端口号避免和别人用一样的端口
from torch.utils.tensorboard import SummaryWriter # 导入类
writer = SummaryWriter("logs") # 实例化类, 对应的事件文件保存在logs文件夹
for i in range(100):
writer.add_scalar("y=x", i, i)
#最后关闭
writer.close()
from torch.utils.tensorboard import SummaryWriter # 导入类
writer = SummaryWriter("logs") # 实例化类, 对应的事件文件保存在logs文件夹
for i in range(100):
writer.add_scalar("y=2x", 2i, i)
#最后关闭
writer.close()
writer.add_image('test',img_arrry,2,dataformats='HWC')
其中img_arrry需要是numpy或者tensor类型,但是用PIL方法读取到的数据是jepg类型。
tensorboard可以在网页可视化图像
# 2是代表第二步
writer.add_image('test',img_arrry,2,dataformats='HWC')
# img_arrry 需要是numpy或者Tensor类型,且形状是chw,否则要转换
图片从pil转换为numpy型
img_arrry = np.array(img)
step从3开始,并且很多图像没显示的,在terminal那里,最后面加一个 --samples_per_plugin=images=1000
from torch.utils.tensorboard import SummaryWriter # 导入类
from PIL import Image
import numpy as np
image_path='dataset/train/bees/29494643_e3410f0d37.jpg'
img = Image.open(image_path) # 获取图片
img_arrry = np.array(img)
print(img_arrry.shape)
writer = SummaryWriter("logs") # 实例化类 保存在logs文件夹
writer.add_image('test',img_arrry,2,dataformats='HWC') # 在项目文件夹中放入图片,可以是自己的也可以是下载数据集
# 用OpenCV获取图片是numpy型
#
# for i in range(100):
# writer.add_scalar("y=2x", 2*i, i) # 在终端输入tensorboard --logdir=logs --port=6007
writer.close()
p10transforms的使用
对图片进行变换
Transforms.py就像一个工具箱,里面有to_tensor、resize等工具
图片经过工具后输出我们想要的一个图片的结果
为什么想使用tensor: tensor包装了我们反向神经网络所需要的理论基础的一些参数
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
# 读取numpy类型
import cv2
cv_image = cv2.imread("dataset/train/ants/49375974_e28ba6f17e.jpg")
print(type(cv_image)) # <class 'numpy.ndarray'>
write = SummaryWriter("logs")
img = Image.open("dataset/train/ants/49375974_e28ba6f17e.jpg") # python内置的一个打开图片的库
print(img) # 可以看一下图片的类型
# <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=500x313 at 0x28BBF148850>
# totensor transforms该如何被使用
trans_totensor = transforms.ToTensor() # 实例化transform里的totensor
# 因为to_tensor是transforms里的类所以要先实例化
img_tensor = trans_totensor(img) # 把图片转换为tensor类型
write.add_image("ant", img_tensor)# 因为write.add_image里numpy面的图片参数要或tensor类型
# tensor包装了我们反向神经网络所需要的理论基础的一些参数
write.close()
p12,13 常见的transforms
不同图片类型各自的打开方式:
transforms.compose把transforms进行组合
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
# 读取numpy类型
import cv2
cv_image = cv2.imread("dataset/train/ants/49375974_e28ba6f17e.jpg")
print(type(cv_image)) # <class 'numpy.ndarray'>
write = SummaryWriter("logs")
img = Image.open("dataset/train/ants/49375974_e28ba6f17e.jpg") # python内置的一个打开图片的库
print(img) # 可以看一下图片的类型
# <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=500x313 at 0x28BBF148850>
# totensor的使用 transforms该如何被使用
trans_totensor = transforms.ToTensor() # 实例化transform里的totensor
# 因为to_tensor是transforms里的类所以要先实例化
img_tensor = trans_totensor(img) # 把图片转换为tensor类型
write.add_image("ant", img_tensor)# 因为write.add_image里numpy面的图片参数要或tensor类型
# tensor包装了我们反向神经网络所需要的理论基础的一些参数
write.close()
# normalize 归一化 灰度直方图的时候有用
# output[channel] = (input[channel] - mean[channel]) / std[channel]归一化的公式
print(img_tensor[0][0][0]) # 原始图片的第【0】【0】【0】位置的像素的输出
trans_norm = transforms.Normalize([0.8, 0.8, 0.8], [0.5, 0.5, 0.5])
# 参数是均值和标准差,因为图片有RGB三个通道所以要三个
img_norm = trans_norm(img_tensor)
write.add_image("norm", img_norm, 2)
print(img_norm[0][0][0])# 归一化后图片的第【0】【0】【0】位置的像素的输出
# resize 改变图片尺寸
print(img.size) # 看一下图片原本大小
trans_resize = transforms.Resize((100, 100)) #实例化transform的resize类,改变大小
img_resize = trans_resize(img) # 输入图片 图片需要是pil类型
print(img_resize) # 看一下img resize 输出类型 结果输出还是PIL类型
# 现在想让输出在transboard显示 需要把图片转换为Tensor类型
img_resize = trans_totensor(img_resize) # 把图片转换为tensor类型
write.add_image("resize", img_resize,1)
print(img_resize)
write.close()
# compose 把不同的transforms连接在一起
trans_resize_2 = transforms.Resize(100) # 先实例化 按照边最短的缩放,等比缩放
trans_compose = transforms.Compose([trans_resize_2, trans_totensor])# 相当于一个组合 先变化大小 再变成Tensor类型
trans_resize_2 = trans_compose(img) # 这里也需要是pil类型
write.add_image("resize", trans_resize_2, 2)
# Randomcrop 随机裁剪,如果只给一个值不会像resize等比缩放,只会是一个正方形
trans_crop = transforms.RandomCrop((100, 100))
trans_compose2 = transforms.Compose([trans_crop, trans_totensor])
for i in range(10):
img_crop = trans_compose2(img)
write.add_image("randomcrop1", img_crop, i)
总结:
使用类里的方法的话要
关注输入和输出类型
多看官方文档
关注方法需要什么参数
不知道返回值的时候 print();print(type());debug;
p14 torchvision中数据集的使用
下载了数据集CIFR10
实现了transforms和datasets的联合使用
# transforms和dataset的联合使用
import torchvision
from torch.utils.tensorboard import SummaryWriter
dataset_transform = torchvision.transforms.Compose(
[
torchvision.transforms.ToTensor()
]
)
# datasets和之前自定义的dataset一样
# 在torchvision里导入CIFAR10数据集
# root 把数据集存放在什么位置, train=True训练数据集,download=True进行一个下载
train_set = torchvision.datasets.CIFAR10(root="./dataset1", train=True, transform=dataset_transform,download=True)
# 测试数据集,download=True
test_set = torchvision.datasets.CIFAR10(root="./dataset1", train=False,transform=dataset_transform, download=True)
# 想看测试集里的第一个数据集
# print(test_set[0]) # 输入有两部分 一部分是图片,一部分是target
# # 可以打一个断点,可以看到train_set里面有的参数
# print(test_set.classes)
# img, target = test_set[0]
# print(img, target)
# print(train_set[0])
# img1, target1 = train_set[0]
# print(img1, target1)
# print(test_set.classes)
# print(test_set.classes[target])
# print(test_set.classes[target1])
# img.show()
# 用tensorboard显示
# 测试集中包含两个数据 一个是图片 一个是类别标签
writer = SummaryWriter("p14")
for i in range(10):
img, target = test_set[i]
writer.add_image("test", img, i)
writer.close() # 一定要记得关闭
如果想自己下载数据集,但是在输出中,没有给出下载路径,可以按住ctrl键点击,查看源码,里面有一个URL,粘贴到迅雷当中下载,之后再把文件拷贝到项目中。
p15dataloader的使用
dataloader相当于一个加载器,把数据加载到神经网络当中
dataset就是告诉我们数据集在什么地方,第一张,第二张数据是什么,这个数据集中共有多少张数据 dataloader batch_size=4每次取牌取四张,shuffle=True是不是打乱,num_workers=0加载数据是采用多进程,还是单进程,drop_last=False最后除不尽是舍去还是不舍
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
# 准备的测试数据集,transform中把图片转为Tensor类型
test_data = torchvision.datasets.CIFAR10(root="./dataset1", train=False, transform=torchvision.transforms.ToTensor(), download=True)
# dataset就是告诉我们数据集在什么地方,第一张,第二张数据是什么,这个数据集中共有多少张数据
# dataloader相当于一个加载器,把数据加载到神经网络当中
# dataloader batch_size=4每次取牌取四张,shuffle=True是不是打乱,num_workers=0加载数据是采用多进程,还是单进程,drop_last=False最后除不尽是舍去还是不舍
test_loader = DataLoader(dataset=test_data, batch_size=4, shuffle=True,num_workers=0, drop_last=False)
# 测试数据中第一张图片及target
# batch_size=4 相当于取test_data[0]、test_data[1]、test_data[2]、test_data[3],
# 对应的img和target分别进行打包,打包成imgs,targets,作为dataloader的一个返回
img, target = test_data[0]
print(img.shape) # 图片大小 3通道 32*32 》》》torch.Size([3, 32, 32])
print(target) # 返回3 >>>3
writer = SummaryWriter("p15")
step = 0
for data in test_loader:
imgs, targets = data # 图片是随机抓取的
# print(imgs.shape) # 图片大小 >>>torch.Size([4, 3, 32, 32])
# print(targets) # >>>tensor([2, 0, 4, 4])
writer.add_images("testp15", imgs, step) #writer.add_images 注意这里加s
step += 1
writer.close()
最后在终端输入:tensorboard --logdir=p15 --port=6007 在tensorboard中显示
dataloader中的imgs是在dataset中随机取得4张图片,targets是对应的四个标签
p16神经网络的基本骨架--nn.module的使用
神经网络的工具都在torch.nn里
container是一个骨架,给神经网络定义了一些结构,在结构中添加不同的内容,就可以组成神经网络
骨架的搭建
container中有6个模块
最常用的是module
初始化函数:菜单栏,code,gennerate, override methods(重写这个方法)
下面写一个简单的神经网络
import torch
from torch import nn
# 搭建自己的神经网络,名字叫Tudui
# containers里面是神经网络的骨架
# 定义神经网络的模板
class Tudui(nn.Module):
def __init__(self) -> None:
super().__init__() # 这个是必须要的,调用父类的初始化函数
def forward(self, input): # 前向传播
output = input + 1
return output # 我们这个神经网络很简单 你给他一个输入 输出就是输入+1
# 创建神经网络
tudui = Tudui() # 相当于拿上面的模板创建出来的网络
x = torch.tensor(1.0)
out = tudui(x)
print(out)
如果想看执行的过程可以打一个断点,进行调试
p17卷积操作
torch.nn是对torch.function的一个封装
CLASStorch.nn.
Conv2d
(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')
torch.nn.functional.
conv2d
(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1)
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]
])
# 因为nn.conv是需要四个参数,改变数据形状
# 卷积的inpute(minibatch,in_channels,高,宽)
input = torch.reshape(input, (1, 1, 5, 5))
# WEIGHT要求,(out_channel,in_channels/groups,高,宽)
kernel = torch.reshape(kernel, (1, 1, 3, 3))
print(input.shape)
print(kernel.shape)
output = F.conv2d(input, kernel, stride=1) # 调用卷积 步长为1,padding默认为0
print(output)
output = F.conv2d(input, kernel, stride=2) # 调用卷积 步长为2
print(output)
output = F.conv2d(input, kernel, stride=1, padding=1) # 调用卷积,padding=1
print(output)
p18卷积层
nn.Conv1d 1维卷积
nn.Conv2d 2维卷积
CLASS
torch.nn.
Conv2d
(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')
dilation=1空洞卷积,bias=True加一个偏置
如果输入通道是1,输出通道是2,那就是有两个卷积核,这两个卷积核不一定是一样的
如果输入通道是3,输出通道是4,是用了几个卷积核??
比较重要的公式,写论文,或者看别人论文的时候,没有给padding和stride,就要自己计算。
vgg16
p19神经网络-最大池化层的使用
nn.maxpool最大池化层,有时也被称为下采样
nn.maxunpool上采样
最常用torch.nn.
MaxPool2d
(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
一般只设置一个kernel_size
stride的默认值和卷积层不一样,它的默认值是kernel_size
dilation:卷积核没有挨在一起,也叫空洞卷积
ceil_mode:floor向下取整,ceiling:向上取整
最大池化操作: kernel_size池化核,取池化核覆盖之下最大的数
ceil允许有出界部分,floor不允许
池化操作的输入也是需要4维,(batchsize,通道数,高,宽)
上面的矩阵进行池化操作,最后输出池化后的结果。
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) # 最大池化没有办法对Long类型操作,需要转换一下类型
# 因为nn.conv是需要四个参数,改变数据形状,满足输入要求
input = torch.reshape(input, (-1, 1, 5, 5))
# 神经网络
class Tudui(nn.Module):
def __init__(self) -> None:
super().__init__()
# 最大池化层
self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=True)
def forward(self,input):
output = self.maxpool1(input)
return output # 神经网络就搭建好了
# 创建神经网络
tudui = Tudui()
# 给神经网络一个输入
output = tudui(input)
print(output)
输出:
池化有什么作用呢:保留输入特征,但降低数据量
可以输入图片看一下
import torch
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(root="./dataset1", train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(dataset=dataset, batch_size=64)
# 神经网络
class Tudui(nn.Module):
def __init__(self) -> None:
super().__init__()
# 最大池化层
self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=True)
def forward(self,input):
output = self.maxpool1(input)
return output # 神经网络就搭建好了
# 创建神经网络
tudui = Tudui()
# 在tensboard显示
writer = SummaryWriter("p19")
# 从dataloader取数据
step = 0
for data in dataloader:
imgs, target = data
output = tudui(imgs)
writer.add_images("input1", imgs, step)
writer.add_images("output1",output, step)
step = step + 1
writer.close()
经过池化操作后,图片会变得有点模糊,神经网络当中训练的数据量就会大大减少,加快训练。
所以一般都会在经过卷积之后,进行池化,然后进行一次线性激活。
p20非线性激活
给神经网络引入一些非线性的特质,先看RELU怎么使用的
torch.nn.
ReLU
(inplace=False)
inplace参数是否原地操作的意思,是否对原来的变量改变,默认是False
输入,输出类型:输入是(batch_size,*)
输入一个张量,看进行非线性激活ReLU的结果
import torch
from torch.nn import ReLU
from torch import nn
input = torch.tensor([[1, -0.5],
[-1, 3]])
input = torch.reshape(input, (-1,1,2,2))
print(input.shape)
class Tudui(nn.Module):
def __init__(self):
super(Tudui,self).__init__()
self.relu1 = ReLU()
def forward(self,input):
output = self.relu1(input)
return output
# 创建网络
tudui = Tudui()
output = tudui(input)
print(input)
print(output)
用图片展示sigmoid激活函数
import torch
from torch.nn import ReLU, Sigmoid
from torch import nn
from torch.utils.data import DataLoader
import torchvision
from torch.utils.tensorboard import SummaryWriter
# 导入数据集
dataset = torchvision.datasets.CIFAR10(root="./dataset1", train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(dataset=dataset, batch_size=64)
# 搭建网络
class Tudui(nn.Module):
def __init__(self):
super(Tudui,self).__init__()
self.relu1 = ReLU()
self.sigmod1 = Sigmoid()
def forward(self,input):
output = self.sigmod1(input)
return output
# 创建网络
tudui = Tudui()
# 在tensboard显示
writer = SummaryWriter("p20")
step = 0
for data in dataloader:
imgs, target = data
output = tudui(imgs)
writer.add_images("input", imgs, step)
writer.add_images("output",output, step)
step = step + 1
writer.close()
step从3开始,并且很多图像没显示的,在terminal那里,最后面加一个
--samples_per_plugin=images=1000
即 tensorboard --logdir=p20 --samples_per_plugin=images=1000
p21神经网络-----线性层及其它层介绍
normalization layers(归一化)的作用:加快神经网络的训练速度
transformer layers
linear layers
dropout layers:防止过拟合,比较简单可以拿来练手,看有没有看懂官方文档
embedding layers:自然语言处理用到的,
distance function:计算误差
重点说一下线性层:
import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10(root="./dataset1", train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(dataset=dataset, batch_size=64,drop_last=True)
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.liner1 = Linear(196608,10)# 输入feature, 输出feature
def forward(self, input):
output = self.liner1(input)
return output
# 搭建网络
tudui = Tudui()
for data in dataloader:
imgs, target = data
print(imgs.shape) #torch.Size([64, 3, 32, 32]) 64张3通道32*32的图片
# output =torch.reshape(imgs,(1,1,1,-1)) # 把图片展平了
output = torch.flatten(imgs) # 展平变成一行 torch.Size([196608])
print(output.shape)# torch.Size([1, 1, 1, 196608])
output = tudui(output)
print(output.shape) #torch.Size([1, 1, 1, 10]) #之前产生了报错 把dataloader里的drop_last=True
p22搭建小实战和Sequential的使用
container中的Sequential还没讲
结合CIFAR10来讲解Sequential
第一层卷积,输入是3*32*32,输出是32*32*32,从3通道变成了32通道
- 卷积核也是3通道的,对应计算之后加权结果是1通道,也就是32个卷积核产生32通道;
- 输出通道数等于卷积核个数,与输入通道数无关
- 经过卷积图片的大小没变,还是32*32,可以通过公式计算参数值
没有用Sequential
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.conv1 = Conv2d(in_channels=3, out_channels=32, kernel_size=5,padding=2)
# 输出还是32*32,没有改变尺寸,肯定有padding,根据卷积公式计算padding
self.maxpool1 = MaxPool2d(kernel_size=2)
self.conv2 = Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding=2)
self.maxpool2 = MaxPool2d(kernel_size=2)
self.conv3 = Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2)
self.maxpool3 = MaxPool2d(kernel_size=2)
self.flatten = Flatten()# 展平
self.liner1 = Linear(in_features=1024,out_features=64)
# 最后生成10个类别所以out_features=10
self.liner2 = Linear(in_features=64, out_features=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.liner1(x)
x = self.liner2(x)
return x
tudui = Tudui()
print(tudui)
# Tudui(
# (conv1): Conv2d(3, 5, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
# (maxpool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
# (conv2): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
# (maxpool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
# (conv3): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
# (maxpool3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
# (flatten): Flatten(start_dim=1, end_dim=-1)
# (liner1): Linear(in_features=1024, out_features=64, bias=True)
# (liner2): Linear(in_features=64, out_features=10, bias=True)
# )
# 测试网络对不对
input = torch.ones((64, 3, 32, 32))
output = tudui(input)
print(output.shape) # torch.Size([64, 10])一张图片第10 但是有64张图片
使用Sequential,测试网络对不对,除了print,也可以在tensorboard中显示
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2),
# 输出还是32*32,没有改变尺寸,肯定有padding,根据卷积公式计算padding
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Flatten(), # 展平
Linear(in_features=1024, out_features=64),
# 最后生成10个类别所以out_features=10
Linear(in_features=64, out_features=10)
)
def forward(self, x):
x = self.model1(x)
return x
tudui = Tudui()
print(tudui)
# Tudui(
# (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)
# )
# )
# 测试网络对不对
input = torch.ones((64, 3, 32, 32))
output = tudui(input)
print(output.shape)# torch.Size([64, 10])一张图片第10 但是有64张图片
# 除了print,也可以在tensorboard中显示
writer = SummaryWriter("p22")
writer.add_graph(tudui,input)
writer.close()
p23损失函数与反向传播
loss funcion
- 计算实际输出和目标之间的差距
- 为我们更新输出提供一定的依据(反向传播)
- 注意输入和输出的类型
import torch
from torch.nn import L1Loss
from torch import nn
input = torch.tensor([1, 2, 3], dtype=float)
target = torch.tensor([1, 2, 5], dtype=float)
# 因为想要的输入是由batch_size (1batch_size,1channel,1*3)
input = torch.reshape(input, (1, 1, 1, 3))
target = torch.reshape(target,(1, 1, 1, 3))
# L1损失函数 一定要关注输入的大小
loss = L1Loss()
result = loss(input, target)
print(result)
# mse损失函数,平方差
loss_mse = nn.MSELoss()
result_mse = loss_mse(input, target)
print(result_mse)
# 交叉熵CrossEntropyLoss适用于分类
x = torch.tensor([0.1, 0.2, 0.3])
y = torch.tensor([1])
# 前面要求输入是(N,C)c是有多少类的意思
x = torch.reshape(x, (1, 3))# 1batch_size,有3类
loss_cross = nn.CrossEntropyLoss()
result = loss_cross(x, y)
print(result)
在网络中用损失函数
result_loss.backward()计算反向传播,知道每个结点的梯度
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
# 导入数据集
dataset = torchvision.datasets.CIFAR10(root="./dataset1", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset=dataset, batch_size=4)
# 新建的神经网络
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2),
# 输出还是32*32,没有改变尺寸,肯定有padding,根据卷积公式计算padding
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Flatten(), # 展平
Linear(in_features=1024, out_features=64),
# 最后生成10个类别所以out_features=10
Linear(in_features=64, out_features=10)
)
def forward(self, x):
x = self.model1(x)
return x
loss = nn.CrossEntropyLoss()
tudui = Tudui()
for data in dataloader:
imgs, targets = data # 图片是随机抓取的
outputs = tudui(imgs) # 通过神经网络得到的一个输出
# # 我们看一下outputs和targets长什么样子,看选择什么样的损失函数
# print(outputs)
# print(targets)
result_loss = loss(outputs,targets)
result_loss.backward() # 反向传播,计算梯度
print("ok")
p24优化器
在设置优化器的时候基本上就是设置一个模型参数和一个学习率。
一开始使用大的学习率,后面使用小的学习率学习
result_loss.backward()计算反向传播,知道每个结点的梯度
有了这个梯度我们就可以使用优化器,调整参数
在官方文档找优化器
有很多不同的优化器
优化器就是:
- 先定义一个优化器,optim = torch.optim.SGD(tudui.parameters(), lr=0.01)
- 然后把每个参数的梯度都清零,optim.zero_grad()
- 调用损失函数的backward(反向传播)求出每个节点的梯度result_loss.backward()
- 最后调用optim.step()对每个参数进行调优,optim.step()
loss和优化器进行关联
- 你对optim进行step操作时,step就会把它自身反向传播后的结果得用进去
- loss函数在其中只是起到了一个提供梯度的作用,而这个梯度就藏在optim中
先复制上一节有损失函数的代码,再加上优化器
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
# 导入数据集
dataset = torchvision.datasets.CIFAR10(root="./dataset1", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset=dataset, batch_size=1)
# 新建的神经网络
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2),
# 输出还是32*32,没有改变尺寸,肯定有padding,根据卷积公式计算padding
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Flatten(), # 展平
Linear(in_features=1024, out_features=64),
# 最后生成10个类别所以out_features=10
Linear(in_features=64, out_features=10)
)
def forward(self, x):
x = self.model1(x)
return x
# 损失函数
loss = nn.CrossEntropyLoss()
tudui = Tudui()
# 设置优化器,用随机梯度下降
# torch.optim.SGD(params, lr=<required parameter>, momentum=0, dampening=0, weight_decay=0, nesterov=False)
optim = torch.optim.SGD(tudui.parameters(), lr=0.01)
for epoch in range(20):
running_loss = 0.0
# 这个循环只是对这个循环的一轮学习
for data in dataloader:
imgs, targets = data # 图片是随机抓取的
outputs = tudui(imgs) # 通过神经网络得到的一个输出
result_loss = loss(outputs, targets) # 得到输出和真实数据的一个差距
optim.zero_grad()# 把参数的梯度设置为0
result_loss.backward() # 反向传播,计算梯度 得到了每个可以调节参数的梯度
optim.step()# 对每个参数进行调优
# print(result_loss) # 每一个结点上的loss值
running_loss = running_loss + result_loss# 每一轮总体误差的总和
print(running_loss)
p25pytorch给我们提供的网络模型
这些模型该如何使用和修改,
本节介绍了torchvision中 的模型
本节讲解分类模型VGG16
torchvision.models.
vgg16
(pretrained: bool = False, progress: bool = True, **kwargs) → torchvision.models.vgg.VGG
Parameters: |
---|
pretrained= True,说明已经在训练集上把参数训练好了
导入ImageNet数据集,提示报错这个数据集不公开,需要自己手动下载,放到根目录中。
数据集太大,up没有进行展示
split="train"是要训练还是验证
# 一个点是当前路径,split="train" 是要训练集还是测试集
train_data = torchvision.datasets.ImageNet(root="./dataImgNet",split="train", download=True,
transform=torchvision.transforms.ToTensor())
imagenet是1000个类别,如何把用这个1000个类别的模型用到10分类上
如何使用vgg16这种可以分1000中类别的网络分类CIFAR10这种10类别的
- 方法1:向其中加入一个线性层把1000输出为10类别
vgg16_true.add_module('add_linear',nn.Linear(1000,10))
- 方法2:改变现有模型的线性层使其输出10类别
vgg16_false.classifier[6] = nn.Linear(4096,10)
全部代码
import torchvision
from torch import nn
# # 一个点是当前路径,split="train" 是要训练集还是测试集
# train_data = torchvision.datasets.ImageNet(root="./dataImgNet",split="train", download=True,
# transform=torchvision.transforms.ToTensor())
# pretrained=False只是加载网络模型,只是把代码加载了,其中的参数不用下载
vgg16_false = torchvision.models.vgg16(pretrained=False,progress=True)
# pretrained=True,需要下载卷积层对应的参数是多少,池化层对应的参数,这些参数就是刚刚说的在ImageNet数据集中训练好的
vgg16_true = torchvision.models.vgg16(pretrained=True,progress=True)
# print(vgg16_true) # 可以分1000种类别
# 把数据分成了10类,之前的vgg16是把数据分成了1000个类别,那我们如何应用这个模型
train_data = torchvision.datasets.CIFAR10(root="./dataset1", train=True, transform=torchvision.transforms.ToTensor(),
download=True)
print("-----------------------------------------------------------")
# 改动现有的网络结构
# 方法1:加一层
vgg16_true.add_module('add_linear',nn.Linear(1000,10))
# 输出添加后的网络
print(vgg16_true)
print("-----------------------------------------------------------")
# 如果想在这里加(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)
# )
vgg16_true.classifier.add_module('add_linear',nn.Linear(1000,10))
# 输出添加后的网络
print(vgg16_true)
print("-----------------------------------------------------------")
# 方法2 修改模型参数,改成输入是4096,输出是10个特征
vgg16_false.classifier[6] = nn.Linear(4096,10)
print(vgg16_false)
p26网络模型的保存与读取
后面的讲解内容:
- 模型的保存,加载
- 完整的模型训练套路-GPU训练
- 完整的模型验证套路(测试、demo)套路,利用训练好的模型,然后给它提供输入
两种方式保存和加载模型
方式1,保存模型
先定义了一个模型,然后保存
# 保存方式1,不仅保存了网络模型的结构,也保存了网络模型中的一些参数
vgg16 = torchvision.models.vgg16(pretrained=False)
# 保存方式1,不仅保存了网络模型的结构,也保存了网络模型中的一些参数
torch.save(vgg16, "vgg16_method1.pth")
方式1,对应加载模型
# 方式1 --》保存方式1,加载模型
model = torch.load("vgg16_method1.pth")# 指定路径
print(model)
方式2,保存模型,官方推荐的保存方式
不保存模型结构只保存模型参数
vgg16 = torchvision.models.vgg16(pretrained=False)
# 保存方式2,不保存模型的结构,只保存了模型的参数
# 参数保存成字典的形式
# 官方推荐的保存方式
torch.save(vgg16.state_dict(),"vgg16_method2.pth")
# 把vgg16保存成字典的形式
方式2,对应的加载模型
# 方式2
model = torch.load("vgg16_method2.pth")
print(model) #输出只有字典
上面只有参数,如果想恢复网络模型,就要新建模型结构,然后再加载保存的字典
# 方式2 --》保存方式2,加载模型
# 想通过字典找到网络结构
vgg16 =torchvision.models.vgg16(pretrained=False)
# 获取状态
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
# model = torch.load("vgg16_method2.pth")
print(vgg16)
如果要用方式1的话,要让程序能够访问到模型定义的方式
可以在加载模型的.py文件中加上
from p26_moldel_save import * # 把所有的都导入过来
方式1的陷阱
在一个.py文件中,先定义一个神经网络
# 陷阱
class Tudui(nn.Module):
def __init__(self):
super(Tudui,self).__init__()
self.conv1 = nn.Conv2d(3, 64,kernel_size=3)
def forward(self,x):
x = self.conv1(x)
return x
tudui = Tudui()
torch.save(tudui,"tudui_method1.pth")
陷阱,在另一个.py文件中加载出问题
# 陷阱,会出问题
model =torch.load('tudui_method1.pth')
print(model)
解决办法:需要把Tudui这个类放到这里来,但是不需要创建对象tudui = Tudui()
# 需要把Tudui这个类放到这里来,但是不需要创建对象tudui = Tudui()
class Tudui(nn.Module):
def __init__(self):
super(Tudui,self).__init__()
self.conv1 = nn.Conv2d(3, 64,kernel_size=3)
def forward(self,x):
x = self.conv1(x)
return x
model =torch.load('tudui_method1.pth')
print(model)
# Tudui(
# (conv1): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1))
# )
实际项目中的解决办法在加载模型的.py文件中 加上from p26_moldel_save import *# 把所有的都导入过来
# 也不算一个陷阱,真正写项目当中,不会把模型复制来复制去,会定义在一个单独的文件夹
from p26_moldel_save import *# 把所有的都导入过来
model =torch.load('tudui_method1.pth')
print(model)
p27、28、29完整的模型训练套路
训练
1.准备数据集,并知道数据集的长度:
2.利用dataloader加载数据集
3.搭建神经网络
验证模型输入时 64个batchsize,3通道,图片大小是32*32
4.在训练文件夹引入model.py文件夹,创建网络模型
model.py和train.py在一个文件夹下
5.定义损失函数和优化器
6.设置训练网络的参数
7.训练
验证:如何知道训练一轮之后,模型的效果
在每轮验证之后,测试
不进行调优,只测试
改进测试输出:
在tensorboard显示
分类问题中的正确率
tudui.train()# 对特定层有作用dropout 训练开始时
tudui.eval()# 对特定层有作用dropout等 测试开始时
总结完整过程:
准备数据集
准备dataloader
创建网络模型、损失函数、优化器
设置一些训练当中的参数
设置训练的轮数epoch
tudui.train()# 对特定层有作用dropout让网络进入训练状态 # 没有梯度,这里是用在测试数据集中的。 with torch.no_grad():
以CIFAR10作为本节课的例子,完成对这个数据集的分类问题
model.py一定要和你的主文件在同一个文件夹下
模型单独在一个.py文件中
# 搭建神经网络
import torch
from torch import nn
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model = nn.Sequential(
nn.Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=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(64 * 4 * 4, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
# to test if the neural network is right 主方法
# 在这里测试这个网络的正确性
if __name__ == '__main__':
tudui = Tudui() #创造一个网络模型
# 验证模型的准确性
# 给定一个确定的尺寸,看输出的尺寸是不是我们想要的
input = torch.ones((64, 3, 32, 32)) # 64个batch_size,3个通道,32*32的
output = tudui(input)
# 输出torch.Size([64, 10]) 说明有64行 10列,64说明有64张图片 10列每一列是是那个种类的概率
print(output.shape)
其他代码
1. 一般会把loss写成loss.item(),加上loss.item()转换为一个真实的数,不带tensor()
# 计算正确率,1是指横向比较
2.outputs.argmax(1)横向最大,argmax可以求出横向的最大值所在位置
accuracy = (outputs.argmax(1) == targets).sum() # 括号里输出的值会是 ture false,然后false相当于0,ture相当于1,求和
完整代码
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
# 把模型中所有东西都引入过来
# 引入网络
from model import *
# 准备数据集
# 训练数据集
train_data = torchvision.datasets.CIFAR10(root="./dataset1", train=True, transform=torchvision.transforms.ToTensor(),
download=True)
# 测试数据集
test_data = torchvision.datasets.CIFAR10(root="./dataset1", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
# length 训练数据集有多少张 新知识点 如何获得数据集的长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# if train_data_size=10,训练数据集的长度为:10
print(f"训练数据集的长度为:{train_data_size}") # 这个写法是常用的字符串格式化
print("测试数据集的长度为:{}".format(test_data_size))
# load dataset with DataLoder
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
# 把模型剪切到了model中
# 创建网络模型
tudui = Tudui()
# 创建损失函数,也放在nn包里,分类问题可以用交叉熵
loss_fn = nn.CrossEntropyLoss()
# 定义优化器
# 学习速率
# learning_rate = 0.01
# 1e-2 = 1 x (10)^(-2) = 1/100 = 0.01
learning_rate = 1e-2
# 随机梯度下降优化器,参数是 对哪一个部分进行优化,网络模型.parameters 学习速率
optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate)
# 接下来开始训练网络
# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练轮数,循环10轮
epoch = 10
# 添加tensorboard
writer = SummaryWriter("logs_train")
# i从0一直到9 训练10轮
for i in range(epoch):
print("------第{} 轮开始------".format(i + 1))
# 训练网络模型开始
# 从训练数据中取数据
# 训练步骤开始
tudui.train()# 对特定层有作用dropout
for data in train_dataloader:
imgs, targets = data
outputs = tudui(imgs) # 把训练数据放到网络当中
# 输出和真实目标的误差是多少
# 参数是预测的输出的真实的目标放进去
loss = loss_fn(outputs, targets)
# 优化器优化模型
# 优化 梯度清零,为了上次的梯度不会影响这次的优化
optimizer.zero_grad()
# 调用损失函数的反向传播,得到每个参数结点的梯度
loss.backward()
# 对参数进行优化
optimizer.step() # 到此就是对参数进行了一次优化,也就是做了一次训练
# 训练次数加1,
# loss.item() 如果a= torch.tensor(5) ptint(a) 得到tensor(5)
#print(a.item()) 得到 5
total_train_step = total_train_step + 1
# 一般会把loss写成loss.item(),,加上loss.item()转换为一个真实的数,不带tensor()
#print(f"训练次数是:{total_train_step}时, 损失值是:{loss}")
# 不想全部输出 没次100的倍数输出
if total_train_step % 100 == 0:
print(f"训练次数是:{total_train_step}时, 损失值是:{loss.item()}")
writer.add_scalar("train_loss", loss.item(), total_train_step)
# 怎么知道有没有达到训练的需求
# 测试不用对模型进行调优了 就是对现有模型的测试
# 测试步骤开始
tudui.eval()# 对特定层有作用dropout等
total_test_loss = 0 # 求整个测试集上的loss
total_accuracy = 0 # 整体正确率
# 没有梯度
with torch.no_grad():
for data in test_dataloader:
imgs, targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs, targets)
# 相求整个测试集上的loss
total_test_loss = total_test_loss + loss.item()
# 计算正确率,1是指横向比较
accuracy = (outputs.argmax(1) == targets).sum() # 括号里输出的值会是 ture false,然后false相当于0,ture相当于1,求和
total_accuracy = 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/test_data_size, total_test_step)
total_test_step = total_test_step + 1
# 保存每一轮的模型
torch.save(tudui, "tudui_{}.pth".format(i))
print("模型已保存")
writer.close()
p30利用GPU训练
2种方式使用gpu
第一种使用GPU的方式
1.找到网络模型tudui、数据(输入、标注)imgs,tragets、损失函数loss_fun,再调用.cuda()进行返回就可以。优化器和数据集(train_dataset)是没有cuda的
tudui = tudui.cuda() loss_fn = loss_fn.cuda() imgs = imgs.cuda() targets = targets.cuda()
是否有cuda
用来计时
import time
start_time = time.time()
end_time = time.time()
print("时间", end_time-start_time)
自己电脑没gup的可以去colab(需要梯子)、kaggle、阿里云天池 极光
google.colab 新建笔记本
kaggle跑项目
文件》修改》笔记本设置
完整代码
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import time #用来计时
# 准备数据集
# 训练数据集
train_data = torchvision.datasets.CIFAR10(root="./dataset1", train=True, transform=torchvision.transforms.ToTensor(),
download=True)
# 测试数据集
test_data = torchvision.datasets.CIFAR10(root="./dataset1", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
# length 训练数据集有多少张 新知识点 如何获得数据集的长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# if train_data_size=10,训练数据集的长度为:10
print(f"训练数据集的长度为:{train_data_size}") # 这个写法是常用的字符串格式化
print("测试数据集的长度为:{}".format(test_data_size))
# load dataset with DataLoder
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
# 把模型剪切到了model中
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model = nn.Sequential(
nn.Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=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(64 * 4 * 4, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
# 创建网络模型
tudui = Tudui()
tudui = tudui.cuda()
# 创建损失函数,也放在nn包里,分类问题可以用交叉熵
loss_fn = nn.CrossEntropyLoss()
loss_fn = loss_fn.cuda()
# 定义优化器
# 学习速率
# learning_rate = 0.01
# 1e-2 = 1 x (10)^(-2) = 1/100 = 0.01
learning_rate = 1e-2
# 随机梯度下降优化器,参数是 对哪一个部分进行优化,网络模型.parameters 学习速率
optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate)
# 接下来开始训练网络
# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练轮数,循环10轮
epoch = 10
# 添加tensorboard
writer = SummaryWriter("p30logs_train")
start_time = time.time()
# i从0一直到9 训练10轮
for i in range(epoch):
print("------第{} 轮开始------".format(i + 1))
# 训练网络模型开始
# 从训练数据中取数据
# 训练步骤开始
tudui.train()# 对特定层有作用dropout
for data in train_dataloader:
imgs, targets = data
imgs = imgs.cuda()
targets = targets.cuda()
outputs = tudui(imgs) # 把训练数据放到网络当中
# 输出和真实目标的误差是多少
# 参数是预测的输出的真实的目标放进去
loss = loss_fn(outputs, targets)
# 优化器优化模型
# 优化 梯度清零,为了上次的梯度不会影响这次的优化
optimizer.zero_grad()
# 调用损失函数的反向传播,得到每个参数结点的梯度
loss.backward()
# 对参数进行优化
optimizer.step() # 到此就是对参数进行了一次优化,也就是做了一次训练
# 训练次数加1,
# loss.item() 如果a= torch.tensor(5) ptint(a) 得到tensor(5)
#print(a.item()) 得到 5
total_train_step = total_train_step + 1
# 一般会把loss写成loss.item(),,加上loss.item()转换为一个真实的数,不带tensor()
#print(f"训练次数是:{total_train_step}时, 损失值是:{loss}")
# 不想全部输出 没次100的倍数输出
if total_train_step % 100 == 0:
end_time = time.time()
print("时间", end_time-start_time)
print(f"训练次数是:{total_train_step}时, 损失值是:{loss.item()}")
writer.add_scalar("train_loss", loss.item(), total_train_step)
# 怎么知道有没有达到训练的需求
# 测试不用对模型进行调优了 就是对现有模型的测试
# 测试步骤开始
tudui.eval()# 对特定层有作用dropout等
total_test_loss = 0 # 求整个测试集上的loss
total_accuracy = 0 # 整体正确率
# 没有梯度
with torch.no_grad():
for data in test_dataloader:
imgs, targets = data
imgs = imgs.cuda()
targets = targets.cuda()
outputs = tudui(imgs)
loss = loss_fn(outputs, targets)
# 相求整个测试集上的loss
total_test_loss = total_test_loss + loss.item()
# 计算正确率,1是指横向比较
accuracy = (outputs.argmax(1) == targets).sum() # 括号里输出的值会是 ture false,然后false相当于0,ture相当于1,求和
total_accuracy = 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/test_data_size, total_test_step)
total_test_step = total_test_step + 1
# 保存每一轮的模型
torch.save(tudui, "tudui_{}.pth".format(i))
print("模型已保存")
writer.close()
第二种GPU训练的方式
.to(device)
Device = torch.device("cpu")
torch.device("cuda")
torch.device("cuda:0") 看有几张显卡
# 定义训练的设备
device = torch.device("cuda:0")/device = torch.device("cpu")
tudui = tudui.to(device)
loss_fn = loss_fn.to(device)
imgs = imgs.to(device)
targets = targets.to(device)
对比第一种使用GPU的方法
tudui = tudui.cuda()
loss_fn = loss_fn.cuda()
imgs = imgs.cuda()
targets = targets.cuda()
全部代码
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import time #用来计时
# 定义训练的设备
device = torch.device("cuda:0")
print(device)
# 准备数据集
# 训练数据集
train_data = torchvision.datasets.CIFAR10(root="./dataset1", train=True, transform=torchvision.transforms.ToTensor(),
download=True)
# 测试数据集
test_data = torchvision.datasets.CIFAR10(root="./dataset1", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
# length 训练数据集有多少张 新知识点 如何获得数据集的长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# if train_data_size=10,训练数据集的长度为:10
print(f"训练数据集的长度为:{train_data_size}") # 这个写法是常用的字符串格式化
print("测试数据集的长度为:{}".format(test_data_size))
# load dataset with DataLoder
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
# 把模型剪切到了model中
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model = nn.Sequential(
nn.Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=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(64 * 4 * 4, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
# 创建网络模型
tudui = Tudui()
tudui = tudui.to(device)
# 创建损失函数,也放在nn包里,分类问题可以用交叉熵
loss_fn = nn.CrossEntropyLoss()
loss_fn = loss_fn.to(device)
# 定义优化器
# 学习速率
# learning_rate = 0.01
# 1e-2 = 1 x (10)^(-2) = 1/100 = 0.01
learning_rate = 1e-2
# 随机梯度下降优化器,参数是 对哪一个部分进行优化,网络模型.parameters 学习速率
optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate)
# 接下来开始训练网络
# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练轮数,循环10轮
epoch = 10
# 添加tensorboard
writer = SummaryWriter("p30logs_train")
start_time = time.time()
# i从0一直到9 训练10轮
for i in range(epoch):
print("------第{} 轮开始------".format(i + 1))
# 训练网络模型开始
# 从训练数据中取数据
# 训练步骤开始
tudui.train()# 对特定层有作用dropout
for data in train_dataloader:
imgs, targets = data
imgs = imgs.to(device)
targets = targets.to(device)
outputs = tudui(imgs) # 把训练数据放到网络当中
# 输出和真实目标的误差是多少
# 参数是预测的输出的真实的目标放进去
loss = loss_fn(outputs, targets)
# 优化器优化模型
# 优化 梯度清零,为了上次的梯度不会影响这次的优化
optimizer.zero_grad()
# 调用损失函数的反向传播,得到每个参数结点的梯度
loss.backward()
# 对参数进行优化
optimizer.step() # 到此就是对参数进行了一次优化,也就是做了一次训练
# 训练次数加1,
# loss.item() 如果a= torch.tensor(5) ptint(a) 得到tensor(5)
#print(a.item()) 得到 5
total_train_step = total_train_step + 1
# 一般会把loss写成loss.item(),,加上loss.item()转换为一个真实的数,不带tensor()
#print(f"训练次数是:{total_train_step}时, 损失值是:{loss}")
# 不想全部输出 没次100的倍数输出
if total_train_step % 100 == 0:
end_time = time.time()
print("时间", end_time-start_time)
print(f"训练次数是:{total_train_step}时, 损失值是:{loss.item()}")
writer.add_scalar("train_loss", loss.item(), total_train_step)
# 怎么知道有没有达到训练的需求
# 测试不用对模型进行调优了 就是对现有模型的测试
# 测试步骤开始
tudui.eval()# 对特定层有作用dropout等
total_test_loss = 0 # 求整个测试集上的loss
total_accuracy = 0 # 整体正确率
# 没有梯度
with torch.no_grad():
for data in test_dataloader:
imgs, targets = data
imgs = imgs.to(device)
targets = targets.to(device)
outputs = tudui(imgs)
loss = loss_fn(outputs, targets)
# 相求整个测试集上的loss
total_test_loss = total_test_loss + loss.item()
# 计算正确率,1是指横向比较
accuracy = (outputs.argmax(1) == targets).sum() # 括号里输出的值会是 ture false,然后false相当于0,ture相当于1,求和
total_accuracy = 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/test_data_size, total_test_step)
total_test_step = total_test_step + 1
# 保存每一轮的模型
torch.save(tudui, "tudui_{}.pth".format(i))
print("模型已保存")
writer.close()
有的gpu也这么写
device=torch.device("cuda")
device=torch.device("cuda:0")
device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
p32完整的模型验证套路 test
利用已经训练好的模型给他提供输入
想要知道自己的输出
output对应的是哪个类别,可以在test_data处打一个断点,debug一下。
import torch
import torchvision.transforms
from PIL import Image
from torch import nn
# 两个点是上一层级,一个点是这一层级
imge_path = "./imgs/dog.jpg"
img = Image.open(imge_path) # PIL类型的图片
# 把几个变换联立在一起
# 先改变图片的大小
transform =torchvision.transforms.Compose(
[torchvision.transforms.Resize((32, 32)),
torchvision.transforms.ToTensor()]
) # compose() 里面写多种变形联立的时候,应该加上[]
image = transform(img)
print(image.shape) #torch.Size([3, 32, 32])
# 拷贝网络模型
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model = nn.Sequential(
nn.Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=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(64 * 4 * 4, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
# 加载网络模型
model = torch.load("tudui_5.pth")
print(model)
image = torch.reshape(image,(1,3,32,32))
image = image.cuda()
# 模型转化为测试类型
model.eval()
# 可以节约内存 性能
with torch.no_grad():
output = model(image)
print(output) #tensor([[-2.5105, -4.0868, 4.6670, -0.0257, 0.2603, 2.8155, -0.0162, 2.4543,
#-3.6706, -1.8582]], device='cuda:0')
print(output.argmax(1))# 最大的那个数在哪里