机器学习之线性回归与Python实现

今天我将带领大家涉足如今最炽手可热的机器学习领域。其重要性想必不用我过多的说明。许多现实中的问题都应用了机器学习的解决方案,如何较好地理解乃至运用其中的原理呢?通过今天的例子希望能调动大家的兴趣以及对基础概念的理解,甚至能对于其用途有自己的一个想法。

首先我们考虑一个简单的直线拟合问题。假设小明手上现在有一组数据点(如下图,横轴为x(特征值Features), 纵轴为y(目标值Targets)),现在老师要求小明给出一个数学方程来较好地拟合这个数据集,也就是这个数学方程应能较好地表示这个数据集,那小明怎么才能做到最好呢?

图1-1 数据集

带着这个问题,让我们来学习一个新的概念——梯度下降。如果你现在在山上迷路了,但是你能感受到脚下的陡峭程度,那你最快的方法应该就是沿着最陡峭的方向往下走,这没错吧? 而这就是梯度下降最贴切的含义了,它将是我们学到的第一个求解函数最优化的方法。而在求解小明的这道题中,我们要下的山(将要优化的函数)应该是“损失函数”。“损失函数”是用于测量预测值与目标值之间的误差,其形式针对不同的问题有不同的形式,这里我们将介绍普遍的一种叫做“均方误差”的损失函数,接下来我们会简称为“MSE”(mean square error)。所谓预测值和目标值应该很好理解,前者为我们根据后面我们建立的模型所根据数据集训练而得的值,而目标值则是数据集中的标签(真实值)。

图1-2 MSE 损失函数

那么上图中theta * x - y有什么含义呢? 这里需要介绍一下我们这篇文章中的主角——线性回归模型。线性回归模型的公式如下两图所示:

图1-3 线性回归

图1-4 线性回归的矩阵表示

图中x是数据;y^是在x上预测到的预测值;theta是 模型的参数;

图1-3乍得一看是不是有点眼熟呢?如果只看图1-3的前两项我们发现它只是一个直线方程,截距为theta0。其实在我们本次实验中,我们仅需要该模型中n=1的时候的模型即可(即,y = theta0 + theta1*x),因为根据数据集判断,我们只需一个直线即可较好地拟合。这部分知识在机器学习中称为模型复杂度的选择,本篇文章中我们先不深究,以后会跟大家一个清楚的认识。图1-4中的矩阵表示如果学过线性代数的同学应该不会陌生,这只是把方程式用矩阵表达了而已。

所以MSE函数中theta * x - y其实就是预测值与实际值(目标值)之间的误差,因此它是用来测量我们预测结果与实际结果之间的误差,而我们训练的目标就是让它变得最小。也就是说使用梯度下降的目标就是最小化MSE损失函数,让预测值与目标值间的差距变得最小,以使我们的预测结果最佳。具体的方法我会在讲解代码的时候展开。

此外还有一个与梯度下降息息相关的参数需要介绍,有一个新的概念称为“学习率”,在我们前面那个比喻中,应该对应的就是你下山固定的步幅了。显而易见,如果我们的步幅太小了,那我们走到山的最低处便会耗费过多的时间,那你可能会说,那我把步幅调到最大不就好了吗?的确这里就显得我方才的比喻不恰当了,如果步幅太大的话,你将会走过头,超过了我们的最低处,然后一直地走来走去,呈现一种振荡的状态,或许对人来说这显得很蠢,但是对于机器而言,学习率过大会使函数无法收敛,我们将得不到最优值。因此现在我们知道了选择一个好的学习率对于我们的模型是非常必要的。

图1-5 梯度下降与学习率

让我们来概括一下刚刚提及的一些知识点:

观察数据集

选择n=2的线性回归模型

使用梯度下降方法求解

选择合适的学习率

了解了这些之后,先放下心中的疑虑,我们着手于实践:

一、库的安装

这里我们使用的Python3的开发环境,如果还有没有配置好的同学请上网查询安装教程,此处就不再赘述。这里我们介绍我们需要的一些库的安装。

pip install --upgrade matplotlib numpy pandas scipy scikit-learn

该指令将安装我们本教程中需要的模块,还有一些后续教程中会使用到的模块。

二、数据集的生成

前面提到了小明拥有一个数据集(图1-1),首先我们做实验得有小明的老师给小明的数据集才行,所以这里我就假装是小明的老师吧,下面的两行代码就是数据集:

注释:

3. 因此我们的数据集是拥有0到2的特征值(X),输出在y = 4 + 3*X的基础之上加上了高斯分布的噪音。

