数据科学原理与数据处理第二周

特征工程

1 机器学习

机器学习简单来说就是选择一种学习算法,从数据中学习并建立成模型来对新的数据进行预测的计算机科学 。

机器学习是人工智能的一个分支。人工智能的研究是从以“推理”为重点—以“知识”为重点—再到以“学习”为重点,一条自然、清晰的脉络。机器学习是实现人工智能的一个途径,即以机器学习为手段解决人工智能中的问题。机器学习算法是一类从数据中自动分析获得规律(模型),并利用规律对未知数据进行预测的算法。

我们的数据量越来越多,硬件越来越强悍。急需要解放人的生产力,自动去寻找数据的规律。解决更多专业领域的问题。机器学习已广泛应用于数据挖掘、计算机视觉、自然语言处理、生物特征识别、搜索引擎、医学诊断、检测信用卡欺诈、证券市场分析、DNA序列测序、语音和手写识别、战略游戏和机器人等领域。

机器学习适用于以下等问题:

  • 不存在已知算法解决方案的复杂问题
  • 需要大量手动调整或者规则列表超长的问题
  • 可以适应环境波动的系统

1.1 基础概念

  • 样本与标签

标签是提供给算法的包含所需解决方案的训练数据,是我们要预测的事物,即简单线性回归中的 y 变量。

样本每一条数据叫一个样本,即数据的特定实例:x

  • 特征

属性加上其值 就是特征,也可以理解为输入变量,即简单线性回归中的 x 变量。

  • 回归任务

关注预测值和真实值之间的差别,就是通过给定的特征来预测一个目标数值。

  • 训练集

用于训练模型的数据叫训练集

  • 测试集

用于测试模型精度的数据叫测试集

  • 过拟合与欠拟合

从字面的意义上理解就是过度拟合的意思,常发生在线性分类器或者线性模型的训练和预测当中。过拟合的原理就是机器学习算法过度学习了训练集数据。反之欠拟合。

  • 模型训练

是指创建或学习模型。也就是说,向模型展示有标签样本,让模型逐渐学习特征与标签之间的关系。通过训练数据找到算法最合适的参数。

1.2 机器学习执行流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4bGMbJC1-1574825755327)(image/1574144721121.png)]

  1. 理解实际问题,抽象为机器学习能处理的数学问题

    理解实际业务场景问题是机器学习的第一步, 机器学习中特征工程和模型训练都是非常费时的,深入理解要处理的问题,能避免走很多弯路。理解问题,明确可以获得的数据。

  2. 获取数据

    获取数据包括获取原始数据,以及从原始数据中经过特征工程,从原始数据中提取训练、测试数据。数据数量不要有多个数量级的差距。不仅如此还要对评估数据的量级,样本数量、特征数量,估算训练模型对内存的消耗。

  3. 特征工程

    特征工程包括从原始数据中特征构建、特征提取、特征选择。特征工程做的好能发挥原始数据的最大效力,往往能够使得算法的效果和性能得到显著的提升,有时能使简单的模型的效果比复杂的模型效果好。

  4. 模型训练、诊断、调优

    对算法的理解、调节参数,使模型达到最优。模型诊断中至关重要的是判断过拟合、欠拟合,常见的方法是绘制学习曲线,交叉验证。通过增加训练的数据量、降低模型复杂度来降低过拟合的风险,提高特征的数量和质量、增加模型复杂来防止欠拟合。诊断后的模型需要进行进一步调优, 调优后的新模型需要重新诊断,这是一个反复迭代不断逼近的过程,需要不断的尝试,进而达到最优的状态。

  5. 模型预测,获得结果

    我们通过训练数据集得到模型,然后将想要预测的数据加入模型中进行测试,得到测试结果。

这就是我们大致的机器学习流程。

1.3 数据处理与特征工程

  • 数据处理

“数据决定了机器学习的上限,而算法只是尽可能逼近这个上限”,这句话很好的阐述了数据在机器学习中的重要性。大部分直接拿过来的数据都是特征不明显的、没有经过处理的或者说是存在很多无用的数据,那么需要进行一些特征处理,特征的缩放等等,满足训练数据的要求。

数据处理主要解决的问题是:

  1. 数据量不足

  2. 训练数据不具备代表性

  3. 质量差的数据

  4. 特征筛选

  • 特征工程

机器学习的关键是 提取出一组好的用来训练的特征集,这个过 程叫特征工程,包括:

  1. 特征选择 从现有特征中选择最有用的特征进行训练

  2. 特征提取 将现有特征进行整合,产生更有用的特征,比 如降维算法

  3. 通过收集 新数据创造新特征

2 Scikit-learn 与特征工程

2.1 Scikit-learn概述

2007年发布以来,scikit-learn已经成为最给力的Python机器学习库(library)了。scikit-learn支持的机器学习算法包括分类,回归,降维和聚类。还有一些特征提取(extracting features)、数据处理(processing data)和模型评估(evaluating models)的模块。作为Scipy库的扩展,scikit-learn也是建立在Python的NumPy和matplotlib库基础之上。NumPy可以让Python支持大量多维矩阵数据的高效操作,matplotlib提供了可视化工具,SciPy带有许多科学计算的模型。

  • Python语言的机器学习工具
  • 可在不同的上下文中重用
  • 基于NumPy、SciPy和matplotlib构建
  • 开源、商业可用 - BSD许可

2.2 数据的特征工程

从数据中抽取出来的对预测结果有用的信息,通过专业的技巧进行数据处理,是的特征能在机器学习算法中发挥更好的作用。优质的特征往往描述了数据的固有结构。 最初的原始特征数据集可能太大,或者信息冗余,因此在机器学习的应用中,一个初始步骤就是选择特征的子集,或构建一套新的特征集,减少功能来促进算法的学习,提高泛化能力和可解释性。

2.2.1 特征工程的意义

  • 更好的特征意味着更强的鲁棒性
    • 所谓“鲁棒性”,指控制系统在一定(结构,大小)的参数摄动下,维持其它某些性能的特性。
  • 更好的特征意味着只需用简单模型
  • 更好的特征意味着更好的结果

2.2.2 特征处理

特征工程中最重要的一个环节就是特征处理,特征处理包含了很多具体的专业技巧

  • 单个特征
    • 归一化
    • 标准化
    • 缺失值
  • 多个特征
    • 降维
功能 说明
StandardScaler 无量纲化 标准化,基于特征矩阵的列,将特征值转换至服从标准正态分布
MinMaxScaler 无量纲化 区间缩放,基于最大最小值,将特征值转换到[0, 1]区间上
Normalizer 归一化 基于特征矩阵的行,将样本向量转换为“单位向量”
Binarizer 二值化 基于给定阈值,将定量特征按阈值划分
OneHotEncoder 哑编码 将定性数据编码为定量数据
Imputer 缺失值计算 计算缺失值,缺失值可填充为均值等
PolynomialFeatures 多项式数据转换 多项式数据转换
FunctionTransformer 自定义单元数据转换 使用单变元的函数来转换数据函数来转换数据

2.2.3 特征选择与特征抽取

如果说特征处理其实就是在对已有的数据进行运算达到我们目标的数据标准。特征抽取则是将任意数据格式(例如文本和图像)转换为机器学习的数字特征。而特征选择是在已有的特征中选择更好的特征。后面会详细介绍特征选择主要区别于降维。

2.3 数据的来源与类型

  • 数据的来源

通过爬虫爬取、购买数据、网上的开放数据等。

  • 数据的类型

按照机器学习的数据分类我们可以将数据分成:

  • 标称型:标称型目标变量的结果只在有限目标集中取值,如真与假
  • 数值型:数值型目标变量则可以从无限的数值集合中取值,

按照数据的本身分布特性

  • 离散型【无规律】
  • 连续型【有规律】
    • 离散变量是指其数值只能用自然数或整数单位计算的则为离散变量。例如,班级人数
    • 连续型数据是指在指定区间内可以是任意一个数值。例如,票房数据

2.4 数据的特征抽取

现实世界中多数特征都不是连续变量,比如分类、文字等,为了对非连续变量做特征表述,需要对这些特征做数学化表述,因此就用到了特征提取. sklearn.feature_extraction提供了特征提取的很多方法

2.4.1 分类特征提取变量

我们将城市和环境作为字典数据,来进行特征的提取。将映射列表转换为Numpy数组。

from sklearn.feature_extraction import DictVectorizer
# sparse 是否转换为scipy.sparse矩阵表示,默认开启,如果结果不用toarray,请开启sparse= False
onehot = DictVectorizer(sparse= False)
instances = [{'city': '北京', 'temperature': 100}, {'city': '上海', 'temperature': 60}, {'city': '深圳', 'temperature': 30}]

# 应用并转化映射列表X,y为目标类型
X = onehot.fit_transform(instances)

# 将Numpy数组或scipy.sparse矩阵转换为映射列表
Y = onehot.inverse_transform(X)

2.4.2 文本特征处理(只限英文)

文本的特征提取应用于很多方面,比如说文档分类、垃圾邮件分类和新闻分类。那么文本分类是通过词是否存在、以及词的概率(重要性)来表示。

from sklearn.feature_extraction.text import CountVectorizer


# 构建文章【英文】

content = ['This is the first document.', 'This is the second second document.', 'And the third one.', 'Is this the first document? i x y']

#构建实例
con_vet = CountVectorizer()
#进行提取词语
x = con_vet.fit_transform(content)

print(x)   # (0, 1) 1 (文章下标,分词下标) 词在文章中出现的次数  sparse矩阵
print(x.toarray()) # 将 sparse矩阵 转化为 数组

# 获取提取到词语
names = con_vet.get_feature_names()
print(names) # 提取到的词

  • 处理中文,我们可以使用jieba分词,进行分词之后,进行处理
# pip install jieba

import jieba
from sklearn.feature_extraction.text import CountVectorizer

# 构建文章【中文】

content = ["今天阳光真好","我要去看北京天安门","逛完天安门之后我要去王府井","吃烤蝎子与烤蜈蚣","晚上去后海蹦个迪"]

content_list = []

for tmp in content:
    # 使用精确模式进行分词 cut_all默认为精确模式
    res = jieba.cut(tmp,cut_all= False)
    res_str = ','.join(res)
    content_list.append(res_str)


#构建实例
con_vet = CountVectorizer()

#进行提取词语
x = con_vet.fit_transform(content_list)

print(x)   # (0, 1)	1 (文章下标,分词下标) 词在文章中出现的次数  sparse矩阵
print(x.toarray()) # 将 sparse矩阵 转化为 数组

# 获取提取到词语
names = con_vet.get_feature_names()
print(names) # 提取到的词

2.5 特征预处理

  • 单个特征
    • 归一化

      归一化首先在特征(维度)非常多的时候,可以防止某一维或某几维对数据影响过大,也是为了把不同来源的数据统一到一个参考区间下,这样比较起来才有意义,其次可以程序可以运行更快。】常用的方法是通过对原始数据进行线性变换把数据映射到[0,1]之间,变换的函数为:

    X=xminmaxmin X^{'}{=}\frac{x-min}{max-min}

    ​ 其中min是样本中最小值,max是样本中最大值,注意在数据流场景下最大值最小值是变化的,另外,最 大值与最小值非常容易受异常点影响,所以这种方法鲁棒性较差,只适合传统精确小数据场景。

    • 标准化

      将数据转化为同一量级,避免量级对结果产生不利的影响①离差标准化②标准差标准化③小数定标标准化常用的方法是z-score标准化,经过处理后的数据均值为0,标准差为1,处理方法是:

    X=xμσ X^{'}{=}\frac{x-\mu}{\sigma}

    ​ 其中μ是样本的均值,σ是样本的标准差,它们可以通过现有的样本进行估计,在已有的样本足够多的情 况下比较稳定,适合嘈杂的数据场景

    • 缺失值

      由于各种原因,许多现实世界的数据集包含缺少的值,通常编码为空白,NaN或其他占位符。然而,这样的数据集与scikit的分类器不兼容,它们假设数组中的所有值都是数字,并且都具有和保持含义。使用不完整数据集的基本策略是丢弃包含缺失值的整个行和/或列。然而,这是以丢失可能是有价值的数据(即使不完整)的代价。更好的策略是估算缺失值,即从已知部分的数据中推断它们。

      • 删除 ----会对数据产生很大的影响,造成数据缺失,所以在数据大部分为缺失值,才使用删除法;
      • 填充 — 填充之后对结果影响不大的情况,可以使用(均值,中位数,众数);
      • 插值(线性插值;多项式插值;样条插值)
  • 多个特征
    • PCA(Principal component analysis),主成分分析。特点是保存数据集中对方差影响最大的那些特征,PCA极其容易受到数据中特征范围影响,所以在运用PCA前一定要做特征标准化,这样才能保证每维度特征的重要性等同。
    class PCA(n_components, whiten, svd_solver)
       """
       主成成分分析
       :param n_components: int, float, None or string
           这个参数可以帮我们指定希望PCA降维后的特征维度数目。最常用的做法是直接指定降维到的维度数目,此时n_components是一个大于1的整数。
           我们也可以用默认值,即不输入n_components,此时n_components=min(样本数,特征数)
    
       :param whiten: bool, optional (default False)
          判断是否进行白化。所谓白化,就是对降维后的数据的每个特征进行归一化。对于PCA降维本身来说一般不需要白化,如果你PCA降维后有后续的数据处理动作,可以考虑白化,默认值是False,即不进行白化
    
       :param svd_solver:
          选择一个合适的SVD算法来降维,一般来说,使用默认值就够了。
        """
    

2.6 数据特征选择

降维本质上是从一个维度空间映射到另一个维度空间,特征的多少别没有减少,当然在映射的过程中特征值也会相应的变化。举个例子,现在的特征是1000维,我们想要把它降到500维。降维的过程就是找个一个从1000维映射到500维的映射关系。原始数据中的1000个特征,每一个都对应着降维后的500维空间中的一个值。假设原始特征中有个特征的值是9,那么降维后对应的值可能是3。而对于特征选择来说,有很多方法:

  • Filter(过滤式):VarianceThreshold
  • Embedded(嵌入式):正则化、决策树
  • Wrapper(包裹式)

其中过滤式的特征选择后,数据本身不变,而数据的维度减少。而嵌入式的特征选择方法也会改变数据的值,维度也改变。Embedded方式是一种自动学习的特征选择方法,后面讲到具体的方法的时候就能理解了。

特征选择主要有两个功能:

  • 减少特征数量,降维,使模型泛化能力更强,减少过拟合
  • 增强特征和特征值之间的理解

3 sklearn 数据集

sklearn中的数据集类,模块包括用于加载数据集的实用程序,包括加载和获取流行参考数据集的方法。它还具有一些人工数据生成器。sklearn数据集源代码

3.1 数据集概述

datasets.load_*() # 获取小规模数据集,数据包含在datasets里

# 获取大规模数据集,需要从网络上下载,函数的第一个参数是data_home,表示数据集下载的目录,默认是 ~/scikit_learn_data/,要修改默认目录,可以修改环境变量SCIKIT_LEARN_DATA
datasets.fetch_*()

datasets.make_*() # 本地生成数据集
  • load*和 fetch* 函数返回的数据类型是 datasets.base.Bunch,本质上是一个 dict,它的键值对可用通过对象的属性方式访问。主要包含以下属性:
    • data:特征数据数组,二维 数组
    • target:标签数组,一维 数组
    • DESCR:数据描述
    • feature_names:特征名
    • target_names:标签名
  • 数据集目录可以通过datasets.get_data_home()获取,clear_data_home(data_home= None)删除所有下载数据
    • datasets.get_data_home(data_home= None)

      返回scikit学习数据目录的路径。这个文件夹被一些大的数据集装载器使用,以避免下载数据。默认情况下,数据目录设置为用户主文件夹中名为“scikit_learn_data”的文件夹。或者,可以通过“SCIKIT_LEARN_DATA”环境变量或通过给出显式的文件夹路径以编程方式设置它。’〜'符号扩展到用户主文件夹。如果文件夹不存在,则会自动创建。

    • sklearn.datasets.clear_data_home(data_home= None)

      删除存储目录中的数据

3.2 获取小数据集

  • 加载虹膜数据集
名称 数量
类别 3
特征 4
样本数量 150
每个类别数量 50
from sklearn.datasets import load_iris
data = load_iris()
# 查看数据
data
  • 加载数字数据集
名称 数量
类别 10
特征 64
样本数量 1797
from sklearn.datasets import load_digits

digits = load_digits()

# 数据格式
digits.data.shape
# 标签
digits.target
# 标签名
digits.target_names
# 数据展示
digits.images
  • 加载波士顿房价数据集
名称 数量
目标类别 5-50
特征 13
样本数量 506
from sklearn.datasets import load_boston
boston = load_boston()

# 数据格式
boston.data.shape

# 数据列名
boston.feature_names

3.3 获取大数据集

  • 加载20个新闻组数据集中的文件名和数据【14兆左右】
参数说明
subset ‘train’或者’test’,‘all’,可选,选择要加载的数据集:训练集的“训练”,测试集的“测试”,两者的“全部”,具有洗牌顺序
data_home 可选,默认值:无,指定数据集的下载和缓存文件夹。如果没有,所有scikit学习数据都存储在’〜/ scikit_learn_data’子文件夹中
categories 无或字符串或Unicode的集合,如果没有(默认),加载所有类别。如果不是无,要加载的类别名称列表(忽略其他类别)
shuffle 是否对数据进行洗牌
random_state numpy随机数生成器或种子整数
download_if_missing 可选,默认为True,如果False,如果数据不在本地可用而不是尝试从源站点下载数据,则引发IOError
from sklearn.datasets import fetch_20newsgroups

# 'train'或者'test','all',可选,选择要加载的数据集:训练集的“训练”,测试集的“测试”,两者的“全部”,具    有洗牌顺序
# 是否对数据进行洗牌
# numpy随机数生成器或种子整数
data_test = fetch_20newsgroups(subset= 'test', shuffle= True, random_state= 42)

data_train = fetch_20newsgroups(subset= 'train', shuffle= True, random_state= 42)

3.4 获取本地生成数据

  • 生成本地分类数据
参数说明
n_samples int,optional(default = 100),样本数量
n_features:int 可选(默认= 20),特征总数
n_classes 可选(default = 2),类(或标签)的分类问题的数量
random_stat RandomState实例或无,可选(默认=无) 如果int,random_state是随机数生成器使用的种子; 如果RandomState的实例,random_state是随机数生成器; 如果没有,随机数生成器所使用的RandomState实例np.random
from sklearn.datasets.samples_generator import make_classification
X,y= datasets.make_classification(n_samples= 100000, n_features= 20, n_informative= 2, n_redundant= 10, random_state= 42)
  • 生成本地回归数据
参数说明
n_samples int,optional(default = 100),样本数量
n_features int,optional(default = 100),特征数量
coef boolean,optional(default = False),如果为True,则返回底层线性模型的系数
random_state int,RandomState实例或无,可选(默认=无) 如果int,random_state是随机数生成器使用的种子; 如果RandomState的实例,random_state是随机数生成器; 如果没有,随机数生成器所使用的RandomState实例np.random
from sklearn.datasets.samples_generator import make_regression
X, y = make_regression(n_samples= 200, n_features= 5000, random_state= 42)

3.5 estimator的工作流程

在sklearn中,估计器(estimator)是一个重要的角色,分类器和回归器都属于estimator。在估计器中有有两个重要的方法是fit和transform。

  • fit方法用于从训练集中学习模型参数
  • transform用学习到的参数转换数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NsbAHL4F-1574825755329)(image\1574418658501.png)]

使用sklearn工具可以方便地进行特征工程和模型训练工作,特征处理类都有三个方法fit、transform和fit_transform,从命名中可以看到,fit_transform方法是先调用fit然后调用transform,我们只需要关注fit方法和transform方法即可。

transform方法主要用来对特征进行转换。从可利用信息的角度来说,转换分为无信息转换和有信息转换。无信息转换是指不利用任何其他信息进行转换,反之,有信息转换利用其他信息进行转换。

fit方法的主要工作是获取特征信息和目标值信息,通过分析特征和目标值,提取有价值的信息,对于转换类来说是某些统计量,对于模型来说可能是特征的权值系数等。

4 机器学习流程实践

  • 真实数据观察全局
  • 选择性能指标、检查假设
  • 获取数据
    • 创建工作区,快速查看数据结构,创建测试集
  • 从可视化中探索数据
    • 将数据可视化、寻找相关性、试验不同的属性组合
  • 机器学习前的数据准备
    • 数据清理、自定义转换器、特征缩放、转换流水线
  • 选择训练模型
    • 评估训练集、交叉验证、分析最佳模型及其错误、测试集评
  • 模型调优
  • 分析最佳模型和测试集评估
  • 系统维护和监控

4.1 观察数据、选择指标、检查假设

4.1.1 观察数据

  • 机器学习最好使用真实数据,而不仅仅是使用人工数据。可以选择开源数据集。
  • 使用加州人口的普查的数据建立起加州的房价模型。数据中包含很多指标:每个街区人口的数量,收入中位数等,
  • 加州房产价格数据集

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TWnmPZqc-1574825755330)(image/1574214898683.png)]

4.1.2 选择指标

  • 了解建立模型的目的,盈利模式,从而选择相应的指标。因为模型不是最终目的,这些问题将决定你怎么设定问题,选择什么算法,使用什么测量方式来衡量模型的性能。
  • 划定问题:监督或非监督,还是强化学习?这是个分类任务、回归任务,还是其它的?【本章特征工程主要是处理数据,具体的机器学习划分在下一模块‘机器学习’】
    • 这是一个典型的监督学习任务,因为你要使用的是有标签的训练样本(每个实例都有预定的产出,即分区的房价中位数)。并且,这是一个典型的回归任务,因为你要预测一个值。讲的更细些,这是一个多变量回归问题,因为系统要使用多个变量进行预测(要使用分区的人口,收入中位数等等)。
  • 选择性能指标。回归问题的典型指标是均方根误差(RMSE)。均方根误差测量的是系统预测误差的标准差。

4.1.3 检查假设

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BBsylL2t-1574825755332)(image/1574216445901.png)]

最好列出并核对迄今(你或其他人)作出的假设。,这样可以尽早发现严重的问题。例如,你的系统输出的分区房价,会传入到下游的机器学习系统,我们假设这些价格确实会被当做分区房价使用。但是如果下游系统实际上将价格转化成了分类(例如,便宜、中等、昂贵),然后使用这些分类,而不是使用价格。这样的话,获得准确的价格就不那么重要了,你只需要得到合适的分类。问题相应地就变成了一个分类问题,而不是回归任务。

4.2 获取数据

4.2.1 创建工作区

推荐创建一个隔离环境,这样可以在库版本不冲突的情况下处理不同的项目

  • 用pip安装virtualenv
pip install virtualenv
  • 创建目录,并进入文件夹
mkdir myproject
cd myproject
  • 创建一个独立的python环境,命名为venv【命令virtualenv就可以创建一个独立的Python运行环境,我们还加上了参数--no-site-packages,这样,已经安装到系统Python环境中的所有第三方包都不会复制过来,这样,我们就得到了一个不带任何第三方包的“干净”的Python运行环境。】
virtualenv --no-site-packages venv
  • 激活环境
activate myproject
  • 安装模块
pip install jupyter numpy pandas scipy matplotlib  scikit-learn
  • 测试安装模块
python -c 'import jupyter, numpy, pandas, scipy, matplotlib, scikit-learn'
  • 退出独立环境
deactivate 

4.2.2 查看数据,创建测试集

  • 数据获取
## 获取数据
import os
import pandas as pd
def load_housing_data(housing_path = './'):
    csv_path = os.path.join(housing_path, 'housing.csv')
    return pd.read_csv(csv_path)
housing = load_housing_data() # 读取数据
housing.head() # 查看前五条数据
housing.shape  # 查看数据结构
housing.info() # 查看数据集的简单描述, 得到每个属性类型和非空值数据量

#  total_bedrooms  20433 有一部分空值
housing['ocean_proximity'].value_counts()  # 查看多少种分类,5 种分类,获得每种分类下有少区域

housing.describe()  # 显示数值属性摘要

# 使用直方图展示数据
import matplotlib.pyplot as plt
housing.hist(bins = 50, figsize = (20, 15))
plt.show()
  • 创建测试集,训练集
#训练集用于模型训练 占整个数据集的80% ,测试占20%
import numpy as np
def split_train_test(data, test_radio):
    np.random.seed(42)
    indices = np.random.permutation(len(data)) #对原来的数组进行重新洗牌,随机打乱原来的元素顺序
    test_set_size = int(len(data) * test_radio)
    test_indices = indices[:test_set_size]
    train_indices = indices[test_set_size:]
    return data.iloc[train_indices], data.iloc[test_indices]
train_set, test_set = split_train_test(housing, 0.2)



#对样本设置唯一的标识符,对标识符取hash值, 去hash的最后一个字节, 值小于等于51 ,256*20%,放入测试集,hash值相同,对象不一定相同,hash不同,对象一定不同
import hashlib

def test_set_check(identifier, test_radio, hash = hashlib.md5):
     #返回摘要,作为二进制数据字符串
    return hash(np.int64(identifier)).digest()[-1] < 256 * test_radio 

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]

housing_with_id = housing.reset_index()  # 使用行索引作为标识符 ID

housing_with_id.head() # 查看数据

train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, 'index') # 返回测试集,训练集

# 基于行索引,不能删除和中间插入数据,只能末尾插入,否则行索引会变
# 寻找稳定特征来创建唯一标识符,
housing_with_id['id'] = housing['longitude'] * 1000 + housing['latitude']
housing_with_id.head()
train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, 'id')



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

# 如果数据集一直更新,使用最后一种,数据集一直不变使用最基础就行,

4.3 从可视化中探索数据

4.3.1 可视化数据

将数据可视化、寻找相关性、试验不同的属性组合,将数据进行可视化展示,可以帮助我们快速的了解数据类型。

  • 分层采样

分层抽样(保留类别比例的采样方式) :先将总体按照某种特征分为若干次级(层),然后从每一层内进行单纯的随机抽样,组成一个样本群。

抽样偏差:调查公司给1000个人来调研几个问题,不会在电话簿中随机查找1000人,他们视图确保1000人代表全体人口,美国人,51.3女性,48.7男性,你的调查应该维持这一比例,513名女性,487男性,这就是分层采样,人口划分为均匀的子集,每个子集称为一层,然后从每层中抽取正确的实例数量
# 希望测试集能够代表整个数据集中各种不同类型的收入
  #1. 创建一个收入类别属性 
  #2. 不应该数据分层太多,但每一层应该有足够的数据量
  #3. 将收入中位数除以1.5,限制收入类别数量,使用ceil取整,得到离散类别,将大于5的列别合并为类别5

#中位数除以1.5,限制收入类别数量,使用ceil取整
housing['income_cat'] = np.ceil(housing['median_income'] / 1.5)
housing['income_cat'].head(20) # 查看数据

#将大于5的列合并为类别5
housing['income_cat'].where(housing['income_cat'] < 5, 5.0, inplace= True )
housing['income_cat'].head(20)

housing['income_cat'] = pd.cut(housing['median_income'], bins= [0., 1.5, 3.0, 4.5, 6., np.inf], labels = [1,2, 3, 4, 5]) # 把连续值转换成类别标签  

housing['income_cat'].head(20)

# 收入类别直方图
housing['income_cat'].hist()

# 根据收入类别进行分层采样
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(housing, housing['income_cat']):
    strat_train_set = housing.loc[train_index]
    strat_test_set = housing.loc[test_index]
strat_train_set.shape
strat_train_set.head(20)


# 查看所有住房数据根据收入类别比例分布,收入占类别百分比
strat_test_set['income_cat'].value_counts() / len(strat_test_set)  # 分层抽样测试集合

housing['income_cat'].value_counts() / len(housing)  # 完整数据集合

def income_cat_proportions(data):
    return data["income_cat"].value_counts() / len(data)

train_set, test_set = train_test_split(housing, test_size= 0.2, random_state= 42)

compare_props = pd.DataFrame({
    "全部数据": income_cat_proportions(housing),
    "分层抽样": income_cat_proportions(strat_test_set),
    "随机抽样": income_cat_proportions(test_set),
}).sort_index()
compare_props["随机. %error"] = 100 * compare_props["随机抽样"] / compare_props["全部数据"] - 100
compare_props["分层. %error"] = 100 * compare_props["分层抽样"] / compare_props["全部数据"] - 100



# 把测试集已经处理完成,用分层抽样的训练集数据来进行分析,分层抽样的测试集的比例分布与完整数据集中的分布几乎-致。
  • 将地理数据可视化
# 一般来说,我们的大脑非常善于从图片中发现模式,但是你需要玩转可视化参数,才能让这些模式凸显出来
# 建立一个各区域的分布图,以便于数据可视化
housing.plot(kind= 'scatter', x= 'longitude', y= 'latitude')

# 将alpha 设置为 0.1 , 可以看出高密度数据点的位置
housing.plot(kind= 'scatter', x= 'longitude', y= 'latitude', alpha= 0.1)

# 我们的大脑非常善于从图片中发现模式,但是需要玩转可视化参数才能让这些模式凸显出来
housing.plot(kind= "scatter", x= "longitude", y= "latitude", alpha= 0.4,
    s= housing["population"] / 100, label= "population", figsize= (10, 7),
    c= "median_house_value", cmap= plt.get_cmap("jet"), colorbar= True,
    sharex= False)

# 加载本地图片
import matplotlib.image as mpimg
california_img = mpimg.imread('./california.png')

# 设置参数,标题
housing.plot(kind= "scatter", x= "longitude", y= "latitude", alpha= 0.4,
    s= housing["population"] / 100, label= "population", figsize= (10, 7),
    c= "median_house_value", cmap= plt.get_cmap("jet"), colorbar= False,
    sharex= False)
# 展示图片,extent参数: x轴的起始点,y轴的起始点
plt.imshow(california_img, extent= [-124.55, -113.80, 32.45, 42.05], alpha= 0.5,
           cmap= plt.get_cmap("jet"))
# x,y轴显示
plt.ylabel("Latitude", fontsize= 14)
plt.xlabel("Longitude", fontsize= 14)

# 房屋价格中位数
prices = housing["median_house_value"]

# 最大值 ,最小值之间11等分
tick_values = np.linspace(prices.min(), prices.max(), 11)
cbar = plt.colorbar()
# 转化可视化输出
cbar.ax.set_yticklabels(["$%dk"%(round(v / 1000)) for v in tick_values], fontsize= 14)
cbar.set_label('Median House Value', fontsize= 16)

plt.legend(fontsize= 16)

plt.show()

# 从图片中告诉我们,房屋价格与地理位置靠海,和人口密度息息相关

4.3.2 寻找相关度

  • 统计方法
# corr() 计算出每对特征之间的相关系数, 称为皮尔逊相关系数
corr_matrix =  housing.corr()
corr_matrix

corr_matrix['median_house_value'].sort_values(ascending= False)

# 相关系数范围 从 -1 变化到 1,越接近1 ,表示越强的正相关,,比如,当收入中位数上升时,房价中位数也趋于上升,
# 当系数接近-1 ,则表示有强烈的负相关,纬度和房价中位数之间出现轻微的负相关,越往北走,房价倾向于下降,
# 系数靠近0 ,说明二者之间没有线性相关性,
# 相关系数仅检测 线性相关性 ,如果x上升,y上升或者下降,可能会彻底遗漏非线性相关性,例如 如果x接近于0,y上升
  • 图示方法
# 使用padans scatter_matrix 函数,可以绘制特征之间的相关性
from pandas.plotting import scatter_matrix

attributes = ['median_house_value', 'median_income', 'total_rooms', 'housing_median_age']
scatter_matrix(housing[attributes], figsize= (12, 8) )

housing.plot(kind= 'scatter', x= 'median_income', y= 'median_house_value', alpha= 0.1)
plt.axis([0, 16, 0, 550000])

# 50万美元是一条清晰的线,被设置上限了, 45w,35w,28w,也有虚线, 可能是异常值,我可以在后期把数据删除掉
  • 在给机器学习算法输入数据之前,我们识别出了一些异常数据,需要提前清理掉,也发现了不同属性之间的某些联系,跟目标属性相关的联系
  • 某些属性分布显示出了明显的“重尾”分布,对进行转换处理,计算其对数
  • 在给机器学习算法输入数据之前,最后一件事儿,尝试各种属性的组合
  • 每个项目历程都不一样,大致思路都相识

4.3.3 试验不同的组合

在准备给机器学习算法输入数据之前,我们还需要做最后一件事,尝试各种属性的组合。

  • 比如:如果不知道一个地区有多少家庭,那么知道一个地区的房间总数’也没有什么用。我们真正想知道的是,一个家庭的房间的数量。
  • 或者,单看卧室总数这个属性本身,也没有什么意义。你可能是想拿它和“房间总数来对比,或者拿来同“每个家庭的人口数”这个属性结合着使用。
## 试验不同特征的组合
housing.head()
housing['rooms_per_household'] = housing['total_rooms'] / housing['households']
housing['bedrooms_per_room'] = housing['total_bedrooms'] / housing['total_rooms']
housing['population_per_household'] = housing['population'] / housing['households']

# 关联矩阵
corr_matrix = housing.corr()
corr_matrix['median_house_value'].sort_values(ascending= False)

# 新的属性较之“房间总数”、‘卧室总数等属性,与房价中位数的相关性要好很多。

5 机器学习数据准备

5.1 加载数据、分离测试集

## 获取数据
import os
import pandas as pd
def load_housing_data(housing_path = './'):
    csv_path = os.path.join(housing_path, 'housing.csv')
    return pd.read_csv(csv_path)
housing = load_housing_data() # 读取数据
housing.head() # 查看前五条数据
housing.shape  # 查看数据结构
housing.info() # 查看数据集的简单描述, 得到每个属性类型和非空值数据量


#中位数除以1.5,限制收入类别数量,使用ceil取整
housing['income_cat'] = np.ceil(housing['median_income'] / 1.5)
housing['income_cat'].head(20) # 查看数据

#将大于5的列合并为类别5
housing['income_cat'].where(housing['income_cat'] < 5, 5.0, inplace= True )
housing['income_cat'].head(20)

housing['income_cat'] = pd.cut(housing['median_income'], bins= [0., 1.5, 3.0, 4.5, 6., np.inf], labels= [1,2, 3, 4, 5]) # 把连续值转换成类别标签  

housing['income_cat'].head(20)

# 收入类别直方图
housing['income_cat'].hist()

# 根据收入类别进行分层采样
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(housing, housing['income_cat']):
    strat_train_set = housing.loc[train_index]
    strat_test_set = housing.loc[test_index]
strat_train_set.shape
strat_train_set.head(20)


# 查看所有住房数据根据收入类别比例分布,收入占类别百分比
strat_test_set['income_cat'].value_counts() / len(strat_test_set)  # 分层抽样测试集合

housing['income_cat'].value_counts() / len(housing)  # 完整数据集合

def income_cat_proportions(data):
    return data["income_cat"].value_counts() / len(data)

train_set, test_set = train_test_split(housing, test_size= 0.2, random_state= 42)



# 分离测试集
# 得到一个分层抽样代表全局数据集的  训练集和测试集
strat_train_set.head()

# 将数据集的数据和标签分开 
housing_labels = strat_train_set['median_house_value'].copy()

housing = strat_train_set.drop('median_house_value', axis= 1)

housing.head()

housing.info()

5.2 数据清洗

大多数机器学算法无法在缺失的特征上工作,创建一些函数辅助,total_bedrooms有缺失,

  • 放弃这些区域
  • 放弃这个属性
  • 将缺失值设置为某个值,(0,平均数或者中位数都可以)
# 创建一个imputer实例,指定你要用属性中的中位数值替换该属性的缺失值 
from sklearn.preprocessing import Imputer as SimpleImputer

imputer = SimpleImputer(strategy = 'median')

# 使用fit() 方法将 imputer实例适配到训练集
housing_num = housing.drop('ocean_proximity', axis= 1)
imputer.fit(housing_num)

# 获得特征中位数【内置方法】
imputer.statistics_

#获得中位数
housing_num.median().values

# 每个特征的值
X = imputer.transform(housing_num)

# 再次检测一下,观察是否存在缺失值
housing_tr = pd.DataFrame(X, columns= housing_num.columns, index= housing_num.index)
housing_tr.head()
# info也可以查看
housing_tr.info()

#scikit-Learn【具体见下一章机器学习】
#* 一致性
#   1. 估算器
#       比如:各种机器学习算法
#       fit()执行估算器
#
#   2. 转换器  
#       比如:LabelBinarizer
#       transform()  执行转换数据集
#       fit_transform()  先估算,再转换
#   3. 预测器
#       predict()  对给定的新数据集进行预测
#       score()    评估测试集的预测质量
#* 检查
#    1. imputer.strategy_学习参数通过公共实例变量访问

5.3 处理文本和分类属性

大部分的机器学习算法,更容易更数值打交道,所以我们先将文本转化为数字。

housing_cat = housing[['ocean_proximity']]
housing_cat.head(10)


housing['ocean_proximity'].value_counts()

# 将文本转化成对应的数字分类 , 使用转换器
from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder()
housing_cat = housing['ocean_proximity']
housing_cat_encoder = encoder.fit_transform(housing_cat) # 将字符转化为数字
housing_cat_encoder
# 私有属性,可以用来查看字符属性
encoder.classes_

# 1. 机器学习算法会以为两个相近的数字比远的数字更相似,比如0,4 比  0,1相似度更高,
# 2. 创建独热编码,当 <1H ,第0个属性为1,其余都为0, 列别是InLand时候,另一个属性为1,其余为0,1为热,0为冷,独热编码
# 3. OneHotEncoder编码器, fit_trans需要二维数组, 转换housing_cat

# 我们按照上述的方式进行转化,数据编码后的标签具有一定的依赖关系,我们是有onehot编码
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder()
housing_cat_encoder

housing_cat_encoder.reshape(-1, 1)

housing_cat_hot = encoder.fit_transform(housing_cat_encoder.reshape(-1, 1))
housing_cat_hot

# 从稀疏矩阵 转换成 numpy【上述是一个稀疏矩阵, 而不是numpy的一个二 维数组。 稀疏矩阵选择仅存储非零元素的位置。但是你依旧可以像使用一个普通的二维数组那样来使用他。使用toarray()即可以转化为二维数】
housing_cat_hot.toarray()

# 一次完成两个转换 文本--整数类型--独热类型
housing_cat
from sklearn.preprocessing import LabelBinarizer
encoder = LabelBinarizer()
housing_cat_1hot = encoder.fit_transform(housing_cat)
# 返回二维数组
housing_cat_1hot 

