什么是模型复杂度?比较线性回归与决策树与随机森林

使用模拟数据集测试简单和复杂机器学习模型的实用指南。

> 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)

(0)

相关推荐