Tensors 与numpy 中的ndarrays很像,pytorch能够支持GPU操做python
from __future__ import print_function import torch
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]])
x = torch.zeros(5,3)
print(x)
tensor([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]])
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
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)
# 如下代码只有在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() 将不想被追踪的操做代码块包裹起来,这样的方法在评估模型的时候经常使用,由于在评估模型时,不须要计算已经训练出的参数的的梯度。ui
Function是另一个很重要的类,Tensor和Function相互结合就能够构建一个记录有整个计算过程的有向无环图(DAG)????
每一个Tensor都有一个.grad_fn属性,该属性即建立Tensor的Function,就是说该Tensor是否是经过某些运算获得的,若是是,grad_fn返还一个与这些运算相关的对象,不然是None。spa
# 建立一个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 } \]code
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 \]input
数学上,若是有一个函数值和自变量都为向量的函数 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?
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的梯度
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.])