往哪个方向走?
如果您的机器学习算法无法正常工作,下一步该怎么做?有几种选择:
- 获取更多的训练数据非常耗时。甚至可能需要数月的时间才能获得更多的研究数据。
- 获得更多的训练特征。也可能需要很多时间。但是,如果添加一些多项式特征可以工作,那就太酷了。
- 选择较小的一组训练特征。
- 增加正则项
- 减少正则项。
那么,接下来您应该尝试哪一个呢?开始尝试任何操作都不是一个好主意。因为您可能最终会花太多时间在无用的事情上。您需要先发现问题,然后采取相应措施。学习曲线有助于轻松检测问题,从而节省大量时间。
学习曲线对于确定如何提高算法性能非常有用。确定算法是否遭受偏差或拟合不足,方差或拟合过度,或两者兼而有之,这很有用。
学习曲线的工作原理
学习曲线是成本函数的图。在同一图中,训练数据的成本函数和交叉验证数据的成本函数为算法提供了重要的见解。提醒一下,这是成本函数的公式:
换句话说,它是预测输出减去原始输出的平方除以训练数据数量的两倍。要绘制学习曲线,我们需要将这些成本函数绘制为训练数据数量(m)的函数。代替使用所有训练数据,我们将仅使用训练数据的较小子集来训练数据。
看看下面的图片:
如果我们使用太少的数据来训练数据,则该算法将完全适合训练数据,并且成本函数将返回0。
在上面的图片中清楚地表明,当我们仅使用一个,两个或三个数据算法来训练数据时,就可以很好地了解到很少的数据,并且训练成本为零或接近于零。但是,这种类型的算法无法在其他数据上很好地执行。
当您尝试使交叉验证数据适合此算法时,在交叉验证数据上执行效果很差的可能性很高。因此,交叉验证数据的成本函数将返回非常高的值。
另一方面,当我们将需要越来越多的数据来训练算法时,它将不再完全适合训练数据。因此,培训成本将变得更高。
同时,由于该算法针对大量数据进行训练,因此在交叉验证数据上的性能会更好,并且交叉验证数据的成本函数将返回较低的值。这是如何建立学习曲线的方法。
开发学习算法
我将演示如何逐步绘制学习曲线。为了绘制学习曲线,我们首先需要机器学习算法。为简单起见,我将使用线性回归算法。首先,我们开发一个线性回归算法。
首先,导入包和数据集。我在这里使用的数据集取材于安德鲁·伍(Andrew Ng)的Coursera机器学习课程。在此数据集中,X值和y值在Excel文件中的单独工作表中进行组织。
提醒一下,X是我们将用来开发和训练机器学习算法的功能。y是我们需要预测的输出特征。
交叉验证数据的X和y值也被组织在同一Excel文件中的其他两个工作表中。我在本文结尾处提供了到数据集的链接。请随时下载数据集并进行练习。
- %matplotlib inline
- import pandas as pd
- import numpy as np
- import matplotlib.pyplot as plt
- file = pd.ExcelFile('dataset.xlsx')
- df = pd.read_excel(file, 'Xval', header=None)
- df.head()
以相同的方式,导入训练集的y值:
- y = pd.read_excel(file, 'yval', header=None)
- y.head()
让我们快速开发线性回归算法。
(1) 定义假设
线性回归使用非常基本的线性方程式进行我们在学校学习的预测。公式如下:
Y = C + BX
对于机器学习,我们使用不同的术语。
在这里," h"是假设或预测值,theta0和theta1是系数,X是输入特征。
在这里,我们已经有了X。我们必须计算" h",并且期望它与y的值匹配。因为我们的目标是能够预测y的值。
Theta0和theta1在开始时是随机初始化的。我们将通过迭代不断完善theta0和theta1的值。
在每次迭代中,我们将使用成本函数和梯度公式来计算成本以更新theta值
(2) 成本函数和梯度下降
成本函数为我们提供了有关我们的预测值与原始输出特征有何不同的想法。在这里,我们的输出特征为y,预测输出为" h"。因此,成本函数将告诉我们" h"与" y"的偏离量。我们希望成本函数值尽可能低。
这是成本函数的公式:
在成本函数最小之前,我们将不断淘汰算法。在每次迭代中,我们使用梯度下降来更新theta值。
要更新theta值,我们将从先前的theta值中减去梯度下降。当我们对其进行编码时,它将更加清晰。
此处,m是训练数据的数量,而alpha是学习率。
(3) 开发线性回归算法
使用上述公式开发假设和成本函数。
- m = len(df)
- def hypothesis(theta, X):
- return theta[0] + theta[1]*X
- def cost_calc(theta, X, y):
- return (1/2*m) * np.sum((hypothesis(theta, X) - y)**2)
现在,我们将定义梯度下降以优化参数theta0和theta1。在每次迭代中,我们将更新theta值并跟踪成本函数和theta值。
最后,它将返回每个迭代theta值中的成本列表。代码很简单。请在这里检查。
- def gradient_descent(theta, X, y, epoch, alpha):
- cost = []
- theta_hist = []
- i = 0
- while i < epoch:
- hx = hypothesis(theta, X)
- theta[0] -= alpha*(sum(hx-y)/m)
- theta[1] -= (alpha * np.sum((hx - y) * X))/m
- cost.append(cost_calc(theta, X, y))
- i += 1
- return theta, cost
完成了线性回归算法。我们需要一种预测输出的方法。在预测方法中,我们将使用来自梯度下降函数和假设函数的最终theta进行预测。
- def predict(theta, X, y, epoch, alpha):
- theta, cost = gradient_descent(theta, X, y, epoch, alpha)
- return hypothesis(theta, X), cost, theta
现在,将参数初始化为零,并使用预测函数预测输出变量。
- theta = [0,0]
- y_predict, cost, theta = predict(theta, df[0], y[0], 1400, 0.001)
现在,在同一图中绘制df或X的预测输出(h)和原始输出(y)。
- plt.figure()
- plt.scatter(df, y)
- plt.scatter(df, y_predict)
看来算法运作良好。预测的输出线从中间位置开始。
是时候建立学习曲线了!!!
画出学习曲线
现在,我们可以画出学习曲线。首先,让我们为我们的交叉验证数据集导入X和y值。如前所述,我们将它们组织在单独的Excel工作表中。
- file = pd.ExcelFile('dataset.xlsx')
- cross_val = pd.read_excel(file, 'X', header=None)
- cross_val.head()
- cross_y = pd.read_excel(file, 'y', header=None)
- cross_y.head()
为此,我想稍微修改一下gradient_descent函数。
在之前的gradient_descent函数中,我们计算了每次迭代的成本。我这样做是因为这是传统机器学习算法开发中的一种很好的做法。
但是对于学习曲线,我们不需要每次迭代的成本。因此,为了节省运行时间,我将在每个时期中排除计算成本函数。我们将仅返回更新的参数。
- def grad_descent(theta, X, y, epoch, alpha):
- i = 0
- while i < epoch:
- hx = hypothesis(theta, X)
- theta[0] -= alpha*(sum(hx-y)/m)
- theta[1] -= (alpha * np.sum((hx - y) * X))/m
- i += 1
- return theta
如前所述,要开发学习曲线,我们需要使用训练数据的不同子集来训练学习算法。
在我们的训练数据集中,我们有21个数据。我将仅使用一个数据,然后使用两个数据,然后使用三个数据一直到21个数据来训练算法。
因此,我们将在21个训练数据子集上对算法进行21次训练。我们还将跟踪每个训练数据子集的成本函数。请仔细看一下代码,它将更加清晰。
- j_tr = []
- theta_list = []
- for i in range(0, len(df)):
- theta = [0,0]
- theta_list.append(grad_descent(theta, df[0][:i], y[0][:i], 1400, 0.001))
- j_tr.append(cost_calc(theta, df[0][:i], y[0][:i]))
- theta_list
以下是每个训练数据子集的训练参数:
这是每个训练子集的费用:
查看每个子集的成本。当训练数据仅为1或2时,成本为零或几乎为零。随着我们不断增加培训数据,成本也上升了,这是预期的。
现在,对训练数据的所有子集使用上面的参数来计算交叉验证数据的成本:
- j_val = []
- for i in theta_list:
- j_val.append(cost_calc(i, cross_val[0], cross_y[0]))
- j_val
刚开始时,成本确实很高,因为训练参数来自太少的训练数据。但是随着参数的增加和更多训练数据的改进,交叉验证错误不断下降。
让我们在同一图中绘制训练误差和交叉验证误差:
- %matplotlib inline
- import matplotlib.pyplot as plt
- plt.figure()
- plt.scatter(range(0, 21), j_tr)
- plt.scatter(range(0, 21), j_val)
这是我们的学习曲线。
从学习曲线中得出决策
上面的学习曲线看起来不错。它以我们预期的方式流动。最初,训练误差太小,验证误差太高。
慢慢地,它们彼此完全重叠。太完美了!但是在现实生活中,这种情况并不经常发生。
大多数机器学习算法并不是第一次都能完美运行。它几乎始终都遭受一些我们需要解决的问题的困扰。在这里,我将讨论一些问题。
我们可能会发现学习曲线如下所示:
训练误差和验证误差之间是否存在重大差异,表明存在高方差问题。也可以称为过度拟合问题。
获取更多的训练数据或选择较小的功能集或同时使用这两种功能都可以解决此问题。
如果倾斜曲线看起来像这样,则意味着开始时训练误差太小而验证误差太高。缓慢地,训练误差变高而验证误差变低。但在某种程度上,它们变得平行。您可以从图片中看到一点,即使有了更多训练数据,交叉验证错误也不再减少。
在这种情况下,获取更多训练数据将不会改善机器学习算法。
这表明学习算法正在遭受高偏差问题。在这种情况下,获得更多训练功能可能会有所帮助。
修正学习算法
假设我们正在执行线性回归。但是该算法无法正常工作。
该怎么办?
首先,画出一个学习曲线,如我在此处演示的。
- 如果检测到高方差问题,请根据要素的重要性选择一组较小的要素。如果有帮助,可以节省一些时间。如果不是,请尝试获取更多训练数据。
- 如果您从学习曲线中发现高偏差问题,那么您已经知道获得附加功能是一种可能的解决方案。您甚至可以尝试添加一些多项式特征。大量时间可以帮助您节省大量时间。
- 如果您要使用正则化项lambda实现算法,请在该算法遇到高偏差的情况下尝试降低lambda,并在该算法遇到高方差问题的情况下尝试增加lambda。
在神经网络的情况下,我们也可能遇到这种偏差或方差问题。
对于高偏差或欠拟合问题,我们需要增加神经元的数量或隐藏层的数量。为了解决高方差或过度拟合的问题,我们应该减少神经元的数量或隐藏层的数量。我们甚至可以使用不同数量的神经元绘制学习曲线。
非常感谢您阅读本文。我希望这可以帮到你。