1. 背景介绍
本章是全书第一部分,将展示一个完整的机器学习项目,让大家先有个整体性的认识。
假设自己是一家房地产公司的新聘数据科学家。 项目使用“加利福尼亚住房价格”数据集,如下图所示:

一般机器学习项目分为以下一些重要步骤:
- 问题全景分析
2. 问题全景分析
项目的整体目标,是使用加州人口普查数据来建立该州的房价模型。数据包括加利福尼亚州每个街区组(类似我们的居委会)的人口,中位数收入和房价中位数等指标。根据这些数据和所有其他指标来预测任何地区的房价中位数。
2.1 业务目标

通过上图发现,模型通过地区数据,预测得到房价数据。再将房价数据导入投资分析模型,得到投资数据。也就是房屋价格预测模型是一个数据处理的中间环节。
下面设计系统,很显然这是一个监督学习任务,并且是一个回归任务,使用简单的批处理学习就可以解决。
2.2 评价指标
第二个问题是选择评价指标。
说下欧式距离。
规范指数越高,它越关注大值而忽略小值。这就是为什么RMSE对异常值比MAE更敏感的原因。
3. 获取数据
项目的开发语言选择python3, 需要导入必要的包:numpy、pandas、matplotlib等。开发工具使用jupyter。任意创建一个工作目录,后续代码将在此运行(后续代码都在jupyter中创建并运行)。
首先开发一个函数,函数的功能是从外部下载并解压实验数据,代码如下:
import os
import urllib
import tarfile
DOWNLOAD_ROOT = "https://raw.githubusercontent.com/ageron/handson-ml2/master/"
#本地接收地址
HOUSING_PATH = os.path.join("datasets", "housing")
#远端下载地址
HOUSING_URL = DOWNLOAD_ROOT + "datasets/housing/housing.tgz"
def fetch_housing_data(housing_url=HOUSING_URL, housing_path=HOUSING_PATH):
#os.makedirs与os.mkdir的区别是它可以递归创建目录,相当于mkdir -p ,
#如果 exist_ok 为 False (默认值),则如果目标目录已存在将引发 FileExistsError。
os.makedirs(housing_path, exist_ok=True)
#datasets/housing/housing.tgz
tgz_path=os.path.join(housing_path,"housing.tgz")
# 下载文件
urllib.request.urlretrieve(housing_url,tgz_path)
# 解压文件
housing_tgz = tarfile.open(tgz_path)
housing_tgz.extractall(path=housing_path)
housing_tgz.close()
fetch_housing_data()
使用pandas读取数据集后,经常使用以下一些方法来对数据有一个概览的认识:
import pandas as pd
import matplotlib.pyplot as plt
housing_data = pd.read_csv("datasets/housing/housing.csv")
#展示top5数据
housing_data.head()
#ocean_proximity属性是代码,value_counts()方法查看每个代码的数据量,类似group by作用
housing_data["ocean_proximity"].value_counts()
#字段级基本信息
housing_data.info()
#字段级统计信息(中位数、最小值等)
housing_data.describe()
#告诉Jupyter使用自己的后端来设置matplotlib,并画图。
%matplotlib inline
#在整个数据集上调用hist()方法,将为每个数值属性绘制一个直方图
#bins参数:如果bins为整数值,则bins为柱子个数,根据数据的取值范围和柱子个数bins计算每个柱子的范围值,柱宽=(x.max()-x.min())/bins
# 如果bins取值为序列,则该序列给出每个柱子的范围值(即边缘)
#figsize参数:figsize指每张图的尺寸大小
housing_data.hist(bins=50, figsize=(20,15))
plt.plot

