阅读笔记:《Python机器学习及实战》

Abstract:这本书面向的是对机器学习和数据挖掘实践及竞赛感兴趣的读者,以python为基础从零开始,在不涉及数学模型和复杂编程知识的前提下,逐步学习和掌握机器学习、数据挖掘、自然语言处理工具,如scikit-learn、NLTK、Pandas、TensorFlow等。

全书分为4章:

  • 简介篇:机器学习概念和python编程知识
  • 基础篇:如何使用scikit-Learn作为基础机器学习工具
  • 进阶篇:怎样借助高级技术或模型提高机器学习系统的性能
  • 竞赛篇:如何完成kaggle竞赛

1.简介篇

1.1 机器学习

任务:

  • 监督学习 (supervised Learning):预测事物未知表现,包括分类问题(classification)、回归问题(regression),根据目标预测变量类型的不同可分为:

    • 分类问题:预测类别(已知数量,类别离散),如已知一个人的身高、体重和三围等数据预测其性别
    • 回归问题:预测连续变量,如根据房屋的面积、地理位置、建筑年代等预测销售价格(C.V)
  • 无监督学习(Unsupervised Learning):倾向于分析事物本身特性,常用技术:降维(Dimensionality Reduction), 聚类 (Clustering)

    • 数据降维:对事物的特性进行压缩和筛选,如对图像进行降维以保留最有区分度的像素组合

    • 聚类:依赖于数据的相似性从而把相似的数据样本划分为一个簇(区别于分类,簇的数量和含义非已知),如电子商务中对用户信息和购买习惯进行聚类分析,一旦找到数量多且背景相似客户群,则可针对其投放广告促销

经验:

  • 特征(feature):反映数据内在规律的信息
  • 监督学习中的经验:特征、标记(label)
    • 用一个特征向量描述一个数据样本
    • label的表现形式取决于监督学习的种类
    • 数据标注需耗费大量资源,故数据量少
    • 训练集(training set):带label的数据集,用来训练学习系统
  • 无监督学习中的经验:无label故无法做预测,但适合对数据结构作分析
  • 原始数据转化为特征向量的过程中会遭遇多种数据类型(需全部转化为具体数值运算):
    • 类别型特征(categorical)
    • 数值型特征(numerical)
    • 缺失数据(missing value)

性能(performance):

  • 评价学习模型完成任务质量的指标
    • 分类问题:准确性(accuracy)——预测正确类别的百分比
    • 回归问题:衡量预测值与实际值之间的偏差大小
  • 测试集(testing set):与TS具备相同特征,没有被用于训练
  • how:用测试集测试预测的准确率(用具备相同特征的数据,模型在测试集上的预测结果与正确结果进行比对)

1.2 python编程库

  • python
  • numpy:高级数学运算机制,高效向量与矩阵运算
  • scipy:在numpy基础上更强大、应用更广泛的科学计算包,依赖numpy
  • Matplotlib:数据分析与可视化的绘图工具包
  • scikit-learn:封装了大量ML模型
  • pandas:数据处理和分析的工具包

1.3 python基础

常用数据类型

  • 数字
  • 布尔值
  • 字符串
  • 元组(turple):以()表征,元组数据不允许修改
  • 列表:以[]表征,允许修改列表数据
  • 字典(dict):以key-value构成,以{}表征

数据运算

流程控制

函数设计

2.基础篇

Abstract:机器学习模型的使用方法、性能评价和优缺点。

模型阐述角度:模型简介、数据描述 、编程实践、性能测评、特点分析。

2.1 监督学习经典模型

2.1.1 监督学习任务的基本流程

  • 准备训练数据——抽取所需特征、形成特征向量(Feature Vectors)——把特征向量连同labels一起送入学习算法(Machine Learning Algorithm)中——训练出一个预测模型(Predictive Model)

  • 采用同样特征抽取方法作用于测试集、得到用于测试的特征向量——使用预测模型对待测试的特征向量进行预测并得到结果(Expected Label)

监督学习任务的基本流程

2.1.2 分类学习

分类问题:

  • 二分类(binary classification):二选一
  • 多分类(multiclass classification):判断一个样本是否同时属于多个不同类别

2.1.2.1 线性分类器(linear classifiers)

线性分类器:假设特征与分裂结构存在线性关系的模型,通过累加计算每个维度的特征与各自权重的乘积来帮助类别决策(基于线性假设的分类器

logistics regression模型

x = :n维特征向量
w = : 特征向量对应的权重/系数(coefficient)
b:截距(intercept),为避免过原点

线性关系表示为:

我们所需处理的二分类问题中f∈(0,1),故需将函数的f∈R映射到(0,1),故有logistics函数:

logistic

综上,若将z替换为f,则logistics regression模型如下:

该模型处理一个待分类的特征向量:若z=0,则g=0.5;若z<0,则g<0.5,此FV被判别为一类,反之则为另一类。

当使用一组m个用于训练的FV $X=$ 和其对应的分类目标$y=$ ,我们希望该模型能在这组训练集上取得最大似然估计(Maximum Likelihood)的概率 $L(w,b)$, 或者说至少要在训练集上表现为:

为学习到决定模型的参数(parameters),即系数w和截距b,我们普遍使用一种精确计算的解析算法和一种快速估计的随机梯度上升算法(stochasitic gradient ascend)。

1.任何模型在训练集上的表现都不一定能代表其最终在未知待测数据集上的性能,但至少要先保证模型可以被训练集优化。

2.SGA和SGD都属于梯度法迭代渐进估计参数的过程,梯度上升(SGA)用于目标最大化,梯度下降(SGD)用于目标最小化。

数据集说明:

  • 数据集地址:Address
  • 描述:良/恶性肿瘤预测数据
  • 目的:分类预测,并使用精细的测评指标评价模型性能

1.数据预处理

1
2
3
4
5
6
7
8
9
import pandas as pd 
import numpy as np

# create feature list
column_names = ['Sample code number', 'Clump Thickness', 'Uniformity of Cell Size',
'Uniformity of Cell Shape', 'Marginal Adhesion','Single Epithelical Cell Size','Bare Nuclei','Bland Chromatin','Normal Nucleoli','Mitoses','Class']

# read data
data = pd.read_csv('/Users/scarlett/repository/projects/breast_cancer/breast_cancer.csv',names=column_names)
1
2
3
4
5
# clean data
data = data.replace(to_replace='?', value=np.nan)
data = data.dropna(how='any')
print data.shape
print data.info()

output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(683, 11)
<class 'pandas.core.frame.DataFrame'>
Int64Index: 683 entries, 0 to 698
Data columns (total 11 columns):
Sample code number 683 non-null int64
Clump Thickness 683 non-null int64
Uniformity of Cell Size 683 non-null int64
Uniformity of Cell Shape 683 non-null int64
Marginal Adhesion 683 non-null int64
Single Epithelical Cell Size 683 non-null int64
Bare Nuclei 683 non-null object
Bland Chromatin 683 non-null int64
Normal Nucleoli 683 non-null int64
Mitoses 683 non-null int64
Class 683 non-null int64
dtypes: int64(10), object(1)
memory usage: 64.0+ KB
None

由于原始数据没有提供对应的测试样本,故需要对带有label的样本进行分割,一般是25%作为测试集,75%作为训练集。

2.准备训练、测试数据

1
2
3
4
5
6
7
8
9
10
11
# prepare training set and testing set

# use train_test_split in sklearn to split data
from sklearn.cross_validation import train_test_split

# randomly sample 25% for testing,75% for training
X_train,X_test,y_train,y_test = train_test_split(data[column_names[1:10]],data[column_names[10]],test_size=0.25,random_state=33)

# check number and class
print y_train.value_counts()
print y_test.value_counts()
1
2
3
4
5
6
2    344
4 168
Name: Class, dtype: int64
2 100
4 71
Name: Class, dtype: int64

训练样本:512条(344条良性肿瘤数据+168恶性肿瘤数据),测试样本171条(100+71)

sklearn.model_selection.train_test_split解释

from sklearn.cross_validation import train_test_split

一般形式:X_train,X_test, y_train, y_test = cross_validation.train_test_split(train_data,train_target,test_size=0.4, random_state=0)

参数解释:

  • train_data:所要划分的样本特征集
  • train_target:所要划分的样本结果
  • test_size:样本占比,如果是整数的话就是样本的数量
  • random_state:是随机数的种子

随机数种子:其实就是该组随机数的编号,在需要重复试验的时候,保证得到一组一样的随机数。比如你每次都填1,其他参数一样的情况下你得到的随机数组是一样的。但填0或不填,每次都会不一样。

随机数的产生取决于种子,随机数和种子之间的关系遵从以下两个规则:

种子不同,产生不同的随机数;种子相同,即使实例不同也产生相同的随机数。

3.使用线性分类模型进行分类预测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.linear_model import SGDClassifier

# standarlize data,make sure DE=1,EX=0,so that the outcome wont be influenced by big feature
ss = StandardScaler()
X_train=ss.fit_transform(X_train)
X_test=ss.transform(X_test)

# init logisticregression and SGDClassifier
lr = LogisticRegression()
sgdc = SGDClassifier()

# use fit() of LR to train paras
lr.fit(X_train,y_train)
# use trained lr to predict X_test
lr_y_predict = lr.predict(X_test)

# use fit() of SGDC to train paras, use trained lr to predict X_test
sgdc.fit(X_train,y_train)
sgdc_y_predict=sgdc.predict(X_test)

print sgdc_y_predict
print lr_y_predict

output:

1
2
3
4
5
6
7
8
9
10
[4 2 4 4 2 2 2 4 2 2 2 2 4 2 4 4 4 4 4 2 2 4 4 2 4 4 2 2 4 4 4 4 4 4 4 4 2
4 4 4 4 4 2 4 2 2 4 2 2 4 4 2 2 2 4 2 2 2 2 2 4 4 2 2 2 4 2 2 2 2 4 2 2 4
2 2 2 2 4 2 2 2 4 2 2 2 4 2 4 2 4 4 2 2 2 2 4 4 2 2 2 4 2 2 4 2 2 2 2 2 4
2 2 2 2 2 2 4 2 2 4 4 2 4 2 2 2 4 2 2 4 4 2 4 4 2 2 2 2 4 2 4 2 4 2 2 2 2
2 4 4 2 4 4 2 4 2 2 2 2 4 4 4 2 4 2 2 4 2 4 4]
[2 2 4 4 2 2 2 4 2 2 2 2 4 2 4 4 4 4 4 2 2 4 4 2 4 4 2 2 4 4 4 4 4 4 4 4 2
4 4 4 4 4 2 4 2 2 4 2 2 4 4 2 2 2 4 2 2 2 2 2 4 4 2 2 2 4 2 2 2 2 4 2 2 2
2 2 2 4 4 2 2 2 4 2 2 2 4 2 4 2 4 4 2 2 2 2 4 4 2 2 2 4 2 2 4 2 2 2 2 2 4
2 2 2 2 2 2 4 2 2 4 4 2 4 2 2 2 4 2 2 4 4 2 4 4 2 2 2 2 4 2 4 2 4 2 2 2 2
2 4 4 2 4 4 2 4 2 2 2 2 4 4 4 2 4 2 2 4 2 4 4]

混淆矩阵:
二分类任务中,预测结果(predicted condition)与正确标记(true condition)之间存在4种不同的组合:

  • 真阳性(true positive):预测正确的恶性肿瘤
  • 真阴性
  • 假阳性(false positive):误判为恶性肿瘤
  • 假阴性

4.性能评价

性能评价指标

评价指标1:准确率

$ Accuracy = \frac{TP + TN}{TP+TN+FP+FN}$

评价指标2:召回率(Recall)和精确率(Precision),F1 指标(F1 measure)

F1指标:两指标的调和平均数,以综合考量两指标

对肿瘤识别,我们更关心召回率,即应该被正确识别的恶性肿瘤的百分比。

使用线性分类模型进行肿瘤预测任务的性能分析

1
2
3
4
5
6
from sklearn.metrics import classification_report

# 使用逻辑回归模型自带的评分函数score获得模型在测试集上的准确性结果
print 'Acurracy of LR Classifier:', lr.score(X_test,y_test)
# use classification_report to get the other 3 measures of LR
print classification_report(y_test,lr_y_predict,target_names=['Benign','Malignant'])

output:

1
2
3
4
5
6
7
Acurracy of LR Classifier: 0.9883040935672515
precision recall f1-score support

Benign 0.99 0.99 0.99 100
Malignant 0.99 0.99 0.99 71

avg / total 0.99 0.99 0.99 171
1
2
3
4
# 使用随机梯度下降模型自带的score评分函数模型在测试集上的准确性结果
print 'Acurracy of SDG Classifier:', sgdc.score(X_test,y_test)
# use classification_report to get the other 3 measures of SGDC
print classification_report(y_test,lr_y_predict,target_names=['Benign','Malignant'])

output:

1
2
3
4
5
6
7
Acurracy of SDG Classifier: 0.9824561403508771
precision recall f1-score support

Benign 0.99 0.99 0.99 100
Malignant 0.99 0.99 0.99 71

avg / total 0.99 0.99 0.99 171

综上,发现,LR比SGDC在测试集上有更高的准确性,因为sklearn中采用解析的方式精确计算LR的参数,而使用梯度法估计SGDC的参数

特点分析:

  • LR model:精确解析参数,计算时间长但模型性能略高
  • SGDC model:随机梯度上升算法估计参数,计算时间短但模型性能略低
  • 训练数据规模在10万量级以上的数据,考虑到时间耗用,推荐使用随机梯度算法对模型参数进行估计

2.1.2.2 支持向量机(Suport Vector Classifier)(分类)

模型介绍:

SVM

1.不是在所有数据集上SVM的表现一定都优于普通线性模型或其他模型,而是假设未知待测数据也如训练数据一样分布,则SVM可帮助找到最佳分类器;实际应用数据总是有偏差的。

2.上图,H1表现不佳(有分类错误);H2与H3都表现完美。

3.但,分类模型的选取中我们需要更加关注如何最大限度为未知分布的数据集提供足够的待预测空间。如有一个黑色样本稍偏离H2,则会很可能被误判为白色,造成误差,而H3则可为样本提供更多的容忍度,故H3优于H2.

数据描述:

  • 应用场景:邮政系统对收信人邮编进行识别与分类,以便确定信件的投送地;邮编多数为手写
  • 任务:手写数字图片识别与分类
  • 数据集:利用SVM处理sklearn内部集成的手写体数字图片数据集

1.读取数据

1
2
3
4
5
6
7
from sklearn.datasets import load_digits

# 将数据存储在digits变量中
digits=load_digits()

# 检查数据规模与特征维度
print digits.data.shape

Output:

1
(1797, 64)

(1797,64):有1797条图像数据,每幅图片由8*8=64的像素矩阵表示

2.分割数据集

1
2
3
4
from sklearn.cross_validation import train_test_split
X_train,X_test,y_train,y_test=train_test_split(digits.data,digits.target,test_size=0.25,random_state=33)
print y_train.shape
print y_test.shape

Output:

1
2
(1347,)
(450,)

3.使用SVM对手写数字图像进行识别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from sklearn.preprocessing import StandardScaler

# 从SVM里导入基于线性假设的SVM分类器LinearSVC
from sklearn.svm import LinearSVC

# 对训练和测试的特征数据进行标准化
ss=StandardScaler()
X_train=ss.fit_transform(X_train)
X_test=ss.transform(X_test)

# 初始化LinearSVC
lsvc=LinearSVC()
# 进行模型训练
lsvc.fit(X_train,y_train)
# 利用训练好的模型对测试样本的数字类别进行预测,预测结果存在y_predict中
y_predict=lsvc.predict(X_test)
print y_predict

Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
[1 3 7 3 2 4 6 1 4 0 4 7 9 5 2 8 3 6 7 0 6 0 8 3 0 6 2 3 0 9 0 2 0 6 9 1 1
5 8 0 6 1 5 8 9 5 1 6 2 6 6 7 6 7 7 2 7 8 0 7 3 6 3 9 6 6 5 5 4 2 9 3 7 6
5 7 2 8 1 2 2 8 1 1 6 3 5 0 0 1 6 7 6 8 9 7 0 0 9 8 0 8 2 3 6 1 9 9 1 7 3
9 8 8 5 9 5 1 1 7 9 3 3 2 8 1 3 8 6 4 0 0 0 7 1 5 5 1 8 5 1 8 1 6 9 9 4 5
7 5 2 1 2 5 8 7 7 5 1 9 6 9 8 0 6 1 2 1 5 7 8 9 6 8 4 1 0 0 9 8 7 2 8 6 4
8 9 4 2 6 1 8 5 6 7 5 1 9 2 8 3 2 9 4 3 5 5 6 2 4 3 2 6 4 8 5 8 0 8 8 6 3
2 3 0 5 7 1 3 9 3 2 1 6 6 5 1 9 7 2 4 5 2 1 3 1 1 2 1 7 0 1 2 2 1 2 4 9 6
6 3 9 2 8 1 5 5 1 8 6 2 5 6 0 1 4 2 1 8 9 4 3 0 6 8 3 3 2 0 2 0 6 5 6 6 4
6 1 8 3 4 1 3 5 1 4 9 8 7 5 1 1 3 7 8 8 3 7 4 0 7 2 8 7 1 9 4 5 3 5 2 5 1
3 0 5 8 4 7 6 9 9 3 3 4 0 6 4 7 0 6 1 2 3 3 4 5 3 3 5 2 0 9 7 1 5 5 8 4 4
3 6 2 5 1 0 6 1 5 8 4 7 6 4 3 4 0 3 0 1 2 8 0 5 4 5 2 2 9 6 9 8 0 8 8 2 4
6 5 6 4 3 9 8 9 7 1 7 9 4 1 9 9 5 9 8 0 8 2 5 1 4 2 6 3 7 9 3 7 4 3 7 1 8
8 9 5 3 6 6]

4.性能测评

同样使用precision、recall、accuracy、F1这四个测度来评价性能

1
2
# 使用模型自带的评估函数进行准确性测评
print 'The Accuracy of Linear SVC is:',lsvc.score(X_test,y_test)

Output:

1
The Accuracy of Linear SVC is: 0.9533333333333334
1
2
3
# 使用模型自带的classification_report模块对预测结果做更精细的分析
from sklearn.metrics import classification_report
print classification_report(y_test,y_predict,target_names=digits.target_names.astype(str))

Output:

1
precision    recall  f1-score   support
1
2
3
4
5
6
7
8
9
10
11
12
          0       0.92      1.00      0.96        35
1 0.96 0.98 0.97 54
2 0.98 1.00 0.99 44
3 0.93 0.93 0.93 46
4 0.97 1.00 0.99 35
5 0.94 0.94 0.94 48
6 0.96 0.98 0.97 51
7 0.92 1.00 0.96 35
8 0.98 0.84 0.91 58
9 0.95 0.91 0.93 44

avg / total 0.95 0.95 0.95 450

由上可知,SVM可提供较高的手写数字识别性能,平均各项指标都在95%左右。

多分类:判断一个样本是否同时属于多个不同类别;将多分类看成N个二分类任务

如本例的分类目标有10个类别,即0—9这10个数字,因此无法直接计算三指标。故我们逐一评估每个类别的这三指标,把所有其他类别统一看做阴性(负)样本,则创造了10个二分类任务。

特点分析

  • 可帮助在海量甚至高维度数据中筛选对预测任务最有效的少数训练样本,节省数据内存,提高模型预测性能
  • 但计算代价高(CPU资源与计算时间)

2.1.2.3 朴素贝叶斯(Naive Bayes)(分类)

模型介绍:基于贝叶斯理论的分类器

  • 会单独考量每一维度特征被分类的条件概率,进而综合这些概率并对其所在的特征向量做出分类预测
  • 基本数学假设:各个维度上的特征被分类的条件概率之间是相互独立的

若采用概率模型来表述,则定义$x=$为某一n维特征向量,$y\in{c1,c_2,…,c_k}$ 为改特征向量x所有k种可能的类别,记$P(y=c_i|x) $ 为特征向量x属于类别$c_i$的概率,根据贝叶斯概率公式:

我们的目标是寻找所有$y \in {c_1,c_2,…,c_k} $ 中$P(y|x)$ 最大的,即$argmaxP(y|x)$;并考虑到$P(x)$ 对于同一样本都是相同的,因此可忽略不计。故:

若每一种特征可能的取值均为0或1,在无特殊假设的条件下,计算$P(x_1,x_2,..,x_n|y)$ 需要对$k*2^n$ 个可能的参数进行估计:

但由于朴素贝叶斯模型的特征类别独立假设,故$P(xn|x_1,x_2,…,x{n-1},y)=P(x_n|y)$ ;若依然每种特征可能的取值只有2种,则只需要估计$2kn$个参数,即$P(x_1=0|y=c_1),P(x_1=1|y=c_1),…,P(x_n=1|y=c_1)$ .

为估计每个参数的概率,采用如下公式,且改用频率比近似计算概率

数据描述

  • 应用场景:互联网新闻文本分类
  • 数据集:20类新闻文本

1.读取数据

1
2
3
4
5
6
7
8
from sklearn.datasets import fetch_20newsgroups


# 即时从网上下载数据
news=fetch_20newsgroups(subset='all')

print len(news.data)
print news.data[0]

output:

1
2
3
4
5
6
18846
From: Mamatha Devineni Ratnam <mr47+@andrew.cmu.edu>
Subject: Pens fans reactions
Organization: Post Office, Carnegie Mellon, Pittsburgh, PA
Lines: 12
NNTP-Posting-Host: po4.andrew.cmu.edu
1
2
3
4
5
6
7
8
9
I am sure some bashers of Pens fans are pretty confused about the lack
of any kind of posts about the recent Pens massacre of the Devils. Actually,
I am bit puzzled too and a bit relieved. However, I am going to put an end
to non-PIttsburghers' relief with a bit of praise for the Pens. Man, they
are killing those Devils worse than I thought. Jagr just showed you why
he is much better than his regular season stats. He is also a lot
fo fun to watch in the playoffs. Bowman should let JAgr have a lot of
fun in the next couple of games since the Pens are going to beat the pulp out of Jersey anyway. I was very disappointed not to see the Islanders lose the final
regular season game. PENS RULE!!!

数据没有被设定特征,也无数字化的量度,因此需要在被训练前对数据做进一步处理。

2.分割数据

1
2
from sklearn.cross_validation import train_test_split
X_train,X_test,y_train,y_test=train_test_split(news.data,news.target,test_size=0.25,random_state=33)

3.使用朴素贝叶斯分类器对新闻文本数据进行类别预测

  • 先将文本转化为特征向量
  • 再利用朴素贝叶斯模型从训练数据中估计参数
  • 最后利用这些概率参数对同样转化为特征向量的测试集进行类别预测
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 导入用于文本特征向量转换模块
from sklearn.feature_extraction.text import CountVectorizer
vec=CountVectorizer()
X_train=vec.fit_transform(X_train)
X_test=vec.transform(X_test)

# 导入naive bayes
from sklearn.naive_bayes import MultinomialNB
# 初始化NB
mnb=MultinomialNB()
# 利用训练数据对模型参数进行估计
mnb.fit(X_train,y_train)
# 对测试样本进行类别预测,结果存储在变量y_predict中
y_predict=mnb.predict(X_test)

4.性能评估

1
2
3
from sklearn.metrics import classification_report
print 'The Accuracy of NBC is:',mnb.score(X_test,y_test)
print classification_report(y_test,y_predict,target_names=news.target_names)

output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
The Accuracy of NBC is: 0.8397707979626485
precision recall f1-score support

alt.atheism 0.86 0.86 0.86 201
comp.graphics 0.59 0.86 0.70 250
comp.os.ms-windows.misc 0.89 0.10 0.17 248
comp.sys.ibm.pc.hardware 0.60 0.88 0.72 240
comp.sys.mac.hardware 0.93 0.78 0.85 242
comp.windows.x 0.82 0.84 0.83 263
misc.forsale 0.91 0.70 0.79 257
rec.autos 0.89 0.89 0.89 238
rec.motorcycles 0.98 0.92 0.95 276
rec.sport.baseball 0.98 0.91 0.95 251
rec.sport.hockey 0.93 0.99 0.96 233
sci.crypt 0.86 0.98 0.91 238
sci.electronics 0.85 0.88 0.86 249
sci.med 0.92 0.94 0.93 245
sci.space 0.89 0.96 0.92 221
soc.religion.christian 0.78 0.96 0.86 232
talk.politics.guns 0.88 0.96 0.92 251
talk.politics.mideast 0.90 0.98 0.94 231
talk.politics.misc 0.79 0.89 0.84 188
talk.religion.misc 0.93 0.44 0.60 158

avg / total 0.86 0.84 0.82 4712

由上评估结果可知,NBC对4712条新闻文本测试样本分类的准确性约为83.977%,平均精确率、召回率、F1指标分别为86%、84%、82%。

特点分析:

  • 朴素贝叶斯模型广泛应用在互联网文本分类任务
  • 优点:由于其较强的特征条件独立假设,使得模型预测所需估计的参数规模从幂指数量级向线性量级减少,极大节约内存消耗和计算时间
  • 缺点:同样由于这种强假设的限制,模型训练时无法将各个特征之间的联系考量在内,使该模型在其他数据特征关联性强的分类任务上性能不佳

2.1.2.4 K近邻(k-Nearest Neighbor,KNN)(分类)

模型介绍:

  • 最简单的ML算法之一
  • 假设有一些携带分类标记的训练样本,分布于特征空间中;蓝色、绿色样本点各自代表其类别;对一个待分类的红色测试样本点,未知其类别,按照“近朱者赤近墨者黑”的说法,我们需要寻找与这个待分类的样本在特征空间中距离最近的K个已标记样本作为参考,来帮助做出分类决策

  • 思路:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。

数据描述:

  • 应用场景:使用K近邻算法对生物物种进行分类
  • 数据集:Iris

1.读取数据集

1
2
3
4
5
6
7
from sklearn.datasets import load_iris

iris=load_iris()

print iris.data.shape

print iris.DESCR

output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
(150, 4)
Iris Plants Database
====================

Notes
-----
Data Set Characteristics:
:Number of Instances: 150 (50 in each of three classes)
:Number of Attributes: 4 numeric, predictive attributes and the class
:Attribute Information:
- sepal length in cm
- sepal width in cm
- petal length in cm
- petal width in cm
- class:
- Iris-Setosa
- Iris-Versicolour
- Iris-Virginica
:Summary Statistics:

============== ==== ==== ======= ===== ====================
Min Max Mean SD Class Correlation
============== ==== ==== ======= ===== ====================
sepal length: 4.3 7.9 5.84 0.83 0.7826
sepal width: 2.0 4.4 3.05 0.43 -0.4194
petal length: 1.0 6.9 3.76 1.76 0.9490 (high!)
petal width: 0.1 2.5 1.20 0.76 0.9565 (high!)
============== ==== ==== ======= ===== ====================

:Missing Attribute Values: None
:Class Distribution: 33.3% for each of 3 classes.
:Creator: R.A. Fisher
:Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov)
:Date: July, 1988

This is a copy of UCI ML iris datasets.
http://archive.ics.uci.edu/ml/datasets/Iris

The famous Iris database, first used by Sir R.A Fisher

This is perhaps the best known database to be found in the
pattern recognition literature. Fisher's paper is a classic in the field and
is referenced frequently to this day. (See Duda & Hart, for example.) The
data set contains 3 classes of 50 instances each, where each class refers to a
type of iris plant. One class is linearly separable from the other 2; the
latter are NOT linearly separable from each other.

References
----------
- Fisher,R.A. "The use of multiple measurements in taxonomic problems"
Annual Eugenics, 7, Part II, 179-188 (1936); also in "Contributions to
Mathematical Statistics" (John Wiley, NY, 1950).
- Duda,R.O., & Hart,P.E. (1973) Pattern Classification and Scene Analysis.
(Q327.D83) John Wiley & Sons. ISBN 0-471-22361-1. See page 218.
- Dasarathy, B.V. (1980) "Nosing Around the Neighborhood: A New System
Structure and Classification Rule for Recognition in Partially Exposed
Environments". IEEE Transactions on Pattern Analysis and Machine
Intelligence, Vol. PAMI-2, No. 1, 67-71.
- Gates, G.W. (1972) "The Reduced Nearest Neighbor Rule". IEEE Transactions
on Information Theory, May 1972, 431-433.
- See also: 1988 MLC Proceedings, 54-64. Cheeseman et al"s AUTOCLASS II
conceptual clustering system finds 3 classes in the data.
- Many, many more ...

2.分割数据集

1
2
from sklearn.cross_validation import train_test_split
X_train,X_test,y_train,y_test=train_test_split(iris.data,iris.target,test_size=0.25,random_state=33)

3.使用K近邻算法对iris数据进行类别预测

1
2
3
4
5
6
7
8
9
10
11
12
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier

ss=StandardScaler()
X_train=ss.fit_transform(X_train)
X_test=ss.transform(X_test)

knc=KNeighborsClassifier()
knc.fit(X_train,y_train)
y_predict=knc.predict(X_test)

print y_predict

output:

1
2
[1 1 0 1 1 2 0 0 2 2 2 0 2 1 2 1 1 0 1 2 0 0 2 0 1 2 1 1 2 1 1 1 2 2 2 2 2
1]

4.性能评估

1
2
3
4
print 'The Accuracy of KNC is:',knc.score(X_test,y_test)

from sklearn.metrics import classification_report
print classification_report(y_test,y_predict,target_names=iris.target_names)

output:

1
2
3
4
5
6
7
8
The Accuracy of KNC is: 0.8947368421052632
precision recall f1-score support

setosa 1.00 1.00 1.00 8
versicolor 0.73 1.00 0.85 11
virginica 1.00 0.79 0.88 19

avg / total 0.92 0.89 0.90 38

特点分析:

  • k近邻算法是非常直观简单的模型
  • 无参数模型没有参数训练过程,即未通过任何学习算法分析数据,而只是根据测试样本在训练数据中的分布做出分类
  • 缺点:高计算复杂度和内存消耗;平方级别的算法复杂度(每处理一个测试样本就要对所有训练样本进行遍历,逐一计算相似度、排序且选取K个最近邻训练样本的标记,进而做出分类决策)
  • 当然也有KD-Tree这样的数据结构通过“空间换时间”思想节省KNN的决策时间

2.1.2.5 决策树(Decision Tree)(分类)

模型介绍:

  • 描述非线性关系

LR和SVM都要求被学习的数据特征和目标之间遵照线性假设,但现实场景下这种假设不存在。如用年龄预测流感死亡率,年龄与死亡率之间不存在线性关系。

  • 决策树节点(node)——数据特征

  • 各节点下的分支——特征值的分类

  • 决策树的所有叶子节点——显示模型的决策结果

  • 使用多种不同特征组合搭建多层决策树时,需考虑特征节点的选取顺序,常用的度量方式有信息熵(Information Gain)和基尼不纯性(Gini Impurity)

数据描述

  • 借助决策树模型预测泰坦尼克号乘客生还情况
  • 数据集:乘客信息
  • Address

1.数据查验

1
2
3
4
import pandas as pd

titanic=pd.read_csv('/Users/scarlett/repository/projects/titanic/titanic.csv')
print titanic.head()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   row.names pclass  survived  \
0 1 1st 1
1 2 1st 0
2 3 1st 0
3 4 1st 0
4 5 1st 1

name age embarked \
0 Allen, Miss Elisabeth Walton 29.0000 Southampton
1 Allison, Miss Helen Loraine 2.0000 Southampton
2 Allison, Mr Hudson Joshua Creighton 30.0000 Southampton
3 Allison, Mrs Hudson J.C. (Bessie Waldo Daniels) 25.0000 Southampton
4 Allison, Master Hudson Trevor 0.9167 Southampton

home.dest room ticket boat sex
0 St Louis, MO B-5 24160 L221 2 female
1 Montreal, PQ / Chesterville, ON C26 NaN NaN female
2 Montreal, PQ / Chesterville, ON C26 NaN (135) male
3 Montreal, PQ / Chesterville, ON C26 NaN NaN female
4 Montreal, PQ / Chesterville, ON C22 NaN 11 male
1
2
print titanic.info()
print titanic.shape
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1313 entries, 0 to 1312
Data columns (total 11 columns):
row.names 1313 non-null int64
pclass 1313 non-null object
survived 1313 non-null int64
name 1313 non-null object
age 633 non-null float64
embarked 821 non-null object
home.dest 754 non-null object
room 77 non-null object
ticket 69 non-null object
boat 347 non-null object
sex 1313 non-null object
dtypes: float64(1), int64(2), object(8)
memory usage: 112.9+ KB
None
(1313, 11)

2.数据预处理

1
2
3
4
5
6
# 特征选择
X=titanic[['pclass','sex','age']]
y=titanic['survived']

# 探查所选特征
print X.info()
1
2
3
4
5
6
7
8
9
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1313 entries, 0 to 1312
Data columns (total 3 columns):
pclass 1313 non-null object
sex 1313 non-null object
age 633 non-null float64
dtypes: float64(1), object(2)
memory usage: 30.8+ KB
None

数据处理任务:

  • age只有714个,有缺失项,需要补全(使用平均数或中位数来补全,造成的影响最小)
  • sex与pclass是int/object型,需要转换成数值特征,用0/1代替
1
2
X['age'].fillna(X['age'].mean(),inplace=True)
print X.info()
1
2
3
4
5
6
7
8
9
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1313 entries, 0 to 1312
Data columns (total 3 columns):
pclass 1313 non-null object
sex 1313 non-null object
age 1313 non-null float64
dtypes: float64(1), object(2)
memory usage: 30.8+ KB
None

由此可知Age特征得到了补充。

4.数据分割

1
2
from sklearn.cross_validation import train_test_split
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.25,random_state=33)

5.特征转换

1
2
3
4
5
6
7
8
9
# 使用特征转换器
from sklearn.feature_extraction import DictVectorizer
vec=DictVectorizer(sparse=False)

# 转换特征后发现:凡是类别型的特征都单独被剥离出来独立成一列特征,数值型则保持不变
X_train=vec.fit_transform(X_train.to_dict(orient='record'))


X_test=vec.transform(X_test.to_dict(orient='record'))

6.训练模型

1
2
3
4
from sklearn.tree import DecisionTreeClassifier
dtc=DecisionTreeClassifier()
dtc.fit(X_train,y_train)
y_predict=dtc.predict(X_test)

7.性能评估

1
2
3
4
5
from sklearn.metrics import classification_report

print dtc.score(X_test,y_test)

print classification_report(y_predict,y_test,target_names=['died','survived'])
1
2
3
4
5
6
7
0.7811550151975684
precision recall f1-score support

died 0.91 0.78 0.84 236
survived 0.58 0.80 0.67 93

avg / total 0.81 0.78 0.79 329

2.1.2.6 集成模型(Ensemble)(分类)

模型介绍

  • 综合考量多个分类器的预测结果,从而做出分类决策,综合考量方式有2种:
  • 1.利用相同的训练数据同时搭建多个独立的分类模型,然后通过投票以少数服从多数的原则做出最终分类决策,如:
    • 随机森林分类器(Random Forest Classifier):在相同训练数据上同时搭建多棵决策树(每棵树都随机选取特征)
  • 2.按照一定词序搭建多个分类模型,模型间彼此存在依赖关系(每个后续模型的加入都需要对现有集成模型的综合性能有所贡献,进而不断提升更新过后的集成模型的性能,并最终期望借助整合多个分类能力较弱的分类器,搭建出具有更强分类能力的模型),如:
    • 梯度提升决策树(Gradient Tree Boosting):每棵树在生成过程中都会尽可能降低整体集成模型在训练集上的拟合误差

数据描述

  • 对比单一决策树、随机森林、梯度提升决策树三者性能差异
  • 数据集:Titanic乘客数据

1.集成模型对titanic乘客是否生还的预测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd

titanic=pd.read_csv('/Users/scarlett/repository/projects/titanic/titanic.csv')

X=titanic[['pclass','age','sex']]
y=titanic['survived']

X['age'].fillna(X['age'].mean(),inplace=True)

from sklearn.cross_validation import train_test_split
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.25,random_state=33)

from sklearn.feature_extraction import DictVectorizer
vec=DictVectorizer(sparse=False)

X_train=vec.fit_transform(X_train.to_dict(orient='record'))

X_test=vec.transform(X_test.to_dict(orient='record'))
1
2
3
4
5
6
7
# 使用单一决策树训练模型
from sklearn.tree import DecisionTreeClassifier
dtc=DecisionTreeClassifier()
dtc.fit(X_train,y_train)
dtc_y_pred=dtc.predict(X_test)

print dtc_y_pred
1
2
3
4
5
6
7
8
9
[0 1 0 0 0 1 1 0 0 1 0 1 1 0 0 1 0 1 0 1 1 1 0 0 0 0 1 1 0 0 1 0 0 0 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 1 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0
0 1 0 0 0 1 1 1 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 1 1 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0
0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0
0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 1 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 0 0
1 1 0 0 0 1 0 0 0 1 0 0 0 1 1 0 0 0 0 1 0 1 0 0 0 1 0 0 1 1 0 0 0 1 0 0 0
1 0 0 0 1 1 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 0]
1
2
3
4
5
6
7
# 使用随机森林分类器进行集成模型训练
from sklearn.ensemble import RandomForestClassifier
rfc=RandomForestClassifier()
rfc.fit(X_train,y_train)
rfc_y_pred=rfc.predict(X_test)

print rfc_y_pred
1
2
3
4
5
6
7
8
9
[0 1 0 0 0 1 1 0 0 1 0 1 1 0 0 1 0 1 0 1 1 1 0 0 0 0 1 1 0 0 1 0 0 0 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 1 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0
0 1 1 0 0 1 1 1 0 0 0 0 0 0 1 0 0 1 0 0 0 1 1 0 0 0 1 1 0 1 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0
0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0
0 0 1 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 1 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 0 0
1 1 0 0 0 1 0 0 0 1 0 0 0 1 1 0 0 0 0 1 0 1 0 0 0 1 0 0 1 1 0 0 0 1 0 0 0
1 0 0 0 1 1 0 0 0 0 1 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0]
1
2
3
4
5
6
7
# 使用梯度提升模型训练集成模型
from sklearn.ensemble import GradientBoostingClassifier
gbc=GradientBoostingClassifier()
gbc.fit(X_train,y_train)
gbc_y_pred=gbc.predict(X_test)

print gbc_y_pred
1
2
3
4
5
6
7
8
9
[0 1 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 0 0 0 0 1 1 0 0 1 0 0 0 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 1 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0
0 1 0 0 0 1 1 1 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 1 1 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0
0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0
0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 1 0 0 1 1 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 0 0
0 1 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 1 0 0 1 1 0 0 0 1 0 0 0
1 0 0 0 1 1 0 0 0 0 1 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 1 0]

2.性能测评

1
2
3
4
5
6
7
8
9
10
11
12
13
from sklearn.metrics import classification_report

# 输出单一决策树的评估指标
print 'The accuracy of DTC is',dtc.score(X_test,y_test)
print classification_report(dtc_y_pred,y_test)

# 输出随机森林的评估指标
print 'The accuracy of RFC is',rfc.score(X_test,y_test)
print classification_report(rfc_y_pred,y_test)

# 输出梯度提升决策树的评估指标
print 'The accuracy of GBC is',gbc.score(X_test,y_test)
print classification_report(gbc_y_pred,y_test)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
The accuracy of DTC is 0.7811550151975684
precision recall f1-score support

0 0.91 0.78 0.84 236
1 0.58 0.80 0.67 93

avg / total 0.81 0.78 0.79 329

The accuracy of RFC is 0.7781155015197568
precision recall f1-score support

0 0.90 0.78 0.83 233
1 0.59 0.78 0.67 96

avg / total 0.81 0.78 0.79 329

The accuracy of GBC is 0.790273556231003
precision recall f1-score support

0 0.92 0.78 0.84 239
1 0.58 0.82 0.68 90

avg / total 0.83 0.79 0.80 329

上面的输出表明:在相同的训练和测试数据条件下,仅使用模型默认配置,梯度上升决策树具有最佳预测性能。一般工业界为追求更强的预测性能,会把随机森林作为基线系统(Baseline System)

集成模型:

  • 最常见的应用;可整合多种模型
  • 缺点:模型估计参数的过程受概率影响,具有不确定性
  • 优点:虽然模型训练需要耗费更多时间,但得到的综合模型会具有更高的性能和稳定性

2.1.3 回归预测

回归 vs 分类:区别在于其待预测目标是连续变量

2.1.3.1 线性回归器

模型介绍

最小二乘法:数学优化方法;通过最小化误差的平方和寻找数据的最佳函数匹配。利用最小二乘法可以简便地求得未知的数据,并使得这些求得的数据与实际数据之间误差的平方和为最小。

线性回归问题中:优化目标即最小化预测结果与真实值之间的差异(因为预测目标直接是实数域上的数值)

当使用一组m个用于训练的特征向量$X=$ 和其对应的回归目标$y=$ 时,我们希望线性回归模型可以最小二乘(Generalized Least Squares)预测的损失$L(w,b)$ ,则线性回归器的常见优化目标为:*

同样为学习到决定模型的参数$w,b$ ,仍可使用一种精确计算的解析算法和一种快速的随机梯度下降(Stochastic Gradient Descend)估计算法.

数据描述

  • 美国波士顿地区房价预测

性能评估指标:

假设测试数据有m个目标数值$y= $ 且记$\overline{y}$ 为回归模型的预测结果,则:

  • MAE
    • $SS{abs}=\sum{m}^{i=1}\qquad|y^i-\overline{y}|$ ,$
    • $MAE=\frac{SS_{abs}}{m}$
  • MSE
    • $SS{tot}=\sum{m}^{i=1}\qquad(y^i-\overline{y})^2$
    • Missing close brace MSE=\frac{SS_{tot}{m}
  • R-squared
    • $SS{res}=\sum{m}^{i=1}\qquad(y^i-(f(x^i))^2$
    • Missing close braceR^2=1-\frac{SS_{res}{tot}
    • 其中,$SS{tot}$ 代表测试数据真实值的方差(内部差异);$SS{res}$ 代表回归值与真实值之阿金的平方差异(回归差异)
    • R-squared(拟合度)比较预测结果与真实值的吻合程度,既考量了回归值与真实值的差异,又兼顾了问题本身真实值的变动;而MAE、MSE(差值的绝对值或平方)则会随不同预测问题而变化巨大,欠缺在不同问题中的可比性

1.导入和查验数据

1
2
3
4
from sklearn.datasets import load_boston
boston=load_boston()
# 输出数据描述
print boston.DESCR
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 Boston House Prices dataset
===========================

Notes
------
Data Set Characteristics:

:Number of Instances: 506

:Number of Attributes: 13 numeric/categorical predictive

:Median Value (attribute 14) is usually the target

:Attribute Information (in order):
- CRIM per capita crime rate by town
- ZN proportion of residential land zoned for lots over 25,000 sq.ft.
- INDUS proportion of non-retail business acres per town
- CHAS Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)
- NOX nitric oxides concentration (parts per 10 million)
- RM average number of rooms per dwelling
- AGE proportion of owner-occupied units built prior to 1940
- DIS weighted distances to five Boston employment centres
- RAD index of accessibility to radial highways
- TAX full-value property-tax rate per $10,000
- PTRATIO pupil-teacher ratio by town
- B 1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town
- LSTAT % lower status of the population
- MEDV Median value of owner-occupied homes in $1000's

:Missing Attribute Values: None

:Creator: Harrison, D. and Rubinfeld, D.L.

This is a copy of UCI ML housing dataset.
http://archive.ics.uci.edu/ml/datasets/Housing
1
This dataset was taken from the StatLib library which is maintained at Carnegie Mellon University.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
The Boston house-price data of Harrison, D. and Rubinfeld, D.L. 'Hedonic
prices and the demand for clean air', J. Environ. Economics & Management,
vol.5, 81-102, 1978. Used in Belsley, Kuh & Welsch, 'Regression diagnostics
...', Wiley, 1980. N.B. Various transformations are used in the table on
pages 244-261 of the latter.

The Boston house-price data has been used in many machine learning papers that address regression
problems.

**References**

- Belsley, Kuh & Welsch, 'Regression diagnostics: Identifying Influential Data and Sources of Collinearity', Wiley, 1980. 244-261.
- Quinlan,R. (1993). Combining Instance-Based and Model-Based Learning. In Proceedings on the Tenth International Conference of Machine Learning, 236-243, University of Massachusetts, Amherst. Morgan Kaufmann.
- many more! (see http://archive.ics.uci.edu/ml/datasets/Housing)

2.数据分割

1
2
3
4
5
6
7
8
9
from sklearn.cross_validation import train_test_split
import numpy as np

X=boston.data
y=boston.target
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.25,random_state=33)

# 分析回归目标值的差异
print np.max(y),np.min(y),np.mean(y)
1
50.0 5.0 22.532806324110677

由上发现目标房价之间的差异较大,故需要对特征和目标值进行标准化处理。

1
2
3
4
5
6
7
8
9
10
11
from sklearn.preprocessing import StandardScaler

# 分别初始化feature和target的标准化器
ss_X=StandardScaler()
ss_y=StandardScaler()

# 分别对训练和测试数据的feature和target进行标准化处理
X_train=ss_X.fit_transform(X_train)
X_test=ss_X.transform(X_test)
y_train=ss_y.fit_transform(y_train)
y_test=ss_y.transform(y_test)

3.使用线性回归模型和SGDRegressor分别对房价进行预测

1
2
3
4
5
6
7
8
9
from sklearn.linear_model import LinearRegression
lr=LinearRegression()
lr.fit(X_train,y_train)
lr_y_predict=lr.predict(X_test)

from sklearn.linear_model import SGDRegressor
sgdr=SGDRegressor()
sgdr.fit(X_train,y_train)
sgdr_y_predict=sgdr.predict(X_test)

4.性能测评

测量目的:衡量预测值与真实值之间的差距

测评指标:

  • 平均绝对误差(Mean Absolute Error,MAE)
  • 均方误差(Mean Squared Error,MSE)
  • 拟合度(R-squared, R平方):拟合度检验是对已制作好的预测模型进行检验,比较它们的预测结果与实际发生情况的吻合程度

使用三种回归评价机制和两种调用R-squared评价模块的方法,评价此模型的回归性能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 使用LR模型自带的评估模块
print 'The value of default measurement of LR is',lr.score(X_test,y_test)

# 导入MAE和MSE评估回归模型
from sklearn.metrics import r2_score,mean_squared_error,mean_absolute_error

# 使用r2_score模块
print 'The value of R-squared of LR is',r2_score(y_test,lr_y_predict)

# 使用mean_squared_error模块
print 'The MSE of LR is',mean_squared_error(ss_y.inverse_transform(y_test),ss_y.inverse_transform(lr_y_predict))

# 使用mean_absolute_error模块
print 'The MAE of LR is',mean_absolute_error(ss_y.inverse_transform(y_test),ss_y.inverse_transform(lr_y_predict))

# 使用SGDR自带评估模块
print 'The value of default measurement of SGDR is',sgdr.score(X_test,y_test)

# 使用r2_score模块
print 'The value of R-squared of SGDR is',r2_score(y_test,sgdr_y_predict)

# 使用mean_squared_error模块
print 'The MSE of SGDR is',mean_squared_error(ss_y.inverse_transform(y_test),ss_y.inverse_transform(sgdr_y_predict))

# 使用mean_absolute_error模块
print 'The MAE of SGDR is',mean_absolute_error(ss_y.inverse_transform(y_test),ss_y.inverse_transform(sgdr_y_predict))

特点分析:

  • 数据规模超10万,使用随机梯度法估计参数
  • 在不清楚特征之间关系的前提下,可使用线性回归模型作为基线系统(baseline system)

2.1.3.2 支持向量机(回归)

模型介绍

  • 同样是从训练数据中选取一部分更加有效的支持向量,只是这少部分训练样本所提供的并不是类别目标,而是具体的预测数值

继续使用2.1.3.1中的训练集和测试集进行不同核函数配置的SVM回归模型训练,且分别对测试数据做出越策,会发现:

  • 不同配置下的模型在相同测试集上存在非常大的性能差异,且使用径向基(Radical basis function)核函数对特征进行非线性映射后,SVM展现最佳回归性能
  • 可以多尝试几种配置,以活动最佳预测性能

核函数:一种特征映射技巧,即通过某种函数计算,将原有的线性不可分的低维特征映射到更高维度的空间,从而尽可能达到新的高维度特征线性可分的程度。

2.1.2.3 K近邻(回归)

模型介绍

  • 在回归任务中,K近邻(回归)模型同样只是借助周围K个距离最近的训练样本的目标数值,对待测样本的回归值进行决策。

1.使用2种不同配置的K近邻回归模型对美国波士顿放假数据进行回归预测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from sklearn.datasets import load_boston
boston=load_boston()

from sklearn.cross_validation import train_test_split
import numpy as np
X=boston.data
y=boston.target
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.25,random_state=33)

from sklearn.neighbors import KNeighborsRegressor

# 初始化回归器,调整配置,使预测方式为平均回归,weights='uniform'
uni_knr=KNeighborsRegressor(weights='uniform')
uni_knr.fit(X_train,y_train)
uni_knr_y_predict=uni_knr.predict(X_test)

# 初始化回归器,调整配置,使预测方式为根据距离加权回归,weights='distance'
dis_knr=KNeighborsRegressor(weights='distance')
dis_knr.fit(X_train,y_train)
dis_knr_y_predict=dis_knr.predict(X_test)

2.性能测评

1
2
3
4
5
# 使用R-squared、MSE、MAE三指标分别对两种不同配置的模型进行性能评估
from sklearn.metrics import r2_score,mean_squared_error,mean_absolute_error

print 'Uniform',uni_knr.score(X_test,y_test),mean_squared_error(y_test,uni_knr_y_predict),mean_absolute_error(y_test,uni_knr_y_predict)
print 'Distance',dis_knr.score(X_test,y_test),mean_squared_error(y_test,dis_knr_y_predict),mean_absolute_error(y_test,dis_knr_y_predict)
1
2
Uniform 0.6418225886716102 27.773540157480316 3.7645669291338586
Distance 0.6565370125979323 26.63256467749057 3.6251742046017417

由上可知,K近邻加权平均的回归策略具有更好的预测性能

2.1.3.4 回归树

模型介绍

  • 选择不同特征作为分裂节点的策略上,与决策树类似
  • 不同:回归树叶节点的数据类型为连续型非离散型;决策树每个叶子节点依照训练数据表现的概率倾向决定其最终的预测类别,而回归树叶子节点是一个个具体数值,从预测值连续的意义上严格讲,回归树不能称为回归算法(因为回归树叶子节点返回的是“一团”训练数值的均值,而非具体连续的预测值

1.使用回归树对波士顿房价训练数据进行学习,并对测试数据预测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from sklearn.datasets import load_boston
boston=load_boston()

from sklearn.cross_validation import train_test_split
import numpy as np
X=boston.data
y=boston.target
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.25,random_state=33)


# 训练模型
from sklearn.tree import DecisionTreeRegressor

dtr=DecisionTreeRegressor()
dtr.fit(X_train,y_train)
dtr_y_predict=dtr.predict(X_test)

2.性能测评

1
2
3
from sklearn.metrics import r2_score,mean_squared_error,mean_absolute_error

print dtr.score(X_test,y_test),mean_squared_error(y_test,dtr_y_predict),mean_absolute_error(y_test,dtr_y_predict)
1
0.5223911380673973 37.034409448818906 3.4700787401574806

树模型(回归树,决策树)

  • 优点:可解决非线性拟合问题不要求对特征标准化和统一量化(即数值型、类别型特征都可直接被训练);可直观输出决策过程,使决策结果具有可解释性
  • 缺点:容易因为模型搭建得过于复杂而丧失对新数据的精确预测能力(泛化能力);树模型从上至下的预测流程会因为数据细微的更改而发生较大的结构变化,故预测稳定性较差;在有限时间内无法找到最优解(而只是次优解)

2.1.3.5 集成模型(回归)

补充:极端随机森林(Extremely Randomized Trees)

  • 每构建一棵树的分裂节点时,不会任意选取特征,而是先随机选取一部分特征,然后利用信息熵(Information Gain)和基尼不纯性(Gini Impurity)等指标挑选出最佳节点特征

1.使用三种集成回归模型对波士顿房间训练数据进行学习,并对测试数据进行预测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from sklearn.datasets import load_boston
boston=load_boston()

from sklearn.cross_validation import train_test_split
import numpy as np
X=boston.data
y=boston.target
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.25,random_state=33)


from sklearn.ensemble import RandomForestRegressor,ExtraTreesRegressor,GradientBoostingRegressor

rfr=RandomForestRegressor()
rfr=rfr.fit(X_train,y_train)
rfr_y_predict=rfr.predict(X_test)

etr=ExtraTreesRegressor()
etr=etr.fit(X_train,y_train)
etr_y_predict=etr.predict(X_test)

gbr=GradientBoostingRegressor()
gbr=gbr.fit(X_train,y_train)
gbr_y_predict=gbr.predict(X_test)

2.性能评估

1
2
3
4
5
from sklearn.metrics import r2_score,mean_squared_error,mean_absolute_error

print 'RFR',rfr.score(X_test,y_test),mean_squared_error(y_test,rfr_y_predict),mean_absolute_error(y_test,rfr_y_predict)
print 'ETR',etr.score(X_test,y_test),mean_squared_error(y_test,etr_y_predict),mean_absolute_error(y_test,etr_y_predict)
print 'GBR',gbr.score(X_test,y_test),mean_squared_error(y_test,gbr_y_predict),mean_absolute_error(y_test,gbr_y_predict)
1
2
3
RFR 0.8515156020299433 11.513672440944882 2.257322834645669
ETR 0.8003897320777417 15.478038582677165 2.416299212598424
GBR 0.8430286082992219 12.171764921769585 2.277247326989519

2.2 无监督学习经典模型

无监督学习(Unsupervised Learning)

  • 着重发现数据本身的特点
  • 无需标记数据

2.2.1 数据聚类

数据聚类:

  • 无监督学习的主流应用之一

2.2.1.1 K-means算法

模型介绍

  • 最经典易用的聚类模型;要求预先设定聚类个数,然后不断更新聚类中心,经过几轮迭代,最后的目标是让所有数据点到其所属聚类中心距离的平方和趋于稳定
  • 算法执行的过程分4个阶段:
    • 1.随机布设K个特征空间内的点作为初始的聚类中心;
    • 2.根据每个数据的特征向量,从K个聚类中心中寻找距离最近的一个,并且把该数据标记为从属于这个聚类中心;
    • 3.在所有数据都被标记过聚类中心之后,根据这些数据新分配的类簇,重新对K个聚类中心做计算;
    • 4.若一轮下来,所有数据点从属的聚类中心与上一次分配的类簇没有变化,则迭代可停止,否则回到步骤2继续循环

k-means算法迭代过程

数据描述

  • 手写体数字图像识别数据集
  • Address

聚类算法的性能评估指标

1.若被评估数据已被标注正确的类别,则使用2个指标:

  • ARI指标(Adjusted Rand Index)
  • Accuracy(准确性,同分类问题)

2.若被评估数据无所属类别,则使用轮廓系数(Silhouette Coefficient)来度量聚类结果的质量,说明:

  • 轮廓系数兼顾聚类的凝聚度(Cohesion)和分离度(Separation)
  • 取值范围:[-1,1],轮廓系数值越大,则聚类效果越好
  • 具体计算步骤:
    • 1.对已聚类数据中第$i$个样本$x^i$ ,计算$x^i$与其同一个类簇内的所有其他样本距离的平均值,记作$a^i$ ,用于量化簇内的凝聚度;
    • 2.选取$x^i$ 外的一个簇$b$,计算$x^i$与簇$b$中所有样本的平均距离,遍历所有其他簇,找到最近的这个平均距离,记作$b^i$ ,用于量化簇之间分离度;
    • 3.对于样本$x^i$ ,轮廓系数为$sc^i=\frac{b^i-a^i}{max(b^i,a^i)}$ ;
    • 4.最后对所有样本$X$求出平均值,即为当前聚类结果的整体轮廓系数
  • 衡量效果:
    • 若$sc^i < 0$ ,则说明$x^i$ 与其簇内元素的平均距离大于最近的其他簇,表示聚类效果不好;
    • 若$a^i$ 趋于0,或$b^i$ 足够大,则$sc^i $ 趋于1 ,表示聚类效果好;

1.导入和查验数据

1
2
3
4
5
6
7
8
9
import pandas as pd

digits_train=pd.read_csv('/Users/scarlett/repository/projects/digits/optdigits.tra',header=None)
digits_test=pd.read_csv('/Users/scarlett/repository/projects/digits/optdigits.tes',header=None)
# header=None 表示第一行是数据而非文件第一行

print digits_train.shape,digits_test.shape
print digits_train.head()
print digits_train.info()

2.使用K-means算法识别手写体图像数据

图像数据由8*8像素矩阵表示,64个像素维度;1个目标维度用来标记每个图像样本代表的数字类别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np
import matplotlib.pyplot as plt

# 从训练集合测试集上都分离出64维度的像素特征和1维度的数字目标
X_train=digits_train[np.arange(64)]
y_train=digits_train[64]
X_test=digits_test[np.arange(64)]
y_test=digits_test[64]

from sklearn.cluster import KMeans

# 初始化模型,并设置聚类中心数量为10
kmeans=KMeans(n_clusters=10)
kmeans.fit(X_train)
# 逐条判断每个测试图像所属的聚类中心
y_pred=kmeans.predict(X_test)

3.性能测评

1
2
3
# 使用ARI进行K-means聚类性能评估
from sklearn import metrics
print metrics.adjusted_rand_score(y_test,y_pred)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 使用轮廓系数评估不同类簇数量的
from sklearn.cluster import KMeans

# 导入计算轮廓系数的模块
from sklearn.metrics import silhouette_score

# 分割出3*2=6个子图,并在1号子图作图;subplot(m,n,p)是将多个图画到一个平面上的工具,m行n列,p=1代表从左到右从上到下的第一个位置
plt.subplot(3,2,1)

# 初始化原始数据点
x1=np.array([1,2,3,1,5,6,5,5,6,7,8,9,7,9])
x2=np.array([1,3,2,2,8,6,7,6,7,1,2,1,1,3])
X=np.array(zip(x1,x2)).reshape(len(x1),2)

# 在1号子图做出原始数据点阵的分布
plt.xlim([0,10])
plt.ylim([0,10])
plt.title('Instance')
plt.scatter(x1,x2)

colors=['b','g','r','c','m','y','k','b']
markers=['o','s','D','v','^','p','*','+']

clusters=[2,3,4,5,8]
subplot_counter=1
sc_scores=[]

for t in clusters:
subplot_counter += 1
plt.subplot(3,2,subplot_counter)
kmeans_model=KMeans(n_clusters=t).fit(X)

for i,l in enumerate(kmeans_model.labels_):
plt.plot(x1[i],x2[i],color=colors[l],marker=markers[l],ls='None')

plt.xlim([0,10])
plt.ylim([0,10])
sc_score=silhouette_score(X,kmeans_model.labels_,metric='euclidean')
sc_scores.append(sc_score)

plt.title('K=%s,silhouette coefficient=%0.03f' %(t,sc_score))

plt.figure()
plt.plot(clusters,sc_scores,'*-')
plt.xlabel('Number of Clusters')
plt.ylabel('silhouette coefficient score')

plt.show()

由图可知,当聚类中心数量k=3时,轮廓系数最大;由轮廓系数与不同类簇数量的关系曲线可知,聚类中心数量为3也符合数据分布特点。

特点分析:

  • K-means聚类模型采取的是迭代式算法
  • 缺点:容易收敛到局部最优解;需要预先设定簇的数量

局部最优解

  • 最优化:在复杂环境中遇到的许多可能的决策中,挑选“最好”的决策
  • 局部最优:指对于一个问题的解在一定范围或区域内最优,或者说解决问题或达成目标的手段在一定范围或限制内最优(和全局最优不同,局部最优不要求在所有决策中是最好的)
  • 全局最优:针对一定条件/环境下的一个问题/目标,若一项决策和所有解决该问题的决策相比是最优的,就可以被称为全局最优
  • 如下图:左边是实际数据和正确的所属类簇;右下的局部最优情况导致无法继续更新聚类中心,使聚类结果与正确结果相差很大
  • “容易收敛到局部最优解”是算法自身的缺陷,但可通过执行多次kmeans算法来挑选性能最好的初始中心点

K-means局部最优与全局最优比较

肘部观察法

  • 作用:粗略估计相对合理的类簇个数

  • 思路:因为K-means模型最终期望所有数据点到其所属的类簇举例的平方和趋于稳定,所以我们可以通过观察这个数值随K的走势来找出最佳的类簇数量;理想条件下,这个折线在不断下降且趋于平缓的过程中会有斜率的拐点,即从这个拐点对应的K值开始,类簇中心的增加不会过于破坏数据聚类的结构(进一步增加K值不会再有利于算法的收敛),则此拐点K=n是相对最佳的类簇数量

    3个簇

肘部平均距离与类簇数量的关系

肘部观察法示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import numpy as np
from sklearn.cluster import KMeans
from scipy.spatial.distance import cdist
import matplotlib.pyplot as plt

# 使用均匀分布函数随机三个簇,每个簇周围10个数据样本
cluster1=np.random.uniform(0.5,1.5,(2,10))
cluster2=np.random.uniform(5.5,6.5,(2,10))
cluster3=np.random.uniform(10.5,11.5,(2,10))

# 绘制30个数据样本的分布图像
X=np.hstack((cluster1,cluster2,cluster3)).T
plt.scatter(X[:,0],X[:,1])
plt.xlabel('x1')
plt.ylabel('x2')
plt.show()

# 测试9种不同聚类中心数量下,每种情况的聚类质量
K=range(1,10)
meandistortions=[]

for k in K:
kmeans=KMeans(n_clusters=k)
kmeans.fit()
meandistortions.append(sum(np.min(cdist(X,kmeans.cluster_centers_,'euclidean'),axis=1))/X.shape[0])


plt.plot(K,meandistortions,'bx-')
plt.xlabel('k')
plt.ylabel('Average Dispersion')
plt.title('Selecting k with the Elbow Method')
plt.show()

2.2.2 特征降维

特征降维

  • 特征维度过高,无法构建有效特征;无法肉眼观测超过三个维度的特征
  • 重构有效的低维特征向量,为数据拓展提供可能

2.2.2.1 主成分分析(Principle Component Analysis)

模型介绍

  • 最经典使用的特征降维技术;辅助图像识别
  • 举例:若我们有一组2*2的数据[(1,2),(2,4)],假设这两个数据都反映到一个类别或类簇;若我们的学习模型是线性模型,则这两个模型只能帮助权重参数更新1次,因为他们线性相关,所有特征值只是扩张了相同背书;若使用PCA分析,则此矩阵的“秩”=1,即在多样性程度上,此矩阵只有1个自由度。
  • 可把PCA当做特征选择,这种特征选择是先把原来的特征空间作了映射,使得新的映射后特征空间数据彼此正交;则我们通过主成分分析就尽可能保留下具备区分性的低维数据特征

矩阵的秩:一个矩阵A的列秩是A的线性独立的纵列的极大数目,通常表示为r(A)或rank A

自由度:统计学上,指当以样本的统计量来估计总体的参数时,样本中独立或能自由变化的数据的个数;数学上,自由度是一个随机向量的维度数,即一个向量能被完整描述所需的最少单位向量数。如从电脑屏幕到厨房的位移能够用三维向量$\widehat{ai}+\widehat{bj}+\widehat{ck}$来描述,因此这个位移向量的自由度是3。自由度也通常与这些向量的座标平方和,以及卡方分布中的参数有所关联。

求线性相关矩阵的秩

1
2
3
import numpy as np 
test = np.array([[1,2],[2,4]])
print np.linalg.matrix_rank(test,tol=None)
1
1

数据描述

  • 数据集:digits
  • 展示经PCA处理后,这些数字图像映射在二维空间的分布情况;结果会发现把64维度的图像压缩到2维空间后,依然可发现绝大多数数字之间的区分性

1.显示手写体数字图片经PCA压缩后的二维空间分布

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import pandas as pd 
digits_train=pd.read_csv('/Users/scarlett/repository/projects/digits/optdigits.tra',header=None)
digits_test=pd.read_csv('/Users/scarlett/repository/projects/digits/optdigits.tes',header=None)

# 分割训练数据的特征向量和标记,前64维是feature vector,第65维是标记
X_digits=digits_train[np.arange(64)]
y_digits=digits_train[64]

# 导入PCA
from sklearn.decomposition import PCA
# 初始化一个可将高维向量压缩到二维的PCA
estimator=PCA(n_components=2)
X_pca=estimator.fit_transform(X_digits)

# 显示10类图像经PCA压缩后的二维空间分布
from matplotlib import pyplot as plt

def plot_pca_scatter():

colors=['black','blue','purple','yellow','white','red','lime','cyan','orange','gray']

for i in xrange(len(colors)):
px=X_pca[:,0][y_digits.as_matrix()==i]
py=X_pca[:,1][y_digits.as_matrix()==i]
plt.scatter(px,py,c=colors[i])

plt.legend(np.arange(0,10).astype(str))
plt.xlabel('First Principle Component')
plt.ylabel('Second Principle Component')
plt.show()
plt_pca_scatter()

2.使用原始像素特征和经PCA压缩重建的低维特征,在相同配置的SVM上分别进行图像识别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
X_train=digits_train[np.arange(64)]
y_train=digits_train[64]
X_test=digits_test[np.arange(64)]
y_test=digits_test[64]

# 导入基于线性核的SVM分类器,建模,预测
from sklearn.svm import LinearSVC
svc=LinearSVC()
svc.fit(X_train,y_train)
y_predict=svc.predict(X_test)

# 特征压缩到20维,并转化原训练特征
estimator=PCA(n_components=20)
pca_X_train=estimator.fit_transform(X_train)
pca_X_test=estimator.transform(X_test)

# 对压缩后的20维特征的训练数据进行建模,并对测试集预测
pca_svc=LinearSVC()
pca_svc.fit(pca_X_train,y_train)
pca_y_predict=pca_svc.predict(pca_X_test)

print pca_y_predict,y_predict
1
[0 1 1 ... 8 9 8] [0 1 2 ... 8 9 8]

3.性能评估

1
2
3
4
5
6
7
from sklearn.metrics import classification_report

print svc.score(X_test,y_test)
print classification_report(y_test,y_predict,target_names=np.arange(10).astype(str))

print pca_svc.score(pca_X_test,y_test)
print classification_report(y_test,pca_y_predict,target_names=np.arange(10).astype(str))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
0.9309961046188091
precision recall f1-score support

0 0.98 0.98 0.98 178
1 0.85 0.95 0.90 182
2 0.99 0.97 0.98 177
3 0.92 0.95 0.93 183
4 0.95 0.97 0.96 181
5 0.90 0.96 0.93 182
6 0.99 0.98 0.99 181
7 0.98 0.91 0.94 179
8 0.96 0.74 0.83 174
9 0.82 0.91 0.86 180

avg / total 0.93 0.93 0.93 1797

0.9081803005008348
precision recall f1-score support

0 0.97 0.96 0.96 178
1 0.80 0.91 0.85 182
2 0.96 0.94 0.95 177
3 0.96 0.91 0.94 183
4 0.94 0.96 0.95 181
5 0.86 0.97 0.91 182
6 0.98 0.96 0.97 181
7 0.96 0.88 0.92 179
8 0.82 0.83 0.83 174
9 0.87 0.75 0.80 180

avg / total 0.91 0.91 0.91 1797

由上发现,经过PCA处理后会损失2%左右的预测准确性,但相比原始数据64维度的特征,使用PCA可降低68.75%的维度、

特点分析:

  • 降维/压缩是选取数据具有代表性的特征,在保持数据多样性(Variance)的基础上,规避掉大量的特征冗余和噪声;并可节省模型训练时间,提高综合效率
  • 容易损失一些有用的模式信息

3.进阶篇

前一节使用的数据集都是经过规范化处理的的规整数据集,使用的模型也都是默认配置,但现实生活中我们得到的数据集不会如此规整,默认配置也不一定最佳。

本章目的:掌握如何通过抽取或筛选数据特征、优化模型配置,以进一步提升经典模型的性能表现。

3.1 模型实用技巧

依靠默认配置学习到模型所需的参数,不能保证:

  • 所有用于训练的数据特征都是最好的
  • 学习到的参数一定是最优的
  • 默认配置下的模型总是最佳的

本节技巧:预处理数据,控制参数训练、优化模型配置,etc

3.1.1 特征提升

特征抽取逐条将原始数据转化为特征向量的形式,这个过程同时涉及到对数据特征的量化表示

特征筛选:(更进一步)在高维度、已量化的特征向量中选择对指定任务更有效的特征组合,进一步提升模型性能

3.1.1.1 特征抽取

原始数据的种类有很多:数字化的信号数据(声纹、图像),符号化的文本;而我们无法直接将符号化的文本用于计算,而需要通过某些处理手段预先将文本良华为特征向量。

1.DictVectorizer对使用字典存储的数据进行特征抽取和向量化

有些符号化的数据特征已相对结构化,并以字典这种数据结构进行存储,故可使用DictVectorizer对特征进行抽取和向量化。

1
2
3
4
5
6
7
8
9
10
11
12
M=[{'city':'Dubai','temperature':33.},{'city':'London','temperature':12.},{'city':'Beijing','temperature':40.}]

from sklearn.feature_extraction import DictVectorizer

# 初始化特征抽取器
vec=DictVectorizer()

# 输出转化后的特征矩阵
print vec.fit_transform(M).toarray()

# 输出各维度特征的含义
print vec.get_feature_names()
1
2
3
4
[[ 0.  1.  0. 33.]
[ 0. 0. 1. 12.]
[ 1. 0. 0. 40.]]
['city=Beijing', 'city=Dubai', 'city=London', 'temperature']

由输出可知,特征向量化过程中。DictVectorizer对类别型和数值型特征的处理方式不同。

  • 类别型(categorical)特征:借助原特征名称组合产生新特征,并用0/1二值方式进行量化
  • 数值型(numerical):维持原始特征值

2.使用CountVectorizer且在不去掉停用词的条件下,对文本特征进行量化的朴素贝叶斯分类性能测试

处理文本数据的方法:词袋法(Bag of Words)

  • 词袋法:不考虑词语出现的顺序,只将训练文本中的每个出现过的词汇单独视作一列特征;词表:不重复的词汇的集合;每条训练文本都可在高维度词表上映射出一个特征向量;
  • 特征数值的常见计算方式:CountVectorizer & TfidfVectorizer
  • CountVectorizer:只考虑每种词汇(Term)在该条训练文本中出现的频率(Term Frequency)
  • TfidfVectorizer:既考量某一次会在当前文本中出现的频率,又考虑包含这个词汇的文本条数的倒数(Inverse Document Frequency),即训练的条目越多,TfidfVectorizer的特征量化就越有优势;可剔除在每条文本中都出现的常用词汇,以减少它们对模型分类决策的影响
  • 停用词(Stop Words):在每条文本中都出现的常用词汇,如the,a;停用词常在特征抽取中以黑名单的方式过滤掉,以提高模型的性能表现
  • 区别:CountVectorizer只统计词频,而TfidfVectorizer还过滤掉了停用词
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from sklearn.datasets import fetch_20newsgroups
news=fetch_20newsgroups(subset='all')

from sklearn.cross_validation import train_test_split
X_train,y_train,X_test,y_test=train_test_split(news.data,news.target,test_size=0.25,random_state=33)

from sklearn.feature_extraction.text import CountVectorizer
count_vec=CountVectorizer()

# 只使用词频统计将原始训练和测试文本转化为特征向量
X_count_train=count_vec.fit_transform(X_train)
X_count_test=count_vec.transform(X_test)

# 导入naive bayes,默认配置初始化,使用CountVectorizer(未剔除停用词的)后的训练样本进行学习
from sklearn.naive_bayes import MultinomialNB
mnb_count=MultinomialNB()
mnb_count.fit(X_count_train,y_train)
y_count_predict=mnb_count.predict(X_test)

# 输出性能评估结果
from sklearn.metrics import classification_report
print 'Accuracy:',mnb_count.score(X_count_train,y_train)
print classification_report(y_test,y_count_predict,target_names=news.target_names)

output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
The Accuracy of NBC is: 0.8397707979626485
precision recall f1-score support

alt.atheism 0.86 0.86 0.86 201
comp.graphics 0.59 0.86 0.70 250
comp.os.ms-windows.misc 0.89 0.10 0.17 248
comp.sys.ibm.pc.hardware 0.60 0.88 0.72 240
comp.sys.mac.hardware 0.93 0.78 0.85 242
comp.windows.x 0.82 0.84 0.83 263
misc.forsale 0.91 0.70 0.79 257
rec.autos 0.89 0.89 0.89 238
rec.motorcycles 0.98 0.92 0.95 276
rec.sport.baseball 0.98 0.91 0.95 251
rec.sport.hockey 0.93 0.99 0.96 233
sci.crypt 0.86 0.98 0.91 238
sci.electronics 0.85 0.88 0.86 249
sci.med 0.92 0.94 0.93 245
sci.space 0.89 0.96 0.92 221
soc.religion.christian 0.78 0.96 0.86 232
talk.politics.guns 0.88 0.96 0.92 251
talk.politics.mideast 0.90 0.98 0.94 231
talk.politics.misc 0.79 0.89 0.84 188
talk.religion.misc 0.93 0.44 0.60 158

avg / total 0.86 0.84 0.82 4712

3.使用TfidfVectorizer且在不去掉停用词的条件下,对文本特征进行量化的朴素贝叶斯分类性能测试

1
2
3
4
5
6
7
8
9
10
from sklearn.datasets import fetch_20newsgroups

# 即时从网上下载数据
news=fetch_20newsgroups(subset='all')

print len(news.data)
print news.data[0]

from sklearn.cross_validation import train_test_split
X_train,X_test,y_train,y_test=train_test_split(news.data,news.target,test_size=0.25,random_state=33)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from sklearn.feature_extraction.text import TfidfVectorizer
tfidf_vec=TfidfVectorizer()

# 转化为特征向量
X_tfidf_train=tfidf_vec.fit_transform(X_train)
X_tfidf_test=tfidf_vec.transform(X_test)

from sklearn.naive_bayes import MultinomialNB
mnb_tfidf=MultinomialNB()
mnb_tfidf.fit(X_tfidf_train,y_train)
y_tfidf_predict=mnb_tfidf.predict(X_tfidf_test)

# 性能评估
print 'Accuracy:',mnb_tfidf.score(X_tfidf_test,y_test)
from sklearn.metrics import classification_report
print classification_report(y_test,y_tfidf_predict,target_names=news.target_names)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Accuracy: 0.8463497453310697
precision recall f1-score support

alt.atheism 0.84 0.67 0.75 201
comp.graphics 0.85 0.74 0.79 250
comp.os.ms-windows.misc 0.82 0.85 0.83 248
comp.sys.ibm.pc.hardware 0.76 0.88 0.82 240
comp.sys.mac.hardware 0.94 0.84 0.89 242
comp.windows.x 0.96 0.84 0.89 263
misc.forsale 0.93 0.69 0.79 257
rec.autos 0.84 0.92 0.88 238
rec.motorcycles 0.98 0.92 0.95 276
rec.sport.baseball 0.96 0.91 0.94 251
rec.sport.hockey 0.88 0.99 0.93 233
sci.crypt 0.73 0.98 0.83 238
sci.electronics 0.91 0.83 0.87 249
sci.med 0.97 0.92 0.95 245
sci.space 0.89 0.96 0.93 221
soc.religion.christian 0.51 0.97 0.67 232
talk.politics.guns 0.83 0.96 0.89 251
talk.politics.mideast 0.92 0.97 0.95 231
talk.politics.misc 0.98 0.62 0.76 188
talk.religion.misc 0.93 0.16 0.28 158

avg / total 0.87 0.85 0.84 4712

由输出可知,在使用TfidfVectorizer而不去掉停用词的条件下,对训练和测试文本进行特征量化,并利用默认配置的naive bayes,在测试文本上可得到比CountVectorizer更高的预测准确性。证明:在训练文本量较多时,使用TfidfVectorizer压制常用词汇对分类决策的干扰,可提升模型性能

4.分别使用CountVectorizer和TfidfVectorizer,并在去掉停用词的条件下,对文本特征进行量化的Naive Bayes分类性能测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 分别使用停用词过滤器配置初始化CountVectorizer和TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
count_filter_vec,tfidf_filter_vec=CountVectorizer(analyzer='word',stop_words='english'),TfidfVectorizer(analyzer='word',stop_words='english')

# 使用带停用词过滤的CountVectorizer对训练和测试文本进行量化处理
X_count_filter_train=count_filter_vec.fit_transform(X_train)
X_count_filter_test=count_filter_vec.transform(X_test)

# 使用带停用词过滤的TfidfVectorizer对训练和测试文本进行量化处理
X_tfidf_filter_train=tfidf_filter_vec.fit_transform(X_train)
X_tfidf_filter_test=tfidf_filter_vec.transform(X_test)


# 初始化默认配置的朴素贝叶斯,并对CountVectorizer后的数据进行预测和性能评估
from sklearn.naive_bayes import MultinomialNB
mnb_count_filter=MultinomialNB()
mnb_count_filter.fit(X_count_filter_train,y_train)
y_count_predict=mnb_count_filter.predict(X_count_filter_test)

# 初始化另一个默认配置的朴素贝叶斯,并对TfidfVectorizer后的数据进行预测和性能评估
mnb_tfidf_filter=MultinomialNB()
mnb_tfidf_filter.fit(X_tfidf_filter_train,y_train)
y_tfidf_predict=mnb_tfidf_filter.predict(X_tfidf_filter_test)

# CountVectorizer性能评估
from sklearn.metrics import classification_report
print 'Count_Accuracy',mnb_count_filter.score(X_count_filter_train,y_train)
print classification_report(y_test,y_count_predict,target_names=news.target_names)

# TfidfVectorizer性能评估
print 'Tfidf_Accuracy',mnb_tfidf_filter.score(X_tfidf_filter_train,y_train)
print classification_report(y_test,y_tfidf_predict,target_names=news.target_names)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
Count_Accuracy 0.9439649073156926
precision recall f1-score support

alt.atheism 0.85 0.89 0.87 201
comp.graphics 0.62 0.88 0.73 250
comp.os.ms-windows.misc 0.93 0.22 0.36 248
comp.sys.ibm.pc.hardware 0.62 0.88 0.73 240
comp.sys.mac.hardware 0.93 0.85 0.89 242
comp.windows.x 0.82 0.85 0.84 263
misc.forsale 0.90 0.79 0.84 257
rec.autos 0.91 0.91 0.91 238
rec.motorcycles 0.98 0.94 0.96 276
rec.sport.baseball 0.98 0.92 0.95 251
rec.sport.hockey 0.92 0.99 0.95 233
sci.crypt 0.91 0.97 0.93 238
sci.electronics 0.87 0.89 0.88 249
sci.med 0.94 0.95 0.95 245
sci.space 0.91 0.96 0.93 221
soc.religion.christian 0.87 0.94 0.90 232
talk.politics.guns 0.89 0.96 0.93 251
talk.politics.mideast 0.95 0.98 0.97 231
talk.politics.misc 0.84 0.90 0.87 188
talk.religion.misc 0.91 0.53 0.67 158

avg / total 0.88 0.86 0.85 4712

Tfidf_Accuracy 0.9479977359558511
precision recall f1-score support

alt.atheism 0.86 0.81 0.83 201
comp.graphics 0.85 0.81 0.83 250
comp.os.ms-windows.misc 0.84 0.87 0.86 248
comp.sys.ibm.pc.hardware 0.78 0.88 0.83 240
comp.sys.mac.hardware 0.92 0.90 0.91 242
comp.windows.x 0.95 0.88 0.91 263
misc.forsale 0.90 0.80 0.85 257
rec.autos 0.89 0.92 0.90 238
rec.motorcycles 0.98 0.94 0.96 276
rec.sport.baseball 0.97 0.93 0.95 251
rec.sport.hockey 0.88 0.99 0.93 233
sci.crypt 0.85 0.98 0.91 238
sci.electronics 0.93 0.86 0.89 249
sci.med 0.96 0.93 0.95 245
sci.space 0.90 0.97 0.93 221
soc.religion.christian 0.70 0.96 0.81 232
talk.politics.guns 0.84 0.98 0.90 251
talk.politics.mideast 0.92 0.99 0.95 231
talk.politics.misc 0.97 0.74 0.84 188
talk.religion.misc 0.96 0.29 0.45 158

avg / total 0.89 0.88 0.88 4712

由输出可知,TfidfVectorizer的特征抽取和量化方法更具备优势,对停用词进行过滤后的模型性能比未过滤高3%—4%

3.1.1.2 特征筛选

良好的数据特征组合可提高模型性能,冗余特征会浪费CPU计算资源,不良特征会降低模型精度。

主成分分析(PCA):用于去除线性相关的特征组合

特征筛选:不是修改特征值,而是寻找对模型性能提升大的少量特征

使用Titanic数据集,通过特征筛选法一步步提升决策树的预测性能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 导入数据
import pandas as pd
titanic=pd.read_csv('/Users/scarlett/repository/projects/titanic/titanic.csv')
print titanic.shape
print titanic.info()

# 分离数据特征与预测目标
y=titanic['survived']
X=titanic.drop(['row.names','name','survived'],axis=1)

# 填充缺失数据
X['age'].fillna(X['age'].mean(),inplace=True)
X.fillna('UNKNOWN',inplace=True)

# 分割数据
from sklearn.cross_validation import train_test_split
X_train,y_train,X_test,y_test=train_test_split(X,y,test_size=0.25,random_state=33)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(1313, 11)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1313 entries, 0 to 1312
Data columns (total 11 columns):
row.names 1313 non-null int64
pclass 1313 non-null object
survived 1313 non-null int64
name 1313 non-null object
age 633 non-null float64
embarked 821 non-null object
home.dest 754 non-null object
room 77 non-null object
ticket 69 non-null object
boat 347 non-null object
sex 1313 non-null object
dtypes: float64(1), int64(2), object(8)
memory usage: 112.9+ KB
None
1
2
3
4
5
6
# 类别型特征向量化
from sklearn.feature_extraction import DictVectorizer
vec=DictVectorizer()
X_train=vec.fit_transform(X_train.to_dict(orient='record'))
X_test=vec.transform(X_test.to_dict(orient='record'))
print len(vec.feature_names_)
1
2
3
4
from sklearn.tree import DecisionTreeClassifier
dt=DecisionTreeClassifier()
dt.fit(X_train,y_train)
dt.score(X_test,y_test)
1
2
3
4
5
6
7
# 导入特征筛选器
from sklearn import feature_selection
fs=feature_selection.SelectPercentile(feature_selection.chi2,percentile=20)
X_train_fs=fs.fit_transform(X_train,y_train)
dt.fit(X_train_fs,y_train)
X_test_fs=fs.transform(X_test)
dt.score(X_test_fs,y_test)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 通过交叉验证法,按照固定间隔的百分比筛选特征,并作图展示性能岁特征筛选比例的变化
from sklearn.cross_validation import cross_val_score
import numpy as np
percentile=range(1,100,2)
results=[]

for i in percentile:
fs=feature_selection.SelectPercentile(feature_selection.chi2,percentile=i)
X_train_fs=fs.fit_transform(X_train,y_train)
scores=cross_val_score(dt,X_train_fs,y_train,cv=5)
results=np.append(results,scores.mean())
print results

# 找到提现最佳性能的特征筛选的百分比
opt=np.where(results==results.max())[0]
print 'Optimal number of features %d'%percentiles[opt]

3.1.2 模型正则化

任何机器学习模型在训练集上的性能表现都不能作为其对未知测试数据预测能力的评估。

本节:模型泛化力(Generalization),和如何保证模型泛化力

3.1.2.1 欠拟合和过拟合(Underfitting & Overfitting)

拟合:机器学习模型在训练过程中,通过更新参数,使模型不断契合可观测数据(训练集)的过程

阐述:模型复杂度与泛化力的关系

数据描述

  • 披萨饼价格预测
  • 每种直径(Diameter)对应一个报价
  • 需要设计一个学习模型,可根据披萨的直径特征来预测售价

biao

由上表:5组训练数据,4组测试数据且报价未知;只考虑直径与售价的关系,则适合用线性回归模型。

1.使用线性回归模型在披萨训练样本上进行拟合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
X_train=[[6],[8],[10],[14],[18]]
y_train=[[7],[9],[13],[17.5],[18]]

# 导入线性回归模型
from sklearn.linear_model import LinearRegression
regressor=LinearRegression()
regressor.fit(X_train,y_train)

# 导入numpy
import numpy as np
# 在x轴上从0-25均匀采样100个数据点,并以100个数据点为基准,预测回归直线
xx=np.linspace(0,26,100)
xx=xx.reshape(xx.shape[0],1)
yy=regressor.predict(xx)

# 对预测到的直线作图,
import matplotlib.pyplot as plt
plt.scatter(X_train,y_train)
# 使用plt.plot()画(x,y)曲线,degree=1表示特征是一维的,做个标记
plt1,=plt.plot(xx,yy,label="Degree=1")
plt.axis([0,25,0,25]) # axis表示坐标的极值范围
plt.xlabel('Diameter of pizza')
plt.ylabel('Price')
plt.show()

# 输出模型在训练样本上的R-squared值
print regressor.score(X_train,y_train)
1
0.9100015964240102

接下来我们尝试将原特征提高一个维度,用2次多项式回归来拟合训练样本

2.使用2次多项式回归模型在训练样本上进行拟合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from sklearn.preprocessing import PolynomialFeatures
# 使用PolynomialFeatures(degree=2)映射出2次多项式特征
poly2=PolynomialFeatures(degree=2)
X_train_poly2=poly2.fit_transform(X_train)

# 以线性回归模型为基础,初始化模型(特征维度提升,但模型仍是线性回归模型)
regressor_poly2=LinearRegression()
regressor_poly2.fit(X_train_poly2,y_train)

# 从新映射绘图用x轴采样数据
xx_poly2=poly2.transform(xx)

# 预测
yy_poly2=regressor_poly2.predict(xx_poly2)

# 作图
plt.scatter(X_train,y_train)
plt1,=plt.plot(xx,yy,label='degree=1')
plt2,=plt.plot(xx,yy_poly2,label='degree=2')
plt.axis([0,25,0,25])
plt.xlabel('diameter')
plt.ylabel('price')
plt.show()

print regressor_poly2.score(X_train_poly2,y_train)
1
0.9816421639597428

果然在升高特征维度后,模型性能更高,对训练数据的拟合程度更好。接下来我们进一步提高特征维度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from sklearn.preprocessing import PolynomialFeatures
# 使用PolynomialFeatures(degree=4)映射出2次多项式特征
poly4=PolynomialFeatures(degree=4)
X_train_poly4=poly4.fit_transform(X_train)

# 以线性回归模型为基础,初始化模型(特征维度提升,但模型仍是线性回归模型)
regressor_poly4=LinearRegression()
regressor_poly4.fit(X_train_poly4,y_train)

# 从新映射绘图用x轴采样数据
xx_poly4=poly4.transform(xx)

# 预测
yy_poly4=regressor_poly4.predict(xx_poly4)

# 作图
plt.scatter(X_train,y_train)
plt1,=plt.plot(xx,yy,label='degree=1')
plt2,=plt.plot(xx,yy_poly2,label='degree=2')
plt3,=plt.plot(xx,yy_poly4,label='degree=4')
plt.axis([0,25,0,25])
plt.xlabel('diameter')
plt.ylabel('price')
plt.show()

print regressor_poly4.score(X_train_poly4,y_train)
1
1.0

由图和R平方指标可见,4次多项式曲线几乎完全拟合了所有训练样本点。接下来我们看着三种特征维度下的模型分别在测试集上的性能表现。

3.评估3种回归模型在测试集上的性能表现

1
2
X_test=[[6],[8],[11],[16]]
y_test=[[8],[12],[15],[18]]
1
2
3
4
5
6
7
8
9
10
# degree=1
print regressor.score(X_test,y_test)

# degree=2
X_test_poly2=poly2.transform(X_test)
print regressor_poly2.score(X_test_poly2,y_test)

# degree=4
X_test_poly4=poly4.transform(X_test)
print regressor_poly4.score(X_test_poly4,y_test)
1
2
3
0.809726797707665
0.8675443656345108
0.8095880795788558
特征多项式次数 训练集R-squared值 测试集R-squared值
degree=1 0.9100 0.8097
degree=2 0.9816 0.8675
degree=4 1.0000 0.8096

由输出可见

  • 欠拟合:当模型复杂度很低时(degree=1),模型既在训练集上拟合不好,又在测试集上表现一般
  • 过拟合:一味追求高模型复杂度(degree=4),尽管模型完美拟合了几乎所有训练数据,但模型会变得非常波动,几乎丧失了对未知数据的预测能力

这两种都是模型缺乏泛化力的表现。

要求我们在增加模型复杂度、提高在可观测数据上的性能表现的同时,需要兼顾模型的泛化力,防止发生过拟合。为了平衡这两种选择,我们通常采用2种模型正则化方法:L1范数正则化 & L2范数正则化

3.1.2.2 L1范数正则化

正则化(Regularization)

  • 目的:提高模型在位置测试数据上的泛化力,避免过拟合
  • 常见方法:在原模型优化目标的基础上,增加对参数的惩罚项(Penalty)

以最小二乘优化目标为例:

最小二乘优化目标: $argminL(w,b)=argmin\sum_{m}^{k=1}\qquad(f(w,x,b)-y^k)^2$

若加入对模型的L1范数正则化,则新的线性回归目标为:

在原优化目标的基础上,增加了参数向量的L1范数,则在新目标优化过程中需要考虑L1惩罚项的影响

为使目标最小化,这种正则化方法的结果是让参数向量中的许多元素趋向于0,使大部分特征失去对优化目标的贡献。而这种让有效特征变得稀疏的L1正则化模型,称为Lasso

Lasso模型在4次多项式特征上的拟合表现

1
2
3
4
5
6
from sklearn.linear_model import Lasso
lasso_poly4=Lasso()
lasso_poly4.fit(X_train_poly4,y_train)
print lasso_poly4.score(X_test_poly4,y_test)
# 输出lasso模型的参数列表
print lasso_poly4.coef_
1
2
3
0.8388926873604382
[ 0.00000000e+00 0.00000000e+00 1.17900534e-01 5.42646770e-05
-2.23027128e-04]
1
2
3
# 回顾普通4次多项式回归模型拟合后的性能和参数列表
print regressor_poly4.score(X_test_poly4,y_test)
print regressor_poly4.coef_
1
2
3
0.8095880795788558
[[ 0.00000000e+00 -2.51739583e+01 3.68906250e+00 -2.12760417e-01
4.29687500e-03]]

由上可见,默认配置的lasso模型性能提高了约3%。lasso模型拟合后的参数列表中,4次与3次特征的参数均为0.0,使得特征更加稀疏。

3.1.2.3 L2范数正则化

与L1范数正则化略有不同,L2范数正则化在原优化目标上增加了参数向量的L2范数的惩罚项,公式如下:

为使新优化目标最小化,这种正则化方法的结果会让参数向量中的大部分元素都变得很小,压制了参数之间的差异性,这种压制参数间差异性的L2正则化模型被称为Ridge

Ridge模型在4次多项式特征上的拟合表现

1
2
3
4
# 输出普通4次多项式回归模型的参数列表
print regressor_poly4.coef_
# 输出上述参数的平方和,验证参数间的巨大差异
print np.sum(regressor_poly4.coef_ **2)
1
2
3
[[ 0.00000000e+00 -2.51739583e+01  3.68906250e+00 -2.12760417e-01
4.29687500e-03]]
647.3826457370965
1
2
3
4
5
6
from sklearn.linear_model import Ridge
ridge_poly4=Ridge()
ridge_poly4.fit(X_train_poly4,y_train)
print ridge_poly4.score(X_test_poly4,y_test)
print ridge_poly4.coef_
print np.sum(ridge_poly4.coef_ **2)
1
2
3
0.8374201759366331
[[ 0. -0.00492536 0.12439632 -0.00046471 -0.00021205]]
0.01549896520353474

由输出可见,相比普通4次多项式回归模型,默认配置下的Ridge模型性能提高了约3%,且模型拟合后的参数间差异非常小

λ是调节因子。

3.1.3 模型检验

错误的做法:拿测试集的正确结果反复调优模型与特征。

正确的做法:

  • 充分使用现有数据,对现有数据进行采样分割,一部分用于模型参数训练(训练集),另一部分用于调优模型配置和特征选择,并对未知测试性能作出估计(开发集Development Set/验证集Validation Set)

根据验证流程复杂度的不同,模型验证方式分为:留一验证 & 交叉验证

3.1.3.1 留一验证(Leave-one-out cross validation)

留一验证

  • 最简单,即从任务提供的数据中,随机采样一定比例作为训练集,剩下的“留作”验证
  • 通常比例为:7:3
  • 缺点:模型性能不稳定(由于对验证集合随机采样的不确定性)
  • 适用:计算能力较弱、而相对数据规模较大的机器学习发展早期(现在应该很少用了)

3.1.3.2 交叉验证(K-fold cross-validation)

交叉验证

  • 执行了多次留一验证的过程

  • 每次检验所使用的验证集之间互斥,且需保证每一条可用数据都被模型验证过

  • 优点:保证所有数据都有被训练和验证的机会,尽最大可能让优化的模型性能表现可信

以5折(five-fold)交叉验证为例:

  • 全部可用数据被随机分割为平均数量的5组,每次迭代都选取其中的1组数据作为验证集,其余4组作为训练集

5-fold

3.1.4 超参数搜索(hyperparameter)

超参数

  • 开始学习之前设置值的参数(模型配置),而非通过训练得到的参数
  • 如K近邻算法中的K值,SVM中不同的核函数(Kernal)
  • 多数情况下,超参数的选择是无限的;故在有限时间内,除了可验证人工预设的几种超参数组合外,还可通过启发式的搜索方法对超参数组合进行调优,这种启发式的超参数搜索法称为网络搜索
  • 超参数的验证过程之间彼此独立,故适合并行计算

3.1.4.1 网格搜索(GridSearch)

由于超参数的空间无尽,故超参数组合配置只能是更优解,没有最优解

网格搜索

  • 依赖网格搜索对多种超参数组合的空间进行暴力搜索每一套超参数组合被代入到学习函数中作为新的模型,且为比较新模型间的性能,每个模型都会采用交叉验证法在多组相同的训练和开发集下进行评估

使用单线程对文本分类的Naive Bayes模型的超参数组合执行网格搜索

1
2
3
4
5
from sklearn.datasets import fetch_20newsgroups
import numpy as np
news=fetch_20newsgroups(subset='all')
print len(news.data)
print news.data[0]
1
2
3
4
5
6
18846
From: Mamatha Devineni Ratnam <mr47+@andrew.cmu.edu>
Subject: Pens fans reactions
Organization: Post Office, Carnegie Mellon, Pittsburgh, PA
Lines: 12
NNTP-Posting-Host: po4.andrew.cmu.edu
1
2
3
4
5
6
7
8
9
I am sure some bashers of Pens fans are pretty confused about the lack
of any kind of posts about the recent Pens massacre of the Devils. Actually,
I am bit puzzled too and a bit relieved. However, I am going to put an end
to non-PIttsburghers' relief with a bit of praise for the Pens. Man, they
are killing those Devils worse than I thought. Jagr just showed you why
he is much better than his regular season stats. He is also a lot
fo fun to watch in the playoffs. Bowman should let JAgr have a lot of
fun in the next couple of games since the Pens are going to beat the pulp out of Jersey anyway. I was very disappointed not to see the Islanders lose the final
regular season game. PENS RULE!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from sklearn.cross_validation import train_test_split
# 选取前3000条新闻文本进行分割
X_train,X_test,y_train,y_test=train_test_split(news.data[:3000],news.target[:3000],test_size=0.25,random_state=33)

from sklearn.svm import SVC
from sklearn.feature_extraction.text import TfidfVectorizer

# 导入pipeline,使用pipeline简化系统搭建流程(简化代码),将文本抽取与分类器模型串联起来
from sklearn.pipeline import Pipeline
clf=Pipeline([('vect',TfidfVectorizer(stop_words='english',analyzer='word')),('svc',SVC())])

# 这里需要试验的2个超参数的个数分别是4、3,svc_gamma的参数共有10^-2,10^-1...,则共有12种超参数组合,12种不同参数下的模型
parameters={'svc_gamma':np.logspace(-2,1,4),'svc_C':np.logspace(-1,1,3)}

# 导入网络搜索模块GridSearchCV
from sklearn.grid_search import GridSearchCV
# 将12组参数组合、初始化的Pipeline和3折交叉验证的要求全部告诉GridSearchCV,注意refit=True的设定
gs=GridSearchCV(clf,parameters,verbose=2,refit=True,cv=3)

# 执行单线程网络搜索
%time_=gs.fit(X_train,y_train)
gs.best_params_,gs.best_score_

print gs.score(X_test,y_test)
1
UsageError: Line magic function `%time_` not found.

注:

  • np.logspace(a,b,c):创建等比数列,a为起始点,b为终点,c为元素个数;np.linspace(a,b,c):创建等差数列
  • refit=True: 使程序以交叉验证训练集得到的最佳超参数重新对所有可用的训练集合开发集进行,作为最终用于性能评估的最佳模型参数

结果分析:

  • 使用单线程网格搜索技术对朴素贝叶斯模型在文本分类任务中的超参数组合进行调优,共有12组超参数X3折交叉验证=36项独立运行的计算任务。寻找到的最佳超参数组合在测试集上所能达成的最高分类准确性为82.27%。
  • 缺点:耗时
  • 优点:一旦获取到好超参数组合,则可以保持一段时间使用,是一劳永逸提高模型性能的方法

由于各新模型在执行交叉验证过程中相互独立,故我们可充分利用多核处理器甚至是分布式计算资源来从事并行搜索,则可成倍节省运算时间。

使用多线程对文本分类的Naive Bayes模型的超参数组合执行并行化的网络搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from sklearn.datasets import fetch_20newsgroups
import numpy as np
news=fetch_20newsgroups(subset='all')

from sklearn.cross_validation import train_test_split
# 选取前3000条新闻文本进行分割
X_train,X_test,y_train,y_test=train_test_split(news.data[:3000],news.target[:3000],test_size=0.25,random_state=33)

from sklearn.svm import SVC
from sklearn.feature_extraction.text import TfidfVectorizer

# 导入pipeline,使用pipeline简化系统搭建流程(简化代码),将文本抽取与分类器模型串联起来
from sklearn.pipeline import Pipeline
clf=Pipeline([('vect',TfidfVectorizer(stop_words='english',analyzer='word')),('svc',SVC())])

# 这里需要试验的2个超参数的个数分别是4、3,svc_gamma的参数共有10^-2,10^-1...,则共有12种超参数组合,12种不同参数下的模型
parameters={'svc_gamma':np.logspace(-2,1,4),'svc_C':np.logspace(-1,1,3)}

# 导入网络搜索模块GridSearchCV
from sklearn.grid_search import GridSearchCV

# 将12组参数组合、初始化的Pipeline和3折交叉验证的要求全部告诉GridSearchCV,注意refit=True的设定
# 初始化配置并行网络搜索,n_jobs=-1代表使用该计算机的全部CPU
gs=GridSearchCV(clf,parameters,verbose=2,refit=True,cv=3,n_jobs=-1)

# 执行多线程并行网络搜索
%time_=gs.fit(X_train,y_train)
gs.best_params_,gs.best_score_

# 输出最佳模型在测试集上的准确性
print gs.score(X_test,y_test)
1
UsageError: Line magic function `%time_` not found.

结果分析:

  • 相较于单线程,多线程的计算时间仅为51.8秒,且准确性仍为82.27%
  • 并行搜索:有效利用多核处理器的计算资源,几乎成倍提升运算速度,节省最佳超参数组合的搜索时间

3.2 流行库/模型实践

3.2.1 NLTK自然语言处理包

计算语言学

  • 借助计算机强大的运算能力和海量的互联网文本,来提高自然语言处理能力
  • 如何让计算机处理、生成、理解人类的自然语言

NLTK(Natural Language Toolkit)

  • 高效的语言学家,快速完成对自然语言文本的深层处理和分析

1.使用词袋法(bag of words)对文本进行特征向量化

1
2
3
4
5
6
7
8
9
10
11
12
13
sent1='The dog is walking on the street.'
sent2='A cat was running across the room.'

from sklearn.feature_extraction.text import CountVectorizer
cvr=CountVectorizer()
sent=[sent1,sent2]
print cvr.fit_transform(sent).toarray()
print cvr.get_feature_names()

from sklearn.feature_extraction.text import TfidfVectorizer
tfidf=TfidfVectorizer()
print tfidf.fit_transform(sent).toarray()
print tfidf.get_feature_names()
1
2
3
4
5
6
7
8
[[0 0 1 1 1 0 0 1 2 1 0]
[1 1 0 0 0 1 1 0 1 0 1]]
[u'across', u'cat', u'dog', u'is', u'on', u'room', u'running', u'street', u'the', u'walking', u'was']
[[0. 0. 0.37729199 0.37729199 0.37729199 0.
0. 0.37729199 0.53689271 0.37729199 0. ]
[0.4261596 0.4261596 0. 0. 0. 0.4261596
0.4261596 0. 0.30321606 0. 0.4261596 ]]
[u'across', u'cat', u'dog', u'is', u'on', u'room', u'running', u'street', u'the', u'walking', u'was']

2.使用NLTK对文本进行语言学分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import nltk

# 对句子进行分词和正规化
tokens_1=nltk.word_tokenize(sent1)
print tokens_1

# 初始化词性标注器,对每个词汇进行标注
pos_tag_1=nltk.tag.pos_tag(tokens_1)
print pos_tag_1

# 初始化stemmer,寻找各个词汇最原始的词根
stemmer=nltk.stem.PorterStemmer()
stem_1=[stemmer.stem(t) for t in tokens_1]
print stem_1

3.2.2 词向量技术(Word2Vec)

词袋法可对文本向量化,但无法计算两段文本间的相似性。如sent1与sent2在词袋法看来唯一相同的词汇是the,找不到任何语义层面的联系。

Word2Vec模型

  • 用来产生词向量的相关模型,为浅层神经网络,是监督学习系统
  • 网络以词表现,并需猜测相邻位置的输入词;训练完成后,Word2vec模型可用来映射每个词到一个向量,表示词对词之间的关系(该向量为神经网络之隐藏层)
  • 词汇间的联系通过上下文(context)建立
  • 如”A cat was running across the room.”,若需要上下文数量为4的连续词汇片段,则有A cat was running、cat was running across、was running across the、running across the room. 每个连续词汇片段的最后一个单词有可能是什么都是受到前面3个词汇的制约,故形成由前3个词汇预测最后一个单词的监督学习系统
  • 当上下文数量为n时,提供给网络的输入(Input)都是前n-1个连续词汇$w{t-m+1},…,w{t-1}$,指向的目标输出(Output)就是最后一个单词$w{t}$;而在网络中用于计算的都是这些词汇的向量表示,如$C(w{t-1})$,每一个实心红色圆都代表词向量中的元素;每个词汇红色实心圆的个数代表词向量的维度(dimension),且所有词向量的维度一致。神经网络的训练也是一个通过不断迭代、更新参数循环往复的过程,从网络中最终获得的即每个词汇独特的向量表示

word2vec

实践:用20类新闻文本进行词向量训练

3.2.3 XGBoost模型(extreme gradient boosting)

XGBoost模型

  • 提升(Boosting)分类器隶属于集成学习模型,基本思想:把成千上万个分类准确率较低的树模型组合起来,成为一个准确率很高的模型
  • 特点:不断迭代,每次迭代都生成一棵新的树;能自动利用CPU的多线程进行并行计算
  • 如何生成合理的树?如梯度提升树

实践:对比随机决策森林和XGBoost模型对titanic上的乘客是否生还的预测能力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# coding:utf-8
import pandas as pd
titanic=pd.read_csv('/Users/scarlett/repository/projects/titanic/titanic.csv')
print (titanic.info())

# 选取训练特征
X=titanic[['pclass','age','sex']]
y=titanic['survived']

# 补全缺失值
X['age'].fillna(X['age'].mean(),inplace=True)

# 数据分割
from sklearn.cross_validation import train_test_split
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.25,random_state=33)

# 导入文本特征向量化的模块DictVectorizer
from sklearn.feature_extraction import DictVectorizer
vec=DictVectorizer(sparse=False)
# 对原数据进行特征向量化处理
X_train=vec.fit_transform(X_train.to_dict(orient='record'))
X_test=vec.transform(X_test.to_dict(orient='record'))

# 采用默认配置下的随机森林分类器对测试集进行预测
from sklearn.ensemble import RandomForestClassifier
rfc=RandomForestClassifier()
rfc.fit(X_train,y_train)
print ('rfc accuracy:',rfc.score(X_test,y_test))

# 采用默认配置的xgboost模型对相同测试集进行预测
from xgboost import XGBClassifier
xgbc=XGBClassifier()
xgbc.fit(X_train,y_train)
print 'xgbc accuracy:',xgbc.score(X_test,y_test)

Output:

1
2
rfc accuracy:0.775075987842
xgbc accuracy:0.78234042553

由输出可知:XGBoost分类模型的确可发挥出更好的预测能力.

3.2.4 TensorFlow框架

TensorFlow

  • 一个完整的编码框架
  • 使用图(Graph)来表示计算任务,使用会话(Session)来执行图

1.使用TensorFlow输出一句话

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 使用TensorFlow输出一句话

import tensorflow as tf
import numpy as np

# 初始化一个TensorFlow常量,使greeting作为一个计算模块
greeting=tf.constant("Hello Google!")
# 启动一个会话
sess=tf.Session()
# 使用会话执行greeting计算模块
result=sess.run(greeting)
# 输出会话执行结果
print result
# 关闭会话
sess.close()
1
Hello Google!

2.使用TensorFlow完成一次线性函数的运算

1
2
3
4
5
6
7
8
9
10
11
12
13
# 使用TensorFlow完成一次线性函数的运算

# 声明matrix1为一个1*2的行向量,matrix2为一个2*1的列向量
matrix1=tf.constant([[3.,3.]])
matrix2=tf.constant([[2.],[2.]])
# Product将上面两个算子相乘,作为新算例
product=tf.matmul(matrix1, matrix2)
# 继续讲Product与一个标量2.0求和拼接,作为最终linear算例
linear=tf.add(product, tf.constant(2.0))
# 直接在会话中执行linear算例,相当于将上面所有单独的算例拼接成流程图来执行
with tf.Session() as sess:
result=sess.run(linear)
print result
1
[[14.]]

Skflow

  • sklearn X tensorflow

人工神经网络(Artificial Neural Network)

  • 神经信息传递的大致工作原理:神经元的树突(Dendrite)接收其他神经元传递过来的信息,然后神经细胞体对信息进行加工后,会通过轴突(Axon)把加工后的信息传递到轴突终端(Axon Terminal)然后再传递给其他神经元的树突。如此,则大量神经元就连接成了一个结构复杂的神经网络。

感知机

  • 模拟神经元
  • n维输入信号(Input) $x=$,对应的参数向量$w=$,和截距b,输出信号y(Output)等
  • 感知机在具体运算上采用线性加权求和的方式处理输入信号,即

为模拟神经元行为,定义如下激活(符号)函数,由此可知感知机最终会产生两种离散数值的输出(output)信号:

  • 感知机模型的最大贡献在于:设计了一套算法使模型可通过不断根据训练数据更新参数,达到具备线性二分类模型的学习能力

多层感知机(Multi-layer Perceptrons人工神经网络)

实践:使用skflow内置的LinearRegressor、DNN和Sciki-learn中的集成回归模型对“美国波士顿房价”数据进行回归预测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from sklearn import datasets,metrics,preprocessing,cross_validation
boston=datasets.load_boston()
X,y=boston.data,boston.target

X_train,X_test,y_train,y_test=cross_validation.train_test_split(X,y,test_size=0.25,random_state=33)
scaler=preprocessing.StandardScaler()
X_train=scaler.fit_transform(X_train)
X_test=scaler.transform(X_test)

import skflow
tf_lr=skflow.TensorFlowLinearRegressor(steps=10000,learning_rate=0.01,batch_size=50)
tf_lr.fit(X_train, y_train)
tf_lr_y_predict=tf_lr.predict(X_test)

# 输出skflow中linearregressor模型的回归性能
print 'MAE:',metrics.mean_absolute_error(tf_lr_y_predict, y_test)
print 'MSE:',metrics.mean_squared_error(tf_lr_y_predict, y_test)
print 'R-squared:',metrics.r2_score(tf_lr_y_predict, y_test)

# 使用skflow的DNNRegressor,并注意其每个隐层特征数量的配置
tf_dnn_regressor=skflow.TensorFlowDNNRegressor(hidden_units=[100,40],
steps=10000,learning_rate=0.01,batch_size=50)
tf_dnn_regressor.fit(X_train,y_train)
tf_dnn_regressor_y_predict=tf_dnn_regressor.predict(X_test)

# 输出skflow中DNNRegressor模型的回归性能
print 'MSE dnn:',metrics.mean_squared_error(tf_dnn_regressor_y_predict, y_test)
print 'MAE dnn:',metrics.mean_absolute_error(tf_dnn_regressor_y_predict, y_test)
print 'R-squared dnn:',metrics.r2_score(tf_dnn_regressor_y_predict, y_test)

# 使用sklearn中的RandomForestRegressor
from sklearn.ensemble import RandomForestRegressor
rfr=RandomForestRegressor()
rfr.fit(X_train,y_train)
rfr_y_predict=rfr.predict(X_test)
print 'MSE rfr:',metrics.mean_squared_error(rfr_y_predict, y_test)
print 'MAE rfr:',metrics.mean_absolute_error(rfr_y_predict, y_test)
print 'R-squared rfr:',metrics.r2_score(rfr_y_predict, y_test)
1
2
3
4
5
6
7
8
9
10
11
MAE:3.5107
MSE:25.1175
R_squared:0.62000

MSE dnn:14.2555
MAE dnn:2.5278
R_squared dnn:0.8035

MSE rfr:2.5077
MAE rfr:14.3742
R_squared rfr:0.79669

由输出可知:深度神经网络性能更佳。但需要注意:

  • 越是具备描述复杂数据的强力模型,越容易在训练时陷入过拟合,这一点需要在配置DNN的层数和每层特征元的数量时特别注意。

4.实战篇

本章目的:如何使用学习过的模型和编程技巧挑战业界IT公司、科研院所在Kaggle上发布的机器学习任务。

4.1 Kaggle

Kaggle是目前世界上最流行的,采用众包策略(Crowdsouring),为科技公司、研究院所乃至高校课程提供数据分析和预测模型的竞赛平台。

平台宗旨: 汇聚全世界从事数据分析和预测的专家和兴趣爱好者的集体智慧,利用公开数据竞赛的方式,为科技公司、研究院所和高校课程中的研发课题,提供有效的解决方案。使问题提出者与解决者获得双赢。

  • 问题提出者:支付少量奖金即可获得全世界聪明人的解决方案
  • 解决者:获得大量可供分析的真实业务数据

参与流程

  • 下载数据(download)
  • 搭建模型(build)
  • 提交结果(submit)

4.2 Titanic遇难乘客预测

Description

  • In this challenge, we ask you to complete the analysis of what sorts of people were likely to survive.
  • In particular, we ask you to apply the tools of machine learning to predict which passengers survived the tragedy.

Practice Skills

  • Binary classification
  • Python and R basics

Goal

It is your job to predict if a passenger survived the sinking of the Titanic or not.
For each PassengerId in the test set, you must predict a 0 or 1 value for the Survived variable.

Metric

Accuracy:the percentage of passengers you correctly predict

Submission File Format

You should submit a csv file with exactly 418 entries plus a header row. Your submission will show an error if you have extra columns (beyond PassengerId and Survived) or rows.

The file should have exactly 2 columns:

  • PassengerId (sorted in any order)
  • Survived (contains your binary predictions: 1 for survived, 0 for deceased)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# coding:utf-8
import pandas as pd

train=pd.read_csv('train.csv')
test=pd.read_csv('test.csv')
print train.shape
print train.info()
print test.info()

# 人工选取用于预测的有效特征
selected_features=['Pclass','Sex','Age','Embarked','SibSp','Parch','Fare']
X_train=train[selected_features]
X_test=test[selected_features]
y_train=train['Survived']

# 需要补全Embarked特征的缺失值
print X_train['Embarked'].value_counts()
print X_test['Embarked'].value_counts()
# 对于embarked这种类别型特征,可使用出现频率最高的特征值来补充,以减少误差
X_test['Embarked'].fillna('S',inplace=True)
X_train['Embarked'].fillna('S',inplace=True)
# 对于Age这种数值型特征,可使用平均值或中位数来填充缺失值
X_train['Age'].fillna(X_train['Age'].mean(),inplace=True)
X_test['Age'].fillna(X_test['Age'].mean(),inplace=True)
X_test['Fare'].fillna(X_test['Fare'].mean(),inplace=True)
# 检查是否已补全
print X_train.info()
print X_test.info()

