一 Pytorch的安装
1. Pytorch的介绍
Pytorch是一款facebook发布的深度学习框架,由其易用性,友好性,深受广大用户青睐。
2. Pytorch的版本
3. Pytorch的安装
安装地址介绍: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
二 Pytorch的入门使用
1. 张量Tensor
张量是一个统称,其中包含很多类型:
- 0阶张量:标量、常数,0-D Tensor
- 1阶张量:向量,1-D Tensor
- 2阶张量:矩阵,2-D Tensor
- 3阶张量
- …
- N阶张量
2. Pytorch中创建张量
- 使用python中的列表或者序列创建tensor
torch.tensor([[1., -1.], [1., -1.]])
tensor([[ 1.0000, -1.0000],
[ 1.0000, -1.0000]])
- 使用numpy中的数组创建tensor
torch.tensor(np.array([[1, 2, 3], [4, 5, 6]]))
tensor([[ 1, 2, 3],
[ 4, 5, 6]])
- 使用torch的api创建tensor
torch.empty([3,4])
创建3行4列的空的tensor,会用无用数据进行填充torch.ones([3,4])
创建3行4列的全为1的tensortorch.zeros([3,4])
- 创建3行4列的全为0的tensor
- torch.rand([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,方差为1
3. Pytorch中tensor的常用方法
- 获取tensor中的数据(当tensor中只有一个元素可用):
tensor.item()
In [10]: a = torch.tensor(np.arange(1))
In [11]: a
Out[11]: tensor([0])
In [12]: a.item()
Out[12]: 0
- 转化为numpy数组
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- tensor的切片
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])
4. tensor的数据类型
tensor中的数据类型非常多,常见类型如下:
上图中的Tensor types表示这种type的tensor是其实例
- 获取tensor的数据类型:
tensor.dtype
In [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)
5. tensor的其他操作
- tensor和tensor相加
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进行就地修改
- tensor和数字操作
In [97]: x + 10
Out[97]:
tensor([[11., 11., 11.],
[11., 11., 11.],
[11., 11., 11.],
[11., 11., 11.],
[11., 11., 11.]])
- CUDA中的tensor
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一样
三 梯度下降和反向传播
1. 梯度是什么
梯度:是一个向量,导数 + 变化最快的方向(学习的前进方向)
回顾机器学习
收集数据 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
)
(
分类损失
)
\begin{align*} loss & = (Y_{predict}-Y_{true})^2 &(回归损失) \\ loss & = Y_{true} \cdot log(Y_{predict}) &(分类损失) \end{align*}
lossloss=(Ypredict−Ytrue)2=Ytrue⋅log(Ypredict)(回归损失)(分类损失)
目标:通过调整(学习)参数
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的更新方法:
- 计算 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 − α ∇ w w = w - \alpha \nabla w w=w−α∇w
其中:
- $\nabla w <0 $ ,意味着w将增大
- $\nabla w >0 $ ,意味着w将减小
总结:梯度就是多元函数参数的变化趋势(参数学习的方向),只有一个自变量时称为导数,两个自变量称为偏导数
2. 偏导的计算
2.1 常见的导数计算
-
多项式求导数: 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 牛顿 莱布尼兹 \begin{align*} & f^{'}(x) = & \frac{d f(x)}{dx} \\ & 牛顿 &莱布尼兹 \end{align*} f′(x)=牛顿dxdf(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
\begin{align*} \frac{d f(x)}{dx} & = \frac{df}{da} \times \frac{da}{db} \times \frac{db}{dc}\times \frac{dc}{dx} \\ &=-a^{-2} \times 1\times e^c \times (-1) \\ &= -(1+e^{-x})^{-2} \times e^{-x} \times (-1) \\ &= e^{-x}(1+e^{-x})^{-2} \end{align*}
dxdf(x)=dadf×dbda×dcdb×dxdc=−a−2×1×ec×(−1)=−(1+e−x)−2×e−x×(−1)=e−x(1+e−x)−2
2.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
\begin{align*} &f(x,y,z) &= &ax+by+cz \\ &\frac{df(x,y,z)}{dx} &= &a \\ &\frac{df(x,y,z)}{dy} &= &b \\ &\frac{df(x,y,z)}{dz} &= &c \end{align*}
f(x,y,z)dxdf(x,y,z)dydf(x,y,z)dzdf(x,y,z)====ax+by+czabc
例2:
f
(
x
,
y
)
=
x
y
d
f
(
x
,
y
)
d
x
=
y
d
f
(
x
,
y
)
d
y
=
x
\begin{align*} &f(x,y) &= &xy \\ &\frac{df(x,y)}{dx} &= & y\\ &\frac{df(x,y)}{dy} &= &x \end{align*}
f(x,y)dxdf(x,y)dydf(x,y)===xyyx
例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
)
\begin{align*} &f(x,w) &= &(y-xw)^2 \\ &\frac{df(x,w)}{dx} &= & -2w(y-xw)\\ &\frac{df(x,w)}{dw} &= & -2x(y-xw) \end{align*}
f(x,w)dxdf(x,w)dwdf(x,w)===(y−xw)2−2w(y−xw)−2x(y−xw)
练习:
已知
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
\begin{align*} 令:& J(a,b,c) = 3u\\ \frac{dJ}{da} &=\frac{dJ}{du} \times \frac{du}{da} = 3\times1 \\ \frac{dJ}{db} &=\frac{dJ}{du} \times \frac{du}{dv} \times \frac{dv}{db} = 3\times1\times c \\ \frac{dJ}{dc} &=\frac{dJ}{du} \times \frac{du}{dv} \times \frac{dv}{dc} = 3\times1\times b \\ \end{align*}
令:dadJdbdJdcdJJ(a,b,c)=3u=dudJ×dadu=3×1=dudJ×dvdu×dbdv=3×1×c=dudJ×dvdu×dcdv=3×1×b
3. 反向传播算法
3.1 计算图和反向传播
计算图:通过图的方式来描述函数的图形
在上面的练习中, 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
\begin{align*} \frac{dJ}{da} &= 3 \times 1 \\ \frac{dJ}{db} &= 3 \times 1 \times c \\ \frac{dJ}{dc} &= 3 \times 1 \times b \end{align*}
dadJdbdJdcdJ=3×1=3×1×c=3×1×b
3.2 神经网络中的反向传播
3.2.1 神经网络的示意图
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个神经元的权重。
3.2.2 神经网络的计算图
其中:
- ∇ o u t \nabla out ∇out是根据损失函数对预测值进行求导得到的结果
- f函数可以理解为激活函数
**问题:**那么此时 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
)
\begin{align*} &\nabla W_2[1,2] = f(a_1)* \nabla b_2 \\ &\nabla a_2 = f^{'}(a_2)*(w_2[2,1]\nabla b_1 +W_2[2,2] \nabla b_2) \end{align*}
∇W2[1,2]=f(a1)∗∇b2∇a2=f′(a2)∗(w2[2,1]∇b1+W2[2,2]∇b2)
继续反向传播
计算过程如下:
▽
W
1
[
1
,
2
]
=
x
1
∗
▽
a
2
▽
x
1
=
(
W
1
[
1
,
1
]
∗
▽
a
1
+
w
1
[
1
,
2
]
∗
▽
a
2
)
∗
x
1
’
\begin{align*} &▽W_1[1,2]= x_1*▽a_2\\ &▽x_1= (W_1[1,1]*▽a_1+w_1[1,2]*▽a_2)*x_1’ \end{align*}
▽W1[1,2]=x1∗▽a2▽x1=(W1[1,1]∗▽a1+w1[1,2]∗▽a2)∗x1’
通用的描述如下
∇
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完成线性回归
1. 向前计算
对于pytorch中的一个tensor,如果设置它的属性 .requires_grad
为True
,那么它将会追踪对于该张量的所有操作。或者可以理解为,这个tensor是一个参数,后续会被计算梯度,更新该参数。
1.1 计算过程
假设有以下条件(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
\begin{align*} &output = \frac{1}{4}\sum_iz_i \\ &z_i = 3(x_i+2)^2\\ 其中:&\\ &z_i|_{x_i=1}=27\\ \end{align*}
其中:output=41i∑zizi=3(xi+2)2zi∣xi=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=<AddBackward0>)
z = y*y*3 #平方x3
print(x)
#tensor([[27., 27.],
# [27., 27.]], grad_fn=<MulBackward0>)
out = z.mean() #求均值
print(out)
#tensor(27., grad_fn=<MeanBackward0>)
从上述代码可以看出:
- x的requires_grad属性为True,之后的每次计算都会修改其
grad_fn
属性,用来记录做过的操作 - 通过这个函数和grad_fn能够组成一个和前一小节类似的计算图
1.2 requires_grad和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) # <SumBackward0 object at 0x4e2b14345d21>
# 以下代码不会记录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
的可训练的参数,但是 不需要在此过程中对他们进行梯度计算。
2. 梯度计算
对于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的require_grad=False,tensor.data和tensor等价
- require_grad=True时,tensor.data仅仅是获取tensor中的数据
tensor.numpy()
:require_grad=True
不能够直接转换,需要使用tensor.detach().numpy()
3. 线性回归实现
下面, 使用一个自定义的数据,来使用torch实现一个简单的线性回归
假设 的基础模型就是y = wx+b
,其中w和b均为参数, 使用y = 3x+0.8
来构造数据x、y,所以最后通过模型应该能够得出w和b应该分别接近3和0.8
- 准备数据
- 计算预测值
- 计算损失,把参数的梯度置为0,进行反向传播
- 更新参数
实现方法一
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