监督学习实例(多项式回归器、数据预处理、计算准确性和特征相对重要性)

监督学习简介

如果你熟悉机器学习的基础知识,那么肯定知道什么是监督学习。监督学习是指在有标记的样本(labeled samples)上建立机器学习的模型。例如,如果用尺寸、位置等不同参数建立一套模型来评估一栋房子的价格,那么首先需要创建一个数据库,然后为参数打上标记。我们需要告诉算法,什么样的参数(尺寸、位置)对应什么样的价格。有了这些带标记的数据,算法就可以学会如何根据输入的参数计算房价了。

无监督学习与刚才说的恰好相反,它面对的是没有标记的数据。假设需要把一些数据分成不同的组别,但是对分组的条件毫不知情,于是,无监督学习算法就会以最合理的方式将数据集分成确定数量的组别。我们将在后面章节介绍无监督学习。”

数据预处理技术

1
2
3
4
5
6
import numpy as np
from sklearn import preprocessing

data = np.array([[ 3, -1.5, 2, -5.4],
[ 0, 4, -0.3, 2.1],
[ 1, 3.3, -1.9, -4.3]])

均值移除 mean removal

  • “通常我们会把每个特征的平均值移除,以保证特征均值为0(即标准化处理)。这样做可以消除特征彼此间的偏差(bias)”
1
2
3
data_standardized = preprocessing.scale(data)
print ("\nMean特征均值 =", data_standardized.mean(axis=0))
print ("Std deviation标准偏差 =", data_standardized.std(axis=0))
Mean特征均值 = [ 5.55111512e-17 -1.11022302e-16 -7.40148683e-17 -7.40148683e-17]
Std deviation标准偏差 = [1. 1. 1. 1.]

范围缩放 min max scaling

  • “数据点中每个特征的数值范围可能变化很大,因此,有时将特征的数值范围缩放到合理的大小是非常重要的。”
1
2
3
data_scaler = preprocessing.MinMaxScaler(feature_range=(0, 1))
data_scaled = data_scaler.fit_transform(data)
print ("\nMin max scaled data范围缩放数据:\n", data_scaled)
Min max scaled data范围缩放数据:
 [[1.         0.         1.         0.        ]
 [0.         1.         0.41025641 1.        ]
 [0.33333333 0.87272727 0.         0.14666667]]

归一化 normalization

  • “数据归一化用于需要对特征向量的值进行调整时,以保证每个特征向量的值都缩放到相同的数值范围。机器学习中最常用的归一化形式就是将特征向量调整为L1范数,使特征向量的数值之和为1。”
  • “这个方法经常用于确保数据点没有因为特征的基本性质而产生较大差异,即确保数据处于同一数量级,提高不同特征数据的可比性。”
1
2
data_normalized = preprocessing.normalize(data, norm='l1')
print ("\nL1 normalized data归一化后数据:\n", data_normalized)
L1 normalized data归一化后数据:
 [[ 0.25210084 -0.12605042  0.16806723 -0.45378151]
 [ 0.          0.625      -0.046875    0.328125  ]
 [ 0.0952381   0.31428571 -0.18095238 -0.40952381]]

二值化 binarization

  • “二值化用于将数值特征向量转换为布尔类型向量。”
1
2
data_binarized = preprocessing.Binarizer(threshold=1.4).transform(data)
print ("\n二值化 data:\n", data_binarized)
二值化 data:
 [[1. 0. 1. 0.]
 [0. 1. 0. 1.]
 [0. 1. 0. 0.]]

独热编码

  • one hot encoding独热编码
    “通常,需要处理的数值都是稀疏地、散乱地分布在空间中,然而,我们并不需要存储这些大数值,这时就需要使用独热编码(One-Hot Encoding)。可以把独热编码看作是一种收紧 (tighten)特征向量的工具。它把特征向量的每个特征与特征的非重复总数相对应,通过one-of-k 的形式对每个值进行编码。特征向量的每个特征值都按照这种方式编码,这样可以更加有效地表示空间。例如,我们需要处理4维向量空间,当给一个特性向量的第n 个特征进行编码时,编码器会遍历每个特征向量的第n 个特征,然后进行非重复计数。如果非重复计数的值是K ,那么就把这个特征转换为只有一个值是1其他值都是0的K 维向量。”
  • “在下面的示例中,观察一下每个特征向量的第三个特征,分别是1 、5 、2 、4 这4个不重复的值,也就是说独热编码向量的长度是4。如果你需要对5 进行编码,那么向量就是[0, 1, 0, 0] 。向量中只有一个值是1。第二个元素是1,对应的值是5 。”
