年度归档:2017年

第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

第3章 机器学习简单示例

为更好理解机器学习中的有关概念和流程,我们以一个实际案例来说明,这个实例需求就是根据(输入数据为房子面积,目标数据为房子价格),建立一个模型对新房价进行预测。

  • 输入数据(只有一维):房子面积
  • 目标数据(只有一维):房子价格
  • 需要做的:就是根据已知的房子面积和价格进行机器学习,建立一个模型,找到它们之间的内在关系。

下面我们就一步步地进行操作。

1、获取与预处理数据

查看源数据:

system head -5 ./data/python_and_ml/prices.txt
['2104,399900', '1600,329900', '2400,369000', '1416,232000', '3000,539900']

显示前5行数据,每行数据由‘房子面积,房子价格’组成,这些数据比较大,相差也比较大,故需要对源数据进行标准化处理,使用如下公式:

其中  表示房子面积的均值、std(X)表示X的标准差,具体实现代码如下:

%matplotlib inline
#导入需要的库
import numpy as np
import matplotlib.pyplot as plt
# 定义存储输入数据、目标数据的数组
x, y = [], []
#遍历数据集,变量sample对应每个样本
for sample in open("./data/python_and_ml/prices.txt", "r"):
x1, y1 = sample.split(",")
x.append(float(x1))
y.append(float(y1))
#把读取后的数据转换为numpy数组
x, y = np.array(x), np.array(y)
# 对x数据进行标准化处理
x = (x - x.mean()) / x.std()
# 可视化原数据
plt.figure()
plt.scatter(x, y, c="g", s=20)
plt.show()

这里横轴是标准化的房子面积,纵轴为房子价格,通过数据可视化,我们能很直观了解到原数据的分布情况,接下来我们开始训练模型。

2、 选择和训练模型

从数据可视化后的结果看来,我们可以通过线性回归(Linear Regression)中的多项式拟合来得到一个模型,该模型的数学表达式为:

构造模型的损失函数

其中f(x|p;n)就是我们的模型,p,n是模型参数,p是多项式f的各项系统,n是多项式的次数。L(p;n)是模型的损失函数,这里我们采用了常用的平方损失函数。x、y分别是输入向量和目标向量,它们都是47维的向量。

在确定模型后,接下来就开始编写代码训练模型。训练一般是最小化损失函数,这里就是使L(p;n)最小,从而求出相关参数。

当然,这里我们采用数理统计中的正规方程方法。具体实现代码如下:

#在(-2,4)这个区间上取100个点作为画图的基础
x0 = np.linspace(-2, 4, 100)

#利用numpy的函数定义训练并返回多项式回归模型函数
# deg参数表示模型参数中的n
def get_model(deg):
return lambda input_x=x0: np.polyval(np.polyfit(x, y, deg), input_x)

其中polyval和polyfit都是numpy中的函数。

polyfit(x,y,deg):该函数返回使得上述损失函数  最小的参数p;

polyval:根据多项式的各项系统p和多项式中的x,返回多项式的y值。

3、 评估与可视化结果

模型训练好以后,接下来就要判断各种参数下模型的性能了,为简单起见,我们这里采用deg=1,4,10(即多项式最大次数分别为1,4,10),采用损失函数来衡量模型的性能。

# 定义损失函数
def get_cost(deg, input_x, input_y):
return 0.5 * ((get_model(deg)(input_x) - input_y) ** 2).sum()

# 设置n的几个参数值
test_set = (1, 4, 10)
for d in test_set:
print(get_cost(d, x, y))

# Visualize results
plt.scatter(x, y, c="g", s=20)
for d in test_set:
plt.plot(x0, get_model(d)(), label="degree = {}".format(d))
plt.xlim(-2, 4)
plt.ylim(1e5, 8e5)
plt.legend()
plt.show()

损失值分别为:

96732238800.4

94112406641.7

75874846680.1

结果可视化图像:

从损失值来看,n=10时,损失值最小,当从图形来看,当n=10,显然存在过拟合。

n=4的损失值小于n=1,但从图形来看,n=4也是过拟合,所以综合来看,取n=1为一个不错选择。

通过这个简单实例,我们对机器学习的大致流程有个大致了解,对机器学习相关概念也有更具体认识,对后续更复杂的机器学习应该有了一个很好的基础。

 

 

 

 

第2章 机器学习一般流程

机器学习一般流程大致分为收集数据、探索数据、预处理数据,对数据处理后,接下来开始训练模型、评估模型,然后优化模型等步骤,具体可参考下图:

 

通过这个图形我们可以比较直观了解机器学习的一般步骤或整体框架,接下来我们就各部分分别加以说明。

1 数据探索

拿到数据以后,一般不会急于创建模型、训练模型,在这之前,需要对数据、对需求或机器学习的目标进行分析,尤其对数据进行一些必要的探索,如了解数据的大致结构、数据量、各特征的统计信息、整个数据质量情况、数据的分布情况等。为了更好体现数据分布情况,数据可视化是一个不错方法。

2 数据预处理

通过对数据探索后,可能发现不少问题:如存在缺失数据、数据不规范、数据分布不均衡、存在奇异数据、有很多非数值数据、存在很多无关或不重要的数据等等。这些问题的存在直接影响数据质量,为此,数据预处理工作应该就是接下来的重点工作,数据预处理是机器学习过程中必不可少的重要步骤,特别是在生产环境中的机器学习,数据往往是原始、为加工和处理过,数据预处理常常占据整个机器学习过程的大部分时间。

数据预处理过程中,一般包括数据清理、数据转换、规范数据、特征选择等等工作。

3 选择模型并进行训练

在模型选择时,一般不存在某种对任何情况都表现很好的算法(这种现象又称为没有免费的午餐)。因此在实际选择时,一般会选用几种不同方法来训练模型,然后比较它们的性能,从中选择最优的这个。当然,在比较不同模型之前,我们需要先确认衡量性能的指标,对分类问题常用的是准确率或ROC曲线,对回归连续性目标值问题一般采用误差来评估。

训练模型前,一般会把数据集分为训练集和测试集,或对训练集再细分为训练集和验证集,从而对模型的泛化能力进行评估。

4 模型验证和使用未知数据进行预测

使用训练数据构建模型后,通常使用测试数据对模型进行测试,测试模型对新数据的

测试。如果我们对模型的测试结果满意,就可以用此模型对以后的进行预测;如果我们测试结果不满意,我们可以优化模型,优化的方法很多,其中网格搜索参数是一种有效方法,当然我们也可以采用手工调节参数等方法。如果出现过拟合,尤其是回归类问题,我们可以考虑正则化方法来降低模型的泛化误差。

第9章Tensorflow基础

第9章Tensorflow基础

Tensorflow的系统架构和数据流程图的介绍能够方便读者先对Tensorflow运行的基础有所了解,然后引出Tensorflow程序设计中的基本概念,包括张量(Tensor),算子(Operation),变量(Variables),占位符(Place Holder),图(Graph)等,最后介绍如何在Tensorflow可视化数据流程图和Tensorflow的分布式运行。

9.1 Tensorflow系统结构

https://www.tensorflow.org/extend/architecture

TensorFlow的系统结构如下图所示,中间部分是C的API接口,上面是可编程的客户端,下面是后端执行系统。客户端系统提供多语言支持的编程模型,负责构造计算图。后端执行系统是TensorFlow的运行时系统,主要负责计算图的执行过程,包括计算图的剪枝,设备分配,子图计算等。

图9.1 Tensorflow系统结构

下面分别介绍Tensorflow系统结构的核心组件:

Client是前端系统的主要组成部分,它是一个支持多语言的编程环境。它提供基于计算图的编程模型,方便用户构造各种复杂的计算图,实现各种形式的模型设计。

Distributed Master从计算图中反向遍历,找到所依赖的最小子图,再把最小子图分割成子图片段派发给Worker Service。随后Worker Service启动子图片段的执行过程。

Worker Service 可以管理多个设备。Worker Service将按照从Distributed Master接收的子图在设备上调用的Kernel实现完成运算,并发送结果给其他Work Service,以及接受其他Worker Service的运算结果。

Kernel是Operation在不同硬件设备的运行和实现,它负责执行具体的运算。

图9.2 Tensorflow运行机制

如上图,客户端启动Session并把定义号的数据流程图传给执行层,Distributed Master进程拆解最小子图分发给Worker Service,Worker Service调用跨设备的Kernel的Operation,利用各个的资源完成运算。

9.2数据流程图

Tensorflow的特点之一就是用数据流程图定义程序,本节会介绍数据流程图的基本知识。数据流程图用来定义数据在链接在一起的函数中运算,由节点(Node),边(Edge)来确定数据运算的路径图,每个函数会把输出传递给后续函数。在数据流程图的语境中,节点通常以圆圈、椭圆和方框表示,代表了对数据所做的运算或某种算子(Operation)。边对应于向算子传入和从算子传出的实际数值,通常以箭头表示。可从概念上将边视为不同算子之间的连接,因为它们将控制信息流动走向,让信息从一个节点传输到另一个节点。

图9.3 数据流程图

首先上图Data Flow1由节点a, b, c, d, e 和相应的边组成,有两个输入和一个输出,定义了以下计算:

当时,Data Flow 1就可以完成计算 .

如果我们把 Data Flow 1整体视为一个构件,那么其他的数据流程图也可以成为它的输入或输出,在可视化的时候可以把每个数据流程图内部的运算隐藏起来,从而能更容易展示运算的结构链路。

9.3 Tensorflow基本概念

Abadi M, Agarwal A, Barham P, et al. Tensorflow: Large-scale machine learning on heterogeneous distributed systems[J]. arXiv preprint arXiv:1603.04467, 2016.

TensorFlow就是采用数据流图程(Data Flow Graphs)对张量来进行计算。让我们先Tensorflow的几个基本概念做简单介绍,包括张量(Tensor),算子(Operation),会话(Session),变量(Variables),占位符(Place Holder)和图(Graph),图的部分将在下一小节介绍。

名副其实,TensorFlow,就是张量的流动。张量(Tensor),即任意维度的数据,张量有多种. 零阶张量为纯量或标量 (scalar) 也就是一个数值,比如 [1];一阶张量为向量 (Vector), 比如 一维的 [1, 2, 3];二阶张量为矩阵 (Matrix), 比如二维的 [[1, 2, 3],[4, 5, 6],[7, 8, 9]]; 以此类推。另外张量对象用形状(Shape)属性来定义结构, Python中的元组和列表都可以定义张量的形状。张量每一维可以是固定长度,也可以用None表示为可变长度。

###指定0阶张量的形状,可以是任意整数,如1, 3, 5, 7等t_list=[]t_tuple=()###指定一个长度为2的向量,例如[2,3]t_1=[2]###指定一个2*3矩阵的形状##例如 [[1,2,3],[4,5,6]]t_2=(2, 3)###示任意长度的向量t_3=[None]###表示行数任意列数为2的矩阵的形状t_4=(None, 2)###表示第一维长度为3,第二维长度为2,第三维长度任意的3阶张量t_5=[3, 2, None]
[/cc]
Tensorflow和Numpy有很好的兼容,Tensorflow的数据类型是基于Numpy的数据类型的。Tensorflow支持的数据类型如下表:

数据类型 描述
tf.float32 32 位浮点数
tf.float64 64 位浮点数
tf.int64 64 位有符号整型
tf.int32 32 位有符号整型
tf.int16 16 位有符号整型
tf.int8 8 位有符号整型
tf.uint8 8 位无符号整型
tf.string 可变长度的字节数组.每一个张量元素都是一个字节数组
tf.bool 布尔型
tf.complex64 由两个32位浮点数组成的复数:实数和虚数
tf.qint32 用于量化Ops的32位有符号整型
tf.qint8 用于量化Ops的8位有符号整型
tf.quint8 用于量化Ops的8位无符号整型

表9.1 Tensorflow数据类型

对张量进行计算的函数叫算子,算子是不变的,但流动的张量会经函数运算而发生改变。TensorFlow的运行时包括数值计算,多维数组操作,控制流,状态管理等。

(Tensorflow支持的算子类型总结)

客户端使用会话来和执行系统交互,主要包括建立会话和执行数据流图。Tf.session()用来启动会话,Session.run()用来执行会话或初始化变量,返回的张量为Numpy数组。

###引入tensorflow和 numpyimport tensorflow as tfimport numpy as np###定义张量t_1为标量,常数 2t_1=tf.constant(2)###定义t_2 张量为2*1阶矩阵t_2=np.array([[2],[4]],dtype=np.int32)###定义 1*2 阶矩阵,数据类型为int32t_3=np.array([[6,8]],dtype=np.int32)###定义2*1阶矩阵,数据类型为int32t_4=np.array([[1],[3]],dtype=np.int32)###定义对张量 t_2 和t_3做矩阵乘法算子的运算t_5=tf.multiply(t_2,t_3)###定义对张量 t_2和t_4做矩阵加法算子的运算t_6=tf.add(t_2,t_4)###建立会话sess=tf.Session()###用Session.run()执行运算并显示结果print("t_5 :",sess.run(t_5))print ("t_6 :",sess.run(t_6))

以上实例运行结果为:

('t_5 :', array([[12, 16],       [24, 32]], dtype=int32))('t_6 :', array([[3],       [7]], dtype=int32))

Tensorflow中的变量用来保持运算中Tensor的句柄,但与Python不同的是必须要明确定义为变量,并在完成初始化后才能正常运算。Variable对象初值为全0,全1或随机数填的张量。Tensorflow提供了一些Operaiton来方便变量初值的定义,如tf.zeros()、 tf.ones()、 tf.random_normal ()和 tf.random_uniform ()tf.truncated_normal ()

###引入Tensorflowimport tensorflow as tf###定义变量counter,指定初始值为0counter = tf.Variable(0, name="counter")###定义算子add_one完成加1操作    add_one = tf.add(counter, 1)###加1的结果再赋值给变量counterupdate = tf.assign(counter, add_one)###定义会话with tf.Session() as sess:###初始化变量,此时才完成对counter赋值为0的操作sess.run(tf.global_variables_initializer())###循环执行3次counter加1并再赋值给变量counter的运算并每次都打印结果for _ in range(3):     sess.run(update)     print(sess.run(counter))###关闭会话sess.close()

以上代码的运行结果为:

123

在运行时指定张量对象的变量叫占位符(Place holder)。在调用Session.run()时,需要由feed_dict来给占位符传入Tensor对象。

###引入Tensorflow和Numpyimport tensorflow as tfimport numpy as np###定义张量x为3*1阶矩阵x = tf.placeholder(dtype=np.float32, shape=[3, 1])###定义张量y为1*3阶矩阵y = tf.placeholder(dtype=np.float32, shape=[1, 3])###定义张量z为矩阵x乘以矩阵yz = tf.matmul(x, y)###定义会话with tf.Session() as sess:###在运算时给x和y传入值,完成运算z_output = sess.run(z, feed_dict={x: [[1],[2],[3]], y:[[1,2,3]]})###打印运算结果print(z_output)###关闭会话sess.close()

以上代码的运行结果为:

[ [1.  2.  3.]  [ 2.  4.  6.]  [ 3.  6.  9.]]

9.4 TensorFlow实现数据流程图

如数据流图的部分介绍,如果把边当作张量把结点当作算子,那么数据流图就Tensorflow中把张量,算子,变量等元素定义成链接在一起的函数运算。以下代码就是用Tensorflow定义了图9.3的数据流程图,完成了对应的运算。算子的name属性可以帮助标识

###引入Tensorflowimport tensorflow as tf###定义算子a=tf.constant(2)b=tf.constant(4)c=tf.multiply(a,b)d=tf.add(a,b)e=tf.add(c,d)###定义会话sess=tf.Session()#会话调用运算output=sess.run(e)#打印运算结果print(output)###关闭会话sess.close()

以上代码运行结果为:

14

9.5可视化数据流程图

会话会默认初始化一个图对象,可以通过tf.get_default_graph()引用,用户也可以自定义新的Graph (tf.Graph())。如果给算子定义名称,并把生成的数据流程图对象写入log文件,那么最终Tensorboard Sever可以将数据流程图以可视化的形式展现出来。还是以上一小节的实例为基础,代码稍作修改就可以实现数据流程图的可视化。

###引入Tensorflowimport tensorflow as tf###定义算子及算子名称a=tf.constant(2,name="input_a")b=tf.constant(4,name="input_b")c=tf.multiply(a,b,name="mul_c")d=tf.add(a,b,name="add_d")e=tf.add(c,d,name="add_e")sess=tf.Session()output=sess.run(e)print(output)###将数据流程图写入log文件writer=tf.summary.FileWriter('home/feigu/tmp',sess.graph)writer.close()sess.close()

在执行完以上代码,还需要启动 Tensorboard Server来查看数据流图。启动命令为 Tensorflow –logdir=”folder of log”,以下是实例:

