datawhale——task02
线性回归
二元线性回归在直角坐标系中拟合出一条直线,多元线性回归在解空间内拟合出一个超平面。线性回归解释了自变量(特征)与因变量之间的关系,可以根据自变量得到因变量的值。线性回归可以理解为单层神经网络,当线性模型有两个输入时,他就是一个感知机。
线性回归的表达式为:
y
^
=
b
+
w
1
x
1
+
w
2
x
2
+
⋅
⋅
⋅
+
w
n
x
n
\hat{y}=b+w_1x_1+w_2x_2+···+w_nx_n
y^=b+w1x1+w2x2+⋅⋅⋅+wnxn
为了方便计算,写成矩阵的形式
y
=
原始数据
[
y
^
1
y
^
2
⋮
y
^
n
]
拟合出的数据
y
^
=
[
y
^
1
y
^
2
⋮
y
^
n
]
参数矩阵(权重矩阵)
w
⃗
=
[
b
w
1
w
2
⋮
w
n
]
自变量矩阵
X
⃗
=
[
1
x
1
1
x
2
1
x
3
⋮
⋮
1
x
n
]
y
^
=
X
⃗
w
⃗
y= 原始数据 \begin{bmatrix} \hat y_1\\ \hat y_2\\ \vdots\\ \hat y_n \end{bmatrix}\quad 拟合出的数据 \hat{y}= \begin{bmatrix} \hat y_1\\ \hat y_2\\ \vdots\\ \hat y_n \end{bmatrix}\quad 参数矩阵(权重矩阵) \vec{w}= \begin{bmatrix} b\\ w_1\\ w_2\\ \vdots\\ w_n \end{bmatrix}\quad 自变量矩阵 \vec{X}= \begin{bmatrix} 1&x_1\\ 1&x_2\\ 1&x_3\\ \vdots\vdots\\ 1&x_n \end{bmatrix}\\ \hat{y}=\vec{X}\vec{w}
y=原始数据
y^1y^2⋮y^n
拟合出的数据y^=
y^1y^2⋮y^n
参数矩阵(权重矩阵)w=
bw1w2⋮wn
自变量矩阵X=
111⋮⋮1x1x2x3xn
y^=Xw
训练损失:
转换成L2范式的样式:
ℓ
(
X
,
y
,
w
)
=
1
2
n
∥
y
−
X
w
∥
2
\quad\ell(\mathbf{X}, \mathbf{y}, \mathbf{w}) = \frac{1}{2n} \|\mathbf{y} - \mathbf{X}\mathbf{w}\|^2
ℓ(X,y,w)=2n1∥y−Xw∥2
参数优化:让损失\ell最小即可,即
w
∗
,
b
∗
=
a
r
g
m
i
n
w
,
b
ℓ
w^*,b^*=arg \underset{w,b}{min}\ell
w∗,b∗=argw,bminℓ,求解方法就是loss对w矩阵求导,令导函数等于0
∂
ℓ
∂
w
=
0
\frac{\partial \ell}{\partial w}=0
∂w∂ℓ=0
解法1:直接求导
∂
ℓ
∂
w
=
1
2
n
∂
(
y
−
X
w
)
(
y
−
X
w
)
T
∂
w
=
1
2
n
∂
(
y
y
T
−
y
X
T
w
T
−
X
w
y
T
+
X
w
X
T
w
T
)
∂
w
=
1
2
n
∂
(
y
y
T
−
2
X
w
y
T
+
X
w
X
T
w
T
)
∂
w
=
1
2
n
(
0
−
2
X
T
y
+
2
X
X
T
w
)
=
1
n
(
X
X
T
w
−
X
T
y
)
=
0
\begin{align*} \frac{\partial \ell}{\partial w} &= \frac{1}{2n}\frac{\partial (y-Xw)(y-Xw)^T}{\partial w}\\ &= \frac{1}{2n}\frac{\partial (yy^T-yX^Tw^T-Xwy^T+XwX^Tw^T)}{\partial w}\\ &= \frac{1}{2n}\frac{\partial (yy^T-2Xwy^T+XwX^Tw^T)}{\partial w}\\ &= \frac{1}{2n}{(0-2X^Ty+2XX^Tw)}\\ &= \frac{1}{n}{(XX^Tw-X^Ty)}=0\\ \end{align*}
∂w∂ℓ=2n1∂w∂(y−Xw)(y−Xw)T=2n1∂w∂(yyT−yXTwT−XwyT+XwXTwT)=2n1∂w∂(yyT−2XwyT+XwXTwT)=2n1(0−2XTy+2XXTw)=n1(XXTw−XTy)=0
化简求解得
X
X
T
w
=
X
T
y
w
=
X
T
y
(
X
X
T
)
−
1
XX^Tw=X^Ty\\ w=X^Ty(XX^T)^{-1}
XXTw=XTyw=XTy(XXT)−1
解法2:链式求导法则(正向累积)
a
=
X
w
b
=
y
−
a
ℓ
=
b
2
∂
ℓ
∂
w
=
∂
ℓ
∂
b
∂
b
∂
a
∂
a
∂
w
=
1
2
n
2
b
(
−
1
)
X
T
=
1
n
(
y
−
X
w
)
X
T
=
0
求解得:
w
=
y
X
T
(
X
X
T
)
−
1
a=Xw\quad b=y-a\quad \ell=b^2\\ \frac{\partial \ell}{\partial w}=\frac{\partial \ell}{\partial b}\frac{\partial b}{\partial a}\frac{\partial a}{\partial w}\\ =\frac{1}{2n}2b(-1)X^T\\ =\frac{1}{n}(y-Xw)X^T=0\\ 求解得: w=yX^T(XX^T)^{-1}
a=Xwb=y−aℓ=b2∂w∂ℓ=∂b∂ℓ∂a∂b∂w∂a=2n12b(−1)XT=n1(y−Xw)XT=0求解得:w=yXT(XXT)−1
优化算法
梯度下降
对于简单的线性回归参数矩阵的最优解有时是有解析解的,但是当特征的数量大于样本量时一般是没有解析解的,并且这种没有解析解的情况在实际的情景中非常常见所以这里直接讨论如何使用梯度下降算法求解参数矩阵的最优解
梯度下降的一般解释:每次沿着本轮训练的梯度的反方向走一小步,直到走到最优点附近。具体每次走多少由超参数 η 确定。η 不能太大也不能太小,太大每次回走太远导致错过最优点,导致w震荡
下面使用torch的自动求导功能求参数矩阵的梯度,然后定义一个函数专门进行梯度下降操作。代码部分结合了李沐老师动手深度学习课程中的代码
import torch
import numpy as np
import matplotlib.pyplot as plt
def generat_data(w,b,data_num):
features=torch.randn(data_num,len(w));
labels=torch.multiply(w,features).sum(axis=1)+b
labels+=torch.normal(0,0.1,labels.shape)
return features,labels
def linreg_matrix(X,w):
return X@w
def squared_loss(y_hat,y):
return (y_hat-y.reshape(y_hat.shape))**2/2/len(y)
def sgd(w,lr):
# w参数矩阵
with torch.no_grad():
w -= lr * w.grad
w.grad.zero_()
w_true=torch.tensor([8,5.56,-3.33,5.33,1.02,4.98,4.45,3.21,-6.78,7.33]);
b_true=2.0
data_num=1000
features,labels=generat_data(w_true,b_true,data_num)
# 可视化生成的数据
fig, axs = plt.subplots(10, 1,figsize=(5, 60))
for i in range(0,len(axs)):
axs[i].scatter(features[:,i], labels)
plt.show()
lr=0.1
epochs=60
# 初始化weight和feature
w=torch.randn(11,1,requires_grad=True)
X=torch.cat([features,torch.ones(len(features),1)],dim=1)
y=labels
# print(X.shape)
# print(X[0:5,:])
loss_history=[]
for epoch in range(0,epochs):
y_hat=linreg_matrix(X,w)
loss=squared_loss(y_hat,y)
loss.sum().backward()
sgd(w,lr)
with torch.no_grad():
epoch_loss=squared_loss(y,linreg_matrix(X,w))
print(f'epoch:{epoch + 1},loss:{float(epoch_loss.mean()):f}')
loss_history.append(epoch_loss.mean())
plt.plot(np.linspace(0,epochs+1,epochs),loss_history)
plt.xlabel('epoch')
plt.ylabel('loss')
plt.title('TrainLoss')
plt.show()
批量随机梯度下降
批量随机梯度下降引入了批量batch的概念,学过操作系统的同学都知道有单道批处理系统,这两者的batch的概念是相同的。批量随机梯度下降就是将一个很大的样本随机分成多个批次,每次只使用一个批次的样本进行训练(梯度下降),这样会大大减小处理器的压力,同时由于打乱了样本的顺序还会提高模型的泛化能力。
import torch
import numpy as np
import matplotlib.pyplot as plt
def generat_data(w,b,data_num):
features=torch.randn(data_num,len(w));
labels=torch.multiply(w,features).sum(axis=1)+b
labels+=torch.normal(0,0.1,labels.shape)
return features,labels
# 分批
def data_iter(batch_size,features,labels):
num_examples=len(features);
indices=list(range(num_examples))
# 打乱顺序
random.shuffle(indices)
# 从0遍历,步长为batch_size
for i in range(0,num_examples,batch_size):
batch_indices=torch.tensor(indices[i:min(i+batch_size,num_examples)])
yield features[batch_indices,:],labels[batch_indices]
# 定义线性模型
def linreg(X,w,b):
# X是数据特征。w,b是初始化好的数据。w,b在定义tensor时要设置requires_grad=True。保存梯度信息
return X@w+b
def squared_loss(y_hat,y):
return (y_hat-y.reshape(y_hat.shape))**2/2
def sgd(params,lr,batch_size):
# 参数向量
# 小批量梯度下降
with torch.no_grad():
for param in params:
param -= lr * param.grad /batch_size
param.grad.zero_()
w_true=torch.tensor([8,5.56,-3.33,5.33,1.02,4.98,4.45,3.21,-6.78,7.33]);
b_true=2.0
data_num=1000
features,labels=generat_data(w_true,b_true,data_num)
# 可视化生成的数据
fig, axs = plt.subplots(10, 1,figsize=(5, 60))
for i in range(0,len(axs)):
axs[i].scatter(features[:,i], labels)
plt.show()
# 开始训练
lr = 0.06
batch_size=10
epochs=20
# eg:初始化参数
w=torch.normal(0,0.01,size=(10,1),requires_grad=True)
b=torch.rand(size=(),requires_grad=True)
# 创建图像
epoch_losses=[]
for epoch in range(0,epochs):
for X,y in data_iter(batch_size,features,labels):
y_hat=linreg(X,w,b)
loss=squared_loss(y_hat,y)
#对损失值求导
loss.sum().backward()
sgd([w,b],lr,batch_size)
with torch.no_grad():
epoch_loss = squared_loss(linreg(features,w,b),labels)
epoch_losses.append(epoch_loss.mean())
# print(f'epoch:{epoch + 1},loss:{float(epoch_loss.mean()):f}')
# 更新图像
plt.plot(np.linspace(0,epochs+1,epochs),epoch_losses)
plt.xlabel('epoch')
plt.ylabel('loss')
plt.title('TrainLoss')
plt.show()