关于线性回归
即对于几组input和target的值用模型进行线性的拟合。比如对于房子的不同地理位置大小等因素决定的房价。线性的模型就是对于一组input输入得出与target相近的out。
过程
较为普式的把输入对象的属性整合为一组集合 [ x 1 j , x 2 j , x 3 j , . . . , x n j ] [x_1^j,x_2^j,x_3^j,...,x_n^j] [x1j,x2j,x3j,...,xnj],对应的target为 y j y^j yj。
然后对于一个
j
j
j给出预测的out(target的prediction):
h
θ
(
x
)
=
θ
0
x
0
+
θ
1
x
1
+
θ
2
x
2
+
.
.
.
+
θ
n
x
n
h_{\theta}( x) = \theta_0x_0+\theta_1x_1+\theta_2x_2 +...+\theta_nx_n
hθ(x)=θ0x0+θ1x1+θ2x2+...+θnxn
h
θ
(
x
)
=
[
θ
0
.
.
.
θ
n
]
×
[
x
0
.
.
.
x
n
]
=
θ
T
X
h_\theta(x) = \begin{bmatrix} \theta_0 &... & \theta_n \end{bmatrix} \times \begin{bmatrix} x_0 \\ ... \\ x_n \end{bmatrix} = \theta^TX
hθ(x)=[θ0...θn]×⎣⎡x0...xn⎦⎤=θTX
所以开始,先制造数据,在线性函数周围制造噪点(以标量函数举例),然后定义LinearRegression模型。
import torch
import matplotlib.pyplot as plt
from torch import nn
from torch.autograd import Variable
# 对于-1到1进行x取样,并增加一维
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1) # 0为行扩展,1为列扩展
#print(x)
# 定义y,并在准确值的周围制造random噪点
y= 3 * x + 10 + torch.rand(x.size())
# 绘制散点图
plt.scatter(x.data.numpy(),y.data.numpy())
plt.show()
# 定义输入层和输出层均为一维的模型
class LinearRegression(nn.Module):
def __init__(self):
super(LinearRegression, self).__init__()
self.linear = nn.Linear(1,1) # 输入输出层的维度都为1
def forward(self,x):
out = self.linear(x)
return out
# print(LinearRegression())
model = LinearRegression()
定义好model后开始选择损失函数和优化器。
这里选择均方误差的损失函数,默认返回loss的mean值。
然后选择优化器,选择常用的SGD随机梯度下降训练优化,并在参数中选择lr步长。
# 定义损失函数(均方误差)和优化函数(梯度下降)
criterion = nn.MSELoss() # loss function 返回mean
# 构建optimizer对象,SGD随机梯度下降训练优化
optimizer = torch.optim.SGD(model.parameters(),lr = 1e-2)
然后开始模型训练的迭代,将x和y转化为Variable,分别为inputs和targets。
如何训练呢。
首先向前传播:将inputs放入model,得到out的预测值,然后就是利用out和targets计算loss。
然后需要将loss降到最低,就要用梯度下降的方法求其最小值。也就是:
损失函数:
J
(
θ
)
=
1
2
m
∑
i
=
0
m
(
h
θ
(
x
i
)
−
y
i
)
2
J(\theta) = \frac{1}{2m}\sum_{i = 0}^{m}(h_{\theta}(x^i)-y^i)^2
J(θ)=2m1i=0∑m(hθ(xi)−yi)2
那么要找到让其最小的 θ \theta θ,梯度下降的方法使用如下公式:
θ
j
′
=
θ
j
−
η
∂
J
(
θ
)
∂
θ
j
\theta_j^{'} =\theta_j-\eta \frac{\partial J(\theta) }{\partial \theta_j}
θj′=θj−η∂θj∂J(θ)
也就是让
θ
\theta
θ沿着梯度的方向走,一直到计算前后的
θ
\theta
θ值相等或者小于某个阈值。就找到了相应的极值。
对于步长 η \eta η,不宜太大,会导致错过最小值。也不宜太小,容易陷入局部极小值。
但是在计算.backward()求梯度前,要记得将梯度清零,不然会累加。
也就是向后传播:
# 向后传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (epoch + 1)%20 == 0:
print('Epoch[{}/{}], Loss:{:.6f}'.format(epoch + 1,num_epoch,loss.item()))
此处提一嘴.backward(),此处由于是标量的损失函数,所以可以直接求导,不需要参数。而如果损失函数(模型函数)是一个矢量tensor,需要在里面加入一个于函数同维的tensor。
至于为什么,下面来解释:
举一个例子:
这种情况一般出现于,target为一个多维值的情况。
y i = w i 1 x 1 + w i 2 x 2 y_i = w_{i1}x_1+w_{i2}x_2 yi=wi1x1+wi2x2
也就是:
[ y 1 y 2 ] = [ w 11 w 12 w 21 w 22 ] [ x 1 x 2 ] \begin{bmatrix} y_1\\ y_2 \end{bmatrix} = \begin{bmatrix} w_{11} & w_{12} \\ w_{21} & w_{22} \end{bmatrix} \begin{bmatrix} x_1 \\ x_2 \end{bmatrix} [y1y2]=[w11w21w12w22][x1x2]
然后定义一个与y维度相同的参数v:
[
v
1
v
2
]
\begin{bmatrix} v_1 \\ v_2 \end{bmatrix}
[v1v2]
然后执行y.backward(v)
那么:
x . g r a d = [ ∂ y ∂ x 1 ∂ y ∂ x 2 ] = [ ∂ y 1 ∂ x 1 v 1 + ∂ y 2 ∂ x 1 v 2 ∂ y 1 ∂ x 2 v 1 + ∂ y 2 ∂ x 2 v 2 ] = [ w 11 v 1 + w 12 v 2 w 11 v 2 + w 12 v 1 ] x.grad = \begin{bmatrix} \frac{\partial y }{\partial x_1} \\ \frac{\partial y }{\partial x_2} \end{bmatrix}=\begin{bmatrix} \frac{\partial y_1 }{\partial x_1} v_1 + \frac{\partial y_2 }{\partial x_1} v_2\\ \frac{\partial y_1 }{\partial x_2} v_1 + \frac{\partial y_2 }{\partial x_2} v_2 \end{bmatrix}=\begin{bmatrix} w_{11}v_1 + w_{12}v_2\\ w_{11}v_2 + w_{12}v_1 \end{bmatrix} x.grad=[∂x1∂y∂x2∂y]=[∂x1∂y1v1+∂x1∂y2v2∂x2∂y1v1+∂x2∂y2v2]=[w11v1+w12v2w11v2+w12v1]
因为在pytorch中,tensor对tensor求导不允许,对于这种函数target为tensor的情况,可以理解为定义了一个复合函数:
L
=
∑
i
=
1
n
v
i
y
i
L=\sum_{i=1}^{n}v_iy_i
L=∑i=1nviyi,这样对x求偏导就是一个scaler而不是tensor对x求偏导了,无非使用链式求导法则现对y求偏导,再对x求偏导。而v就可以看作
L
L
L的每一项的系数。
最后对模型进行评估:
model.eval() # 设为评估模式
# 测试
if torch.cuda.is_available():
predict = model(Variable(x).cuda())
predict = predict.data.cpu().numpy()
else:
predict = model(Variable(x))
predict = predict.data.numpy() # 转化为numpy
plt.plot(x.numpy(), y.numpy(),'ro',label="Original Data")
plt.plot(x.numpy(),predict,label="Fitting Line")
plt.show()
整体代码:
# -*- coding: utf-8 -*-
"""
Spyder Editor
linear regression demo
"""
# target model:y = 3x + 10
import torch
import matplotlib.pyplot as plt
from torch import nn
from torch.autograd import Variable
# 对于-1到1进行x取样,并增加一维
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1) # 0为行扩展,1为列扩展
#print(x)
# 定义y,并在准确值的周围制造random噪点
y= 3 * x + 10 + torch.rand(x.size())
# 绘制散点图
plt.scatter(x.data.numpy(),y.data.numpy())
plt.show()
# 定义输入层和输出层均为一维的模型
class LinearRegression(nn.Module):
def __init__(self):
super(LinearRegression, self).__init__()
self.linear = nn.Linear(1,1) # 输入输出层的维度都为1
def forward(self,x):
out = self.linear(x)
return out
# print(LinearRegression())
model = LinearRegression()
# 定义损失函数(均方误差)和优化函数(梯度下降)
criterion = nn.MSELoss() # loss function 返回mean
# 构建optimizer对象,SGD随机梯度下降训练优化
optimizer = torch.optim.SGD(model.parameters(),lr = 1e-2)
# 模型训练
num_epoch = 1000 # 迭代次数1000
for epoch in range(num_epoch):
if torch.cuda.is_available():
inputs = Variable(x).cuda()
targets = Variable(y).cuda()
else:
inputs = Variable(x)
targets = Variable(y)
# 向前传播
out = model(inputs) # 放入模型
loss = criterion(out, targets) # 计算loss,参数均为torch in variable
# 向后传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 进度条
if (epoch + 1)%20 == 0:
print('Epoch[{}/{}], Loss:{:.6f}'.format(epoch + 1,num_epoch,loss.item()))
model.eval() # 设为评估模式
# 测试
if torch.cuda.is_available():
predict = model(Variable(x).cuda())
predict = predict.data.cpu().numpy()
else:
predict = model(Variable(x))
predict = predict.data.numpy() # 转化为numpy
plt.plot(x.numpy(), y.numpy(),'ro',label="Original Data")
plt.plot(x.numpy(),predict,label="Fitting Line")
plt.show()