然后在浏览器中进入Torsenboard( http://localhost:6006),点击进入GRAPHS就可以看到以上程序实例的可视化的数据流程图,如下图所示:

9.4 数据流图实例

现实中的模型运算会更复杂,需要对图中的运算进行封装来获得更好的可视化, Tensorflow采用作用域(name space) 来组织运算的封装。

import tensorflow as tfgraph=tf.Graph()with graph.as_default():    in_1=tf.placeholder(tf.float32, shape=[], name="input_a")    in_2=tf.placeholder(tf.float32, shape=[], name="input_b")    const=tf.constant(3, dtype=tf.float32, name="static_value")    with tf.name_scope("Transformation"):        with tf.name_scope("A"):            A_mul=tf.multiply(in_1, const)            A_out=tf.subtract(A_mul, in_1)        with tf.name_scope("B"):            B_mul=tf.multiply(in_2, const)            B_out=tf.subtract(B_mul, in_2)        with tf.name_scope("C"):            C_div=tf.div(A_out, B_out)            C_out=tf.add(C_div, const)        with tf.name_scope("D"):            D_div=tf.div(B_out, A_out)            D_out=tf.add(D_div, const)            out=tf.maximum(C_out, D_out)writer=tf.summary.FileWriter('home/feigu/tmp', graph=graph)writer.close()

执行完以上代码,在Tensorboard Server启动的情况下,在浏览器中进入 http://localhost:6006,点击进入GRAPHS可以看到如下定义的数据流图:

图9.5 数据流图name space实例

9.6 分布式TensorFlow

### 加载图像

### 图像格式

### 把图像转换为TFRecord文件

### 读取文件

### 图像处理实例

第1章 机器学习简介

大数据、人工智能是目前大家谈论比较多的话题,它们的应用也越来越广泛、与我们的生活关系也越来越密切,影响也越来越深远,其中很多已进入寻常百姓家,如无人机、网约车、自动导航、智能家电、电商推荐、人机对话机器人等等。

大数据是人工智能的基础,而使大数据转变为知识或生产力,离不开机器学习(Machine Learning),可以说机器学习是人工智能的核心,是使机器具有类似人的智能的根本途径。

本章主要介绍机器有关概念、与大数据、人工智能间的关系、机器学习常用架构及算法等,具体如下:

  • 机器学习的定义
  • 大数据与机器学习
  • 机器学习与、人工智能及深度学习
  • 机器学习的基本任务
  • 如何选择合适算法
  • Spark在机器学习方面的优势

1.1机器学习的定义

机器学习是什么?是否有统一或标准定义?目前好像没有,即使在机器学习的专业人士,也好像没有一个被广泛认可的定义。在维基百科上对机器学习有以下几种定义:

“机器学习是一门人工智能的科学,该领域的主要研究对象是人工智能,特别是如何在经验学习中改善具体算法的性能”。

“机器学习是对能通过经验自动改进的计算机算法的研究”。

“机器学习是用数据或以往的经验,以此优化计算机程序的性能标准。”

一种经常引用的英文定义是:A computer program is said to learn from experience (E) with respect to some class of tasks( T) and performance(P) measure , if its performance at tasks in T, as measured by P, improves with experience E。

可以看出机器学习强调三个关键词:算法、经验、性能,其处理过程如下图所示。                

图1.1 机器学习处理流程

图1.1表明机器学习是使数据通过算法构建出模型,然后对模型性能进行评估,评估后的指标,如果达到要求就用这个模型测试新数据,如果达不到要求就要调整算法重新建立模型,再次进行评估,如此循环往复,最终获得满意结果。

1.2大数据与机器学习

我们已进入大数据时代,产生数据的能力空前高涨,如互联网、移动网、物联网、成千上万的传感器、穿戴设备、GPS等等,存储数据、处理数据等能力也得到了几何级数的提升,如Hadoop、Spark技术为我们存储、处理大数据提供有效方法。
数据就是信息、就是依据,其背后隐含了大量不易被我们感官识别的信息、知识、规律等等,如何揭示这些信息、规则、趋势,正成为当下给企业带来高回报的热点。其中数据是重要考量,在某个方面来说,数据比算法重要,数据犹如经验。

1.3 机器学习、人工智能及深度学习

人工智能和机器学习这两个科技术语如今已经广为流传,已成为当下的热词,
然而,他们间有何区别?又有哪些相同或相似的地方?虽然人工智能和机器学习高度相关,但却并不尽相同。
人工智能是计算机科学的一个分支,目的是开发一种拥有智能行为的机器,目前
很多大公司都在努力开发这种机器学习技术。他们都在努力让电脑学会人类的行为模式,
以便推动很多人眼中的下一场技术革命——让机器像人类一样“思考”。
过去10年,机器学习已经为我们带来了无人驾驶汽车、实用的语音识别、有效的网络搜索等等。接下来人工智能将如何改变我们的生活?在哪些领域最先发力?我们拭目以待。
对很多机器学习来说,特征提取不是一件简单的事情。在一些复杂问题上,
要想通过人工的方式设计有效的特征集合,往往要花费很多的时间和精力。

 

图1.2 机器学习与深度学习流程对比

深度学习解决的核心问题之一就是自动地将简单的特征组合成更加复杂的特征,并利用这些组合特征解决问题。深度学习是机器学习的一个分支,它除了可以学习特征和任务之间的关联以外,还能自动从简单特征中提取更加复杂的特征。图1.2 中展示了深度学习和传统机器学习在流程上的差异。如图1.2 所示,深度学习算法可以从数据中学习更加复杂的特征表达,使得最后一步权重学习变得更加简单且有效。

前面我们分别介绍了机器学习、人工智能及深度学习,它们间的关系如何?

图1.3 人工智能、机器学习与深度学习间的关系

人工智能、机器学习和深度学习是非常相关的几个领域。图1.3说明了它们之间大致关系。人工智能是一类非常广泛的问题,机器学习是解决这类问题的一个重要手段,深度学习则是机器学习的一个分支。在很多人工智能问题上,深度学习的方法突破了传统机器学习方法的瓶颈,推动了人工智能领域的快速发展。

1.4 机器学习的基本任务

机器学习基于数据,并以此获取新知识、新技能。它的任务有很多,分类是其基本任务之一,分类就是将新数据划分到合适的类别中。分类一般用于目标特征为类别型,如果目标特征为连续型,我们往往采用回归方法,回归对新进行预测,回归是机器学习中使用非常广泛的方法之一。

分类和回归,都是先根据标签值或目标值建立模型或规则,然后利用这些带有目标值的数据形成的模型或规则,对新数据进行识别或预测。这两种方法都属于监督学习。与监督学习相对是无监督学习,无监督学习不指定目标值或预先无法知道目标值,它可以将把相似或相近的数据划分到相同的组里,聚类就是解决这一类问题的方法之一。

除了监督学习、无监督学习这两种最常见的方法外,还有半监督学习、强化学习等方法,这里我们就不展开了,下图形为这些基本任务间的关系。

 

图1.4  机器学习基本任务

1.5 如何选择合适算法

在讲如何选择机器学习算法之前,我们先简单介绍一下机器常用方法,重点介绍算法核心思想、优缺点及模式图示等方面的内容,具体可参考下图:

当我们接到一个数据分析或挖掘的任务或需求时,如果希望用机器学习来处理,首要任务是根据任务或需求选择合适算法,选择哪种算法较合适?分析的一般步骤为:

图1.5 选择算法的一般步骤

充分了解数据及其特性,有助于我们更有效地选择机器学习算法。采用以上步骤在一定程度上可以缩小算法的选择范围,使我们少走些弯路,但在具体选择哪种算法方面,一般并不存在最好的算法或者可以给出最好结果的算法,在实际做项目的过程中,这个过程往往需要多次尝试,有时还要尝试不同算法。不过先用一种简单熟悉的方法,然后,在这个基础上不断优化,时常能收获意想不到的效果。

1.6机器学习常用术语

为更好说明机器学习中一些常用术语,我们以一个预测天气的示例来说明,通过具体数据来介绍一些概念,可能比单纯定义来得更具体一些。

假如我们有一组天气数据,是来自全世界不同国家和地区的每日天气,内容包括最高温度、最低温度、平均湿度、风速之类的相关数据,例如数据的一部分是这样的:

在这组数据中,我们将称A市、B市、C市、D市等以及温度、湿度等情况的总和称为数据集(data set)。表格中的每一行,也就是某城市和它的对应的情况被称为一个样本(sample/instance)。表格中的每一列,例如最高温度、最低温度,被称为特征(feature/attribute),而每一列中的具体数值,例如36℃ 、28℃,被称为属性值(attribute value)。数据中也可能会有缺失数据(missing data),例如B市的某时刻风速,我们会将它视作缺失数据。

如果我们想预测城市的天气,例如是晴朗还是阴雨天,这些数据是不够的,除了特征以外,我们还需要每个城市的具体天气情况,也就是通常语境下的结果。在机器学习中,它会被称为标签(label),用于标记数据。值得注意的是,数据集中不一定包含标签信息,而这种区别会引起方法上的差别。我们可以给上述示例加上一组标签:


在机器学习中,数据集又可以分为三类:训练集(Training Set )、测试集(Testing Set)和交叉验证集(Cross-Validation Set)。 顾名思义,训练集在机器学习的过程中,用来训练我们模型的部分;而测试集用评估、测试模型泛化能力的部分;交叉验证集它是用来调整模型具体参数的数据集。
根据数据有没有标签,我们可以把机器学习分类为监督学习(Supervised Learning)、无监督学习(Unsupervised Learning)和强化学习(Reinforcement  Learning)。

监督学习是学习给定标签的数据集,比如说有一组气候数据,给出他们的详细资料,将它们作为判断或预测天气是晴天还是阴雨等标签的依据,然后预测明天是否会下雨,就是一种典型的监督学习。监督学习中也有不同的分类,如果我们训练的结果是阴雨、晴天之类离散的类型,则称为分类(Classification),如果只有两种类型的话可以进一步称为二分类(Binary Classification);如果我们训练的结果是下雨的概率为0.87之类连续的数字,则称为回归(Regression)。

无监督学习是学习没有标签的数据集,比如在分析大量语句之后,训练出一个模型将较为接近的词分为一类,而后可以根据一个新的词在句子中的用法(和其他信息)将这个词分入某一类中。其中比较微妙的地方在于,这种问题下使用聚类(Clustering)(方法)所获得的簇(Cluster)(结果),有时候是无法人为地观察出其特征的,但是在得到聚类后,可能会对数据集有新的启发。

强化学习是构建一个系统,在与环境交互的过程中提高系统的性能。环境的当前状态信息中通常包含一个反馈信号,这个反馈值不是一个确定的类标或连续类型的值,而是通过反馈函数产生的对当前系统行为的评价。通过探索性的试错或者借助精心设计的激励系统使得正向反馈最大化。象棋或围棋对弈就是一个常见的强化学习例子。下图为强化学习系统原理示意图:

 

对模型性能的评估,或对其泛化能力的评估,往往是一项重要内容。对分类问题我们通常利用准确率、ROC曲线等指标进行评估;对回归类模型,我们通常均方误差(MSE)、均方根误差(RMSE)等指标来确定模型的精确性。

但值得注意的是,模型并不是误差越小就一定越好,因为如果仅仅基于误差,我们可能会得到一个过拟合(Overfitting)的模型;但是如果不考虑误差,我们可能会得到一个欠拟合(Underfitting)的模型,用图像来说的话大致可以这样理解:

如果模型十分简单,往往会欠拟合,对于训练数据和测试数据的误差都会很大;但如果模型太过于复杂,往往会过拟合,那么训练数据的误差可能相当小,但是测试数据的误差会增大。所以需要“驰张有度”,找到最好的那个平衡点。

如果出现过拟合或欠拟合,有哪些解决方法呢?

1、对于欠拟合,一般可考虑提高数据质量、规范特征、增加新特征或训练数据量等方法;采用交叉验证及网格搜索参数等方法调优超参数(不是通过算法本身学习出来的参数,如迭代步数、树高度、步长等);采用其它算法如集成算法等等。

2、对于过拟合问题,可以考虑引入正则化,正则化指修改算法,使其降低泛化误差(而非降低训练误差);对于维数较大的情况,采用PCA降维也是选项之一。

在模型训练过程中,泛化误差不会随着模型复杂度趋于0,相反它一般呈现U型曲线,但训练误差一般训练复杂度增强而变小,直至趋于0。具体关系我们可以参考下图:

聚类问题的标准一般基于距离:簇内距离(Intra-cluster Distance)和簇间距离(Inter-cluster Distance)。根据常识而言,簇内距离是越小越好,也就是簇内的元素越相似越好;而簇间距离越大越好,也就是说簇间(不同簇)元素越不相同越好。