线性回归简介
线性回归,就是能够用一个直线较为精确地描述数据之间的关系。这样当出现新的数据的时候,就能够预测出一个简单的值。线性回归中最常见的就是房价的问题。一直存在很多房屋面积和房价的数据,如下图所示:
在这种情况下,就可以利用线性回归构造出一条直线来近似地描述放假与房屋面积之间的关系,从而就可以根据房屋面积推测出房价:
线性回归的函数模型
通过线性回归构造出来的函数一般称之为了线性回归模型。线性回归模型的函数一般写作为:
线性回归实际上要做的事情就是: 选择合适的参数(θ0, θ1),使得hθ(x)方程,很好的拟合训练集。
线性回归模型的特点:
- 建模速度快,不需要很复杂的计算,在数据量大的情况下依然运行速度很快。
- 可以根据系数给出每个变量的理解和解释。
- 对异常值很敏感。
Python中的sklearn库中有一个LinearRegression类就是线性回归,下面就利用该类写一个简单的单变量线性回归的代码,代码如下:
"""
使用sklearn实现线性回归
"""
import numpy as np
from sklearn.linear_model import LinearRegression
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
lin_reg = LinearRegression()
# fit方法就是训练模型的方法
lin_reg.fit(X, y)
# intercept 是截距 coef是参数
print(lin_reg.intercept_, lin_reg.coef_)
# 预测
X_new = np.array([[0], [2]])
print(lin_reg.predict(X_new))
可的输出结果如下:
前两个数字就是θ0和θ1,后面两个数字是分别当x为0和x为2时的预测结果。
线性回归的代价函数
通过线性回归算法,我们可能会得到很多的线性回归模型,但是不同的模型对于数据的拟合或者是描述能力是不一样的。我们的目的最终是需要找到一个能够最精确地描述数据之间关系的线性回归模型。这是就需要用到代价函数。代价函数就是用来描述线性回归模型与正式数据之前的差异。如果完全没有差异,则说明此线性回归模型完全描述数据之前的关系。如果需要找到最佳拟合的线性回归模型,就需要使得对应的代价函数最小,相关的公式描述如下:
可以看出我们的目标是使损失函数最小化,就引入了梯度下降算法。
梯度下降
梯度下降是一种非常通用的优化算法,能够为大范围的问题找到最优解。梯度下降的中心思想就是迭代的调整参数从而使损失函数最小化。
假设你迷失在山上的浓雾之中,你能感觉到的只有你脚下路面的坡度。快速到达山脚的一个策略就是沿着最陡的方向下坡。这就是梯度下降的做法:通过测量参数向量θ相关的误差函数的局部梯度,并不断沿着降低梯度的方向调整,直到梯度降为0,到达最小值。
具体来说,首先使用一个随机的θ值(这被称为随机初始化),然后逐步改良,每次踏出一步,每一步都尝试降低一点损失函数(如MSE),直到算法收敛出一个最小值。如下图所示:
梯度下降公式为:
梯度下降的方向:
- 只要对损失函数求导,θ的变化方向永远趋近于损失函数的最小值。
- 如果θ已经在最低点,那么梯度将不会发生变化。
梯度下降的步长:
- 梯度下降中一个重要参数是每一步的步长,这取决于超参数的学习率
如果学习率太低,算法需要经过大量迭代才能收敛,这将耗费很长时间,如下图:
如果学习率太高,那你可能会越过谷底直接到达另一边,甚至有可能比之前的起点还高。这会导致算法发散,值越来越大,最后无法找到好的解决方案。如下图:
- 梯度下降的步长会逐渐变小
梯度下降的局部最小值问题
并不是所有的损失函数看起来都像是一个漂亮的碗,有的可能像山洞、山脉、高原或者各种不规则的地形,导致很难收敛到最小值。
比如下面这个图,如果随机初始值从左侧起步,那么会收敛到一个局部最小值,而不是全局最小值;如果算法从右侧起步,那么需要经过很长时间才能越过正整片高原。如果你停下得太早,将永远达不到全局最小值。
那么如何应对?
- 对于MSE来说,因为损失函数是个凸函数,所以不存在局部最小值,只有一个全局最小值
- 通过随机初始化θ,可以避开局部最小值
- 对于多变量,高维度的值,就算在某个维度上陷入和局部最小值,但是还能从别的维度跳出
下面我们就简单编写一个利用梯度下降算法使损失函数达到最小化的代码,具体代码如下:
"""
线性回归实现梯度下降的批处理(batch_gradient_descent )
"""
import numpy as np
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
X_b = np.c_[np.ones((100, 1)), X]
# print(X_b)
learning_rate = 0.1
# 通常在做机器学习的时候,一般不会等到他收敛,因为太浪费时间,所以会设置一个收敛次数
n_iterations = 1000
m = 100
# 1.初始化theta, w0...wn
theta = np.random.randn(2, 1)
count = 0
# 4. 不会设置阈值,之间设置超参数,迭代次数,迭代次数到了,我们就认为收敛了
for iteration in range(n_iterations):
count += 1
# 2. 接着求梯度gradient
gradients = 1/m * X_b.T.dot(X_b.dot(theta)-y)
# 3. 应用公式调整theta值, theta_t + 1 = theta_t - grad * learning_rate
theta = theta - learning_rate * gradients
print(count)
print("theta:", theta)
输出结果如下: