利用专家知识——一个预测自行车租赁的简单例子

通常来说,领域专家可以帮助找出有用的特征,其信息量比原始数据大得多。自行车数据集给出了2015年8月每天的自行车租赁的数目,每隔3小时统计一次,要求预测给定日期和时间,出租自行车的数目。

1.加载数据

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import mglearn

citibike = mglearn.datasets.load_citibike()

print("Citibike data:\n{}".format(citibike.head()))

利用专家知识——一个预测自行车租赁的简单例子_第1张图片

下面绘出整个月租车数目的可视化图形:

plt.figure(figsize=(10, 3))
xticks = pd.date_range(start=citibike.index.min(), end=citibike.index.max(),
                       freq='D')
plt.xticks(xticks.astype("int"), xticks.strftime("%a %m-%d"), rotation=90, ha="left")
plt.plot(citibike, linewidth=1)
plt.xlabel("Date")
plt.ylabel("Rentals")

 利用专家知识——一个预测自行车租赁的简单例子_第2张图片

在对时间序列的预测任务进行评估时,我们通常希望从过去学习并预测未来,也就是说,在划分训练集和测试集时,我们希望使用某个特定日期之前的所有数据作为训练集,该日期之后的所有数据作为测试集。在这里,我们使用前184个数据点(对应前23天0)作为训练集,剩余的64个数据点(对应于后8天)作为测试集。

在我们的预测任务中,我们使用的唯一特征就是某一天租车数量对应的日期和时间,在计算机上存储日期常用的方式是POSIX时间,它是从1970年1月1日00:00:00起至现在的总秒数。

2.采用POSIX特征训练模型

首先导入数据:

# extract the target values (number of rentals)
y = citibike.values
# convert to POSIX time by dividing by 10**9
X = citibike.index.astype("int64").values.reshape(-1, 1) // 10**9

然后定义一个函数eval_features(features,target,regressor)将数据划分成训练集和测试集,构建模型并将结果可视化。

# use the first 184 data points for training, the rest for testing
n_train = 184

# function to evaluate and plot a regressor on a given feature set
def eval_on_features(features, target, regressor):
    # split the given features into a training and a test set
    X_train, X_test = features[:n_train], features[n_train:]
    # also split the target array 
    y_train, y_test = target[:n_train], target[n_train:]
    regressor.fit(X_train, y_train)
    print("Test-set R^2: {:.2f}".format(regressor.score(X_test, y_test)))
    y_pred = regressor.predict(X_test)
    y_pred_train = regressor.predict(X_train)
    plt.figure(figsize=(10, 3))

    plt.xticks(range(0, len(X), 8), xticks.strftime("%a %m-%d"), rotation=90,
               ha="left")

    plt.plot(range(n_train), y_train, label="train")
    plt.plot(range(n_train, len(y_test) + n_train), y_test, '-', label="test")
    plt.plot(range(n_train), y_pred_train, '--', label="prediction train")

    plt.plot(range(n_train, len(y_test) + n_train), y_pred, '--',
             label="prediction test")
    plt.legend(loc=(1.01, 0))
    plt.xlabel("Date")
    plt.ylabel("Rentals")

接着,以POSIX时间特征,在随机森林上训练模型:

from sklearn.ensemble import RandomForestRegressor
regressor = RandomForestRegressor(n_estimators=100, random_state=0)
eval_on_features(X, y, regressor)

利用专家知识——一个预测自行车租赁的简单例子_第3张图片

在训练集上预测结果相当好,这符合随机森林通常的表现。但对于测试集来说,预测结果是一条常数直线 ,说明什么都没有学到。问题在于特征和随机森林的组合。测试集中POSIX时间特征的值超出了训练集中特征的取值范围:测试集中的数据点的时间戳要晚于训练集中的所有数据点。树以及随机森林无法外推到训练集之外的特征范围。结果就是模型只能预测训练集中最近数据点的目标值,即最后一天观测到数据的时间。

通过观察训练集中租车数量的图像,我们发现两个因素非常重要:一天内的时间与一周的星期几。因此,我们来研究这两个特征。

