(原创)一个完整的机器学习项目是怎么建立起来的

Posted rayshaw13

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(原创)一个完整的机器学习项目是怎么建立起来的相关的知识,希望对你有一定的参考价值。

在这篇文章中,将介绍机器学习项目的流程

明确问题

首先,我们需要预览这个项目。项目的目的是什么,以房价预测为例,数据为StatLib的加州房产数据,那么目的就是预测街区的房产价格中位数。

划定问题及分析

要知道商业目的是什么,毕竟建立模型不是最终目的。比如说,目的是输出一个价格传给另一套系统来判断是否值得投资以及利润是多少。
要知道现在的解决方案效果怎么样,比如会给出一个现解决方案的误差率是alpha。
现在我们可以进一步研究问题,明确这个问题是监督/非监督,还是强化模型?是分类/回归,还是聚类等其他。要使用批量学习还是线上学习?
分析,我们有房价的值,所以是一个监督问题;我们最终是要预测得到房价中位数,因此是一个回归问题,而且是一个多变量预测回归,因为有很多影响参数;另外,没有连续的数据流入,没有特别需求需要对数据变动作出快速适应。数据量不大可以放到内存中,因此批量学习就可以。【如果数据量很大,你可以要么在多个服务器上对批量学习做拆分(使用 MapReduce 技术,后面会看到),或是使用线上学习】

选择性能指标

在这里我们需要选择一个评价指标,回归问题的典型指标是均方根误差RMSE,它表征的是系统预测误差的标准差。
另外,也可以使用差平方绝对误差。

核实假设

再一次核实之前的分析是否准确,需要联系下游的处理进行检查。

获取数据

创建工作空间

比如python jupyter及相应的库文件(如numpy, pandas, scipy, 及sklearn等)和框架(tf等)

下载数据

一般来说,可以从数据库中下载数据,但是对于数据库一般需要密码及权限。在这里,我们可以直接从网页数据,当然,这是具体问题具体分析的。

查看数据

下载好数据后,我们需要查看一下数据的结果,预览一下。基本用到以下代码

import pandas as pd
data=pd.read_csv(‘路径‘)
data.head()
data.info()#返回特征的数量及类型
data.describe()#返回数量、均值、标准差、最值等信息

另外也可以使用柱状图通过可视化查看数据的分布,代码:

%matplotlib inline   # only in a Jupyter notebook
import matplotlib.pyplot as plt
data.hist(bins=50, figsize=(20,15))
plt.show()#在jupyter中可以不加这条语句

hist()方法依赖于 Matplotlib,后者依赖于用户指定的图形后端以打印到屏幕上。因此在画图之前,你要指定 Matplotlib 要使用的后端。
最简单的方法是使用 Jupyter 的魔术命令%matplotlib inline。它会告诉 Jupyter 设定好 Matplotlib,以使用 Jupyter 自己的后端。绘图就会在 notebook 中渲染了。

创建测试集

在查看数据前,最好先创建一下测试集,以免查看数据后因为思维定势影响测试集的选择。
一种方法是可以随机选择测试集,比如随机选择20%的数据作为测试集,但是这样当数据集更新时,测试集会变化,我们可以使用随机数处理。代码:

from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)

另外,随机取数有可能丢失掉关键特征的分布。比如,有一个特征A对最终标签的贡献很大(两者之间相关性很强),
那么我们也应该在测试集中保证A的分布符合原数据集的分布趋势。这时可以使用分层采样。代码:

data["A_new"] = np.ceil(data["A"] / 1.5)
data["A_new"].where(data["A_new"] < 5, 5.0, inplace=True)#这两条语句是对数据生成一个新的标签,表征A的分层(分布),这要具体问题具体分析
from sklearn.model_selection import StratifiedShuffleSplit

split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)

for train_index, test_index in split.split(data, data["A_new"]):#按照A_new对data进行划分测试集训练集
    strat_train_set = data.loc[train_index]
    strat_test_set = data.loc[test_index]

可通过以下代码检查分层采样的结果:

data["A_new"].value_counts() / len(data)
strat_test_set["A_new"].value_counts() / len(strat_test_set)

注意最后需要将生成的A_new标签删除,用drop命令,代码:

for set in (strat_train_set, strat_test_set):
    set.drop(["A_new"], axis=1, inplace=True)

这样我们就生成了两组训练-测试集,随机的和分层的。

数据可视化及数据探索

数据可视化

经常性的,我们需要首先对数据进行一下观察,可以判断特征与标签的关系以及哪些特征的作用或者影响更大。在这里使用matplotlib库即可,如:

housing.plot(kind="scatter", x="longitude", y="latitude")#散点图看分布

查找关联

可以使用corr()方法计算每对属性间的标准相关系数,如:

corr_m=housing.corr()
print(corr_m[‘median_house_value‘].sort_values(ascending=True))

注意corr()只能表征线性关系,对于非线性关系直接忽视,因此参考性有局限。还有一种检测属性间相关系数的方法时pandas的scatter_matrix函数,如:

from pandas.tools.plotting import scatter_matrix
attributes = ["median_house_value", "median_income", "total_rooms", "housing_median_age"]#只表示这四个的相关性
scatter_matrix(housing[attributes], figsize=(12, 8))

属性组合试验

有时候仅仅使用原有的特征数据的效果并不好,这是可以考虑一下将一些特征组合产生新的特征,比如将人数/家庭,得到每户的人数这样一个特征。如:

housing[‘population_per_household‘]=housing[‘population‘]/housing[‘households‘]

然后可以重新观察相关性。

为机器学习准备数据

不要手工来做,你需要写一些函数,理由如下:
函数可以让你在任何数据集上(比如,你下一次获取的是一个新的数据集)方便地进行重复数据转换。
你能慢慢建立一个转换函数库,可以在未来的项目中复用。
在将数据传给算法之前,你可以在实时系统中使用这些函数。
这可以让你方便地尝试多种数据转换,查看哪些转换方法结合起来效果最好。
有一点需要注意,那就是要时刻记得复制数据,保证后面的数据处理尽量不要影响最初的数据。做好标记。

数据清洗

原数据中会存在缺失值等问题,因此需要对数据进行清洗。这一非常关键的一步。
对于缺失值的处理,有三种方式
1、直接删掉缺失值所在的行;2、如果一个特征的缺失值太多,那么直接删掉该特征;3、对缺失位置进行赋值(用0、中位数或者平均值等)。如:

housing.dropna(housing[‘total_bedrooms‘])
housing.drop(‘total_bedrooms‘,axis=1)
housing[‘total_bedrooms‘].fillna(median)

尽量采用第三种方式,这样可以充分利用原数据。可以使用sklearn的Imputer类来处理缺失值。

from sklearn.preprocessing import Imputer
imputer = Imputer(strategy="median")#创建一个Imputer类
housing_num = housing.drop("ocean_proximity", axis=1)#创建没有文本属性的数据副本
imputer.fit(housing_num)#将类应用于数据,求出median
X = imputer.transform(housing_num)#对数据进行转换,填充缺失值,得到一个numpy数组
housing_tr = pd.DataFrame(X, columns=housing_num.columns)#数组转化为DataFrame格式

处理文本和类别属性

数据中会有一些文本类型,在处理时我们可以使用one-hot对其进行重新编码,这需要两个转换(文本分类到整数分类,再到one-hot向量)
可以用sklearn的LabelBinarizer实现这两个转换

from sklearn.preprocessing import LabelBinarizer
encoder = LabelBinarizer()
housing_cat_1hot = encoder.fit_transform(housing_cat)#得到one-hot向量
print(housing_cat_1hot)

但是,上面的类也应用于标签列的转换,正确的做法时用sklearn即将提供的CategoricalEncoder类,如:

cat_encoder = CategoricalEncoder()
housing_cat_reshaped = housing_cat.values.reshape(-1, 1)
housing_cat_1hot = cat_encoder.fit_transform(housing_cat_reshaped)
print(housing_cat_1hot)

自定义转换器

转换器的作用是将一些数据处理的操作集中在一起执行,比如前面叙述的清洗、属性组合等,另外可以将自制的转换器与sklearn的流水线无缝衔接工作。这一部分的示例代码可以查看自己写的文件(备注:)。这一部分可以将属性组合写在里面。
注意这里可以为属性设置一些超参数,检查这个属性是否地ML的算法有帮助。

特征缩放

这个步骤很重要,针对的是输入数值属性量度的不同问题。比如,年龄属性在20~50,而收入分布在5000~100000,这样的数据应用于算法的性能不会太好。通常情况下不要对目标值进行缩放。
两种方式:
线性函数归一化(min-max-scaling)-减去最小值,再除以最大值与最小值的差值,sklearn的MinMaxScaler
标准化(standardization)-减去平均值,再除以方差,得到的分布具有单位方差。sklearn的StandardScaler
注:所有的数据转换等操作都要分别作用于训练集和测试集,不要向完成的数据集使用。

转换流水线

流水线的作用时创建一种模式,使得数据可以按照一定顺序进行处理和转化。例如下面是一个完整的处理数值和类别属性的流水线:

from sklearn.pipeline import FeatureUnion
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

num_attribs = list(housing_num)
cat_attribs = ["ocean_proximity"]

num_pipeline = Pipeline([
        (‘selector‘, DataFrameSelector(num_attribs)),
        (‘imputer‘, Imputer(strategy="median")),
        (‘attribs_adder‘, CombinedAttributesAdder()),
        (‘std_scaler‘, StandardScaler()),
    ])

cat_pipeline = Pipeline([
        (‘selector‘, DataFrameSelector(cat_attribs)),
        (‘cat_encoder‘, CategoricalEncoder(encoding="onehot-dense")),
    ])

full_pipeline = FeatureUnion(transformer_list=[
        ("num_pipeline", num_pipeline),
        ("cat_pipeline", cat_pipeline),
    ])

使用时调用:

housing_prepared=full_pipeline.fit_transform(housing)#其中housing是分层抽样并drop掉标签值的分好的训练集。

这样调用步骤:num_pipeline->DataFrameSelector->Imputer->CombinedAttributesAdder->StandardScaler->cat_pipeline->DataFrameSelector->CategoricalEncoder,得到处理好的训练集。
其表示分别为:子流水线数据操作-》选择转化器-》缺失值处理-》属性组合-》标准化-》子流水线分类处理-》选择转化器-》分类标记为one-hot向量
对于选择转换器的解释:通过选择对应的属性(数值或分类)、丢弃其它的,来转换数据,并将输出DataFrame转变成一个 NumPy 数组。Scikit-Learn 没有工具来处理 PandasDataFrame,因此我们需要写一个简单的自定义转换器来做这项工作:

#这一部分最好写在前面
from sklearn.base import BaseEstimator, TransformerMixin
class DataFrameSelector(BaseEstimator, TransformerMixin):
    def __init__(self, attribute_names):
        self.attribute_names = attribute_names
    def fit(self, X, y=None):
        return self
    def transform(self, X):
        return X[self.attribute_names].values

至此,我们得到了处理后的训练集(不带标签)housing_prepared,标签集housing_labels,测试集(未经处理,并带标签)。

选择并训练模型

在训练集上训练和评估

到这里我们就可以选择算法模型对数据进行训练学习(其实我们可以发现大多数的工作都集中在数据的预处理上,包括清洗可视化文类属性转化等)。

from sklearn.linear_model import LinearRegression
lin_reg=LinearRegression()
lin_reg.fit(housing_prepared,housing_labels)

评价模型,可以使用均方根误差。

from from sklearn.metrics import mean_squared_error
housing_predictions = lin_reg.predict(housing_prepared)
lin_mse = mean_squared_error(housing_labels, housing_predictions)
lin_rmse = np.sqrt(lin_mse)

当然不仅是线性回归,同样可以使用其他模型,比如决策树模型、随机森林模型,步骤同上。

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

