第一次使用线性回归时,一直无法想象电脑是怎么找到这条最佳的拟合直线的。知道看过了机器学习我想才对此有了一点点理解
回归分析是确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法。一元线性回归只包括一个x和一个y,可以用一条直线来近似表达他们之间的关系
比如我们在python中制造一个数据,数据按照 设计,每一个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()
就得到这样的一个图形,现在我们就需要用一条直线
最好地拟合这些点,那么如何确定这里的
和
呢
我们需要一个评价它拟合的好不好的指标,那就是每个点向
轴做垂线到这条直线的距离的平方和最小(也就是这里的
最小,这里的
就是真实值和预测值的距离,我们用
表示真实值,
表示预测值)
这里我们用
这个函数表示,我们需要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 = 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()
这时我们可以看到, 与 和与 都是一个二次函数的图像,分别在只有 和只有 时都有 有一个最小值 ,也就是说当 ,方程为大概 ,使得 最小;当 时,方程大概为 ,使得 最小。可是我们知道这个方程应该为 。由以上的内容我们想想一个三维的图形,平面坐标是 和 , 轴是 ,那么这个曲面应该是一个凹面,它拥有一个最低点,而这个最低点对应的平面坐标 和 就是使得 最小的 和 ,也就是方程的最佳拟合时的参数 和 的值。这里我们试着绘制一下,先导入一个包
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()
这个看起来效果不太理想,但是大概能看出其中有一个最低点。现在线性回归要做的就是到这个最低点找到 和 。这里我们使用梯度下降的方法(还可以使用正规方程),就是一个梯度下降的过程了。初始化 和 为零,它会对应这个曲面上的某个点,然后开始用梯度下降逐渐滑到最低点去。比如一个二次函数
梯度下降就是沿着这个曲面下降到最低点,这里就涉及到求导,因为你不知道开始是在坐标轴的坐标还是右边。导数为 ,在对称轴的左边,导数为负;在对称轴的右边,导数为正。这样如果用 的话,无论 为正还是为负,都会向着中间 的最低处移动。比如在下图中的 点, 的值为负, ,此时y’为负,若 ,则 实际是向右边移动了,也就是向着最低点前进。同时由于导数越来越小,最后就会基本到到最低点
这里我们只需要不断地求 ,然后让 和 (这里的 是一个常数),又不断地更新 和 ,再求 ,再重复,直到 基本到达最低点就可以说此时的 和 就是让方程 满足的最好拟合参数。
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
可以发现随着 和 的改变,梯度下降使得 越来越小,此时对应的 , 就可以得到方程 ,与结果就基本差不多了,因为有波动,所以 是不可能等于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
都差不多的,好了,这就是关于线性回归的简单理解