pytorch 初学

Tensors

Tensors 与numpy 中的ndarrays很像,pytorch可以支持GPU操作

from __future__ import print_function
import torch
创建空的tensor
x = torch.empty(5,3) # 5行3列的空的tensors
print(x)
tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
创建随机的一个随机数矩阵
x = torch.rand(5,3)
print(x)
tensor([[0.5109, 0.1927, 0.5499],
[0.8677, 0.8713, 0.9610],
[0.9356, 0.0391, 0.3159],
[0.0266, 0.7895, 0.6610],
[0.7188, 0.1331, 0.2180]])
创建0元素的矩阵
x = torch.zeros(5,3)
print(x)
tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
直接从已经数据创建tensor
x= torch.tensor([5,5,3])
print(x)
tensor([5, 5, 3])
创建新的矩阵
x = x.new_ones(5,3,dtype=torch.double)
print(x)
# 根据现有的张量创建张量。 这些方法将重用输入张量的属性,例如, dtype,除非设置新的值进行覆盖
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
x = torch.randn_like(x,dtype=torch.float)
print(x)
# 更新了x的dtype,保留了原始的x的size
tensor([[ 0.8914,  1.5704, -0.1844],
[ 0.7747, -0.6860, -0.5596],
[ 0.1804, -0.2909, -1.3262],
[-1.3021, -0.4132, -2.7060],
[ 0.8989, -0.7269, 1.3862]])
print(x.size())
torch.Size([5, 3])

计算操作

加法操作
y = torch.rand(5,3)
print(x+y)
tensor([[ 1.6333,  2.1744,  0.4975],
[ 1.5430, -0.5863, -0.1416],
[ 0.6954, 0.6694, -0.4113],
[-0.9279, -0.1156, -1.8519],
[ 1.5791, 0.1524, 2.1037]])
print(torch.add(x,y))
tensor([[ 1.6333,  2.1744,  0.4975],
[ 1.5430, -0.5863, -0.1416],
[ 0.6954, 0.6694, -0.4113],
[-0.9279, -0.1156, -1.8519],
[ 1.5791, 0.1524, 2.1037]])
result = torch.empty(5,3)
torch.add(x,y,out=result)
print(result)
tensor([[ 1.6333,  2.1744,  0.4975],
[ 1.5430, -0.5863, -0.1416],
[ 0.6954, 0.6694, -0.4113],
[-0.9279, -0.1156, -1.8519],
[ 1.5791, 0.1524, 2.1037]])
# 加法操作,inplace 操作,替换y值,类似于y+=x
y.add_(x)
print(y)
tensor([[ 1.6333,  2.1744,  0.4975],
[ 1.5430, -0.5863, -0.1416],
[ 0.6954, 0.6694, -0.4113],
[-0.9279, -0.1156, -1.8519],
[ 1.5791, 0.1524, 2.1037]])
转化形状
x = torch.randn(4,4)
y= x.view(16)
z=x.view(-1,8) # the size -1 是根据其他维度进行计算出来的
print(x.size(),y.size(),z.size())
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
torch.numel(x)  # return the number of the input tensor
16
tensor 与numpy 的转化
import numpy as np
a = np.array([1,2,3])
t = torch.as_tensor(a)
print(t)
tensor([1, 2, 3])
a = torch.ones(5)
print(a)
tensor([1., 1., 1., 1., 1.])
b = a.numpy()  # 类似于a和b,a 变了,b也跟着变,类似于numpy 中的view的操作
print(b)
[1. 1. 1. 1. 1.]
a.add_(1)
print(a)
print(b)
tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]
id(a),id(b)
(4487109080, 4807132064)

数据在GPU上的操作

# 以下代码只有在PyTorch GPU版本上才会执行,配的mac没有GPU,所以没有显示结果
if torch.cuda.is_available():
device = torch.device("cuda") # GPU
y = torch.ones_like(x, device=device) # 直接创建一个在GPU上的Tensor
x = x.to(device) # 等价于 .to("cuda")
z = x + y
print(z)
print(z.to("cpu", torch.double)) # to()还可以同时更改数据类型

自动梯度求导

深度学习中通常需要对函数求梯度(gradient),pytorch提供的autograd包能够根据输入和前向传播过程自动构建计算图,并执行方向传播过程,后续将主要介绍autograd包实现自动求梯度的有关操作

自动求导的概念

上节介绍的Tensor是这个包的核心类,如果将其属性 .required_grad 设置为True,将开始追踪(track)在其上的所有操作(可以利用链式法则进行梯度传播了)。计算完成后,可以调用.backward() 来完成所有的梯度计算。此Tensor的梯度将累积到.grad属性中。

需要注意的是,如果调用y.backward()时,如果y是标量,则不需要为backward() 传入任何参数。其余情况,需要传入一个与y相同shape的Tensor。

如果不想被继续追踪,可以调用.detach()将其追踪记录中分离出来,这样就可以防止将来的计算被追踪,这样梯度就传不过去了。此外还可以用with torch.no_grad() 将不想被追踪的操作代码块包裹起来,这样的方法在评估模型的时候常用,因为在评估模型时,不需要计算已经训练出的参数的的梯度。

Function 类

Function是另外一个很重要的类,Tensor和Function相互结合就可以构建一个记录有整个计算过程的有向无环图(DAG)????

每个Tensor都有一个.grad_fn属性,该属性即创建Tensor的Function,就是说该Tensor是不是通过某些运算得到的,如果是,grad_fn返还一个与这些运算相关的对象,否则是None。

Tensor实例
# 创建一个Tensor并设置requires_grad=True
x= torch.ones(2,2,requires_grad=True)
print(x)
print(x.grad_fn)# 返回结果为None,x是直接创建的,则说明该Tensor不是通过运算得到
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
None
y =x+2
print(y)
print(y.grad_fn) ## AddBackward0,y是通过一个假发操作创建的 '''
想x这样直接通过创建的称为叶子节点,叶子节点对应grad_fn 是None '''
print(x.is_leaf,y.is_leaf)
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward0>)
<AddBackward0 object at 0x11eb55cc0>
True False
复杂运算
z = y*y*3
out = z.mean()
print(z,out)
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward1>)
"""
可以通过.requires_grad_()来用in_place的方式改变requires_grad的属性
"""
a= torch.randn(2,2) # 缺失的情况下默认requires_grad=False
a =(a*3)/(a-1)
print(a.requires_grad) # False
a.requires_grad_(True)
print(a.requires_grad) # True
b = (a*a).sum()
print(b.grad_fn)
False
True
<SumBackward0 object at 0x11eb65780>
梯度
'''
因为out是一个标量,所以调用backward()时不需要指定求导变量: '''
out.backward() # 等价于out.backward(torch.tensor(1.))

我们看下out关于x的梯度

\[\frac { d ( o u t ) } { d x }
\]

print(x.grad)
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])

\[o = \frac { 1 } { 4 } \sum _ { i = 1 } ^ { 4 } z _ { i } = \frac { 1 } { 4 } \sum _ { i = 1 } ^ { 4 } 3 \left( x _ { i } + 2 \right) ^ { 2 }
\]

\[\left. \frac { \partial o } { \partial x _ { i } } \right| _ { x _ { i } = 1 } = \frac { 9 } { 2 } = 4.5
\]

数学上的意义

数学上,如果有一个函数值和自变量都为向量的函数 y=f(x)

y=f(x), 那么 y关于x 的梯度就是一个雅可比矩阵(Jacobian matrix):

\[J = \left( \begin{array} { c c c } { \frac { \partial y 1 } { \partial x _ { 1 } } } & { \cdots } & { \frac { \partial y _ { 1 } } { \partial x _ { n } } } \\ { \vdots } & { \ddots } & { \vdots } \\ { \frac { \partial y _ { m } } { \partial x _ { 1 } } } & { \cdots } & { \frac { \partial y _ { m } } { \partial x _ { n } } } \end{array} \right)
\]

而torch.autograd这个包就是用来计算一些雅克比矩阵的乘积的,例如如果v是一个标量函数的L = g(y)的梯度:

\[v = \left( \begin{array} { c c c } { \frac { \partial l } { \partial y 1 } } & { \cdots } & { \frac { \partial l } { \partial y m } } \end{array} \right)
\]

那么根据链式法则,可以得到:L关于x的雅克比矩阵就是

\[v J = \left( \begin{array} { c c c } { \frac { \partial l } { \partial y _ { 1 } } } & { \cdots } & { \frac { \partial l } { \partial y _ { m } } } \end{array} \right) \left( \begin{array} { c c c } { \frac { \partial y _ { 1 } } { \partial x _ { 1 } } } & { \cdots } & { \frac { \partial y _ { 1 } } { \partial x _ { n } } } \\ { \vdots } & { \ddots } & { \vdots } \\ { \frac { \partial y _ { m } } { \partial x _ { 1 } } } & { \cdots } & { \frac { \partial y _ { m } } { \partial x _ { n } } } \end{array} \right) = \left( \begin{array} { c c c } { \frac { \partial l } { \partial x _ { 1 } } } & { \cdots } & { \frac { \partial l } { \partial x _ { n } } } \end{array} \right)
\]

注意:grad在反向传播过程中是累加的(accumulated),这意味着运行反向传播,梯度都会累加到前一次的梯度,所以一般在反正传播之前需要把梯度清零

# 再来反向传播一次,注意grad是累加的
out2 = x.sum()
out2.backward()
print(x.grad) out3 = x.sum()
x.grad.data.zero_() # 梯度清零,将梯度的数据变成0
out3.backward()
print(x.grad)
tensor([[5.5000, 5.5000],
[5.5000, 5.5000]])
tensor([[1., 1.],
[1., 1.]])

现在需要解释一个问题:

为什么在y.backward()时,如果y是标量,责不需要为backward()传入任何参数;否则需要传入一个与y同形的Tensor?

  1. 首先为了避免向量(甚至更高维张量)对张量求导,而转换成标量对张量求导;
  2. 不允许张量对张量求导,只允许标量与张量求导,求导的结果是和自变量同形的张量。这个地方说的就是不能让函数对函数求导(估计是一个意思吧)
x = torch.tensor([1.0,2.0,3.0,4.0],requires_grad=True)
y = 2*x
z= y.view(2,2)
print(z)
tensor([[2., 4.],
[6., 8.]], grad_fn=<ViewBackward>)

现在的y不是一个标量,所以在调用backward()时需要传入一个和y同形的权重向量进行加权就和得到一个标量。

v = torch.tensor([[1.0,1.0],[0.01,0.01]],dtype=torch.float)
z.backward(v) # 此时v就是与y同形的权重向量
print(x.grad) # x.grad是和x同形的张量
tensor([2.0000, 2.0000, 0.0200, 0.0200])
中断梯度追踪
x = torch.tensor(1.0,requires_grad=True)
y1 = x**2
with torch.no_grad():
y2 = x**3
y3 = y1+y2
print(x.requires_grad)
print(y1,y1.requires_grad) # 平方
print(y2,y2.requires_grad) # 标量,在with torch.no_grad未被追踪
print(y3,y3.requires_grad) # 求和
print(y2,y2.is_leaf) # 标量没有计算公式,y2也称为称为叶子节点,叶子节点对应grad_fn 是None
True
tensor(1., grad_fn=<PowBackward0>) True
tensor(1.) False
tensor(2., grad_fn=<AddBackward0>) True
tensor(1.) True
y3.backward()
print(x.grad)
tensor(2.)

y3 = y1+y2 =x2+ y3,当x=1 时,dy3/dx应该是5,但是y2的定义被torch.no_grad()包裹的,

所以与y2相关的梯度是不会被回传的,只有与y1有关的梯度才会回传,即x**2对x的梯度

修改tensor的数字,不被autograd记录(即不会影响方向传播),可以对tensor.data 进行操作
x = torch.ones(1,requires_grad=True)
print(x.data) # 也是一个tensor
print(x.data.requires_grad) # 但是已经是独立于计算图之外
y = 2*x x.data *=100 # 只改变了data属性值,不会记录在计算图,因此不会影响梯度传播
y.backward()
print(x)
print(x.grad)
tensor([1.])
False
tensor([100.], requires_grad=True)
tensor([2.])

动手学深度学习1- pytorch初学的更多相关文章

  1. 小白学习之pytorch框架(2)-动手学深度学习(begin-random.shuffle()、torch.index_select()、nn.Module、nn.Sequential())

    在这向大家推荐一本书-花书-动手学深度学习pytorch版,原书用的深度学习框架是MXNet,这个框架经过Gluon重新再封装,使用风格非常接近pytorch,但是由于pytorch越来越火,个人又比 ...

  2. 对比《动手学深度学习》 PDF代码+《神经网络与深度学习 》PDF

    随着AlphaGo与李世石大战的落幕,人工智能成为话题焦点.AlphaGo背后的工作原理"深度学习"也跳入大众的视野.什么是深度学习,什么是神经网络,为何一段程序在精密的围棋大赛中 ...

  3. 【动手学深度学习】Jupyter notebook中 import mxnet出错

    问题描述 打开d2l-zh目录,使用jupyter notebook打开文件运行,import mxnet 出现无法导入mxnet模块的问题, 但是命令行运行是可以导入mxnet模块的. 原因: 激活 ...

  4. 动手学深度学习9-多层感知机pytorch

    多层感知机 隐藏层 激活函数 小结 多层感知机 之前已经介绍过了线性回归和softmax回归在内的单层神经网络,然后深度学习主要学习多层模型,后续将以多层感知机(multilayer percetro ...

  5. 动手学深度学习14- pytorch Dropout 实现与原理

    方法 从零开始实现 定义模型参数 网络 评估函数 优化方法 定义损失函数 数据提取与训练评估 pytorch简洁实现 小结 针对深度学习中的过拟合问题,通常使用丢弃法(dropout),丢弃法有很多的 ...

  6. 动手学深度学习6-认识Fashion_MNIST图像数据集

    获取数据集 读取小批量样本 小结 本节将使用torchvision包,它是服务于pytorch深度学习框架的,主要用来构建计算机视觉模型. torchvision主要由以下几个部分构成: torchv ...

  7. 《动手学深度学习》系列笔记—— 1.2 Softmax回归与分类模型

    目录 softmax的基本概念 交叉熵损失函数 模型训练和预测 获取Fashion-MNIST训练集和读取数据 get dataset softmax从零开始的实现 获取训练集数据和测试集数据 模型参 ...

  8. 动手学深度学习4-线性回归的pytorch简洁实现

    导入同样导入之前的包或者模块 生成数据集 通过pytorch读取数据 定义模型 初始化模型 定义损失函数 定义优化算法 训练模型 小结 本节利用pytorch中的模块,生成一个更加简洁的代码来实现同样 ...

  9. 动手学深度学习11- 多层感知机pytorch简洁实现

    多层感知机的简洁实现 定义模型 读取数据并训练数据 损失函数 定义优化算法 小结 多层感知机的简洁实现 import torch from torch import nn from torch.nn ...

随机推荐

  1. .net core中serilog的基本使用

    Serilog的基本使用 (一)  引言 (二)  导入包 (三)  配置 直接配置 配置文件配置 (四)  使用 (五)  结语 一 引言 作为一枚小白,来复习一下serilog的使用,如果有错误的 ...

  2. Linux搭建www,mail,ftp三大DNS服务器

    ##############################-----服务器端----###############################1. 安装bind# yum install bin ...

  3. php对象复制、clone、浅复制与深复制实例详解

    php对象复制.clone.浅复制与深复制实例详解 一.用clone(克隆)来复制对象$obj1 = new Object();$obj2 = clone $obj1;clone方法会触发对象里定义的 ...

  4. https申请证书并部署到网站流程,浏览器验证证书流程

    https申请证书并部署到网站流程: 1.生成一对秘钥,设公钥为pubk1,私钥为prik12.假设发布的网站地址为https://www.example.com3.生成一个CSR文件(Cerific ...

  5. AJAX小示例

    一. 基本内容 定义:AJAX(Asynchronous Javascript And XML)翻译成中文就是"异步的Javascript和XML",即使用Javascript语言 ...

  6. SQLAlchemy(1)

    介绍 SQLAlchemy是一个基于Python实现的ORM框架.该框架建立在 DB API之上,使用关系对象映射进行数据库操作,简言之便是:将类和对象转换成SQL,然后使用数据API执行SQL并获取 ...

  7. CF891B Gluttony

    原题链接 DOWNLOAD AS PDF 题目大意 给你一个有\(n\)个元素的数组\(a\),让你构造一个数组\(b\),满足从 \(a\).\(b\)中任选出\(k\)个下标对应的元素,它们的和不 ...

  8. oracle 行转列~列转行(几种方法)

    工作中,我们经常会碰到行转列的情况 这里我介绍几种简单的方法--行转列 1.oracle的pivot函数 原表 使用pivot函数: with temp as(select '四川省' nation ...

  9. flask如何返回真正意义上的json字符串?以及中文如何正常显示?

    flask中,不能直接return字典,需要把字典转换为json字符串方式有三种:1. return str(字典)2.return json.dumps(字典)3.return jsonify(字典 ...

  10. CF261E Maxim and Calculator

    CF261E Maxim and Calculator 洛谷评测传送门 题目描述 Maxim has got a calculator. The calculator has two integer ...