PyTorch 的基础操作
1 张量
- 张量如同数组和矩阵一样,即一种特殊的数据结构。
- 多作为 pytorch 中,神经网络的输入、输出以及网格的参数等数据,都用张量来描述
- 张量的使用 和 numpy 的 ndarrays 类似,区别在于张量可以在GPU或者其他专用硬件上运行,以达到更快的效果
1.1 张量初始化与创建
# 初始化张量
'''
张量与numpy的数组最大的区别在于张量可以在gpu上运行
'''
data = [[1,2],[3,4]]
x_data= torch.tensor(data)
# numpy 初始化张量
from turtle import shape
import numpy as np
np_array = np.array(data)
x_np = torch.from_numpy(np_array)
# 使用张量去创建张量
# 新的张量将继承已有张量的数据属性(结构、类型),也可重新定义新的数据类型
x_ones = torch.ones_like(x_data) # 创建一个与x_data框架一样的全1张量保留 x_data的属性
print(f"Ones Tensor:\n {x_ones} \n")
x_rand = torch.rand_like(x_data, dtype=torch.float) # 创建一个与x_data框架一样的随机张量,重写 x_data 的数据类型 int——> float
print(f"Random Tensor: \n {x_rand} \n")
print("-----------------")
# 指定数据维度来生成张量
# shape 是元组类型,用来描述张量的维数,以下通过shape 来指定生成张量的维数
shape = (2,3,) # 元组必须写最后一个逗号
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)
print(f"rand_tensor:\n {rand_tensor} \n")
print(f"ones_tensor:\n {ones_tensor} \n")
print(f"zeros_tensor:\n {zeros_tensor} \n")
# 张量的属性
# 从属性上我们可以得到张量的维数、数据类型以及它们所存储的设备(CPU、GPU)
tensor = torch.rand(3,4)
print(f"Shape : {tensor.shape}") # 维数
print(f"Datetype :{tensor.dtype}") # 数据类型
print(f"Device tensor is stored on : {tensor.device}") # 存储设备
print("-------------")
# view 操作可改变张量的维度
x = torch.randn(4,4)
y = x.view(16) # 以16为维度赋值
z = x.view(-1,8) # -1 表示自动计算,4*4 / 8 = 2
print(x.size(), y.size(), z.size()) # 输出其维度
输出结果:
Ones Tensor:
tensor([[1, 1],
[1, 1]])
Random Tensor:
tensor([[0.8275, 0.6488],
[0.4253, 0.0760]])
-----------------
rand_tensor:
tensor([[0.6493, 0.7338, 0.3523],
[0.6045, 0.2229, 0.1748]])
ones_tensor:
tensor([[1., 1., 1.],
[1., 1., 1.]])
zeros_tensor:
tensor([[0., 0., 0.],
[0., 0., 0.]])
Shape : torch.Size([3, 4])
Datetype :torch.float32
Device tensor is stored on : cpu
-------------
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
1.2 张量运算操作
#索引与切片
from re import T
tensor = torch.ones(4,4)
tensor[:,1] = 0
print(tensor)
# 张量的拼接
t1 = torch.cat([tensor, tensor, tensor], dim=1) # 表示限定维数为1维,tensor 会按顺序依次排列成一维
print(t1)
print("-----------")
# 张量的乘积与矩阵的乘积
# 逐个元素相乘
print(f"tensor.mul(tensor):\n {tensor.mul(tensor)}\n")
# 等价写法
print(f"tensor * tensor : \n {tensor * tensor}")
#矩阵乘积
print(f"tensor.matmul(tensor.T): \n{tensor.matmul(tensor.T)} \n")
#等价写法
print(f"tensor @ tensor.T :\n {tensor @ tensor.T} \n" )
print("-----------")
# 自动赋值运算
'''
自动赋值运算通常在方法后有 _ 作为后缀,如 x.copy_(y), x.t_() 操作会改变x的取值
'''
print(tensor, "\n")
tensor.add_(5) # 每个元素加5 ,并改变tensor 的值
print(tensor)
tensor.add(1)
print(tensor)
PyTorch 的GPU环境启动
import torch
tensor = torch.ones(4,4)
tensor[:,1] = 0
print(tensor)
# 调用 GPU环境
# 判断当前环境GPU是否可用,然后将tensor导入GPU内运行
if torch.cuda.is_available():
tensor = tensor.to('cuda')
torch.cuda.is_available()
# dir(工具区间) # 打开查看()
# help(道具) # 说明书
import torch
dir(torch.cuda)
help(torch.cuda.is_available)
输出结果:
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])
Help on function is_available in module torch.cuda:
is_available() -> bool
Returns a bool indicating if CUDA is currently available.
# dir(工具区间) # 打开查看()
# help(道具) # 说明书
import torch
dir(torch.cuda)
help(torch.cuda.is_available)
Help on function is_available in module torch.cuda:
is_available() -> bool
Returns a bool indicating if CUDA is currently available.
数据操作
PyTorch 数据存储与加载
* Dataset (数据获取)
- Dataset 工具:用于将我们准备的数据集进行打包,方便我们提取数据以及对应的label
它主要能为我们做的就是
(1)如何获取每一个数据及其label ,(2)告诉我们总共有多少的数据
- Dataloader 工具: 为后面网络提供不同的加载数据的形式
- 数据存放规范或者组织形式:
(1)train文件夹(含ants文件夹和bees文件夹等,对应文件夹中存储对应的图像数据集),测试文件另外创建vel
(2)train_images文件夹(保存如100.jpg,101.jpg。。。) 和 train_labels文件夹(保存如gt_100.txt,gt_101.txt。。。。),
opencv 基础补充
# opencv的补充(加载图片)
import cv2
gray_img = cv2.imread('datas\hymenoptera_data\train\ants\0013035.jpg', 0) #加载灰度图像
rgb_img = cv2.imread('datas\hymenoptera_data\train\ants\0013035.jpg', 1) #加载RGB彩色图像
cv2.imshow("test", rgb_img)
# 必须写这个两句,就是等待有按键按下,再销毁窗口,不然会窗口卡死
cv2.waitKey()
cv2.destroyAllWindows()
实战操作——数据获取与加载
# 实战加载数据(以方式1)
from torch.utils.data import Dataset # utils 是一个常用工具箱
import cv2
import os # 用于获取地址
class MyData(Dataset): #继承于Dateset
def __init__(self, root_dir, label_dir):
self.root_dir = root_dir # 文件夹地址
self.label_dir = label_dir # labol名(比如ants,bees)
self.path = os.path.join(self.root_dir, self.label_dir) # 将两者拼接起来(window下对应\)
self.img_path = os.listdir(self.path) # 把对应文件的所有地址保存为列表
def __getitem__(self, index):
img_name = self.img_path[index] # index访问对应的地址
img_item_path = os.path.join(self.root_dir, self.label_dir, img_name) # 形成整个地址
img = cv2.imread(img_item_path, 1)
label = self.label_dir
return img, label
def __len__(self):
return len(self.img_path)
#获取数据集
root_dir = "datas\hymenoptera_data\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)
trains_dataset = ants_dataset + bees_dataset
len(trains_dataset)
245
* DataLoader (数据)
- 从 Dateset 中取数据,即所谓的数据加载
# DataLoader 测试
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()) # 将数据存储在Dataset中
# 参数1:Dataset数据集, 参数2(batch_size)表示多少个打包一次, shuffle表示是否打乱顺序,num_workers未知一般为0,
# drop_last是为了当剩余数据不足以凑够batch_size大小时,true则舍去,false 则继续保留
test_loader = DataLoader(dataset=test_data, batch_size=64, shuffle=True, num_workers=0, drop_last=True)
# 测试数据集中第一张图片及 target
img, target = test_data[0] # 可以查看dataset的返回结构
print(img.shape)
print(target)
writer = SummaryWriter("logs") # 显示图片工具(下面讲)
for epoch in range(2):
step = 0
for data in test_loader:
imgs, targets = data
writer.add_images("test_dataloader: {}".format(epoch), imgs, step) # 这里是add_images 多张照片被被打包
step = step + 1
writer.close()
Tensorboad 使用(数据显示)
- 用于展示图像 add_image 或 add_scalor
- 执行完以下代码后,在(pytorch)的anconda虚拟环境下,在 logs 同名下,
执行:tensorboard --logdir=logs 或者 tensorboard --logdir=logs --port=6007 由此指定端口号
然后将生成的 网络地址 放到浏览器打开,即可看到图像(数据图像与画面图像操作都一样)
from torch.utils.tensorboard import SummaryWriter
import cv2
import numpy as np
writer = SummaryWriter("logs") # 图像显示口定义(当前目录下生成一个logs文件夹)
# add_image 的使用(获取图像数据)
images_path = "datas\hymenoptera_data\train\ants_image\0013035.jpg"
img = cv2.imread(images_path,1) #以rgb方式读取
img_array = np.array(img)
writer.add_image("test", img_array, 1, dataformats='HWC') # 参数1:名称, 参数2:(张量tensor 或 numpy的ndarray 或 字符串),参数3为 步骤数
# add_scalar 的使用(“名”,x轴, y轴)
# 绘制 y=x 的图像
for i in range(100):
writer.add_scalar("y=x", 2*i,i)
writer.close()
* transform (数据转换)
- 对图像进行变换(图像转换成张量) transforms.ToTensor()
- 归一化 处理的公式 其实就是正态分布进行处理成标准标准正态,意义为数值距离均值有几个标准差,当E(Z)=0,SD(Z)=1,即均值为0,标准差为1,则表示经过处理后的数据符合标准正态分布(transforms.Normalize(tensor))
- 对图像进行等比缩放,transform.Resize((长,宽))
- transform.compose([…]) 组合变换一起作业
# transform 结构及其作用,其实它就是个.py文件
# 包含各种对图像的转换,可将图像转换为多种数据类型
# transform.totensor (就是将 图像数据转换为tensor数据类型)
# 1 transforms 该如何使用;
from email.policy import default
import torch
import cv2
from torchvision import transforms
from torch.utils.tensorboard import SummaryWriter
from PIL import Image
img_path = "datas/hymenoptera_data/train/ants_image/0013035.jpg"
img = cv2.imread(img_path, 1)
# 转换成 ToTensor (张量处理)
tensor_trans = transforms.ToTensor() # 实例化一个对象(这个对象可以将图像转换成张量格式)
tensor_img = tensor_trans(img) # 通过该对象进行转换并输出成张量
# 2 为什么我们需要Tensor数据类型
# 结合Tensorboard 使用, 显示图片
writer = SummaryWriter("logs")
writer.add_image("test",tensor_img) # 如果采用张量的话,不需要限定默认参数 dataformats='HWC'
#writer.close()
# 转换成 Normalize (归一化处理)(相当于变标准正态)
print(tensor_img[0][0][0])
trans_norm = transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5]) # 参数1 为均值(mean),参数2 为标准差(std),图片有三个信道,所以三个数据为一组
img_norm = trans_norm(tensor_img)
print(img_norm[0][0][0])
writer.add_image("norm",img_norm)
# 对图像进行等比缩放 Resize
print(img.shape)
trans_resize = transforms.Resize((512, 512)) #进行放缩实例化
#help(trans_resize)
#img_resize = trans_resize(tensor_img) # 会根据形参输入自动匹配对应的成员函数
img_resize = trans_resize.forward(tensor_img)
print(img_resize.shape)
# 对多种转换进行和并,再一并对图像进行转换 Compose
img_PIL = Image.open(img_path)
# PIL -> PIL -> Tensor
trans_compose = transforms.Compose([transforms.Resize((512,512)), transforms.ToTensor()])
img_compose = trans_compose(img_PIL) # 输入必须符合组合转换中每一个转换输入的要求
print(img_compose.shape)
writer.close()
输出结果:
tensor(0.9137)
tensor(0.8275)
(512, 768, 3)
torch.Size([3, 512, 512])
torch.Size([3, 512, 512])
官方数据集如何下载使用
- 结合 Dataset 同样 Transform 一同使用
# 如何下载数据集(在pytorch官网的torchvision下 可以查看对应公开数据集的操作方法)
import torchvision
# 以下是下载pytorch 官网的数据集方法,三个参数分别是 root(数据集存放位置), train(是否为训练数据true为训练,false为测试,download是否下载)
train_set = torchvision.datasets.CIFAR10(root="./dataset", train=True, download=True) # 下载训练数据集
test_set = torchvision.datasets.CIFAR10(root="./dataset", train=False, download=True) # 下载测试数据集
# Dateset 和 Trnasform 联合使用 对数据集进行批量处理
import torchvision
from torch.utils.tensorboard import SummaryWriter
dataset_transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()]) # 对数据集图像进行转换
# 以下是下载pytorch 官网的数据集方法,三个参数分别是 root(数据集存放位置), train(是否为训练数据true为训练,false为测试,download是否下载)
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("logs")
for i in range(10):
img, target = test_set[i] # 访问对应元素每一个元素由元组组成(img,target)
writer.add_image("test_set", img, i)
writer.close()
神经网络
卷积层 (Convolution layer)
nn.Conv1d
, 其中 1d 代表 1维nn.Conv2d
代表2维nn.Conv3d
代表3维- 用的最多的是 Conv2d ,比如图像
卷积方法1:
torch.nn.functional.Conv2d(input, weight, bias, stride, padding, dilation, group)
input
: 输入,其结构必须为(minibatch, in_channels, iH, iW),
其中minibtch代表打包包含多少个图像,in_channels代表多少个通道, iH表示高,iW 表示宽
weiget
:权重,也叫卷积核,其结构也必须为( out_channels, , kH, kW)
stride
padding
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]])
input = torch.reshape(input, (1, 1, 5, 5)) # 使用重塑,为了符合 (minibatch, in_channels, iH, iW)
kernel = torch.reshape(kernel, (1, 1, 3, 3)) # 为了符合 ( out_channels, ${\frac{in_channels}{group}}$, kH, kW)
print(input.shape)
print(kernel.shape)
output = F.conv2d(input, kernel, stride=1, padding=1) # 卷积核横向步长和纵向步长都为1, 横向(左右都长1)和纵向(上下都长1)向外扩展1
print(output)
输出:
torch.Size([1, 1, 5, 5])
torch.Size([1, 1, 3, 3])
tensor([[[[ 1, 3, 4, 10, 8],
[ 5, 10, 12, 12, 6],
[ 7, 18, 16, 16, 8],
[11, 13, 9, 3, 4],
[14, 13, 9, 7, 4]]]])
卷积方法2*
torch.nn.Conv2d(in_channels, out_channels, kernel_size, strside=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')
dilation
:表示卷积核的对应位的距离groups
: 分组卷积(几乎用不到)bias
: 偏置(是否对卷积后的结果是否加减一个常数),一般默认设置 Truepadding_mode
: 对被卷积对象进行填充(选择填充模式,默认为0填充(zeros)),可选 ‘reflect’,‘replicate’,‘circular’
in_channels
: 输入通道数out_channels
: 输出通道数 ,1个通道数表示采用1个卷积核,2个通道表示采用2个卷积核得到2个卷积输出(这两卷积核内容不一样相同),依次类推
kernel_size : 卷积核的大小(可以是单数(如3 即表示3X3),也可以是元组),内容不需要设置,内容是系统在一些特殊分布中采样得到的
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
# 获取数据
test_dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(), download=False)
# 加载数据
test_dataloader = DataLoader(dataset=test_dataset, batch_size=64) # 打包个数64个,其他参数默认
# 定义神经网络
class My_nn(nn.Module):
def __init__(self) -> None:
super(My_nn, self).__init__()
self.Conv = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3, stride=1, padding=0)
def forward(self, x):
x = self.Conv(x)
return x
nn_test = My_nn() # 实例化
writer = SummaryWriter("logs")
step=0
for data in test_dataloader:
imgs, targets = data # 一个data 表示一个打包好的组合
outputs = nn_test(imgs)
# torch.Size([64, 3, 32, 32])
print(imgs.shape)
# torch.Size([64, 6, 30, 30])
print(outputs.shape)
writer.add_images("imgs", imgs, step)
writer.add_images("outputs", outputs, step)
step = step +1
池化层 (Pooling lays)
- 也叫下采样 (**
nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=false, ceil_mode=False)
(二维下采样) (最重要)
参数:
1) kernel_size
:生成窗口(池化核)的尺寸(类似于前面的卷积核),可以是单数,可以是元组
stride
: 与卷积层类似,移动的步长(默认值是kernel_size, 这与前面卷积层不一样)padding
:下外扩展
4) dilation
: 表示加入空格
return_indices
(直接默认,基本不用)ceil_mode
: True 为ceil模式(向上取整), False 为floor模式(向下取整)
调用方法与卷积方法2类似,需要创建完整神经网络框架
输入要求:
input :(N,C,, )
output : (N, C, , )
其中池化操作:
-
nn.MaxUnpool2d
(二维上采样)
MaxPool 即最大池化,对池化核对应的输入的几个数中取最大,用途:
比如降低分辨率,1080X1920,-> 720p,其实就是缩小文件尺寸,降低神经网络的数据量(必用)
import torchvision # 专门用于视觉操作的
from torch.utils.data import DataLoader # 数据加载
from torch import nn
from torch.utils.tensorboard import SummaryWriter
img_dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),download=False) # 获取
img_loader = DataLoader(dataset=img_dataset, batch_size=24) # 加载
writer = SummaryWriter("logs")
class MyMaxPool(nn.Module):
def __init__(self) -> None:
super(MyMaxPool, self).__init__()
self.MaxPooltest= nn.MaxPool2d(kernel_size=3, ceil_mode=False)
def forward(self, input):
output = self.MaxPooltest(input)
return output
test_pool = MyMaxPool() # 池化操作
step = 0
for data in img_loader:
img, target = data
output = test_pool(img)
writer.add_images("img", img, step)
writer.add_images("output", output, step)
step = step + 1
writer.close()
非线性激活 (None-linear Activations)
- 为了给神经网络引入一些非线性特质
- 比如:
1) torch.nn.ReLU(inplace=False) :
作用是 当 input > 0 时 取 output = input , 当input <= 0时,output = 0; 其中input:(N,* ),output:(N,* ),N表示batch_size
2)torch.nn.Sigmoid
表示:
其中input:(N,* ),output:(N,* ),N表示batch_size
基本操作与前面一致
线性化处理
- torch.nn.Linear(in_features, out_features, bias = True)
相当于在求:
bias 相当于 要不要 b
- torch.flatten(x) ,将 张量 x 展开成 1 个序列
序列网络模型
- torch.nn.Sequential(args)
作用是将多个操作按序列形式逐个操作
以下以一个 CIFAR10 为这个神经网络模型为例进行搭建
import torchvision
import torch
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader
cifar_dataset = torchvision.datasets.CIFAR10("./dataset", train=True, transform=torchvision.transforms.ToTensor(),download=False)
cifar_dataloader = DataLoader(cifar_dataset,batch_size=64)
class cifar_module(nn.Module):
def __init__(self) -> None:
super(cifar_module, self).__init__()
self.sq_module = Sequential(
Conv2d(3, 32, 5, padding=2), # 这个padding 需要使用卷积公式计算一下
MaxPool2d(2),
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, padding=2),
MaxPool2d(2),
Flatten(), # 64*4*4 变成一行
Linear(1024, 64), # 线性化处理 进去64*4*4,出去时变64
Linear(64, 10) # 进去 64 出去 10 (本身里面只有10种可分辨类型)
)
def forward(self, input):
output = self.sq_module(input)
return output
# 实例化一个神经网络框架
Cifar = cifar_module()
print(Cifar)
# 使用一个全1张量测试
input = torch.ones((64, 3, 32, 32)) # 对应 N,C, W, H(输入通道数,输出通道数,长,宽)
output = Cifar(input)
print(output.shape)
输出:
cifar_module(
(sq_module): Sequential(
(0): Conv2d(3, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(2): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(4): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(6): Flatten(start_dim=1, end_dim=-1)
(7): Linear(in_features=1024, out_features=64, bias=True)
(8): Linear(in_features=64, out_features=10, bias=True)
)
)
torch.Size([64, 10])
损失函数 反向传播 优化器
nn.L1Loss
nn.MSELoss
-
nn.CrossEntropyLoss
(分类常用,交叉熵) 常用
比如一个分类问题,分类目标有 person, dog, cat
- Target 就是对应上述分类目标的位置,从0开始计, person为0, dog为1, cat为2
- output 是一个神经网络模型的输出,一般表现为对应分类目标的概率,如
[0.1,0.2,0.3]
即有0.1的概率为person, 0.2的概率为dog, 0.3的概率为cat - 公式中的 代表的就是output,就是output的概率元组
- 公式中的 代表的是Target 值,
- 这里的 一般是
优化器
torch.optim
- 官方的优化器使用方法示例:参数更新步骤
for input, target in dataset:
optimizer.zero_grad() # 将上一次操作求出的梯度进行清零,以免影响后面的结果
output = model(input) # 经过一个神经网络模型 得到输出
loss = loss_fn(output, target) # 求出输出与目标误差,即损失函数
loss.backward() # 使用损失进行反向传播,以得到每个要更新(神经元)的梯度
optimizer.step() # 优化器,根据每一步的梯度进行卷积核等参数的优化,以减少loss
torch.optim.xxx(params,…) # 优先器使用, xxx表示优化算,params 是我们需要提供的
例如:optimizer = torch.optim.SGD(模型对象.parameters(), lr=0.01) # lr学习率
#-*-coding:GBK -*-
from pickletools import optimize
import torchvision
import torch
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader
cifar_dataset = torchvision.datasets.CIFAR10("./dataset", train=True, transform=torchvision.transforms.ToTensor(),download=False)
cifar_dataloader = DataLoader(cifar_dataset,batch_size=64)
class cifar_module(nn.Module):
def __init__(self) -> None:
super(cifar_module, self).__init__()
self.sq_module = Sequential(
Conv2d(3, 32, 5, padding=2), # 这个padding 需要使用卷积公式计算一下
MaxPool2d(2),
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, padding=2),
MaxPool2d(2),
Flatten(), # 64*4*4 变成一行
Linear(1024, 64), # 线性化处理 进去64*4*4,出去时变64
Linear(64, 10) # 进去 64 出去 10 (本身里面只有10种可分辨类型)
)
def forward(self, input):
output = self.sq_module(input)
return output
# 实例化一个神经网络框架
Cifar = cifar_module()
print(Cifar)
# 使用一个全1张量测试
'''
input = torch.ones((64, 3, 32, 32)) # 对应 N,C, W, H(输入通道数,输出通道数,长,宽)
output = Cifar(input)
print(output.shape)
'''
# 损失函数-交叉熵
loss = nn.CrossEntropyLoss()
# 定义优化器
optimizer = torch.optim.SGD(Cifar.parameters(), lr=0.01) # SGD随机参数优化, Cifar.parameter()获取参数,lr为学习率, 学习率小点训练稍微稳定些
for data in cifar_dataloader:
imgs, targets = data
outputs = Cifar(imgs)
result_loss = loss(outputs, targets) # 计算实际输出与目标之间的差距,作用2 是为我们更新输出提供一定的依据(反向传播),grad(梯度)
optimizer.zero_grad() # 先对模型的grad进行清零
result_loss.backward() # 反向传播,这样才会对每一层神经网络生成一个梯度grad(梯度用于确定最终的分类结果)
# 有了梯度后,选择优化器进行调节,从而降低整体的误差
optimizer.step() # 优化参数
print("ok")
网络模型保存与加载
## 修改已有模型的参数
# 以官方的 vgg16 网络架构为例
vgg16_true = torchvision.models.vgg16(pretrained=True) # 加载已经下载好的Vgg16网络,pretrained 表示已经训练好的,下面的 fasle 表示没有训练好的
vgg16_false = torchvision.models.vgg16(pretrained=False)
# 修改模型参数
vgg16_true.add_module("name", nn.Linear(1000, 10)) # 对现有模型进行修改, 加入一层名为“name” 的神经元,该神经元的作用是以输入为1000的数据线性化成输出为10的数据
## 保存模型
## 。。。。(前面的神经网络框架)
# 实例化一个神经网络框架
Cifar = cifar_module()
## 保存 与加载 模型
# 方式1
torch.save(Cifar, "./Cifar.pth") # 路径最好以.pth为后缀
torch.load("./Cifar.pth")
## 方式2保存 (官方推荐,空间占用较少)
torch.save(Cifar.state_dict(), "./Cifar.pth")
# 方式2的加载
Cifar = cifar_module()
Cifar.load_state_dict(torch.load("./Cifar.pth"))
神经网络整体的基本搭建方法
- 1:获取数据:datasets
- 2:加载数据:dataloader
- 3:构建神经网络模型
- 4:定义损失函数
- 5:定义优化器
- 6:for循环处理dataloader数据
- 处理步骤:
(1)经过神经网络模型对象
(2)损失函数计算损失
-优化器清零梯度
(3)损失进行反向传播
(4)优化器优化 - 测试数据测试,监视每一轮训练的损失,损失或者正确率合格则停止训练
示例
GPU 训练方法1
# -*- coding:GBK -*-
# 如何调用gpu训练:#
# 针对 网络模型、数据(输入、标注)、损失函数变量, 调用对应的.cuda()即可
# #
import torchvision
from torch.utils.data import DataLoader
from torch import nn
import torch
from torch.utils.tensorboard import SummaryWriter
import time # 用于计时
#--------- 下载训练数据与测试数据集 到 Dataset
cifar_train_data = torchvision.datasets.CIFAR10("./datas", train=True, transform=torchvision.transforms.ToTensor(),download=True)
cifar_test_data = torchvision.datasets.CIFAR10("./datas", train=False, transform=torchvision.transforms.ToTensor(),download=True)
train_len = len(cifar_train_data)
test_len = len(cifar_test_data)
print("训练数据集大小:{}".format(train_len))
print("测试数据集大小:{}".format(test_len))
#--------- 加载数据集到 Dataloader
cifar_train_dataloaders = DataLoader(cifar_train_data, batch_size=64)
cifar_test_dataloaders = DataLoader(cifar_test_data, batch_size=64)
# 加入 tensorboad的显示
writer = SummaryWriter("logs")
#--------- 设计神经网络模型
class cifar_module(nn.Module):
def __init__(self) -> None:
super(cifar_module, self).__init__()
self.netwoke = nn.Sequential(
nn.Conv2d(3,32,5,stride=1, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, stride=1, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, stride=1, padding=2),
nn.MaxPool2d(2),
nn.Flatten(), # 排成序列
nn.Linear(64*4*4, 64),
nn.Linear(64, 10)
)
def forward(self, input):
output = self.netwoke(input)
return output
Cifar_one = cifar_module() # 实例化一个神经网络模型
# gpu 训练 —— 网络模型
if torch.cuda.is_available():
Cifar_one = Cifar_one.cuda()
#--------- 定义损失函数
loss = nn.CrossEntropyLoss()
# gpu 训练 —— 损失函数#
if torch.cuda.is_available():
loss = loss.cuda()
#--------- 定义优化器
Leaning_rate = 1e-2 # 学习率
optimizer = torch.optim.SGD(Cifar_one.parameters(), lr=Leaning_rate)
# 记录训练次数
train_steps = 0
# 记录测试次数
test_steps = 0
# 设定训练轮数
episode = 10
# 训练轮数:定义(for)
# DataLoader 打包处理(for)
# 神经网络处理
# 损失计算
# 反向传播
# 优化器优化
# start_time = time.time()
for i in range(episode): # 训练轮次
print("-----第 {} 轮训练-----".format(i+1))
# --------- 单轮训练步骤
Cifar_one.train() # 在存在特定神经网络层(pytorch官方查看)时需要(这里可有可不),让网络进入训练状态
for data in cifar_train_dataloaders:
imgs, targets = data
# gpu 训练 —— 输入数据#
if torch.cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
output = Cifar_one(imgs)
result_loss = loss(output, targets) # target 就是标签
optimizer.zero_grad() # 清除上层的梯度
result_loss.backward() # 反向传播 产生梯度
optimizer.step() # 优化器优化模型参数
# end_time = time.time()
# print(end_time-start_time)
train_steps +=1 # 记录训练次数
if train_steps%50 == 0 : # 每50次打印一次
print("训练次数:{}, Loss:{}".format(train_steps, result_loss.item())) # 加个item是为让其以数字形式显示,而不是以张量显示
writer.add_scalar("train_step", result_loss.item(), train_steps)
# --------- 单轮测试步骤
Cifar_one.eval() # 在存在特定神经网络层(pytorch官方查看)时需要(这里可有可不),让网络进入验证状态
total_right_num = 0 # 记录预测成功的个数
total_test_loss = 0 # 记录测试的总损失
# 测试每一轮的训练结果是否被训练好(),以测试集上的损失或者正确率来评估
with torch.no_grad(): # 在测试时,需要使梯度没有
for data in cifar_test_dataloaders:
imgs, targets = data
# gpu 训练 ——输入数据#
if torch.cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
output = Cifar_one(imgs)
result_loss = loss(output, targets)
total_test_loss += result_loss.item()
right_num = (output.argmax(1) == targets).sum() # 张量或者列表采用argmax(1)内行向最大的,argmax(0)列内最大的
total_right_num += right_num
print("经过该轮训练,整体测试数据集上的loss为:{}".format(total_test_loss))
print("第{}轮的正确率为:{}".format(i+1, (total_right_num/test_len)))
writer.add_scalar("test_step", total_test_loss, test_steps) # 测试的是每轮测试的总误差
test_steps += 1
#--------- 保存模型参数
torch.save(Cifar_one.state_dict(), "Cifar_{}.pth".format(i+1)) # 保存每轮的模型数据参数
print("第{}轮训练的模型参数已保存".format(i))
writer.close()
GPU 训练方法2
# -*- coding:GBK -*-
# 如何调用gpu训练:#
# 针对 网络模型、数据(输入、标注)、损失函数变量, 调用对应的.cuda()即可
# #
import torchvision
from torch.utils.data import DataLoader
from torch import nn
import torch
from torch.utils.tensorboard import SummaryWriter
import time # 用于计时
# device = torch.device("cpu")
# device = torch.device("cuda") # 使用gpu
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # python的三目表达式
#--------- 下载训练数据与测试数据集 到 Dataset
cifar_train_data = torchvision.datasets.CIFAR10("./datas", train=True, transform=torchvision.transforms.ToTensor(),download=True)
cifar_test_data = torchvision.datasets.CIFAR10("./datas", train=False, transform=torchvision.transforms.ToTensor(),download=True)
train_len = len(cifar_train_data)
test_len = len(cifar_test_data)
print("训练数据集大小:{}".format(train_len))
print("测试数据集大小:{}".format(test_len))
#--------- 加载数据集到 Dataloader
cifar_train_dataloaders = DataLoader(cifar_train_data, batch_size=64)
cifar_test_dataloaders = DataLoader(cifar_test_data, batch_size=64)
# 加入 tensorboad的显示
writer = SummaryWriter("logs")
#--------- 设计神经网络模型
class cifar_module(nn.Module):
def __init__(self) -> None:
super(cifar_module, self).__init__()
self.netwoke = nn.Sequential(
nn.Conv2d(3,32,5,stride=1, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, stride=1, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, stride=1, padding=2),
nn.MaxPool2d(2),
nn.Flatten(), # 排成序列
nn.Linear(64*4*4, 64),
nn.Linear(64, 10)
)
def forward(self, input):
output = self.netwoke(input)
return output
Cifar_one = cifar_module() # 实例化一个神经网络模型
# gpu 训练 —— 网络模型
#if torch.cuda.is_available():
# Cifar_one = Cifar_one.cuda()
Cifar_one = Cifar_one.to(device)
#--------- 定义损失函数
loss = nn.CrossEntropyLoss()
# gpu 训练 —— 损失函数#
#if torch.cuda.is_available():
# loss = loss.cuda()
loss = loss.to(device)
#--------- 定义优化器
Leaning_rate = 1e-2 # 学习率
optimizer = torch.optim.SGD(Cifar_one.parameters(), lr=Leaning_rate)
# 记录训练次数
train_steps = 0
# 记录测试次数
test_steps = 0
# 设定训练轮数
episode = 10
# 训练轮数:定义(for)
# DataLoader 打包处理(for)
# 神经网络处理
# 损失计算
# 反向传播
# 优化器优化
# start_time = time.time()
for i in range(episode): # 训练轮次
print("-----第 {} 轮训练-----".format(i+1))
# --------- 单轮训练步骤
Cifar_one.train() # 在存在特定神经网络层(pytorch官方查看)时需要(这里可有可不),让网络进入训练状态
for data in cifar_train_dataloaders:
imgs, targets = data
# gpu 训练 —— 输入数据#
#if torch.cuda.is_available():
# imgs = imgs.cuda()
# targets = targets.cuda()
imgs = imgs.to(device)
targets = targets.to(device)
output = Cifar_one(imgs)
result_loss = loss(output, targets) # target 就是标签
optimizer.zero_grad() # 清除上层的梯度
result_loss.backward() # 反向传播 产生梯度
optimizer.step() # 优化器优化模型参数
# end_time = time.time()
# print(end_time-start_time)
train_steps +=1 # 记录训练次数
if train_steps%50 == 0 : # 每50次打印一次
print("训练次数:{}, Loss:{}".format(train_steps, result_loss.item())) # 加个item是为让其以数字形式显示,而不是以张量显示
writer.add_scalar("train_step", result_loss.item(), train_steps)
# --------- 单轮测试步骤
Cifar_one.eval() # 在存在特定神经网络层(pytorch官方查看)时需要(这里可有可不),让网络进入验证状态
total_right_num = 0 # 记录预测成功的个数
total_test_loss = 0 # 记录测试的总损失
# 测试每一轮的训练结果是否被训练好(),以测试集上的损失或者正确率来评估
with torch.no_grad(): # 在测试时,需要使梯度没有
for data in cifar_test_dataloaders:
imgs, targets = data
# gpu 训练 ——输入数据#
#if torch.cuda.is_available():
# imgs = imgs.cuda()
# targets = targets.cuda()
imgs = imgs.to(device)
targets = targets.to(device)
output = Cifar_one(imgs)
result_loss = loss(output, targets) # 输出与目标的损失
total_test_loss += result_loss.item()
right_num = (output.argmax(1) == targets).sum() # 张量或者列表采用argmax(1)内行向最大的,argmax(0)列内最大的
total_right_num += right_num
print("经过该轮训练,整体测试数据集上的loss为:{}".format(total_test_loss))
print("第{}轮的正确率为:{}".format(i+1, (total_right_num/test_len)))
writer.add_scalar("test_step", total_test_loss, test_steps) # 测试的是每轮测试的总误差
test_steps += 1
#--------- 保存模型参数
torch.save(Cifar_one.state_dict(), "Cifar_{}.pth".format(i+1)) # 保存每轮的模型数据参数
print("第{}轮训练的模型参数已保存".format(i))
writer.close()
如何使用已经训练好的模型进行预测
- 预测对应的标签与target 可以通过对 dataset 的变量打断点的方式的在debug模式下查找到,比如 Cifar10 的
在 class_to_idex 下面:
# -*- coding:GBK -*-
from statistics import mode
import torch
import torchvision
from torch import nn
import cv2
from PIL import Image
#import train
image_path = "./imgs/deer.png" # 用于预测的图片路径
image = cv2.imread(image_path, 1) # 1 表示以rgb图片加载
print(image.shape) # numpy的数据格式【H*W*C】
#cv2.imshow("test", image)
#cv2.waitKey()
#cv2.destroyAllWindows()
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),
torchvision.transforms.ToTensor()])
# opencv -> PIL
img_pil = Image.fromarray(cv2.cvtColor(image,cv2.COLOR_BGR2RGB))
# 已经转换成能够识别的格式了
image = transform(img_pil) # 因为transform只能定对 PIL 进行读入的数据操作,所以要对cv进行转换
print(image.shape) # tensor的数据格式【C*H*W】 通道数*高*宽
#--------- 设计神经网络模型b (如果是自己设计的模型, 如果不使用import的话,就需要把模型再写一遍)
class cifar_module(nn.Module):
def __init__(self) -> None:
super(cifar_module, self).__init__()
self.netwoke = nn.Sequential(
nn.Conv2d(3,32,5,stride=1, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, stride=1, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, stride=1, padding=2),
nn.MaxPool2d(2),
nn.Flatten(), # 排成序列
nn.Linear(64*4*4, 64),
nn.Linear(64, 10)
)
def forward(self, input):
output = self.netwoke(input)
return output
Cifar = cifar_module() # 实例化模型对象
Cifar.load_state_dict(torch.load("./nn_test/module_data/Cifar_30.pth"))
# 如果是gpu训练的,但是又是cpu测试,则需要使用map_loaction参数进行映射
# Cifar.load_state_dict(torch.load("./nn_test/module_data/Cifar_30.pth",map_location="cpu"))
# 将image 设定为 N*C*H*W 格式
image = torch.reshape(image, (1, 3, 32, 32)) # batch_size
Cifar.eval() # 对模型进行测试预备
with torch.no_grad():# 保证没有梯度
output = Cifar(image) # 将测试图片输入模型进行测试
print(output)
print(output.argmax(1)) # 求行最大 即为对应预测结果