所谓学习问题,是指观察由n个样本组成的集合,并依据这些数据来预測未知数据的性质。

学习任务(一个二分类问题):

区分一个普通的互联网检索Query是否具有某个垂直领域的意图。如果如今有一个O2O领域的垂直搜索引擎,专门为用户提供团购、优惠券的检索;同一时候存在一个通用的搜索引擎,比方百度,通用搜索引擎希望可以识别出一个Query是否具有O2O检索意图,如果有则调用O2O垂直搜索引擎,获取结果作为通用搜索引擎的结果补充。

我们的目的是学习出一个分类器(classifier),分类器能够理解为一个函数,其输入为一个Query,输出为0(表示该Query不具有o2o意图)或1(表示该Query具有o2o意图)。

特征提取:

要完毕这样一个学习任务,首先我们必须找出决定一个Query是否具有O2O意图的影响因素,这些影响因素称之为特征(feature)。

特征的好坏非常大程度上决定了分类器的效果。在机器学习领域我们都知道特征比模型(学习算法)更重要。(顺便说一下,工业界的人都是这么觉得的,学术界的人可能不以为然。他们整天捣鼓算法,发出来的文章大部分都没法在实际中应用。

)举个样例,如果我们的特征选得非常好。可能我们用简单的规则就能推断出终于的结果。甚至不须要模型。比方,要推断一个人是男还是女(人类当然非常好推断。一看就知道,这里我们如果由计算机来完毕这个任务,计算机有非常多传感器(摄像头、体重器等等)能够採集到各种数据),我们能够找到非常多特征:身高、体重、皮肤颜色、头发长度等等。由于依据统计我们知道男人一般比女人重。比女人高,皮肤比女人黑,头发比女人短。所以这些特征都有一定的区分度。可是总有反例存在。

我们用最好的算法可能准确率也达不到100%。如果计算机还能够读取人的身份证号码,那么我们可能获得一个更强的特征:身份证号码的倒数第二位是否是偶数。依据身份证编码规则,我们知道男性的身份证号码的倒数第二位是奇数,女生是偶数。

因此。有了这个特征其它的特征都不须要了,并且我们的分类器也非常easy,不须要复杂的算法。

言归正传。对于O2O Query意图识别这一学习任务,我们可以用的特征可能有:Query在垂直引擎里可以检索到的结果数量、Query在垂直引擎里可以检索到的结果的类目困惑度(perplexity)(检索结果的类目越集中说明其意图越强)、Query是否能预測到特征的O2O商品类目、Query是否包括O2O产品词或品牌词、Query在垂直引擎的历史展现次数(PV)和点击率(ctr)、Query在垂直引擎的检索结果相关性等等。

特征表示:

特征表示是对特征提取结果的再加工,目的是增强特征的表示能力,防止模型(分类器)过于复杂和学习困难。比方对连续的特征值进行离散化,就是一种经常使用的方法。

这里我们以“Query在垂直引擎里能够检索到的结果数量”这一特征为例,简要介绍一下特征值分段的过程。首先,分析一下这一维特征的分布情况。我们对这一维特征值的最小值、最大值、平均值、方差、中位数、三分位数、四分位数、某些特定值(比方零值)所占比例等等都要有一个大致的了解。获取这些值,python编程语言的numpy模块有非常多现成的函数能够调用。最好的办法就是可视化。借助python的matplotlib工具我们能够非常easy地划出数据分布的直方图,从而推断出我们应该对特征值划多少个区间。每一个区间的范围是如何的。比方说我们要对“结果数量”这一维特征值除了“0”以为的其它值均匀地分为10个区间,即每一个区间内的样本数大致同样。

“0”是一个特殊的值,因此我们想把它分到一个单独的区间,这样我们一共同拥有11个区间。python代码实现例如以下:

import numpy as np

def bin(bins):
assert isinstance(bins, (list, tuple))
def scatter(x):
if x == 0: return 0
for i in range(len(bins)):
if x <= bins[i]: return i + 1
return len(bins)
return np.frompyfunc(scatter, 1, 1) data = np.loadtxt("D:\query_features.xls", dtype='int')
# descrete
o2o_result_num = data[:,0]
o2o_has_result = o2o_result_num[o2o_result_num > 0]
bins = [ np.percentile(o2o_has_result, x) for x in range(10, 101, 10) ]
data[:,0] = bin(bins)(o2o_result_num)

我们首先获取每一个区间的起止范围,即分别算法特征向量的10个百分位数,并依此为基础算出新的特征值(通过bin函数,一个numpy的universal function)。

训练数据:

这里我们通过有监督学习的方法来拟合分类器模型。

所谓有监督学习是指通过提供一批带有标注(学习的目标)的数据(称之为训练样本),学习器通过分析数据的规律尝试拟合出这些数据和学习目标间的函数。使得定义在训练集上的整体误差尽可能的小,从而利用学得的函数来预測未知数据的学习方法。注意这不是一个严格的定义,而是我依据自己的理解简化出来的。

一批带有标注的训练数据从何而来,一般而言都须要人工标注。我们从搜索引擎的日志里随机採集一批Query,而且保证这批Query可以覆盖到每维特征的每一个取值(从这里也可以看出为什么要做特征分区间或离散化了,由于如不这样做我们就不能保证可以覆盖到每维特征的每一个取值)。然后,通过人肉的方法给这边Query打上是否具有O2O意图的标签。数据标注是一个痛苦而漫长的过程。须要具有一定领域知识的人来干这种活。

标注质量的好坏非常有可能会影响到学习到的模型(这里指分类器)在未知Query上判别效果的好坏。

即正确的老师更可能教出正确的学生,反之,错误的老师教坏学生的可能性越大。在我自己标注数据的过程中,发现有一些Query的O2O意图比較模棱两可,导致我后来回头看的时候总认为自己标得不正确。反重复复改动了好几次。

选择模型:

在我们的问题中,模型就是要学习的分类器。有监督学习的分类器有非常多,比方决策树、随机森林、逻辑回归、梯度提升、SVM等等。

怎样为我们的分类问题选择合适的机器学习算法呢?当然,假设我们真正关心准确率。那么最佳方法是測试各种不同的算法(同一时候还要确保对每一个算法測试不同參数)。然后通过交叉验证选择最好的一个。可是,假设你仅仅是为你的问题寻找一个“足够好”的算法,或者一个起点,也是有一些还不错的一般准则的。比方假设训练集非常小。那么高偏差/低方差分类器(如朴素贝叶斯分类器)要优于低偏差/高方差分类器(如k近邻分类器),由于后者easy过拟合。然而,随着训练集的增大,低偏差/高方差分类器将開始胜出(它们具有较低的渐近误差),由于高偏差分类器不足以提供准确的模型。

这里我们重点介绍一次完整的机器学习全过程,所以不花大篇幅在模型选择的问题上。推荐大家读一些这篇文章:《怎样选择机器学习分类器?》。

通过交叉验证拟合模型:

机器学习会学习数据集的某些属性,并运用于新数据。

这就是为什么习惯上会把数据分为两个集合,由此来评价算法的优劣。这两个集合。一个叫做训练集(train data),我们从中获得数据的性质;一个叫做測试集(test
data)。我们在此測试这些性质,即模型的准确率。

将一个算法作用于一个原始数据。我们不可能仅仅做出随机的划分一次train和test data,然后得到一个准确率。就作为衡量这个算法好坏的标准。

由于这样存在偶然性。我们必须好多次的随机的划分train data和test data。分别在其上面算出各自的准确率。这样就有一组准确率数据,依据这一组数据,就能够较好的准确的衡量算法的好坏。

交叉验证就是一种在数据量有限的情况下的很好evaluate
performance的方法。

 1 from sklearn import cross_validation
2 from sklearn import tree
3 from sklearn import ensemble
4 from sklearn import linear_model
5 from sklearn import svm
6
7 lr = linear_model.LogisticRegression()
8 lr_scores = cross_validation.cross_val_score(lr, train_data, train_target, cv=5)
9 print("logistic regression accuracy:")
10 print(lr_scores)
11
12 clf = tree.DecisionTreeClassifier(criterion='entropy', max_depth=8, min_samples_split=5)
13 clf_scores = cross_validation.cross_val_score(clf, train_data, train_target, cv=5)
14 print("decision tree accuracy:")
15 print(clf_scores)
16
17 rfc = ensemble.RandomForestClassifier(criterion='entropy', n_estimators=3, max_features=0.5, min_samples_split=5)
18 rfc_scores = cross_validation.cross_val_score(rfc, train_data, train_target, cv=5)
19 print("random forest accuracy:")
20 print(rfc_scores)
21
22 etc = ensemble.ExtraTreesClassifier(criterion='entropy', n_estimators=3, max_features=0.6, min_samples_split=5)
23 etc_scores = cross_validation.cross_val_score(etc, train_data, train_target, cv=5)
24 print("extra trees accuracy:")
25 print(etc_scores)
26
27 gbc = ensemble.GradientBoostingClassifier()
28 gbc_scores = cross_validation.cross_val_score(gbc, train_data, train_target, cv=5)
29 print("gradient boosting accuracy:")
30 print(gbc_scores)
31
32 svc = svm.SVC()
33 svc_scores = cross_validation.cross_val_score(svc, train_data, train_target, cv=5)
34 print("svm classifier accuracy:")
35 print(svc_scores)

上面的代码我们尝试同交叉验证的方法对照五种不同模型的准确率,结果例如以下:

 1 logistic regression accuracy:
2 [ 0.76953125 0.83921569 0.85433071 0.81102362 0.83858268]
3 decision tree accuracy:
4 [ 0.73828125 0.8 0.77559055 0.71653543 0.83464567]
5 random forest accuracy:
6 [ 0.75 0.76862745 0.76377953 0.77165354 0.80314961]
7 extra trees accuracy:
8 [ 0.734375 0.78039216 0.7992126 0.76377953 0.79527559]
9 gradient boosting accuracy:
10 [ 0.7578125 0.81960784 0.83464567 0.80708661 0.84251969]
11 svm classifier accuracy:
12 [ 0.703125 0.78431373 0.77952756 0.77952756 0.80708661]

在O2O意图识别这个学习问题上,逻辑回归分类器具有最好的准确率。其次是梯度提升分类器;决策树和随机森林在我们的測试结果中并没有体现出明显的差异。可能是我们的特殊数量太少而且样本数也较少的原因;另外大名典典的SVM的表现却比較让人失望。

整体而言,准确率仅仅有82%左右,分析其原因。一方面我们实现的特征数量较少;还有一方面临时未能实现区分能力强的特征。兴许会对此持续优化。

因为逻辑回归分类器具有最好的性能。我们决定用所有是可能训练数据来拟合之。

lr = lr.fit(train_data, train_target)

模型数据持久化:

学到的模型要可以在将来利用起来,就必须把模型保存下来,以便下次使用。同一时候。数据离散化或数据分区的范围数据也要保存下来。在预測的时候相同也须要对特征进行区间划分。python提供了pickle模块用来序列号对象,并保存到硬盘上。同一时候。scikit-learn库也提供了更加高效的模型持久化模块。可以直接使用。

1 from sklearn.externals import joblib
2 joblib.dump(lr, 'D:\lr.model')
3 import pickle
4 bin_file = open(r'D:\result_bin.data', 'wb')
5 pickle.dump(bins, bin_file)
6 bin_file.close()

分类器的使用:

如今大功告成了。我们须要做的就是用学习到的分类器来推断一个新的Query究竟是否具有O2O意图。由于我们分类器的输入是Query的特征向量,而不是Query本身,因此我们须要实现提取好Query的特征。

如果我们已经离线算好了每一个Query的特征。如今使用的时候仅仅须要将其载入进内场就可以。

分类器使用的过程首先是从硬盘读取模型数据和Query特征。然后调用模型对Query进行预測。输出结果。

 1 # load result bin data and model
2 bin_file = open(r'D:\result_bin.data', 'rb')
3 bins = pickle.load(bin_file)
4 bin_file.close()
5
6 lr = joblib.load('D:\lr.model')
7
8 # load data
9 query = np.genfromtxt(r'D:\o2o_query_rec\all_query', dtype='U2', comments=None, converters={0: lambda x: str(x, 'utf-8')})
10 feature = np.loadtxt(r'D:\o2o_query_rec\all_features', dtype='int', delimiter='\001')
11
12 # descrite
13 feature[:,0] = bin(bins)(feature[:,0])
14 feature[:,1] = ufunc_segment(feature[:,1])
15
16 # predict
17 result = lr.predict(feature)
18
19 # save result
20 #np.savetxt(r'D:\o2o_query_rec\classify_result.txt', np.c_[query, result], fmt=u"%s", delimiter="\t")
21 result_file = open(r'D:\o2o_query_rec\classify_result.txt', 'w')
22 i = 0
23 for q in query:
24 result_file.write('%s\t%d\n' % (q, result[i]))
25 i += 1
26 result_file.close()

须要注意的是我们Query的编码是UTF-8,load的时候须要做对应的转换。

另外。在python 3.3版本号,numpy的savetxt函数并不能正确保持UTF-8格式的中文Query(第20行凝视掉的代码输出的Query都变成了bytes格式的)。假设小伙伴们有更好的办法可以解决问题。请告诉我,谢谢!

机器学习完整过程案例分布解析,python代码解析的更多相关文章

  1. Redis源码解析:18Hiredis同步API和回复解析API代码解析

    Redis的sentinel模式使用了Hiredis代码,Hiredis是redis数据库一个轻量级的C语言客户端库.它实现的向Redis发送命令的API函数redisCommand,使用方法类似于p ...

  2. 机器学习实战-边学边读python代码(4)

    程序2-4 分类器针对约会网站的测试代码(4) def datingClassTest():hoRatio = 0.10 //将文件读入内存矩阵datingDataMat,datingLabels = ...

  3. 《机器学习实战》之一:knn(python代码)

    数据 标称型和数值型 算法 归一化处理:防止数值较大的特征对距离产生较大影响 计算欧式距离:测试样本与训练集 排序:选取前k个距离,统计频数(出现次数)最多的类别 def classify0(inX, ...

  4. 机器学习实战-边学边读python代码(5)

    def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):    p1 = sum(vec2Classify * p1Vec) + log(pClass1 ...

  5. 机器学习实战-边学边读python代码(3)

    程序清单2-3 归一化特征值: def autoNorm(dataSet): /* >>> barray([[ 1., 2., 3.], [ 2., 3., 4.], [ 10., ...

  6. Python代码样例列表

    扫描左上角二维码,关注公众账号 数字货币量化投资,回复“1279”,获取以下600个Python经典例子源码 ├─algorithm│       Python用户推荐系统曼哈顿算法实现.py│    ...

  7. 机器学习_线性回归和逻辑回归_案例实战:Python实现逻辑回归与梯度下降策略_项目实战:使用逻辑回归判断信用卡欺诈检测

    线性回归: 注:为偏置项,这一项的x的值假设为[1,1,1,1,1....] 注:为使似然函数越大,则需要最小二乘法函数越小越好 线性回归中为什么选用平方和作为误差函数?假设模型结果与测量值 误差满足 ...

  8. 字符型图片验证码识别完整过程及Python实现

    字符型图片验证码识别完整过程及Python实现 1   摘要 验证码是目前互联网上非常常见也是非常重要的一个事物,充当着很多系统的 防火墙 功能,但是随时OCR技术的发展,验证码暴露出来的安全问题也越 ...

  9. 10 种机器学习算法的要点(附 Python 和 R 代码)

    本文由 伯乐在线 - Agatha 翻译,唐尤华 校稿.未经许可,禁止转载!英文出处:SUNIL RAY.欢迎加入翻译组. 前言 谷歌董事长施密特曾说过:虽然谷歌的无人驾驶汽车和机器人受到了许多媒体关 ...

随机推荐

  1. PHP 二维数组去掉重复值并保持原结构

    PHP 二维数组去掉重复值并保持原结构 直接上代码,解释很详细 //二维数组去掉重复值 function arrunique($a){ foreach($a[0] as $k => $v){ / ...

  2. zzulioj--1746--三角形面积(几何水题)

    1746: 三角形面积 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 100  Solved: 31 SubmitStatusWeb Board De ...

  3. Android框架-Volley(三)

    经过前面两篇文章的学习,我们已经掌握了Volley各种Request的使用方法,包括StringRequest.JsonRequest.ImageRequest等.其中StringRequest用于请 ...

  4. Bayes++ Library入门学习之熟悉UKF相关类

    UKF-SLAM是一种比较流行SLAM方案.相比EKF-SLAM,UKF利用unscented transform代替了EKF的线性化趋近,因而具有更高的精度.Bayes++库中的unsFlt.hpp ...

  5. c# 的类成员

    1 字段和变量的区别 字段是在类中定义的数据成员 由访问修饰符+数据类型+字段名(public string name) 字段就像类的一个小数据库,用来存放和类相关的数据; 而变量是没有修饰符的(in ...

  6. gcd(最大公约数)lcm(最小公倍数)E - Wolf and Rabbit

    1.gcd 递归实现 int gcd(int i,int j){ if(j==0) return i; else return gcd(j,i%j);} 2.lcm int gcd(int i,int ...

  7. 理解JavaScript Call()函数原理。

    最近在做面试题的过程中偶然碰到关于call函数的问题.然后再百度上查了查.偶然看到一篇文章:JavaScript中的call.apply.bind深入理解 抛开其对call函数基本概念的介绍还有其他原 ...

  8. 昼猫笔记 JavaScript -- 异步执行 | 定时器真的定时执行?

      本篇主要内容:异步.定时器引发的思考 预计阅读时间:8分钟 了解 我们都知道在js中定时器有两种  setInterval()  . setTimeout()   setInterval() :按 ...

  9. Linux中常用命令(文件)

    1.cat 显示出文件的全部内容 (1)格式:cat 文件名 -n 显示行号 (2)特点:一次性显示所有文件内容 2.tac 从最后一行倒着显示文件全部内容 3.more 全屏方式分页显示文件内容 回 ...

  10. 紫书 例题 10-5 UVa 12716 (枚举方式)

    设gcd(a, b) = a xor b = c 那么我们可以证明c=a-b 那么同时c又是a的因子(c是a与b的最大公因数) 所以我们可以枚举c,然后枚举c的倍数,也就是a 有了a和c可以算出b为a ...