PYTORCH学习

本文是自己学习的输出,有很多说不清道不明的点,往大师们指明。

非常好的入门课程:PyTorch深度学习快速入门教程

PyTorch 中文官方教程 1.7 · Pytorch 中文文档https://pytorch.apachecn.org/docs/1.7/

一. TORCH官网

官网链接:PyTorchAn open source machine learning framework that accelerates the path from research prototyping to production deployment.https://pytorch.org/

官网中DOCS中有PyTorch、torchaudio、torchtext、torchvision、TorchServe、PyTorch on XLA Devices等模块的详细讲解,基础知识扎实,是我们非常好的学习依据。

PYTORCH学习_第1张图片

 PyTorch:PyTorch is an optimized tensor library for deep learning using GPUs and CPUs,PyTorch是一个针对深度学习,并且使用GPU和CPU来优化的tensor library(张量库),是一个深度学习框架,不仅能实现强大的GPU加速,同时还支持动态神经网络。


torchaudio:简单的音频输入/输出音频库


torchtext:文本数据预处理工具,本文学习重点,参考链接:torchtext
 

torchvision:处理图像视频工具
 

TorchServe:开源模型服务器
 

PyTorch on XLA Devices:PyTorch on XLA Devices PyTorch runs on XLA devices, like TPUs, with the torch_xla package

 
二. Tensors

1、定义张量的方法

定义方法1:将已有数据转换成张量

import torch
import numpy as np

# 建议使用torch.tensor()来直接赋值
a = torch.tensor([1., 2., 3.])  # 直接赋值(建议)
# 不建议用FloatTensor来直接赋值,避免混淆
a_1 = torch.FloatTensor([1.,2.,3.]) # 也可以用FloatTensor赋值
print('a:   ', a)
print("-"*5)
print('a_1: ', a_1)
print("-"*5)
#定义一个张量
a_2=torch.tensor(5)
print('a_2: ', a_2)


# 同numpy来转换数据
e_np = np.ones((3, 3))  # 定义numpy的全1 ndarray
e = torch.from_numpy(e_np)  # 使用numpy转换到tensor
print('e: ', e)
print("-"*5)
f=torch.tensor(e_np) #将Numpy转换为tensor
print('f: ', f)
import torch
a=torch.tensor(5)
print(a)
print(a.item())  #item()取出单元素张量的元素值并返回该值,保持原元素类型不变

 定义方法2:根据指定的形状、类型生成张量

系统默认类型: torch.Tensor()

浮点型: torch.FloatTensor()

整型: torch.IntTensor()

双精度浮点型: torch.DoubleTensor()

长整型: torch.LongTensor()

字节型: torch.ByteTensor()

字符型: torch.CharTensor()

短整型: torch.ShortTensor()

# 建议使用FloatTensor传入shape来定义数据结构
b = torch.FloatTensor(1)  # 参数表示shape,这里是2个元素的向量,值未初始化,可能很大或很小
c = torch.FloatTensor(3, 2)  # 这里表示维度为[3,2]的矩阵,值未初始化,可能很大或很小

d = torch.ones(3, 3)  # 定义维度为[3,3]的全1矩阵

print('b: ', b)
print("-"*5)
print('c: ', c)
print("-"*5)
print('d: ', d)

 大家注意,torch.tensor和torch.Tensor大小写的不同,意义就不同,小写的是定义成张量,大写的是定义形状,

PYTORCH学习_第2张图片

 设置类型

print(torch.get_default_dtype()) #输出默认类型
print(torch.tensor([1,3]).dtype)
torch.set_default_dtype(torch.float64) #将默认类型修改为float64
print(torch.get_default_dtype()) #输出默认类型 
print(torch.tensor([1,3]).dtype)

定义方法3:根据指定形状生成固定值的张量

a = torch.ones(3, 3)  # 定义维度为[3,3]的全1矩阵
print('a:', a)
print("-"*5)

b = torch.zeros(3, 3)  # 定义维度为[3,3]的全1矩阵
print('b:', b)
print("-"*5)

a_2 = torch.ones_like(a) #生成与目标形状相同、值为1的张量数组
print('a_2:', a_2)
print("-"*5)

b_2 = torch.zeros_like(a) #生成与目标形状相同、值为1的张量数组
print('b_2:', b_2)

#生成同元素的矩阵:
# 生成一个元素全是7.0的2*3矩阵
a = torch.full([2,3],7.)
print(a)
# 生成一个元素全是7.0的2维向量
b = torch.full([2],7.)
print(b)
# 生成值为7.0的标量
c = torch.full([],7.)
print(c)

2、生成随机张量

