什么是模型复杂度?比较线性回归与决策树与随机森林
使用模拟数据集测试简单和复杂机器学习模型的实用指南。
> Photo by Daniel Giannone on Unsplash
机器学习模型是一种学习数据集的输入(独立)特征与目标(独立)特征之间关系的系统,可用于将来进行预测。为了测试模型的有效性,引入了一个全新的类似数据集,其中仅包含输入特征,并且模型应基于在训练过程中获得的见解来预测目标变量。然后使用适当的性能指标来比较预测值与实际目标值的匹配程度。在本文中,我们将测试三种复杂度各异的流行模型的有效性。
· 线性回归
· 决策树
· 随机森林
然后,我们将使用两个度量系统检查它们的性能:均方误差和均值绝对误差。
为了实现上述目的,我们需要一个数据集,我们将自己创建一个全新的数据集。要了解为什么创建自己的数据集,让我们首先了解什么是机器学习。机器学习是识别数据模式的过程。您可以将模式视为'事物运作的方式'。机器学习通过了解特征(X)和标签(y)之间的关系来做到这一点。一些模式很简单,例如高温导致冰淇淋销量增加是简单的线性关系,而其他模式则很复杂,例如关于房屋的详细信息以及它们如何影响房价,这就是机器学习的亮点。就我而言,我创建了一个数据集,其中X和y特征之间的关系是余弦曲线。
> Image by author
步骤1:数据集模拟
总的来说,一切都有一定的工作方式,一个好的模型应该揭示这种模式。在我们的例子中,'真实状态'是余弦曲线,即y = cos(x)。然后,我们将添加一些噪声来模拟'真实状态'值,因为现实世界中的情况永远都不是完美的。
第一步是初始化我们的x值。我使用了np.linspace(开始,结束,长度)。我们希望我们的轴从0开始,以2 * pi(即6.3)结束,并具有100个均匀间隔的步距。
x = np.linspace(0, 2*np.pi, 100)
为了获得上面的真实余弦曲线,我使用了y = np.cos(x)。但是,我们将在cos曲线周围的数据中添加噪声,以创建模拟的y值。
true_y = np.cos(x)
为了产生噪音,我使用了np.random.normal(mean,std,length)。这将创建以均值为中心的正态分布随机值,并且仅以std(标准差)与均值发生变化。我将0用作平均值,将0.5用作std,因此噪声值将在-0.5至0.5之间。我将随机种子设置为567,以便所产生的样本噪声可再现,否则,每次运行代码时,都会产生新数据。
np.random.seed(567)noise = np.random.normal(0, 0.5, 100)
最后一步是将真实的y映射添加到噪声中以创建目标变量y。
y = np.cos(x) + noise
现在我们的值已经准备好,我们将创建一个数据框来保存x和y值。我们将之称为数据框训练,因为这将是训练模型的训练集。
train = pd.DataFrame({'x':x, 'y':y})
现在,我们可以绘制余弦曲线(真实关系)和模拟训练数据。
plt.scatter(train.x, train.y)plt.plot(train.x, np.cos(train.x), color='k')plt.xlabel('x')plt.ylabel('y')plt.show()
> Image by author
测试集
我们还将模拟一个测试集。这对于通过使用测试集中看不见的x值检查模型可以很好地预测y值来测试模型的有效性非常重要。除了使用不同的随机种子765产生的噪声外,我们创建与训练数据完全相同的测试集。
x = np.linspace(0, 2*np.pi, 100)np.random.seed(765)noise = np.random.normal(0, 0.5, 100)y = np.cos(x)+noisetest = pd.DataFrame({'x':x, 'y':y})
让我们一起绘制训练图(蓝点)和测试图(红点)。
> Plot of train and test simulated data by author
步骤2:建立模型
简单线性回归
该模型拟合数据的直线。y ^ =β0+β1x是表示简单线性回归模型的公式;β0是y截距,β1是x的系数,即直线的斜率。y ^是预测值。截距和系数由模型从数据中获知。因此,该模型的任务是获得代表数据中真实模式的β0和β1的最佳估计。
我使用sci-kit learn实现模型。首先,我们导入库,然后初始化模型的实例。
from sklearn.linear_model import LinearRegressionbasic_linear_model = LinearRegression()
接下来,我们将特征(x)与数据集中的目标变量(y)分开。我使用了df.drop('y',axis = 1)。通常的做法是仅删除(删除)目标特征并将其余的列用作输入特征。
features = train.drop('y', axis=1)target = train.y
最后一步是使模型适合数据。这也称为训练模型,并确定β0和β1的最佳估计。
basic_linear_model.fit(features, target)
您可以通过调用线性回归模型的intercept_和coef_方法来获得β0和β1。
print(basic_linear_model.intercept_)print(basic_linear_model.coef_)### Results0.09271652228649466[-0.0169538]
为了进行预测,我们使用model.predict(features)。基本函数为β0+β1x,因此我们的预测由一条直线表示,我们可以绘制该直线以及模拟数据集。
y_preds = basic_linear_model.predict(features)plt.scatter(df.x, df.y)plt.plot(df.x, y_preds, 'k')plt.show()
> Image by author
如观察到的,线性回归模型假设数据中存在线性关系,这对于我们的数据而言不是很好的表示。
多项式线性回归-增加复杂度
与简单的线性回归不同,多项式模型通过添加x的多项式因子(例如x²)来向数据添加曲线。
首先,通过将x²作为另一列添加到我们的数据集中,创建一个二阶多项式模型。我们将新列称为x2,现在公式为y ^ =β0+β1x+β2x²。
我创建了数据框的副本以保留原始副本。为了得到x2,我使用了np.power(value,power)
df2 = train.copy()df2['x2'] = np.power(df2['x'], 2)
创建可重用函数:为了避免编写大量代码,我创建了一个函数,该函数初始化模型,将数据集分为特征和目标变量,然后绘制模型的预测。该函数接收一个数据框和一个模型,然后返回经过训练的模型。
def model_fitter(data, model): features = data.drop('y', axis=1) target = data.y model.fit(features, target) y_preds = model.predict(features) plt.scatter(data.x, data.y) plt.plot(data.x, y_preds, 'r--') return model
现在让我们调用函数并创建我们的二阶多项式模型。
polynomial_second_order = model_fitter(df2, LinearRegression())
> Second order polynomial model by author
哇,看那个!它很好地模拟了数据中的曲线。但是,当我们对全新的看不见的数据进行预测时,我们将了解更多。
我还创建了一个更复杂的三阶多项式模型,其结果几乎与二阶模型相同。通常,如果两个模型的性能相同,则最好选择不太复杂的模型,因为它通常可以更好地推广到新数据。
决策树
决策树通过基于目标变量将数据分为子集来构建模型。这种分支性质允许在数据中建立非常复杂的关系。我们将首先创建一个没有任何参数的决策树。这将创建一个不受约束的决策树,该决策树将继续分裂为越来越小的子集,直到最终叶节点只有1个值。
> Image from www.datasciencecentral.com
from sklearn.tree import DecisionTreeRegressordecision_tree_unconstrained = model_fitter(train,DecisionTreeRegressor())
> Unconstrained Decision Tree by author
这些线代表我们的预测值,很明显,该模型已完全了解了训练数据并完美地预测了所有点。这称为过拟合,当提供新数据时,该模型将无法很好地概括。模型的想法是忽略噪声并学习实际信号,但是该模型也学习了噪声。
深度限制决策树
通过限制决策树可以下降到的级别数,我们将为决策树增加一些复杂性。在我们的例子中,我们将最大深度设置为3,这意味着该树将只能向下分支3个级别,然后才能进行预测。
decision_tree_by_depth = model_fitter(train,DecisionTreeRegressor(max_depth = 3))
> Decision Tree with max_depth=3 by author
这是更好的表现。您可以通过步骤看到决策树的分支性质,这与多项式线性回归的平滑曲线不同。让我们使用另一个参数来约束模型。
逐叶约束决策树
在这里,我们将每个叶子在达到预测值之前可以拥有的最小样本数设置为5.这意味着最小子集将具有5个x值,从而避免了我们在无约束决策树中有每个叶子一个样本的情况。节点。
decision_tree_by_leaf = model_fitter(train,DecisionTreeRegressor(min_samples_leaf = 5))
> Decision Tree with min_samples_leaf = 5 by author
决策树可以使用几个参数。选择最佳模型参数的过程称为超参数优化或调整。
随机森林
您可以将随机森林视为决策树的集合。
随机森林并行训练许多决策树。每个决策树仅在观察值的随机子集上进行训练,并且将预测合并到一个决策树中。这对于防止过度安装非常有效。我们需要在初始化模型时设置random_state以获得可重现的结果,因为每棵树都是在一组随机观测值上训练的。
from sklearn.ensemble import RandomForestRegressorrandom_forest_unconstrained = model_fitter(train, RandomForestRegressor(random_state=111))
> Unconstrained Random Forest by author
不受约束的随机森林仍然是过拟合的,但不如不受约束的决策树那么多。
我们也可以像决策树一样约束随机森林。max_depth = 3和min_samples_leaf = 5。
random_forest_by_depth = model_fitter(train, RandomForestRegressor(random_state=111, max_depth=3))
> Random forest with max_depth=3 by author
random_forest_by_leaf = model_fitter(train, RandomForestRegressor(random_state=111, min_samples_leaf=5))
> Random Forest with min_samples_leaf=5 by author
正如上面的约束随机森林所观察到的,与以步骤显示的决策树相比,预测曲线具有一般的平滑效果。这是因为随机林木是在观测的子集上训练的,然后将这些预测组合起来以创建更好的广义预测。
我们仅探讨了随机森林的两个参数;max_depth和min_samples_leaf。在此处找到扩展列表。
步骤3:在看不见的测试数据集上测试模型性能
我们将使用两种常用的回归机器学习指标来评估模型:均方误差(MSE)和均值绝对误差(MAE)。MSE取平方误差的平均值,并且较大的误差会按比例放大,因此,由于指数的影响,会受到更多的惩罚。MAE取绝对误差的平均值。较低的MSE和MAE是首选。
一般而言,MSE的计算速度要快一些,而MAE则更易于解释。
from sklearn.metrics import mean_squared_errorfrom sklearn.metrics import mean_absolute_error
现在让我们对测试数据进行预测。我创建了一个函数来使其自动化。该函数接收测试数据集,训练好的模型以及要打印的名称。它将测试集分为特征和目标变量,对测试集进行预测,计算MAE和MSE并打印出来。
def model_performance(data, model, name): test_features = data.drop('y', axis=1) test_target = data.y test_preds = model.predict(test_features) mae = mean_absolute_error(test_preds, test_target) mse = mean_squared_error(test_preds, test_target) print(name) print('MAE', np.round(mae,3)) print('MSE', np.round(mse, 3))
对于二阶多项式线性模型,我们需要通过将另一列添加到包含x²值的测试数据中来设计与训练数据集相同的功能。我们将首先创建测试数据的副本,然后添加x2列。
test2 = test.copy()test2['x2'] = np.power(test2.x, 2)
现在,我们将为每个模型运行该函数,并返回均方误差和均值绝对误差。
model_performance(test, basic_linear_model, 'Basic linear regression')model_performance(test2, polynomial_second_order, 'Second Order Polynomial Model')model_performance(test, decision_tree_unconstrained, 'Uncontrained Decision Tree Model')model_performance(test, decision_tree_by_depth, 'Decision Tree Constrained by max_depth = 3')model_performance(test, decision_tree_by_leaf, 'Decision Tree Constrained by min_samples_leaf = 5')model_performance(test, random_forest_unconstrained, 'Unconstrained Random Forest')model_performance(test, random_forest_by_depth, 'Random Forest Constrained by max_depth = 3')model_performance(test, random_forest_by_leaf, 'Random Forest Constrained by min_samples_leaf = 5')
以下是结果
Basic linear regression
MAE 0.677
MSE 0.669
Second Order Polynomial Model
MAE 0.427
MSE 0.277
Uncontrained Decision Tree Model
MAE 0.523
MSE 0.434
Decision Tree Constrained by max_depth = 3
MAE 0.441
MSE 0.289
Decision Tree Constrained by min_samples_leaf = 5
MAE 0.427
MSE 0.287
Unconstrained Random Forest
MAE 0.47
MSE 0.333
Random Forest Constrained by max_depth = 3
MAE 0.424
MSE 0.28
Random Forest Constrained by min_samples_leaf = 5
MAE 0.416
MSE 0.276
我们可以看到,对于这两个指标,受叶子约束的随机森林的误差最小,因此表现最佳。不出所料,简单线性回归的表现最差,因为数据显然缺乏线性关系。但是,即使曲线看起来拟合得很好,二阶多项式模型还是表现第二好的模型。这说明了为什么随机森林在应用机器学习中如此广泛地被使用。
在本文中,我们模拟了训练和测试数据集,拟合了各种模型(线性模型和基于树的模型),并探讨了各种模型的复杂性;从简单的线性模型到高阶多项式模型,以及受约束的决策树和随机森林。在实践中,您可以基于其他函数(例如y = np.sin(x)或正切的正弦)模拟数据集;y = np.tan(x)。在github上获取完整的代码。https://github.com/suemnjeri/medium-articles/blob/main/Cosine%20Simulated%20Data%20for%20medium%202.ipynb
(本文由闻数起舞翻译自Susan Maina的文章《What is Model Complexity? Compare Linear Regression to Decision Trees to Random Forests》,转载请注明出处,原文链接:https://towardsdatascience.com/what-is-model-complexity-compare-linear-regression-to-decision-trees-to-random-forests-7ec837b062a9)