创建一个测试集,一般通常是全部数据的20%(如果数据集很大,可以少一些),创建测试集是未了模型对训练集的过拟合。下面代码使用二种方法来从全部数据集中选取测试数据集(随机、固定方式):
import numpy as np
# zlib模块为需要数据压缩的程序提供了一系列函数,用于压缩和解压缩。crc32用于计算data的CRC(循环冗余校验)值。计算的结果是一个32位的整数。
# 要在所有的 Python 版本和平台上获得相同的值,请使用 crc32(data) & 0xffffffff
from zlib import crc32
# test_ratio 在[0,1] 表示测试集占整体集合的比例
# 该种方法因为是随机选取,会导致每次选取的测试集不一样。
def random_split_train_test(data, test_ratio):
#np.random.permutation 如果提供一个整数参数n,则返回 0到n-1的随机顺序的seq。
# 如果提供一个一维序列,则返回该序列的随机序列结果(一维序列)。
# 如果提供一个多维序列,则按照第一维随机排序的结果。
shuffled_indices = np.random.permutation(len(data))
test_set_size = int(len(data)*test_ratio)
test_data_seq = shuffled_indices[:test_set_size] #0~25%
train_data_seq = shuffled_indices[test_set_size:] #25%~1
test_data_set = data.iloc[test_data_seq]
train_data_set = data.iloc[train_data_seq]
return test_data_set,train_data_set
# 通过ID(也就是数据行号)编码的方式,来确定该行数据是否属于测试集/训练集,这种方式可以保证多次执行能得到同一份测试集。
def test_set_check(identifier, test_ratio=0.5):
# 在测试集中返回True,否则False
return crc32(np.int64(identifier)) & 0xffffffff < test_ratio * 2**32
def split_train_test_by_id(data, test_ratio, id_column):
ids = data[id_column]
in_test_set = ids.apply(lambda id_: test_set_check(id_, test_ratio))
return data.loc[~in_test_set], data.loc[in_test_set]
#为pandas dataframe添加序号列index,值即为数据row的行号。
housing_with_id = housing_data.reset_index()
fix_train_set,fix_test_set = split_train_test_by_id(housing_with_id,0.2,"index")
len(housing_data),len(fix_train_set), len(fix_test_set)
#简单使用Scikit-Learn提供的测试集筛选方法 类似于random_split_train_test方法
from sklearn.model_selection import train_test_split
#参数test_size是测试集占全体数据集比例,random_state是随机种子
sk_train_set, sk_test_set = train_test_split(housing_data, test_size=0.2, random_state=42)
len(housing_data),len(sk_train_set), len(sk_test_set)
我们在抽取样本时,可能并没有那么简单。举例说明,我们做人口分析,总样本中男女比例是 53%和47%,那么我们在抽取样本时,也应该尽量保持这个比例,否则抽样的结果会大概率错误。这叫做分层抽样。
# 根据median_income取值范围打标志,生成新列median_income,取值1-5
housing_data["income_cat"] = pd.cut(housing_data["median_income"],
bins=[0., 1.5, 3.0, 4.5, 6., np.inf],labels=[1, 2, 3, 4, 5])
housing_data["income_cat"].hist()
#使用sk-learn包方法实现分层抽样
from sklearn.model_selection import StratifiedShuffleSplit
# n_splits是将训练数据分成train/test对的组数,可根据需要进行设置,默认为10,我们分一组训练/测试集出来。
split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
for train_index, test_index in split.split(housing_data, housing_data["income_cat"]):
strat_train_set = housing_data.loc[train_index]
strat_test_set = housing_data.loc[test_index]
strat_test_set["income_cat"].value_counts()/len(strat_test_set)
#删掉分层抽样数据集中的income_cat列,该列是上边为了验证分层抽样的比例与总数据比例一致而增加的,无实际意义。
for set_ in (strat_train_set, strat_test_set):
set_.drop("income_cat", axis=1, inplace=True)
strat_test_set.head()
4. 进一步探索和可视化数据:
4.1基于地理信息(经纬度)可视化数据:
前边只是简单浏览了下数据,这节针对训练集数据,进行深入研究。首先,按照地理信息来讲数据展示为散点图:
#备份一个训练数据集
housing_train_copy = strat_train_set.copy()
housing_train_copy.head()
# 通过dataframe生成散点图的方法 ,简单方法
housing_train_copy.plot(kind="scatter", y="latitude", x="longitude", alpha =0.1)
# 详细参数画图:参数详见 https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.plot.html
#
housing_train_copy.plot(
kind="scatter",
x= "longitude",
y="latitude",
alpha = 0.4,
s = housing_train_copy["population"]/100, #点半径大小按照population列区分
label = "population",
figsize = (10,7), #图片的大小
c = "median_house_value",
cmap = plt.get_cmap("jet"),
colorbar = True
)
plt.legend


4.2 数值数据的相关性分析:
数据集不大,可以直接调用pands的corr 方法获取相关性值矩阵。
5. 为算法模型准备数据:
为算法准备数据的过程建议写成函数,为了方便以后的复用(或者快速转换新的dataset)。
网友评论