3.使用每天时刻作为特征

X_hour = citibike.index.hour.values.reshape(-1, 1)
eval_on_features(X_hour, y, regressor)

利用专家知识——一个预测自行车租赁的简单例子_第4张图片

4.添加一周星期几作为特征

X_hour_week = np.hstack([citibike.index.dayofweek.values.reshape(-1, 1),
                         citibike.index.hour.values.reshape(-1, 1)])
eval_on_features(X_hour_week, y, regressor)

 利用专家知识——一个预测自行车租赁的简单例子_第5张图片

此时模型的预测效果很好,模型学到 的内容可能是8月前23天中星期几与时刻每种组合的平均租车数量(交互特征)。这实际上不需要像随机森林那样复杂的模型,我们尝试一下LinearRegression:

5.线性模型使用一周星期几和每天时刻进行预测

from sklearn.linear_model import LinearRegression
eval_on_features(X_hour_week, y, LinearRegression())

利用专家知识——一个预测自行车租赁的简单例子_第6张图片

LinearRegression的效果差得多,而且周期性模式看起来很奇怪。其原因在于我们用整数编码一周星期几和一天内的时刻,它们被解释为线性函数。因此,线性模型只能学到关于每天时间的线性函数。它学到的是,时间越晚,租车数量越多。但实际模式比这复杂得多。我们可以通过将整数解释为分类变量(用OneHotEncoder进行转换)来获取这种模式:

enc = OneHotEncoder()
X_hour_week_onehot = enc.fit_transform(X_hour_week).toarray()

eval_on_features(X_hour_week_onehot, y, Ridge())

 利用专家知识——一个预测自行车租赁的简单例子_第7张图片

它给出了比连续特征编码好得多的匹配。现在现在模型为一周内的每天都学到了一个系数,为一天内的每个时刻都学到了一个系数。

6.线性模型使用星期几和一天内的时刻的交互特征进行预测

poly_transformer = PolynomialFeatures(degree=2, interaction_only=True,
                                      include_bias=False)
X_hour_week_onehot_poly = poly_transformer.fit_transform(X_hour_week_onehot)
lr = Ridge()
eval_on_features(X_hour_week_onehot_poly, y, lr)

 利用专家知识——一个预测自行车租赁的简单例子_第8张图片

这一变换最终得到一个性能与随机森林类似的模型。这一模型的一大优点是,可以很清楚地看到学到的内容:对每个星期几和时刻的交互项学到一个系数。我们可以将模型学到的系数作图,这对于随机森林是做不到的。

首先,为时刻和星期几创建特征名称:

hour = ["%02d:00" % i for i in range(0, 24, 3)]
day = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
features =  day + hour

 然后,利用get_feature_names方法对PolynomialFeatures提取的所有交互特征进行命名,并且保留系数不为零的那些特征:

features_poly = poly_transformer.get_feature_names(features)
features_nonzero = np.array(features_poly)[lr.coef_ != 0]
coef_nonzero = lr.coef_[lr.coef_ != 0]

最后,将系数可视化:

plt.figure(figsize=(15, 2))
plt.plot(coef_nonzero, 'o')
plt.xticks(np.arange(len(coef_nonzero)), features_nonzero, rotation=90)
plt.xlabel("Feature name")
plt.ylabel("Feature magnitude")

利用专家知识——一个预测自行车租赁的简单例子_第9张图片

从上面的例子中我们可以看出,对于模型选取合适的特征数据表示方式,通过特征工程生成新特征和利用专家知识从数据中创建导出特征的运用很重要,特别是对于线性模型,可能会从分箱、添加多项式和交互项而生成新特征中大大受益;对于更复杂的非线性模型(如随机森林、SVM),在无需显式扩展特征空间的前提下就可以学习更复杂的任务。在实践中,所使用的特征(以及特征与方法之间的匹配)通常是机器学习表现良好的重要因素。 

你可能感兴趣的:(机器学习,特征工程,sklearn)