1
2
3
4
encoder = preprocessing.OneHotEncoder()
encoder.fit([[0, 2, 1, 12], [1, 3, 5, 3], [2, 3, 2, 12], [1, 2, 4, 3]])
encoded_vector = encoder.transform([[2, 3, 5, 3]]).toarray()
print ("\n编码矢量:\n", encoded_vector)
编码矢量:
 [[0. 0. 1. 0. 1. 0. 0. 0. 1. 1. 0.]]

标记编码方法

在监督学习中,经常需要处理各种各样的标记。这些标记可能是数字,也可能是单词。如果标记是数字,那么算法可以直接使用它们,但是,许多情况下,标记都需要以人们可理解的形式存在,因此,人们通常会用单词标记训练数据集。标记编码就是要把单词标记转换成数值形式,让算法懂得如何操作标记。接下来看看如何标记编码。

1
2
3
4
5
6
7
8
9
10
11
from sklearn import preprocessing
# 定义一个标记编码器
label_encoder = preprocessing.LabelEncoder()

# label_encoder对象知道如何理解单词标记,接下来创建标记
input_classes = ['audi', 'ford', 'audi', 'toyota', 'ford', 'bmw']
# 开始标记
label_encoder.fit(input_classes)
print("Classes mapping: 结果显示单词背转换成从0开始的索引值")
for i, item in enumerate(lable_encoder.classes_):
print(item, '-->', i)
Classes mapping: 结果显示单词背转换成从0开始的索引值
audi --> 0
bmw --> 1
ford --> 2
toyota --> 3

这时,如果遇到一组数据就可以轻松的转换它们了。(如药品数据的药品名)

1
2
3
4
labels = ['toyota', 'ford', 'audi']
encoded_labels = label_encoder.transform(labels)
print ("\nLabels =", labels)
print ("Encoded labels =", list(encoded_labels))
Labels = ['toyota', 'ford', 'audi']
Encoded labels = [3, 2, 0]

还可以数字反转回单词(或字符串):

1
2
3
4
encoded_labels = [2,1,0,3,1]
decoded_labels = label_encoder.inverse_transform(encoded_labels)
print(encoded_labels)
print(list(decoded_labels))
[2, 1, 0, 3, 1]
['ford', 'bmw', 'audi', 'toyota', 'bmw']


/Users/hadoop/anaconda3/lib/python3.6/site-packages/sklearn/preprocessing/label.py:151: DeprecationWarning: The truth value of an empty array is ambiguous. Returning False, but in future this will result in an error. Use `array.size > 0` to check that an array is not empty.
  if diff:

创建线性回归

回归是估计输入数据与连续值输出数据之间关系的过程。数据通常是实数形式的,我们的目标是估计满足输入到输出映射关系的基本函数。

线性回归的目标是提取输入变量与输出变量的关联线性模型,这就要求实际输出与线性方程预测的输出的残差平方和(sum of squares of differences)最小化。这种方法被称为普通最小二乘法 (Ordinary Least Squares,OLS)。

你可能觉得用一条曲线对这些点进行拟合效果会更好,但是线性回归不允许这样做。线性回归的主要优点就是方程简单。如果你想用非线性回归,可能会得到更准确的模型,但是拟合速度会慢很多。线性回归模型就像前面那张图里显示的,用一条直线近似数据点的趋势

1
2
3
4
5
6
7
8
9
10
11
12
import sys
import numpy as np
# 加载数据
filename = sys.argv[1]
X = []
y = []
with open('data_singlevar.txt', 'r') as f:
for line in f.readlines():
data = [float(i) for i in line.split(',')]
xt, yt = data[:-1], data[-1]
X.append(xt)
y.append(yt)
1
2
3
4
5
6
7
8
# 80%训练集和20%测试集
num_train = int(0.8 * len(X))
num_test = len(X) - num_train
X_train = np.array(X[:num_train]).reshape(num_train,1)
y_train = np.array(y[:num_train])

X_test = np.array(X[num_train:]).reshape(num_test, 1)
y_test = np.array(y[num_train:])
1
2
3
from sklearn import linear_model
linear_regr = linear_model.LinearRegression()
linear_regr.fit(X_train, y_train)
/Users/hadoop/anaconda3/lib/python3.6/site-packages/scipy/linalg/basic.py:1226: RuntimeWarning: internal gelsd driver lwork query error, required iwork dimension not returned. This is likely the result of LAPACK bug 0038, fixed in LAPACK 3.2.2 (released July 21, 2010). Falling back to 'gelss' driver.
  warnings.warn(mesg, RuntimeWarning)





LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)

我们利用训练数据集训练了线性回归器。向fit 方法提供输入数据即可训练模型。用下面的代码看看它如何拟合

1
2
3
4
5
6
7
8
import matplotlib.pyplot as plt
print('训练集拟合效果')
y_train_pred = linear_regr.predict(X_train)
plt.figure()
plt.scatter(X_train, y_train)
plt.plot(X_train, y_train_pred, color='green', linewidth=2)
plt.title('Training Data')
plt.show()

Alt text

1
2
3
4
5
6
y_test_pred = linear_regr.predict(X_test)
print("测试集拟合效果")
plt.scatter(X_test, y_test)
plt.plot(X_test, y_test_pred, color='green')
plt.title("Test Data")
plt.show()

Alt text

计算回归准确性

现在已经建立了回归器,接下来最重要的就是如何评价回归器的拟合效果。在模型评价的相关内容中,用误差 (error)表示实际值与模型预测值之间的差值。

下面快速了解几个衡量回归器拟合效果的重要指标(metric)。回归器可以用许多不同的指标进行衡量,部分指标如下所示。

  • 平均绝对误差(mean absolute error) :这是给定数据集的所有数据点的绝对误差平均值。

  • 均方误差(mean squared error) :这是给定数据集的所有数据点的误差的平方的平均值。这是最流行的指标之一。

  • 中位数绝对误差(median absolute error) :这是给定数据集的所有数据点的误差的中位数。这个指标的主要优点是可以消除异常值(outlier)的干扰。测试数据集中的单个坏点不会影响整个误差指标,均值误差指标会受到异常点的影响。

  • 解释方差分(explained variance score) :这个分数用于衡量我们的模型对数据集波动的解释能力。如果得分1.0分,那么表明我们的模型是完美的。

  • R方得分(R2 score) :这个指标读作“R方”,是指确定性相关系数,用于衡量模型对未知样本预测的效果。最好的得分是1.0,值也可以是负数。

“每个指标都描述得面面俱到是非常乏味的,因此只选择一两个指标来评估我们的模型。通常的做法是尽量保证均方误差最低,而且解释方差分最高”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import sklearn.metrics as sm

print("平均绝对误差(mean absolute error) :"
, round(sm.mean_absolute_error(y_test, y_test_pred), 2))

print("均方误差(mean squared error) :"
, round(sm.mean_squared_error(y_test, y_test_pred), 2))

print("中位数绝对误差(median absolute error) :"
, round(sm.median_absolute_error(y_test, y_test_pred), 2))

print("解释方差分(explained variance score) :"
, round(sm.explained_variance_score(y_test, y_test_pred), 2))

print("R方得分(R2 score) :"
, round(sm.r2_score(y_test, y_test_pred)))
平均绝对误差(mean absolute error) : 0.54
均方误差(mean squared error) : 0.38
中位数绝对误差(median absolute error) : 0.54
解释方差分(explained variance score) : 0.68
R方得分(R2 score) : 1.0

保存模型数据

1
2
3
4
5
import pickle

regr = pickle.dumps(linear_regr) # 保存
regr1 = pickle.loads(regr) # 加载
regr1.predict(X_test)
array([2.20369892, 4.45873314, 2.12918475, 3.1253216 , 3.21944477,
       3.75673118, 3.91360313, 2.66647116, 3.32925513, 2.77235973])

在scikit的具体情况下,使用 joblib 替换 pickle( joblib.dump & joblib.load )可能会更有趣,这对大数据更有效,但只能序列化 (pickle) 到磁盘而不是字符串变量:

之后,您可以加载已保存的模型(可能在另一个 Python 进程中):

1
2
3
4
from sklearn.externals import joblib
joblib.dump(linear_regr, 'regr.pkl')
regr2 = joblib.load('regr.pkl')
regr2.predict(X_test)
array([2.20369892, 4.45873314, 2.12918475, 3.1253216 , 3.21944477,
       3.75673118, 3.91360313, 2.66647116, 3.32925513, 2.77235973])

创建岭回归

线性回归的主要问题是对异常值敏感。在真实世界的数据收集过程中,经常会遇到错误的度量结果。而线性回归使用的普通最小二乘法,其目标是使平方误差最小化。这时,由于异常值误差的绝对值很大,因此会引起问题,从而破坏整个模型。