# 采用DictVectorizer进行特征向量化
from sklearn.feature_extraction import DictVectorizer
dict_vec=DictVectorizer(sparse=False)
X_train=dict_vec.fit_transform(X_train.to_dict(orient='record'))
dict_vec.feature_names_
X_test=dict_vec.transform(X_test.to_dict(orient='record'))

# 训练模型
from sklearn.ensemble import RandomForestClassifier
rfc=RandomForestClassifier()

from xgboost import XGBClassifier
xgbc=XGBClassifier()

from sklearn.cross_validation import cross_val_score
# 使用5折交叉验证法在训练集上分别对默认配置的RandomForestClassifier和XGBClassifier进行性能评估,并获得平均分类准确性的得分
cross_val_score(rfc, X_train,y_train,cv=5).mean()
cross_val_score(xgbc,X_train,y_train,cv=5).mean()

# 使用模型进行预测
rfc.fit(X_train,y_train)
rfc_y_predict=rfc.predict(X_test)
rfc_submission=pd.DataFrame({'PassengerId': test['PassengerId'],'Survived': rfc_y_predict})
rfc_submission.to_csv('/Users/scarlett/repository/projects/kaggle-titanic/rfc_submission.csv',index=False)

xgbc.fit(X_train, y_train)
xgbc_y_predict=xgbc.predict(X_test)
xgbc_submission=pd.DataFrame({'PassengerId': test['PassengerId'],'Survived': rfc_y_predict})
xgbc_submission.to_csv('/Users/scarlett/repository/projects/kaggle-titanic/xgbc_submission.csv',index=False)

# 使用并行网络搜索的方式寻找更好的参数组合,以进一步提高XGBC的预测性能
from sklearn.grid_search import GridSearchCV
params={'max_depth':range(2,7),'n_estimators':range(100,1100,200),'learning_rate':[0.05,0.1,0.25,0.5,1.0]}
xgbc_best=XGBClassifier()
gs=GridSearchCV(xgbc_best, params,n_jobs=-1,cv=5,verbose=1)
gs.fit(X_train,y_train)

# 查验优化之后的XGBC的超参数配置和交叉验证的准确性
print gs.best_score_
print gs.best_params_

# 使用经优化超参数配置的XGBC对测试数据的预测结果存储在文件中
xgbc_best_y_predict=gs.predict(X_test)
xgbc_best_submission=pd.DataFrame({'PassengerId': test['PassengerId'],'Survived':xgbc_best_y_predict})
xgbc_best_submission.to_csv('/Users/scarlett/repository/projects/kaggle-titanic/xgbc_best_submission.csv',index=False)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
(891, 12)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId 891 non-null int64
Survived 891 non-null int64
Pclass 891 non-null int64
Name 891 non-null object
Sex 891 non-null object
Age 714 non-null float64
SibSp 891 non-null int64
Parch 891 non-null int64
Ticket 891 non-null object
Fare 891 non-null float64
Cabin 204 non-null object
Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB
None
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):
PassengerId 418 non-null int64
Pclass 418 non-null int64
Name 418 non-null object
Sex 418 non-null object
Age 332 non-null float64
SibSp 418 non-null int64
Parch 418 non-null int64
Ticket 418 non-null object
Fare 417 non-null float64
Cabin 91 non-null object
Embarked 418 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 36.0+ KB
None
S 644
C 168
Q 77
Name: Embarked, dtype: int64
S 270
C 102
Q 46
Name: Embarked, dtype: int64
/Users/scarlett/anaconda3/envs/python27/lib/python2.7/site-packages/pandas/core/generic.py:4355: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
self._update_inplace(new_data)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 7 columns):
Pclass 891 non-null int64
Sex 891 non-null object
Age 891 non-null float64
Embarked 891 non-null object
SibSp 891 non-null int64
Parch 891 non-null int64
Fare 891 non-null float64
dtypes: float64(2), int64(3), object(2)
memory usage: 48.8+ KB
None
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 7 columns):
Pclass 418 non-null int64
Sex 418 non-null object
Age 418 non-null float64
Embarked 418 non-null object
SibSp 418 non-null int64
Parch 418 non-null int64
Fare 418 non-null float64
dtypes: float64(2), int64(3), object(2)
memory usage: 22.9+ KB
None

kaggle scores:

1
2
xgbc: 0.75598
rfc:0.77990

4.3 IMDB影评得分估计

要求分析IMDB网站上的留言,判断每条留言的情感倾向。

数据描述

  • labeledTrainData.tsv 已标注情感倾向的训练文件
  • testData.tsv 待测试文件
  • unlabeledTrainData 无标注但数据量更大的影评文件
  • sampleSubmission.csv 样例文件(提交结果的格式)
  • Address:Bag of Words Meets Bags of Popcorn

模型描述

  • 采用sklearn的朴素贝叶斯和隶属于集成模型的梯度提升树分类模型,对电影评论文本进行情感分析
  • 先利用无标注影评文件训练词向量,然后将每条电影评论中所有词汇的平均向量作为特征训练梯度提升树分类模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# coding:utf-8
import pandas as pd
train=pd.read_csv('/Users/scarlett/repository/projects/kaggle-IMDB/labeledTrainData.tsv',delimiter='\t')
test=pd.read_csv('/Users/scarlett/repository/projects/kaggle-IMDB/testData.tsv',delimiter='\t')
# print train.head()
# print test.head()

# 使用bs4来清洗原始文本
from bs4 import BeautifulSoup
# 导入正则表达式工具包
import re
# 导入停用词列表
from nltk.corpus import stopwords



# 定义review_to_text函数,完成对原始评论的三项数据预处理任务
def review_to_text(review,remove_stopwords):
# 1.去掉HTML标记
raw_text=BeautifulSoup(review,"html.parser").get_text()
# 2.去掉非字母字符
letters=re.sub('[^a-zA-Z]', ' ', raw_text)
words=letters.lower().split()
# 3.若remove_stopwords被激活,则进一步去掉评论中的停用词
if remove_stopwords:
stop_words=set(stopwords.words('english'))
words=[w for w in words if w not in stop_words]
return words
# 返回每条评论经此三项预处理的词汇列表


# 分别对原始训练和测试数据集进行上述三项预处理
X_train=[]
for review in train['review']:
X_train.append(' '.join(review_to_text(review, True)))
X_test=[]
for review in test['review']:
X_test.append(' '.join(review_to_text(review, True)))

y_train=train['sentiment']


# 导入文本特征抽取器
from sklearn.feature_extraction.text import CountVectorizer,TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
# 导入pipeline方便搭建系统流程,导入GridSearchCV用于超参数组合的网格搜索
from sklearn.pipeline import Pipeline
from sklearn.grid_search import GridSearchCV

# 使用pipeline搭建两组使用朴素贝叶斯分类器,区别在于分别使用2个不同特征抽取器进行文本特征抽取
pip_count=Pipeline([('count_vec',CountVectorizer(analyzer='word')),('mnb',MultinomialNB())])
pip_tfidf=Pipeline([('tfidf_vec',TfidfVectorizer(analyzer='word')),('mnb',TfidfVectorizer())])

# 分别配置用于模型超参数搜索的组合
params_count={'count_vec_binary':[True,False],'count_vec_ngram_range':[(1,1),(1,2)],'mnb_alpha':[0.1,1.0,10.0]}
params_tfidf={'tfidf_vec_binary':[True,False],'tfidf_vec_ngram_range':[(1,1),(1,2)],'mnb_alpha':[0.1,1.0,10.0]}

# 使用4折交叉验证法分别对使用CountVectorizer和TfidfVectorizer的朴素贝叶斯模进行并行化超参数搜索,并分别输出交叉验证中最佳的准确性得分和超参数组合
gs_count=GridSearchCV(pip_count, params_count,cv=4,verbose=1)
gs_count.fit(X_train,y_train)
print (gs_count.best_score_)
print (gs_count.best_params_)

gs_tfidf=GridSearchCV(pip_count, params_tfidf,cv=4,verbose=1)
gs_tfidf.fit(X_train,y_train)
print (gs_tfidf.best_score_)
print (gs_tfidf.best_params_)

# 以最佳超参数组合配置模型并对测试数据进行预测
tfidf_y_predict=gs_tfidf.predict(X_test)

# 使用pandas对需要提交的数据进行格式化
submission_count=pd.DataFrame({'id': test['id'],'sentiment': coount_y_predict})
submission_tfidf=pd.DataFrame({'id': test['id'],'sentiment': tfidf_y_predict})
# 结果输出到硬盘
submission_count.to_csv('/Users/scarlett/repository/projects/kaggle-IMDB/submission_count.csv',index=False)
submission_tfidf.to_csv('/Users/scarlett/repository/projects/kaggle-IMDB/submission_tfidf.csv',index=False)

# 从本地读取未标记数据
unlabeled_train=pd.read_csv('/Users/scarlett/repository/projects/kaggle-IMDB/unlabeledTrainData.tsv',delimiter='\t',quoting=3)

import nktk.data
# 准备使用nltk的tokenizer对影评中的英文句子进行分割
tokenizer=nltk.data.load('tokenizers/punkt/english.pickel')
# 定义函数review_to_sentences逐条对影评进行分句
def review_to_sentences(review,tokenizer):
raw_sentences=tokenizer.tokenize(review.strip())
sentences=[]
for raw_sentences in raw_sentences:
if len(raw_sentences) > 0:
sentences.append(review_to_text(raw_sentences, False))
return sentences

corpora=[]
# 准备用于训练词向量的数据
for review in unlabeled_train['review']:
corpora+=review_to_sentences(review.decode('utf8'), tokenizer)
# 配置训练词向量模型的超参数
num_features=300
min_word_count=20
context=10
downsampling=le-3

# 导入word2vec
from gensim.models import word2vec
# 开始词向量模型训练
model=word2vec.Word2Vec(corpora,workers=num_workers,\
size=num_features,min_count=min_word_count,\
window=context,sample=downsampling)
model.init_sims(replace=True)

model_names="/Users/scarlett/repository/projects/kaggle-IMDB/300features_20minwords_10context"
# 保存词向量模型的训练结果
model.save(model_names)

# 直接读入已训练好的词向量模型
from gensim.models import Word2Vec
model=Word2Vec.load("/Users/scarlett/repository/projects/kaggle-IMDB/300features_20minwords_10context")

# 探查下该词向量模型的训练结果
model.most_similar("man")


import numpy as np
# 定义一个函数使用词向量产生文本特征向量
def makeFeatureVec(words,model,num_features):
featureVec=np.zeros((num_features,), dtype="float32")
nwords=0.
index2word_set=set(model.index2word)
for word in words:
if word in index2word_set:
nwords=nwords+1
featureVec=np.add(featureVec,model[word])
featureVec=np.divide(featureVec, nwords)
return featureVec


# 定义每一条影评转化为基于词向量的特征向量(平均词向量)
def getAvgFeatureVecs(reviews,model,num_features):
counts=0
reviewFeatureVecs=np.zeros(len(reviews),num_features,dtype="float32")

for review in reviews:
reviewFeatureVecs[counter]=makeFeatureVec(review, model, num_features)
counter+=1
return reviewFeatureVecs

# 准备新的基于词向量表示的训练和测试特征向量
clean_train_reviews=[]
for review in train["review"]:
clean_train_reviews.append(review_to_text(review,remove_stopwords=True))

trainDataVecs=getAvgFeatureVecs(clean_train_reviews, model, num_features)

clean_test_reviews=[]
for review in test["review"]:
clean_test_reviews.append(review_to_text(review, remove_stopwords=True))

testDataVecs=getAvgFeatureVecs(clean_test_reviews, model, num_features)

# 导入GBC模型进行情感分析
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.grid_search import GridSearchCV
gbc=GradientBoostingClassifier()
params_gbc={'n_estimators':[10,100,500],'learning_rate':[0.01,0.1,1.0],'max_depth':[2,3,4]}
gs=GridSearchCV(gbc, params_gbc,cv=4,n_jobs=-1,verbose=1)
gs.fit(trainDataVecs,y_train)

# 输出网格搜索得到的最佳性能和最优超参数组合
print (gs.best_score_)
print (gs.best_params_)

result=gs.predict(testDataVecs)
output=pd.DataFrame(data={"id":test["id"],"sentiment":result})
output.to_csv("/Users/scarlett/repository/projects/kaggle-IMDB/submission_w2v.csv",index=False,quoting=3)

4.4 MNIST手写体数字图片识别

项目介绍

  • CV选手的入门级课题
  • 关注对数字图片的识别
  • Address: Digit Recognizer

数据描述

  • train.csv + test.csv
  • 每个手写体数字图像都被首尾拼接为一个28X28=784维的像素向量,且每个像素都使用[0,1]之间的灰度值来显示手写笔画的明暗程度

MNIST手写体图片像素表示矩阵:

mnist手写体图片像素表示矩阵

搭建模型

  • 采用多种基于skflow的模型完成大规模手写体数字图片识别任务
  • 模型:线性回归器,全连接并包含3个隐藏层的深度神经网络(DNN),1个较为复杂但性能强大的卷积神经网络(CNN)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# coding:utf-8
import pandas as pd
train=pd.read_csv('train.csv')
print train.shape
# print train.head()
test=pd.read_csv('test.csv')
print test.shape

# 分离训练集中的数据特征和标记
y_train=train['label']
X_train=train.drop('label',1)
# 准备测试特征
X_test=test

# 导入TensorFlow和skflow,使用skflow中已封装好的基于tf搭建的线性分类器TensorFlowLinearClassifier进行学习预测
import skflow
import tensorflow as tf
classifier=skflow.TensorFlowLinearClassifier(n_classes=10,batch_size=100,steps=1000,learning_rate=0.01)
classifier.fit(X_train, y_train)

linear_y_predict=classifier.predict(X_test)
linear_submission=pd.DataFrame({'ImageId':range(1,28001),'label':linear_y_predict})
linear_submission.to_csv('/Users/scarlett/repository/projects/kaggle-mnist/linear_submission.csv',index=False)

# 使用tf里已封装好的TensorFlowDNNClassifier进行学习预测
classifier=skflow.TensorFlowDNNClassifier(hidden_units=[200,50,10], n_classes=10,steps=5000,learning_rate=0.01,batch_size=50)
classifier.fit(X_train, y_train)
dnn_y_predict=classifier.predict(X_test)
dnn_submission=pd.DataFrame({'ImageId':range(1,28001),'label':dnn_y_predict})
dnn_submission.to_csv('/Users/scarlett/repository/projects/kaggle-mnist/dnn_submission.csv',index=False)

# 使用tf中的算子自行搭建更为复杂的卷积神经网络,并使用skflow的程序接口从事NINST数据的学习与预测
def max_pool_2x2(tensor_in):
return tf.nn.max_pool(tensor_in,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')

def conv_model(X,y):
X=tf.reshape(X, [-1,28,28,1])
with tf.variable_scope('conv_layer1'):
h_conv1=skflow.ops.conv2d(X, n_filters=32, filter_shape=[5,5],bias=True,activation=tf.nn.relu)
h_pool11=max_pool_2x2(h_conv1)
with tf.variable_scope('conv_layer2'):
h_conv2=skflow.ops.conv2d(h_pool11, n_filters=64, filter_shape=[5,5],bias=True,activation=tf.nn.relu)
h_pool2=max_pool_2x2(h_conv2)
h_pool2_flat=tf.reshape(h_pool2, [-1,7*7*64])
h_fc1=skflow.ops.dnn(h_pool2_flat, [1024],activation=tf.dnn.relu,keep_prob=0.5)
return skflow.models.logistic_regression(h_fc1, y)

classifier=skflow.TensorFlowEstimator(model_fn=conv_model, n_classes=10,batch_size=100,steps=20000,learning_rate=0.001)
classifier.fit(X_train, y_train)

conv_y_predict=[]

import numpy as np
for i in np.arange(100,28001,100):
conv_y_predict=np.append(conv_y_predict,classifier.predict(X_test[i-100:i]))
conv_submission=pd.DataFrame({'ImageId':range(1,28001),'label':np.int32(conv_y_predict)})
conv_submission.to_csv('/Users/scarlett/repository/projects/kaggle-mnist/conv_submission',index=False)

Outcome: CNN取得最佳性能表现。

Thanks!