# 输出稀疏矩阵
from sklearn.preprocessing import LabelBinarizer
encoder = LabelBinarizer(sparse_output= True)
housing_cat_1hot = encoder.fit_transform(housing_cat)
housing_cat_1hot

5.4 自定义转化器

#希望自定义的转换器与skleam自身的功能无缝衔接,所以需要创建一个类,然后应用以下三个方法: fit、transform、fit_transform.

from sklearn.base import BaseEstimator, TransformerMixin
rooms_ix, bedrooms_ix, population_ix, household_ix = [list(housing.columns).index(col) for col in ("total_rooms", "total_bedrooms", "population", "households")]
rooms_ix, bedrooms_ix, population_ix, household_ix 

class CombinedAttributesAdder(BaseEstimator, TransformerMixin):
    def __init__(self, add_bedrooms_per_room = True):
        self.add_bedrooms_per_room = add_bedrooms_per_room
    def fit(self, X, y= None):
        return self
    def transform(self, X,  = None):
        rooms_per_household = X[:, rooms_ix] / X[:, household_ix]
        population_per_household = X[:, population_ix] / X[:, household_ix]
        if self.add_bedrooms_per_room:
            bedrooms_per_room = X[:, bedrooms_ix] / X[:, rooms_ix]
            return np.c_[X, rooms_per_household,population_per_household, bedrooms_per_room]
        else:
            return np.c_[X, rooms_per_household,population_per_household]
        
# 传入自己的变量     
attr_adder = CombinedAttributesAdder(add_bedrooms_per_room= False)
housing_extra_attribs = attr_adder.transform(housing.values)

# 查看数据
housing_tr = pd.DataFrame(housing_extra_attribs)
housing_tr.head()

# 给数据添加列名
housing_extra_attribs = pd.DataFrame(
    housing_extra_attribs,
    columns=list(housing.columns)+["rooms_per_household", "population_per_household"],
    index=housing.index)
housing_extra_attribs.head()

5.5 特征缩放

最重要也是最需要应用在数据上的转换器,就是特征缩放,输入数值属性有很大的比例差异,会导致机器学习算法性能表现不佳

  • 注意:目标值通常不进行缩放。
  • 常见的缩放的方法:最小-最大缩放、标准化
    • 最小最大缩放(MinMaxScaler)又叫归一化,将值重新缩放到0到1之间,将值减去最小值并除以最大值和最小值差,如果你不希望是0到1,可以调整超参数feature_range进行更改
    • 标准化减去平均值 ,所以标准化均值总是0,然后除以方差,结果的分布具备单位方差,不同于归一化,标准化不将值绑定到特定范围,受异常值影响小
  • 注意:和所有的转换器一样, 缩放器仅用来拟合训练集,而不是完整的数据集。

5.6 转换流水线

# 许多数据转换的步骤需要以正确的顺序来执行, PipeLine来支持这样的转换
# pipline构造函数会通过一系列名称/估算器的配对来定义步骤的序列,必须是转换器,必须有fit_fransform()方法
# 调用流水芡的fit方法时,会在所有转换器上按照顺序依次调用fit_transform(),将一个调用的输出作为参数传递给下一个调用方法,直到传递到最终
# 估算器,只会调用fit方法
  • 估算器:能够根据数据集对某系参数进行估算的任意对象都可以被称为估算器。估算由fit方法执行,他只需要一个数据集作为参数。 引导估算过程的任何其他参数都算作是超参数,它必须被设置为一个实例变量。

  • 转换器:有些估算器也可以转换数据集,这些被称为转换器。由transform()方法和作为参数的待转换数据集一起执行。返回转换后的数据集。这些转换器是依赖于学习的参数的, 如imputer.所有的转换器都可以使用一个很方便的方法t transform () ,相当于先调用fit方法,后调用transform()。

  • 预测器:还有一-些估算器能够基于个给定的数据集进行预测, 这个被称为预测器。预测器的predict()方法会接受-个新实例的数据集, 然后返回一个包含相应预测的数据集。值得一提的是,还有一个score方法,可以用来衡量给定测试集的预测质量。

  • 所有估算器的超参数都可以使用公共实例变量直接访问,并且所有估算器的学习参数也可以通过有下划线后缀的公共实例变量来访问。

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

num_pipeline = Pipeline([
        ('imputer', SimpleImputer(strategy= "median")),
        ('attribs_adder', CombinedAttributesAdder()),
        ('std_scaler', StandardScaler())
    ])

housing_num_tr = num_pipeline.fit_transform(housing_num)

housing_num.info()

# dataFrame -> series -> ndarray
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
#获得所有的列名
num_attribs =list(housing_num) 
num_attribs


# 数据集
num_pipeline = Pipeline([
        ('selector', DataFrameSelector(num_attribs)), # dataFrame -> series
        ('imputer', SimpleImputer(strategy= "median")),# 缺失值转换【中位数】
        ('attribs_adder', CombinedAttributesAdder()),# 合成属性
        ('std_scaler', StandardScaler()),# 标准化值比较大的数据
    ])

from sklearn.base import TransformerMixin 
#自定义转化器
class MyLabelBinarizer(TransformerMixin):
    def __init__(self, *args, **kwargs):
        self.encoder = LabelBinarizer(*args, **kwargs)
    def fit(self, x, y= 0):
        self.encoder.fit(x)
        return self
    def transform(self, x, y= 0):
        return self.encoder.transform(x)
    
# 获得文本列  
cat_attribs = ['ocean_proximity']
# 对文本列进行转化
from sklearn.preprocessing import LabelBinarizer
cat_pipeline = Pipeline([
        ('selector', DataFrameSelector(cat_attribs)),               
        ('LabelBinarizer', MyLabelBinarizer()),
    ])


#特征合并
from sklearn.pipeline import FeatureUnion

full_pipeline = FeatureUnion(transformer_list= [
        ("num_pipline", num_pipeline,),
        ('cat_pipline', cat_pipeline),
    ])
#调用
housing_prepared = full_pipeline.fit_transform(housing)
housing_prepared
# 上述得到的ndarry 转化为df格式
housing_prepared = pd.DataFrame(housing_prepared)
housing_prepared.head()

6 选择和训练模型

到目前为止,定义了问题,获得了数据,也进行了数据探索,然后对训练集和测试集进行了抽样并编写了转换流水线。从而可以自动清理和准备机器学习的数据。

6.1 训练和评估训练集

## 初始化估算器
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
# 匹配数据,做模拟训练
lin_reg.fit(housing_prepared, housing_labels)

# 预测数据
# 取出少量数据进行测试
some_data = housing.iloc[:5]
some_labels = housing_labels.iloc[:5]

# 
some_data_prepared = full_pipeline.transform(some_data)

