作者归档:feiguyun

第2章 操作数据库

人工智能最重要的动力就是数据,没有充分数据的人工智能是毫无意义的,数据越大人工智能将越聪明,所以数据是非常重要的。因此整合各种数据的能力也是各种开发工具非常重视的,Python在这方面功能很强大。这一章我们将介绍如何利用Python存取数据库中的数据。
目前企业数据大都存储在数据库中,如MySQL、Oracle、DB2等关系型数据库,这些数据库目前使用非常广泛,由于其独特优势,未来还将大量使用;但随着数据量、数据种类的不断增长,目前非关系型数据库也越来越普及了,如MongoDB、Redis、HBase等等,这些数据库有时又称为NoSQL型数据库。所有这些数据是目前大数据的主要源头,因此,如何使用抽取、整合、处理及分析这些数据是非常重要。
这章主要内容:
 操作MySQL
 操作MongoDB

2.1 连接MySQL

Python使用MySQL非常简单,先导入python有关mysql的驱动模块,然后建立与数据库的连接即可。
以下通过实例来说明。

In [2]: import numpy as np
In [3]: import pandas as pd
In [4]: from pandas import DataFrame
In [5]: import MySQLdb

In [6]: conn= MySQLdb.connect(host='slave02',port=3306,user='feigu', passwd='feigu', db='testdb',charset='utf8')

In [7]: data= pd.read_sql('select * from stud_score', conn)
In [8]: df=DataFrame(data)

In [9]: df.count()

2.2 存取MongoDB

MongoDB 是一个基于分布式文件存储的数据库。由C++语言编写。它是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。 它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo可以有多个数据库,每个数据库中数据或文档被分组存储在数据集合(Collection)中。每个集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档。集合的概念类似关系型数据库(RDBMS)里的表(table),不同的是它不需要定义任何模式(schema)。
以下通过一个实例来说明,如何连接mongodb,然后创建集合,并往集合中插入记录,并查询该条记录或文档。

In [1]: from pymongo import MongoClient
In [2]: conn=MongoClient(host='localhost',port=27017)
In [3]: testdb=conn.testdb
In [4]: coll=testdb.items
In [5]:
item={
"type":"operation",
"itemno":"10000",
"contents":{
"python":"2.7.11",
"spark":"2.0"
},
"date":"201611"
}
In [6]: coll.insert(item)

In [7]: coll.find_one()
Out[7]:
{u'_id': ObjectId('5832b9bfeb0a1957669cdf68'),
u'contents': {u'python': u'2.7.11', u'spark': u'2.0'},
u'date': u'201611',
u'itemno': u'10000',
u'type': u'operation'}

当然也可同时往集合插入多条记录:

###多条记录格式[{},{}]
In [8]: item1=[{
...: "type":"operation",
...: "itemno":"20000",
...: "contents":{
...: "Hadoop":"2.7.0",
...: "HBase":"2.0"
...: },
...: "date":"201611"
...: } ,
...: {
...: "type":"operation",
...: "itemno":"30000",
...: "contents":{
...: "Jave":"1.8.0",
...: "Scala":"2.10.0"
...: },
...: "date":"201611"
...: }
In [9]: coll.insert(item1) ###插入多条记录
Out[9]: [ObjectId('5833c36ceb0a197a4306abea'), ObjectId('5833c36ceb0a197a4306abeb')]
In [10]: coll.find({"type":"operation"})
Out[10]: In [10]: for doc in coll.find({"type":"operation"}):
print doc ###查询多条记录
....:
{u'date': u'201611', u'itemno': u'10000', u'_id': ObjectId('5832b9bfeb0a1957669cdf68'), u'type': u'operation', u'contents': {u'python': u'2.7.11', u'spark': u'2.0'}}
{u'date': u'201611', u'itemno': u'20000', u'_id': ObjectId('5833c36ceb0a197a4306abea'), u'type': u'operation', u'contents': {u'HBase': u'2.0', u'Hadoop': u'2.7.0'}}
{u'date': u'201611', u'itemno': u'30000', u'_id': ObjectId('5833c36ceb0a197a4306abeb'), u'type': u'operation', u'contents': {u'Jave': u'1.8.0', u'Scala': u'2.10.0'}}

关系型数据可以使用DataFrame函数转换为DataFrame格式,Mongodb格式的数据能否转换?如果能,该如何转换呢?可以的,而且方式与关系型数据库类型,请看以下示例:

In [11]: import numpy as np
In [12]: from pandas import Series,DataFrame

In [13]: query1=coll.find({"type":"operation"}) ##定义查询结果
In [14]: column=['type','itemno','contents','date'] ##定义显示列名(关键字)
In [15]: df1=DataFrame(list(query1),columns=column)

In [16]: df1
Out[16]:
type itemno contents date
0 operation 10000 {u'python': u'2.7.11', u'spark': u'2.0'} 201611
1 operation 20000 {u'HBase': u'2.0', u'Hadoop': u'2.7.0'} 201611
2 operation 30000 {u'Jave': u'1.8.0', u'Scala': u'2.10.0'} 201611

第12章 化繁为简的高手----正则化方法


正则化在机器学习、深度学习中运用非常广泛,机器学习中在回归、降维时经常使用,在目前很火的深度学习中为避免过拟合也常使用。为何它作用如此之大,它的大致原理和作用是啥?接下来我们来做个简单介绍。

12.1 正则化简介

在机器学习中,很多被显式地设计用来减少测试误差的策略,统称为正则化。正则化旨在减少泛化误差而不是训练误差。目前有许多正则化策略。有些策略向机器学习模型添加限制参数值的额外约束,如L1、L2等;有些策略向目标函数增加额外项对参数值进行软约束,如作为约束的范数惩罚等。这些惩罚和约束通常用来使模型更简单,以便提高模型的泛化能力,当然也有些正则化,如集成方法,是为了结合多个假说来更好解释训练数据。
正则化是机器学习领域的中心问题之一,其重要性只有优化问题才能与之匹敌。
正则化的作用、原理,我们还是先从几张图来说明吧,图形直观明了,易说明问题。

 

(图1 测试误差与训练误差)

(图2 根据房屋面积预测房价几个回归模型)

这两个图说明了:
(1)、图1说明了,模型不是越复杂越好,训练精度随复杂度逐渐变小;测试误差刚开始随着模型复杂度而变小,但当复杂度超过某点之后,测试误差不但不会变小,反而会越来越大;
(2)、图2是对图1的一个示例说明。

上面我们讲了正则化的主要目的就是提高模型的泛化能力,或降低模型的测试误差。在实际模型中,它是如何实现的呢?
我们以图2中最右边这个图来说,其模型是一个4次多项式,因它把一些噪音数据也包括进来了,所以导致模型很复杂,实际上房价与房屋面积应该是2次多项式函数,如图2中间这个图。

当然这里我们取10000只是用来代表一个"大值",现在,如果我们要最小化这个函数,那么为了最小化这个新的代价函数,我们要让 θ3 和 θ4 尽可能小。因为,如果你在原有代价函数的基础上加上 10000 乘以 θ3 这一项 ,那么这个新的代价函数将变得很大,所以,当我们最小化这个新的代价函数时, 我们将使 θ3 的值接近于 0,同样 θ4 的值也接近于 0,就像我们忽略了这两个值一样。如果我们做到这一点( θ3 和 θ4 接近 0 ),那么我们将得到一个近似的二次函数。如下图:

图3 利用正则化提升模型泛化能力

12.2 机器学习中常用的两种正则化策略

机器学习中几乎都可以看到损失函数后面会添加一个额外项,常用的额外项一般有两种,一般英文称作ℓ1-norm和ℓ2-norm,中文称作L1正则化和L2正则化,或者L1范数和L2范数。
L1正则化和L2正则化可以看做是损失函数的惩罚项。所谓『惩罚』是指对损失函数中的某些参数做一些限制。对于线性回归模型,使用L1正则化的模型建叫做Lasso回归,使用L2正则化的模型叫做Ridge回归(岭回归)。下式是Python中Lasso回归的损失函数,式中加号后面一项α||w||1即为L1正则化项。

下式是Python中Ridge回归的损失函数,式中加号后面一项α||w||22即为L2正则化项。

一般回归分析中回归w表示特征的系数,从上式可以看到正则化项是对系数做了处理(限制)。L1正则化和L2正则化的说明如下:
L1正则化是指权值向量w中各个元素的绝对值之和,通常表示为〖||w||〗_1
L2正则化是指权值向量w中各个元素的平方和然后再求平方根(可以看到Ridge回归的L2正则化项有平方符号),通常表示为〖||w||〗_2
一般都会在正则化项之前添加一个系数,Python中用α表示,一些文章也用λ表示。这个系数需要用户指定。

12.3.正则化的主要作用

L1正则化和L2正则化的作用:
L1正则化可以产生稀疏权值矩阵,即产生一个稀疏模型,可以用于特征选择
L2正则化可以防止模型过拟合(overfitting);一定程度上,L1也可以防止过拟合
1)L1正则化和特征选择
假设有如下带L1正则化的损失函数:

图4 L1正则化

同样可以画出他们在二维平面上的图形,如下:

图5 L2正则化

2)L2正则化和过拟合
拟合过程中通常都倾向于让权值尽可能小,最后构造一个所有参数都比较小的模型。因为一般认为参数值小的模型比较简单,能适应不同的数据集,也在一定程度上避免了过拟合现象。可以设想一下对于一个线性回归方程,若参数很大,那么只要数据偏移一点点,就会对结果造成很大的影响;但如果参数足够小,数据偏移得多一点也不会对结果造成什么影响,提高模型的鲁棒性。
那为什么L2正则化可以获得值很小的参数?
以线性回归中的梯度下降法为例。假设要求的参数为θ,h_θ (x)是我们的假设函数,那么线性回归的代价函数如下:

 

其中λ就是正则化参数。从上式可以看到,与未添加L2正则化的迭代公式相比,每一次迭代,θj都要先乘以一个小于1的因子(如1-αλ/m),从而使得θj不断减小,因此总得来看,θ是不断减小的。
L1正则化一定程度上也可以防止过拟合。L1可以是权重系数逼近于0或等于0,这样可以简化模型,提高模型的泛化能力,从而达到与L2正则化类似的效果。

12.4 逻辑回归中使用正则化实例分析

这里我们以逻辑回归实例来具体说明,在算法中如何利用正则化策略惩罚参数值的。

因此,减少正则化参数倒数C的值就相当于增强正则化的强度,以下我们通过绘制对四个权重系数进行L2正则化的图像予以展示。

1)导入数据

from sklearn import datasets
import numpy as np
import matplotlib.pyplot as plt

iris = datasets.load_iris()
X = iris.data
y = iris.target

2)把数据集划分为训练集和测试集,划分比例为7:3

from sklearn.cross_validation import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=0)

from sklearn.preprocessing import StandardScaler

sc = StandardScaler()
sc.fit(X_train)
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)

3)对逻辑回归加入正则化项,并对C的不同取值,对权重的影响图形化。

from sklearn.linear_model import LogisticRegression

weights, params = [], []
for c in range(-5, 5):
lr = LogisticRegression(C=10 ** c, random_state=0)
lr.fit(X_train_std, y_train)
weights.append(lr.coef_[1])
params.append(10**c)

weights = np.array(weights)

plt.plot(params, weights[:, 0], linestyle='-',
label='sepal width')
plt.plot(params, weights[:, 1], linestyle='-.',
label='sepal width')

plt.plot(params, weights[:, 2],linestyle=':',
label='petal length')
plt.plot(params, weights[:, 3], linestyle='--',
label='petal width')

plt.ylabel('weight coefficient')
plt.xlabel('C')
plt.legend(loc='upper left')
plt.xscale('log')

plt.show()

图6 利用正则化控制逻辑回归权重参数值

从上图我们不难看出,如果我们减小参数C的值,即增强正则化项的强度,可以使权重系统逐渐收缩。

12.5 Dropout正则化简介

Dropout是Srivastava等人在2014年的一篇论文中提出的一种针对神经网络模型的正则化方法 Dropout: A Simple Way to Prevent Neural Networks from Overfitting。
Dropout的做法是在训练过程中按一定比例(比例参数可设置)随机地忽略一些神经元。这些神经元被随机地“抛弃”了。也就是说它们在正向传播过程中对于下游神经元的贡献效果暂时消失了,反向传播时该神经元也不会有任何权重的更新。
随着神经网络模型不断地学习,神经元的权值会与整个网络的上下文相匹配。神经元的权重针对某些特征进行调优,具有一些特殊化。周围的神经元则会依赖于这种特殊化,如果过于特殊化,模型会因为对训练数据过拟合而变得脆弱不堪。神经元在训练过程中的这种依赖于上下文的现象被称为复杂的协同适应(complex co-adaptations)。
这么做的效果就是,网络模型对神经元特定的权重不那么敏感。这反过来又提升了模型的泛化能力,不容易对训练数据过拟合。
Dropout训练的集成包括所有从基本的基础网络除去非输出单元形成 子网络,如在图7所示。


图 7 基础网络Dropout为多个子网络

Dropout训练由所有子网络组成的集成,其中子网络通过从基本网络中删除非输出单元构 建。我们从具有两个可见单元和两个隐藏单元的基本网络开始。这四个单元有十六个可能的子集。 右图展示了从原始网络中丢弃不同的单元子集而形成的所有十六个子网络。在这个小例子中,所 得到的大部分网络没有输入单元或没有从输入连接到输出的路径。当层较宽时,丢弃所有从输入 到输出的可能路径的概率变小,所以这个问题对于层较宽的网络不是很重要。

较先进的神经网络基于一系列仿射变换和非线性变换,我 们可以将一些单元的输出乘零就能有效地删除一个单元。这个过程需要对模型一些 修改,如径向基函数网络,单元的状态和参考值之间存在一定区别。为了简单起见, 我们在这里提出乘零的简单Dropout算法,但是它被简单地修改后,可以与从网络中 移除单元的其他操作一起工作。
回想一下使用Bagging学习,我们定义 k 个不同的模型,从训练集有替换采样 构造 k 个不同的数据集,然后在训练集 i 上训练模型 i。Dropout的目标是在指数 级数量的神经网络上近似这个过程。具体来说,训练中使用Dropout,我们使用基 于minibatch的学习算法和小的步长,如梯度下降等。我们每次在minibatch加载一个 样本,然后随机抽样应用于网络中所有输入和隐藏单元的不同二值掩码。对于每个 单元,掩码是独立采样的。掩码值为 1 的采样概率(导致包含一个单元)是训练开 始前固定一个超参数。它不是模型当前参数值或输入样本的函数。通常一个输入单 元包括的概率为 0.8,一个隐藏单元包括的概率为 0.5。然后,我们运行之前一样的 前向传播、反向传播以及学习更新。图8说明了在Dropout下的前向传播。

图 8
在使用Dropout的前馈网络中前向传播的示例。(顶部) 在此示例中,我们使用具有两个输入 单元,具有两个隐藏单元的隐藏层以及一个输出单元的前馈网络。(底部) 为了执行具有Dropout的 前向传播,我们随机地对向量 μ 进行采样,其中网络中的每个输入或隐藏单元对应一项。μ 中的 每项都是二值的且独立于其他项采样。每项为 1 的概率是超参数,对于隐藏层通常为 0.5,对于输 入通常为 0.8。网络中的每个单元乘以相应的掩码,然后正常地继续通过网络的其余部分前向传 播。这相当于从图7中随机选择一个子网络并通过它前向传播。

12.6 使用Dropout的小技巧

1)通常丢弃率控制在20%~50%比较好,可以从20%开始尝试。如果比例太低则起不到效果,比例太高则会导致模型的欠学习。
2)在大的网络模型上应用。当dropout用在较大的网络模型时更有可能得到效果的提升,模型有更多的机会学习到多种独立的表征。
3)在输入层(可见层)和隐藏层都使用dropout。在每层都应用dropout被证明会取得好的效果。
4)增加学习率和冲量。把学习率扩大10~100倍,冲量值调高到0.9~0.99.
5)限制网络模型的权重。大的学习率往往导致大的权重值。对网络的权重值做最大范数正则化等方法被证明会提升效果。

12.7 小结

L1、L2是我们常见正则化方式,正则化在机器学习中运用非常广泛,而且功能很强大,经常用来提升模型的泛化能力,此外,正则化策略还包括dropout、数据集增强等,甚至提前终止、集成方法、参数绑定与共享等都与正则化有千丝万缕的关系。其中有些正则化策略这里不展开来说了,后续在深度学习中我们会介绍。

本章参考以下博客或书:
http://www.cnblogs.com/jianxinzhou/p/4083921.html
http://blog.csdn.net/jinping_shi/article/details/52433975
《Python机器学习》塞巴斯蒂安.拉施卡 著

第11章 神奇的SVM及实例分析

11.1 SVM简介

支持向量机(SupportVector Machines),在处理线性数据集、非线性数据集都有较好效果,在机器学习或者模式识别领域可是无人不知,无人不晓。八九十年代的时候,和神经网络一决雌雄,独领风骚几十年,甚至有人把神经网络八九年代的再度沉寂归功于它。
它的强大或神奇与它采用的相关技术不无关系,如最大类间隔、松弛变量、核函数等等(这些技术本章都会介绍),使用这些技术使其在众多机器学习方法中脱颖而出。虽然几十年过去了,但风采依旧,究其原因:与其高效、简洁、易用的特点分不开,其中一些处理思想与当今深度学习技术有很大关系,如其用核方法解决非线性数据集分类问题,很像带隐含层的神经网络,可以说两者有同工异曲之妙。
分类的方法有很多,如线性回归、逻辑回归、决策树、贝叶斯等等,分类的目的是学会一个分类函数或分类模型(或者叫做分类器),该模型能把数据集中的数据项映射到给定类别中的某一个,对新数据通过这个映射,预测其类别。
支持向量机进行分类,目的也是一样:得到一个分类器或分类模型,不过它的分类器一个超平面(如果数据集是二维,这个超平面就是直线,三维数据集,就是平面,以此类推),这个超平面把样本一分为二,当然,这种划分不是简单划分,需要使正例和反例之间的间隔最大。间隔最大,其泛化能力就最强。如何得到这样一个超平面?下级我们通过用一个二维空间例子来说明。

11.2 分类间隔最大的超平面

何为超平面?哪种超平面是我们需要?,我们首先看下图的几个超平面或直线。

如何获取最大化分类间隔?分类算法优化目标通常是最小化分类误差,但对SVM而言,我们的优化的目标是最大化分类间隔。所谓间隔是指两个分离的超平面间的距离,其中最靠近超平面的训练样本又称为支持向量(support vector)。以下我们通过一个二维空间的简单实例来进一步说明。
假设在一个二维空间,分布下例这些点(有些是圆点,有些是方块)。



所以我们的问题就变成:

这是个凸二次规划问题。什么叫凸?凸集是指这样一些点的集合,其中任取两个点连一条直线,这条线上的点仍然在这个集合内部,因此说“凸”。例如下图,对于凸函数(在数学表示上,满足约束条件是仿射函数,也就是线性的Ax+b的形式)来说,局部最优就是全局最优,但对非凸函数来说就不是了。二次表示目标函数是自变量的二次函数。


既然是凸二次规划问题,就可以通过一些现成的 QP (Quadratic Programming) 的优化工具来得到最优解。所以,我们的问题到此为止就算全部解决了。虽然这个问题确实是一个标准的 QP 问题,但是它也有它的特殊结构,通过 Lagrange Duality 变换到对偶变量 (dual variable) 的优化问题之后,可以找到一种更加有效的方法来进行求解,而且通常情况下这种方法比直接使用通用的 QP 优化包进行优化要高效得多。也就说,除了用解决QP问题的常规方法之外,还可以应用拉格朗日对偶性,通过求解对偶问题得到最优解,这就是线性可分条件下支持向量机的对偶算法,这样做的优点在于:一是对偶问题往往更容易求解;二者可以自然的引入核函数,进而推广到非线性分类问题。至于对偶问题及其优化这里就不展开来说了,有兴趣的读者可以参考有关资料。

11.3使用松弛向量解决非线性可分问题

实际数据集往往很难找到一条直线或一个超平面就可一分为二,或者这些数据集是线性可分的。如果出现非线性数据集,我们该如何处理呢?这里我们介绍一种所谓软间隔的分类方法,这种方法由Vladimir Vapnik在1995年引入,其基本思想是引入一个松弛系数ξ,放松线性约束的条件,以便在适当的罚项成本下,对错误分类的情况优化时也能收敛。
为了处理这种情况,我们允许数据点在一定程度上偏离超平面。也就是允许一些点跑到H1和H2之间,也就是他们到分类面的间隔会小于1。如下图:


其中C是离群点的权重,引入非负参数ξi后(称为松弛变量),就允许某些样本点的函数间隔小于1,即在最大间隔区间里面,或者函数间隔是负数,即样本点在对方的区域中。而放松限制条件后,我们需要重新调整目标函数,以对离群点进行处罚,目标函数后面加上的第二项就表示离群点越多,目标函数值越大,而我们要求的是尽可能小的目标函数值。通过变量C我们可以控制对错误分类的惩罚程度。C越大表明离群点对目标函数影响越大,也就是越不希望看到离群点。这时候,间隔也会很小。我们看到,目标函数控制了离群点的数目和程度,使大部分样本点仍然遵守限制条件,如下图所示:

至此,我们已经了解了线性SVM的基本概念,主要原理,接下来我们通过一个实例来演示一个SVM模型对鸢尾花数据集中的样本进行分类。

11.4 SVM分类实例

接下来我们以鸢尾花卉数据集(lris)样本,使用SVM对其进行分类,lris是一类多重变量分析的数据集。通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类。
1)导入数据
使用sklearn中导入数据的库(datasets)进行数据的导入,为方便可视化起见,这里我们花瓣长度,花瓣宽度为两个特征,并由此构建一个特征矩阵X,同时将对应花的类标赋给向量Y,具体实现如下:

from sklearn import datasets
import numpy as np

iris = datasets.load_iris()
X = iris.data[:, [2, 3]]
y = iris.target

2)对数据进行标准化处理

from sklearn.preprocessing import StandardScaler

sc = StandardScaler()
sc.fit(X)
X_std = sc.transform(X)

4)为更好的显示划分后的效果,这里我们先定义几个函数,用来图形化数据。

from matplotlib.colors import ListedColormap
import matplotlib.pyplot as plt
import warnings

def versiontuple(v):
return tuple(map(int, (v.split("."))))

def plot_decision_regions(X, y, classifier, test_idx=None, resolution=0.02):

# setup marker generator and color map
markers = ('s', 'x', 'o', '^', 'v')
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
cmap = ListedColormap(colors[:len(np.unique(y))])

# plot the decision surface
x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
np.arange(x2_min, x2_max, resolution))
Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
Z = Z.reshape(xx1.shape)
plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
plt.xlim(xx1.min(), xx1.max())
plt.ylim(xx2.min(), xx2.max())

for idx, cl in enumerate(np.unique(y)):
plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
alpha=0.8, c=cmap(idx),
marker=markers[idx], label=cl)

5)利用SVM对对数据集进行分类,这里使用了松弛变量来处理非线性数据集。

%matplotlib inline
from sklearn.svm import SVC
import matplotlib.font_manager as fm ###便于中文显示
myfont = fm.FontProperties(fname='/home/hadoop/anaconda3/lib/python3.6/site-packages/matplotlib/mpl-data/fonts/ttf/simhei.ttf')

svm = SVC(kernel='linear', C=1.0, random_state=0)
svm.fit(X_std, y)

plot_decision_regions(X_std, y,classifier=svm)
plt.xlabel("标准化处理后花瓣长度",fontproperties=myfont,size=12)
plt.ylabel("标准化处理后花瓣宽度",fontproperties=myfont,size=12)
plt.legend(loc='upper left')
plt.tight_layout()

plt.show()

11.5在 SVM中应用核函数

上节我们介绍了使用松弛变量处理非线性数据集(含有一些离群点)问题,效果还不错。但有些数据集其分布就是非线性可分的,如比较典型的“异或”问题,遇到这种数据集,我们该如何处理呢?显然使用松弛变量难以达到满意效果,不过没有关系,此时,我们可以使用SVM的一种强大而神奇的核函数法。通过核函数我们可以把线性不可分数据集转换为线性可分(在更高维度空间中)。
接下来我们简单介绍一下核函数或核方法。
我们先通过一个图及一个简单实例,先来大致了解一下SVM的核威力。我们先一个图,给大家一点感性认识:

图1是一个二维空间上的一些散点分布图,通过一个核函数ϕ:
ϕ(x1,x2)=(z1,z2,z3)=(x1,x2,〖x1〗^2+〖x2〗^2)
把一个在二维空间线性不可分的数据集映射到三维空间(图2)后,就可以线性可分了(其中超平面可以把数据分成上下两部分)。

这是核方法的一个直观认识,下面我们使用scikit-learn包的SVM类,具体实现一个核方法。
1)在二维空间,生成一个类似‘异或’的数据集

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np

np.random.seed(0)
X_xor = np.random.randn(200, 2)
y_xor = np.logical_xor(X_xor[:, 0] > 0,
X_xor[:, 1] > 0)
y_xor = np.where(y_xor, 1, -1)

plt.scatter(X_xor[y_xor == 1, 0],
X_xor[y_xor == 1, 1],
c='b', marker='x',
label='1')
plt.scatter(X_xor[y_xor == -1, 0],
X_xor[y_xor == -1, 1],
c='r',
marker='s',
label='-1')

plt.xlim([-3, 3])
plt.ylim([-3, 3])
plt.legend(loc='best')
plt.tight_layout()
plt.show()


显然,对这个数据集是线性不可分的,即无法找到一条直线或超平面,把这个数据集划分为两类。
接下来我们采用SVM类中一个高斯核或径向基函数核(Radial Basis Function Kernel,RBF Kernel ),把数据映射到一个高维空间,然后利用SVM对其进行分类或划分。

svm = SVC(kernel='rbf', random_state=0, gamma=0.10, C=10.0)
svm.fit(X_xor, y_xor)
plot_decision_regions(X_xor, y_xor,
classifier=svm)

plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

从以上图可以看到,SVM比较完美的把不同类型的点划分成了两部分。

核函数我们可以把它理解为把低维数据集映射为高维数据集的映射或相似函数。接下来我们看一下核函数一般形式及特点、常用核函数。
设xi,xj∈X,X属于R(n)空间,非线性函数Φ实现输入空间X到特征空间F的映射,其中F属于R(m),n<<m。根据核函数技术有:
K(xi,xj) =<Φ(xi),Φ(xj) >
其中:<, >为内积,K(xi,xj)为核函数。从上式可以看出,核函数将m维高维空间的内积运算转化为n维低维输入空间的核函数计算,从而巧妙地解决了在高维特征空间中计算的“维数灾难”等问题,从而为在高维特征空间解决复杂的分类或回归问题奠定了理论基础。
特点编辑
核函数方法的广泛应用,与其特点是分不开的:
(1)核函数的引入避免了“维数灾难”,大大减小了计算量。而输入空间的维数n对核函数矩阵无影响,因此,核函数方法可以有效处理高维输入。
(2)无需知道非线性变换函数Φ的形式和参数.
(3)核函数的形式和参数的变化会隐式地改变从输入空间到特征空间的映射,进而对特征空间的性质产生影响,最终改变各种核函数方法的性能。
(4)核函数方法可以和不同的算法相结合,形成多种不同的基于核函数技术的方法,且这两部分的设计可以单独进行,并可以为不同的应用选择不同的核函数和算法。
常见分类编辑
核函数的确定并不困难,满足Mercer定理的函数都可以作为核函数。常用的核函数有:线性核函数,多项式核函数,径向基核函数,Sigmoid核函数等,这些核函数的表达式如下:

参考网站:http://blog.csdn.net/zouxy09/article/details/17291543
参考书:Python机器学习

第10章 K-means聚类算法及实例

10.1 K-means简介

机器学习中有两类的大问题,一个是分类,一个是聚类。
分类是根据一些给定的已知类别标识的样本,训练某种学习机器,使它能够对未知类别的样本进行分类。这属于监督学习。而聚类指事先并不知道任何样本的类别标识,希望通过某种算法来把一组未知类别的样本划分成若干类别,这在机器学习中被称作无监督学习。在本章,我们关注其中一个比较简单的聚类算法:k-means算法。

10.2 K-means 算法

k-means算法是一种很常见的聚类算法,它的基本思想是:
(1)适当选择k个类的初始中心
(2)在第i次迭代中,对任意一个样本,求其到K个中心的距离,将该样本归到距离 最短的中心所在的类
(3)利用均值等方法更新该类的中心值
(4)对于所有的K个聚类中心,如果利用(2)(3)的迭代法更新后,值保持不变, 则迭代结束,否则继续迭代。
最后结果是同一类簇中的对象相似度极高,不同类簇中的数据相似度极低。

10.3 K-means简单实例

假定我们有如下9个点:
A1(2, 10) A2(2, 5) A3(8, 4) A4(5, 8) A5(7, 5) A6(6, 4) A7(1, 2) A8(4, 9)
现希望分成3个聚类(即k=3)
初始化选择 A1(2, 10), A4(5, 8) ,A7(1, 2)为聚类中心点,假设两点距离定义为ρ(a, b) = |x2 – x1| + |y2 – y1| . (当然也可以定义为其它格式,如欧氏距离)
第一步:选择3个聚类中,分别为A1,A4,A7

这些点的分布图如下:

第二步:计算各点到3个类中心的距离,那个点里类中心最近,就把这个样本点
划归到这个类。选定3个类中心(即:A1,A4,A7),如下图:

对A1点,计算其到每个cluster 的距离
A1->class1 = |2-2|+|10-10}=0
A1->class2 = |2-5|+|10-8|=5
A1->class3 = |2-1|+|10-2|=9
因此A1 属于cluster1,如下图:

按照类似方法,算出各点到聚类中心的距离,然后按照最近原则,把样本点放在那个族中。如下表格:

根据距离最短原则,样本点的第一次样本划分,如下图:

第三步:求出各类中样本点的均值,并以此为类的中心。
cluster1只有1个点,因此A1为中心点
cluster2的中心点为 ( (8+5+7+6+4)/5,(4+8+5+4+9)/5 )=(6,6)。注意:这个点并非样本点。
cluster3的中心点为( (2+1)/2, (5+2)/2 )= (1.5, 3.5),
新族的具体请看下图中x点:

第四步:计算各样本点到各聚类中心的距离,重复以上第二、第三步,把样本划分到新聚类中,如下图:

持续迭代,直到前后两次迭代不发生变化为止,如下:

10.4 sklearn.K-means的实例

初始化聚类中心比较有讲究,上面这个实例我们是人工指定,A1、A4、A7为初始化的聚类中心;如果K值选择不当或聚类中心选择不当,则可能导致聚类效果不佳。因此,在实际项目中往往采用随机选择K个聚类中心,另外,K的选取也是很重要的,K的选取我们也有比较好的方法,如肘(elbow)方法、轮廓图(silhouette plot)方法等,这里就不展开来说,有兴趣的读者可以查阅相关资料。

%matplotlib inline
import numpy as np #科学计算包
import matplotlib.pyplot as plt #python画图包

from sklearn.cluster import KMeans #导入K-means算法包
from sklearn.datasets import make_blobs

import matplotlib.font_manager as fm ###便于中文显示
myfont = fm.FontProperties(fname='/home/hadoop/anaconda3/lib/python3.6/site-packages/matplotlib/mpl-data/fonts/ttf/simhei.ttf')

plt.figure(figsize=(12, 12))

'''''
make_blobs函数是为聚类产生数据集
产生一个数据集和相应的标签
n_samples:表示数据样本点个数,默认值100
n_features:表示数据的维度,默认值是2
centers:产生数据的聚类中心点,默认值3
init:采用随机还是k-means++等(可使聚类中心尽可能的远的一种方法)
cluster_std:数据集的标准差,浮点数或者浮点数序列,默认值1.0
center_box:中心确定之后的数据边界,默认值(-10.0, 10.0)
shuffle :洗乱,默认值是True
random_state:随机生成器的种子

'''

n_samples = 1500
random_state = 10
X, y = make_blobs(n_samples=n_samples, random_state=random_state)

# Incorrect number of clusters
y_pred = KMeans(n_clusters=2, random_state=random_state).fit_predict(X)

plt.subplot(221) #在2图里添加子图1
plt.scatter(X[:, 0], X[:, 1], c=y_pred) #scatter绘制散点
plt.title("不恰当的聚类数",fontproperties=myfont,size=12) #加标题

# Different variance
X_varied, y_varied = make_blobs(n_samples=n_samples,
cluster_std=[1.0, 2.5, 0.5],
random_state=random_state)
y_pred = KMeans(n_clusters=3, init='random',random_state=random_state).fit_predict(X_varied)

plt.subplot(222)#在2图里添加子图3
plt.scatter(X_varied[:, 0], X_varied[:, 1], c=y_pred)
plt.title("方差不同的散点",fontproperties=myfont,size=12)

plt.show()

10.5 Mini Batch K-Means算法

对K-means算法,如果原数据很多,其计算量非常大,对于大数据是否有更好的方法呢?在计算梯度时,我们用整个数据集计算,如果数据量大的话,我们可以采用随机梯度的方法。同理,我们在处理聚类算法时,也有类似的方法,这就是scikit-learn提供的Mini Batch K-Means算法,其具体方法如下:
Mini Batch K-Means算法是K-Means算法的变种,采用小批量的数据子集减小计算时间,同时仍试图优化目标函数,这里所谓的小批量是指每次训练算法时所随机抽取的数据子集,采用这些随机产生的子集进行训练算法,大大减小了计算时间,与其他算法相比,减少了k-均值的收敛时间,小批量k-均值产生的结果,一般只略差于标准算法。
该算法的迭代步骤有两步:
1:从数据集中随机抽取一些数据形成小批量,把他们分配给最近的质心
2:更新质心
与K均值算法相比,数据的更新是在每一个小的样本集上。对于每一个小批量,通过计算平均值得到更新质心,并把小批量里的数据分配给该质心,随着迭代次数的增加,这些质心的变化是逐渐减小的,直到质心稳定或者达到指定的迭代次数,停止计算
Mini Batch K-Means比K-Means有更快的 收敛速度,但同时也降低了聚类的效果,但是在实际项目中却表现得不明显,这是一张k-means和mini batch k-means的实际效果对比图。

import time

import numpy as np
import matplotlib.pyplot as plt

from sklearn.cluster import MiniBatchKMeans, KMeans
from sklearn.metrics.pairwise import pairwise_distances_argmin
from sklearn.datasets.samples_generator import make_blobs

##############################################################################
# Generate sample data
np.random.seed(0)

batch_size = 45
centers = [[1, 1], [-1, -1], [1, -1]] #初始化三个中心
n_clusters = len(centers) #聚类的数目为3
#产生3000组两维的数据,以上边三个点为中心,以(-10,10)为边界,数据集的标准差是0.7
X, labels_true = make_blobs(n_samples=3000, centers=centers, cluster_std=0.7)

##############################################################################
# Compute clustering with Means

k_means = KMeans(init='k-means++', n_clusters=3, n_init=10)
t0 = time.time() #当前时间
k_means.fit(X)
#使用K-Means 对 3000数据集训练算法的时间消耗
t_batch = time.time() - t0

##############################################################################
# Compute clustering with MiniBatchKMeans

mbk = MiniBatchKMeans(init='k-means++', n_clusters=3, batch_size=batch_size,
n_init=10, max_no_improvement=10, verbose=0)
t0 = time.time()
mbk.fit(X)
#使用MiniBatchKMeans 对 3000数据集训练算法的时间消耗
t_mini_batch = time.time() - t0

##############################################################################
# Plot result

#创建一个绘图对象, 并设置对象的宽度和高度, 如果不创建直接调用plot, Matplotlib会直接创建一个绘图对象
'''''
当绘图对象中有多个轴的时候,可以通过工具栏中的Configure Subplots按钮,
交互式地调节轴之间的间距和轴与边框之间的距离。
如果希望在程序中调节的话,可以调用subplots_adjust函数,
它有left, right, bottom, top, wspace, hspace等几个关键字参数,
这些参数的值都是0到1之间的小数,它们是以绘图区域的宽高为1进行正规化之后的坐标或者长度。
'''

fig = plt.figure(figsize=(8, 3))
fig.subplots_adjust(left=0.02, right=0.98, bottom=0.05, top=0.9)
colors = ['#4EACC5', '#FF9C34', '#4E9A06']

# We want to have the same colors for the same cluster from the
# MiniBatchKMeans and the KMeans algorithm. Let's pair the cluster centers per
# closest one.
k_means_cluster_centers = np.sort(k_means.cluster_centers_, axis=0)
mbk_means_cluster_centers = np.sort(mbk.cluster_centers_, axis=0)
k_means_labels = pairwise_distances_argmin(X, k_means_cluster_centers)
mbk_means_labels = pairwise_distances_argmin(X, mbk_means_cluster_centers)
order = pairwise_distances_argmin(k_means_cluster_centers,
mbk_means_cluster_centers)

# KMeans
ax = fig.add_subplot(1, 3, 1) #add_subplot 图像分给为 一行三列,第一块
for k, col in zip(range(n_clusters), colors):
my_members = k_means_labels == k
cluster_center = k_means_cluster_centers[k]
ax.plot(X[my_members, 0], X[my_members, 1], 'w',
markerfacecolor=col, marker='.')
ax.plot(cluster_center[0], cluster_center[1], 'o', markerfacecolor=col,
markeredgecolor='k', markersize=6)
ax.set_title('KMeans')
ax.set_xticks(())
ax.set_yticks(())
plt.text(-3.5, 1.8, 'train time: %.2fs\ninertia: %f' % (
t_batch, k_means.inertia_))

# MiniBatchKMeans
ax = fig.add_subplot(1, 3, 2)#add_subplot 图像分给为 一行三列,第二块
for k, col in zip(range(n_clusters), colors):
my_members = mbk_means_labels == order[k]
cluster_center = mbk_means_cluster_centers[order[k]]
ax.plot(X[my_members, 0], X[my_members, 1], 'w',
markerfacecolor=col, marker='.')
ax.plot(cluster_center[0], cluster_center[1], 'o', markerfacecolor=col,
markeredgecolor='k', markersize=6)
ax.set_title('MiniBatchKMeans')
ax.set_xticks(())
ax.set_yticks(())
plt.text(-3.5, 1.8, 'train time: %.2fs\ninertia: %f' %
(t_mini_batch, mbk.inertia_))

# Initialise the different array to all False
different = (mbk_means_labels == 4)
ax = fig.add_subplot(1, 3, 3)#add_subplot 图像分给为 一行三列,第三块

for k in range(n_clusters):
different += ((k_means_labels == k) != (mbk_means_labels == order[k]))

identic = np.logical_not(different)
ax.plot(X[identic, 0], X[identic, 1], 'w',
markerfacecolor='#bbbbbb', marker='.')
ax.plot(X[different, 0], X[different, 1], 'w',
markerfacecolor='m', marker='.')
ax.set_title('Difference')
ax.set_xticks(())
ax.set_yticks(())

plt.show()


参考网站:http://blog.csdn.net/gamer_gyt/article/details/51244850

第9章 自然语言处理---情感分析实例

在自然语言处理中,首先需要把文本或单词等转换为数值格式,为后续机器学习或深度学习使用,把文本或单词转换为数值,有几种模型,如词袋模型(bag of words或简称为BOW)、word2vec等。

9.1 词袋模型(BOW)示例

BOW模型是信息检索领域常用的文档表示方法。在信息检索中,BOW模型假定对于一个文档,忽略它的单词顺序和语法、句法等要素,将其仅仅看作是若干个词汇的集合,文档中每个单词的出现都是独立的,不依赖于其它单词是否出现。也就是说,文档中任意一个位置出现的任何单词,都不受该文档语意影响而独立选择的。例如有如下三个文档:
1、The sun is shining
2、The weather is sweet
3、The sun is shining and the the weather is sweet
基于这三个文本文档(为简便起见这里以一个句子代表一个文档),构造一个词典或词汇库。如果构建词典?首先,看出现哪些单词,然后,给每个单词编号。在这三个文档中,共出现7个单词(不区分大小写),分别是:the,is ,sun,shining,and,weather,sweet。
然后,我们把这7个单词给予编号,从0开始,从而得到一个单词:序号的字典:
{'and':0,'is':1,'shining':2,'sun':3,'sweet':4,'the':5,'weather':6}
现在根据这个字典,把以上三个文档转换为特征向量(在对应序列号中是否有对应单词及出现的频率):
第一句可转换为:
[0 1 1 1 0 1 0]
第二句可转换为:
[0 1 0 0 1 1 1]
第三句可转换为:
[1 2 1 1 1 2 1]
0表示字典中对应单词在文档中未出现,1表示对应单词在文档出现一次,2表示出现2次。出现在特征向量中值也称为原始词频(raw term frequency):tf(t,d),单词t在文档d出现的次数)
这个一个简单转换,如果有几个文档,而且有些单词在每个文档中出现的频度都较高,这种频繁出现的单词往往不含有用或特别的信息,在向量中如何降低这些单词的权重?这里我们可以采用逆文档频率(inverse document frequency,idf)技术来处理。
原始词频结合逆文档频率,称为词频-逆文档词频(term frequency - inverse document frequency,简称为tf-idf)。
tf-idf如何计算呢?我们通过以下公式就明白了:
tf-idf(t,d)=tf(t,d)*idf(t,d)
其中idf(t,d)=log□(n_d/(1+df(d,t)))
n_d 表示总文档数(这里总文档数为3),df(d,t)为文档d中的单词t涉及的文档数量。
取对数是为了保证文档中出现频率较低的单词被赋予较大的权重,分母中的加1是为了防止df(d,t)为零的情况。有些模型中也会在分子加上1,分子变为1+n_d,tf-ifd(t,d)= tf(t,d)*(idf(t,d)+1),Scikit-learn采用这中计算方法。
如我们看单词'the'在第一个句子或第一个文档(d1来表示)中的tf-idf(t,d)的值
tf-idf('the',d1)=tf('the',d1)*idf('the',d1)
=1*log3/(1+3)=1*log0.75=-0.125

这些计算都有现成的公式,以下我们以Scikit-learn中公式或库来计算。

import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
count=CountVectorizer()
docs=np.array(['The sun is  shining',
'The weather is sweet',
'The sun is shining and the the weather is  sweet'])
bag=count.fit_transform(docs)

print(count.vocabulary_) #vocabulary_表示字典

运行结果:
{'the': 5, 'sun': 3, 'is': 1, 'shining': 2, 'weather': 6, 'sweet': 4, 'and': 0}

print(bag.toarray())
打印结果为:
[[0 1 1 1 0 1 0]
 [0 1 0 0 1 1 1]
 [1 2 1 1 1 2 1]]

以下求文档的tf-idf

from sklearn.feature_extraction.text import TfidfTransformer
tfidf=TfidfTransformer()
np.set_printoptions(precision=2)
print(tfidf.fit_transform(count.fit_transform(docs)).toarray())
#打印结果为:
[[ 0.    0.43  0.56  0.56  0.    0.43  0.  ]
 [ 0.    0.43  0.    0.    0.56  0.43  0.56]
 [ 0.4   0.48  0.31  0.31  0.31  0.48  0.31]]

说明:sklearn计算tf-idf时,还进行了归一化处理,其中TfidfTransformer缺省使用L2范数。
我们按照sklearn的计算方式,即tf-idf(t,d)=tf(t,d)*(log(1+n_d)/(1+df(d,t))+1),不难验证以上结果,以第一语句为例。
第一个语句的v=tf-idf(t,d1)=[0,1,1.28,1.28,0,1,0]

tf-idf(t,d1)norm=||v||/(〖||v||〗_2)=v/sqrt(∑▒v_i^2 )=v/2.29
=[0,0.43,0.56,0.56,0,0.43,0]
这个与上面的计算结果一致。

9.2情感分析实例

情感分析,有时也称为观点挖掘,是自然语言处理(NLP)领域一个非常重要的一个分支,它主要分析评论、文章、报道等的情感倾向,掌握或了解人们这些情感倾向非常重要。这些倾向对我们处理后续很多事情都有指定或借鉴作用。
这里我们以人们对一个互联网电影的评论为数据集。该数据集包含50,000个关于电影的评论,正面评论高于6星,负面评论低于5星。
以下我们采用词袋模型(BOW),用Python语言处理包(NLTK)对数据进行处理,由于数据量比较大,我们使用随机梯度下载方法来优化,利用逻辑蒂斯回归分类器进行分类。具体步骤如下:

9.2.1 加载数据

下载数据:
http://ai.stanford.edu/~amaas/data/sentiment

tar -zxf aclImdb_v1.tar.gz

文件结构:
在aclImdb目录下有test和train等目录,在train和test目录下,各有二级子目录neg和pos目录。其中neg目录存放大量评级负面或消极txt文件,pos存放大量评级为正面或积极的评论txt文件

hadoop@master:~/data/nlp_im/aclImdb$ ll
total 1732
-rw-r--r-- 1 hadoop hadoop 903029 Jun 12  2011 imdbEr.txt
-rw-r--r-- 1 hadoop hadoop 845980 Apr 13  2011 imdb.vocab
-rw-r--r-- 1 hadoop hadoop   4037 Jun 26  2011 README
drwxr-xr-x 4 hadoop hadoop   4096 Aug 29 15:16 test/
drwxr-xr-x 5 hadoop hadoop   4096 Aug 29 15:16 train/
hadoop@master:~/data/nlp_im/aclImdb$ cd train/
hadoop@master:~/data/nlp_im/aclImdb/train$ ll
total 66580
-rw-r--r-- 1 hadoop hadoop 21021197 Apr 13  2011 labeledBow.feat
drwxr-xr-x 2 hadoop hadoop   352256 Aug 29 15:18 neg/
drwxr-xr-x 2 hadoop hadoop   352256 Aug 29 15:16 pos/
drwxr-xr-x 2 hadoop hadoop  1409024 Aug 29 15:16 unsup/
-rw-r--r-- 1 hadoop hadoop 41348699 Apr 13  2011 unsupBow.feat
-rw-r--r-- 1 hadoop hadoop   612500 Apr 12  2011 urls_neg.txt
-rw-r--r-- 1 hadoop hadoop   612500 Apr 12  2011 urls_pos.txt
-rw-r--r-- 1 hadoop hadoop  2450000 Apr 12  2011 urls_unsup.txt

把这些文件附加到df中,同时显示加载进度。

import pyprind
import pandas as pd
import os
pbar=pyprind.ProgBar(50000)
labels={'pos':1,'neg':0}
df=pd.DataFrame()
for s in ('test','train'):
    for l in ('pos','neg'):
        path='./aclImdb/%s/%s'% (s,l)
        for file in os.listdir(path):
            with open(os.path.join(path,file),'r') as infile:
                txt=infile.read()
            df=df.append([[txt,labels[l]]],ignore_index=True)
            pbar.update()
df.columns=['review','snetiment']

运行了大概2分多钟:
0% [##############################] 100% | ETA: 00:00:00
Total time elapsed: 00:02:42

重排标签顺序,并把数据集存储到cvs文件中

import numpy as np
np.random.seed(0)
df=df.reindex(np.random.permutation(df.index))
df.to_csv('./movie_data.csv',index=False)

查看或检查存储数据

df=pd.read_csv('./movie_data.csv')
df.head(4)

查询结果如下:

这里有个拼写错误,snetiment,应该是sentiment,如果要更改过来,只要修改df的列名即可:

df.columns=['review','sentiment']

9.2.2数据预处理

1)、首先使用自然语言处理工具NLTK,下载停用词,然后过来文件。

import nltk
nltk.download('stopwords')

2)、对文件进行预处理,过来停用词、删除多余符号等。

from nltk.corpus import stopwords
import re
stop=stopwords.words('english')
def tokenizer(text):
    text=re.sub('<[^>]*>','',text)
    emoticons=re.findall('(?::|;|=)(?:-)?(?:</span>|<span class=" />|D|P)',text.lower())
    text=re.sub('[\W]+',' ',text.lower())+' '.join(emoticons).replace('-','')
    tokenized=[w for w in text.split() if w not in stop]
    return tokenized

3)、定义一个生成器函数,从csv文件中读取文档

def stream_docs(path):
    with open(path,'r') as csv:
        next(csv)# skip header
        for line in csv:
            text,label=line[:-3],int(line[-2])
            yield text,label

4)、定义一个每次获取的小批量数据的函数

def get_minibatch(doc_stream,size):
    docs,y=[],[]
    try:
        for _ in range(size):
            text,label=next(doc_stream)
            docs.append(text)
            y.append(label)
    except StopIteration:
            return None,None
    return docs,y

5)、利用sklearn中的HashingVectorizer进行语句的特征化、向量化等。

from sklearn.feature_extraction.text import HashingVectorizer
from sklearn.linear_model import SGDClassifier
vect=HashingVectorizer(decode_error='ignore',n_features=2**21,preprocessor=None,tokenizer=tokenizer)
clf=SGDClassifier(loss='log',random_state=1,n_iter=1)
doc_stream=stream_docs(path='./movie_data.csv')

9.2.3训练模型

训练模型

import pyprind
pbar=pyprind.ProgBar(45)
classes=np.array([0,1])
for _ in range(45):
    x_train,y_train=get_minibatch(doc_stream,size=1000)
    if not x_train:
        break
    x_train=vect.transform(x_train)
    clf.partial_fit(x_train,y_train,classes=classes)
    pbar.update()

9.2.4评估模型

x_test,y_test=get_minibatch(doc_stream,size=5000)
x_test=vect.transform(x_test)
print('accuracy: %.3f' % clf.score(x_test,y_test))

测试结果为:
accuracy: 0.879
效果还不错,准确率达到近88%

第8章 航空公司客户价值分析

传统的识别客户价值应用最广泛的模型主要通过3个指标(最近消费时间间隔(Recency)、消费频率(Frequency)和消费金额(Monetary))来进行客户细分,识别出价值高的客户,简称RFC模型。
在RFC模型中,消费金额表示在一段时间内,客户购买产品的总金额。但是不适用于航空公司的数据处理。因此我们用客户在一段时间内的累计飞行里程M和客户在一定时间内乘坐舱位的折扣系数C代表消费金额。再在模型中增加客户关系长度L,所以我们用LRFMC模型。

8.1 探索数据

探索数据的缺失情况、异常值等。

#-*- coding: utf-8 -*-
#对数据进行基本的探索
#返回缺失值个数以及最大最小值

import pandas as pd

#航空原始数据,第一行为标题
datafile= '/home/hadoop/data/air_customer/air_data.csv'
#数据探索结果表
resultfile = '/home/hadoop/data/air_customer/tmp/explore.xls'
data = pd.read_csv(datafile, encoding = 'utf-8')
explore = data.describe().T
#计算空值
explore['null'] = len(data)-explore['count']
##统计空值,最大,最小值等
explore = explore[['null', 'max', 'min']]
explore

数据部分结果

null max min
MEMBER_NO 0.0 62988.000000 1.00
FFP_TIER 0.0 6.000000 4.00
AGE 420.0 110.000000 6.00
FLIGHT_COUNT 0.0 213.000000 2.00
BP_SUM 0.0 505308.000000 0.00
EP_SUM_YR_1 0.0 0.000000 0.00
EP_SUM_YR_2 0.0 74460.000000 0.00
SUM_YR_1 551.0 239560.000000 0.00
SUM_YR_2 138.0 234188.000000 0.00
SEG_KM_SUM 0.0 580717.000000 368.00
WEIGHTED_SEG_KM 0.0 558440.140000 0.00
AVG_FLIGHT_COUNT 0.0 26.625000 0.25

#保存结果
explore.to_excel(resultfile)

8.2 预处理数据

根据上面的数据统计,丢弃所有不符合的数据,
1、票价为空的
2、票价为0,但是折扣不是0,而且飞行里程大于0,
这样的数据是错误数据,直接删除

#票价非空值才保留
data = data[data['SUM_YR_1'].notnull()]
data = data[data['SUM_YR_2'].notnull()]
# 去掉票价为0,但是折扣不是0,或飞行里程大于0
data = data.drop(data['SUM_YR_1'] ==0 & (data['SEG_KM_SUM'] != 0) | (data['avg_discount'] > 0))
##保存清理后数据
cleanedfile = '/home/hadoop/data/air_customer/tmp/data_cleaned.csv'
data.to_csv(cleanedfile, encoding = 'utf-8')

8.3 数据归约

# 属性规约:去掉不相管的属性,只留下与LRFMC模型相关的属性
# FFP_DATE 入会时间
# LOAD_TIME 观测窗口结束时间
# FLIGHT_COUNT 飞行频率
# avg_discount 平均折扣
# SEG_KM_SUM 观测窗口总飞行公里数
# LAST_TO_END 最后一次乘机时间至观察窗口末端时长

data = data[['FFP_DATE','LOAD_TIME', 'FLIGHT_COUNT', 'avg_discount', 'SEG_KM_SUM','LAST_TO_END']]
#保存数据
cleanedfile = '/home/hadoop/data/air_customer/tmp/data_cleaned.csv'
data.to_csv(cleanedfile, encoding = 'utf-8')

数据变化的LRFMC数据:
L = LOAD_TIME - FFP_DATE (观测窗口时间 - 入会时间)
R = LOAD_TIME - LAST_TO_END (观测窗口时间 - 最后一次乘机时间)
F = FLIGHT_COUNT
M = SEG_KM_SUM
C = avg_discount

from datetime import datetime
import time

def normal_time(date):
'''
格式化数据
'''

return datetime.strptime(date,"%Y/%m/%d")

def interval_time(dd):
'''
计算时间间隔,以月为单位
'''

return dd.days / 30

# 计算LRFMC数据
data_LRFMC= pd.read_csv(cleanedfile,encoding='utf-8')
# data_LRFMC.columns = ['L', 'R', 'F','M', 'C']
data_LRFMC['L'] = (data['LOAD_TIME'].apply(normal_time) - data['FFP_DATE'].apply(normal_time)).apply(interval_time)
data_LRFMC['R'] = data['LAST_TO_END']
data_LRFMC['F'] = data['FLIGHT_COUNT']
data_LRFMC['M'] = data['SEG_KM_SUM']
data_LRFMC['C'] = data['avg_discount']

# 显示数据的描述,最大值和最小值
data_LRFMC_describe = data_LRFMC.describe().T
data_LRFMC_describe = data_LRFMC_describe[['max','min']].T
LRFMCfile = '/home/hadoop/data/air_customer/tmp/LRFMC.csv'
#数据写入文件
data_LRFMC.to_csv('LRFMCfile')
data_LRFMC_describe[['L','R','F','M','C']]
L R F M C
max 114.0 731.0 213.0 375074.0 1.500000
min 12.0 1.0 2.0 751.0 0.136017

最大值和最小值间隔较大,需要对数据进行标准化。

data_LRFMC=data_LRFMC[['L','R','F','M','C']]

# 标准化、重命名、写入文件
data_normal = (data_LRFMC - data_LRFMC.mean()) / (data_LRFMC.std())
data_normal.columns = ['Z'+i for i in data_normal.columns]

data_normafile = '/home/hadoop/data/air_customer/tmp/data_norma.csv'
data_normal.to_csv('data_normafile')

8.4 训练模型

数据处理完毕,下面进行模型的构建,
1、使用聚类算法,将数据生成5类用户
2、针对聚类结果进行特征分析

from sklearn.cluster import KMeans
k = 5
kmodel = KMeans(k,n_jobs=2) #得到模型
data_normal1=data_normal.dropna()
kmodel.fit(data_normal1) #训练模型

# 查看聚类中心和对应的类别
print(kmodel.cluster_centers_)
print(kmodel.labels_)
[[ 0.17343002 -0.07166046 -0.11266706 -0.09427382 2.69339174]
[-0.69751929 -0.40379498 -0.1697544 -0.17211799 -0.21902225]
[-0.3148445 1.67877305 -0.57462591 -0.5402772 -0.11557315]
[ 0.47921629 -0.79631365 2.48086723 2.43227193 0.27007082]
[ 1.15091348 -0.36698843 -0.09473823 -0.10441368 -0.13723191]]

print(kmodel.labels_)
[3 3 3 ..., 1 1 4]

8.4 可视化数据结果

import matplotlib.pyplot as plt

clu = kmodel.cluster_centers_
x = [1,2,3,4,5]

colors = ['red','green','yellow','blue','black']
for i in range(5):
plt.plot(x,clu[i],label='clustre '+str(i),linewidth=6-i,color=colors[i],marker='o')

plt.xlabel('L R F M C')
plt.ylabel('values')
plt.show()

8.5 客户价值分析

注意kmeans每次运行的时候得到的类会有差别,簇号也会相应的改变,但是中间点基本不会改变:
cluster L R F M C color
客户群1 0.17343 -0.07166 -0.11267 -0.09427 0.693392 red
客户群2 -0.69752 -0.40379 -0.16975 -0.17212 -0.21902 gree
客户群3 -0.31484 1.678773 -0.57463 -0.54028 -0.11557 yellow
客户群4 0.479216 -0.79631 2.480867 2.432272 0.270071 blue
客户群5 1.150913 -0.36699 -0.09474 -0.10441 -0.13723 black

我们重点关注的是L,F,M,从图中可以看到:
1、客户群4[blue] 的F,M很高,L也不低,可以看做是重要保持的客户;
2、客户群3[yellow] 的R比较高,为重要发展客户
3、客户群1[red] 重要挽留客户,原因:C较高,但是F,M较低
4、客户群2[green] 一般客户
5、客户群5[black] 低价值客户

第7章集成学习

7.1集成学习概述

集成学习(ensemble learning)可以说是现在非常火爆的机器学习方法了。它本身不是一个单独的机器学习算法,而是通过构建并结合多个机器学习器来完成学习任务。也就是我们常说的“博采众长”。集成学习可以用于分类问题集成,回归问题集成,特征选取集成,异常点检测集成等等,可以说所有的机器学习领域都可以看到集成学习的身影,甚至在目前最火的深度学习中也常见其影子。
集成学习的主要思想:对于一个比较复杂的任务,综合许多人的意见来进行决策往往比一家独大好,正所谓集思广益。其过程如下:

7.2投票分类器(VotingClassifier)

投票分类器的原理是结合了多个不同的机器学习分类器,使用多数票或者平均预测概率(软票),预测类标签。这类分类器对一组相同表现的模型十分有用,同时可以平衡各自的弱点。投票分类又可进一步分为多数投票分类(Majority Class Labels)、加权平均概率(soft vote,软投票)。
7.2.1多数投票分类(MajorityVote Class)
多数投票分类的分类原则为预测标签不同时,按最多种类为最终分类;如果预测标签相同时,则按顺序,选择排在第1的标签为最终分类。举例如下:
 预测类型的标签为该组学习器中相同最多的种类:例如给出的分类如下
 分类器1 -> 标签1
 分类器2 -> 标签1
 分类器3 -> 标签2
投票分类器(voting=‘hard’)则该预测结果为‘标签1’。
 在各个都只有一个的情况下,则按照顺序来,如下:
 分类器1 -> 标签2
 分类器2 -> 标签1
最终分类结果为“标签2”

7.2.1.1Iris数据集概述
首先,我们取得数据,下面这个链接中有数据的详细介绍,并可以下载数据集。https://archive.ics.uci.edu/ml/datasets/Iris
从数据的说明上,我们可以看到Iris有4个特征,3个类别。但是,我们为了数据的可视化,我们只保留2个特征(sepal length和petal length)。数据可视化代码如下:

%matplotlib inline
import pandas as pd
import matplotlib.pylab as plt
import numpy as np

# 加载Iris数据集作为DataFrame对象
df = pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data', header=None)
X = df.iloc[:, [0, 2]].values # 取出2个特征,并把它们用Numpy数组表示

plt.scatter(X[:50, 0], X[:50, 1],color='red', marker='o', label='setosa') # 前50个样本的散点图
plt.scatter(X[50:100, 0], X[50:100, 1],color='blue', marker='x', label='versicolor') # 中间50个样本的散点图
plt.scatter(X[100:, 0], X[100:, 1],color='green', marker='+', label='Virginica') # 后50个样本的散点图
plt.xlabel('petal length')
plt.ylabel('sepal length')
plt.legend(loc=2) # 把说明放在左上角,具体请参考官方文档
plt.show()

示例代码如下:

from sklearn import datasets
from sklearn import cross_validation
from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import VotingClassifier

iris = datasets.load_iris()
X, y = iris.data[:, 1:3], iris.target

clf1 = LogisticRegression(random_state=1)
clf2 = RandomForestClassifier(random_state=1)
clf3 = GaussianNB()

eclf = VotingClassifier(estimators=[('lr', clf1), ('rf', clf2), ('gnb', clf3)], voting='hard', weights=[2,1,2])

for clf, label in zip([clf1, clf2, clf3, eclf], ['Logistic Regression', 'Random Forest', 'naive Bayes', 'Ensemble']):
scores = cross_validation.cross_val_score(clf, X, y, cv=5, scoring='accuracy')
print("Accuracy: %0.2f (+/- %0.2f) [%s]" % (scores.mean(), scores.std(), label))

运行结果如下:
Accuracy: 0.90 (+/- 0.05) [Logistic Regression]
Accuracy: 0.93 (+/- 0.05) [Random Forest]
Accuracy: 0.91 (+/- 0.04) [naive Bayes]
Accuracy: 0.95 (+/- 0.05) [Ensemble]

7.2.2多数投票分类(MajorityVote Class)
相对于多数投票(hard voting),软投票返回预测概率值的总和最大的标签。可通过参数weights指定每个分类器的权重;若权重提供了,在计算时则会按照权重计算,然后取平均;标签则为概率最高的标签。
举例说明,假设有3个分类器,3个类,每个分类器的权重为:w1=1,w2=1,w3=1。如下表:

下面例子为线性SVM,决策树,K邻近分类器:

from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from itertools import product
from sklearn.ensemble import VotingClassifier

#Loading some example data
iris = datasets.load_iris()
X = iris.data[:, [0,2]]
y = iris.target

#Training classifiers
clf1 = DecisionTreeClassifier(max_depth=4)
clf2 = KNeighborsClassifier(n_neighbors=7)
clf3 = SVC(kernel='rbf', probability=True)
eclf = VotingClassifier(estimators=[('dt', clf1), ('knn', clf2), ('svc', clf3)], voting='soft', weights=[2,1,2])

clf1 = clf1.fit(X,y)
clf2 = clf2.fit(X,y)
clf3 = clf3.fit(X,y)
eclf = eclf.fit(X,y)

##这些分类器分类结果
x_min,x_max = X[:,0].min()-1,X[:,0].max()+1
y_min,y_max = X[:,1].min()-1,X[:,1].max()+1
xx,yy = np.meshgrid(np.arange(x_min,x_max,0.1),
np.arange(y_min,y_max,0.1))
f, axarr = plt.subplots(2, 2, sharex='col', sharey='row', figsize=(10, 8))
for idx, clf, tt in zip(product([0, 1], [0, 1]),
[clf1, clf2, clf3, eclf],
['Decision Tree (depth=4)', 'KNN (k=7)',
'Kernel SVM', 'Soft Voting']):

Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

axarr[idx[0], idx[1]].contourf(xx, yy, Z, alpha=0.4)
axarr[idx[0], idx[1]].scatter(X[:, 0], X[:, 1], c=y, alpha=0.8)
axarr[idx[0], idx[1]].set_title(tt)
plt.show()

7.3自适应分类器(Adaboost)

Adaboost是一种迭代算法,其核心思想是针对同一个训练集训练不同的分类器(弱分类器),然后把这些弱分类器集合起来,构成一个更强的最终分类器(强分类器)。其算法本身是通过改变数据分布来实现的,它根据每次训练集之中每个样本的分类是否正确,以及上次的总体分类的准确率,来确定每个样本的权值。将修改过权值的新数据集送给下层分类器进行训练,最后将每次训练得到的分类器最后融合起来,作为最后的决策分类器。使用adaboost分类器可以排除一些不必要的训练数据特征,并放在关键的训练数据上面。
下面的例子展示了AdaBoost算法拟合100个弱学习器

from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_iris
from sklearn.ensemble import AdaBoostClassifier

iris = load_iris()
clf = AdaBoostClassifier(n_estimators=100)
scores = cross_val_score(clf, iris.data, iris.target)
scores.mean()

输出结果为:
0.95996732026143794

实现原理:
1、假设我们有如下样本图:

2、第一次分类

第2次分类

第3次分类

第4次综合以上分类

第6章 模型评估与模型优化

6.1模型评估一般方法

对模型的性能或优劣进行评估,通过评价指标来衡量。模型评估是机器学习任务中非常重要的一环。不同的机器学习任务有着不同的评价指标,同时同一种机器学习任务也有着不同的评价指标,每个指标的着重点不一样。如分类(classification)、回归(regression)、聚类(clustering)、SVM、推荐(recommendation)等。并且很多指标可以对多种不同的机器学习模型进行评价,如精确率-召回率(precision-recall),可以用在分类、推荐等。像分类、回归、SVM都是监督式机器学习,本节的重点便是监督式机器学习的一些评价指标。
在实际情况中,我们会用不同的度量去评估我们的模型,而度量的选择,完全取决于模型的类型和模型以后要做的事。下面我们就会学习到一些用于评价模型的常用度量和图表以及它们各自的使用场景。

6.1.1分类评价指标
分类是指对给定的数据记录预测该记录所属的类别。并且类别空间已知。它包括二分类与多分类,二分类便是指只有两种类别,其输出不是0就是1;或是与否等,也可以称为“负”(negative)与正(positive)两种类别,一般在实际计算中,将其映射到“0”-“1” class中;而多分类则指类别数超过两种。下面主要根据二分类的评价指标进行讲解,不过同时它们也可扩展到多分类任务中。下面对分类中一些常用的评价指标进行介绍。
1)混淆矩阵(Confusion Matrix)
混淆矩阵显示了分类模型相对数据的真实输出(目标值)的正确预测和不正确预测数目。矩阵为NxN,其中N为目标值(类)数目。这类模型的性能通常使用矩阵中的数据评估。下表为两个类别(阳性和阴性)的2x2混淆矩阵。

常用术语:
阳性 (P, positive)
阴性 (N, Negative)
真阳性 (TP, true positive):正确的肯定。又称:命中 (hit)
真阴性 (TN, true negative):正确的否定。又称:正确拒绝 (correct rejection)
伪阳性 (FP, false positive):错误的肯定,又称:假警报 (false alarm)、第二型错误
伪阴性 (FN, false negative):错误的否定,又称:未命中(miss)、第一型错误
灵敏度(sensitivity)或真阳性率(TPR, true positive rate):sensitivity = TP/(TP + FN);
灵敏度又称为召回率(Recall)=TP/(TP+FN) = 1 - FN/P
特异性(真阴性率)specificity = TN /(FP + TN)
准确度 (ACC, accuracy):ACC = (TP + TN) / (P + N)
预测正确的数占样本数的比例。
F1评分:
精度和灵敏度的调和平均数。
F1 = 2 precision * recall / (precision+recall) = 2TP/(2TP+FP+FN)

示例:

混淆矩阵 目标
阳性 阴性
模型 阳性 70 20 阳性预测值 0.78
阴性 30 80 阴性预测值 0.73
灵敏度 特异度 准确度 = 0.75
0.7 0.8

2)ROC曲线
判定方法:ROC曲线应尽量偏离参考线。
原理:ROC全称为Receiver Operation Characteristic Curve,ROC曲线其实就是从混淆矩阵衍生出来的图形,其横坐标为1-Specificity,纵坐标为Sensitivity。

上面那条曲线就是ROC曲线,随着阈值的减小,更多的值归于正类,敏感度和1-特异度也相应增加,所以ROC曲线呈递增趋势。而那条45度线是一条参照线,也就是说ROC曲线要与这条曲线比较。
简单的说,如果我们不用模型,直接随机把客户分类,我们得到的曲线就是那条参照线,然而我们使用了模型进行预测,就应该比随机的要好,所以ROC曲线要尽量远离参照线,越远,我们的模型预测效果越好。
3)AUC(ROC曲线下面积)
判定方法:AUC应该大于0.5.
原理:ROC曲线是根据与那条参照线进行比较来判断模型的好坏,但这只是一种直觉上的定性分析,如果我们需要精确一些,就要用到AUC,也就是ROC曲线下面积。

看上图,参考线的面积是0.5,ROC曲线与它偏离越大,ROC曲线就越往左上方靠拢,它下面的面积(AUC)也就越大,这里面积是0.869。我们可以根据AUC的值与0.5相比,来评估一个分类模型的预测效果。
6.1.2回归评估指标
均方差(MSE,Mean Squared Error):
(∑(prec-act)**2)/n (prec为预测值,act为实际值,n为总样本数)
均方根差(RMSE,Root Mean Squared Error):
就是MSE开根号
平均绝对值误差(MAE,Mean Absolute Error):
(∑|prec-act|)/n
6.1.3评估指标在Sciktlearn中对应的函数
评估分类模型:

评估回归模型:

6.2模型评估与优化

对模型评估和优化时,我们常常使用训练集-验证集二划分验证(Hold-out validation)、K折交叉验证(k-Cross-validation)、超参数调优(hyperparameter tuning)等方法。三者从不同的层次对机器学习模型进行校验。Hold-out validation是评估机器学习模型泛化性能一个经典且常用的方法,通过这种方法,我们将把数据集划分为训练集和测试集,前者用于模型训练,后者用于性能的评估。
6.2.1Hold-out validation方法
使用Hold-out validation还可以采用更好的一种划分方法,把数据划分为三个部分,训练集、验证集和测试集,训练集用于模型的拟合,模型在验证集上的性能表现作为模型选择的标准,模型评估在测试集上。Hold-out validation的一般流程图如下:

6.2.2 K-fold交叉验证
一种更好,但是计算量更大的交叉验证方法是K-fold交叉验证。如同Holdout方法,K-fold交叉验证也依赖于训练数据的若干个相互独立子集。主要的区别在于K-fold交叉验证一开始就随机把数据分割成K个不相连的子集,成为folds(一般称作K折交叉验证,K的取值有5、10或者20)。每次留一份数据作为测试集,其余数据用于训练模型。
当每一份数据都轮转一遍之后,将预测的结果整合,并与目标变量的真实值比较来计算准确率。K-fold交叉验证的图形展示如下:

以下我们通过实例来进一步说明K-折交叉验证的使用方法及其优越性。这里我们使用UCI数据集中一个有关威斯康星乳腺癌数据集。
1)读取数据

import pandas as pd

df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/wdbc.data', header=None)
df.head()

部分数据:

2)对标签数据转换为整数,目前是字符M(恶性)或B(良性)

from sklearn.preprocessing import LabelEncoder
X = df.loc[:, 2:].values
y = df.loc[:, 1].values
le = LabelEncoder()
y = le.fit_transform(y)
le.transform(['M', 'B'])

转换结果如下:
array([1, 0])
3)把数据划分为训练数据和测试数据,划分比例为7:3

from sklearn.cross_validation import train_test_split

X_train, X_test, y_train, y_test = \
train_test_split(X, y, test_size=0.20, random_state=1)

4)使用PCA对原数据集进行降维
这里我们采用管道或流水线方式,把对数据的标准化等组装在一起。

from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline

pipe_lr = Pipeline([('scl', StandardScaler()),
('pca', PCA(n_components=2)),
('clf', LogisticRegression(random_state=1))])

5)在训练集上进行k-折交叉验证
这里我们取k=10,采用sklearn中StratifiedKFold函数。

import numpy as np
from sklearn.cross_validation import StratifiedKFold

kfold = StratifiedKFold(y=y_train,
n_folds=10,
random_state=1)

scores = []
for k, (train, test) in enumerate(kfold):
pipe_lr.fit(X_train[train], y_train[train])
score = pipe_lr.score(X_train[test], y_train[test])
scores.append(score)
print('Fold: %s, Class dist.: %s, Acc: %.3f' % (k+1, np.bincount(y_train[train]), score))

print('\nCV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores)))

运行结果如下:
Fold: 1, Class dist.: [256 153], Acc: 0.891
Fold: 2, Class dist.: [256 153], Acc: 0.978
Fold: 3, Class dist.: [256 153], Acc: 0.978
Fold: 4, Class dist.: [256 153], Acc: 0.913
Fold: 5, Class dist.: [256 153], Acc: 0.935
Fold: 6, Class dist.: [257 153], Acc: 0.978
Fold: 7, Class dist.: [257 153], Acc: 0.933
Fold: 8, Class dist.: [257 153], Acc: 0.956
Fold: 9, Class dist.: [257 153], Acc: 0.978
Fold: 10, Class dist.: [257 153], Acc: 0.956

CV accuracy: 0.950 +/- 0.029
准确率达到95%,说明结果还不错。
6)改进的k-折交叉验证
k-折交叉验证比较耗资源,而且抽取数据是随机的,这就可能导致有些分发中的类别比例不很恰当,是否有更好方法?以下我们采用分层k-折交叉验证法,使用scikit-learn中cross_val_score。

from sklearn.cross_validation import cross_val_score

scores = cross_val_score(estimator=pipe_lr,
X=X_train,
y=y_train,
cv=10,
n_jobs=1)
print('CV accuracy scores: %s' % scores)
print('CV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores)))

运算结果如下:
CV accuracy scores: [ 0.89130435 0.97826087 0.97826087 0.91304348 0.93478261 0.97777778 0.93333333 0.95555556 0.97777778 0.95555556]
CV accuracy: 0.950 +/- 0.029

n_jobs参数很特别,使用这个参数,我们可以把K轮的交叉验证分布到几个CPU块上,如果n_jobs=2就是2块,如果n_jobs=-1,则系统将利用所有CPU进行计算。

6.3 利用验证曲线查看是否过拟合或欠拟合

验证曲线反应参数与性能指标之间的关系图,通过验证曲线图有利于定位过拟合或欠拟合等问题,是提高模型性能的有效工具。以下我们以建立在上个数据集的逻辑斯谛模型中的正则化参数C与准确率间的关系为例,使用validation_curve函数,该函数对分类算法,默认使用分层K折交叉验证来评估模型性能,使用scikit-learn来绘制验证曲线。

import matplotlib.font_manager as fm
myfont = fm.FontProperties(fname='/home/hadoop/anaconda3/lib/python3.6/site-packages/matplotlib/mpl-data/fonts/ttf/simhei.ttf')

from sklearn.learning_curve import validation_curve

param_range = [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]
train_scores, test_scores = validation_curve(
estimator=pipe_lr,
X=X_train,
y=y_train,
param_name='clf__C',
param_range=param_range,
cv=10)

train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

plt.plot(param_range, train_mean,
color='blue', marker='o',
markersize=5, label='训练准确率')

plt.fill_between(param_range, train_mean + train_std,
train_mean - train_std, alpha=0.15,
color='blue')

plt.plot(param_range, test_mean,
color='green', linestyle='--',
marker='s', markersize=5,
label='验证准确率')

plt.fill_between(param_range,
test_mean + test_std,
test_mean - test_std,
alpha=0.15, color='green')

plt.grid()
plt.xscale('log')
plt.legend(loc='lower right',prop=myfont)
plt.xlabel('参数C',fontproperties=myfont,size=12)
plt.ylabel('准确率',fontproperties=myfont,size=12)
plt.ylim([0.8, 1.0])
plt.tight_layout()
plt.show()


从以上图形不难看出,参数C的最优点是0.1附近,如果加大正则化强度(C较小),会导致一定程度的欠拟合;如果降低正则化强度,可能导致模型的过拟合。

6.4 利用参数网格搜索调优模型

在机器学习中,有两类参数:一种是通过训练数据得到的参数,如回归模型、神经网络中系统;一种是需要单独进行优化的参数,如正则化系统、迭代次数、树的深度等,这类参数称为调优参数,又称为超参。
这里我们将介绍一种功能强大的超参数优化方法,即网格搜索(grid search)方法,通过这种方法自动选择最优参数组合,并选择最优模型。以下利用Scikit learn中GridSearchCV函数来实现网格搜索。

from sklearn.grid_search import GridSearchCV
from sklearn.svm import SVC

pipe_svc = Pipeline([('scl', StandardScaler()),
('clf', SVC(random_state=1))])

param_range = [0.0001, 0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0]

param_grid = [{'clf__C': param_range,'clf__gamma': param_range, 'clf__kernel': ['linear','rbf']}]
gs = GridSearchCV(estimator=pipe_svc,
param_grid=param_grid,
scoring='accuracy',
cv=10,
n_jobs=-1)
gs = gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_params_)

运行结果:
0.978021978021978
{'clf__C': 0.1, 'clf__gamma': 0.0001, 'clf__kernel': 'linear'}

由此可知,最优参数组合是:clf__C=0.1 , clf__kernel='linear', clf__gamma=0.0001
这个组合对应模型性能达到近98%。
此外,还有随机搜索(randomized search)、嵌套交叉验证选择模型等方法。大家可作为练习去练习。这里就不展开来说了。

1、整体思路:

理解问题:查看每个变量并且根据他们的意义和对问题的重要性进行哲学分析。
单因素研究:只关注因变量(‘SalePrice’),并且进行更深入的了解。
多因素研究:分析因变量和自变量之间的关系。、
基础清洗:清洗数据集并且对缺失数据,异常值和分类数据进行一些处理。
检验假设:检查数据是否和多元分析方法的假设达到一致。
开始之前,先导入需要的库及文件:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from scipy.stats import norm
from sklearn.preprocessing import StandardScaler
from scipy import stats
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline

#bring in the six packs
df_train = pd.read_csv('./data/house_train.csv')

查看特征

df_train.columns
Index(['Id', 'MSSubClass', 'MSZoning', 'LotFrontage', 'LotArea', 'Street', 'Alley', 'LotShape', 'LandContour', 'Utilities', 'LotConfig','LandSlope', 'Neighborhood', 'Condition1', 'Condition2', 'BldgType','HouseStyle', 'OverallQual', 'OverallCond', 'YearBuilt', 'YearRemodAdd','RoofStyle', 'RoofMatl', 'Exterior1st', 'Exterior2nd', 'MasVnrType','MasVnrArea', 'ExterQual', 'ExterCond', 'Foundation', 'BsmtQual','BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinSF1','BsmtFinType2', 'BsmtFinSF2', 'BsmtUnfSF', 'TotalBsmtSF', 'Heating','HeatingQC', 'CentralAir', 'Electrical', '1stFlrSF', '2ndFlrSF','LowQualFinSF', 'GrLivArea', 'BsmtFullBath', 'BsmtHalfBath', 'FullBath','HalfBath', 'BedroomAbvGr', 'KitchenAbvGr', 'KitchenQual','TotRmsAbvGrd', 'Functional', 'Fireplaces', 'FireplaceQu', 'GarageType','GarageYrBlt', 'GarageFinish', 'GarageCars', 'GarageArea', 'GarageQual','GarageCond', 'PavedDrive', 'WoodDeckSF', 'OpenPorchSF','EnclosedPorch', '3SsnPorch', 'ScreenPorch', 'PoolArea', 'PoolQC','Fence', 'MiscFeature', 'MiscVal', 'MoSold', 'YrSold', 'SaleType','SaleCondition', 'SalePrice'], dtype='object')

2、准备工作

为了了解我们的数据,我们可以分析每个变量并且尝试理解他们的意义和与该问题的相关程度。
首先建立一个 Excel 电子表格,有如下目录:

变量 – 变量名。
类型 – 该变量的类型。这一栏只有两个可能值,“数据” 或 “类别”。 “数据” 是指该变量的值是数字,“类别” 指该变量的值是类别标签。
划分 – 指示变量划分. 我们定义了三种划分:建筑,空间,位置。
期望 – 我们希望该变量对房价的影响程度。我们使用类别标签 “高”,“中” 和 “低” 作为可能值。
结论 – 我们得出的该变量的重要性的结论。在大概浏览数据之后,我们认为这一栏和 “期望” 的值基本一致。
评论 – 我们看到的所有一般性评论。
我们首先阅读了每一个变量的描述文件,同时思考这三个问题:
我们买房子的时候会考虑这个因素吗?
如果考虑的话,这个因素的重要程度如何?
这个因素带来的信息在其他因素中出现过吗?
我们根据以上内容填好了电子表格,并且仔细观察了 “高期望” 的变量。然后绘制了这些变量和房价之间的散点图,填在了 “结论” 那一栏,也正巧就是对我们的期望值的校正。

我们总结出了四个对该问题起到至关重要的作用的变量:
OverallQual
YearBuilt.
TotalBsmtSF.
GrLivArea.
最重要的事情——分析 “房价”

df_train['SalePrice'].describe()
count 1460.000000
mean 180921.195890
std 79442.502883
min 34900.000000
25% 129975.000000
50% 163000.000000
75% 214000.000000
max 755000.000000
Name: SalePrice, dtype: float64

绘制直方图

sns.distplot(df_train['SalePrice']);


用seaborn绘图,得出结论:
-偏离正态分布
-数据正偏
-有峰值

数据偏度和峰度度量:

print("Skewness: %f" % df_train['SalePrice'].skew())
print("Kurtosis: %f" % df_train['SalePrice'].kurt())
Skewness: 1.882876
Kurtosis: 6.536282

偏度(Skewness)是描述某变量取值分布对称性的统计量。如果偏度=0,和正态分布的偏度相同;Skewness>0,长尾巴拖在右边;Skewness<0,长尾巴拖在左边,Skewness越大,分布形态偏移程度越大。 峰度(Kurtosis)是描述某变量所有取值分布形态陡缓程度的统计量。它是和正态分布相比较的:Kurtosis=0,与正态分布的陡缓程度相同;Kurtosis>0,比正态分布的高峰更加陡峭;反之亦然。

3、与数值类型的关系

(1)grlivarea/saleprice的散点图绘制
首先观察一下saleprice和数值型变量grlivarea之间的关系。我们还是可以观察到整体的趋势的,随着grlivarea的增大,saleprice有变高的趋势。存在线性关系!

var = 'GrLivArea'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));


可以看出’’SalePrice’和’'GrLivArea' 关系很密切,并且基本呈线性关系。

var = 'TotalBsmtSF'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));

TotalBsmtSF' 和 'SalePrice'关系也很密切,从图中可以看出基本呈指数分布,但从最左侧的点可以看出特定情况下’TotalBsmtSF' 对'SalePrice' 没有产生影响。

4、与类别类型的关系

(1)overallqual/saleprice的箱形图
这里有一个问题,为什么我们不用散点图了呢?那么先画一个散点图看下效果。如下图:

#box plot overallqual/saleprice
var = 'OverallQual'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
f, ax = plt.subplots(figsize=(8, 6))
fig = sns.boxplot(x=var, y="SalePrice", data=data)
fig.axis(ymin=0, ymax=800000);


可见也是有相互关系的,如果用箱形图表示的话,会更加清晰:
(2)YearBuilt/saleprice的箱形图
两个变量之间的关系没有很强的趋势性,但是可以看出建筑时间较短的房屋价格更高。

var = 'YearBuilt'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
f, ax = plt.subplots(figsize=(16, 8))
fig = sns.boxplot(x=var, y="SalePrice", data=data)
fig.axis(ymin=0, ymax=800000);
plt.xticks(rotation=90);

两个变量之间的关系没有很强的趋势性,但是可以看出建筑时间较短的房屋价格更高。

总结:

'GrLivArea' 和 'TotalBsmtSF' 与 'SalePrice'似乎线性相关,并且都是正相关。 对于 'TotalBsmtSF',线性关系的斜率十分的高。
'OverallQual' 和 'YearBuilt' 与 'SalePrice'也有关系。'OverallQual'的相关性更强, 箱型图显示了随着整体质量的增长,房价的增长趋势。

我们只分析了四个变量,但是还有许多其他变量我们也应该分析,这里的技巧在于选择正确的特征(特征选择)而不是定义他们之间的复杂关系(特征工程)。它们之间的复杂关系可以通过模型学习。

5、客观分析

上面的分析过于主观,我们应该更客观的去分析。
主要有三方面的分析:
-相关矩阵热图
-SalePrice相关矩阵热图
-最相关变量之间的散点图

(1)相关矩阵热图

#correlation matrix
corrmat = df_train.corr()
f, ax = plt.subplots(figsize=(12, 9))
sns.heatmap(corrmat, vmax=.8, square=True);


热图是观察特征和特征、特征和label之间关系的一种快速的方式。首先,最吸引注意的是两个红色方块。第一处指向TotalBsmtSF和1stFlrSF这两个变量,第二处指向Garage相关的变量。这两块显示了TotalBsmtSF和1stFlrSF的相关性、GarageCar和GarageArea的相关性很大。事实上,这种关联性很强,暗示它们存在多重共线性。我们能够推断当变量间具有多重共线性,那么它们给出的信息也基本上是一样的。Heatmaps恰恰可以检测多重共线性的情况并决定选择哪些特征,所以是一个非常重要的工具。

另外观察特征和SalePrice之间的关系,可以看到GrLivArea、TotalBsmtSF、OverallQual,也看到了一些其他的变量应该纳入我们考虑。

(2)SalePrice相关矩阵热图
筛选与SalePrice相关性强的特征,重点观察。

#saleprice correlation matrix
k = 10 #number of variables for heatmap
cols = corrmat.nlargest(k, 'SalePrice')['SalePrice'].index
cm = np.corrcoef(df_train[cols].values.T)
sns.set(font_scale=1.25)
hm = sns.heatmap(cm, cbar=True, annot=True, square=True, fmt='.2f', annot_kws={'size': 10}, yticklabels=cols.values, xticklabels=cols.values)
plt.show()

从图中可以看出:
'OverallQual', 'GrLivArea' 以及 'TotalBsmtSF' 与 'SalePrice'有很强的相关性。
'GarageCars' 和 'GarageArea' 也是相关性比较强的变量. 车库中存储的车的数量是由车库的面积决定的,它们就像双胞胎,所以不需要专门区分'GarageCars' 和 'GarageArea' ,所以我们只需要其中的一个变量。这里我们选择了'GarageCars'因为它与'SalePrice' 的相关性更高一些。
'TotalBsmtSF' 和 '1stFloor' 与上述情况相同,我们选择 'TotalBsmtSF' 。
'FullBath'几乎不需要考虑。
'TotRmsAbvGrd' 和 'GrLivArea'也是变量中的双胞胎。
'YearBuilt' 和 'SalePrice'相关性似乎不强。
(3).'SalePrice' 和相关变量之间的散点图

#scatterplot
sns.set()
cols = ['SalePrice', 'OverallQual', 'GrLivArea', 'GarageCars', 'TotalBsmtSF', 'FullBath', 'YearBuilt']
sns.pairplot(df_train[cols], size = 2.5)
plt.show();


尽管我们已经知道了一些主要特征,这一丰富的散点图给了我们一个关于变量关系的合理想法。其中,'TotalBsmtSF' 和 'GrLiveArea'之间的散点图是很有意思的。我们可以看出这幅图中,一些点组成了线,就像边界一样。大部分点都分布在那条线下面,这也是可以解释的。地下室面积和地上居住面积可以相等,但是一般情况下不会希望有一个比地上居住面积还大的地下室。
'SalePrice' 和'YearBuilt' 之间的散点图也值得我们思考。在“点云”的底部,我们可以观察到一个几乎呈指数函数的分布。我们也可以看到“点云”的上端也基本呈同样的分布趋势。并且可以注意到,近几年的点有超过这个上端的趋势。

6、缺失数据

关于缺失数据需要思考的重要问题:
这一缺失数据的普遍性如何?
缺失数据是随机的还是有律可循?
这些问题的答案是很重要的,因为缺失数据意味着样本大小的缩减,这会阻止我们的分析进程。除此之外,以实质性的角度来说,我们需要保证对缺失数据的处理不会出现偏离或隐藏任何难以忽视的真相。

#missing data
total = df_train.isnull().sum().sort_values(ascending=False)
percent = (df_train.isnull().sum()/df_train.isnull().count()).sort_values(ascending=False)
missing_data = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])
missing_data.head(20)


(1)当超过15%的数据都缺失的时候,我们应该删掉相关变量且假设该变量并不存在。根据这一条,一系列变量都应该删掉,例如'PoolQC', 'MiscFeature', 'Alley'等等,这些变量都不是很重要,因为他们基本都不是我们买房子时会考虑的因素。
(2)'GarageX' 变量群的缺失数据量都相同,由于关于车库的最重要的信息都可以由'GarageCars' 表达,并且这些数据只占缺失数据的5%,我们也会删除上述的'GarageX' 变量群。同样的逻辑也适用于 'BsmtX' 变量群。
(3)对于 'MasVnrArea' 和 'MasVnrType',我们可以认为这些因素并不重要。除此之外,他们和'YearBuilt' 以及 'OverallQual'都有很强的关联性,而这两个变量我们已经考虑过了。所以删除 'MasVnrArea'和 'MasVnrType'并不会丢失信息。

(4)最后,由于'Electrical'中只有一个损失的观察值,所以我们删除这个观察值,但是保留这一变量。

#dealing with missing data
df_train = df_train.drop((missing_data[missing_data['Total'] > 1]).index,1)
df_train = df_train.drop(df_train.loc[df_train['Electrical'].isnull()].index)
df_train.isnull().sum().max() #just checking that there's no missing data missing...

总结:处理空值,我们删掉所有有较多空值的特征,除了特征Electrical,因为只有一个缺失值,所以删除含该空值的样本即可。

7、异常值处理

异常值也是我们应该注意的东西。因为异常值能明显的影响我们的模型,并且是一个有价值的信息来源,帮助我们对特定行为有更多的见解。异常值是一个复杂的主题,并且值得研究。在这里,我们将用SalePrice的标准差和一系列的散点来进行快速分析。

7.1单因素分析
这里的关键在于如何建立阈值,定义一个观察值为异常值。
我们对数据进行正态化,意味着把数据值转换成均值为0,方差为1的数据。

#standardizing data
saleprice_scaled = StandardScaler().fit_transform(df_train['SalePrice'][:,np.newaxis]);
low_range = saleprice_scaled[saleprice_scaled[:,0].argsort()][:10]
high_range= saleprice_scaled[saleprice_scaled[:,0].argsort()][-10:]
print('outer range (low) of the distribution:')
print(low_range)
print('\nouter range (high) of the distribution:')
print(high_range)
outer range (low) of the distribution:
[[-1.83820775]
[-1.83303414]
[-1.80044422]
[-1.78282123]
[-1.77400974]
[-1.62295562]
[-1.6166617 ]
[-1.58519209]
[-1.58519209]
[-1.57269236]]

outer range (high) of the distribution:
[[ 3.82758058]
[ 4.0395221 ]
[ 4.49473628]
[ 4.70872962]
[ 4.728631 ]
[ 5.06034585]
[ 5.42191907]
[ 5.58987866]
[ 7.10041987]
[ 7.22629831]]

进行正态化后,可以看出:
低范围的值都比较相似并且在0附近分布。
高范围的值离0很远,并且7点几的值远在正常范围之外。
现在看,我们不把任何值作为异常值,但是我们应该注意这两个大于7的值。
7.2双变量分析

#bivariate analysis saleprice/grlivarea
var = 'GrLivArea'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));


这是之前GrLivArea和SalePrice关系散点图:
(1)有两个离群的'GrLivArea' 值很高的数据,我们可以推测出现这种情况的原因。或许他们代表了农业地区,也就解释了低价。 这两个点很明显不能代表典型样例,所以我们将它们定义为异常值并删除。

(2)图中顶部的两个点是七点几的观测值,他们虽然看起来像特殊情况,但是他们依然符合整体趋势,所以我们将其保留下来。

删除异常点:

df_train.sort_values(by = 'GrLivArea', ascending = False)[:2]
df_train = df_train.drop(df_train[df_train['Id'] == 1299].index)
df_train = df_train.drop(df_train[df_train['Id'] == 524].index)

(3). 'TotalBsmtSF'和'SalePrice'双变量分析

#bivariate analysis saleprice/grlivarea
var = 'TotalBsmtSF'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));


我们可以感觉到试图消除一些观察(例如,TotalBsmtSF> 3000),但是我们认为这是不值得的。 我们可以保留它们,所以我们不会做任何事情。

8、核心部分

核心部分
“房价”到底是谁?
这个问题的答案,需要我们验证根据数据基础进行多元分析的假设。
我们已经进行了数据清洗,并且发现了“SalePrice”的很多信息,现在我们要更进一步理解‘SalePrice’如何遵循统计假设,可以让我们应用多元技术。

应该测量4个假设量:
(1)正态性:它的重要在于很多统计检验是基于正态分布的,在房价预测的问题中,我们只检查了单变量的正态性。但是单变量正态性不能确保多变量的正态性,但是会其帮助作用。
(2)同方差性:假设在预测变量的范围因变量表现出同等水平的方差
(3)线性:通过观察散点图,看是否为线性关系,如果不是,需要数据转换,但是大多数情况下都是满足线性关系的。
(4)相关错误缺失
1). ‘SalePrice’
正态性:

应主要关注以下两点:
直方图 - 峰度和偏度。
正态概率图 - 数据分布应紧密跟随代表正态分布的对角线。

