第一次使用线性回归时,一直无法想象电脑是怎么找到这条最佳的拟合直线的。知道看过了机器学习我想才对此有了一点点理解

回归分析是确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法。一元线性回归只包括一个x和一个y,可以用一条直线来近似表达他们之间的关系

比如我们在python中制造一个数据,数据按照y=2*x+3设计,每一个x对应的y的值上下波动0.2

x = [1, 2, 3, 4, 5]
y = [4.8, 7.2, 8.8, 11.2, 12.8]
plt.scatter(x, y)
plt.xlim(0,6)
plt.ylim(0,14)
plt.show()

就得到这样的一个图形,现在我们就需要用一条直线y=a*x+b最好地拟合这些点,那么如何确定这里的a和b呢
如何理解线性回归

我们需要一个评价它拟合的好不好的指标,那就是每个点向x轴做垂线到这条直线的距离的平方和最小(也就是这里的d1^2+d2^2+d3^2最小,这里的di就是真实值和预测值的距离,我们用y表示真实值,y_h表示预测值)
如何理解线性回归

这里我们用J=∑(y-y_h)^2这个函数表示,我们需要J的值最小时,就是最佳的拟合直线,这个J也可以写为J=∑[y-(a*x+b)]^2,所以我们就是要寻找a和b使得J最小
现在我们假设b=0,不断改变a的值,去做出J关于a的一个函数图形如下(横轴为a,竖轴为J)

a = 0
b = 0
J = 0
Js = []
for i in range(0,10):
    a = i
    J = 0
    for j in range(0,5):
        y_h = (a*x[j])+b
        J = J+(y[j]-y_h)**2
    Js.append(J)
plt.plot(Js)
plt.show()

如何理解线性回归

同理我们使a=0,不断改变b的值,得到J关于b的一个函数图像如图(横轴为b,竖轴为J)

a = 0
b = 0
J = 0
Js = []
for i in range(0,20):
    b = i
    J = 0
    for j in range(0,5):
        y_h = (a*x[j])+b
        J = J+(y[j]-y_h)**2
    Js.append(J)
plt.plot(Js)
plt.show()

如何理解线性回归

这时我们可以看到,J与a和与b都是一个二次函数的图像,分别在只有a和只有b时都有J有一个最小值Jmin,也就是说当b=0,方程为大概y=2.5*x,使得J最小;当a=0时,方程大概为y=8,使得J最小。可是我们知道这个方程应该为y=2*x+3。由以上的内容我们想想一个三维的图形,平面坐标是a和b,z轴是J,那么这个曲面应该是一个凹面,它拥有一个最低点,而这个最低点对应的平面坐标a和b就是使得J最小的a和b,也就是方程的最佳拟合时的参数a和b的值。这里我们试着绘制一下,先导入一个包

from mpl_toolkits.mplot3d import Axes3D

绘制

def func(x, y, a, b):
    J = 0
    for k in range(0,5):
        y_h = (a*x[k])+b
        J = J+(y[k]-y_h)**2
    return J

figure = plt.figure()
ax = Axes3D(figure)
a = np.arange(0, 4, 0.01)
b = np.arange(-1, 9, 0.01)
a, b = np.meshgrid(a, b)
ax.plot_surface(a, b, func(x, y, a, b), cmap='rainbow')
plt.show()

如何理解线性回归

这个看起来效果不太理想,但是大概能看出字中有一个最低点。现在线性回归要做的就是到这个最低点找到a和b。这里就是一个梯度下降的过程了。随便初始化一个a和b,它会对应这个曲面上的某个点,然后开始用梯度下降逐渐滑到最低点去。比如一个二次函数y=x^2
如何理解线性回归

这里就涉及到求导,因为你不知道开始是在坐标轴的坐标还是右边。导数为y'=2*x,在对称轴的左边,导数为负;在对称轴的右边,导数为正。这样如果用x=x-y'的话,无论x为正还是为负,都会向着中间x=0的最低处移动。比如在下图中的p点,p的值为负,y'=2*p,此时y’为负,若x=x-y',则x实际是向右边移动了,也就是向着最低点前进。同时由于导数越来越小,最后就会基本到到最低点
如何理解线性回归

这里我们只需要不断地求J,然后让a=a-α*(d/da)*Jb=b-α*(d/da)*J(这里的α是一个常数),又不断地更新a和b,再求J,再重复,直到J基本到达最低点就可以说此时的a和b就是让方程y=a*x+b满足的最好拟合参数。

a = 0
b = 0
J = 0
ah = 0.0005

def updata(x, y, a, b):
    Ja = 0
    Jb = 0
    for j in range(0,5):
        y_h = (a*x[j])+b
        Ja = Ja+(y_h-y[j])
        Jb = Jb+Ja*x[j]
    a = a-ah*Ja
    b = b-ah*Jb
    return a, b
    
for i in range(20000):
    J = func(x, y, a, b)   # 前面定义了
    a, b = updata(x, y, a, b)
    if i % 2000 == 0:
        print('a='+str(a)+' b='+str(b)+' J='+str(J))

这里我们看一下循环得到的结果

a=0.0224 b=0.2216 J=441.6
a=1.7664841446040385 b=3.580773535019366 J=0.770107135157793
a=1.9492674087971757 b=3.0994595812218364 J=0.21858310750245388
a=1.9784539542211008 b=3.02260412272189 J=0.19666523846829964
a=1.9831144160940348 b=3.010331963449066 J=0.19485203231430406
a=1.983858591348545 b=3.0083723642721525 J=0.19460550610000818
a=1.9839774200879534 b=3.0080594585439284 J=0.19456723765429518
a=1.9839963944749992 b=3.0080094942471955 J=0.19456115496582815
a=1.983999424275414 b=3.008001516026525 J=0.19456018440448342
a=1.9839999080691983 b=3.0080002420767453 J=0.1945600294449439

可以发现随着a和b的改变,梯度下降使得J越来越小,此时对应的a=1.98b=3.01就可以得到方程y=1.98*x+3.01,与结果就基本差不多了,因为有波动,所以J是不可能等于0的,就是说一条直线不能完全地经过这些点,我们看看拟合的效果

plt.scatter(x, y)
x2 = np.linspace(0, 6, 100)
fy = a*x2+b
plt.plot(x2, fy)
plt.xlim(0,6)
plt.ylim(0,14)
plt.show()

如何理解线性回归

在python中可以使用sklearn这个包里的工具快速实现线性回归

from sklearn.linear_model import LinearRegression

x = [[1], [2], [3], [4], [5]]
y = [[4.8], [7.2], [8.8], [11.2], [12.8]]
model = LinearRegression()
model.fit(x, y)
model.score(x, y)
beta= model.intercept_[0]
alpha= model.coef_[0][0]
print('线性回归函数为:\n')
print(str(alpha) + ' x + '+ str(beta))

得到结果就是这样的

线性回归函数为:

2.0000000000000004 x + 2.9599999999999973

都差不多的,好了,这就是关于线性回归的理解

相关文章: