[关闭]
@spiritnotes 2016-03-16T08:45:37.000000Z 字数 14494 阅读 3830

《机器学习系统设计》

读书笔记 机器学习


机器学习系统设计封面

第一章 Python机器学习入门

学习numpy

  1. import numpy as np
  2. a = np.array([1,2,3,4,5,6])
  3. a.ndim, a.shape
  4. a.reshape((3,2))
  5. a[1][0] = 77
  6. c = a.copy()
  7. #对数组的操作传递到每个元素上
  8. a*2
  9. a**2

学习SciPy

Scipy提供了基于NumPy数据结构之上的高效算法
fp1,*_ = sp.polyfit(x, y, n, full=True) 趋势线
f = sp.polyld(fp1) 创建模型函数

过拟合和欠拟合

对数据进行拟合除了正常的数据还包括噪声。

第二章 如何对真实数据分类

Iris数据集

  1. data = sklearn.datasets.load_iris()
  2. features = data['data']
  3. feature_names = data['feature_names']
  4. target = data['target']
  5. for t,marker,c in zip(range(3), '>ox', 'rgb'):
  6. plt.scatter(features[target == t,0],
  7. features[target == t,1],
  8. marker=marker,c=c)

第一个分类模型

通过花瓣长度可以将Iris Setosa与其他两种区分,其最大为1.9,而另外两种最小为3,另外两种之前的分类可以通过在特征上遍历阀值,最后选用最好特征上的最好阀值。

训练误差:训练集上面的误差;测试误差:测试集上面的误差;交叉验证:将数据分成n份,迭代使用n-1份进行训练,另外一份进行测试;
去一法: 极端交叉验证,每次只取出一个样本进行测试,对所有的样本同样进行;

常使用5折或者10折交叉验证。注意保持数据分布的均衡。去一法是对模型泛化能力的可靠估计。

更复杂的分类器

分类模型的构成:
- 模型结构
- 搜索过程
- 损失函数

谨慎选择损失函数,一种可能情况是一种错误比另一种的代价更大,例如医疗中的假阴性和假阳性。

特征和特征工程

特征工程: 实际上不是新的测量值,而是部分可测量特征所组成的函数。这些特征对系统性能可能有很大影响。

好特征的目标是在重要的地方取不同的值,而在不重要的地方不变。特征选择就是将其中的好特征选择出来。

归一化到Z值

Z-score: 表示特征值离它的平均值有多远,用标准方差的数量来计算。

  1. features -= features.mean(axis=0)
  2. features /= features.std(axis=0)

多分类系统

多分类系统往往是通过二分类来实现的。

Created with Raphaël 2.1.2Is A?Is A!Is B?Is B!Is C!yesnoyesno

第三章 聚类:寻找相关的帖子

评估帖子的关联性

预处理:用相近的公共词语个数来衡量相似性

将原始文本转换为词袋

  1. vec = sklearn.feature_selection.text.CountVectorizer(min_df=1)
  2. x = vec.fit_transform(contents)
  3. vec.get_feature_names()
  4. x.toarray()
  5. n_samples, n_features = x.shape
  6. vec_new = vec.transform(new_content)

如下参数:
min_df = 1 / 0.1
analyzer = word
token_pattern = ...

计算距离(欧式距离)

  1. sp.linalg.norm((V1-V2).toarray())

进行遍历时,可以使用 sys.maxint 来赋初始值。

词语频次向量的归一化

  1. V1_normalized = V1 / sp.linalg.norm(V1.toarray())

删除不重要的词语

  1. vectorizer = CountVectorizer(min_df=1, stop_words='english') #也可传入词表
  2. vectorizer.get_stop_words()

词干处理

语义相同而形式不同(过去式/进行时等)需要归约到特定的词干形式。

  1. nltk.stem.SnowballStemmer('english').stem('buying')

将stemmer通过钩子放入向量化中:

  1. class StemmedCountVectorizer(CountVectorizer):
  2. def build_analyzer(self):
  3. analyzer = super(StemmedCountVectorizer, self).build_analyzer()
  4. return lambda doc: (english_stemmer.stem(w) for w in analyzer(doc))

TF-IDF

TF-IDF(词频-反转文档频率),倾向于过滤常见的词语,留下重要的词语。
词频 (term frequency, TF) 指的是词语在该文件中出现的次数。为防止文件长短的影响,字通常会被归一化,通过除以文件总词数或者文件中词语出现的最大次数。
逆向文件频率 (inverse document frequency, IDF)是指示词语对该文件的识别度。可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。

  1. class StemmedTfidfVectorizer(sklearn.feature_extraction.text.TfidfVectorizer):
  2. def build_analyzer(self):
  3. analyzer = super(TfidfVectorizer, self).build_analyzer()
  4. return lambda doc: (english_stemmer.stem(w) for w in analyzer(doc))

词袋模型的缺点

聚类

聚类分为扁平聚类和层次聚类,扁平聚类往往需要预先指定簇的个数,而层次聚类则形成类似树的结构。

K均值

  1. vectorizer = StemmedTfidVectrizer(min_df=10,max_df=0.5,stop_words='english',charset_error='ignore')
  2. vectorized = vectorizer.fit_transform(dataset.data)
  3. from sklearn.cluster import KMeans
  4. km = kMeans(n_clusters=50,init='random',n_init=1,verbose=1)
  5. km.fit(vectorized)
  6. km.labels_
  7. km.cluster_centers_

分配新的帖子

  1. new_post_vec = vectorizer.transform([new_pos])
  2. new_post_label = km.predict(new_post_vec)[0]

构建帖子列表

  1. similar_indices = (km.labels_==new_post_label).nonzero()[0]
  2. similar = []
  3. for i in similar_indices:
  4. dist = sp.linalg.norm((new_pos_vec - vectorized[i]).toarray())
  5. similar.append((dist, data[i]))
  6. similar = sorted(similar)

参数调整

max_features
sklearn.metrics

第四章 主题模型

潜在狄利克雷分配(LDA)

线性判别式分析也称为LDA,在sklearn中有实现,非这里的LDA。

第5章 分类:检测劣质答案

5.1 路线图概述

从最近邻开始,揭示其效果为啥不好
切换回逻辑回归
如何选择最佳模型

5.2 学习如何区分出优秀的答案

调整样本

数据样本为文本,标签为二值数据

调整分类器

多种训练器选择

5.3 获取数据

Stackoverflow的数据

将数据消减到可处理的程度

按时间选择一部分
用cElementTree来解析XML

对属性进行预选择和处理

定义什么是优质答案

将大于0定义为正例,小于等于0的定义为负类

5.4 创建第一个分类器

从KNN开始

  1. from sklearn import neighbors
  2. knn = neighbors.KneighborsClassifier(n_neighbors=2)
  3. knn.fit(data, target)
  4. knn.predict(new_data)
  5. knn.predict_proba(new_data)

特征工程

特征:答案中的html链接数,可以发现多数答案没有链接
采用链接数使用cv进行测试,正确率很低,低于随机

  1. from sklearn.cross_validation import KFold
  2. scores = []
  3. cv = KFold(n=len(X), k=10, indices=True)
  4. for train,test in cv:
  5. x_train, y_train = X[train], y[train]
  6. x_test, y_test = X[test], y[test]
  7. clf = KneighborsClassifier()
  8. clf.fit(x_train, y_train)
  9. scores.append(clf.score(x_test, y_test))

设计更多的特征

统计答案中代码行数

句子中的平均词语个数
帖子中词语的平均字符个数
大写形式的词语个数
感叹号的个数

由于采用knn,其不同特征维度范围不一致,其导致结果变差

5.5 决定怎么样提升效果

解决高偏差

这种时候增加数据用处不大,模型过于简答化,方法是增加更多特征,让模型更复杂或其他模型
测试误差开始有下降,随着数据规模扩大,和训练误差越来越接近

解决高方差

模型对于数据过于复杂,获取更多数据,降低模型复杂度,删减特征
测试误差与训练误差之间存在着巨大差距

5.6 采用逻辑回归

  1. from sklearn.liner_model import LogisticRegression
  2. clf = LogisticRegression() #C为则化参数
  3. clf.fit(x, y)
  4. clf.intercept_, clf.coef_

在帖子分类上应用逻辑回归

从结果看仍然是高偏差

5.7 观察正确率的背后: 准确率和召回率

准确率
召回率

  1. from sklearn.metrics import precision_recall_curve
  2. precision, recall, thresholds = precision_recall_curve(y_test, clf.predict(x_test))
  3. #选取所需阀值
  4. thresholds = np.hstack(([0],thresholds[midium]))
  5. idx80 = precision>= 0.8
  6. thresh80 = threshold[idx80][0]
  7. probs_for_good = clf.predict_proba(answer_feature)[:1]
  8. answer_class = probs_for_good > thresh80
  9. from sklearn.metrics import classification_report
  10. classification_report(y_test, clf.predict_proba[:1]>0.63, target_names=['not accepted','accepted'])

5.8 为分类器瘦身

一个特征的系数越高其决定好坏的作用就越大,负值表示将其分成负类的作用。
可视化

第6章 分类II:情感分析

观点挖掘

6.1 路线图概述

介绍分类算法:朴素贝叶斯
阐释词性标注(Part of speech,POS)是如何工作以及如何帮助我们
展示sklearn工具箱中的一些小技巧

6.2 获取推特(Twitter)数据

6.3 朴素贝叶斯介绍

贝叶斯


朴素,是指特征之间独立

考虑未出现的词语和其他古怪情况

Lidstone平滑:


Laplace平滑: +1平滑

考虑算术下溢

使用log

6.4 创建第一个分类器并调优

sklearn.naive_bayes

先解决第一个问题

很多推文是中性的,将中性推文过滤掉,将Y赋值为0,1,
构建分类器

  1. from sklearn.feature_extraction.text import TfidfVectorizer
  2. from sklaern.naive_bayes import MultinomiaNB
  3. from sklearn.pipeline import Pipeline
  4. tfidf_ngrams = TfidfVectorizer(ngram_range=(1,3),analyer='word',binary=False)
  5. clf = MultinomiaNB()
  6. pipeline = Pipeline([('vect',tfidf_ngrams),('clf',clf)])

训练

  1. from sklearn.metrics import precision_recall_curve,auc
  2. from sklearn.cross_validation import ShuffleSplit
  3. cv = ShuffleSplit(n=len(X),n_iter=10,test_size=0.3,indices=True,random_state=0)
  4. for train,test in cv:
  5. clf = pipeline()
  6. clf.fit(x_train, y_train)
  7. train_score = clf.score(x_train,y_train)
  8. test_score = clf.score(x_test,y_test)
  9. scores.append(test_score)
  10. proba = clf.predict_proba(x_test)
  11. precisionrecall,pr_thresholds=precision_recall_curve(y_test,proba[:,1])
  12. pr_scores.append(auc(recall,precision))
  13. np.mean(scores),np.std(scores),np.mean(pr_scores),np.std(pr_scores)

AUC=0.88

使用所有的类

对分类器的参数进行调优

使用GridSearchCV():
Param_grid={"vect__ngram_range"={(1,1),(1,2)...}

  1. from sklearn.grid_search import GridSearchCV
  2. from sklearn.metrics import f1_score
  3. cv = ShuffleSplit(n=len(X),n_iter=10,test_size=0.3,indices=True,random_state=0)
  4. param_grid=dict(vect__ngram_range=[(1,1),...]...)
  5. grid_search = GridSearchCV(pipeline(),param_grid=param_grid,cv=cv,score_fun=f1_score,verbose=10)
  6. grid_search.fit(X,Y)
  7. grid_serach.best_estimator_

6.5 清洗推文

为tfidfVectorizer提供自己定制的preprocessor()
将表情符号以及替代词语转换为正面或者负面语言
用正则表达式及其扩展来定义那些缩写形式:
r'\br\b','are'
TfidfVectorizer(preprocessor=preprocessor,...)

6.6 将词语类型考虑进去

确定词语的类型

nltk.pos_tag()

用SentiWordNet成功作弊

http://sentiwordnet.isti.cnr.it
提高的文件中包含了POS分数和NEG分数,对于有几个意思的词语取平均dict_[(type_, word_)]

第一个估算器

实现方式是继承自BaseEstimator

  1. class LinguisticVectorizer(BaseEstimator):
  2. pass

针对每个doc分别将其拆分后计算出如下值,各个词之间可以求均值
中性分值,正分值,负分值,名词个数,副词个数,动词个数,形容词个数,allcaps,!个数,?个数,#个数,@个数

把所有东西融合在一起

  1. tfidf_ngrams = TfidfVectorizer(preprocessor=preprocessor,analyzer="word")
  2. ling_stats = LinguisticVectorizer()
  3. all_features = FeatureUnion([('ling',ling_states),('tffidf',tfidf_ngrams)])
  4. clf = MultinomialNB()
  5. pipeline = Pipeline([('all',all_features),('clf',clf)]

第7章 回归:推荐

普通最小二乘法(OLS,Ordinary Least Squares)
lasso法
岭(ridge)回归
弹性网络(elastic net)

7.1 用回归预测房价

w, total_error, _, _ = np.linalg.lstsq(x,y)

均方根误差,与标准差

多维回归

  1. x = np.array([np.concatenate([v,[1]],axis=0) for v in boston.data])
  2. w, error, _, _ = np.linalg.lstsq(x, boston.target)

回归里的交叉验证

  1. from sklearn.linear_model import LinearRegression
  2. lr = LinearRegression(fit_intercept=True)
  3. lr.fit(boston.data, boston.target)
  4. p = map(lr.predict, x)
  5. e = p-y
  6. total_error = np.sum(e*e)
  7. rmse_train = np.sqrt(total_error/len(p))
  1. from sklearn.cross_validation import Kfold
  2. kf = KFold(len(x), n_folds=10)
  3. err = 0
  4. for train, test in kf:
  5. lr.fit(x[train],y[test])
  6. p = map(lr.predict, x[test])
  7. e = p - y[test]
  8. err += np.sum(e*e)
  9. rmse_10cv = np.sqrt(err/len(x))

7.2 惩罚式回归

惩罚式回归是偏差-方法折中的另一个例子,在使用惩罚项的时候,由于增加了偏差,我们会得到一个训练效果差一些的拟合。但另外一个方面,我们降低了方差,从而更易于避免过拟合。整体泛化效果更好。

L1惩罚和L2惩罚

L1惩罚
通过系数的绝对值之和对回归进行惩罚
L2惩罚
通过系数的平房和进行惩罚

L1惩罚模型通常叫做Lasso法,L2惩罚模型叫做岭回归(Ridge regression)。两者结合形成弹性网(Elastic net)模型。

Lasso法会使更多的系数为0,可以通过该方法进行特征选择。

在sklearn中使用lasso或弹性网

  1. from sklearn.linear_model import ElasticNet
  2. en = ElasticNet(fit_intercept=True, alpha=0.5)

7.3 当P大于N的情形

即特征数多于样本数目

基于文本的例子

采用10-k reports数据,由于特征比样本多。

巧妙地设置超参数(hyperparameter)

两层CV训练
例如:数据划分为10折,1用于测试,其余9个用于训练。由于训练时需要选择参数,则可以将训练集再拆分成10折,针对每个参数的取值进行CV测试。
外层CV用于确定模型泛化能力,内层CV用于内层训练时选择最佳参数。

sklearn中含有LassoCV,RidgeCV,ElasticNetCV来进行内部参数封装交叉验证检查。

  1. from sklearn.linear_model import ElasticNetCV
  2. met = ElasticNetCV(fit_intercept=True)
  3. kf = KFold(len(target),n_folds=10)
  4. for train,test in kf:
  5. met.fit(data[train],target[train])
  6. p = map(met.predict, data[test])
  7. p = np.array(p).ravel()
  8. e = p - target[test]
  9. err += np.dot(e,e)
  10. rmse_10cv = np.sqrt(err/len(target))

评分预测和推荐

设置为回归问题更好

构建电影特定模型

构建用户特定模型

  1. reg = LassoCV(fit_intercept=True,alphas=[.125,.25,.5,1,2,4,8])
  2. u = reviews[i]
  3. u = u.array().ravel() #转为一维数组
  4. ps, = np.where(u > 0)
  5. us = np.delete(np.arange(reivews.shape[0]),i) #其他用户除了i
  6. x = reviews[us][:,ps].T
  7. y = u[ps] #用户打分过电影
  8. kf = KFold(len(y), n_folds=4)
  9. for train,test in kf:
  10. xc,x1 = movie_norm(x[train])
  11. reg.fit(xc,y[train]-x1)
  12. xc,x1 = movie_norm(x[test])
  13. p = np.array(map(reg.predict,xc)).ravel()
  14. e = (p+x1)-y[test]
  15. err += np.sum(e*e)
  16. def movie_norm(x):
  17. xc = x.copy().toarray()
  18. x1 = np.array([xi[xi>0]].mean() for xi in xc)
  19. x1 = np.nan_to_num(x1) #只有本用户看的电影,其他会为NAN,赋值0
  20. for i in range(xc.shape[0]):
  21. xc[i] -= (xc[i]>0)*x1[i] #减去均值进行归一化
  22. return xc,x1

第8章 回归:改进的推荐

8.1 改进的推荐

使用二值推荐矩阵

将之前的打分矩阵,改变为二值矩阵,看过的就改为1,没有看过改为0
可视化

  1. from matplotlib import pyplot as plt
  2. imamedata = reviews[:200,:200].todense()
  3. plt.imshow(imagedata, interpolation='nearest')

计算用户之间的相似性

  1. np.corroef(a,b)[0,1]

估算分数

  1. bu = user > 0
  2. br = rest > 0
  3. ws = all_correlations(bu,br)
  4. selected = ws.argsort()[-100:]
  5. estimates = rest[selected].mean(0)
  6. estimates /= (.1+br[selected].mean(0))

审视电影的近邻

  1. for i in range(nmovies):
  2. movie_likeness[i] = all_correlations(review[:,i],reviews.T)
  3. movie_likeness[i,i] = -1
  1. likes = movie_likeness[movie_id].argsort()
  2. likes = [::-1]
  3. for movie in likes:
  4. if reviews[u_id, movie] > 0:
  5. return reviews[u_id, movie]

组合多种方法

集成学习

8.2 购物篮分析

寻找真正的是:相比基准,在统计上更可能购买Y的购买了X的客户
而不是推荐热销产品,个性化推荐上没有更好

分析超市购物篮

超市购物篮数据
可视化

Apriori算法
从底向上进行,从最小候选集合开始(一个元素),每次加入一个元素
定义最小支持度 minsupport = 80,就是商品被一起购买的次数

构建关联规则

提升度:规则和基线所得到的概率之间的比值

使用提升度避免推荐热销商品,实践中往往认为提升度至少为10或者100.

pymining程序

第9章 分类III:音乐体裁分类

9.1 路线图概述

对MP3分类:古典、乡村、流行。。。

9.2 获取音乐数据

数据集: GTZAN
把所有音乐转换为WAV格式,scipy可以直接读取

9.3 观测音乐

  1. import scipy as sp
  2. from matplotlib.pyplot import specgram
  3. sample_rate, X = scipy.io.wavfile.read(wave_filename)
  4. specgram(X, Fs=sample_rate, xextent=(0,30))

将音乐分解成正弦波形成分

从原始样本中(储存在X中)提取频率强度,并传进分类器。频率强度可以通过快速傅里叶变换得到(Fast Fourier Transform,FFT)

  1. sox --null -r 22050 sina_a.wav synth 0.2 sine 400

9.4 用FFT构建第一个分类器

  1. fft_features = abs(scipy.fft(X)[:1000])
  2. np.save(data_fn, fft_features)

在多分类问题中使用混淆矩阵评估正确率

  1. from sklearn.metrics import confusion_matrix
  2. cm = confusion_matrix(y_test, y_pred)

第10章 计算机视觉:模式识别

10.1 图像处理简介

通常说的图像处理是对图像进行处理,得到一张新的或更好的图片(可能含有较少的噪声或者是另外一个样子)。

图像预处理
分类
图像的局部特征

10.2 读取和显示图像

  1. import mahotas as mh
  2. image = mh.imread(file_name)

阀值

  1. binarized = (image > threshold_value)
  2. image = mh.colors.rgb2gray(image, dtype=np.uint8)
  3. thresh = mh.thresholding.otsu(image)
  4. thresh2 = mh.thresholding.rc(image)

高斯模糊

  1. image = mh.colors.rgb2gray(image)
  2. im8 = mh.gaussian_filter(iamge, 8) #滤波器的标准差,越大越模糊

增加椒盐噪声

  1. salt = np.random.random(lenna.shape) > 0.975 #白
  2. pepper = np.random.random(lenna.shape) > 0.975 #黑
  3. lenna = mh.stretch(lenna)
  4. lenna = np.maximum(salt*170, sep)
  5. lenna = np.minimum(pepper*30+lenna*(~pepper),lenna)

计算特征

  1. mh.features.haralick(image)

10.3 在更难的数据集上分类

特征

10.4 局部特征表示

加速稳健特征(speeded up robust feature)
尺度不变特征变换(scala-invariant feature transform, SIFT)

第11章 降维

降维的原因:

11.1 路线图

大致分为特征选择法和特征抽取法。使用PCA(principal component analysis,主成分分析)/LDA(linear discriminant analysis,线性判别式法)/MDS(multidimensional scaling,多维标度法)

11.2 特征选择

如果数据特征之间没有依赖关系,同时又和预测值相关,删除任何一个特征都会导致性能的下降。
两种通用做法:筛选器(filter)和封装器(wrapper)。

筛选法

基于统计的方法找出冗余或者无关特征

相关性
scipy.stat.pearsonr(A, B) 计算皮尔逊相关系数,返回相关系数值和p值,p值指示两组数据由不相干系统产生的概率。
缺点:只能检测出线性关系
互信息
考虑在已经给定一个特征的情况下一个特征可以提供多少信息量。互信息会通过计算两个特征所共有的信息,考虑的是数据的分布。
scipy.stats.entropy([p1,...], base=2)
互信息的定义:
P可以通过将特征值分成一些桶,然后计算进入每个桶里面的数字的比例来得到
归一化的互信息量:
具有较高互信息的特征对,可以去掉其中之一;进行回归的时候,则可以将互信息非常低的特征去掉;

封装器

把特征的重要性放到模型的训练流程中。
RFE(recursive feature elimination,特征递归消除),将一个估算器和预期数量的特征当做参数,然后只要发现一个足够小的特征子集,就在这个特征子集中训练估算器。

  1. from sklearn.feature_selection import RFE
  2. selector = RFE(clf, n_feature_to_select = 3)
  3. selector = selector.fit(X,Y)
  4. selector.support_
  5. selector.ranking_

11.3 其他特征选择方法

有些选择是根植于机器学习方法中。如决策树、L1正则化。

11.4 特征抽取

PCA(主成分分析)

PCA是一个线性方法。在给定原始特征空间,PCA会找到一个更低维空间的线性映射。

矩阵的特征值预示着方差的大小。

  1. from sklearn.decomposition import PCA
  2. pca = PCA(n_components=x)
  3. Xtrans = pca.fit_transform(X)
  4. pca_.explained_variance_ratio ## 保留的方差

只能针对线性数据,对于非线性数据,有 kernel PCA 进行扩展
如果分类最佳特征出现在方差弱的特征上,则其有问题(PCA不考虑结果值)

LDA

LDA是一个有监督的降维方法。

  1. from sklearn import lda
  2. lda_ = lda.LDA(n_components=1)
  3. X_trans = lda_.fit_transform(X, Y)

当类别变多后,每种类的样本空间变少,LDA的效果会变差。因此选用方法只能是具体情况具体分析。

11.5 多维标度法(MDS)

MDS在降维的时候试图尽可能保留样本间的相对距离。对一个高维数据集试图获得视觉印象的时候非常有用。

MDS多数据点本身并不关心,对数据集点间的不相似性很感兴趣,并将不相似性解释为距离。

MDS试图在低空间中放置数据点,使得新的距离尽可能与原始空间中的距离相似。MDS是一个揭示数据相似性的有用工具。

  1. from sklearn import manifold
  2. mds_ = manifold.MDS(n_components=3)
  3. X_trans = mds_.fit_transform(X)

第12章 大数据

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注