这是DC竞赛网的训练赛中的回归问题。
详情前往:北京PM2.5浓度回归分析训练赛
任务:
由给定一段时间内的北京天气相关指数数据和北京PM2.5指数等,建立模型预测接下来一段时间内北京的PM2.5指数。
数据:
数据主要包括2010年1月1日至2014年12月31日间北京pm2.5指数以及相关天气指数数据。 数据分为训练数据和测试数据,分别保存在pm25_train.csv和pm25_test.csv两个文件中。
其中训练数据主要包括35746条记录,13个字段,主要字段说明如下:
date:观测数据发生的日期(年-月-日)
hour:观测数据发生的时间点(时)
pm2.5:观测时间点对应的pm2.5指数((ug/m^3)
DEWP:露点,空气中水气含量达到饱和的气温(℃)
TEMP:温度,观测时间点对应的温度(℃)
PRES:压强,观测时间点对应的压强(hPa)
Iws:累积风速,观测时间点对应的累积风速(m/s)
Is:累计降雪,到观测时间点为止累计降雪的时长(小时)
Ir:累计降雨,到观测时间点为止累计降雨的时长(小时)
cbwd_NE:观测时间点对应的风向为东北风(m/s)
cbwd_NW:观测时间点对应的风向为西北风(m/s)
cbwd_SE:观测时间点对应的风向为东南风(m/s)
cbwd_cv:观测时间点对应的风向为静风(m/s)
测试数据主要包括6011条记录,12个字段,测试数据的字段信息和训练数据相比,除了不包括pm2.5字段以外其他完全相同。利用训练数据建立回归模型,并用于预测测试数据相应的pm2.5指数。
评分标准:
算法通过计算平均预测误差来衡量回归模型的优劣。平均预测误差越小, 说明回归模型越好。平均预测误差计算公式如下:
mse是平均预测误差, m是测试数据的记录数(即6011), ypred是参赛者提交的PM2.5预测指数, y是对应的真实PM2.5指数。
一、读取导入数据
1.1 导入数据
import os
import pandas as pd
def assert_msg(condition, msg):
if not condition:
raise Exception(msg)
def read_data(filename):
# 获取数据路径
file_path = os.path.join(os.path.dirname(__file__), filename)
# 判定文件是否存在
assert_msg(file_path, '文件不存在')
# 返回CSV文件
return pd.read_csv(file_path,
parse_dates=[0], # 2014-12-31 转换成日期值 2014-12-31
infer_datetime_format=True,
)
1.2 读取数据
test = read_data('pm25_test.csv')
train = read_data('pm25_train.csv')
二、数据预处理
2.1 取出训练集中的目标值PM2.5数列,然后将训练集与测试集合并成一个数据集。
# 目标PM2.5值
temp_target = pd.DataFrame()
temp_target['pm2.5'] = train.pop('pm2.5')
# 合并训练集 测试集
data_all = pd.concat([train, test])
data_all.reset_index(inplace=True)
2.2 创建新特征
2.2.1 时间数据,# 年份 季度 月份 周数 每日
data_all['year'] = data_all['date'].apply(lambda x: x.year)
data_all['quarter'] = data_all['date'].apply(lambda x: x.quarter)
data_all['month'] = data_all['date'].apply(lambda x: x.month)
data_all['week'] = data_all['date'].apply(lambda x: x.week)
data_all['day'] = data_all['date'].apply(lambda x: x.day)
2.2.2 是否下雨或下雪
data_all['is_snow'] = np.zeros((data_all.shape[0], 1))
data_all['is_rain'] = np.zeros((data_all.shape[0], 1))
for i in range(data_all.shape[0]):
if data_all['Is'][i] > 0:
data_all['is_snow'][i] = 1
if data_all['Ir'][i] > 0:
data_all['is_rain'][i] = 1
2.3 离散化 和 二元化数据
feats_dummy = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'Is', 'Ir', 'is_snow', 'is_rain']
temp_all = pd.get_dummies(data_all, columns=feats_dummy)
2.4 特征选择
temp_all.drop(['date', 'index'], axis=1, inplace=True)
2.5 训练集 测试集划分
temp_train = temp_all[temp_all.index < 35746]
temp_test = temp_all[temp_all.index >= 35746]
temp_train['pm2.5'] = temp_target['pm2.5']
三、创建模型
3.1 导入所需库 和 数据集
import numpy as np
from sklearn.model_selection import KFold
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import AdaBoostRegressor
from sklearn.preprocessing import StandardScaler
import warnings
warnings.filterwarnings('ignore')
train_ = temp_train
valid_ = temp_test
3.2 评分标准,MSE函数
def mse_func(y_true, y_predict):
assert isinstance(y_true, list), 'y_true must be type of list'
assert isinstance(y_predict, list), 'y_true must be type of list'
m = len(y_true)
squared_error = 0
for i in range(m):
error = y_true[i]/1000 - y_predict[i]/1000
squared_error = squared_error + error ** 2
mse = squared_error / m
return mse
3.3 通过去除均值并缩放到单位方差来标准化特征
def feat_standard(data):
st_scaler = StandardScaler()
st_scaler.fit(data)
data = st_scaler.transform(data)
return data
3.4
folds ——K折交叉验证器,n_splits分为5折,shuffle拆分成批次之前是否对数据进行混洗
pred —— 用于预测的特征子集列名表,除去pm2.5 特征
sub_preds—— 创建test测试集目标值矩阵
res_e —— 模型评估score值
print(f'data shape:\ntrain--{train_.shape}\nvalid--{valid_.shape}')
folds = KFold(n_splits=5, shuffle=True, random_state=1024)
pred = [k for k in train_.columns if k not in ['pm2.5']]
sub_preds = np.zeros((valid_.shape[0], folds.n_splits))
print(f'Use {len(pred)} features ...')
res_e = []
3.5 进行5折交叉验证并 预测
对Adaboost调参时,主要要对两部分内容进行调参,第一部分是对我们的Adaboost的框架进行调参, 第二部分是对我们选择的弱分类器进行调参,两者相辅相成。
1)base_estimator:弱回归学习器。默认使用CART回归树DecisionTreeRegressor。
2)loss:有线性‘linear’, 平方‘square’和指数 ‘exponential’三种选择, 默认是线性。
3) n_estimators:弱学习器的最大迭代次数,或者说最大的弱学习器的个数。一般来说n_estimators太小,容易欠拟合,n_estimators太大,又容易过拟合,一般选择一个适中的数值。默认是50。在实际调参的过程中,与参数learning_rate一起考虑。
4) learning_rate: 每个弱学习器的权重缩减系数ν。ν的取值范围为0<ν≤1。对于同样的训练集拟合效果,较小的ν意味着需要更多的弱学习器的迭代次数。通常我们用步长和迭代最大次数一起来决定算法的拟合效果。默认是1。
CART回归树DecisionTreeRegressor。
1) 划分时考虑的最大特征数max_features: 默认是"None",意味着划分时考虑所有的特征数;如果是"log2"意味着划分时最多考虑log2Nlog2N个特征;如果是"sqrt"或者"auto"意味着划分时最多考虑N−−√N个特征。如果是整数,代表考虑的特征绝对数。如果是浮点数,代表考虑特征百分比,即考虑(百分比xN)取整后的特征数。其中N为样本总特征数。一般来说,如果样本特征数不多,比如小于50,我们用默认的"None"就可以了,如果特征数非常多,我们可以灵活使用刚才描述的其他取值来控制划分时考虑的最大特征数,以控制决策树的生成时间。
2) 决策树最大深max_depth: 默认可以不输入,如果不输入的话,决策树在建立子树的时候不会限制子树的深度。一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间。
3) 内部节点再划分所需最小样本数min_samples_split: 这个值限制了子树继续划分的条件,如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分。 默认是2.如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
4) 叶子节点最少样本数min_samples_leaf: 这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。 默认是1,可以输入最少的样本数的整数,或者最少样本数占样本总数的百分比。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
5)叶子节点最小的样本权重和min_weight_fraction_leaf:这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝。 默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。
6) 最大叶子节点数max_leaf_nodes: 通过限制最大叶子节点数,可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制,具体的值可以通过交叉验证得到。
for n_fold, (train_idx, valid_idx) in enumerate(folds.split(train_, train_['pm2.5']), start=1):
print(f'the {n_fold} training start ...')
# 划分评估的 训练集和测试集
train_x, train_y = train_[pred].iloc[train_idx], train_['pm2.5'].iloc[train_idx]
valid_x, valid_y = train_[pred].iloc[valid_idx], train_['pm2.5'].iloc[valid_idx]
print('数据标准化...')
feat_st_cols = ['DEWP', 'TEMP', 'PRES', 'Iws']
train_x[feat_st_cols] = feat_standard(train_x[feat_st_cols])
valid_x[feat_st_cols] = feat_standard(valid_x[feat_st_cols])
dt_stump = DecisionTreeRegressor(max_depth=20,
min_samples_split=15,
min_samples_leaf=20,
max_features=30,
random_state=11,
max_leaf_nodes=300)
reg = AdaBoostRegressor(base_estimator=dt_stump, n_estimators=100)
reg.fit(train_x, train_y)
train_pred = reg.predict(valid_x)
tmp_score = mse_func(list(valid_y), list(train_pred))
res_e.append(tmp_score)
sub_preds[:, n_fold - 1] = reg.predict(valid_[pred])
3.6 求取5折均值为 测试集预测PM2.5值
print('5 folds 均值:', np.mean(res_e))
valid_['pm2.5'] = np.mean(sub_preds, axis=1)
四、输出csv文档,列出测试数据每条记录对应的pm2.5指数
pred_y = valid_['pm2.5']
pred_y.to_csv('./pm25_pred_1010.csv', index=False, header=['pm2.5'])
提交结果: