机器学习项目的实例分析设计(附源码)
摘要说明:
最近在学习“Hands-On Machine Learning with Scikit-Learn &TensorFlow”,其中一些机器学习的思考和处理方式很具有项目级开发的特点。我在原文提供的实例基础上,结合自己的分析,模拟了一个机器学习项目的分析和实现过程,包括项目分析,数据分析,模型分析,性能分析等,在原文的样本代码基础上,编写了完整的可全面自动化运行的代码,供大家一起参考。(文章后面将提供源码的 github 地址)
一. 项目的总体分析
- 业务分析
-
内容
分析设计
项目名称
加利福尼亚房价预测
商业背景及价值
本公司是一家投资公司,现有一套综合投资分析系统,将汇总各类信息,对行政区进行全面分析后,决定是否值得进行投资。
本项目获得的各行政区的房价中值预测值,将提供给公司的综合投资分析系统,作为其中的一项分析指标,帮助公司进行决策。
目前房价中值的计算,完全依靠业务专家,通过复杂的人工推导分析后获得,费时费力,不及时,不准确。
本项目将实现房价中值的自动化预测计算和处理,其预测结果将影响公司投资决策的速度和准确性,从而直接影响公司的商业业绩。
业务需求
利用加利福尼亚房价数据库提供的统计数据,通过机器学习,从这些数据中构建模型,获得信息,用于预测加利福尼亚州不同行政区的房价中值
非功能性需求
目前采用人工分析的方法,全部计算一次所有行政区的房价中值,需要5 人天。
本项目完成后,正常情况下,要求在1小时内完成所有计算。
- 系统分析
内容 | 分析设计 | ||||||||||||||||||
基本训练模型方案 |
1. 由于有明确的预测值要求,因此将使用有监督的模型(不是无监督模型); 2. 由于预测的是具体数值,不是标签,因此属于回归问题(不是分类问题,或聚类问题); 3. 根据跟专家的沟通,房价的预测将需要涉及数据集中提供的多个特征属性,属于多变量回归问题 4. 房价数据源信息不是实时连续变化的数据,数据量也不是特别大,完全可以装载到内存后进行处理,因此采用简单的单机批处理方式来完成。(不考虑大数据下的分布式处理及实时数据流的在线处理方式)
综上分析,本项目将采用的基本回归分析模型有:
| ||||||||||||||||||
模型效果的测量指标 |
回归分析模型的性能,可采用的测量指标有:
RMSE对异常数据比较敏感,若数据集的异常数据很少时(即数据分布比较集中,像一个钟型),用RMSE来衡量模型效果还是比较好的, 因此,本项目将采用 RMSE 指标进行模型性能的衡量比较,RMSE值越小,模型性能越好。 | ||||||||||||||||||
开发环境 |
Python的生态环境提供了我们完成该项目需要的机器学习函数库,开源,通用性好,运行性能可满足项目要求;如 numpy, pandas, scikit-Learn等,其中scikit-Learn模块中有一些流程化处理的函数,可以让自动化处理的实现更加简洁和优雅。 因此,本项目将采用 Python + Scikit-Learn 为主要开发平台 | ||||||||||||||||||
自动化及可重用性 | 这是一个需要可重复使用的生产系统,不是一次性工具,也不是单纯研究用的人机交互程序;需要尽量把处理代码自动化,以提高调试优化的效率,并在生产环境中部署后自动化运行,提供必要的运维日志信息 | ||||||||||||||||||
前提假设 |
1. 加利福尼亚房价数据库持续提供更新的房价数据 2. 公司综合投资分析系统的数据接口保持不变,需要本项目提供的是房价的具体数值,而不是“昂贵”,“中等”,“低廉”的分类标签。否则,本项目的学习模型将会更改为分类模型 | ||||||||||||||||||
术语说明 |
关于记录集变量的定义说明如下:(括号内为全名称)
|
二. 原始数据分析
- 数据源分析
内容 | 分析设计 |
数据来源 |
StatLib 库为公开的数据库,其中提供了自1990年以来的加利福尼亚房价数据 Github 上的镜像,也可更方便地获得该数据集 https://raw.githubusercontent.com/ageron/handson-ml/master/datasets/housing/housing.tgz (注:本演示项目将使用github上的数据集样本文件) |
数据内容及数据量 | 主要为加利福尼亚的各行政区的房价数据,下载解压,保存为CSV文件后,在几百兆左右。 |
数据法律义务,授权情况及访问权限 | 项目用到的数据源,属于对外公开的数据集,可合理使用在项目中,不需要授权,不需要承担任何法律义务。 |
磁盘空间的分析 | 根据目前数据源的下载量分析和数据可能的增长趋势,5年内,数据集文件不会超过500M,考虑系统的环境安装,数据处理过程的需要,原始数据的备份,以及处理完的数据的备份等,该项目的磁盘空间建议预留100G以支持5年的运维。 |
- 获取数据分析
内容 | 分析设计 |
获取数据 |
1. 数据源: https://raw.githubusercontent.com/ageron/handson-ml/master/datasets/housing/housing.tgz 2. 获取 hoursing.tgz 文件后,解压出 housing.csv数据集文件 |
抽取一个原始测试集 |
需要留一部分原始且未做任何处理的数据,来完整测试整个处理过程,并验证训练模型的实际性能。 该原始测试集不同于交叉验证时测试集,交叉验证是将已经清理干净的输入学习模型的数据随机分成训练集,测试集;而原始测试集是未清理过的数据。 基本处理过程: 1. 将原始数据拆分为2个数据集,train_set, test_set, 分别保存到文件中(train_set.csv, test_set.csv),供以后重复使用 2. 装入训练数据集(train_set.csv),供后续的分析,清理,训练使用 |
实现整个过程的自动化处理 |
获取数据集,抽取原始测试集,装载训练集,需要自动化完成 参看代码: load_data.py |
三. 数据特征分析
该步骤主要是在人机交互的 Jupyter notebook中完成,需要业务专家的配合,通过分析,可以使我们了解清楚需要处理的数据集的基本特征和业务特征,为下一阶段的数据清理和模型构建提供信息。该步骤不修改数据,也没有相应的自动化处理代码,需要记录分析结果。
本文列出了一些分析检查表,其中的分析内容和举例仅供参考。而实际项目中,该步骤很重要,需要花费很多的时间,因为后续的处理,都将基于对数据集的全面了解。(Jupyter notebook 文件包含在提供的源代码中)
内容 | 分析设计 | ||||||||||||||||||||
将数据集装载到内存 |
import pandas as pd import matplotlib.pyplot as plt %matplotlib inline
datafilename = r"train_set.csv" train_set_original = pd.read_csv(datafilename) | ||||||||||||||||||||
创建一个数据集的副本
|
train_set = train_set_original.copy()
这个数据集,是已经抽取掉原始测试集后的数据集,专门用于分析,训练,交叉测试用的。 该数据集包含标签列和特征列 | ||||||||||||||||||||
检查数据集的大小 |
train_set.shape (16512, 10) 一共有16512条样本数据,每条数据有10个特征列(其中含标签列) | ||||||||||||||||||||
获得数据集的所有特征名称及数据类型 |
train_set.dtypes longitude float64 latitude float64 housing_median_age float64 total_rooms float64 total_bedrooms float64 population float64 households float64 median_income float64 median_house_value float64 ocean_proximity object
除[ocean_proximity] 为字符型数据列(类别意义特征属性),其他特征列均为数值意义特征。 类别意义特征需要在后续处理中,转换为数值意义特征,因为输入学习训练模型的必须是数值特征 | ||||||||||||||||||||
若为监督学习任务,需要确定标签列 |
本项目中,标签列为 [median_house_value] 房屋价格的中值 (不是平均值)
明确标签列后,可以针对性地分析了解特征列跟标签列的相关性 | ||||||||||||||||||||
数据集的样本具体内容 |
train_set.head() train_set.tail() | ||||||||||||||||||||
特征数据的分布情况 |
比如:字符分类特征列 ocean_proximity train_set[“ocean_proximity”].value_counts() <1H OCEAN 7341 小于1小时车程 INLAND 5227 内陆 NEAR OCEAN 2086 靠近海 NEAR BAY 1854 靠近海湾 ISLAND 4 海岛
| ||||||||||||||||||||
分析特征属性的基本业务含义 |
需要在跟业务专家的配合下,分析主要特征属性的含义及对标签列的影响程度,机器学习是用来分析和解决实际业务问题的,不单是数据处理。这里做了简单分析,纯属个人观点
| ||||||||||||||||||||
数据集的快速统计分析 |
train_set.isnull().sum() train_set.info() train_set.describe() | ||||||||||||||||||||
可视化数据进行分析 |
利用各种图表工具,对数据集进行可视化查看分析
每个列的数据直方图 train_set.hist(bins=50,figsize=(20,15)) 按地理位置的人口分布散点图 train_set.plot(kind='scatter',x='longitude', y='latitude', alpha=0.4, s=train_set['population']/100,label='population', c='median_house_value',cmap=plt.get_cmap("jet"),colorbar=True) plt.legend() 数据可视化分析很重要,涉及的内容也相对广泛,本文不再继续展开 | ||||||||||||||||||||
分析样本属性之间的关系 |
特征属性跟标签列 及 特征属性间的线性相关性 corr_matrix = train_set.corr() corr_matrix corr_matrix["median_house_value"].sort_values(ascending=False)
median_house_value 1.000000 median_income 0.690647 total_rooms 0.133989 housing_median_age 0.103706 households 0.063714 total_bedrooms 0.047980 population -0.026032 longitude -0.046349 latitude -0.142983
media_income, total_rooms, housing_media_age 跟房价中值的相关性比较大
可视化这些属性的关系 from pandas.tools.plotting import scatter_matrix attributes = ["median_house_value","median_income","total_rooms","housing_median_age"] scatter_matrix(train_set[attributes],figsize=(12,8)) 行政区的media_house_value(房价中值) 跟 media income (收入中值)的线性相关性确实比较明显。 | ||||||||||||||||||||
确认可用的额外字段 |
对属性字段进行加工及合并的分析,以便生成新的特征属性,表现出更多的内部关系。 例如: 增加属性列 population_per_household = poplation / households, 可获得每个家庭的平均人口数 | ||||||||||||||||||||
确认不需要的字段 |
对预测标签没有任何影响的字段,如随机字段等,可以标记为删除。如日志记录中的随机***等。 本数据集中的特征属性,通过分析,都跟标签列有关系,因此,没有需要删除的列。 | ||||||||||||||||||||
记录所有的分析内容,用于后续的数据自动化处理 | 以上分析结果,我们需要进行详细记录。而这些只是初步的分析处理方案,在实际训练过程中,需要根据模型性能评分,多次反复地分析,调整,才能获得最理想的效果。 |
四. 特征工程
特征工程是在原始数据特征分析的基础上,对数据进行全面特征处理和特征提取,构建最终的特征数据集,供学习模型进行训练使用。提供给模型的数据和特征,决定了机器学习的上限,可见其重要性。
该步骤需要能自动化运行,以便重复使用。分析,开发中,可以使用Jupyter进行调试,最终,应该是可以完整运行的 Python 代码模块。
内容 | 分析设计 |
创建一个特征数据集的副本
|
该步骤进行处理的数据集不包含标签列。 上一步骤的数据集包含标签列,是因为可以用来分析特征列和标签列的相关性,而清理数据时,不应该包含标签列的,因为标签列一般不需要进行清理或单独进行处理。 |
修复或删除异常值 |
任何情况都应该考虑null值,即使当前训练集没有,也无法保证测试集及将来的数据集都没有。 null值的处理方式有以下几种: 删除行,删除列,填充具体值等,本项目采用填充中值的方式处理null值 本项目使用 scikit-learn的转换器 Imputer 来完成 |
聚合生成新的特征 |
本项目将增加 population_per_household 等新特征。 编写自定义转换器来完成 class CombinedAttributesAdder() |
添加多项式新特征 |
本项目没有其他新特征列 在代码中,提供了一个在对 pandas.DataFrame类型数据进行字段增加的转换器供参考。 class FeatureAdder() |
删除不需要的特征 |
本项目没有删除特征列 在代码中,提供了一个在对 pandas.DataFrame类型数据进行字段删除的转换器供参考 class FeatureDeleter() |
对连续特征离散化处理 |
本项目没有用到 就是把数据分段,线性值变成离散数据或分类值,如可以将population_per_household进行离散化处理 n <= 3 : 小家庭, 取值1 3 < n <= 7 : 中等家庭, 取值2 n > 7 : 大家庭, 取值3 |
对特征进行归一化处理或标准化处理 |
归一化处理后,数据的业务敏感度降低,数据的分布和方差保持不变,数据量减少,算法更容易收敛。 本项目使用scikit-learn提供的StandardScaler,进行归一化处理 |
对文本分类属性的处理 |
机器学习算法一般认为数据值越相近,则距离越近。将文本分类属性单纯用数字来代替,跟实际情况不符合,需要进行处理。 本项目采用 one-hot编码,即为每个分类值创建一个新特征属性,若原分类值为对应分类列,则该列特征值为1,否则为0 本项目使用 scikit-learn的 LabelBinarizer 转换器,并做了兼容性调整 |
自动化处理数据清理过程 |
所有转换处理都用函数实现自动化处理,从而可快速处理新的训练集,原始测试集。 scikit-learn 提供了一套 Pipeline 的流程化处理函数,可以让数据清理过程按设计流程顺序依次执行,很是干净和优雅。 |
|
|
五. 训练模型
内容 | 分析设计 |
若数据量太大,可抽取部分数据进行快速模型分析 | 本项目数据量不大,将使用全部训练集数据进行处理 |
训练模型并分析性能 |
传入训练数据集,训练模型,采用交叉验证的方式,随机生成训练集,测试集,计算RMSE评分。 保存每次测量的训练模型,用于后续分析和预测使用,同时显示模型训练的性能评分,以便跟其他训练结果进行比较 |
使用标准参数训练不同的基本模型 |
本项目的基本训练模型为
|
调整模型参数,进行优化 |
可能需要在多个模型中,使用多套参数组合方案,然后分析模型性能结果。 利用 Grid Search, 给出参数列表,然后让系统自动构建所有组合参数进行训练,返回性能最好的模型参数 |
集成多个模型,共同进行训练 |
本项目暂不需要
|
比较模型性能,获得最佳模型 |
需要重复以上分析步骤和过程,调整数据,调整特征,调整模型参数等,并对每次的训练结果进行记录和全面分析,以获得最佳的训练器。 本项目采用 RMSE 指标进行模型性能的衡量比较,RMSE值越小,模型性能越好 |
所有训练过程实现自动化 |
运行 python main.py , 完成数据获取,清理,训练,性能结果比较的自动化处理。 获得最佳的训练模型 |
六. 对原始测试集进行测试
内容 | 分析设计 |
对原始测试集进行预测 |
选用训练获得的最佳模型,对原始测试数据集进行测试,分析性能,并处理预测值 |
七. 后续工作
到目前为止,主要完成的是项目分析,设计和开发部分的工作,后续还有很多项目级的工作需要逐步配合完成,本文只做基本列举,不再详细描述
内容 |
提供项目的结果分析报告,并跟业务部门沟通分析 |
根据业务部门的反馈,继续进行参数和代码的优化,到达最佳的业务效果 |
开发跟公司决策分析系统的数据接口 |
项目提交测试部门进行正式测试 |
测试通过后,进行项目的生产部署和运维管理 |
定期更新训练数据,重新训练模型,保持并提高模型的性能 |
项目的经验总结(有效做法,无效做法,前提假设,局限性及改进方案),这些都将是公司和个人在项目开发和项目管理上的宝贵财富 |
附录:
该演示项目的源代码 github 地址:
https://github.com/EdwinZhang1970/Python/tree/master/California%20Housing%20Prices