另外,我们可以使用交叉验证来验证模型,以决策树为例:

from sklearn.model_selection import cross_val_score
scores = cross_val_score(tree_reg, housing_prepared, housing_labels,scoring="neg_mean_squared_error", cv=10)
rmse_scores = np.sqrt(-scores)

以上,随机地将训练集分成十个不同的子集,成为“折”,然后训练评估决策树模型 10 次,每次选一个不用的折来做评估,用其它 9 个来做训练。结果是一个包含 10 个评分的数组。
Scikit-Learn 交叉验证功能期望的是效用函数(越大越好)而不是损失函数(越低越好),因此得分函数实际上与 MSE 相反(即负值),这就是为什么前面的代码在计算平方根之前先计算-scores。
结果查看:

def display_scores(scores):
...     print("Scores:", scores)
...     print("Mean:", scores.mean())
...     print("Standard deviation:", scores.std())
display_scores(tree_rmse_scores)

当然也可以将线性回归和随机森林适用到交叉验证上。另外还可以使用其他算法,比如神经网络、不同核心的支持向量机等。不要将太多时间花在调参上。
到这儿的目标是先列出几个合适的模型列表。

模型微调

网格搜索

使用 Scikit-Learn 的GridSearchCV方法。以针对随机森林为例。

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

分析可知,以上训练总共有18 × 5 = 90轮。
参数的最佳组合及最佳估计器:

print(grid_search.best_params_)
print(grid_search.best_estimator_)

随机搜索

当超参数的搜索空间很大时,最好使用RandomizedSearchCV。这个类的使用方法和类GridSearchCV很相似,但它不是尝试所有可能的组合,而是通过选择每个超参数的一个随机值的特定数量的随机组合。

集成方法

另一种微调系统的方法是将表现最好的模型组合起来。

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

通过分析最佳模型,常常可以获得对问题更深的了解。比如,RandomForestRegressor可以指出每个属性对于做出准确预测的相对重要性:

feature_importances = grid_search.best_estimator_.feature_importances_
print(feature_importances)
#将重要性分数与属性名放在一起
extra_attribs = ["rooms_per_hhold", "pop_per_hhold", "bedrooms_per_room"]
cat_one_hot_attribs = list(encoder.classes_)
attributes = num_attribs + extra_attribs + cat_one_hot_attribs
sorted(zip(feature_importances,attributes), reverse=True)#输出重要性和对应的属性名

根据以上重要性分数,我们可以舍弃一些不重要的属性等

用测试集评估系统

终于调试完模型,接下来我们需要用测试集来测试,注意,我们之前分割数据之后,测试集一直没用,这时我们需要先对测试集进行一下处理,比如丢掉标签、流水线处理等。然后再将我们的模型应用上去。

final_model = grid_search.best_estimator_
X_test = strat_test_set.drop("median_house_value", axis=1)
y_test = strat_test_set["median_house_value"].copy()
X_test_prepared = full_pipeline.transform(X_test)
final_predictions = final_model.predict(X_test_prepared)
final_mse = mean_squared_error(y_test, final_predictions)
final_rmse = np.sqrt(final_mse) 
print(final_rmse)

到这,我们就基本完成了模型的创建和测试,接下来就需要将结果或者结论展示出来。

启动、监控和维护系统

将以上模型植入公司系统,实现自动化运行。

实践

不要浪费在高级算法上,会使用就可以了。重点在于理解业务和数据,以及数据的处理。

注:此文章参考了GitHub-PeterHo的文章,感谢。

以上是关于(原创)一个完整的机器学习项目是怎么建立起来的的主要内容,如果未能解决你的问题,请参考以下文章

[机器学习]推荐系统简介——一起来看看你是怎么被大数据杀熟的

机器学习项目实战10例目录项目详解 + 数据集 + 完整源码

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

从零到部署,在线实现一个完整的机器学习任务(以美国居民收入预测为例)

机器学习完整项目实战附代码:探索型数据分析+特征工程+建模+报告

简要说说一个完整机器学习项目的流程