附录:PyTorch记事本
tensor.cuda()
在使用GPU的情况下,一般会将所有相关tensor都放到GPU上计算,所以如果仅仅model=model.cuda()
,程序将不能正确执行,因为输入tensor和输出tensor还没布置到GPU上,还需要:
x=x.cuda()
y=y.cuda()
model=model.cuda()
注意,这样的迁移比较特殊,在完成设备迁移的同时,叶子张量属性is_leaf并不会发生变化,虽然x=x.cuda()
不是in-place,但叶子节点并没有变成非叶子节点,这对训练有重要作用
torch.nn.CrossEntropyLoss
对于一般的分类训练,会写:
loss_fn=torch.nn.CrossEntropyLoss()
...
loss=loss_fn(y_pred,target)
注意到:
y_pred.shape:(batch_size,class_number);
target.shape:(batch_size);
所以target的每个元素需要是整数(一般是int64),取值范围为[0,class_number-1],这才能被函数认为是真实类别
注意一个细节:当使用了torch.nn.CrossEntropyLoss的时候,其实不需要在model最后一层添加torch.nn.Softmax(),因为在官方文档中,pytorch使用CrossEntropyLoss计算交叉熵的公式为:
这也正好解释了前面target值在[0,class_number-1]的奇怪规则
torch.no_grad()
torch.no_grad()的作用是使在with关键字下的计算图不被追踪,此时,就算叶子张量的requires_grad=True,也可以进行in-place操作:
x = torch.tensor([1],dtype=torch.float,requires_grad=True)
with torch.no_grad():x-=0.1
另一方面,计算图不被追踪的言外之意是Disabling gradient calculation(禁用梯度计算),相当于在torch.no_grad()下,显存不必将资源用于梯度计算,节省了显存,适用于inference;
model.eval()与torch.no_grad():model.eval()不开启BN和Dropout,注意只有torch.no_grad()可以关闭梯度计算,用于节省显存。为了确保梯度传播的准确,在loss.backward()前执行optimizer.zero_grad()即可。
tensor.max()
常用于索引分类结果:
tensor.max(dim=None)->(Tensor,Tensor)
dim用于选择对哪个轴方向进行操作,返回对象元组有两个元素,一个是索引到的最大值结果,一个是最大值所在轴上的位置;
假设现有前向传播的结果pred(N,4),N个样本,4个类别,若写pred.max(dim=1)
代表沿着第二轴(列轴即水平方向)求最大值
torch.bmm
批处理张量乘法bmm:batch matrix multiplication,在batch中,两组张量对应相乘:
(b,m,n)*(b,n,p)->(b,m,p)
tensor.unsqueeze和tensor.squeeze
tensor.unsqueeze用于增加维度,增加在哪一维度由关键字参数dim确定:
x=torch.randn(5,3)
x.unsqueeze(dim=2).size()
#>torch.Size([5, 3, 1])x=torch.randn(5,3)
x.unsqueeze(dim=1).size()
#>torch.Size([5, 1, 3])
对应的,tensor.squeeze用于去除冗余维度(某个维度shape为1):
x=torch.randn(5,3,1)
x.squeeze(dim=2).size()
#>torch.Size([5, 3])x=torch.randn(5,3,1)
x.squeeze(dim=0).size()
#>torch.Size([5, 3, 1])
注意squeeze和unsqueeze不是in-place的
反向传播公式的简单解释
在pytorch入门中,简要给出了反向传播计算权重梯度的公式:
∂J∂W=∂J∂za\frac{\partial J}{\partial W}=\frac{\partial J}{\partial z}a∂W∂J=∂z∂Ja
为什么要乘z前面的输出a?其实上式可以理解为:
∂J∂W=∂J∂z∂z∂W\frac{\partial J}{\partial W}=\frac{\partial J}{\partial z}\frac{\partial z}{\partial W}∂W∂J=∂z∂J∂W∂z
因为下一个激活前的输入值z为:z=WTaz=W^Taz=WTa
torch.randint
用于生成随机的整数张量(dtype=torch.int64):
randint(low=0, high, size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) -> Tensor
值在low到high之间,形状由size决定:
torch.randint(6,(2,2))
"""
tensor([[5, 1],[4, 3]])
"""
tensor.fill_
属于in-place操作,将张量内部的值进行替换,内存地址不变,fill_只支持0维张量(元素是一个数值),fill_不改变数据类型:
x=torch.tensor(1)
y=torch.tensor(2.)
x.fill_(y)
print(x)
x.dtype#tensor(2)
#torch.int64
torch.multinomial
常用于随机采样:
multinomial(input, num_samples, replacement=False, out=None) -> LongTensor
input通常是概率序列(其实只要是浮点型的一维张量就行),返回为LongTensor,内容是采样序列的索引,值越大越容易被采样到;
replacement代表有放回,设为True可以无限次采样:
x=torch.tensor([8.,9.,11.,23.,100.])
torch.multinomial(x,10,replacement=True)
#tensor([4, 0, 2, 3, 4, 3, 4, 4, 4, 0])
tensor.transpose和tensor.permute
transpose只能选择两个维度进行调整,permute可以任意设置维度的顺序:
transpose(dim0, dim1) -> Tensor
permute(*dims) -> Tensor
实例如下:
x=torch.randn(5,3,2)
x.transpose(1,2).size()#torch.Size([5, 2, 3])x=torch.randn(5,3,2)
x.permute(2,1,0).size()#torch.Size([2, 3, 5])
torch.nn.BCEWithLogitsLoss
这个损失函数只能用于二分类问题,该损失函数包括两个部分:一个sigmoid函数和BinaryCrossEntropy函数;
使用举例:
crit = nn.BCEWithLogitsLoss()
# preds [batch_size]
# batch.label [batch_size]
loss = crit(preds, batch.label)
可见网络输出与标签的shape都是[batch_size],假设对于batch中的一组数,网络输出xix_{i}xi,标签为yiy_{i}yi,则loss计算为:
loss=−[yilogσ(xi)+(1−yi)log(1−σ(xi))]loss=-[y_{i}log \sigma (x_{i})+(1-y_{i})log(1- \sigma (x_{i}))]loss=−[yilogσ(xi)+(1−yi)log(1−σ(xi))]
损失函数两个项反应了属于第0类的对象被分为第0类,属于第1类的对象被分到第1类,才能最小化loss
torch.round
对结果四舍五入:
x=torch.randn(5,3)
print(x)
y=torch.round(x)
print(y)
"""
tensor([[ 1.3226, -0.6480, -1.0701],[-1.4932, -0.6328, 1.0331],[ 0.9444, 0.4172, 0.8942],[-0.3619, 0.1024, -1.8857],[-3.2601, 1.2551, -0.5345]])
tensor([[ 1., -1., -1.],[-1., -1., 1.],[ 1., 0., 1.],[-0., 0., -2.],[-3., 1., -1.]])
"""
张量拼接torch.cat
张量可以通过cat拼接:
torch.cat(tensors, dim=0, *, out=None) → Tensor
dim决定了拼接操作基于的轴,比如:
hidden=torch.randn(4,2,3)#[4,batch_size,hidden_size]
print(hidden.size())
hidden=torch.cat((hidden[-1],hidden[-2]),dim=1)
print(hidden.size())#torch.Size([4, 2, 3])
#torch.Size([2, 6])x=torch.randn(2,3)
y=torch.randn(2,6)
torch.cat((x,y),dim=1).size()
#torch.Size([2, 9])
torch.nn.LogSoftmax和torch.nn.NLLLoss
torch.nn.LogSoftmax的本质就是对每个元素都计算LogSoftmax:
LogSoftmax(xi)=log(exp(xi)∑jexp(xj))LogSoftmax(x_{i})=log(\frac{exp(x_{i})}{\sum_{j}exp(x_{j})})LogSoftmax(xi)=log(∑jexp(xj)exp(xi))
输入输出张量的shape不会改变:
x=torch.randn(6,3)
print(x)
# 沿着列轴方向操作
F.log_softmax(x,dim=1)
"""
tensor([[ 0.5185, -0.4387, 0.8417],[ 1.0252, 0.8140, -0.1921],[ 0.6522, 0.2619, -1.5904],[-0.1942, -0.5435, 0.5055],[-1.3790, 0.2573, 0.5489],[ 1.4560, 1.9608, 0.5123]])tensor([[-1.0172, -1.9744, -0.6940],[-0.7446, -0.9558, -1.9620],[-0.5783, -0.9686, -2.8209],[-1.3133, -1.6626, -0.6136],[-2.5658, -0.9295, -0.6379],[-1.1137, -0.6090, -2.0575]])
"""torch.log(torch.exp(x[0][1])/(torch.exp(x[0][0])+torch.exp(x[0][1])+torch.exp(x[0][2])))
"""
tensor(-1.9744)
"""
一般与logsoftmax配合使用的损失函数为NLL loss(负对数似然损失),因为logsoftmax取相反数正好就是交叉熵;
把交叉熵拆分开的原因是为了给类别加上权重信息,这样可以通过训练加强某些类别的分辨能力;常用于数据量不平衡的训练集;
torch.nn.NLLLoss(weight: Optional[torch.Tensor] = None, reduction: str = 'mean')
可选参数weight是一个向量,如果数据集共C个类别,则weight的元素个数为C,它反映了每个类别的重要程度;
假设现在调用一次NLLLoss:
"""
input (N,C)
traget (N)
output 一个值,具体值取决于reduction是sum还是mean,默认mean
如果reduction=None,则返回tensor (N)
"""functional.nll_loss(pred,target)
pred应该是来自LogSoftmax计算后的张量,假设这个batch的tensor形状为[N,C][N,C][N,C],则target应该是[N][N][N],类似CrossEntropyLoss,target就像一个索引,其中每个元素值在0到C−1C-1C−1之间;
负对数似然损失NLL loss的计算为:
1.reduction=None
l(pred,target)={l1,...,lN}l(pred,target)=\left \{ l_{1},...,l_{N} \right \}l(pred,target)={l1,...,lN}
li=−(weight[target[i]])×(x[i][y[i]])l_{i}=-(weight[target[i]])\times (x[i][y[i]])li=−(weight[target[i]])×(x[i][y[i]])
2.reduction=sum
l(pred,target)=∑i=1Nlil(pred,target)=\sum_{i=1}^{N}l_{i}l(pred,target)=i=1∑Nli
3.reduction=mean
l(pred,target)=1∑j=1Nweight[target[j]]∑i=1Nlil(pred,target)=\frac{1}{\sum_{j=1}^{N}weight[target[j]]}\sum_{i=1}^{N}l_{i}l(pred,target)=∑j=1Nweight[target[j]]1i=1∑Nli
tensor.data_ptr()
在pytorch中,想要获取张量对象的地址,一般不使用id()
,而是使用tensor.data_ptr()
;
tensor.data_ptr()可以返回tensor第一个元素所在的物理地址:
data_ptr() → int
tensor.clone和tensor.detach
1.tensor.clone()
返回tensor的拷贝,返回的新tensor和原来的tensor具有同样的大小和数据类型;
但是,clone()返回的tensor是非叶子节点(有计算图连接);即:如果原tensor.requires_grad=True,则新tensor的梯度会流向原tensor,即新tensor的梯度会叠加在原tensor上:
>>> import torch
>>> a = torch.tensor(1.0, requires_grad=True)
>>> b = a.clone()>>> a.data_ptr(), b.data_ptr() # 不指向同一物理地址
(94724519502912, 94724519533952)>>> a.requires_grad, b.requires_grad # 但b的requires_grad属性和a的一样,同样是True
(True, True)
>>> c = a * 2
>>> c.backward()
>>> a.grad
tensor(2.)
>>> d = b * 3
>>> d.backward()
>>> b.grad # b的梯度值为None,因为是非叶子节点,梯度值不会被保存
>>> a.grad # b的梯度叠加在a上
tensor(5.)
2.tensor.detach()
返回一个新的tensor,新的tensor和原来的tensor共享内存,但不涉及梯度计算,即requires_grad=False,相当于从计算图中分离出来了;
因为是共享同一物理地址,修改其中一个tensor的值,另一个也会改变,但如果对其中一个tensor执行内置操作,则会报错,例如resize_、resize_as_、set_、transpose_:
>>> import torch
>>> a = torch.rand((3, 4), requires_grad=True)
>>> b = a.detach()>>> a.data_ptr(), b.data_ptr() # 指向同一物理地址
(94724518609856, 94724518609856)>>> a.requires_grad, b.requires_grad # b的requires_grad为False
(True, False)>>> b[0][0] = 1
>>> a[0][0] # 修改b的值,a的值也会改变
tensor(1., grad_fn=<SelectBackward>)>>> b.resize_((4, 3)) # 报错
关于梯度在计算图中的流动
属于同一个计算图的张量,其梯度在反向传播中累乘,对于两个不同的计算图,即计算图出现了分支,对两个分支分别计算梯度,两计算图共享张量的梯度需要求和:
import torchx=torch.tensor(1.0, requires_grad=True)
y=2*xa=torch.tensor(3.0, requires_grad=True)
b=3*a# 两个计算图,共同依赖张量x
z=4*y+b
m=3*xz.backward()
m.backward()x.grad # tensor(11.) 11=4*2+3
torch.nn.Dropout
Dropout定义为:
torch.nn.Dropout(p: float = 0.5, inplace: bool = False)
在训练期间,按照概率ppp从伯努利分布中采样将输入张量的元素置0,输出张量的元素再乘缩放系数11−p\frac{1}{1-p}1−p1;
inplace如果为True,输入张量会被设置为需要in-place操作:
import torch.nn as nn
import torchm = nn.Dropout(p=0.5)
input = torch.randn(5,3)
output = m(input)print(input)
print(output)"""
tensor([[-1.2560, -1.3158, 0.3405],[ 1.4020, -1.3544, -2.8470],[-1.4518, -0.5322, 0.8935],[-1.3207, 0.9328, -0.3308],[-1.5071, 0.6539, -1.6561]])
tensor([[-2.5120, -2.6316, 0.0000],[ 0.0000, -2.7088, -0.0000],[-2.9037, -0.0000, 1.7870],[-2.6415, 1.8656, -0.6615],[-3.0143, 1.3078, -0.0000]])
"""
torchvision:tensor与PILImage
pytorch中tensor形状为(c,h,w)(c,h,w)(c,h,w),而PILImage形状为(h,w,c)(h,w,c)(h,w,c),所以在不使用torchvision的transforms时,需要注意形状的转换
附录:PyTorch记事本相关推荐
- 第三课.使用简单的NN模拟fizzbuzz
游戏介绍 这个游戏非常简单,名叫fizzbuzz,我猜测应该起源于早期的聚会活动,游戏规则如下:从1开始计数,当遇到是3的倍数的时候,就说fizz,当遇到是5的倍数的时候,就说buzz,当遇到是3和5 ...
- 第八课.简单的图像分类(二)
目录 常见的卷积神经网络架构 卷积网络的平移不变性 卷积网络的识别原理简述 卷积神经网络的缺陷 CNN的迁移学习 迁移学习简介 数据集 使用dataloader生成batch 设置超参数 使用data ...
- 第七课.简单的图像分类(一)
第七课目录 图像分类基础 卷积神经网络 Pooling layer BatchNormalization BatchNormalization与归一化 torch.nn.BatchNorm2d MNI ...
- 第六课.NLP文本分类任务
第六课目录 NLP文本分类简介 IMDB数据集准备 设置随机种子 下载IMDB并划分数据集 构建词汇表 使用torchtext生成batch WordAveraging 模型定义 加载由glove.6 ...
- HWC格式(Torch)
人工智能小白,不对之处,希望各位大佬不吝赐教^_^ 目录 前言 正文 1.关于HWC维度的理解 2.为什么pytorch中transforms.ToTorch要把(H,W,C)的矩阵转为(C,H,W) ...
- Pytorch 小白记事本 1
Pytorch 小白记事本 1 Python pip 安装与使用 下载就不多说了,我们从判断是否安装开始记录: pip --version pip 最常用命令 显示版本和路径 pip --versio ...
- 附录1:python记事本
dir()函数:函数不带参数时,返回当前范围内的变量.方法和定义的类型列表:带参数时,返回参数的属性.方法列表. notebook中的line型开关%:针对全局 cell型开关%%:针对当前cell ...
- 364 页 PyTorch 版《动手学深度学习》分享(全中文,支持 Jupyter 运行)
1 前言 最近有朋友留言要求分享一下李沐老师的<动手学深度学习>,小汤本着一直坚持的"好资源大家一起分享,共同学习,共同进步"的初衷,于是便去找了资料,而且还是中文版的 ...
- pytorch无坑超详细图文CPU版小白安装教程(配gpu版链接、conda命令教程)
想安装gpu版本的朋友们请移步gpu版pytorchan安装教程直达 文章目录 创建.激活.退出.删除环境 法一:官网默认指令安装(可能比较慢) 法二:更换清华源下载 法三:下载包安装 版本对应问题 ...
最新文章
- 记录一下从标定模型中读取参数
- [OpenGL ES 03]3D变换:模型,视图,投影与Viewport
- java基础—几种for循环编程思想
- elementui树状菜单tree_Vue+Element UI 树形控件整合下拉功能菜单(tree + dropdown +input)...
- 光纤交换机巡检配置常用命令
- Android_adb_Wifi_无线调试,脱离数据线/
- 神经网络中的Epoch、Iteration、Batchsize
- python图片保存为txt文件_python实现对文件中图片生成带标签的txt文件方法
- 计算机应用系统统考配书光盘,统考配书光盘计算机应用基础使用手册
- 英文名字大全解释 (详)
- jquery设置css样式、style属性 示例(超强解析)
- c语言程序设计施莹答案,C语言课件-位运算.ppt
- jenkins中maven的安装及配置,如何在jenkins中创建maven任务。
- ESP8266读取网络时间TM1637显示时间
- 价值投资要做段王爷,不要做杨过
- 第四讲:各种形态的描述
- 搭建wiki oracle,Wiki系统搭建 JspWiki
- html带颜色方块,HTML5 彩色方块组合动画
- 【Visual C++】游戏开发笔记三十六 浅墨DirectX提高班之四 顶点缓存的逆袭
- java校园导航_Java实现的具有GUI的校园导航系统