第9章-随机森林项目实战-温度预测(1/2)
第8章已经解释了随机森林的基本原理。本章将从实践角度出发,使用Python工具包完成温度预测任务,其中涉及多个模块,主要包括随机森林建模、特征选择、效率比较等。 、参数调优等。这个例子太长,分三个介绍。这是第一篇文章。
随机森林建模:温度预测的任务目标是利用一条与天气相关的数据来预测某一天的最高温度。这是一个回归任务。首先观察数据集:
输出结果中表头的含义如下。
年、月、日、周:分别代表具体时间。
temp_2:前天最高气温值。
temp_1:昨天的最高气温值。
平均值:每年这一天的历史平均最高气温值。
实际:为标签值,即当天实际最高气温。
朋友:这个专栏可能只是为了好玩。你的朋友猜到了可能的值,不要管它。
本项目主要完成以下三项任务。
1。使用随机森林算法完成基本建模任务:包括数据预处理、特征展示、建模完成和可视化展示分析。
2。分析数据样本量和特征数量对结果的影响:在保证算法一致的前提下,增加数据样本数量,观察结果的变化。重新思考特征工程并观察引入新特征后结果的趋势。
3。调整随机森林算法的参数,找到最合适的参数:掌握机器学习中两种经典的参数调整方法,为当前模型选择最合适的参数。
9.1.1 特征可视化和预处理
拿到数据后,我通常会看看数据的规模,就会有一个想法:
print('数据维度:', features.shape)#数据维度: (348, 9)
输出结果显示数据共有348条记录,每个样本有9个特征。如果想进一步观察各个指标的统计特征,可以使用.describe()来显示:
输出显示每列的编号。如果有缺失数据,则数量将会减少。由于每列的统计值为348,说明数据集中不存在缺失值,平均值、标准差、最大值、最小值等指标都显示在这里。
对于时间数据,还可以进行格式转换。原因是一些工具包使得在绘图或计算过程中使用标准时间格式更加方便:
1 #处理时间数据
2 导入日期时间3
4 #分别获取年月日
5 年 = 特征['年']6 个月 = 特征['月']7 天 = 特征['天']8
9 #日期时间格式
10 个日期 = [str(int(year)) + '-' + str(int(month)) + '-' + str(int(day)) 表示年、月、日 inzip(years, Months, days)]11 个日期 = [datetime.datetime.strptime(date, '%Y-%m-%d') for dates]
为了更直观地观察数据,最简单有效的方法就是画图来展示。首先导入Matplotlib工具包,然后选择合适的样式(其实样式差别不是很大):
1 #准备画画
2 将matplotlib.pyplot导入为plt3
4 %matplotlib 内联5
6 #指定默认样式
7 plt.style.use(' Fivethirtyeight')
开始布局,需要显示4个指标,分别是最高气温的标签值、前天、昨天、朋友预测的最高气温。既然有4个数字,不妨用2×2的比例,这样会更清晰。只需指定每个图的标题和坐标轴即可:
1 #设置布局
2 Fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2, Figsize = (10,10))3 Fig.autofmt_xdate(rotation = 45)4
5 #标签值
6 ax1.plot(日期, 特征['实际'])7 ax1.set_xlabel(''); ax1.set_ylabel('温度'); ax1.set_title('最高温度')8
9 #昨天
10 ax2.plot(日期, 特征['temp_1'])11 ax2.set_xlabel(''); ax2.set_ylabel('温度'); ax2.set_title('之前的最高温度')12
13 #天天
14 ax3.plot(日期, 特征['temp_2'])15 ax3.set_xlabel('日期'); ax3.set_ylabel('温度'); ax3.set_title('两天前最高温度')16
17 #我的朋友
18 ax4.plot(日期, 特征['朋友'])19 ax4.set_xlabel('日期'); ax4.set_ylabel('温度'); ax4.set_title('好友预估')20
21 plt.tight_layout(pad=2)
上面的代码可以生成图9-1的输出。
图9-1 各项特性指标
从图中可以看出,各项指标看起来都正常(因为是国外的天气数据,统计标准存在一些差异)。接下来考虑数据预处理的问题。原始数据中的星期列并不是一些数字特征,而是代表星期几的字符串。计算机无法理解这些数据,需要进行转换。
图9-2是一种常用的转换方法,称为one-hot编码或one-hot编码。目的是将属性值转换为数值。如果对应特征中有多个可选属性值,则构造几列新特征,对应位置标记为1,其他位置标记为0。
图9-2 特征编码
你既可以使用Sklearn工具包中现成的方法来完成转换,也可以使用Pandas中的函数。综合比较后,我认为在Pandas中使用.get_dummies()函数是最简单的:
1 # 独特的热编码
2 个特征 =pd.get_dummies(features)3 个特征.head(5)
完成数据集中属性值的预处理后,所有属性值都会默认转换为one-hot编码格式,并自动添加后缀,使其看起来更清晰。
其实你也可以按照自己的方式设置编码特征的名称。使用时,如果遇到不熟悉的功能,想查看详细信息,更直接的方法是直接在Notebook中调用help。工具看看它的API文档。下面返回 get_dummies 的详细信息。您还可以查看在线文档:
帮助(pd.get_dummies)
关于函数 get_dummiesinmodule pandas.core.reshape.reshape 的帮助:
get_dummies(data, prefix=None, prefix_sep='_', dummy_na=False, columns=None,稀疏=False, drop_first=False, dtype=None) -> 'DataFrame'将分类变量转换为虚拟/指标变量.
参数----------数据:类似数组、系列或DataFrame
要获取虚拟指标的数据。
前缀:str,str的列表,str的ordict,默认无
附加 DataFrame 列名称的字符串。
传递一个长度等于列数的列表
在 DataFrame 上调用 get_dummies 时。或者,`前缀`
可以是将列名称映射到前缀的字典。
prefix_sep :str,默认'_'如果附加前缀,则使用分隔符/定界符。或者帕萨
列表字典与“前缀”相同。
dummy_na :布尔值,默认 False
添加一列来指示 NaN,如果忽略 False NaN。
列:类似列表,默认无
DataFrame 中要编码的列名称。
如果`columns`为None,则所有具有
的列`object`或`category`数据类型将被转换。
sparse : bool, 默认 False
虚拟编码列是否应由
支持a :class:`SparseArray` (True) 或常规 NumPy 数组 (False)。
drop_first : bool, 默认 False
是否通过删除
从 k 个分类级别中获得 k-1 个虚拟对象第一级。
dtype:dtype,默认np.uint8
新列的数据类型。只允许使用单一 dtype。
..版本已添加::0.23.0
返回-----DataFrame
虚拟编码数据。
另请参阅--------Series.str.get_dummies:将系列转换为虚拟代码。
示例--------
>>> s = pd.Series(list('abca'))>>>pd.get_dummies(s)
a b c
010 01 0 102 0 0 1
3 10 0>>> s1 = ['a', 'b', np.nan]>>>pd.get_dummies(s1)
a b
0101 0 1
20 0>>> pd.get_dummies(s1, dummy_na=True)
a b NaN
010 01 0 102 0 0 1
>>> df = pd.DataFrame({'A': ['a', 'b', 'a'], 'B': ['b', 'a', 'c'],
...'C': [1, 2, 3]})>>> pd.get_dummies(df, prefix=['col1', 'col2'])
C col1_a col1_b col2_a col2_b col2_c
01 1 0 0 101 2 0 1 10 02 3 1 0 0 0 1
>>> pd.get_dummies(pd.Series(list('abcaa')))
a b c
010 01 0 102 0 0 1
3 10 04 10 0>>> pd.get_dummies(pd.Series(list('abcaa')), drop_first=True)
b c
0 0 01 102 0 1
30 040 0>>> pd.get_dummies(pd.Series(list('abc')), dtype=float)
a b c
01.0 0.0 0.0
1 0.0 1.0 0.0
2 0.0 0.0 1.0
查看代码
特征预处理完成后,需要重新组织数据。特征是特征,标签是标签。它们是从原始数据集中提取的:
print('one-hot 编码后的特征形状:', features.shape)#one-hot 编码后的特征形状: (348, 15)
1 #数据&标签
2 importnumpy 作为 np3
4 #标签
5 个标签 = np.array(features['actual'])6
7 #从功能中删除标签
8 个特征= features.drop('实际', 轴 = 1)9
10 #单独保存名称以备将来参考
11 feature_list =列表(features.columns)12
13 #转换为合适的格式
14 个特征 = np.array(features)
在训练模型之前,需要对数据集进行分割:
1 #数据集分割
2 来自 sklearn.model_selection importtrain_test_split3
4 train_features, test_features, train_labels, test_labels = train_test_split(features, labels, test_size = 0.25,5 random_state = 42)6 print('训练集特征:', train_features.shape)7 print('训练集标签:' , train_labels.shape)8 print('测试集特征:', test_features.shape)9 print('测试集标签:', test_labels.shape)
训练集功能:(261, 14)
训练集标签:(261,)
测试集功能:(87, 14)
测试集标签:(87,)
9.1.2 随机森林回归模型
一切准备就绪,开始构建随机森林模型,首先导入工具包,先尝试构建1000棵树模型,其他参数使用默认值,然后进入参数调整任务:
1 #导入算法
2 来自 sklearn.ensemble importRandomForestRegressor3
4 #建模
5 rf = RandomForestRegressor(n_estimators= 1000, random_state=42)6
7 #训练
8 www.sychzs.cn(train_features, train_labels)
RandomForestRegressor(bootstrap=True, ccp_alpha=0.0, criteria='mse',
max_深度=无,max_features='自动',max_leaf_nodes=无,
max_samples=无,min_impurity_decrease=0.0,
min_impurity_split=无,min_samples_leaf=1,
min_samples_split=2,min_weight_fraction_leaf=0.0,
n_estimators=1000,n_jobs=无,oob_score=False,
random_state=42,详细=0,warm_start=False)
由于数据样本量很小,所以可以很快得到结果。这里我们选择首先使用MAPE指标进行评估,即平均绝对百分比误差。
1 #预测结果
2 个预测 =rf.predict(test_features)3
4 #计算错误
5 个错误 = abs(预测 -test_labels)6
7 #平均绝对百分比误差 (MAPE)
8映射= 100 *(错误/测试标签)9
10 print ('MAPE:',np.mean(mape))
MAPE:6.011244187972058
其实回归任务的评估方法有很多种。下面列出了一些很容易实现的。您还可以选择其他指标进行评估。
9.1.3 树模型可视化方法
获得随机森林模型后,现在介绍如何使用工具包直观地展示树模型。首先,您需要安装 Graphviz 工具。配置过程如下。
步骤①:下载并安装。
下载 graphviz-2.38.msi。完成后,双击msi文件,然后一直点击下一步按钮,安装Graphviz软件(注意:一定要记住安装路径,因为路径信息后面会用来配置环境变量。默认安装路径系统的路径是 C:\Program Files (x86)\Graphviz2.38)。
第2步:配置环境变量。
将Graphviz安装目录下的bin文件夹添加到Path环境变量中。
在此示例中:
D:\tools\GraphViz\bin
步骤 ③:验证安装。
进入Windows命令行界面,输入“dot-version”命令,然后按住Enter键。如果显示Graphviz的相关版本信息,则说明安装配置成功,
点版
dot-graphviz版本2.38.0(20140413.2041)
libdir= "D:\tools\GraphViz\bin"激活插件库:gvplugin_dot_layout.dll
使用布局:dot:dot_layout
激活插件库:gvplugin_core.dll
使用渲染:dot:core
使用设备:点:点:核心
插件配置文件:
D:\tools\GraphViz\bin\config6
已成功加载。
渲染:开罗点Fig gd gdiplus地图pic pov ps svg tk vml vrml xdot
布局:circo dot fdp neato nop nop1 nop2 osage patchwork sfdp Twopi
文本布局:文本布局
设备:bmp canon cmap cmpx cmpx_np dot emf emfplus eps Fig gd gd2 gif gv imap imap_np ismap jpe jpeg jpg
元文件 pdf pic plain plain-ext png pov ps ps2 svg svgz tif tiff tk vml vmlz vrml wbmp xdot xdot1.2 xdot1.4loadimage : (lib) bmp eps gd gd2 gif jpe jpeg jpg ps svg png
最后需要安装graphviz、pydot和pydotplus插件,并在命令行输入相关命令。代码如下:
1 pip3 安装 graphviz2 pip3 安装 pydot23 pip3 安装 pydotplus4 pip3 安装 pydot
安装完以上工具包后,就可以绘制决策树模型了:
1 #导入所需的工具包
2 from sklearn.tree importexport_graphviz3 import pydot #pip install pydot
4
5 #获取一棵树
6 树 = rf.estimators_[5]7
8 #导出到点文件
9 export_graphviz(tree, out_file = 'www.sychzs.cn', feature_names = feature_list, rounded = True, precision = 1)10
11 #绘画
12 (图, ) = pydot.graph_from_dot_file('www.sychzs.cn')13
14 #show
15 graph.write_png('tree.png');
执行上述代码后,会在指定目录下生成一个tree.png文件(如果只指定名称,则在代码所在路径下)。这是绘制的一棵树的模型,如图9-8所示。树模型显得有点太大了,不方便观察。您可以使用参数来限制决策树的大小。还记得修剪策略吗?预修剪方案在这里可以派上用场。
1 print('这棵树的深度是:', tree.tree_.max_depth)2 #这棵树的深度是:15
图9-9标识了生成的树模型中各个指标的含义,看起来还是比较容易理解的。非叶子节点包括4个指标:选择的特征和分割点、评估结果、本节点的样本数和节点预测结果(回归中的平均值)。
图9-9 树模型可视化中各个指标的含义
9.1.4 功能重要性
在解释随机森林算法时,我提到使用集成算法很容易得到特征重要性。 sklearn工具包里也有现成的函数,调用起来非常方便:
1 #获取特征重要性
2 个重要性 =list(rf.feature_importances_)3
4 #转换格式
5 feature_importances = [(feature, round(importance, 2)) 对于特征,重要性 inzip(feature_list, importantions)]6
7 #排序
8 feature_importances = 排序(feature_importances,key = lambda x:x [1],reverse = True)9
10 #相应打印
11 [print('变量: {:20} 重要性: {}'.format(*pair)) for feature_importances 中的对]
变量:temp_1 重要性:0.7变量:平均值 重要性:0.19变量:日 重要性:0.03变量:temp_2 重要性:0.02变量:朋友 重要性:0.02变量:月份 重要性:0.01变量:年份 重要性:0.0变量:week_Fri 重要性: 0.0变量:week_Mon 重要性:0.0变量:week_Sat 重要性:0.0变量:week_Sun 重要性:0.0变量:week_Thurs 重要性:0.0变量:week_Tues 重要性:0.0变量:week_Wed 重要性:0.0
以上输出结果分别打印当前特征及其对应的特征重要性。画个图表分析比较容易:
1 #转换为列表格式
2 x_values =list(范围(len(重要性)))3
4 #绘画
5 www.sychzs.cn(x_values,重要性,方向='垂直')6
7 #x轴名称
8 plt.xticks(x_values, feature_list, 旋转='垂直')9
10 #图名
11 plt.ylabel('重要性'); plt.xlabel('变量'); plt.title('变量重要性');
上面的代码可以生成图9-10的输出。可以明显发现temp_1和average这两个特征的重要性占据了总数的绝大多数,而其他特征的重要性似乎微乎其微。那么,如果只用最强大的特征来建模,效果会不会更好呢?其实并不能保证效果会更好,但速度肯定会更快。先来看看结果吧:
图9-10 随机森林特征重要性
1 #选择两个最重要的功能来尝试
2 rf_most_important = RandomForestRegressor(n_estimators= 1000, random_state=42)3
4 #获取这两个特征
5 important_indices = [feature_list.index('temp_1'), feature_list.index('average')]6 train_important =train_features[:, important_indices]7 test_important =test_features[:, important_indices]8
9 #重新训练模型
10 rf_most_www.sychzs.cn(train_important,train_labels)11
12 #预测结果
13 个预测 =rf_most_important.predict(test_important)14
15 个错误 = abs(预测 -test_labels)16
17 #评估结果
18
19 mape = np.mean(100 * (错误/测试标签))20
21 print('mape:', mape)
地图:6.229055723613811
从loss值观察,没有减少,反而增加了,这说明其他特征还是有价值的。不能仅根据特征的重要性来否定部分特征数据。一切都必须通过实验来判断。
但是,在考虑时间效率时,您必须仔细考虑是否应该消除那些对加速模型构建没有多大用处的特征。至此,我们已经获得了基本的随机森林模型,并且可以进行预测了。我们来看看模型的预测值与真实值的差异:
1 #日期数据
2 months = features[:, feature_list.index('month')]3 days = features[:, feature_list.index('day')]4 years = features[:, feature_list.index('year')]5
6 #转换日期格式
7 dates = [str(int(year)) + '-' + str(int(month)) + '-' + str(int(day)) for year, month, day inzip(years, months, days)]8 dates = [datetime.datetime.strptime(date, '%Y-%m-%d') for date indates]9
10 #创建一个表格来存日期和其对应的标签数值
11 true_data = pd.DataFrame(data = {'date': dates, 'actual': labels})12
13 #同理,再创建一个来存日期和其对应的模型预测值
14 months = test_features[:, feature_list.index('month')]15 days = test_features[:, feature_list.index('day')]16 years = test_features[:, feature_list.index('year')]17
18 test_dates = [str(int(year)) + '-' + str(int(month)) + '-' + str(int(day)) for year, month, day inzip(years, months, days)]19
20 test_dates = [datetime.datetime.strptime(date, '%Y-%m-%d') for date intest_dates]21
22 predictions_data = pd.DataFrame(data = {'date': test_dates, 'prediction': predictions})23
24 #真实值
25 plt.plot(true_data['date'], true_data['actual'], 'b-', label = 'actual')26
27 #预测值
28 plt.plot(predictions_data['date'], predictions_data['prediction'], 'ro', label = 'prediction')29 plt.xticks(rotation = '60');30 plt.legend()31
32 #图名
33 plt.xlabel('Date'); plt.ylabel('Maximum Temperature (F)'); plt.title('Actual and Predicted Values');
通过上述输出结果的走势可以看出,模型已经基本能够掌握天气变化情况,接下来还需要深入数据,考虑以下几个问题。
1.如果可利用的数据量增大,会对结果产生什么影响呢?
2.加入新的特征会改进模型效果吗?此时的时间效率又会怎样?
未完待续。