普通最小二乘法在建模时会考虑每个数据点的影响,因此,最终模型就会瘦异常值影响较大。显然,我们发现这个模型不是最优的。为了避免这个问题,我们引入正则化项 的系数作为阈值来消除异常值的影响。这个方法被称为岭回归 。

1
import pandas as pd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
X = []
y = []
with open('data_multivar.txt', 'r') as f:
for line in f.readlines():
data = [float(i) for i in line.split(',')]
xt, yt = data[:-1], data[-1]
X.append(xt)
y.append(yt)
# 80%训练集和20%测试集
num_train = int(0.8 * len(X))
num_test = len(X) - num_train
X_train = np.array(X[:num_train]).reshape(num_train,3)
y_train = np.array(y[:num_train])

X_test = np.array(X[num_train:]).reshape(num_test, 3)
y_test = np.array(y[num_train:])

alpha 参数控制回归器的复杂程度。当alpha 趋于0 时,岭回归器就是用普通最小二乘法的线性回归器。因此,如果你希望模型对异常值不那么敏感,就需要设置一个较大的alpha 值。这里把alpha 值设置为0.01 。

1
2
3
4
rid = linear_model.Ridge(alpha=0.01, fit_intercept=True, max_iter=10000)

rid.fit(X_train, y_train)
y_test_pred = rid.predict(X_test)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import sklearn.metrics as sm

print("平均绝对误差(mean absolute error) :"
, round(sm.mean_absolute_error(y_test, y_test_pred), 2))

print("均方误差(mean squared error) :"
, round(sm.mean_squared_error(y_test, y_test_pred), 2))

print("中位数绝对误差(median absolute error) :"
, round(sm.median_absolute_error(y_test, y_test_pred), 2))

print("解释方差分(explained variance score) :"
, round(sm.explained_variance_score(y_test, y_test_pred), 2))

print("R方得分(R2 score) :"
, round(sm.r2_score(y_test, y_test_pred)))
平均绝对误差(mean absolute error) : 3.95
均方误差(mean squared error) : 23.15
中位数绝对误差(median absolute error) : 3.69
解释方差分(explained variance score) : 0.84
R方得分(R2 score) : 1.0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pyecharts import Line
line = Line("期望值测试对比")
line.add('测试目标值', np.linspace(-20,40,len(y_test)), y_test, mark_line=["average"], is_datazoom_show=True)
line.add('实际测试值', np.linspace(-20,40,len(y_test)), y_test_pred, mark_line=["average"], is_datazoom_show=True)
line

# 80%训练集和20%测试集
num_train = int(0.8 * len(X))
num_test = len(X) - num_train
X_train = np.array(X[:num_train]).reshape(num_train,1)
y_train = np.array(y[:num_train])

X_test = np.array(X[num_train:]).reshape(num_test, 1)
y_test = np.array(y[num_train:])

Alt text

创建多项式回归器(重点)

数据点本身的模式中带有自然的曲线,而线性模型是不能捕捉到这一点的。多项式回归模型的曲率是由多项式的次数决定的。随着模型曲率的增加,模型变得更准确。但是,增加曲率的同时也增加了模型的复杂性,因此拟合速度会变慢。当我们对模型的准确性的理想追求与计算能力限制的残酷现实发生冲突时,就需要综合考虑了。

下面使用岭回归的数据,注意和简单线性回归的区别。 这里使用的观察数据是:

1
2
X_train[0] : array([0.39, 2.78, 7.11])
y_train[0] : -8.07

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from sklearn.preprocessing import PolynomialFeatures

#将曲线的多项式次数初始值设置为3
poly = PolynomialFeatures(degree = 20)
# “其中,X_train_transformed 表示多项式形式的输入,与线性回归模型是一样的。”
X_train_transformed = poly.fit_transform(X_train)

#测试一下
dp = X_train[0].reshape(1,-1)
poly_dp = poly.fit_transform(dp)

poly_liner = linear_model.LinearRegression()
poly_liner.fit(X_train_transformed, y_train) #这里注意输入转换后的X_train

print ("\nLinear regression:", rid.predict(dp)[0])
print ("\nPolynomial regression:", poly_liner.predict(poly_dp)[0]) ##这输入转换后的X_test
Linear regression: -11.058646635286552

Polynomial regression: -8.070076359128953
  • 多项式次数为1时 返回预测结果为:-11.058729498335897,欠拟合
  • 多项式次数为10时 返回预测结果为:-8.206005341193759,这里与真实值-8.07已经非常接近了
  • 多项式次数为20时 返回预测结果为:-8.070076359128953,针对这个值的预测最完美
  • 多项式次数为100时 返回预测结果为:10.01397529328105,说明出现过拟合

