==线性回归==是机器学习中非常常用的模型之一,特别在研究定量数据的问题中,它能分析变量之间的关系,并给出很好的解释。此外,它还是新方法的一个良好起点:许多有趣的统计学习方法可以被视为线性回归的推广或扩展。例如Lasso回归
,岭回归
,logistic regression
,softmax回归
。具体理论介绍部分大家可以看我这篇文章:统计学习方法之线性回归详解
简单线性回归模型具体形式可以如下表示:
写成向量的形式:
在我们拿到一堆数据之后,我们要做就是找到最好的参数W,bW,bW,b,如何找到最好的参数呢?在那之前我们介绍一下==损失函数==和==梯度下降==
损失函数是衡量一个模型拟合的重要指标,表示实际值和拟合值之间的差距,在线性回归中,损失函数也被称作==平方误差函数==。我们之所以要求出误差的平方和,是因为一般我们认为误差是非负的,而误差绝对值在求导时不太便利,而误差平方损失函数,对于大多数问题,特别是回归问题,都是一个合理的选择。具体定义如下:
我们的目标便是选择出可以使得损失函数能够最小的模型参数,因为线性回归是一个很简单的问题,所有大部分情况下都存在解析解,对L求梯度为0的点。为了方便表示,这里我将bbb也放入到WWW中,则有:
==梯度下降==是一种==优化算法==,后续还会介绍一些其他的优化方法,例如Adam,SGD等。本章暂时用梯度下降来计算参数。梯度下降背后的思想是:开始时我们随机选择一个参数的组合,计算损失函数,然后我们寻找下一个能让损失函数值下降最多的参数组合。
具体公式如下:
我们持续这么做直到得到一个==局部最小值==(local minimum),因为我们并没有尝试完所有的参数组合,所以不能确定我们得到的局部最小值是否便是==全局最小值==(global minimum),选择不同的初始参数组合,可能会找到不同的局部最小值。此时如何设置合适的η\etaη值是需要我们考虑的,如果η\etaη太大,则可能到不了最低点,导致无法收敛,如果η\etaη太小,那收敛过程太慢,这些细节问题在之后再讨论,接下来我们来看看如何实现一个简单的线性回归模型。
导入相关库
import random
import torch
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
为了方便举例,这里使用模拟数据集进行展示。假设样本来自标准正态分布,每个样本有两个特征,我们生成1000个数据集,w=[1,−1]Tw=[1,-1]^Tw=[1,−1]T,b=2b=2b=2,ϵ\epsilonϵ是一个均值为0,标准差为0.1的正态分布
def simulation_data(w,b,n):
X = torch.normal(0,1,(n,len(w)))#生成标准正态分布
y = torch.matmul(X, w)+b#计算回归拟合值
y += torch.normal(0,0.1,y.shape) #加上随机扰动项
return X,y.reshape((-1,1))
true_w = torch.tensor([-1,1],dtype=torch.float32)
true_b = 2
features, target = simulation_data(true_w,true_b,1000)
此时已经生成好了模拟数据集,下面我们绘制图形观察一下
plt.scatter(features[:,(1)].detach().numpy(),target.detach().numpy(),2)
从上图我可以看出,y和一个特征之间的关系呈现明显的线性关系。
下面我们开始初始化我们要求的参数,通常将www设置为均值为0的正态分布,bbb设置为0向量
w = torch.normal(0,0.1,(2,1),requires_grad=True)
b = torch.zeros(1,requires_grad=True)
初始化参数之后,我们下一步就是开始定义我们的线性回归模型
def reg(X,w,b):
return torch.matmul(X,w)+b
def loss_fun(y_hat,y):
return(y_hat-y)**2/2/len(y)
def gd(params,n):
with torch.no_grad():#在外面不需要求解梯度,只有参数更新的时候求参数
for param in params:
param -= n*param.grad#进行梯度下降
param.grad.zero_()#重新将梯度设置为0,这样就不会受到上一次影响
n = 0.01#学习率
num_epochs = 3#训练次数
net = reg
loss = loss_fun
X = features
y = target
for epoch in range(num_epochs):
l = loss(reg(X,w,b),y)#计算损失函数
l.sum().backward()#反向传播求梯度
gd([w,b],n)#更新参数
with torch.no_grad():
train = loss(net(features,w,b),target)
print(f'epoch{epoch+1},loss:{float(train.mean()):f}')
epoch1,loss:0.001797
epoch2,loss:0.001763
epoch3,loss:0.001729
由于本例中是模拟数据集,我们知道真实的参数,因此可以计算出参数估计的误差
print(f'w的误差:{true_w-w.reshape(true_w.shape)}')
print(f'b的误差:{true_b-b}')
w的误差:tensor([-0.8461, 0.8311], grad_fn=<SubBackward0>)
b的误差:tensor([1.4857], grad_fn=<RsubBackward1>)
阅读量:2019
点赞量:0
收藏量:0