#histogram and normal probability plot
sns.distplot(df_train['SalePrice'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['SalePrice'], plot=plt)

可以看出,房价分布不是正态的,显示了峰值,正偏度。但是,信息没有丢失,简单的数据变换就可以解决这个问题,为了防止正偏,log变换作用很好。

#applying log transformation
df_train['SalePrice'] = np.log(df_train['SalePrice'])

#transformed histogram and normal probability plot
sns.distplot(df_train['SalePrice'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['SalePrice'], plot=plt)

从图中可以看出:
显示出了偏度
大量为0的观察值(没有地下室的房屋)
含0的数据无法进行对数变换,可用log(x+1)

2). GrLivArea
绘制直方图和正态概率曲线图:

#histogram and normal probability plot
sns.distplot(df_train['GrLivArea'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['GrLivArea'], plot=plt)

进行对数变换:

#data transformation
df_train['GrLivArea'] = np.log(df_train['GrLivArea'])

#transformed histogram and normal probability plot
sns.distplot(df_train['GrLivArea'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['GrLivArea'], plot=plt)

3). 'TotalBsmtSF'

绘制直方图和正态概率曲线图:

#histogram and normal probability plot
sns.distplot(df_train['TotalBsmtSF'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['TotalBsmtSF'], plot=plt)

从图中可以看出:
 显示出了偏度
 大量为 0 的观察值(没有地下室的房屋)
 含 0 的数据无法进行对数变换

我们建立了一个变量,可以得到有没有地下室的影响值(二值变量),我们选择忽略零值,只对非零值进行对数变换。这样我们既可以变换数据,也不会损失有没有地下室的影响。

#create column for new variable (one is enough because it's a binary categorical feature)
#if area>0 it gets 1, for area==0 it gets 0
df_train['HasBsmt'] = pd.Series(len(df_train['TotalBsmtSF']), index=df_train.index)
df_train['HasBsmt'] = 0
df_train.loc[df_train['TotalBsmtSF']>0,'HasBsmt'] = 1

对该变量进行转换

#transform data
df_train.loc[df_train['HasBsmt']==1,'TotalBsmtSF'] = np.log(df_train['TotalBsmtSF'])

#histogram and normal probability plot
sns.distplot(df_train[df_train['TotalBsmtSF']>0]['TotalBsmtSF'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train[df_train['TotalBsmtSF']>0]['TotalBsmtSF'], plot=plt)

9、同方差性

最好的测量两个变量的同方差性的方法就是图像。
1). SalePrice 和 GrLivArea 同方差性
绘制散点图:

#scatter plot
plt.scatter(df_train['GrLivArea'], df_train['SalePrice']);

2).'SalePrice' with 'TotalBsmtSF'同方差性

绘制散点图:

#scatter plot
plt.scatter(df_train[df_train['TotalBsmtSF']>0]['TotalBsmtSF'], df_train[df_train['TotalBsmtSF']>0]['SalePrice']);


可以看出'SalePrice'在整个 'TotalBsmtSF'变量范围内显示出了同等级别的变化。
这就是正态化的作用,我们仅仅需要保证特征的正态化,就能解决同方差的问题。

10、虚拟变量

对类别变量进行虚拟编码:

df_train['SaleCondition'].unique()
array(['Normal', 'Abnorml', 'Partial', 'AdjLand', 'Alloca', 'Family'], dtype=object)

#convert categorical variable into dummy
df_train = pd.get_dummies(df_train)

df_train

1457 rows × 222 columns
[/cceN_python]

结论

整个方案中,我们使用了很多《多元数据分析》中提出的方法。我们对变量进行了哲学分析,不仅对'SalePrice'进行了单独分析,还结合了相关程度最高的变量进行分析。我们处理了缺失数据和异常值,我们验证了一些基础统计假设,并且将类别变量转换为虚拟变量。

但问题还没有结束,我们还需要预测房价的变化趋势,房价预测是否适合线性回归正则化的方法?是否适合组合方法?或者一些其他的方法?这篇文章解决了数据预处理的问题,预处理后,我们选择正确的模型算法进行拟合。

参考以下网站:

http://www.jianshu.com/p/defcbbfdb324

第4章 Scikit-learn简介

自2007年发布以来,Scikit-learn已经成为Python重要的机器学习库了。Scikit-Learn简称Sklearn,支持包括分类、回归、降维和聚类四大机器学习算法。还包含了特征提取、数据处理和模型评估三大模块。
Sklearn是Scipy的扩展,建立在NumPy和matplotlib库的基础上。利用这几大模块的优势,可以大大提高机器学习的效率。
Sklearn拥有着完善的文档,上手容易,具有着丰富的API,在学术界颇受欢迎。Sklearn已经封装了大量的机器学习算法,同时Sklearn内置了大量数据集,节省了获取和整理数据集的时间
1、 Sklearn安装
推荐使用Anaconda科学计算环境,里面已经内置了NumPy、SciPy、sklearn等模块,直接可用。或者使用conda进行包管理。

conda install scikit-learn

当然也可使用pip安装,在安装前,需要先安装如下依赖库:
Python(>=2.7 or >=3.5)
NumPy
SciPy
安装以后,我们可以参考安装的版本。

import sklearn
sklearn.__version__
'0.18.1'

2、 Sklearn基础
2.1 估计器(Estimator)
估计器,很多时候可以直接理解成分类器,主要包含两个函数:
fit():训练算法,设置内部参数。接收训练集和类别两个参数。
predict():预测测试集类别,参数为测试集。
大多数scikit-learn估计器接收和输出的数据格式均为numpy数组或类似格式。
2.2 转换器(Transformer)
转换器用于数据预处理和数据转换,主要是三个方法:
fit():训练算法,设置内部参数。
transform():数据转换。
fit_transform():合并fit和transform两个方法。
2.3 流水线(Pipeline)
流水线的功能:
跟踪记录各步骤的操作(以方便地重现实验结果),对各步骤进行一个封装,确保代码的复杂程度不至于超出掌控范围。
基本使用方法:
流水线的输入为一连串的数据挖掘步骤,其中最后一步必须是估计器,前几步是转换器。输入的数据集经过转换器的处理后,输出的结果作为下一步的输入。最后,用位于流水线最后一步的估计器对数据进行分类。
每一步都用元组( ‘名称’,步骤)来表示。现在来创建流水线。
使用示例:

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import MinMaxScaler
from sklearn.neighbors import KNeighborsClassifier

scaling_pipeline = Pipeline([('scale', MinMaxScaler()),('predict', KNeighborsClassifier())])

2.4 预处理
要在from sklearn.preprocessing包下。
规范化:
MinMaxScaler :最大最小值规范化
Normalizer :使每条数据各特征值的和为1
StandardScaler :为使各特征的均值为0,方差为1
编码:
LabelEncoder :把字符串类型的数据转化为整型
OneHotEncoder :特征用一个二进制数字来表示
Binarizer :为将数值型特征的二值化
MultiLabelBinarizer:多标签二值化
pandas.get_dummies: 对离散型特征进行one-hot编码,以下是对get_dummies使用示例:
离散特征的编码分为两种情况:
1、离散特征的取值之间没有大小的意义,比如color:[red,blue],那么就使用one-hot编码
2、离散特征的取值有大小的意义,比如size:[X,XL,XXL],那么就使用数值的映射{X:1,XL:2,XXL:3}
使用pandas可以很方便的对离散型特征进行one-hot编码
(1)生成一个矩阵

import pandas as pd
df = pd.DataFrame([
['green', 'M', 10.1, 'class1'],
['red', 'L', 13.5, 'class2'],
['blue', 'XL', 15.3, 'class1']])

df.columns = ['color', 'size', 'prize', 'class label']
df

(2)把size列用数值化,对应关系为:

size_mapping = { 'XL': 3,'L': 2, 'M': 1}
size_mapping = {
'XL': 3,
'L': 2,
'M': 1}
df['size'] = df['size'].map(size_mapping)
df


(3)对列class label数值化

class_mapping = {label:idx for idx,label in enumerate(set(df['class label']))}
df['class label'] = df['class label'].map(class_mapping)
df


(4)使用get_dummies进行one-hot编码

pd.get_dummies(df)


2.5 特征
sklearn常见的转换功能:


归一化使不同规格的数据转换到同一规格。无量刚好与标准化(Normalizer)的区别:
简单来说,无量纲化是依照特征矩阵的列处理数据,标准化是依照特征矩阵的行处理数据。标准化的前提是样本各特征值服从正态分布,标准化后将其转换成标准正态分布。标准化的公式类似于标准化,不同的是样本均值和样本标准差改为特征值均值和特征值标准差。
2.5.1 特征抽取
包:from sklearn.feature_extraction
特征抽取是机器学习任务最为重要的一个环节,一般而言,它对最终结果的影响要高过机器学习算法本身。只有先把现实用特征表示出来,才能借助机器学习的力量找到问题的答案。特征选择的另一个优点在于:降低真实世界的复杂度,模型比现实更容易操纵。
一般最常使用的特征抽取技术都是高度针对具体领域的,对于特定的领域,如图像处理,在过去一段时间已经开发了各种特征抽取的技术,但这些技术在其他领域的应用却非常有限。
DictVectorizer: 将dict类型的list数据,转换成numpy array
FeatureHasher : 特征哈希,相当于一种降维技巧
image:图像相关的特征抽取
text: 文本相关的特征抽取
text.CountVectorizer:将文本转换为每个词出现的个数的向量
text.TfidfVectorizer:将文本转换为tfidf值的向量
text.HashingVectorizer:文本的特征哈希
示例

CountVectorize只数出现个数:

TfidfVectorizer:个数+归一化(不包括idf:

HashingVectorizer:

2.5.2 特征选择
包:sklearn.feature_selection
特征选择的原因如下:
(1)降低复杂度
(2)降低噪音
(3)增加模型可读性
VarianceThreshold: 删除特征值的方差达不到最低标准的特征
SelectKBest: 返回k个最佳特征
SelectPercentile: 返回表现最佳的前r%个特征
单个特征和某一类别之间相关性的计算方法有很多。最常用的有卡方检验(χ2)。其他方法还有互信息和信息熵。
2.6 降维
包:sklearn.decomposition
主成分分析算法(Principal Component Analysis, PCA)的目的是找到能用较少信息描述数据集的特征组合。它意在发现彼此之间没有相关性、能够描述数据集的特征,确切说这些特征的方差跟整体方差没有多大差距,这样的特征也被称为主成分。这也就意味着,借助这种方法,就能通过更少的特征捕获到数据集的大部分信息。
2.7 组合
包:sklearn.ensemble
组合技术即通过聚集多个分类器的预测来提高分类准确率。
常用的组合分类器方法:
(1)通过处理训练数据集。即通过某种抽样分布,对原始数据进行再抽样,得到多个训练集。常用的方法有装袋(bagging)和提升(boosting)。
(2)通过处理输入特征。即通过选择输入特征的子集形成每个训练集。适用于有大量冗余特征的数据集。随机森林(Random forest)就是一种处理输入特征的组合方法。
(3)通过处理类标号。适用于多分类的情况,将类标号随机划分成两个不相交的子集,再把问题变为二分类问题,重复构建多次模型,进行分类投票。

BaggingClassifier: Bagging分类器组合
BaggingRegressor: Bagging回归器组合
AdaBoostClassifier: AdaBoost分类器组合
AdaBoostRegressor: AdaBoost回归器组合
GradientBoostingClassifier:GradientBoosting分类器组合
GradientBoostingRegressor: GradientBoosting回归器组合
ExtraTreeClassifier:ExtraTree分类器组合
ExtraTreeRegressor: ExtraTree回归器组合
RandomTreeClassifier:随机森林分类器组合
RandomTreeRegressor: 随机森林回归器组合
使用举例

from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
AdaBoostClassifier(DecisionTreeClassifier(max_depth=1),
algorithm="SAMME",
n_estimators=200)

解释
装袋(bagging):根据均匀概率分布从数据集中重复抽样(有放回),每个自助样本集和原数据集一样大,每个自助样本集含有原数据集大约60%的数据。训练k个分类器,测试样本被指派到得票最高的类。
提升(boosting):通过给样本设置不同的权值,每轮迭代调整权值。不同提升算法之间的差别,一般是(1)如何更新样本的权值,(2)如何组合每个分类器的预测。其中Adaboost中,样本权值是增加那些被错误分类的样本的权值,分类器C_i的重要性依赖于它的错误率。
Boosting主要关注降低偏差,因此Boosting能基于泛化性能相当弱的学习器构建出很强的集成;Bagging主要关注降低方差,因此它在不剪枝的决策树、神经网络等学习器上效用更为明显。偏差指的是算法的期望预测与真实预测之间的偏差程度,反应了模型本身的拟合能力;方差度量了同等大小的训练集的变动导致学习性能的变化,刻画了数据扰动所导致的影响。

2.8 模型评估(度量)
包:sklearn.metrics
sklearn.metrics包含评分方法、性能度量、成对度量和距离计算。
分类结果度量
参数大多是y_true和y_pred。
accuracy_score:分类准确度
condusion_matrix :分类混淆矩阵
classification_report:分类报告
precision_recall_fscore_support:计算精确度、召回率、f、支持率
2.9 交叉验证
包:sklearn.cross_validation

KFold:K-Fold交叉验证迭代器。接收元素个数、fold数、是否清洗
LeaveOneOut:LeaveOneOut交叉验证迭代器
LeavePOut:LeavePOut交叉验证迭代器
LeaveOneLableOut:LeaveOneLableOut交叉验证迭代器
LeavePLabelOut:LeavePLabelOut交叉验证迭代器
LeaveOneOut(n) 相当于 KFold(n, n_folds=n) 相当于LeavePOut(n, p=1)。
LeaveP和LeaveOne差别在于leave的个数,也就是测试集的尺寸。LeavePLabel和LeaveOneLabel差别在于leave的Label的种类的个数。
LeavePLabel这种设计是针对可能存在第三方的Label,比如我们的数据是一些季度的数据。那么很自然的一个想法就是把1,2,3个季度的数据当做训练集,第4个季度的数据当做测试集。这个时候只要输入每个样本对应的季度Label,就可以实现这样的功能。
以下是实验代码:

import numpy as np
import sklearn
import sklearn.cross_validation as cross_validation
X = np.array([[1, 2], [3, 4], [5, 6], [7, 8],[9, 10]])
y = np.array([1, 2, 1, 2, 3])
def show_cross_val(method):
if method == "lolo":
labels = np.array(["summer", "winter", "summer", "winter", "spring"])
cv = cross_validation.LeaveOneLabelOut(labels)
elif method == 'lplo':
labels = np.array(["summer", "winter", "summer", "winter", "spring"])
cv = cross_validation.LeavePLabelOut(labels,p=2)
elif method == 'loo':
cv = cross_validation.LeaveOneOut(n=len(y))
elif method == 'lpo':
cv = cross_validation.LeavePOut(n=len(y),p=3)
for train_index, test_index in cv:
print("TRAIN:", train_index, "TEST:", test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
print ("X_train: ",X_train )
print ("y_train: ", y_train )
print ("X_test: ",X_test )
print ("y_test: ",y_test )
if __name__ == '__main__':
show_cross_val("lpo")

常用方法
• train_test_split:分离训练集和测试集(不是K-Fold)
• cross_val_score:交叉验证评分,可以指认cv为上面的类的实例
• cross_val_predict:交叉验证的预测
2.10 网格搜索
包:sklearn.grid_search
网格搜索最佳参数

GridSearchCV:搜索指定参数网格中的最佳参数
ParameterGrid:参数网格
ParameterSampler:用给定分布生成参数的生成器
RandomizedSearchCV:超参的随机搜索
通过best_estimator_.get_params()方法,获取最佳参数。
2.11 多分类、多标签分类
包:sklearn.multiclass
OneVsRestClassifier:1-rest多分类(多标签)策略
OneVsOneClassifier:1-1多分类策略
OutputCodeClassifier:1个类用一个二进制码表示
示例代码

from sklearn import metrics
from sklearn import cross_validation
from sklearn.svm import SVC
from sklearn.multiclass import OneVsRestClassifier
from sklearn.preprocessing import MultiLabelBinarizer
import numpy as np
from numpy import random
X=np.arange(15).reshape(5,3)
y=np.arange(5)
Y_1 = np.arange(5)
random.shuffle(Y_1)
Y_2 = np.arange(5)
random.shuffle(Y_2)
Y = np.c_[Y_1,Y_2]
def multiclassSVM():
X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.2,random_state=0)
model = OneVsRestClassifier(SVC())
model.fit(X_train, y_train)
predicted = model.predict(X_test)
print (predicted)
def multilabelSVM():
Y_enc = MultiLabelBinarizer().fit_transform(Y)
X_train, X_test, Y_train, Y_test = cross_validation.train_test_split(X, Y_enc, test_size=0.2, random_state=0)
model = OneVsRestClassifier(SVC())
model.fit(X_train, Y_train)
predicted = model.predict(X_test)
print (predicted)
if __name__ == '__main__':
multiclassSVM()

上面的代码测试了svm在OneVsRestClassifier的包装下,分别处理多分类和多标签的情况。特别注意,在多标签的情况下,输入必须是二值化的。所以需要MultiLabelBinarizer()先处理。
3、 创建自己的转换器
在特征抽取的时候,经常会发现自己的一些数据预处理的方法,sklearn里可能没有实现,但若直接在数据上改,又容易将代码弄得混乱,难以重现实验。这个时候最好自己创建一个转换器,在后面将这个转换器放到pipeline里,统一管理。
如果我们想接收一个numpy数组,根据其均值将其离散化,任何高于均值的特征值替换为1,小于或等于均值的替换为0。
代码实现:

from sklearn.base import TransformerMixin
from sklearn.utils import as_float_array

class MeanDiscrete(TransformerMixin):

#计算出数据集的均值,用内部变量保存该值。
def fit(self, X, y=None):
X = as_float_array(X)
self.mean = np.mean(X, axis=0)
#返回self,确保在转换器中能够进行链式调用(例如调用transformer.fit(X).transform(X))
return self

def transform(self, X):
X = as_float_array(X)
assert X.shape[1] == self.mean.shape[0]
return X > self.mean

参考博客:
http://www.jianshu.com/p/516f009c0875