daBoost算法估算房屋价格

利用AdaBoost算法的决策树回归器(decision tree regreessor)来估算房屋价格

决策树是一个树状模型,每个节点都做出一个决策,从而影响最终结果。叶子节点表示输出数值,分支表示根据输入特征做出的中间决策。AdaBoost算法是指自适应增强(adaptive boosting)算法,这是一种利用其他系统增强模型准确性的技术。这种技术是将不同版本的算法结果进行组合,用加权汇总的方式获得最终结果,被称为弱学习器 (weak learners)。AdaBoost算法在每个阶段获取的信息都会反馈到模型中,这样学习器就可以在后一阶段重点训练难以分类的样本。这种学习方式可以增强系统的准确性。

首先使用AdaBoost算法对数据集进行回归拟合,再计算误差,然后根据误差评估结果,用同样的数据集重新拟合。可以把这些看作是回归器的调优过程,直到达到预期的准确性。假设你拥有一个包含影响房价的各种参数的数据集,我们的目标就是估计这些参数与房价的关系,这样就可以根据未知参数估计房价了。

1
2
3
4
5
6
7
8
9
import numpy as np
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import AdaBoostRegressor
from sklearn import datasets
from sklearn.metrics import mean_squared_error, explained_variance_score
from sklearn.utils import shuffle
import matplotlib.pyplot as plt

hous_data = datasets.load_boston()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 利用shuffle函数把数据的顺序打乱(参数random_state用来控制如何打乱数据)
X, y = shuffle(hous_data.data, hous_data.target, random_state=7)

num = int(0.8 * len(X))
X_train, y_train = X[:num], y[:num]
X_test, y_test = X[num:], y[num:]

# 选择最大深度为5的决策树回归模型
dtre = DecisionTreeRegressor(max_depth=5)
dtre.fit(X_train, y_train)

# 再用带AdaBoost算法的决策树回归模型进行拟合与上面进行比较
abre = AdaBoostRegressor(DecisionTreeRegressor(max_depth=5), n_estimators=400, random_state=7)
abre.fit(X_train, y_train)

# 看看AdaBoost算法对决策树回归器的训练效果有多大改善
y_pred_dt = dtre.predict(X_test)
mse = mean_squared_error(y_test, y_pred_dt)
evs = explained_variance_score(y_test, y_pred_dt)
print("决策树-均方误差: ", mse)
print("决策树-解释方差: ", evs)

y_pred_ab = abre.predict(X_test)
mse = mean_squared_error(y_test, y_pred_ab)
evs = explained_variance_score(y_test, y_pred_ab)
print("\nAbaBoost决策树-均方误差: ", mse)
print("AbaBoost决策树-解释方差: ", evs)
决策树-均方误差:  12.74782456548819
决策树-解释方差:  0.8454595720920495

AbaBoost决策树-均方误差:  7.015648111222207
AbaBoost决策树-解释方差:  0.9147414844474588

计算特征的相对重要性 (如交通案例计算各出口贡献率)

(modle.feature__importances)

在这个案例中,我们用了13个特征,它们对模型都有贡献。但是,有一个重要的问题出现了:如何判断哪个特征更加重要?显然,所有的特征对结果的贡献是不一样的。如果需要忽略一些特征,就需要知道哪些特征不太重要。scikit-learn里面有这样的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def plot_feature_importances(feature_importances, title, feature_names):
# 将重要性值标准化
feature_importances = 100.0 * (feature_importances / max(feature_importances))

# 将得分从高到低排序
index_sorted = np.flipud(np.argsort(feature_importances))

# 让X坐标轴上的标签居中显示
pos = np.arange(index_sorted.shape[0]) + 0.5

# 画条形图
plt.figure()
plt.bar(pos, feature_importances[index_sorted], align='center')
plt.xticks(pos, feature_names[index_sorted])
plt.ylabel('Relative Importance')
plt.title(title)
plt.show()

# 画出特征的相对重要性
plot_feature_importances(dtre.feature_importances_,
'Decision Tree regressor', hous_data.feature_names)
plot_feature_importances(abre.feature_importances_,
'AdaBoost regressor', hous_data.feature_names)

Alt text

Alt text