#1、设置随机数种子
print(torch.initial_seed())  #查看随机数种子
torch.manual_seed(2) #设定随机数种子
print(torch.initial_seed())  #查看随机数种子
#2、通过指定形状生成随机值
torch.randn(2,3)
#3、生成线性空间的随机值 提示:step是步长,steps是均匀的取几个值
print(torch.arange(1,10,step=2))  #arange只包括起始值,不包括终止值
print(torch.linspace(1,9,steps=5)) #既包括起始值,又包含终止值,注意和arange中的step不一样
#4、生成对数空间的随机值
print(torch.logspace(1,9,steps=5))
#5、生成未初始化的矩阵
print(torch.empty(1,2))
#6、更多的随机值生成函数
a = torch.empty(3, 3).uniform_(0, 1) ## generate a uniform random matrix with range [0, 1]
print(torch.bernoulli(a)) #伯努利分布函数
print(torch.Tensor.bernoulli_()    torch.bernoulli() 的 in-place 版本
print(torch.Tensor.cauchy_())     从柯西分布中抽取数字
print(torch.Tensor.exponential_()   从指数分布中抽取数字
print(torch.Tensor.geometric_()    从几何分布中抽取元素
print(torch.Tensor.log_normal_()  对数正态分布中的样本
print(torch.Tensor.normal_()  是 torch.normal() 的 in-place 版本
print(torch.Tensor.random_()    离散均匀分布中采样的数字
print(torch.Tensor.uniform_()   正态分布中采样的数字
# 正态分布随机数
randn_mat = torch.randn(2,3)
print(randn_mat)
# 均匀分布随机数,范围[0,1]
rand_mat = torch.rand(2,3)
print(rand_mat)
# Int随机,返回[0,10),注意是前闭后开区间
randint_mat = torch.randint(0,10,[3,3])
print(randint_mat)

# 二维tensor,可以表示4张mnist图片(图片已fla)
tensor_2d = torch.rand(4,784)
# 三维tensor,可以表示20句话,每句话10个单词,每个单词用onehot来表示[1,100]
tensor_3d = torch.rand(20,10,100)
# 四维tensor,可以表示4张mnist图片,h w都是28,channel为1
tensor_4d = torch.rand(4,1,28,28)

# 使用和tensor_4d相同的随机方式和维度定义tensor_4d_2
tensor_4d_2 = torch.rand_like(tensor_4d)

# 看tensor_4d有多少元素
print(torch.numel(tensor_4d))

3、张量的基本操作

import torch

# 获得张量中元素的个数
a = torch.empty(3, 3)
print(torch.numel(a))
# 张量的判断
print(torch.is_tensor(a))
# 张量的类型转换
print(a.type(torch.FloatTensor))  #type:通用转换
print(a.int())

### 基本运算
a = torch.rand(3, 4)
b = torch.rand(4)

# a+b broadcasting
ab_sum1 = a + b
ab_sum2 = torch.add(a, b)
print(torch.all(ab_sum1.eq(ab_sum2)))
# a-b broadcasting
ab_sub1 = a - b
ab_sub2 = torch.sub(a, b)
print(torch.all(ab_sub1.eq(ab_sub2)))
# a*b broadcasting
ab_mul1 = a * b
ab_mul2 = torch.mul(a, b)
print(torch.all(ab_mul1.eq(ab_mul2)))
# a/b broadcasting
ab_div1 = a / b  # 整除用//
ab_div2 = torch.div(a, b)
print(torch.all(ab_div1.eq(ab_div2)))

### 矩阵乘法
c = torch.rand(2, 3)
d = torch.rand(3, 4)
# 矩阵乘法的三种方式,推荐第二种,即matmul()和第三种@
cd_mm1 = torch.mm(c, d)
cd_mm2 = torch.matmul(c, d)
cd_mm3 = c @ d
print(torch.all(cd_mm1.eq(cd_mm2)))
print(torch.all(cd_mm2.eq(cd_mm3)))

### 超过二维的矩阵乘法
e = torch.rand(4, 3, 28, 64)
f = torch.rand(4, 3, 64, 32)
# 只针对最后两维做乘法,前面的两维至少要满足能够broadcasting
ef_mm = e @ f
print(ef_mm.size())  # 输出torch.Size([4,3,28,32])

g = torch.rand(4, 1, 64, 32)
# 这里的第二个维度使用了broadcasting
eg_mm = e @ g
print(eg_mm.size())  # 输出torch.Size([4,3,28,32])

### 错误示范
# h = torch.rand(4, 64, 32)
# # 由于无法执行broadcast,报错
# eh_mm = e @ h
# print(eh_mm.size())


aa = torch.full([3, 3], 10)
### N次方
# 使用以下两种方式计算N次方
print(aa.pow(2))
print(aa ** 3)

### 平方根
print(aa.sqrt())
# 平方根的倒数
print(aa.rsqrt())
# 开三次方
print(aa ** (1 / 3))

### exp
bb = torch.exp(aa)
print(bb)

### log
a_log10 = torch.log10(aa)
a_log2 = torch.log2(aa)
b_log = torch.log(bb)  # 以e为底
print(a_log10)
print(a_log2)
print(b_log)

### 向上向下取整
aaa = torch.randn(2, 3)
a_floor = aaa.floor()  # 向下取整
a_ceil = aaa.ceil()  # 向上取整
print(a_floor)
print(a_ceil)

### 截取整数和小数
a_trunc = aaa.trunc()  # 截取整数部分
a_frac = aaa.frac()  # 截取小数部分
print(a_trunc)
print(a_frac)

### 四舍五入
a_round = aaa.round()
print(a_round)

### 最大值最小值,中值,平均
grad = torch.randn(2, 3) * 15
print(grad)
print(grad.max())  # 最大值
print(grad.min())  # 最小值
print(grad.mean())  # 平均值
print(grad.median())  # 中间值
print(grad.prod()) # 所有元素累乘
print(grad.sum()) #所有元素求和
# 将小于10的数全部置为5,大于5的数不变
print(grad.clamp(5))
# 将数值全部限定在0-10范围,大于10的取10,小于0的取0.
print(grad.clamp(0, 10))

4、在CPU和GPU控制的内存中定义张量

#指定为代码运行的设备
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0"

5、张量间的数据操作

import torch
import torchvision
import numpy as np

#1、 维度的变换
a = torch.empty(4, 2)
print(a.reshape(2,4))

a = torch.rand(4, 1, 28, 28)
a_1 = a.view(4, 784)
print(a_1.size())
a_2 = a.view(4, 1, 28, 28)
print(a_2.size())
a_3 = a.view(4 * 1 * 28, 28)
print(a_3.size())
# 尽量不要这样转,因为乱转维度可能破坏数据的几何特性
a_4 = a.view(4, 28, 28, 1)
print(a_4.size())

#2、 squeeze对张量的维度进行压缩,在变形过程中将维度=1的维度去掉
a = torch.ones(4, 2, 1)
b=torch.squeeze(a)
c=torch.unsqueeze(b,dim=2) #为张量增加一个值为1的维度
print(a.shape,b.shape,c.shape) 

#3、 图片按比例缩放
"""size (sequence or int): Desired output size. If size is a sequence like (h, w), output size will be matched to this.
If size is an int,smaller edge of the image will be matched to this number.i.e, if height > width, 
then image will be rescaled to (size * height / width, size)."""
a = torch.ones(400,300)
a_resize=np.array(torchvision.transforms.Resize((200,100)))  # size is a sequence like (h, w),是按照比例把图像最小的一个边长放缩到200,另一边按照放缩到100。
b_resize=np.array(torchvision.transforms.Resize((50)))   #size is an int,最短边将匹配到50

print(a.shape,a_resize.shape,b_resize.shape) 

#4、 矩阵拼接
### 使用concat拼接矩阵
a = torch.rand(3, 4)
b = torch.rand(5, 4)
# 对行拼接,即3行+5行=8行。类似于excel中条目累加
ab_cat = torch.cat([a, b, ], dim=0)
print(ab_cat.size())  # 输出torch.Size([8,4])

c = torch.rand(4, 5)
d = torch.rand(4, 6)
# 对列拼接,即5列+6列=11列。类似于excel中不同字段拼接
cd_cat = torch.cat([c, d], dim=1)
print(cd_cat.size())  # 输出torch.Size([4,11])

# 在googLenet中对于Inception的拼接,是按channel进行拼接的
res_conv3 = torch.rand(4, 64, 28, 28)
res_conv1 = torch.rand(4, 128, 28, 28)
res = torch.cat([res_conv3, res_conv1], 1)
print(res.size())  # 输出torch.Size([4,192,28,28])

### 使用stack组合两个矩阵
aa = torch.rand(32, 8)
bb = torch.rand(32, 8)
# 将两个矩阵组合起来,并且在指定位置创建新维度
# 可以理解为两张图片组成一个batch,而不是两张图片拼在一起
ac_stack = torch.stack([aa, bb], dim=0)
print(ac_stack.size())  # 输出torch.Size([2,32,8])

#5、矩阵拆分
### 使用split拆分矩阵
a = torch.rand(2, 32, 8)
# 平均拆分
a1, a2 = a.split(1, dim=0)
print(a1.size())  # torch.Size([1,32,8])

b = torch.rand(7, 32, 8)
# 按个数拆分
b1, b2, b3 = b.split([3, 3, 1], dim=0)
print(b1.size())  # torch.Size([3,32,8])

### 使用chunk拆分矩阵
c = torch.rand(8, 32, 8)
# 将c拆分在dim=0上拆分为两半
c1, c2 = c.chunk(2, dim=0)
print(c1.size())
# 拆分为4份
c3, c4, c5, c6 = c.chunk(4, dim=0)
print(c3.size())
# 拆分为3份,3+3+2
c7, c8, c9 = c.chunk(3, dim=0)
print(c7.size(), c8.size(), c9.size())

#6、对数据进行检索
### 高级操作where,可以实现高度并行的赋值
a = torch.tensor([[1, 2], [3, 4]])
b = torch.tensor([[5, 6], [7, 8]])

# 我们使用一个condition矩阵来决定取a和b中的哪些值来组成c
cond = torch.ByteTensor([[0, 1], [1, 0]])
# 通过cond来选择每一个元素从a还是b中获得,1表示a,0表示b
c = torch.where(cond, a, b)
print(c)

# 还可以这样用
cond2 = torch.rand(2, 2)
c2 = torch.where(cond2 > 0.5, a, b)
print(c2)

### 高级操作gather,实现查表
# 假设33是dog,44是cat,55是fish
table = torch.tensor([33, 44, 55])
# 假设我有一个向量,所有元素都是0,1,2。对应table中dim=0的3个index
find_list = torch.tensor([2, 1, 2, 0, 0, 1, 2])
found_in_table = torch.gather(table, dim=0, index=find_list)
print(found_in_table)  # 输出tensor([55,44,55,33,33,44,55])

#7、矩阵比较
a = torch.randn(3, 4)
print(a)
# 大于,满足的位置为1,不满足的位置为0
print(a > 0)
print(torch.gt(a, 0))
# 大于等于,同上
print(a >= 0)
print(torch.ge(a, 0))
# 小于,同上
print(a < 0)
print(torch.lt(a, 0))
# 小于等于,同上
print(a <= 0)
print(torch.le(a, 0))
# 不等于,同上
print(a != 0)
# 等于,同上
print(a == 0)
print(torch.eq(a, a))

# 判断是否一样,和上面的不一样
print(torch.equal(a, a))  # 输出True(和前面不一样)

#8、找出张量中的非0值索引
randint_mat = torch.tensor([[9, 0, 0],
                            [0, 5, 0],
                            [8, 0, 8]])
print(randint_mat)
print(torch.nonzero(randint_mat))

#9、根据阈值进行数据截断
# 将小于10的数全部置为5,大于5的数不变
print(randint_mat.clamp(5))
# 将数值全部限定在3-7范围,大于7的取7,小于3的取3.
print(randint_mat.clamp(3, 7))

#10、获取数据中最大值、最小值的索引
a = torch.arange(12)
idx = torch.randperm(12)  #返回一个0到n-1的数组
a = a[idx]
a = a.view(3, 4).type(torch.float32)
print(a)

# 不带参数的argmax和argmin会把矩阵压平来返回index
print(a.argmax())
print(a.argmin())

# 如果想要在某个维度上使用argmax和argmin
# 返回每一列上最大值的index组成的向量,维度等于行的维度
print(a.argmax(dim=0))
# 获取每一列的最大值组成的向量,以及对应index组成的向量
print(a.max(dim=0))
# 返回每一行上最小值的index组成的向量,维度等于列的维度
print(a.argmin(dim=1))
# 获取每一行的最小值组成的向量,以及对应index组成的向量
print(a.min(dim=1))

### keepdim
# 返回的不是一个向量,返回保持是矩阵[3,4]--->[3,1],而不是[3]
print(a.max(dim=1, keepdim=True).values.size())  # torch.Size([3,1])

### 获取topk
# 获取最大top2,[3,4]--->[3,2]
print(a.topk(2, dim=1))
# 获取最小top3,[3,4]--->[3,3]
print(a.topk(3, dim=1, largest=False))

### 获取第n小
# 获取每行第3小的数及index
print(a.kthvalue(3, dim=1))
# 获取每列第2小的数及index
print(a.kthvalue(2, dim=0))

reshape和view的区别

  • view函数和reshape函数在我们使用过程中的基本功能是类似的,reshape可在不连续的情况下不会报错,view()方法比reshape()方法更底层也更不智能,只能作用于整块内存中的张量,在pytorch中有些张量是由不同的数据块组成的,它们并没有分布在相同的整块内存中,view()方法无法对这样的张量进行变形处理,同样view()方法也无法对已经用transpose() 、puemute()等方法改变的形状进行变形处理,如果要用view()方法,则最好和contiguous()方法一起使用,contiguous()方法可以把张量复制到连续的整块内存中,示例代码如下:
import torch
b=torch.tensor([[5,6,7],[1,2,3]])
print(b.is_contiguous()) #判断内存是否连续 

c=b.transpose(0,1)  #PyTorch 两大转置函数 transpose() 和 permute()
print(c.is_contiguous())
print(c.contiguous().is_contiguous())
print(c.contiguous().view(-1))
  • 对于-1的用法,两者都是:如果输入(B, C, H, W)经过view(B, -1)或者reshape(B, -1)都会变成(B, W×H×C),而不是(B, C×H×W),这是需要特别注意的。【即反着来的】
  • 只需要记住了,每次在使用view()之前,该tensor只要使用了transpose()和permute()这两个函数一定要contiguous().

详细区别请参照:分析pytorch中的view和reshape_laizi_laizi的博客-CSDN博客_view和reshape

6、Variable类型与自动微分模块

Tensor是Pytorch的一个完美组件(可以生成高维数组),但是要构建神经网络还是远远不够的,我们需要能够计算图的Tensor,那就是Variable。Variable是对Tensor的一个封装,操作和Tensor是一样的,但是每个Variable都有三个属性,Varibale的Tensor本身的.data,对应Tensor的梯度.grad,以及这个Variable是通过什么方式得到的.grad_fn。

# 通过一下方式导入Variable
from torch.autograd import Variable
import torch
x_tensor = torch.randn(10,5)
y_tensor = torch.randn(10,5)

#将tensor转换成Variable
x = Variable(x_tensor,requires_grad=True) #Varibale 默认时不要求梯度的,如果要求梯度,需要说明
y = Variable(y_tensor,requires_grad=True)
z = torch.sum(x + y)
print(z.data) #z的Tensor数值
print(z.grad_fn) #grad_fn得到其是通过sum这种方式得到的

z.backward()
print(x.grad) #通过.grad得到了x和y的梯度
print(y.grad)

#构建一个y = x^2 函数 求x = 2 时y的导数
import numpy as np
import torch
from torch.autograd import Variable
# 1、画出函数图像
import matplotlib.pyplot as plt
x = np.arange(-3,3.01,0.1)
y = x**2
plt.plot(x,y)
plt.plot(2,4,'ro')
plt.show()

#定义点variable类型的x = 2

x = Variable(torch.FloatTensor([2]),requires_grad=True)
y = x ** 2
y.backward()
print(x.grad)

此处代码来自 Pytorch之认识Variable


三. Datasets and DataLoaders

PYTORCH学习_第3张图片

1、DataSets

Dataset的作用是Loading a Dataset,即提供一种方式去获取数据及其label,有两种方法

一种方法:加载网上已有的数据集如CIFAR-10、FashionMNIST,此种方法注意以下3点:

  • 所定义的 dataset 数据集的类别标签储存于 dataset.classes 中
  • 使用 torch.utils.data.DataLoader 加载 dataset 时,其类别标签返回的是相应类别的索引,而非类别标签本身
  • 在训练模型时,直接使用类别标签的索引作为 target ,若有需要,可在训练结束后进行索引和类别的转换即可
import torch
from torch.utils.data import Dataset
from torchvision import datasets
from torchvision.transforms import ToTensor
import matplotlib.pyplot as plt
#1、直接从网上下载
training_data = datasets.FashionMNIST(
    root="data",
    train=True,  #train为True下载训练集,为False下载测试集
    download=True, #是否从网上下载
    transform=ToTensor() #数据是否变换
)
 
test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor()
)

figure = plt.figure(figsize=(8, 8))
cols, rows = 3, 3
for i in range(1, cols * rows + 1):
    sample_idx = torch.randint(len(training_data), size=(1,)).item()
    img, label = training_data[sample_idx]
    figure.add_subplot(rows, cols, i)
    plt.title(labels_map[label])
    plt.axis("off")
    plt.imshow(img.squeeze(), cmap="gray")
plt.show()
 
 
#2、从电脑路径读取
root='D:/jupyter/TORCH/data/FashionMNIST/'
training_data = datasets.FashionMNIST(
    root=root,
    train=True,  #train为True下载训练集,为False下载测试集
    download=False, #是否从网上下载
    transform=ToTensor() #数据是否变换
)
test_data = datasets.FashionMNIST(
    root=root,
    train=False,
    download=False,
    transform=ToTensor()
)

#类别标签
labels_map={}
for key,value in enumerate(training_data.classes):
    labels_map[key]=value
    print(key,value)

另一种方法,加载自定义的数据集

自定义 dataset 时,需要重写 __len__ 和 __getitem__

  • __len__ 提供 dataset 的大小
  • __getitem__ 提供 dataset 的索引
  • PYTORCH学习_第4张图片

import os
from PIL import Image
from torch.utils.data import Dataset

class MyDataset(Dataset):
    def __init__(self, root_dir, label_dir):
        """
        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):
        """
        getitem接收一个index,返回图片数据和标签,
        这个index通常指的是一个list的index,这个list的每个元素就包含了图片数据的路径和标签信息
        """
        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="D:/jupyter/TORCH/data/filmstar/"
label_dir="Gregory_Peck"
idol_dataset=MyDataset(root_dir, label_dir)
#数据集中第1个图片
img,label=idol_dataset[0]
img.show()

2、DataLoader

pytorch 的数据加载到模型的操作顺序是这样的:

① 创建一个 Dataset 对象
② 创建一个 DataLoader 对象
③ 循环这个 DataLoader 对象,将img, label加载到模型中进行训练

DataLoader,它是PyTorch中数据读取的一个重要接口,该接口定义在dataloader.py中,只要是用PyTorch来训练模型基本都会用到该接口(除非用户重写…),该接口的目的:将自定义的Dataset根据batch size大小、是否shuffle等封装成一个Batch Size大小的Tensor,用于后面的训练。

官方对DataLoader的说明是:

“数据加载由数据集和采样器组成,基于python的单、多进程的iterators来处理数据。”

先介绍一下DataLoader(object)的参数:

dataset(Dataset): 传入的数据集

batch_size(int, optional): 每个batch有多少个样本

shuffle(bool, optional): 在每个epoch开始的时候,对数据进行重新排序

sampler(Sampler, optional): 自定义从数据集中取样本的策略,如果指定这个参数,那么shuffle必须为False

batch_sampler(Sampler, optional): 与sampler类似,但是一次只返回一个batch的indices(索引),需要注意的是,一旦指定了这个参数,那么batch_size,shuffle,sampler,drop_last就不能再制定了(互斥——Mutually exclusive)

num_workers (int, optional): 这个参数决定了有几个进程来处理data loading。0意味着所有的数据都会被load进主进程。(默认为0)

collate_fn (callable, optional): 将一个list的sample组成一个mini-batch的函数

pin_memory (bool, optional): 如果设置为True,那么data loader将会在返回它们之前,将tensors拷贝到CUDA中的固定内存(CUDA pinned memory)中.

drop_last (bool, optional): 如果设置为True:这个是对最后的未完成的batch来说的,比如你的batch_size设置为64,而一个epoch只有100个样本,那么训练的时候后面的36个就被扔掉了…
如果为False(默认),那么会继续正常执行,只是最后的batch_size会小一点。

timeout(numeric, optional): 如果是正数,表明等待从worker进程中收集一个batch等待的时间,若超出设定的时间还没有收集到,那就不收集这个内容了。这个numeric应总是大于等于0。默认为0

worker_init_fn (callable, optional): 每个worker初始化函数 If not None, this will be called on each
worker subprocess with the worker id (an int in [0, num_workers - 1]) as
input, after seeding and before data loading. (default: None)
一个简单的DataLoader

#定义好Dataset数据类,之后使用DataLoader导入
from torch.utils.data import DataLoader
train_dataloader = DataLoader(idol_dataset, batch_size=4, shuffle=True)

和 Dataset结合使用

import numpy as np
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
import torchvision
from torch.utils.data import DataLoader
# 加载数据
compose_transforms=transforms.Compose([transforms.ToTensor()]) 
train_datasets=torchvision.datasets.CIFAR10(root='D:/jupyter/TORCH/data/CIFAR10/',train= True,transform= compose_transforms,download= False)  

train_dataloader = DataLoader(train_datasets, batch_size=64, shuffle=True, num_workers=0,drop_last=False)

for data in train_dataloader:
    imgs,targets=data
    print("imgs:",imgs.shape)
    print("-"*5)
    print("targets:",targets,targets.shape)
    print("="*15)


四. torch.nn

torch.nn方法 解释 参考链接
Containers Containers即容器,比较常见的有Module, ModuleList, Sequential, ModuleDict等。Module是构建神经网络的基类(Base Class)。 Pytorch(四):神经网络之torch.nn——containers_犬冢紬希的博客-CSDN博客
Convolution Layers 卷积层,主要是用一个卷积 对原图片进行扫描(一格一格扫描)  图片参数与卷积参数与运算   得出简化的图片数据 Conv2d — PyTorch 1.9.1 documentation
Pooling layers 池化层,为了减少运算量和数据维度而设置的一种层 吴恩达深度学习笔记(79)-池化层讲解(Pooling layers) - 简书
Padding Layers 填充层 https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md
Non-linear Activations (weighted sum, nonlinearity) 非线性激活,加入非线性因素,以解决线性模型的缺陷,常跟在全连接层后面,如:
nn.Linear(512, 512),
nn.ReLU(),
pytorch系列文档之Non-linear activations详解(ReLU、Sigmoid、Tanh)_yrwang_xd的博客-CSDN博客
Non-linear Activations (other) (other)  
Normalization Layers 正则化层,卷积层之后总会添加Norm进行数据的归一化处理,这使得数据在进行Relu之前不会因为数据过大而导致网络性能的不稳定 https://blog.csdn.net/weixin_40519315/article/details/104798984
Recurrent Layers 循环(也叫递归)神经网络层,包含nn.RNN、nn.LSTM、nn.GRU,可以预测下一个时序的数据 Pytorch 神经网络模块之 Recurrent Layers - _yanghh - 博客园
Transformer Layers 带有注意力机制的预训练模型 图解Transformer(完整版)_龙心尘-CSDN博客_transformer
Linear Layers 用于设置网络中的全连接层的,需要注意的是全连接层的输入与输出都是二维张量,一般形状为 [batch_size, size] Pytorch 神经网络模块之 Linear Layers - _yanghh - 博客园
Dropout Layers 一种防止神经网络过拟合的手段。随机的拿掉网络中的部分神经元,从而减小对W权重的依赖,以达到减小过拟合的效果。在训练过程中,使用来自伯努利分布的样本,以概率p随机地将输入张量的一些元素归零。在每次forward call时,每个通道都将独立归零。常跟在全连接层后面,如:
nn.Linear(512, 512),
m = nn.Dropout(p=0.2),
Dropout — PyTorch 1.9.1 documentation
Sparse Layers 稀疏层
nn.Embedding:保存词嵌入和用下标检索它们
nn.EmbeddingBag:用于计算嵌入的”袋子”的总和或平均值
pytorch学习之nn.Embedding和nn.EmbeddingBag_yuanyue-CSDN博客
Distance Functions CosineSimilarity、PairwiseDistance 看pytorch文档学深度学习——Distance function - 知乎
Loss Functions 损失函数,又叫目标函数,用于计算真实值和预测值之间差异的函数 Pytorch 的损失函数Loss function_rosefun96的博客-CSDN博客
Vision Layers vision 层 https://blog.csdn.net/u014380165/article/details/79119664https://blog.csdn.net/g11d111/article/details/82855946
Shuffle Layers 该OP将输入 x 的通道混洗重排。 它将每个组中的输入通道分成 group 个子组,并通过逐一从每个子组中选择元素来获得新的顺序。
用来进行不同分组的特征之间的信息流动, 以提高性能
https://zhuanlan.zhihu.com/p/113211369
shuffle_channel-API文档-PaddlePaddle深度学习平台
DataParallel Layers (multi-GPU, distributed) 平行计算层  
Utilities 其它功能  
Quantized Functions    
Lazy Modules Initialization    

在定义模型实操之前,我们先介绍下torch.nn,熟悉TensorFlow的同学知道,TensorFlow通过构建图。添加变量和操作,并按照逐层建立神经网络的顺序传递它们(让张量流动),来实现模型的运算,PyTorch作为深度学习的框架,主要是利用torch.nn来搭建框架(骨架)、定义网络的层(卷积层、池化层、填充层)、同时forward(input)可以返回output。

torch.nn中有如下方法

1、    Containers

1.1 torch.nn.Module    
Base class for all neural network modules.

orch.nn.Module是所有网络的基类。我们创建的任何模型都应该继承这个类。

Modules也可以包含其它Modules,允许使用树结构嵌入他们。你可以将子模块赋值给模型属性。













构建模型

import torch.nn as nn
import torch.nn.functional as F

"""
super(Model, self).__init__()
对继承自父类nn.Module的属性进行初始化。而且是用父类nn.Module的初始化方法来初始化继承的属性。
也就是说,子类Model继承了父类nn.Module的所有属性和方法,父类nn.Module属性自然会用父类nn.Module方法来进行初始化。
当然,如果初始化的逻辑与父类nn.Module的不同,不使用父类nn.Module的方法,自己重新初始化也是可以的
"""
class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.conv1 = nn.Conv2d(1, 20, 5)
        self.conv2 = nn.Conv2d(20, 20, 5)

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

1.2 torch.nn.Sequential    
A sequential container.

一个时序容器。Modules 会以他们传入的顺序被添加到容器中。

#定义模型结构  注释部分=nn.Sequential部分,在初始化时将网络结构添加到Sequential中,forward里直接调用即可,不用重新调一遍网络结构
class model(nn.Module):
    def __init__(self):
        super(model,self).__init__()
        #第一次卷积,从3@32*32经过5*5变成32@32*32,stride和stride经过上面换算stride=1,padding=2
#         self.conv1=nn.Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2)
#         self.maxpool1=nn.MaxPool2d(kernel_size=2,ceil_mode=True)
#         self.conv2=nn.Conv2d(in_channels=32,out_channels=32,kernel_size=5,stride=1,padding=2)
#         self.maxpool2=nn.MaxPool2d(kernel_size=2,ceil_mode=True)
#         self.conv3=nn.Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=2)
#         self.maxpool3=nn.MaxPool2d(kernel_size=2,ceil_mode=True)
#         self.flatten=nn.Flatten()
#         self.linear1=nn.Linear(64*4*4,64)
#         self.linear2=nn.Linear(64,10)
        self.model1=nn.Sequential(nn.Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2),
                                  nn.MaxPool2d(kernel_size=2,ceil_mode=True),
                                  nn.Conv2d(in_channels=32,out_channels=32,kernel_size=5,stride=1,padding=2),
                                  nn.MaxPool2d(kernel_size=2,ceil_mode=True),
                                  nn.Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=2),
                                  nn.MaxPool2d(kernel_size=2,ceil_mode=True),
                                  nn.Flatten(),
                                  nn.Linear(64*4*4,64),
                                  nn.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)  
        x=self.model1(x)
        return x

1.3 torch.nn.ModuleList    
Holds submodules in a list.

ModuleList    和 Sequential    的区别:详解PyTorch中的ModuleList和Sequential - 知乎

import torch
from torch.autograd import Variable
import torch.nn as nn

# 创建一个model-1
class MyModule(nn.Module):
    def __init__(self):
        super(MyModule, self).__init__()
        # 构建一个moduleList,里面连续放10个linear字Module
        self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(10)])
        
    def forward(self, x):
        # 可以这样取出来
        for i, l in enumerate(self.linears):
            x = l(x)
        return x

import torch.nn as nn

print(torch.initial_seed())  #查看随机数种子
torch.manual_seed(2) #设定随机数种子
print(torch.initial_seed())  #查看随机数种子

data=torch.randn(8,10)

# 创建一个model-2
class MyModule2(nn.Module):
    def __init__(self):
        super(MyModule2, self).__init__()
        # 构建一个moduleList,里面连续放10个linear字Module
        self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(10)])
        
    def forward(self, x):
        for i, l in enumerate(self.linears):
            # 也可以根据索引来取
            x = self.linears[i // 2](x)
            print("="*5)
        return x
    
model = MyModule2()
print(model(data))

"""
for i in range(10):
    print(i // 2)
0
0
1
1
2
2
3
3
4
4

MyModule2(
  (linears): ModuleList(
    (0): Linear(in_features=10, out_features=10, bias=True)
    (1): Linear(in_features=10, out_features=10, bias=True)
    (2): Linear(in_features=10, out_features=10, bias=True)
    (3): Linear(in_features=10, out_features=10, bias=True)
    (4): Linear(in_features=10, out_features=10, bias=True)
    (5): Linear(in_features=10, out_features=10, bias=True)
    (6): Linear(in_features=10, out_features=10, bias=True)
    (7): Linear(in_features=10, out_features=10, bias=True)
    (8): Linear(in_features=10, out_features=10, bias=True)
    (9): Linear(in_features=10, out_features=10, bias=True)
  )
)
"""

1.4 torch.nn.ModuleDict    
Holds submodules in a dictionary. 

代码来源:【翻译】class torch.nn.ModuleDict(modules=None)_敲代码的小风-CSDN博客

ModuleDict  ModuleDict章节

class torch.nn.ModuleDict(modules=None)
类型 class torch.nn.ModuleDict(modules=None)
    Holds submodules in a dictionary.
    该类能够以字典的方式持有子模块.
    ModuleDict can be indexed like a regular Python dictionary, but modules it contains 
    are properly registered, and will be visible by all Module methods.
    ModuleDict 类型能够像普通Python字典一样被索引访问,但是它和普通Python字典不同的是,该类型所
    包含的模块会被正确地注册登记,并且这些模块能被所有地Module模块方法可见.
    ModuleDict is an ordered dictionary that respects
    ModuleDict 类型是一个有序字典,它遵循:
        the order of insertion, and
        插入地先后顺序,并且
        in update(), the order of the merged OrderedDict or another ModuleDict 
        (the argument to update()).
		在方法update()中,遵循被合并的有序字典OrderedDict的顺序或者
		另一个ModuleDict(即,传递给方法update()的参数)的顺序.
    Note that update() with other unordered mapping types (e.g., Python’s plain dict) does 
    not preserve the order of the merged mapping.
    值得注意的是,在这个update()方法中如果传递了一个无序的映射类型(比如,Python的普通字典),那么不会
    保持被合并的这个映射类型的顺序.

    Parameters  参数
        modules (iterable, optional) – a mapping (dictionary) of (string: module) or 
        an iterable of key-value pairs of type (string, module)
		modules (iterable可迭代类型, 可选) – 一个映射(字符串:模块)类型(字典)或者
		一个键值对(字符串,模块)类型的可迭代对象.


    Example:  例子:

    class MyModule(nn.Module):
        def __init__(self):
            super(MyModule, self).__init__()
            self.choices = nn.ModuleDict({
                    'conv': nn.Conv2d(10, 10, 3),
                    'pool': nn.MaxPool2d(3)
            })
            self.activations = nn.ModuleDict([
                    ['lrelu', nn.LeakyReLU()],
                    ['prelu', nn.PReLU()]
            ])

        def forward(self, x, choice, act):
            x = self.choices[choice](x)
            x = self.activations[act](x)
            return x

    clear()
    方法: clear()
        Remove all items from the ModuleDict.
        移除ModuleDict中的所有项目.

    items()
    方法: items()
        Return an iterable of the ModuleDict key/value pairs.
        返回一个ModuleDict的键/值对的可迭代对象.

    keys()
    方法: keys()
        Return an iterable of the ModuleDict keys.
        返回ModuleDict关键字的可迭代对象.

    pop(key)
    方法: pop(key)
        Remove key from the ModuleDict and return its module.
        在ModuleDict中移除关键字key.并且返回这个关键字对应的模块.
        Parameters  参数
            key (string) – key to pop from the ModuleDict
            
    update(modules)
    方法: update(modules)
        Update the ModuleDict with the key-value pairs from a mapping or an iterable, 
        overwriting existing keys.
        用一个键值对的映射类型或者可迭代对象来更新ModuleDict,覆写已经存在的关键字.
        Note  注意:
        If modules is an OrderedDict, a ModuleDict, or an iterable of key-value pairs, 
        the order of new elements in it is preserved.
        如果modules参数是一个有序字典OrderedDict或者ModuleDict或者键值对的可迭代对象,
        那么新元素的顺序也同样被维持.
        Parameters  参数
            modules (iterable) – a mapping (dictionary) from string to Module, or an 
            iterable of key-value pairs of type (string, Module)
            modules (iterable可迭代对象) – 字符串映射到Module模块的映射类型(字典),或者
            是键值对(字符串,Module模块)类型的可迭代对象.

    values()
    方法: values()
        Return an iterable of the ModuleDict values.
        返回一个ModuleDict键值对中的值的可迭代对象.

1.5 torch.nn.ParameterList    
Holds parameters in a list.

1.6 torch.nn.ParameterDict    
Holds parameters in a dictionary.
 


2、    Convolution Layers


3、    Pooling layers


4、    Padding Layers


5、    Non-linear Activations (weighted sum, nonlinearity)


6、    Non-linear Activations (other)


7、    Normalization Layers


8、    Recurrent Layers


9、    Transformer Layers


10、    Linear Layers


11、    Dropout Layers


12、    Sparse Layers


nn.Embedding:创建一个词嵌入模型,记录一共有多少个词以及为每个词创建一个多少维的向量来表示它,从基追踪和压缩感知的角度看, embedding就是找一个 基字典, 能把原来稀疏的one/multi hot编码的向量表达为稠密的向量.

PYTORCH学习_第5张图片

也就是说,假如我们有一个100W X10W的矩阵,用它乘上一个10W X 20的矩阵,我们可以把它降到100W X 20,瞬间量级降了。。。10W/20=5000倍!!!

然后中间那个10W X 20的矩阵,可以理解为查询表,也可以理解为映射表,也可以理解为过度表。

"""
torch.nn.Embedding(numembeddings,embeddingdim)的意思是创建一个词嵌入模型,
numembeddings代表一共有多少个词, 
embedding_dim代表你想要为每个词创建一个多少维的向量来表示它
"""
import torch
from torch import nn

embedding = nn.Embedding(5, 4) # 假定字典中只有5个词,词向量维度为4
word = [[1, 2, 3],
        [2, 3, 4]] # 每个数字代表一个词,例如 {'!':0,'how':1, 'are':2, 'you':3,  'ok':4}
         		   #而且这些数字的范围只能在0~4之间,因为上面定义了只有5个词
print(len(word), len(word[0])) #词的维度
embed = embedding(torch.LongTensor(word))
print(embed) 
#embed输出的维度是[2,3,4],这就代表对于输入维度为2x3的词,每个词都被映射成了一个4维的向量。
print(embed.size())

nn.EmbeddingBag:nn.EmbeddingBag:对各个袋子里词向量求和或求平均,这些袋子是通过offsets分成的,具体看下面示例代码:

import torch
from torch import nn
# 定义一个[10,3]的embedding,其中bag的方式是sum(默认是mean)
embedding_sum = nn.EmbeddingBag(5, 4, mode='sum')
# offsets会分为两个bags:input[0:4]和input[4:]
input = torch.tensor([1,2,4,3,4,3,2,1], dtype=torch.long)
offsets = torch.tensor([0,2], dtype=torch.long)
#这两个bags分别从embedding_sum中取数,并进行sum操作。
embedding_sum(input, offsets)
#tensor([[-1.0517, -0.3359,  2.7502,  3.2271],
#        [-2.5845,  1.8707,  7.7038,  2.1080]])

#-----------------------------------------------------------------
#换个更简单理解的我们假若input已经创建好一个词嵌入模型embedding_matrix
import torch.nn.functional as F
embedding_matrix=torch.tensor([
        [0.4374, 0.1339, 0.7450],
        [0.0474, 0.9686, 0.7150],
        [0.8620, 0.7665, 0.6189],
        [0.2219, 0.7626, 0.8072],
        [0.4802, 0.6104, 0.3866],
        [0.6612, 0.7640, 0.8739],
        [0.5572, 0.4434, 0.3521],
        [0.9706, 0.6686, 0.1007],
        [0.0546, 0.2351, 0.3617],
        [0.2108, 0.9643, 0.4921]])
inputs = torch.tensor([4,5,4,3,2])
offsets = torch.tensor([0, 3, 1])
#结果是
result=F.embedding_bag(embedding_matrix, inputs, offsets)
print(result)

"""
result
tensor([[0.5405, 0.6616, 0.5490],
        [0.0000, 0.0000, 0.0000],
        [0.5563, 0.7259, 0.6716]])
"""

#1、根据offsets我们解析embedding_bag是怎么工作的

"""
sample_1: inputs[0:3] # tensor([4, 5, 4])
sample_2: inputs[3:1] # tensor([])
sample_3: inputs[1:]  # tensor([5, 4, 3, 2])
"""

#2、根据上面的samples提取embedding_matrix相关索引数据并取平均(embedding_bag默认平均)
"""
# tensor([4, 5, 4])    => lookup 4, 5, 4     => (embedding_matrix[4]+embedding_matrix[5]+embedding_matrix[4])                     求平均 => tensor([0.5405, 0.6616, 0.5490])
# tensor([])           => empty bag                                                                                                     => tensor([0., 0., 0.])
# tensor([5, 4, 3, 2]) => lookup 5, 4, 3, 2  => (embedding_matrix[5]+embedding_matrix[4]+embedding_matrix[3]+embedding_matrix[2]) 求平均 => tensor([0.5563, 0.7259, 0.6716])

"""

很好的学习资料 :embedding 浅谈


13、    Distance Functions


14、    Loss Functions


15、    Vision Layers


16、    Shuffle Layers


17、    DataParallel Layers (multi-GPU, distributed)


18、    Utilities


19、    Quantized Functions


20、    Lazy Modules Initialization
 


五. Build Model

定义模型步骤,结构如下:

1、初始化网络结构:定义该类中的网络层结构

2、正向传播接口:将网络层结构按照正向传播的顺序搭建起来

3、预测接口:利用搭建好的正向传播接口预测结果

4、损失值接口:计算模型预测的结果和真实值之间的误差,在反向传播时使用

5、如何训练模型

1、初始化网络结构:定义该类中的网络层结构

2、正向传播接口:将网络层结构按照正向传播的顺序搭建起来

3、预测接口:利用搭建好的正向传播接口预测结果

4、损失值接口:计算模型预测的结果和真实值之间的误差,在反向传播时使用

5、如何训练模型

训练过程可以用“三实例五过程”来概括,三实例依次是模型实例化model = MyModel()、损失函数实例化criterion、优化器实例化optimizer,也就是我们开始使用模型工作了,同时选择合适的损失函数和优化器,五过程依次是1)将数据传入模型、2)计算预测值和真实值的差异、3)梯度清零、4)反向传播计算得到每个参数的梯度值、5)通过梯度下降执行一步参数更新。

在遍历epochs的过程中依次用到optimizer.zero_grad(),loss.backward()optimizer.step()三个函数,这三个函数的作用是先将梯度归零(optimizer.zero_grad()),然后反向传播计算得到每个参数的梯度值(loss.backward()),最后通过梯度下降执行一步参数更新(optimizer.step())

#1、实例化模型
model = MyModel()
#2、实例化损失函数
criterion = nn.CrossEntropyLoss()
#3、实例化优化器
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4)
 
#遍历epochs的过程中依次用到optimizer.zero_grad(),loss.backward()和optimizer.step()三个函数
for epoch in range(1, epochs):
    running_loss=0.0  #记录每一轮的误差
    for i, (inputs, labels) in enumerate(train_loader):
#        1)、数据传入模型
        output= model(inputs)
#        2)、计算预测值和真实值的差异
        loss = criterion(output, labels)
        
        # compute gradient and do SGD step
#        3)、将每个参数的梯度值设为0,即上一次的梯度记录被清空。
        optimizer.zero_grad()
#        4)、反向传播计算得到每个参数的梯度值,tensor的梯度将会累加到它的.grad属性里面去。
        loss.backward()
#        5)、通过梯度下降执行一步参数更新
        optimizer.step()
        running_loss+=loss
    print(epoch,running_loss)

6、完整模型训练套路

定义网络结构cifar_classes.py如下:

import torch
import torch.nn as nn

#定义模型结构
class cifar_model(nn.Module):
    def __init__(self):
        super(cifar_model,self).__init__()
        #第一次卷积,从3@32*32经过5*5变成32@32*32,stride和stride经过上面换算stride=1,padding=2 3@(in_channels=3)  32@(out_channels=32)
        self.model=nn.Sequential(nn.Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2),
                                  nn.MaxPool2d(kernel_size=2,ceil_mode=True),
                                  nn.Conv2d(in_channels=32,out_channels=32,kernel_size=5,stride=1,padding=2),
                                  nn.MaxPool2d(kernel_size=2,ceil_mode=True),
                                  nn.Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=2),
                                  nn.MaxPool2d(kernel_size=2,ceil_mode=True),
                                  nn.Flatten(),
                                  nn.Linear(64*4*4,64),
                                  nn.Linear(64,10))
    
    def forward(self,x): 
        x=self.model(x)
        return x

if __name__=='__main__':
    #测试用,运行此程序,看是否输出报错
    cifar_model=cifar_model()
    inputs=torch.ones((64,3,32,32))
    outputs=cifar_model(inputs)
    print(outputs.shape)
    

完整训练和测试如下:

import torch
import torchvision
import torch.nn as nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
#导入 cifar_model
import os
origin="D:/jupyter/TORCH/"
os.chdir(origin)
import cifar_classes as CM

train_data=torchvision.datasets.CIFAR10(root="D:/1corpus_chapter/cifar10/",
                                        train=True,
                                        transform=torchvision.transforms.ToTensor(),
                                        download=False)

test_data=torchvision.datasets.CIFAR10(root="D:/1corpus_chapter/cifar10/",
                                        train=False,
                                        transform=torchvision.transforms.ToTensor(),
                                        download=False)

#有多少个训练集/测试集
train_data_size=len(train_data)
test_data_size=len(test_data)

#利用DataLoader加载数据集
train_dataloader=DataLoader(train_data,batch_size=64, shuffle=True, num_workers=0,drop_last=False)
test_dataloader=DataLoader(test_data,batch_size=64, shuffle=True, num_workers=0,drop_last=False)


#开始训练模型
#1、实例化模型
model = CM.cifar_model()
#2、实例化损失函数
criterion = nn.CrossEntropyLoss()
#3、实例化优化器
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4)
 
#记录训练的次数
total_train_step=0
#记录测试的次数
total_test_step=0
#训练的轮数 
epochs=20
#添加tensorboard
os.chdir('D:/1corpus_chapter/')  #logs文件夹上一层地址
writer=SummaryWriter("logs")
#遍历epochs的过程中依次用到optimizer.zero_grad(),loss.backward()和optimizer.step()三个函数
for epoch in range(1, epochs):
    running_loss=0.0  #记录每一轮的误差
#    model.train()  #只有网络结构中有Dropout,BatchNorm,此行才有作用,否则注释掉也不影响训练
    for i, (inputs, labels) in enumerate(train_dataloader):
#        1)、数据传入模型
        output= model(inputs)
#        2)、计算预测值和真实值的差异
        loss = criterion(output, labels)
        
        # compute gradient and do SGD step
#        3)、将每个参数的梯度值设为0,即上一次的梯度记录被清空。
        optimizer.zero_grad()
#        4)、反向传播计算得到每个参数的梯度值,tensor的梯度将会累加到它的.grad属性里面去。
        loss.backward()
#        5)、通过梯度下降执行一步参数更新
        optimizer.step()
        running_loss+=loss
        total_train_step+=1
        if total_train_step%100==0:
            writer.add_scalar("train_loss",loss,total_train_step)
    #        print("第{}次训练".format(total_train_step),"本次误差为{}".format(loss))
    print("="*10,"第{}轮训练".format(epoch),"本轮误差为{}".format(running_loss),"="*10)
    #利用现有模型有没有训练好,或达到预测的要求,在每轮训练后测试
#    model.eval()  #只有网络结构中有Dropout,BatchNorm,此行才有作用,否则注释掉也不影响训练
    total_test_loss=0 #记录测试误差
    total_accuracy=0 #准确率
    with torch.no_grad(): #no_grad保证不再调优
        for inputs, labels in test_dataloader:
            output= model(inputs)
            loss = criterion(output, labels)
            total_test_loss=total_test_loss+loss
            accuracy=(output.argmax(1)==labels).sum()
            total_accuracy=total_accuracy+accuracy
    print("整体测试集上的LOSS为{}".format(total_test_loss)) #分类问题最好用正确率来衡量
    print("整体测试集上的准确率为{}".format(total_accuracy/test_data_size)) 
    writer.add_scalar("test_accuracy",total_accuracy/test_data_size,total_test_step)
    total_test_step+=1
    #保存每一轮下的模型
    torch.save(model,"D:/1corpus_chapter/cifar_model/cifar_model_{}.pth".format(epoch))
    print("第{}轮模型已保存".format(epoch))
        
writer.close()
    

""" 
打开logs文件

1、打开Prompt,输入 D:   ,因为我的logs文件在D盘

2、将路径定位到 logs 文件夹的上一层 cd D:/1corpus_chapter/

3、输入:tensorboard --logdir=logs --port=6007 --host=127.0.0.1
   port后面端口号可自己自定义
"""

7、现有网络模型的修改和使用

import torch
import torchvision
#1、往现有模型中加入一层
#pretrained=True会下载模型中的参数,参数已经在模型中训练好了
vgg16_true=torchvision.models.vgg16(pretrained=True)
#vgg16的网络架构
print(vgg16_true)
vgg16_true.classifier.add_module('linear',torch.nn.Linear(1000,10))
print(vgg16_true)

#2、修改现有网络的一层
vgg16_false=torchvision.models.vgg16(pretrained=False)
#vgg16的网络架构
print(vgg16_false)
vgg16_false.classifier[6]=torch.nn.Linear(in_features=4096, out_features=10, bias=True)
print(vgg16_false)

8、网络模型的保存与读取

import torch
import torchvision
import torch.nn as nn

vgg16_false=torchvision.models.vgg16(pretrained=False)
#1、保存模型方式一  不光保存网络的结构,还保存网络的参数
torch.save(vgg16_false,"D:/aaa/vgg16_false_method_1.pth")
#加载模型
model=torch.load("D:/aaa/vgg16_false_method_1.pth")
print(model)

#2、保存方式二 不在保存网络的结构,只保存网络的参数 官方推荐因为所需空间小
torch.save(vgg16_false.state_dict(),"D:/aaa/vgg16_false_method_2.pth")
#加载模型
model=torch.load("D:/aaa/vgg16_false_method_2.pth")
print(model)

#若还想从没有保存网络结构的模型中加载网络结构,做以下操作
vgg16_false=torchvision.models.vgg16(pretrained=False)
vgg16_false.load_state_dict(torch.load("D:/aaa/vgg16_false_method_2.pth"))
print(vgg16_false)

#3、陷阱
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,"D:/aaa/tudui_method_1.pth")
#如果直接加载会报错
model=torch.load("D:/aaa/tudui_method_1.pth")

#建议将Tudui复制import过来再加载
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("D:/aaa/tudui_method_1.pth")

9、完整的模型验证套路

10、利用GPU训练

11、开源项目


六.  Loss Functions

损失函数用来评价模型的预测值真实值不一样的程度,损失函数越好,通常模型的性能越好。不同的模型用的损失函数一般也不一样。

损失函数分为经验风险损失函数结构风险损失函数。经验风险损失函数指预测结果和实际结果的差别,结构风险损失函数是指经验风险损失函数加上正则项。

当使用sigmoid作为激活函数的时候,常用交叉熵损失函数而不用均方误差损失函数

损失函数 解释 名称 应用场景
nn.L1Loss Creates a criterion that measures the mean absolute error (MAE) between each element in the input xx and target yy. 回归
nn.MSELoss Creates a criterion that measures the mean squared error (squared L2 norm) between each element in the input xx and target yy. 均方误差 回归
nn.CrossEntropyLoss This criterion combines LogSoftmax and NLLLoss in one single class. 交叉熵损失 分类
nn.CTCLoss The Connectionist Temporal Classification loss. 连接时序分类损失 以对没有对齐的数据进行自动对齐,主要用在没有事先对齐的序列化数据训练上。比如语音识别、ocr识别等等
nn.NLLLoss The negative log likelihood loss. 负对数似然损失 训练 C 个类别的分类问题
nn.PoissonNLLLoss Negative log likelihood loss with Poisson distribution of target. 泊松分布的负对数似然损失  
nn.GaussianNLLLoss Gaussian negative log likelihood loss.    
nn.KLDivLoss The Kullback-Leibler divergence loss measure KL 散度损失 KL 散度可用于衡量不同的连续分布之间的距离, 在连续的输出分布的空间上(离散采样)上进行直接回归时 很有效.
nn.BCELoss Creates a criterion that measures the Binary Cross Entropy between the target and the output: 二进制交叉熵损失 二分类任务时的交叉熵计算函数。用于测量重构的误差, 例如自动编码机. 注意目标的值 t[i] 的范围为0到1之间.
nn.BCEWithLogitsLoss This loss combines a Sigmoid layer and the BCELoss in one single class.   BCEWithLogitsLoss损失函数把 Sigmoid 层集成到了 BCELoss 类中. 该版比用一个简单的 Sigmoid 层和 BCELoss 在数值上更稳定, 因为把这两个操作合并为一个层之后, 可以利用 log-sum-exp 的 技巧来实现数值稳定.
nn.MarginRankingLoss Creates a criterion that measures the loss given inputs x1x1x2x2, two 1D mini-batch Tensors, and a label 1D mini-batch tensor yy (containing 1 or -1).    
nn.HingeEmbeddingLoss Measures the loss given an input tensor xx and a labels tensor yy (containing 1 or -1).    
nn.MultiLabelMarginLoss Creates a criterion that optimizes a multi-class multi-classification hinge loss (margin-based loss) between input xx (a 2D mini-batch Tensor) and output yy (which is a 2D Tensor of target class indices). 多标签分类损失  
nn.HuberLoss Creates a criterion that uses a squared term if the absolute element-wise error falls below delta and a delta-scaled L1 term otherwise.    
nn.SmoothL1Loss Creates a criterion that uses a squared term if the absolute element-wise error falls below beta and an L1 term otherwise. 平滑版L1损失  
nn.SoftMarginLoss Creates a criterion that optimizes a two-class classification logistic loss between input tensor xx and target tensor yy (containing 1 or -1). 2分类的logistic损失  
nn.MultiLabelSoftMarginLoss Creates a criterion that optimizes a multi-label one-versus-all loss based on max-entropy, between input xx and target yy of size (N, C)(N,C). 多标签 one-versus-all 损失  
nn.CosineEmbeddingLoss Creates a criterion that measures the loss given input tensors x_1x1x_2x2 and a Tensor label yy with values 1 or -1. cosine 损失  
nn.MultiMarginLoss Creates a criterion that optimizes a multi-class classification hinge loss (margin-based loss) between input xx (a 2D mini-batch Tensor) and output yy (which is a 1D tensor of target class indices, 0 \leq y \leq \text{x.size}(1)-10≤y≤x.size(1)−1): 多类别分类的hinge损失  
nn.TripletMarginLoss Creates a criterion that measures the triplet loss given an input tensors x1x1x2x2x3x3 and a margin with a value greater than 00. 三元组损失  
nn.TripletMarginWithDistanceLoss Creates a criterion that measures the triplet loss given input tensors aapp, and nn (representing anchor, positive, and negative examples, respectively), and a nonnegative, real-valued function (“distance function”) used to compute the relationship between the anchor and positive example (“positive distance”) and the anchor and negative example (“negative distance”).    
import torch
import torch.nn as nn
import math

inputs=torch.tensor([1,2,3],dtype=torch.float32)
targets=torch.tensor([1,2,6],dtype=torch.float32)

#1、 L1LOSS
l1loss=nn.L1Loss(reduction='sum')  #if reduction=‘mean’;  if reduction=‘sum’.
result_l1loss=l1loss(inputs,targets)
#手动计算
result_mannual_l1loss=((targets[0]-inputs[0])+(targets[1]-inputs[1])+(targets[2]-inputs[2]))
#对比
print(result_l1loss,result_mannual_l1loss)
#2、 MSELOSS
mseloss=nn.MSELoss(reduction='mean') 
result_mseloss=mseloss(inputs,targets) #ln =(xn −yn) 平方
print(result_mseloss)

#手动计算
result_mannual_mseloss=(pow((targets[0]-inputs[0]),2)+pow((targets[1]-inputs[1]),2)+pow((targets[2]-inputs[2]),2))/3
#对比
print(result_mseloss,result_mannual_mseloss)

#3、CROSSENTROPYLOSS
x=torch.tensor([0.1,0.2,0.3])
y=torch.tensor([1])
input=torch.reshape(x,(1,3)) ##Input: (N, C) where C = number of classes(分类数量), N=batchsize; Target: (N), N=batchsize

loss_cross = nn.CrossEntropyLoss()
result_crossloss=loss_cross(input,y) 
#手动计算 -如果分类正确,那么概率大,x[int(y)]就小
result_mannual_cross=-x[int(y)]+math.log(math.exp(x[0])+math.exp(x[1])+math.exp(x[2]))
print(result_crossloss,result_mannual_cross)

#4、 应用到案例中,gradient descent 梯度下降
import numpy as np
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
import torchvision
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
import torch.nn.functional as F

#定义模型结构
class model(nn.Module):
    def __init__(self):
        super(model,self).__init__()
        #第一次卷积,从3@32*32经过5*5变成32@32*32,stride和stride经过上面换算stride=1,padding=2
#         self.conv1=nn.Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2)
#         self.maxpool1=nn.MaxPool2d(kernel_size=2,ceil_mode=True)
#         self.conv2=nn.Conv2d(in_channels=32,out_channels=32,kernel_size=5,stride=1,padding=2)
#         self.maxpool2=nn.MaxPool2d(kernel_size=2,ceil_mode=True)
#         self.conv3=nn.Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=2)
#         self.maxpool3=nn.MaxPool2d(kernel_size=2,ceil_mode=True)
#         self.flatten=nn.Flatten()
#         self.linear1=nn.Linear(64*4*4,64)
#         self.linear2=nn.Linear(64,10)
        self.model1=nn.Sequential(nn.Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2),
                                  nn.MaxPool2d(kernel_size=2,ceil_mode=True),
                                  nn.Conv2d(in_channels=32,out_channels=32,kernel_size=5,stride=1,padding=2),
                                  nn.MaxPool2d(kernel_size=2,ceil_mode=True),
                                  nn.Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=2),
                                  nn.MaxPool2d(kernel_size=2,ceil_mode=True),
                                  nn.Flatten(),
                                  nn.Linear(64*4*4,64),
                                  nn.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)  
        x=self.model1(x)
        return x

model=model()
#测试模型是否可用
input=torch.ones((64,3,32,32))
output=model(input)
print(output.shape)

#加载数据
compose_transforms=transforms.Compose([transforms.ToTensor()]) 
train_datasets=torchvision.datasets.CIFAR10(root='D:/jupyter/TORCH/data/CIFAR10/',train= True,transform= compose_transforms,download= False)  

train_dataloader = DataLoader(train_datasets, batch_size=1, shuffle=True, num_workers=0,drop_last=False)

loss_cross = nn.CrossEntropyLoss()
for data in train_dataloader:
    imgs,targets=data
    result=model(imgs)
    result_crossloss=loss_cross(result,targets) 
    #torch.Size([1, 3, 32, 32]) torch.Size([1]) torch.Size([1, 10])  targets.shape!=result.shape
    print(imgs.shape,targets.shape,result.shape)
    #https://www.cnblogs.com/JeasonIsCoding/p/10164948.html
    result_crossloss.backward()


七. Optimization Loop

实现随机梯度下降算法
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
optimizer = optim.Adam([var1, var2], lr=0.0001)

for input, target in dataset:
#梯度必须初始化为零(一个batch的loss关于weight的导数是所有sample的loss关于weight导数的累加和)
    optimizer.zero_grad()
    output = model(input)
    loss = loss_fn(output, target)
    loss.backward()
    optimizer.step()

optim.SGD参数:
  • params (iterable) – 待优化参数的iterable或者是定义了参数组的dict
  • lr (float) – 学习率
  • momentum (float, 可选) – 动量因子(默认:0)
  • weight_decay (float, 可选) – 权重衰减(L2惩罚)(默认:0)
  • dampening (float, 可选) – 动量的抑制因子(默认:0)
  • nesterov (bool, 可选) – 使用Nesterov动量(默认:False)

八. Save, Load and Use Model


九. Pytorch工具集

1、torchvision

 torchvision是pytorch的一个图形库,它服务于PyTorch深度学习框架的,主要用来构建计算机视觉模型。

torchvision.transforms主要是用于常见的一些图形变换。以下是torchvision的构成:

torchvision.datasets: 一些加载数据的函数及常用的数据集接口;
torchvision.models: 包含常用的模型结构(含预训练模型),例如AlexNet、VGG、ResNet等;
torchvision.transforms: 常用的图片变换,例如裁剪、旋转等;
torchvision.utils: 其他的一些有用的方法。
 

 PYTORCH学习_第6张图片

 torchvision.transforms : 常用的图像预处理方法,提高泛化能力
• 数据中心化
• 数据标准化
• 缩放
• 裁剪
• 旋转
• 翻转
• 填充
• 噪声添加
• 灰度变换
• 线性变换
• 仿射变换
• 亮度、饱和度及对比度变换

#1、图片转换成tensor
from PIL import Image
from torchvision import transforms
import numpy as np

img_item_path="D:/jupyter/TORCH/data/filmstar/Gregory_Peck/6.jfif" #图片的位置
img=Image.open(img_item_path)
#transforms转换图片
tensor_trans=transforms.ToTensor()  #transforms.ToTensor,并将数据归一化到[0,1](是将数据除以255),transforms.ToTensor()会把HWC会变成C *H *W
img_tensor=tensor_trans(img)
print(img_tensor.shape)

#2、Normalize数据标准化
img_item_path="D:/jupyter/TORCH/data/filmstar/Gregory_Peck/6.jfif" #图片的位置
img=Image.open(img_item_path)
tensor_trans=transforms.ToTensor()
img_tensor=tensor_trans(img)
#[0,1]只是范围改变了, 并没有改变分布,mean和std处理后可以让数据正态分布
means = [0, 0, 0]
stdevs = [0, 0, 0]
for i in range(3):
    means[i] += img_tensor[i,:, :].mean()
    stdevs[i] += img_tensor[i,:, :].std()
trans_norm=transforms.Normalize(mean=means, std=stdevs)  
img_tensor_nor=trans_norm(img_tensor)

#3、缩放图片Resize
"""size (sequence or int): Desired output size. If size is a sequence like (h, w), output size will be matched to this.
If size is an int,smaller edge of the image will be matched to this number.i.e, if height > width, 
then image will be rescaled to (size * height / width, size)."""

img_item_path="D:/jupyter/TORCH/data/filmstar/Gregory_Peck/6.jfif" #图片的位置
img=Image.open(img_item_path)  #size=658x833
print(img)  #
# PILimage→resize→img_resize
trans_resize=transforms.Resize((400,300))  #是按照比例把图像最小的一个边长放缩到400,另一边按照放缩到300。
img_resize=trans_resize(img)
print(img_resize)
# img_resize→ToTensor→img_resize_tensor
img_resize_arr=np.array(img_resize)  #tensor_trans=transforms.ToTensor() img_resize_tensor=tensor_trans(img_resize)
print(img_resize_arr.shape)

#4、Compose串联多个图片变换的操作
img_item_path="D:/jupyter/TORCH/data/filmstar/Gregory_Peck/6.jfif" #图片的位置
img=Image.open(img_item_path)
  
compose_transformer = transforms.Compose([
#    transforms.ToPILImage(),  #cv2读取图片才需要,直接PIL图片此步不需要
    transforms.Resize(256),
    # transforms.RandomResizedCrop(224,scale=(0.5,1.0)),
    transforms.RandomHorizontalFlip(), #依概率p垂直翻转
    transforms.ToTensor()
])

img_compose_transformer=compose_transformer(img)
print(img_compose_transformer.shape)

2、torchtext


十. function


十一. function


十二. function


十三. function


十四. function

参考资料:

PyTorch :Torch 工具包的数学操作汇总速查_bug404-CSDN博客

理解optimizer.zero_grad(), loss.backward(), optimizer.step()的作用及原理

Transformer 输入输出维度以及 Pytorch nn.Transformer 记录-老唐笔记

你可能感兴趣的:(nlp,pytorch,深度学习,神经网络)