偏差-方差分解与集成学习

机器学习中,回归问题的预测误差可以通过偏差、方差和噪声三个部分来分解。偏差衡量了模型预测值与最佳可能模型(贝叶斯模型)预测值之间的平均差异。方差则衡量了模型在不同数据集上训练时预测值的变化程度。噪声则是由于数据本身的变异性导致的不可减少的误差部分。

决策树为例,可以通过训练单个决策树模型来观察其预测的偏差和方差。在左侧的图表中,可以看到单个决策树在不同数据集上的预测结果(暗红色曲线),以及这些预测结果的分布(浅红色区域)。这个分布的宽度直观地反映了模型预测的方差。偏差则通过模型预测的平均值(青色曲线)与最佳可能模型(深蓝色曲线)之间的差异来表示。在这个例子中,可以看到偏差相对较低,而方差较大。

在右侧的图表中,使用了集成方法(如Bagging)来训练多个决策树,并取它们的平均预测值。这种集成方法通常会增加一些偏差,但可以显著降低方差。因此,虽然集成模型的偏差项(青色曲线与深蓝色曲线之间的差异)比单个决策树模型的偏差项要大,但方差项(预测分布的宽度)却更小。这表明,通过集成多个模型,能够在一定程度上平衡偏差和方差,从而降低整体的预测误差。

在实际应用中,可以通过比较不同模型的偏差-方差分解来选择最适合特定问题的模型。例如,如果一个模型的方差过大,可能需要考虑使用集成方法来降低方差。相反,如果模型的偏差过大,可能需要寻找更复杂的模型或调整模型的参数。

为了进一步理解偏差-方差分解,可以参考《统计学习元素》一书的第7.3节。这本书详细介绍了偏差-方差分解的概念,并提供了许多实际应用的例子。通过学习这些内容,可以更好地理解模型的预测误差,并采取相应的措施来优化模型的性能。

代码示例

以下是一个使用Python和scikit-learn库来演示偏差-方差分解的简单代码示例。这个代码生成了一个简单的一维回归问题的数据,并分别训练了单个决策树模型和Bagging集成模型。然后,它计算并绘制了这两个模型的预测误差、偏差和方差的分解。

import matplotlib.pyplot as plt import numpy as np from sklearn.ensemble import BaggingRegressor from sklearn.tree import DecisionTreeRegressor # 设置 n_repeat = 50 # 计算期望值的迭代次数 n_train = 50 # 训练集大小 n_test = 1000 # 测试集大小 noise = 0.1 # 噪声的标准差 np.random.seed(0) # 设置随机种子 # 定义模型 estimators = [ ("Tree", DecisionTreeRegressor()), ("Bagging(Tree)", BaggingRegressor(DecisionTreeRegressor())), ] # 生成数据 def f(x): x = x.ravel() return np.exp(-(x**2)) + 1.5 * np.exp(-((x-2)**2)) def generate(n_samples, noise, n_repeat=1): X = np.random.rand(n_samples) * 10 - 5 X = np.sort(X) if n_repeat == 1: y = f(X) + np.random.normal(0.0, noise, n_samples) else: y = np.zeros((n_samples, n_repeat)) for i in range(n_repeat): y[:, i] = f(X) + np.random.normal(0.0, noise, n_samples) X = X.reshape((n_samples, 1)) return X, y # 训练模型并计算偏差-方差分解 X_train = [] y_train = [] for i in range(n_repeat): X, y = generate(n_samples=n_train, noise=noise) X_train.append(X) y_train.append(y) X_test, y_test = generate(n_samples=n_test, noise=noise, n_repeat=n_repeat) plt.figure(figsize=(10, 8)) for n, (name, estimator) in enumerate(estimators): # 计算预测值 y_predict = np.zeros((n_test, n_repeat)) for i in range(n_repeat): estimator.fit(X_train[i], y_train[i]) y_predict[:, i] = estimator.predict(X_test) # 偏差^2 + 方差 + 噪声分解的均方误差 y_error = np.zeros(n_test) for i in range(n_repeat): for j in range(n_repeat): y_error += (y_test[:, j] - y_predict[:, i])**2 y_error /= n_repeat * n_repeat y_noise = np.var(y_test, axis=1) y_bias = (f(X_test) - np.mean(y_predict, axis=1))**2 y_var = np.var(y_predict, axis=1) print("{0}: {1:.4f} (error) = {2:.4f} (bias^2) + {3:.4f} (var) + {4:.4f} (noise)".format( name, np.mean(y_error), np.mean(y_bias), np.mean(y_var), np.mean(y_noise))) # 绘制图表 plt.subplot(2, len(estimators), n+1) plt.plot(X_test, f(X_test), "b", label="$f(x)$") plt.plot(X_train[0], y_train[0], ".b", label="LS ~ $y = f(x)+noise$") for i in range(n_repeat): if i == 0: plt.plot(X_test, y_predict[:, i], "r", label=r"$\hat{y}(x)$") else: plt.plot(X_test, y_predict[:, i], "r", alpha=0.05) plt.plot(X_test, np.mean(y_predict, axis=1), "c", label=r"$\mathbb{E}_{LS}\hat{y}(x)$") plt.xlim([-5, 5]) plt.title(name) if n == len(estimators) - 1: plt.legend(loc=(1.1, 0.5)) plt.subplot(2, len(estimators), len(estimators) + n + 1) plt.plot(X_test, y_error, "r", label="$error(x)$") plt.plot(X_test, y_bias, "b", label="$bias^2(x)$") plt.plot(X_test, y_var, "g", label="$variance(x)$") plt.plot(X_test, y_noise, "c", label="$noise(x)$") plt.xlim([-5, 5]) plt.ylim([0, 0.1]) if n == len(estimators) - 1: plt.legend(loc=(1.1, 0.5)) plt.subplots_adjust(right=0.75) plt.show()
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485