上图可以看出不带AbaBoost的决策树回归器显示最重要的特征是RM,而带AbaBoost算法的决策回归器现实的最主要特征是LASTAT。现实生活中如果对这个数据集建立不同的回归器会发现最重要的特征就是LSTAT,这足以体现AbaBoost算法对决策树训练效果的改善。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pyecharts import Pie

attr = f_name
v1 = rf_regr.feature_importances_
pie = Pie("影响房价的因素分析")
pie.add("决策树回归器", hous_data.feature_names, dtre.feature_importances_, is_label_show=True, label_emphasis_textcolor='red',
label_emphasis_textsize=14, is_random=True,
legend_orient='vertical', legend_pos='1', legend_top='40',
center=[35, 50],radius=[0, 50])

pie.add("AbaBoost决策树", hous_data.feature_names, abre.feature_importances_, is_label_show=True, label_emphasis_textcolor='red',
label_emphasis_textsize=14, is_random=True,
legend_orient='vertical', legend_pos='1', legend_top='40',
center=[75, 50],radius=[0, 50])
pie

Alt text

随机森林评估共享单车的需求分布

采用随机森林回归器(random forest regressor)估计输出结果。

随机森林死一个决策树合集,它基本上就是用一组由数据集的若干子集构建的决策树构成,再用决策树平均值改善整体学习效果

我们将使用bike_day.csv文件中的数据集,它可以在 https://archive.ics.uci.edu/ml/datasets/Bike+Sharing+Dataset 获取。这份数据集一共16列,前两列是序列号与日期,分析的时候可以不用;最后三列数据是不同类型的输出结果;最后一列是第十四列与第十五列的和,因此建立模型时可以不考虑第十四列与第十五列。

参数n_estimators是指评估器(estimator)的数量,表示随机森林需要使用的决策树数量;
参数max_depth 是指每个决策树的最大深度;参数min_samples_split是指决策树分裂一个节点需要用到的最小数据样本量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
from housing import plot_feature_importances #这个方法源码参考上例

data = pd.read_csv('bike_day.csv',sep=',')

X = data[data.columns[2:13]]
y = data[data.columns[-1]]
f_name = X.columns

X, y = shuffle(X, y, random_state=7)

num = int(0.9 * len(X))
X_train, y_train = X[:num], y[:num]
X_test, y_test = X[num:], y[num:]

rf_regr = RandomForestRegressor(n_estimators=1000, max_depth=15, min_samples_split=12)
rf_regr.fit(X_train, y_train)

y_pred = rf_regr.predict(X_test)

mse = mean_squared_error(y_test, y_pred)
evs = explained_variance_score(y_test, y_pred)

print("随机森林回归器效果:")
print("均方误差:", round(mse, 2))
print("解释方差分:", round(evs, 2))
随机森林回归器效果:
均方误差: 368026.24
解释方差分: 0.89
1
2
3
4
5
6
7
8
9
from pyecharts import Pie

attr = f_name
v1 = rf_regr.feature_importances_
pie = Pie("共享单车因素分析")
pie.add("因素", attr, v1, is_label_show=True, label_emphasis_textcolor='red',
label_emphasis_textsize=14, is_random=True,
legend_orient='vertical', legend_pos='1', legend_top='40')
pie

Alt text

利用按小时的数据计算相关性

这里要用到3~14列

1
len(X_train)
15641
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
hour_data = pd.read_csv('bike_hour.csv', sep=',')
X = hour_data[hour_data.columns[2:14]]
y = hour_data[hour_data.columns[-1]]
X, y = shuffle(X, y, random_state=7)

num = int(0.9*len(X))
X_train, y_train = X[:num], y[:num]
X_test, y_test = X[num:], y[num:]
f_names = X.columns

hrf_regr = RandomForestRegressor(n_estimators=1000, max_depth=15, min_samples_split=10)
hrf_regr.fit(X_train, y_train)
y_pred = hrf_regr.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
evs = explained_variance_score(y_test, y_pred)

print("均方误差:", mse)
print("解释方差分:", evs)
均方误差: 1884.1767363623571
解释方差分: 0.9414038595964176
1
2
3
4
5
6
7
attr = f_names
v1 = hrf_regr.feature_importances_
pie = Pie("共享单车因素分析")
pie.add("因素", attr, v1, is_label_show=True, label_emphasis_textcolor='red',
label_emphasis_textsize=14, is_random=True,
legend_orient='vertical', legend_pos='1', legend_top='40')
pie

Alt text

由图可见,其中最重要的特征是一天中的不同时间点(hr),其次重要的是温度,这完全符合人们的直觉。