Pytorch是一款facebook发布的深度学习框架,由其易用性,友好性,深受广大用户青睐。

安装地址介绍:https://pytorch.org/get-started/locally/
带GPU安装步骤:
conda install pytorch torchvision cudatoolkit=9.0 -c pytorch
不带GPU安装步骤
conda install pytorch-cpu torchvision-cpu -c pytorch
安装之后打开ipython
输入:
In [1]: import torch
In [2]: torch.__version__
Out[2]: '1.0.1'
注意:安装模块的时候安装的是pytorch ,但是在代码中都是使用torch
张量是一个统称,其中包含很多类型:
torch.tensor([[1., -1.], [1., -1.]])
tensor([[ 1.0000, -1.0000],
[ 1.0000, -1.0000]])
torch.tensor(np.array([[1, 2, 3], [4, 5, 6]]))
tensor([[ 1, 2, 3],
[ 4, 5, 6]])
torch.empty([3,4])创建3行4列的空的tensor,会用无用数据进行填充torch.ones([3,4]) 创建3行4列的全为1的tensortorch.zeros([3,4]) 创建3行4列的**随机值**的tensor,随机值的区间是[0, 1)`>>> torch.rand(2, 3)
tensor([[ 0.8237, 0.5781, 0.6879],
[ 0.3816, 0.7249, 0.0998]])
torch.randint(low=0,high=10,size=[3,4]) 创建3行4列的随机整数的tensor,随机值的区间是[low, high)>>> torch.randint(3, 10, (2, 2))
tensor([[4, 5],
[6, 7]])
torch.randn([3,4]) 创建3行4列的随机数的tensor,随机值的分布式均值为0,方差为1tensor.item()In [10]: a = torch.tensor(np.arange(1))
In [11]: a
Out[11]: tensor([0])
In [12]: a.item()
Out[12]: 0
In [55]: z.numpy()
Out[55]:
array([[-2.5871205],
[ 7.3690367],
[-2.4918075]], dtype=float32)
tensor.size()In [72]: x
Out[72]:
tensor([[ 1, 2],
[ 3, 4],
[ 5, 10]], dtype=torch.int32)
In [73]: x.size()
Out[73]: torch.Size([3, 2])
tensor.view((3,4))。类似numpy中的reshape,是一种浅拷贝,仅仅是形状发生改变In [76]: x.view(2,3)
Out[76]:
tensor([[ 1, 2, 3],
[ 4, 5, 10]], dtype=torch.int32)
tensor.dim()In [77]: x.dim()
Out[77]: 2
tensor.max()In [78]: x.max()
Out[78]: tensor(10, dtype=torch.int32)
tensor.t()In [79]: x.t()
Out[79]:
tensor([[ 1, 3, 5],
[ 2, 4, 10]], dtype=torch.int32)
tensor[1,3] 获取tensor中第一行第三列的值tensor[1,3]=100 对tensor中第一行第三列的位置进行赋值100 In [101]: x
Out[101]:
tensor([[1.6437, 1.9439, 1.5393],
[1.3491, 1.9575, 1.0552],
[1.5106, 1.0123, 1.0961],
[1.4382, 1.5939, 1.5012],
[1.5267, 1.4858, 1.4007]])
In [102]: x[:,1]
Out[102]: tensor([1.9439, 1.9575, 1.0123, 1.5939, 1.4858])
tensor中的数据类型非常多,常见类型如下:

上图中的Tensor types表示这种type的tensor是其实例
tensor.dtypeIn [80]: x.dtype
Out[80]: torch.int32
In [88]: torch.ones([2,3],dtype=torch.float32)
Out[88]:
tensor([[9.1167e+18, 0.0000e+00, 7.8796e+15],
[8.3097e-43, 0.0000e+00, -0.0000e+00]])
In [17]: a
Out[17]: tensor([1, 2], dtype=torch.int32)
In [18]: a.type(torch.float)
Out[18]: tensor([1., 2.])
In [19]: a.double()
Out[19]: tensor([1., 2.], dtype=torch.float64)
In [94]: x = x.new_ones(5, 3, dtype=torch.float)
In [95]: y = torch.rand(5, 3)
In [96]: x+y
Out[96]:
tensor([[1.6437, 1.9439, 1.5393],
[1.3491, 1.9575, 1.0552],
[1.5106, 1.0123, 1.0961],
[1.4382, 1.5939, 1.5012],
[1.5267, 1.4858, 1.4007]])
In [98]: torch.add(x,y)
Out[98]:
tensor([[1.6437, 1.9439, 1.5393],
[1.3491, 1.9575, 1.0552],
[1.5106, 1.0123, 1.0961],
[1.4382, 1.5939, 1.5012],
[1.5267, 1.4858, 1.4007]])
In [99]: x.add(y)
Out[99]:
tensor([[1.6437, 1.9439, 1.5393],
[1.3491, 1.9575, 1.0552],
[1.5106, 1.0123, 1.0961],
[1.4382, 1.5939, 1.5012],
[1.5267, 1.4858, 1.4007]])
In [100]: x.add_(y) #带下划线的方法会对x进行就地修改
Out[100]:
tensor([[1.6437, 1.9439, 1.5393],
[1.3491, 1.9575, 1.0552],
[1.5106, 1.0123, 1.0961],
[1.4382, 1.5939, 1.5012],
[1.5267, 1.4858, 1.4007]])
In [101]: x #x发生改变
Out[101]:
tensor([[1.6437, 1.9439, 1.5393],
[1.3491, 1.9575, 1.0552],
[1.5106, 1.0123, 1.0961],
[1.4382, 1.5939, 1.5012],
[1.5267, 1.4858, 1.4007]])
注意:带下划线的方法(比如:add_)会对tensor进行就地修改
In [97]: x + 10
Out[97]:
tensor([[11., 11., 11.],
[11., 11., 11.],
[11., 11., 11.],
[11., 11., 11.],
[11., 11., 11.]])
CUDA(Compute Unified Device Architecture),是NVIDIA推出的运算平台。 CUDA™是一种由NVIDIA推出的通用并行计算架构,该架构使GPU能够解决复杂的计算问题。
torch.cuda这个模块增加了对CUDA tensor的支持,能够在cpu和gpu上使用相同的方法操作tensor
通过.to方法能够把一个tensor转移到另外一个设备(比如从CPU转到GPU)
#device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
if torch.cuda.is_available():
device = torch.device("cuda") # cuda device对象
y = torch.ones_like(x, device=device) # 创建一个在cuda上的tensor
x = x.to(device) # 使用方法把x转为cuda 的tensor
z = x + y
print(z)
print(z.to("cpu", torch.double)) # .to方法也能够同时设置类型
>>tensor([1.9806], device='cuda:0')
>>tensor([1.9806], dtype=torch.float64)
torch的各种操作几乎和numpy一样
梯度:是一个向量,导数 + 变化最快的方向(学习的前进方向)
回顾机器学习
收集数据 x x x ,构建机器学习模型 f f f,得到 f ( x , w ) = Y p r e d i c t f(x,w) = Y_{predict} f(x,w)=Ypredict
判断模型好坏的方法:
l
o
s
s
=
(
Y
p
r
e
d
i
c
t
−
Y
t
r
u
e
)
2
(
回归损失
)
l
o
s
s
=
Y
t
r
u
e
⋅
l
o
g
(
Y
p
r
e
d
i
c
t
)
(
分类损失
)
目标:通过调整(学习)参数
w
w
w,尽可能的降低
l
o
s
s
loss
loss,那么 该如何调整
w
w
w呢?

随机选择一个起始点 w 0 w_0 w0,通过调整 w 0 w_0 w0,让loss函数取到最小值

w w w的更新方法:
$$
\begin{align*}
\nabla w = \frac{f(w+0.000001)-f(w-0.000001)}{2*0.000001}
\end{align*}
$$
w = w − α ∇ w w = w - \alpha \nabla w w=w−α∇w
其中:
总结:梯度就是多元函数参数的变化趋势(参数学习的方向),只有一个自变量时称为导数,两个自变量称为偏导数
多项式求导数: f ( x ) = x 5 f(x) = x^5 f(x)=x5 , f ′ ( x ) = 5 x ( 5 − 1 ) f^{'}(x) = 5x^{(5-1)} f′(x)=5x(5−1)
基本运算求导: f ( x ) = x y f(x) = xy f(x)=xy , f ′ ( x ) = y f^{'}(x) = y f′(x)=y
指数求导: f ( x ) = 5 e x f(x) = 5e^x f(x)=5ex , f ′ ( x ) = 5 e x f^{'}(x) = 5e^x f′(x)=5ex
对数求导: f ( x ) = 5 l n x f(x) = 5lnx f(x)=5lnx , f ′ ( x ) = 5 x f^{'}(x) = \frac{5}{x} f′(x)=x5,ln 表示log以e为底的对数
导数的微分形式:
f
′
(
x
)
=
d
f
(
x
)
d
x
牛顿
莱布尼兹
那么:如何求 f ( x ) = ( 1 + e − x ) − 1 f(x) = (1+e^{-x})^{-1} f(x)=(1+e−x)−1 的导数呢?那就可以使用
f ( x ) = ( 1 + e − x ) − 1 f(x) = (1+e^{-x})^{-1} f(x)=(1+e−x)−1 ==> f ( a ) = a − 1 , a ( b ) = ( 1 + b ) , b ( c ) = e c , c ( x ) = − x f(a) = a^{-1},a(b) = (1+b),b(c) = e^c,c(x) = -x f(a)=a−1,a(b)=(1+b),b(c)=ec,c(x)=−x
则有:
d
f
(
x
)
d
x
=
d
f
d
a
×
d
a
d
b
×
d
b
d
c
×
d
c
d
x
=
−
a
−
2
×
1
×
e
c
×
(
−
1
)
=
−
(
1
+
e
−
x
)
−
2
×
e
−
x
×
(
−
1
)
=
e
−
x
(
1
+
e
−
x
)
−
2
一元函数,即有一个自变量。类似 f ( x ) f(x) f(x)
多元函数,即有多个自变量。类似 f ( x , y , z ) , 三个自变量 x , y , z f(x,y,z),三个自变量x,y,z f(x,y,z),三个自变量x,y,z
多元函数求偏导过程中:对某一个自变量求导,其他自变量当做常量即可
例1:
f
(
x
,
y
,
z
)
=
a
x
+
b
y
+
c
z
d
f
(
x
,
y
,
z
)
d
x
=
a
d
f
(
x
,
y
,
z
)
d
y
=
b
d
f
(
x
,
y
,
z
)
d
z
=
c
例2:
f
(
x
,
y
)
=
x
y
d
f
(
x
,
y
)
d
x
=
y
d
f
(
x
,
y
)
d
y
=
x
例3:
f
(
x
,
w
)
=
(
y
−
x
w
)
2
d
f
(
x
,
w
)
d
x
=
−
2
w
(
y
−
x
w
)
d
f
(
x
,
w
)
d
w
=
−
2
x
(
y
−
x
w
)
练习:
已知
J
(
a
,
b
,
c
)
=
3
(
a
+
b
c
)
,
令
u
=
a
+
v
,
v
=
b
c
J(a,b,c) = 3(a+bc),令u=a+v,v = bc
J(a,b,c)=3(a+bc),令u=a+v,v=bc,求a,b,c各自的偏导数。
令
:
J
(
a
,
b
,
c
)
=
3
u
d
J
d
a
=
d
J
d
u
×
d
u
d
a
=
3
×
1
d
J
d
b
=
d
J
d
u
×
d
u
d
v
×
d
v
d
b
=
3
×
1
×
c
d
J
d
c
=
d
J
d
u
×
d
u
d
v
×
d
v
d
c
=
3
×
1
×
b
计算图:通过图的方式来描述函数的图形
在上面的练习中, J ( a , b , c ) = 3 ( a + b c ) , 令 u = a + v , v = b c J(a,b,c) = 3(a+bc),令u=a+v,v = bc J(a,b,c)=3(a+bc),令u=a+v,v=bc,把它绘制成计算图可以表示为:

绘制成为计算图之后,可以清楚的看到向前计算的过程
之后,对每个节点求偏导可有:

那么反向传播的过程就是一个上图的从右往左的过程,自变量
a
,
b
,
c
a,b,c
a,b,c各自的偏导就是连线上的梯度的乘积:
d
J
d
a
=
3
×
1
d
J
d
b
=
3
×
1
×
c
d
J
d
c
=
3
×
1
×
b
w 1 , w 2 , . . . . w n w1,w2,....wn w1,w2,....wn表示网络第n层权重
w
n
[
i
,
j
]
w_n[i,j]
wn[i,j]表示第n层第i个神经元,连接到第n+1层第j个神经元的权重。


其中:
**问题:**那么此时 w 1 [ 1 , 2 ] w_1[1,2] w1[1,2]的偏导该如何求解呢?
通过观察,发现从 o u t out out 到 w 1 [ 1 , 2 ] w_1[1,2] w1[1,2]的来连接线有两条

结果如下:
d
o
u
t
d
W
1
[
1
,
2
]
=
x
1
∗
f
′
(
a
2
)
∗
(
W
2
[
2
,
1
]
∗
f
′
(
b
1
)
∗
W
3
[
1
,
1
]
∗
∇
o
u
t
+
W
2
[
2
,
2
]
∗
f
′
(
b
2
)
∗
W
3
[
2
,
1
]
∗
∇
o
u
t
)
\frac{dout}{dW_1[1,2]} = x1*f^{'}(a2)*(W_2[2,1]*f^{'}(b1)*W_3[1,1]*\nabla out +W_2[2,2]*f^{'}(b2)*W_3[2,1]*\nabla out)
dW1[1,2]dout=x1∗f′(a2)∗(W2[2,1]∗f′(b1)∗W3[1,1]∗∇out+W2[2,2]∗f′(b2)∗W3[2,1]∗∇out)
公式分为两部分:
但是这样做,当模型很大的时候,计算量非常大
所以反向传播的思想就是对其中的某一个参数单独求梯度,之后更新,如下图所示:

计算过程如下
$$
\begin{align*}
&\nabla W_3[1,1] = f(b_1)\nabla out & (计算W_3[1,1]梯度)\
&\nabla W_3[2,1] = f(b_2)\nabla out & (计算W_3[2,1]梯度)\
\
&\nabla b_1= f^{‘}(b_1)W_3[1,1]\nabla out & (计算W_3[2,1]梯度)\
&\nabla b_2= f^{’}(b_2)W_3[2,1]\nabla out & (计算W_3[2,1]梯度)\
\end{align*}
$$
更新参数之后,继续反向传播

计算过程如下:
∇
W
2
[
1
,
2
]
=
f
(
a
1
)
∗
∇
b
2
∇
a
2
=
f
′
(
a
2
)
∗
(
w
2
[
2
,
1
]
∇
b
1
+
W
2
[
2
,
2
]
∇
b
2
)
继续反向传播

计算过程如下:
▽
W
1
[
1
,
2
]
=
x
1
∗
▽
a
2
▽
x
1
=
(
W
1
[
1
,
1
]
∗
▽
a
1
+
w
1
[
1
,
2
]
∗
▽
a
2
)
∗
x
1
’
通用的描述如下
∇
w
i
,
j
l
=
f
(
a
i
l
)
∗
∇
a
j
i
+
1
∇
a
i
l
=
f
′
(
a
i
l
)
∗
(
∑
j
=
1
m
w
i
,
j
∗
∇
a
j
l
+
1
)
\nabla w^{l}_{i,j} = f(a^l_i)* \nabla a^{i+1}_{j}\\ \nabla a^{l}_i = f'(a^l_i)*(\sum_{j=1}^{m}w_{i,j}*\nabla a_j^{l+1})
∇wi,jl=f(ail)∗∇aji+1∇ail=f′(ail)∗(j=1∑mwi,j∗∇ajl+1)
对于pytorch中的一个tensor,如果设置它的属性 .requires_grad为True,那么它将会追踪对于该张量的所有操作。或者可以理解为,这个tensor是一个参数,后续会被计算梯度,更新该参数。
假设有以下条件(1/4表示求均值,xi中有4个数),使用torch完成其向前计算的过程
o
u
t
p
u
t
=
1
4
∑
i
z
i
z
i
=
3
(
x
i
+
2
)
2
其中
:
z
i
∣
x
i
=
1
=
27
如果x为参数,需要对其进行梯度的计算和更新
那么,在最开始随机设置x的值的过程中,需要设置他的requires_grad属性为True,其默认值为False
import torch
x = torch.ones(2, 2, requires_grad=True) #初始化参数x并设置requires_grad=True用来追踪其计算历史
print(x)
#tensor([[1., 1.],
# [1., 1.]], requires_grad=True)
y = x+2
print(y)
#tensor([[3., 3.],
# [3., 3.]], grad_fn=)
z = y*y*3 #平方x3
print(x)
#tensor([[27., 27.],
# [27., 27.]], grad_fn=)
out = z.mean() #求均值
print(out)
#tensor(27., grad_fn=)
从上述代码可以看出:
grad_fn属性,用来记录做过的操作a = torch.randn(2, 2)
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) #
# 以下代码不会记录c之前的操作
with torch.no_gard():
c = (a * a).sum() #tensor(151.6830),此时c没有gard_fn
print(c.requires_grad) #False
注意:
为了防止跟踪历史记录(和使用内存),可以将代码块包装在with torch.no_grad():中。在评估模型时特别有用,因为模型可能具有requires_grad = True的可训练的参数,但是 不需要在此过程中对他们进行梯度计算。
对于1.1 中的out而言, 可以使用backward方法来进行反向传播,计算梯度
out.backward(),此时便能够求出导数
d
o
u
t
d
x
\frac{d out}{dx}
dxdout,调用x.gard能够获取导数值
得到
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
因为:
d
(
O
)
d
(
x
i
)
=
3
2
(
x
i
+
2
)
\frac{d(O)}{d(x_i)} = \frac{3}{2}(x_i+2)
d(xi)d(O)=23(xi+2)
在
x
i
x_i
xi等于1时其值为4.5
注意:在输出为一个标量的情况下, 可以调用输出tensor的backword() 方法,但是在数据是一个向量的时候,调用backward()的时候还需要传入其他参数。
很多时候 的损失函数都是一个标量,所以这里就不再介绍损失为向量的情况。
loss.backward()就是根据损失函数,对参数(requires_grad=True)去计算他的梯度,并且把它累加保存到x.gard,此时还并未更新其梯度
注意点:
tensor.data:
tensor.numpy():
require_grad=True不能够直接转换,需要使用tensor.detach().numpy()下面, 使用一个自定义的数据,来使用torch实现一个简单的线性回归
假设 的基础模型就是y = wx+b,其中w和b均为参数, 使用y = 3x+0.8来构造数据x、y,所以最后通过模型应该能够得出w和b应该分别接近3和0.8
import torch
import matplotlib.pyplot as plt
learning_rate = 0.1
# 1. 准备数据 #y = 3x + 0.8
x = torch.randn([500,1])
y_true = 3*x + 0.8
# 2. 计算预测值 y_pred = x * w + b
w = torch.rand([],requires_grad=True)
b = torch.tensor(0,dtype=torch.float,requires_grad=True)
for k in range(30):
for i in [w,b]:
if i.grad is not None:
i.grad.data.zero_()
y_predict = x * w + b
# 3. 计算损失,把参数的梯度置为0,进行反向传播
loss = (y_predict-y_true).pow(2).mean()
loss.backward()
# 3.1 能够得到w和b的梯度
# 4. 更新参数
w.data = w.data - learning_rate * w.grad
b.data = b.data - learning_rate * b.grad
if k%10 == 0:
print(k,loss.item(),w.item(),b.item())
# print(w,b)
#绘图
plt.figure(figsize=(20,8))
plt.scatter(x.numpy(),y_true.numpy())
y_predict = x * w + b
plt.plot(x.numpy(),y_predict.detach().numpy(),c="red")
plt.show()
import torch
import numpy as np
from matplotlib import pyplot as plt
#1. 准备数据 y = 3x+0.8,准备参数
# x为50个从0到1的值
x = torch.rand([50])
y = 3*x + 0.8
# w和b的初始值
w = torch.rand(1,requires_grad=True)
b = torch.rand(1,requires_grad=True)
# 损失函数
def loss_fn(y,y_predict):
loss = (y_predict-y).pow(2).mean()
for i in [w,b]:
#每次反向传播前把梯度置为0,如果不置为0,梯度会累加
if i.grad is not None:
i.grad.data.zero_()
# [i.grad.data.zero_() for i in [w,b] if i.grad is not None]
loss.backward() #反向传播
return loss.data
# 更新参数
def optimize(learning_rate):
# print(w.grad.data,w.data,b.data)
w.data -= learning_rate* w.grad.data
b.data -= learning_rate* b.grad.data
for i in range(3000):
#2. 计算预测值
y_predict = x*w + b
#3.计算损失,把参数的梯度置为0,进行反向传播
loss = loss_fn(y,y_predict)
if i%500 == 0:
print(i,loss)
#4. 更新参数w和b
optimize(0.01)
# 绘制图形,观察训练结束的预测值和真实值
predict = x*w + b #使用训练后的w和b计算预测值
# 绘制散点图
plt.scatter(x.data.numpy(), y.data.numpy(),c = "r")
plt.plot(x.data.numpy(), predict.data.numpy())
plt.show()
print("w",w)
print("b",b)
图形效果如下:

打印w和b,可有
w tensor([2.9280], requires_grad=True)
b tensor([0.8372], requires_grad=True)
可知,w和b已经非常接近原来的预设的3和0.8