本文参考python机器学习手写算法系列——线性回归进行适当自己的修改
回归分析是一种因变量为连续值的监督学习
线性回归是一种x和y之间的关系为线性关系的回归分析,也就是最简单那种类似 y=ax+b 即为一元线性回归。
假设问题
从最简单的一元线性回归来谈,我们会得到具体的x列表及对应的y值,例如下:
X | Y |
---|---|
13854 | 21332 |
12213 | 20162 |
11009 | 19138 |
10655 | 18621 |
9503 | 18016 |
我们看一下散点图
从散点图上其实我们也能看到大致的线的趋势,所以问题其实就是如何选择合适的斜率a、截距b点
我们先想一下我们能怎么休闲手动做这件事情:
调整斜率a,调到觉得适合了,我们就调调截距b,总会有个相对拟合情况较好的。
再想:
第一个问题:我们怎么判断哪个好哪个不好?
答:损失函数 Cost Function
第二个问题:怎么调整斜率a和截距b?
答:暴力求解当然是不可能的,所以我们使用到了 优化方法 Optimization Function
所以整个思想就变成了如下:
不断通过损失函数计算目前a、b的模型的优劣,再通过优化算法更新a、b,重复一定次数
损失函数
在线性回归中,我们经常用方差作为损失函数,使得方差尽可能最小
损失函数作为一个评判目前模型的优劣性,还有很多不同的评判标准,对于不同的实际问题也会有不同的计算损失的方法。
我们下面用到损失函数为:
yˆ 为当前a、b值下的x对应的值,y为原本给出的值
优化方法
其实机器学习的优化方法可以看成是去算损失的方向,即我们现在在分析的这个问题,斜率变大了,但我们计算的损失反而增大了,这就说明斜率目前变化的这个方向不对,也就是应该改成斜率减小。需要说明的一点是,我们在进行优化时,变化量是较小的,呈现小幅度改变,通常需要引入一个超参数α来控制,正常取值为0.01。
优化方法的计算一般依据的是偏微分,得到损失函数的变化量,如下所示
慢慢修改我们的a、b值,即能使我们的损失函数的值趋于最小值
我们下面用的算法为偏微分的方法,得到损失函数的变化量
算法步骤
详见程序注释
import matplotlib.pyplot as plt
import numpy as np
def model(a, b, x): #一元线性模型
#a为斜率 b为截距
return [a*i + b for i in x] #返回出当前a、b值所对应的y
def model_single(a,b): #求出模型上的两点用于划线
return (0.9,a*0.9+b),(1.5,a*1.5+b)
def cost_function(a, b, x, y): #损失函数
n = 5
#损失函数公式
return 0.5/n * sum([np.square(y[i]-a*x[i]-b) for i in range(len(y))])
def optimize(a,b,x,y): #优化函数
n = 5
alpha = 1e-1
y_hat = model(a,b,x)
#计算a、b变化量的优化公式
da = (1.0/n) * sum([(y_hat[i]-y[i])*x[i] for i in range(len(y))])
db = (1.0 / n) * sum([(y_hat[i]-y[i]) for i in range(len(y))])
#对a、b进行变化并更新a、b值
a = a - alpha*da
b = b - alpha*db
return a, b
if __name__ == '__main__':
#原始数据
x=[1.3854,1.2213,1.1009,1.0655,0.9503]
y=[2.1332,2.0162,1.9138,1.8621,1.8016]
#使a、b的初始值如下,可随意设置
a,b=(0.1,0)
#选择图表的显示某次更新时的模型情况(可修改,最大值不能大于下面循环的最大值)
sign=[1,5,50,100,1000,10000]
ab=[] #存储上诉点的a、b情况
#循环更新了10100次
for i in range(10100):
a,b=optimize(a,b,x,y)
if i+1 in sign:
ab.append([a,b,cost_function(a,b,x,y)])
# print('第%d次 损失:' % i, model_single(a,b))
#画图
fig = plt.figure()
for i in range(len(sign)):
print('第%d次 损失:' % sign[i], cost_function(a, b, x, y))
ax = fig.add_subplot(231+i)
ax.scatter(x, y, c='r', marker='o')
print(ab[i][0],ab[i][1])
X,Y=model_single(ab[i][0],ab[i][1])
print(X,Y)
ax.plot([X[0],Y[0]],[X[1],Y[1]])
ax.set_title('%d -> %.10f'%(sign[i],ab[i][2]))
plt.show()
结果
我们可以很容易看到,随着训练次数的增加,回归线越趋近于样本,损耗也越来越小。