我们利用计算机内存(RAM)历史价格的数据集来更详细地研究这一点。图 2-31 给出了这个数据集的图像,x 轴为日期,y 轴为那一年 1 兆字节(MB)RAM 的价格:
import pandas as pd ram_prices = pd.read_csv("data/ram_price.csv") plt.semilogy(ram_prices.date, ram_prices.price) plt.xlabel(\'Year\') plt.ylabel("Pirce in $/Mbyte")注意 y 轴的对数刻度。在用对数坐标绘图时,二者的线性关系看起来非常好,所以预测应该相对比较容易,除了一些不平滑之处之外。
我们将利用 2000 年前的历史数据来预测 2000 年后的价格,只用日期作为特征。我们将对比两个简单的模型: DecisionTreeRegressor 和 LinearRegression 。我们对价格取对数,使得二者关系的线性相对更好。这对 DecisionTreeRegressor 不会产生什么影响,但对LinearRegression 的影响却很大(我们将在第 4 章中进一步讨论)。训练模型并做出预测之后,我们应用指数映射来做对数变换的逆运算。为了便于可视化,我们这里对整个数据集进行预测,但如果是为了定量评估,我们将只考虑测试数据集:
from sklearn.tree import DecisionTreeRegressor from sklearn.linear_model import LinearRegression # 利用历史数据预测2000年后的价格 data_train = ram_prices[ram_prices.date < 2000] data_test = ram_prices[ram_prices.date >= 2000] # 基于日期来预测价格 X_train = data_train.date[:, np.newaxis] # 我们利用对数变换得到数据和目标之间更简单的关系 y_train = np.log(data_train.price) tree = DecisionTreeRegressor().fit(X_train, y_train) linear_reg = LinearRegression().fit(X_train, y_train) # 对所有数据进行预测 X_all = ram_prices.date[:, np.newaxis] pred_tree = tree.predict(X_all) pred_lr = linear_reg.predict(X_all) # 对数变换逆运算 price_tree = np.exp(pred_tree) price_lr = np.exp(pred_lr)这里创建的图 2-32 将决策树和线性回归模型的预测结果与真实值进行对比:
plt.semilogy(data_train.date, data_train.price, label="Training data") plt.semilogy(data_test.date, data_test.price, label="Test data") plt.semilogy(ram_prices.date, price_tree, label="Tree prediction") plt.semilogy(ram_prices.date, price_lr, label="Linear prediction") plt.legend()两个模型之间的差异非常明显。线性模型用一条直线对数据做近似,这是我们所知道的。这条线对测试数据(2000 年后的价格)给出了相当好的预测,不过忽略了训练数据和测试数据中一些更细微的变化。与之相反,树模型完美预测了训练数据。由于我们没有限制树的复杂度,因此它记住了整个数据集。但是,一旦输入超出了模型训练数据的范围,模型就只能持续预测最后一个已知数据点。树不能在训练数据的范围之外生成“新的”响应。所有基于树的模型都有这个缺点。
5. 优点、缺点和参数如前所述,控制决策树模型复杂度的参数是预剪枝参数,它在树完全展开之前停止树的构造。通常来说,选择一种预剪枝策略(设置 max_depth 、 max_leaf_nodes 或 min_samples_leaf )足以防止过拟合。
与前面讨论过的许多算法相比,决策树有两个优点:一是得到的模型很容易可视化,非专家也很容易理解(至少对于较小的树而言);二是算法完全不受数据缩放的影响。由于每个特征被单独处理,而且数据的划分也不依赖于缩放,因此决策树算法不需要特征预处理,比如归一化或标准化。特别是特征的尺度完全不一样时或者二元特征和连续特征同时存在时,决策树的效果很好。决策树的主要缺点在于,即使做了预剪枝,它也经常会过拟合,泛化性能很差。因此,在大多数应用中,往往使用下面介绍的集成方法来替代单棵决策树。