print("Predictions:", lin_reg.predict(some_data_prepared))

# 均方根误差处理数据
from sklearn.metrics import mean_squared_error

housing_predictions = lin_reg.predict(housing_prepared)
lin_mse = mean_squared_error(housing_labels, housing_predictions)
lin_mse
lin_rmse = np.sqrt(lin_mse)
lin_rmse

# 大多数地区的房屋中位数 在120000到265000美元之间,预测误差高达 68628,这是一个典型的模型对训练数据拟合不足的案例,
# 原因可能是特征无法提供足够的信息来做出更好的预测,或者模型本身不够强大,
# 1. 选择强大的模型,2 为算法提供更好的特征,3.减少对模型的限制等方法,


# 决策树可以找到复杂的非线性关系
from sklearn.tree import DecisionTreeRegressor

tree_reg = DecisionTreeRegressor(random_state= 42)
tree_reg.fit(housing_prepared, housing_labels)

housing_predictions = tree_reg.predict(housing_prepared)
tree_mse = mean_squared_error(housing_labels, housing_predictions)
tree_rmse = np.sqrt(tree_mse)
tree_rmse
# 完美,也可能是这个模型对数据严重过度拟合了,如何确认?轻易不要启动测试集,拿训练集中的一部分用于训练,另一部分用于模型的验证

6.2 交叉验证


# 使用train_test_split函数将训练集分为较小的训练集和验证集,然后根据这些较小的训练集来训练模型,并对其进行评估
# sklearn的交叉验证,将训练集随机分割成10个不同的子集,每个子集称为一个折叠,对模型进行10次训练和评估,每次挑选1个折叠进行评估,另外9个进行训练
from sklearn.model_selection import cross_val_score
# neg_mean_squared_error‘ 也就是 均方差回归损失 该统计参数是预测数据和原始数据对应点误差的平方和的均值

# 决策树交叉验证
scores = cross_val_score(tree_reg, housing_prepared, housing_labels,
                         scoring= "neg_mean_squared_error", cv= 10)
tree_rmse_scores = np.sqrt(-scores)
tree_rmse_scores


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

display_scores(tree_rmse_scores)

# 线性 交叉验证
lin_scores = cross_val_score(lin_reg, housing_prepared, housing_labels,
                             scoring= "neg_mean_squared_error", cv= 10)
lin_rmse_scores = np.sqrt(-lin_scores)
display_scores(lin_rmse_scores)


# 随机森林模型
from sklearn.ensemble import RandomForestRegressor

forest_reg = RandomForestRegressor(n_estimators= 10, random_state= 42)
forest_reg.fit(housing_prepared, housing_labels)

# 均方根误差
housing_predictions = forest_reg.predict(housing_prepared)
forest_mse = mean_squared_error(housing_labels, housing_predictions)
forest_rmse = np.sqrt(forest_mse)
forest_rmse

# 随机森林 交叉验证
from sklearn.model_selection import cross_val_score
forest_scores = cross_val_score(forest_reg, housing_prepared, housing_labels,
                                scoring= "neg_mean_squared_error", cv= 10)
forest_rmse_scores = np.sqrt(-forest_scores)
display_scores(forest_rmse_scores)

6.3 模型调优

6.3.1 网格搜索

1.手动调整超参数,找到很好的组合很困难
2.使用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(random_state=4 2)
# 网格确定参数
grid_search = GridSearchCV(forest_reg, param_grid, cv= 5,
                           scoring= 'neg_mean_squared_error', return_train_score= True)
grid_search.fit(housing_prepared, housing_labels)

# 查看最好的参数
grid_search.best_params_
# 查看最好估算器
grid_search.best_estimator_

# 显示分数结果
cvres = grid_search.cv_results_
for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
    print(np.sqrt(-mean_score), params)

# 数据准备步骤也可以当作超参数来处理,网格搜索会自动查找是否添加你不确定的特征,比如是否使用转换器 combinedAttre的超参数add_bedrooms_per_rom
# 也可是使用它自动寻找处理问题的最佳方法,比如异常值,缺失特征,特征选择等

6.3.2 随机搜索


# 如果探索的组合数少的时候,可以考虑使用网格搜索,但是如果探索的组合数较多的时候,应该使用随机搜索
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint
# 给定随机数
param_distribs = {
        'n_estimators': randint(low= 1, high= 200),
        'max_features': randint(low= 1, high= 8),
    }

forest_reg = RandomForestRegressor(random_state= 42)
rnd_search = RandomizedSearchCV(forest_reg, param_distributions= param_distribs,
                                n_iter= 10, cv= 5, scoring= 'neg_mean_squared_error', random_state= 42)
rnd_search.fit(housing_prepared, housing_labels)
# 最佳模型参数
cvres = rnd_search.cv_results_
for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
    print(np.sqrt(-mean_score), params)

6.3.3 分析最佳模型

# 获得特征重要度
feature_importances = grid_search.best_estimator_.feature_importances_
feature_importances

# num_attribs
# 合并特征
extra_attribs = ["rooms_per_household", "population_per_household", "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)

6.3.4 通过测试集评估系统

# 通过测试集评估系统
   1. 从测试集中获取预测器和标签
   2. 运行full_pipline来转换数据
   3. 在测试集上评估最终模型
# 最佳模型
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)
# X_test_prepared

# df = pd.DataFrame(X_test_prepared)
# df.head()


# 进行预测
final_predictions = final_model.predict(X_test_prepared)

# 均方根误差,等到最优值
final_mse = mean_squared_error(y_test, final_predictions)
final_rmse = np.sqrt(final_mse)
final_rmse

6.4 启动、监控、维护系统

## 项目启动阶段
   1. 展示解决方案 学习了什么
   2. 什么有用
   3. 什么没有用
   4. 基于什么假设
   5. 以及系统的限制有哪些
   6. 制作漂亮的演示文稿,例如收入中位数是预测房价的首要指标

## 启动,监控和维护系统
   1. 为生产环境做好准备,将生产数据源接入系统
   2. 编写监控代码,定期检查系统的实时性能,性能下降时触发警报,系统崩溃和性能退化
   3. 时间推移,模型会渐渐腐坏,定期使用新数据训练模型

## 评估系统性能 
   1. 需要对系统的预测结果进行抽样评估,需要人工分析,分析师可能是专家,平台工作人员,都需要将人工评估的流水线接入你的系统
   2. 评估输入系统的数据质量
   3. 使用新鲜数据定期训练你的模型,最多6个月

总结

本周主要学习 数据准备,构建监控工作,建立人工评估流水线,自动化定期训练模型上,熟悉整个机器学习流程。源代码

发布了5 篇原创文章 · 获赞 162 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章