关灯

Sklearn 与 TensorFlow 机器学习实用指南第2章 一个完整的机器学习项目(下)

[复制链接]
admin 发表于 2019-1-20 13:06:50 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
 

自定义转换器

尽管 Scikit-Learn 提供了许多有用的转换器,你还是需要自己动手写转换器执行任务,比如自定义的清理操作,或属性组合。你需要让自制的转换器与 Scikit-Learn 组件(比如流水线)无缝衔接工作,因为 Scikit-Learn 是依赖鸭子类型的(而不是继承),你所需要做的是创建一个类并执行三个方法:

  1. fit()
复制代码
(返回
  1. self
复制代码
),
  1. transform()
复制代码
,和
  1. fit_transform()
复制代码
。通过添加
  1. TransformerMixin
复制代码
作为基类,可以很容易地得到最后一个。另外,如果你添加
  1. BaseEstimator
复制代码
作为基类(且构造器中避免使用
  1. *args
复制代码
  1. **kargs
复制代码
),你就能得到两个额外的方法(
  1. get_params()
复制代码
  1. set_params()
复制代码
),二者可以方便地进行超参数自动微调。例如,一个小转换器类添加了上面讨论的属性:

  1. from sklearn.base import BaseEstimator, TransformerMixin
  2. rooms_ix, bedrooms_ix, population_ix, household_ix = 3, 4, 5, 6
  3. class CombinedAttributesAdder(BaseEstimator, TransformerMixin):
  4.    def __init__(self, add_bedrooms_per_room = True): # no *args or **kargs
  5.        self.add_bedrooms_per_room = add_bedrooms_per_room
  6.    def fit(self, X, y=None):
  7.        return self  # nothing else to do
  8.    def transform(self, X, y=None):
  9.        rooms_per_household = X[:, rooms_ix] / X[:, household_ix]
  10.        population_per_household = X[:, population_ix] / X[:, household_ix]
  11.        if self.add_bedrooms_per_room:
  12.            bedrooms_per_room = X[:, bedrooms_ix] / X[:, rooms_ix]
  13.            return np.c_[X, rooms_per_household, population_per_household,
  14.                         bedrooms_per_room]
  15.        else:
  16.            return np.c_[X, rooms_per_household, population_per_household]
  17. attr_adder = CombinedAttributesAdder(add_bedrooms_per_room=False)
  18. housing_extra_attribs = attr_adder.transform(housing.values)
复制代码

 

在这个例子中,转换器有一个超参数

  1. add_bedrooms_per_room
复制代码
,默认设为
  1. True
复制代码
(提供一个合理的默认值很有帮助)。这个超参数可以让你方便地发现添加了这个属性是否对机器学习算法有帮助。更一般地,你可以为每个不能完全确保的数据准备步骤添加一个超参数。数据准备步骤越自动化,可以自动化的操作组合就越多,越容易发现更好用的组合(并能节省大量时间)。

 

特征缩放

数据要做的最重要的转换之一是特征缩放。除了个别情况,当输入的数值属性量度不同时,机器学习算法的性能都不会好。这个规律也适用于房产数据:总房间数分布范围是 6 到 39320,而收入中位数只分布在 0 到 15。注意通常情况下我们不需要对目标值进行缩放。

 

有两种常见的方法可以让所有的属性有相同的量度:线性函数归一化(Min-Max scaling)和标准化(standardization)。

 

线性函数归一化(许多人称其为归一化(normalization))很简单:值被转变、重新缩放,直到范围变成 0 到 1。我们通过减去最小值,然后再除以最大值与最小值的差值,来进行归一化。Scikit-Learn 提供了一个转换器

  1. MinMaxScaler
复制代码
来实现这个功能。它有一个超参数
  1. feature_range
复制代码
,可以让你改变范围,如果不希望范围是 0 到 1。

 

标准化就很不同:首先减去平均值(所以标准化值的平均值总是 0),然后除以方差,使得到的分布具有单位方差。与归一化不同,标准化不会限定值到某个特定的范围,这对某些算法可能构成问题(比如,神经网络常需要输入值得范围是 0 到 1)。但是,标准化受到异常值的影响很小。例如,假设一个街区的收入中位数由于某种错误变成了100,归一化会将其它范围是 0 到 15 的值变为 0-0.15,但是标准化不会受什么影响。Scikit-Learn 提供了一个转换器

  1. StandardScaler
复制代码
来进行标准化。

警告:与所有的转换一样,缩放器只能向训练集拟合,而不是向完整的数据集(包括测试集)。只有这样,你才能用缩放器转换训练集和测试集(和新数据)。

 

 

转换流水线

你已经看到,存在许多数据转换步骤,需要按一定的顺序执行。幸运的是,Scikit-Learn 提供了类

  1. Pipeline
复制代码
,来进行这一系列的转换。下面是一个数值属性的小流水线:

  1. from sklearn.pipeline import Pipeline
  2. from sklearn.preprocessing import StandardScaler
  3. num_pipeline = Pipeline([
  4.        ('imputer', Imputer(strategy="median")),
  5.        ('attribs_adder', CombinedAttributesAdder()),
  6.        ('std_scaler', StandardScaler()),
  7.        ])
  8. housing_num_tr = num_pipeline.fit_transform(housing_num)
复制代码

 

  1. Pipeline
复制代码
构造器需要一个定义步骤顺序的名字/估计器对的列表。除了最后一个估计器,其余都要是转换器(即,它们都要有
  1. fit_transform()
复制代码
方法)。名字可以随意起。

 

当你调用流水线的

  1. fit()
复制代码
方法,就会对所有转换器顺序调用
  1. fit_transform()
复制代码
方法,将每次调用的输出作为参数传递给下一个调用,一直到最后一个估计器,它只执行
  1. fit()
复制代码
方法。

 

流水线暴露相同的方法作为最终的估计器。在这个例子中,最后的估计器是一个

  1. StandardScaler
复制代码
,它是一个转换器,因此这个流水线有一个
  1. transform()
复制代码
方法,可以顺序对数据做所有转换(它还有一个
  1. fit_transform
复制代码
方法可以使用,就不必先调用
  1. fit()
复制代码
再进行
  1. transform()
复制代码
)。

 

你现在就有了一个对数值的流水线,你还需要对分类值应用

  1. LabelBinarizer
复制代码
:如何将这些转换写成一个流水线呢?Scikit-Learn 提供了一个类
  1. FeatureUnion
复制代码
实现这个功能。你给它一列转换器(可以是所有的转换器),当调用它的
  1. transform()
复制代码
方法,每个转换器的
  1. transform()
复制代码
会被并行执行,等待输出,然后将输出合并起来,并返回结果(当然,调用它的
  1. fit()
复制代码
方法就会调用每个转换器的
  1. fit()
复制代码
)。一个完整的处理数值和类别属性的流水线如下所示:

  1. from sklearn.pipeline import FeatureUnion
  2. num_attribs = list(housing_num)
  3. cat_attribs = ["ocean_proximity"]
  4. num_pipeline = Pipeline([
  5.        ('selector', DataFrameSelector(num_attribs)),
  6.        ('imputer', Imputer(strategy="median")),
  7.        ('attribs_adder', CombinedAttributesAdder()),
  8.        ('std_scaler', StandardScaler()),
  9.    ])
  10. cat_pipeline = Pipeline([
  11.        ('selector', DataFrameSelector(cat_attribs)),
  12.        ('label_binarizer', LabelBinarizer()),
  13.    ])
  14. full_pipeline = FeatureUnion(transformer_list=[
  15.        ("num_pipeline", num_pipeline),
  16.        ("cat_pipeline", cat_pipeline),
  17.    ])
复制代码

译注:

如果你在上面代码中的

  1. cat_pipeline
复制代码
流水线使用
  1. LabelBinarizer
复制代码
转换器会导致执行错误,解决方案是用上文提到的
  1. CategoricalEncoder
复制代码
转换器来代替:

  1. cat_pipeline = Pipeline([
  2.        ('selector', DataFrameSelector(cat_attribs)),
  3.        ('cat_encoder', CategoricalEncoder(encoding="onehot-dense")),
  4.    ])
复制代码

你可以很简单地运行整个流水线:

 

  1. >>> housing_prepared = full_pipeline.fit_transform(housing)
  2. >>> housing_prepared
  3. array([[ 0.73225807, -0.67331551,  0.58426443, ...,  0.        ,
  4.         0.        ,  0.        ],
  5.       [-0.99102923,  1.63234656, -0.92655887, ...,  0.        ,
  6.         0.        ,  0.        ],
  7.       [...]
  8. >>> housing_prepared.shape
  9. (16513, 17)
复制代码

 

每个子流水线都以一个选择转换器开始:通过选择对应的属性(数值或分类)、丢弃其它的,来转换数据,并将输出

  1. DataFrame
复制代码
转变成一个 NumPy 数组。Scikit-Learn 没有工具来处理 Pandas
  1. DataFrame
复制代码
,因此我们需要写一个简单的自定义转换器来做这项工作:

  1. from sklearn.base import BaseEstimator, TransformerMixin
  2. class DataFrameSelector(BaseEstimator, TransformerMixin):
  3.    def __init__(self, attribute_names):
  4.        self.attribute_names = attribute_names
  5.    def fit(self, X, y=None):
  6.        return self
  7.    def transform(self, X):
  8.        return X[self.attribute_names].values
复制代码

 

 

选择并训练模型

可到这一步了!你在前面限定了问题、获得了数据、探索了数据、采样了一个测试集、写了自动化的转换流水线来清理和为算法准备数据。现在,你已经准备好选择并训练一个机器学习模型了。

 

在训练集上训练和评估

好消息是基于前面的工作,接下来要做的比你想的要简单许多。像前一章那样,我们先来训练一个线性回归模型:

  1. from sklearn.linear_model import LinearRegression
  2. lin_reg = LinearRegression()
  3. lin_reg.fit(housing_prepared, housing_labels)
复制代码

 

完毕!你现在就有了一个可用的线性回归模型。用一些训练集中的实例做下验证:

  1. >>> some_data = housing.iloc[:5]
  2. >>> some_labels = housing_labels.iloc[:5]
  3. >>> some_data_prepared = full_pipeline.transform(some_data)
  4. >>> print("Predictions:\t", lin_reg.predict(some_data_prepared))
  5. Predictions:     [ 303104.   44800.  308928.  294208.  368704.]
  6. >>> print("Labels:\t\t", list(some_labels))
  7. Labels:         [359400.0, 69700.0, 302100.0, 301300.0, 351900.0]
复制代码

 

行的通,尽管预测并不怎么准确(比如,第二个预测偏离了 50%!)。让我们使用 Scikit-Learn 的

  1. mean_squared_error
复制代码
函数,用全部训练集来计算下这个回归模型的 RMSE:

  1. >>> from sklearn.metrics import mean_squared_error
  2. >>> housing_predictions = lin_reg.predict(housing_prepared)
  3. >>> lin_mse = mean_squared_error(housing_labels, housing_predictions)
  4. >>> lin_rmse = np.sqrt(lin_mse)
  5. >>> lin_rmse
  6. 68628.413493824875
复制代码

 

OK,有总比没有强,但显然结果并不好:大多数街区的

  1. median_housing_values
复制代码
位于 $120000 到 $265000 之间,因此预测误差 $68628 不能让人满意。这是一个模型欠拟合训练数据的例子。当这种情况发生时,意味着特征没有提供足够多的信息来做出一个好的预测,或者模型并不强大。就像前一章看到的,修复欠拟合的主要方法是选择一个更强大的模型,给训练算法提供更好的特征,或去掉模型上的限制。这个模型还没有正则化,所以排除了最后一个选项。你可以尝试添加更多特征(比如,人口的对数值),但是首先让我们尝试一个更为复杂的模型,看看效果。

 

来训练一个

  1. DecisionTreeRegressor
复制代码
。这是一个强大的模型,可以发现数据中复杂的非线性关系(决策树会在第 6 章详细讲解)。代码看起来很熟悉:

  1. from sklearn.tree import DecisionTreeRegressor
  2. tree_reg = DecisionTreeRegressor()
  3. tree_reg.fit(housing_prepared, housing_labels)
复制代码

 

现在模型就训练好了,用训练集评估下:

  1. >>> housing_predictions = tree_reg.predict(housing_prepared)
  2. >>> tree_mse = mean_squared_error(housing_labels, housing_predictions)
  3. >>> tree_rmse = np.sqrt(tree_mse)
  4. >>> tree_rmse
  5. 0.0
复制代码

等一下,发生了什么?没有误差?这个模型可能是绝对完美的吗?当然,更大可能性是这个模型严重过拟合数据。如何确定呢?如前所述,直到你准备运行一个具备足够信心的模型,都不要碰测试集,因此你需要使用训练集的部分数据来做训练,用一部分来做模型验证。

 

使用交叉验证做更佳的评估

评估决策树模型的一种方法是用函数

  1. train_test_split
复制代码
来分割训练集,得到一个更小的训练集和一个验证集,然后用更小的训练集来训练模型,用验证集来评估。这需要一定工作量,并不难而且也可行。

 

另一种更好的方法是使用 Scikit-Learn 的交叉验证功能。下面的代码采用了 K 折交叉验证(K-fold cross-validation):它随机地将训练集分成十个不同的子集,成为“折”,然后训练评估决策树模型 10 次,每次选一个不用的折来做评估,用其它 9 个来做训练。结果是一个包含 10 个评分的数组:

  1. from sklearn.model_selection import cross_val_score
  2. scores = cross_val_score(tree_reg, housing_prepared, housing_labels,
  3.                         scoring="neg_mean_squared_error", cv=10)
  4. rmse_scores = np.sqrt(-scores)
复制代码

警告:Scikit-Learn 交叉验证功能期望的是效用函数(越大越好)而不是损失函数(越低越好),因此得分函数实际上与 MSE 相反(即负值),这就是为什么前面的代码在计算平方根之前先计算

  1. -scores
复制代码

 

来看下结果:

  1. >>> def display_scores(scores):
  2. ...     print("Scores:", scores)
  3. ...     print("Mean:", scores.mean())
  4. ...     print("Standard deviation:", scores.std())
  5. ...
  6. >>> display_scores(tree_rmse_scores)
  7. Scores: [ 74678.4916885   64766.2398337   69632.86942005  69166.67693232
  8.          71486.76507766  73321.65695983  71860.04741226  71086.32691692
  9.          76934.2726093   69060.93319262]
  10. Mean: 71199.4280043
  11. Standard deviation: 3202.70522793
复制代码

现在决策树就不像前面看起来那么好了。实际上,它看起来比线性回归模型还糟!注意到交叉验证不仅可以让你得到模型性能的评估,还能测量评估的准确性(即,它的标准差)。决策树的评分大约是 71200,通常波动有 ±3200。如果只有一个验证集,就得不到这些信息。但是交叉验证的代价是训练了模型多次,不可能总是这样。

 

让我们计算下线性回归模型的的相同分数,以做确保:

  1. >>> lin_scores = cross_val_score(lin_reg, housing_prepared, housing_labels,
  2. ...                              scoring="neg_mean_squared_error", cv=10)
  3. ...
  4. >>> lin_rmse_scores = np.sqrt(-lin_scores)
  5. >>> display_scores(lin_rmse_scores)
  6. Scores: [ 70423.5893262   65804.84913139  66620.84314068  72510.11362141
  7.          66414.74423281  71958.89083606  67624.90198297  67825.36117664
  8.          72512.36533141  68028.11688067]
  9. Mean: 68972.377566
  10. Standard deviation: 2493.98819069
复制代码

判断没错:决策树模型过拟合很严重,它的性能比线性回归模型还差。

 

现在再尝试最后一个模型:

  1. RandomForestRegressor
复制代码
。第7章我们会看到,随机森林是通过用特征的随机子集训练许多决策树。在其它多个模型之上建立模型称为集成学习(Ensemble Learning),它是推进 ML 算法的一种好方法。我们会跳过大部分的代码,因为代码本质上和其它模型一样:

  1. >>> from sklearn.ensemble import RandomForestRegressor
  2. >>> forest_reg = RandomForestRegressor()
  3. >>> forest_reg.fit(housing_prepared, housing_labels)
  4. >>> [...]
  5. >>> forest_rmse
  6. 22542.396440343684
  7. >>> display_scores(forest_rmse_scores)
  8. Scores: [ 53789.2879722   50256.19806622  52521.55342602  53237.44937943
  9.          52428.82176158  55854.61222549  52158.02291609  50093.66125649
  10.          53240.80406125  52761.50852822]
  11. Mean: 52634.1919593
  12. Standard deviation: 1576.20472269
复制代码

现在好多了:随机森林看起来很有希望。但是,训练集的评分仍然比验证集的评分低很多。解决过拟合可以通过简化模型,给模型加限制(即,规整化),或用更多的训练数据。在深入随机森林之前,你应该尝试下机器学习算法的其它类型模型(不同核心的支持向量机,神经网络,等等),不要在调节超参数上花费太多时间。目标是列出一个可能模型的列表(两到五个)。

提示:你要保存每个试验过的模型,以便后续可以再用。要确保有超参数和训练参数,以及交叉验证评分,和实际的预测值。这可以让你比较不同类型模型的评分,还可以比较误差种类。你可以用 Python 的模块

  1. pickle
复制代码
,非常方便地保存 Scikit-Learn 模型,或使用
  1. sklearn.externals.joblib
复制代码
,后者序列化大 NumPy 数组更有效率:

  1. from sklearn.externals import joblib
  2. joblib.dump(my_model, "my_model.pkl")
  3. # 然后
  4. my_model_loaded = joblib.load("my_model.pkl")
复制代码

 

 

模型微调

假设你现在有了一个列表,列表里有几个有希望的模型。你现在需要对它们进行微调。让我们来看几种微调的方法。

 

网格搜索

微调的一种方法是手工调整超参数,直到找到一个好的超参数组合。这么做的话会非常冗长,你也可能没有时间探索多种组合。

 

你应该使用 Scikit-Learn 的

  1. GridSearchCV
复制代码
来做这项搜索工作。你所需要做的是告诉
  1. GridSearchCV
复制代码
要试验有哪些超参数,要试验什么值,
  1. GridSearchCV
复制代码
就能用交叉验证试验所有可能超参数值的组合。例如,下面的代码搜索了
  1. RandomForestRegressor
复制代码
超参数值的最佳组合:

  1. from sklearn.model_selection import GridSearchCV
  2. param_grid = [
  3.    {'n_estimators': [3, 10, 30], 'max_features': [2, 4, 6, 8]},
  4.    {'bootstrap': [False], 'n_estimators': [3, 10], 'max_features': [2, 3, 4]},
  5.  ]
  6. forest_reg = RandomForestRegressor()
  7. grid_search = GridSearchCV(forest_reg, param_grid, cv=5,
  8.                           scoring='neg_mean_squared_error')
  9. grid_search.fit(housing_prepared, housing_labels)
复制代码

当你不能确定超参数该有什么值,一个简单的方法是尝试连续的 10 的幂(如果想要一个粒度更小的搜寻,可以用更小的数,就像在这个例子中对超参数

  1. n_estimators
复制代码
做的)。

 

  1. param_grid
复制代码
告诉 Scikit-Learn 首先评估所有的列在第一个
  1. dict
复制代码
中的
  1. n_estimators
复制代码
  1. max_features
复制代码
  1. 3 × 4 = 12
复制代码
种组合(不用担心这些超参数的含义,会在第 7 章中解释)。然后尝试第二个
  1. dict
复制代码
中超参数的
  1. 2 × 3 = 6
复制代码
种组合,这次会将超参数
  1. bootstrap
复制代码
设为
  1. False
复制代码
而不是
  1. True
复制代码
(后者是该超参数的默认值)。

 

总之,网格搜索会探索

  1. 12 + 6 = 18
复制代码
  1. RandomForestRegressor
复制代码
的超参数组合,会训练每个模型五次(因为用的是五折交叉验证)。换句话说,训练总共有
  1. 18 × 5 = 90
复制代码
轮!K 折将要花费大量时间,完成后,你就能获得参数的最佳组合,如下所示:

  1. >>> grid_search.best_params_
  2. {'max_features': 6, 'n_estimators': 30}
复制代码

提示:因为 30 是

  1. n_estimators
复制代码
的最大值,你也应该估计更高的值,因为评估的分数可能会随
  1. n_estimators
复制代码
的增大而持续提升。

你还能直接得到最佳的估计器:

  1. >>> grid_search.best_estimator_
  2. RandomForestRegressor(bootstrap=True, criterion='mse', max_depth=None,
  3.           max_features=6, max_leaf_nodes=None, min_samples_leaf=1,
  4.           min_samples_split=2, min_weight_fraction_leaf=0.0,
  5.           n_estimators=30, n_jobs=1, oob_score=False, random_state=None,
  6.           verbose=0, warm_start=False)
复制代码

注意:如果

  1. GridSearchCV
复制代码
是以(默认值)
  1. refit=True
复制代码
开始运行的,则一旦用交叉验证找到了最佳的估计器,就会在整个训练集上重新训练。这是一个好方法,因为用更多数据训练会提高性能。

 

当然,也可以得到评估得分:

  1. >>> cvres = grid_search.cv_results_
  2. ... for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
  3. ...     print(np.sqrt(-mean_score), params)
  4. ...
  5. 64912.0351358 {'max_features': 2, 'n_estimators': 3}
  6. 55535.2786524 {'max_features': 2, 'n_estimators': 10}
  7. 52940.2696165 {'max_features': 2, 'n_estimators': 30}
  8. 60384.0908354 {'max_features': 4, 'n_estimators': 3}
  9. 52709.9199934 {'max_features': 4, 'n_estimators': 10}
  10. 50503.5985321 {'max_features': 4, 'n_estimators': 30}
  11. 59058.1153485 {'max_features': 6, 'n_estimators': 3}
  12. 52172.0292957 {'max_features': 6, 'n_estimators': 10}
  13. 49958.9555932 {'max_features': 6, 'n_estimators': 30}
  14. 59122.260006 {'max_features': 8, 'n_estimators': 3}
  15. 52441.5896087 {'max_features': 8, 'n_estimators': 10}
  16. 50041.4899416 {'max_features': 8, 'n_estimators': 30}
  17. 62371.1221202 {'bootstrap': False, 'max_features': 2, 'n_estimators': 3}
  18. 54572.2557534 {'bootstrap': False, 'max_features': 2, 'n_estimators': 10}
  19. 59634.0533132 {'bootstrap': False, 'max_features': 3, 'n_estimators': 3}
  20. 52456.0883904 {'bootstrap': False, 'max_features': 3, 'n_estimators': 10}
  21. 58825.665239 {'bootstrap': False, 'max_features': 4, 'n_estimators': 3}
  22. 52012.9945396 {'bootstrap': False, 'max_features': 4, 'n_estimators': 10}
复制代码

 

在这个例子中,我们通过设定超参数

  1. max_features
复制代码
为 6,
  1. n_estimators
复制代码
为 30,得到了最佳方案。对这个组合,RMSE 的值是 49959,这比之前使用默认的超参数的值(52634)要稍微好一些。祝贺你,你成功地微调了最佳模型!

提示:不要忘记,你可以像超参数一样处理数据准备的步骤。例如,网格搜索可以自动判断是否添加一个你不确定的特征(比如,使用转换器

  1. CombinedAttributesAdder
复制代码
的超参数
  1. add_bedrooms_per_room
复制代码
)。它还能用相似的方法来自动找到处理异常值、缺失特征、特征选择等任务的最佳方法。

 

 

随机搜索

当探索相对较少的组合时,就像前面的例子,网格搜索还可以。但是当超参数的搜索空间很大时,最好使用

  1. RandomizedSearchCV
复制代码
。这个类的使用方法和类
  1. GridSearchCV
复制代码
很相似,但它不是尝试所有可能的组合,而是通过选择每个超参数的一个随机值的特定数量的随机组合。这个方法有两个优点:

  • 如果你让随机搜索运行,比如 1000 次,它会探索每个超参数的 1000 个不同的值(而不是像网格搜索那样,只搜索每个超参数的几个值)。

  • 你可以方便地通过设定搜索次数,控制超参数搜索的计算量。

 

 

集成方法

另一种微调系统的方法是将表现最好的模型组合起来。组合(集成)之后的性能通常要比单独的模型要好(就像随机森林要比单独的决策树要好),特别是当单独模型的误差类型不同时。我们会在第7章更深入地讲解这点。

 

分析最佳模型和它们的误差

通过分析最佳模型,常常可以获得对问题更深的了解。比如,

  1. RandomForestRegressor
复制代码
可以指出每个属性对于做出准确预测的相对重要性:

  1. >>> feature_importances = grid_search.best_estimator_.feature_importances_
  2. >>> feature_importances
  3. array([  7.14156423e-02,   6.76139189e-02,   4.44260894e-02,
  4.         1.66308583e-02,   1.66076861e-02,   1.82402545e-02,
  5.         1.63458761e-02,   3.26497987e-01,   6.04365775e-02,
  6.         1.13055290e-01,   7.79324766e-02,   1.12166442e-02,
  7.         1.53344918e-01,   8.41308969e-05,   2.68483884e-03,
  8.         3.46681181e-03])
复制代码

 

将重要性分数和属性名放到一起:

  1. >>> extra_attribs = ["rooms_per_hhold", "pop_per_hhold", "bedrooms_per_room"]
  2. >>> cat_one_hot_attribs = list(encoder.classes_)
  3. >>> attributes = num_attribs + extra_attribs + cat_one_hot_attribs
  4. >>> sorted(zip(feature_importances,attributes), reverse=True)
  5. [(0.32649798665134971, 'median_income'),
  6. (0.15334491760305854, 'INLAND'),
  7. (0.11305529021187399, 'pop_per_hhold'),
  8. (0.07793247662544775, 'bedrooms_per_room'),
  9. (0.071415642259275158, 'longitude'),
  10. (0.067613918945568688, 'latitude'),
  11. (0.060436577499703222, 'rooms_per_hhold'),
  12. (0.04442608939578685, 'housing_median_age'),
  13. (0.018240254462909437, 'population'),
  14. (0.01663085833886218, 'total_rooms'),
  15. (0.016607686091288865, 'total_bedrooms'),
  16. (0.016345876147580776, 'households'),
  17. (0.011216644219017424, '<1H OCEAN'),
  18. (0.0034668118081117387, 'NEAR OCEAN'),
  19. (0.0026848388432755429, 'NEAR BAY'),
  20. (8.4130896890070617e-05, 'ISLAND')]
复制代码

 

有了这个信息,你就可以丢弃一些不那么重要的特征(比如,显然只要一个

  1. ocean_proximity
复制代码
的类型(INLAND)就够了,所以可以丢弃掉其它的)。你还应该看一下系统犯的误差,搞清为什么会有些误差,以及如何改正问题(添加更多的特征,或相反,去掉没有什么信息的特征,清洗异常值等等)。

 

用测试集评估系统

调节完系统之后,你终于有了一个性能足够好的系统。现在就可以用测试集评估最后的模型了。这个过程没有什么特殊的:从测试集得到预测值和标签,运行

  1. full_pipeline
复制代码
转换数据(调用
  1. transform()
复制代码
,而不是
  1. fit_transform()
复制代码
!),再用测试集评估最终模型:

  1. final_model = grid_search.best_estimator_
  2. X_test = strat_test_set.drop("median_house_value", axis=1)
  3. y_test = strat_test_set["median_house_value"].copy()
  4. X_test_prepared = full_pipeline.transform(X_test)
  5. final_predictions = final_model.predict(X_test_prepared)
  6. final_mse = mean_squared_error(y_test, final_predictions)
  7. final_rmse = np.sqrt(final_mse)   # => evaluates to 48,209.6
复制代码

评估结果通常要比交叉验证的效果差一点,如果你之前做过很多超参数微调(因为你的系统在验证集上微调,得到了不错的性能,通常不会在未知的数据集上有同样好的效果)。这个例子不属于这种情况,但是当发生这种情况时,你一定要忍住不要调节超参数,使测试集的效果变好;这样的提升不能推广到新数据上。

 

然后就是项目的预上线阶段:你需要展示你的方案(重点说明学到了什么、做了什么、没做什么、做过什么假设、系统的限制是什么,等等),记录下所有事情,用漂亮的图表和容易记住的表达(比如,“收入中位数是房价最重要的预测量”)做一次精彩的展示。

 

启动、监控、维护系统

很好,你被允许启动系统了!你需要为实际生产做好准备,特别是接入输入数据源,并编写测试。

 

你还需要编写监控代码,以固定间隔检测系统的实时表现,当发生下降时触发报警。这对于捕获突然的系统崩溃和性能下降十分重要。做监控很常见,是因为模型会随着数据的演化而性能下降,除非模型用新数据定期训练。

 

评估系统的表现需要对预测值采样并进行评估。这通常需要人来分析。分析者可能是领域专家,或者是众包平台(比如 Amazon Mechanical Turk 或 CrowdFlower)的工人。不管采用哪种方法,你都需要将人工评估的流水线植入系统。

 

你还要评估系统输入数据的质量。有时因为低质量的信号(比如失灵的传感器发送随机值,或另一个团队的输出停滞),系统的表现会逐渐变差,但可能需要一段时间,系统的表现才能下降到一定程度,触发警报。如果监测了系统的输入,你就可能尽量早的发现问题。对于线上学习系统,监测输入数据是非常重要的。

 

最后,你可能想定期用新数据训练模型。你应该尽可能自动化这个过程。如果不这么做,非常有可能你需要每隔至少六个月更新模型,系统的表现就会产生严重波动。如果你的系统是一个线上学习系统,你需要定期保存系统状态快照,好能方便地回滚到之前的工作状态。

回复

使用道具 举报

 
*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


1关注

0粉丝

1603帖子

排行榜

关注我们:微信订阅号

官方微信

APP下载

全国服务热线:

4000-018-018

公司地址:上海市嘉定区银翔路655号B区1068室

运营中心:成都市锦江区东华正街42号广电仕百达国际大厦25楼

邮编:610066 Email:3318850993#qq.com

Copyright   ©2015-2016  比特趋势Powered by©Discuz!技术支持:迪恩网络