其中呢,X是数据集的特征值,y是数据集的标签。其实从中我们可以看出,我们理想的模型应该是y = 3 * X + 4。而我们现在要假装我们不知道,然后让我们的电脑从一堆数据点中得到一个最接近的解。

这里需要注意的是,由图1-3中可知,我们需要的特征集应该每一个实例拥有两个特征(因为我们选择线性回归模型n=2,即只有theta0theta1项),而第一个特征应该是等于1。(即theta0 = 1)因此我们给X中每一个实例的第一列增加多一个1:

X_b = np.c_[np.ones((100, 1)), X]

三、选择合适的学习率和迭代次数

eta = 0.01 # 学习率

n_iterations = 1000 # 迭代次数

m = 100 # 因为我们的X有100行对应的就是100个实例

在这里我们选择了 0.01 的学习率,并且让它循环1000次,看看结果会怎么样。

四、随机生成参数theta

这里我们选择生成了2个theta参数,对应于我们先前X_b中的两个特征值。我们将在这两个随机生成的参数的基础上优化MSE损失函数。如果不理解theta的含义的话,你应该观察一下MSE方程,theta决定了MSE的大小,我们训练的目的就是学习到一组最优的theta来使得MSE的值最小。

五、开始训练

for iteration in range(n_iterations):

gradients = 2/m * X_b.T.dot(X_b.dot(theta)-y)

theta = theta - eta * gradients

注释:其中T为对矩阵的转置dot为矩阵的点乘。

这一步可能对刚接触的同学而已会有些脑瓜子疼,首先让我们解释一下gradients的获得,gradients(梯度)是我们在损失函数(MSE)上对theta向量求偏导数而得到的梯度向量(如图1-6)

图1-6 MSE对于多个theta参数的梯度

跟我第四步中提到的一样,梯度下降方法是通过不断地调整参数theta来使损失函数得到最优化的结果。一旦我们得到了梯度向量,我们就得知了损失函数通向最小值的方向,我们要做的就是在这个方向上去更新我们的theta参数, 以使我们的MSE不断地减小直至抵达最小处。theta的调整公式如下图所示:

图1-7 theta 更新公式

图中还表明了学习率 eta作用在于控制grad(MSE)theta影响的大小。我们再用一幅更加直观的图来表示我们这个训练任务究竟做了些什么:

图1-8 训练过程

图中展示了我们的训练是从某一组theta(因为我们这里有两个theta的值,起始的theta值我们前面通过了随机生成的方法)开始,利用梯度下降的优化方法,计算得MSE在theta方向上的梯度,再根据此梯度来更新theta值,使得MSE损失函数的值不断减小,最终抵达最优化的点。

六、训练结果

print(theta)

是不是很简便呢? 最后我们将训练得到的theta显示出来,与我们原先知道的“课后答案”(y = 4 + 3x)相比较发现,答案已经相当接近了。结果如下图:

图1-9 训练结果

图中向量第一位表示的是theta0=3.74, theta1=3.18。而我们的理论值是theta0=4, theta1=3,所以这是个不错的结果。

七、测试模型

X_new = np.array([[0], [2]])

X_new_b = np.c_[np.ones((2, 1)), X_new]

y_pred = X_new_b.dot(theta)

plt.plot(X, y, '.', X_new, y_pred)

plt.show()

这里同样的,我们首先创建两个新的点,x = 0 和 x = 2,接下来我们将测试这两个点对应的y值。其中x_new是新的数据点,而x_new_b是加入了值为1的截距项,与先前数据集的预处理没什么不同,然后我们根据图1-3中的公式来使用我们的模型预测,并用matplotlab模块将其变得直观可见(将原始数据X和y、测试数据X_newy_pred共同显示出来),可以看到直线就是我们的预测值的两点连成的直线,它几乎经过了原始数据集的中点,取得相当不错的效果。

图1-10 拟合直线

Well done. 现在我们拥有一个神奇的模型,能通过输入新的数据点,来预测这个点的值应该是多少(至少在我们能够忍受的误差之内)。这就是机器学习中线性回归模型的一个例子,然而这只是机器学习的一小部分,其真正的魅力在于其与时俱进的能力,在下来的文章中,我会尝试着把新的模型诸如多项式回归、Logistic 回归、甚至是深度神经网络等知识与实现用简单易懂的形式给大家呈现出来。

谢谢阅读

喜欢就关注我吧

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180616G0OTLH00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券