任何机器学习算法都会有一个很常见的问题,就是过拟合问题(overfitting),经常都能看到很多人在问随机森林会不会出现过拟合问题,在外国的网站看到了这篇文章,觉得写的很好,所以翻译转载在这里。

提出问题:随机森林是否会过拟合?

当我第一次看到这个问题时,我有点惊讶,第一个想法是,当然!任何复杂的机器学习算法都会过拟合。我已经训练了数百个随机森林(RF)模型,并且多次观察到它们过拟合。第二个想法是,为什么人们会问这样的问题?我们多做一些研究,在Google了一下之后,我在Leo Breiman(随机森林算法的创建者)网站上找到了以下段落:

Random forests does not overfit. You can run as many trees as you want.

Breiman原文(在他的文章里面声明了一点,文章中的RF的模型是通过800Hz的处理器运行的),大意就是说随机森林不会过拟合,你想跑多少树就跑多少树。

听到这里,可能大家都觉得不可思议,为啥随机森林不会过拟合?我想该算法的开创者是最了解的,他比我聪明多了,他肯定是对的。但是,之前我确实已经看到很多次RF过拟合,这就不得不让我很困惑,所以可以一点点的揭开随机森林的面纱。

什么是随机森林?

随机森林就是很多棵树,单科决策树对数据的变化很敏感,很容易对一些噪声进行过拟合,所以说只有一棵树的随机森林也会出现过拟合,和决策树是一样的道理,当我们逐个添加决策树到随机森林中时,过拟合的趋势会减少(由于袋装(Bagging)和随机特征选择),但是,通常错误不会变为零,加上更多的树之后,误差方差将接近零,但偏差不会!这是一个有用的特性,它告诉我们RF中的树越多越好,也许这就是Breiman在写关于RF和过拟合的文章时的想法。

更多了解随机森林可以参考:https://www.cnblogs.com/maybe2030/p/4585705.html (写的很好)

一个随机森林过拟合的例子(Python)

为了展示一个随机森林过拟合的例子,在这里将使用以下公式生成一个非常简单的数据:

y = 10 * x + noise

这里使用从0到1均匀分布的x,噪声是平均值和单位方差为零的正态分布,加起来得到变量y,下面是我们的数据示例图。

使用以下代码可以生成以上的数据并将其拆分为一个序列和测试子集:

data = np.random.uniform(0, 1,(1000, 1))
noise = np.random.normal(size=(1000,))
X = data[:,:1]
y = 10.0*(data[:,0]) + noise
# split to train and test
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, test_size=0.4, random_state=2019)

这个RF模型中只有一个输入变量x和一个输出变量y,为了训练这个模型我使用python中的scikit-learn库,这里我会训练两个模型,一个是满树模型,另一个是由min_samples_leaf超参数控制的修剪模型。训练满树随机森林的代码:

rf = RandomForestRegressor(n_estimators=50)
rf.fit(X_train, y_train)
y_train_predicted = rf.predict(X_train)
y_test_predicted_full_trees = rf.predict(X_test)
mse_train = mean_squared_error(y_train, y_train_predicted)
mse_test = mean_squared_error(y_test, y_test_predicted_full_trees)
print("RF with full trees, Train MSE: {} Test MSE: {}".format(mse_train, mse_test))

误差采用均方误差,越低越好,结果:

可以看到满树的RF模型在训练数据上得到MSE(均方差):0.20,在测试数据上得到MSE:1.41。

接下来我们用修剪过的树检验一下RF:

rf = RandomForestRegressor(n_estimators=50, min_samples_leaf=25)
rf.fit(X_train, y_train)
y_train_predicted = rf.predict(X_train)
y_test_predicted_pruned_trees = rf.predict(X_test)
mse_train = mean_squared_error(y_train, y_train_predicted)
mse_test = mean_squared_error(y_test, y_test_predicted_pruned_trees)
print("RF with pruned trees, Train MSE: {} Test MSE: {}".format(mse_train, mse_test))

结果:

可以看到,对于修剪过的树,训练数据集的MSE结果为0.91,测试数据集上的MSE为1.04。

到这里其实就有明显的过拟合的证据!满树的RF在训练数据集上的误差比具有修剪树的RF小得多,但测试数据上的误差会比修剪树的更高——出现了过拟合。我们可以在散点图上想象一下,左边是过拟合RF的结果,右边是修剪后随机林的结果。

我们看到满树的RF过拟合了,可以预测在训练过程中它学习到的噪声,修剪过的树对RF的结果要平滑得多,因此,它的泛化能力更好。

拟合和树的增长

我们可以检验一下随机森林模型在增加树的数量时的行为,我们从1棵树开始训练RF模型,并在每个循环迭代中添加1棵树,在每一步中,我们都会测试训练数据集和测试数据集上的MSE。

rf = RandomForestRegressor(n_estimators=1)
for iter in range(50):
  rf.fit(X_train, y_train)
  y_train_predicted = rf.predict(X_train)
  y_test_predicted = rf.predict(X_test)
  mse_train = mean_squared_error(y_train, y_train_predicted)
  mse_test = mean_squared_error(y_test, y_test_predicted)
  print("Iteration: {} Train mse: {} Test mse: {}".format(iter, mse_train, mse_test))
  rf.n_estimators += 1

plot绘制结果图:

显而易见,可以看到我们在使用满树RF的时候,出现了过拟合,但是可以从上面散点图中看到,过拟合不会根据RF模型中的树的增加而增加,它随着更多的树而趋于稳定。

结论

  • RF算法确实会过拟合。
  • 当算法中加入更多的树时,随机林中的泛化误差方差将减小到零,然而,泛化的偏差并没有改变。
  • 为了避免在RF中过拟合,应调整算法的超参数(hyper-parameters),例如,叶子节点中的样本数。

上文中所有的代码链接:Google Colab

原文地址:https://mljar.com/blog/random-forest-overfitting/

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