机器学习实战笔记1
机器学习实战&统计学习方法
前两部分主要讨论监督学习(只需要给定输入样本集,机器就可以从中推演出指定目标变量的可能结果)
第一部分 分类
目标变量(也叫类别):一般为标称型(在限定的目标集中取值,离散有限型,一般是分类算法)和数值型(无限的数值集合中取值,连续型,一般是回归算法)
特征:属性
第1章机器学习基础
机器学习的主要任务
监督学习:分类,回归。[k-近邻,朴素贝斯,svm,决策树,等]
无监督学习:聚类,密度估计。[k-means,等]
如何选择算法
考虑使用机器学习算法的目的:预测目标变量的值,选择监督学习,确定目标变量的类型,离散型使用分类算法,连续型使用回归算法;否则,选择无监督学习,如果将数据分为离散的组,使用聚类算法,估计数据,使用密度估计算法;
【这里的分类即y的取值是离散的,函数f(x)的结果就给定的几种可能;回归即y的取值是连续的,函数f(x)的结果不定,但是对于好的模型f(x),结果y是围绕f(x)上下波动的】
第2章 K-NN
原理
给定一个训练集,对于新输入的实例(测试集),在训练集中找到与该实例最近的k个实例(一般使用欧式距离),统计这k个实例所属的类,把新输入的实例归到包含实例最多的类
上面提到的最原始的方法为线性扫描,对于大规模的数据,计算非常耗时,不可行;优化的方法是使用特殊的结构来存储训练数据,以减少计算距离的次数。下面介绍kd树
构造kd树:
对于给定的数据集
(1)对未分类点的一个维度
(2)
最终形成一颗kd树
搜索kd树:
这一块有个问题一直没有解决,如何判断超球体与超矩形相交,如果相交,对于相交的子树如何递归???
这篇文章让我理解了kd 树算法之详细篇
假设需要找到离测试点最近的k个实例,以k维数组存储
指定节点为根节点,执行(1)
(1)
从指定节点出发,向下搜索,找到测试节点对应的叶节点,指针指向叶节点
(2)
如果k维数组未填满,将节点添加到数组中,标记该节点:
如果节点有子节点且未被标记:
指向该子节点,转向(1)
如果节点没有子节点或者子节点被标记:
指针指向父节点,转向(2)
(3)
如果为叶节点或者该节点的子节点均被标记,标记该节点,判断该节点与测试点之间的距离t和k维数组中最远距离s的大小关系:
如果t
实例2:用K-NN算法改进约会网址的配对效果
给定了1000条数据,有3个参数和一个类别,3个参数分别是每年获得的飞行常客里程数,玩视频游戏所耗时间的百分比,每周消费的冰淇淋公升数;类别分别是不喜欢,魅力一般,极具魅力
绘制两两特征之间的关系图,发现特征game和fly之间的图能够将三个类分的更加清楚,如下图所示
对于fly和game之间的关系,如下,但没有上面类型区分度高
(上面的这些图,并不知道具体有什么用???,仅仅是为了直观吗。。。,下面使用KNN算法,并评估算法的误差率)
首先需要归一化数据,是数据分布在[0,1]之间,这样每个特征对判别函数(这里用到的是欧氏距离最近的k个点)的影响一样:
value=(value-minvalue)/(maxvalue-minvalue)
#归一化数据,但是对于上面画的图像是没有影响的,集体变化;后面会用到
def autoNorm(dataSet,fea):
for i in fea:
maxValue=max(dataSet[i])
minValue=min(dataSet[i])
#关于DataFrame数据处理,应该是一列列(行行)的处理,而不是单个值处理;复制元素的使用:np.tile
dataSet[i]=(dataSet[i]-np.tile(minValue,len(dataSet[i])))/(maxValue-minValue)
return dataSet
[注意上面归一化数据的方法:不是遍历单个列中的单个元素,而是对整个列(行)操作]
做好归一化数据之后,下面就需要将测试数据分为样本数据和测试数据,这里我用800条数据作为样本数据,200条数据作为测试数据
同样对于单条(行)测试数据,使用tile函数将其行变为800,再与整个样本数据进行距离计算,对结果排序,选取前k个,统计类别最多的类作为测试数据的类别
[这与传统的方法最大的不同,不是单个单个计算,而是一整列(行)计算,速度会快很多]
代码如下:
dataf=autoNorm(df,['ice','game','fly'])#归一化【0,1】,使用公式:value=(value-minvalue)/(maxvalue-minvalue)
dres=dataf[:800]#前800条数据作为原始集
#这个之所以要再构建DataFrame,是后面在DF的切片中操作时(新建一列或者是对列中的元素赋值时),有警告
#A value is trying to be set on a copy of a slice from a DataFrame.
dres=pd.DataFrame(dres,columns=['fly','game','ice','category'])
dtes=dataf[800:]#后200条作为测试集
xt=dtes[['ice','game','fly']].as_matrix()#获取numpy数组
yt=dtes['category'].tolist()#测试集的类别
tes=classify0(xt,dres[['ice','game','fly']],dres,10)#knn
eva=get_evaluation(yt,tes)#误差判断:错误集/全集
KNN算法与判断误差函数
其中距离度量使用的时欧式距离:
p=1时,为曼哈顿距离:
#KNN算法
def classify0(inx,dataSet,labels,k):
output=[]
dataSize=dataSet.shape[0]
for i in inx:
diffi=np.tile(i,[dataSize,1])-dataSet#相减
sqi=diffi**2#对diff中的元素分别平方
sui=sqi.sum(axis=1)#axis=1表示对行求和,变成一列了
disi=sui**0.5
labels['distance']=disi
labels=labels.sort_values(by='distance')#通过某列的值来排序
"""
c1=labels['category'][:k][labels.category==1].count()#选取category列的前k行,计算类别为1的个数
c2=labels['category'][:k][labels.category==2].count()
c3=labels['category'][:k][labels.category==3].count()
c=max(c1,c2,c3)
if c==c1:c=1
elif c==c2:c=2
else:c=3
"""
#上面的一大块,此处一行就搞定,而且抽象化:获取前k行,以category为索引分组,并计算索引出现的个数,排序,获取值最大的索引
c=labels['category'][:k].groupby([labels['category']]).count().sort_values().index[-1]
output.append(c)
return output
#判断错误率
def get_evaluation(res,tes):
num=0
n=len(res)
for i in range(n):
if res[i]!=tes[i]:
num=num+1
return num/n
实例3:手写识别
这个我做的是kaggle上的Digit Recognizer,用上面的KNN算法,做完后会补上
KNN算法小节
1.在应用中,k值一般取较小的值,一般采用交叉验证法来选取最优的k值
2.(待补)
第三章 决策树
决策树的定义
是一种基于分类和回归的方法。在分类问题中,表示特征对实例进行分类的过程,也可以认为是定义在特征空间与类空间上的条件概率
模型
决策树由节点和有向边组成;节点有两种类型:内部节点和叶节点。内部节点表示一个特征(属性),叶节点表示一个类
构建
构建根节点,将所有训练数据都放在根节点,选择一个最优的特征,按照这一特征将训练数据分割成子集,如果这些子集能够被基本正确分类,那么构建叶节点;如果不能正确分类,继续对这些子集选择最优特征,进行分类…
决策树的学习
决策树学习包含三点:特征选择,决策树生成,决策树剪枝
下面先介绍原理,然后通过实例来与之对应,方便更好的理解
特征选择
按照某一个最优的特征去分支,判断特征最优的方法是判断信息增益(在划分数据集之前和之后信息发生的变化),即通过该特征分类后,不确定因素下降最大;
熵:表示随机变量不确定性的度量
设X为离散的随机变量,其概率分布为
则随机变量X的熵为
熵越大,随机变量的不确定性就越大,见百度百科:
*通常,一个信源发送出什么符号是不确定的,衡量它可以根据其出现的概率来度量。概率大,出现机会多,不确定性小;反之就大。
不确定性函数f是概率P的单调递降函数;两个独立符号所产生的不确定性应等于各自不确定性之和,即f(P1,P2)=f(P1)+f(P2),这称为可加性。同时满足这两个条件的函数f是对数函数,即 。
在信源中,考虑的不是某一单个符号发生的不确定性,而是要考虑这个信源所有可能发生情况的平均不确定性。若信源符号有n种取值:U1…Ui…Un,对应概率为:P1…Pi…Pn,且各种符号的出现彼此独立。这时,信源的平均不确定性应当为单个符号不确定性-logPi的统计平均值(E),可称为信息熵,即 ,式中对数一般取2为底,单位为比特。*
统计学习方法中给出
证明:
由基本不等式有:
且有
即:
条件熵:H(Y|X)
设随机变量(X,Y),其联合概率分布为
条件熵表示在已知随机变量X的条件下随机变量Y的不确定性。定义为X给定条件下Y的条件概率分布的熵对X的数学期望
[当熵和条件熵中的概率由数据估计得到时,所对应的熵和条件熵分别称为经验熵和经验条件熵]
信息增益:g(D,A)
特征A对训练数据集D的信息增益g定义为经验熵H(D)与特征A给定条件下D的经验条件熵H(D|A)之差;即由于特征A而使得对数据集D进行分类的不确定性减少的程度。
实例部分(这里用到的例子是统计学习方法上的实例)
关于用python绘制决策树就不实现了。。。
分为:计算信息熵,计算条件熵,计算信息增益,求解最优特征,分类…
计算信息熵:这个是数据集在没有分类后的混乱程度,针对的是最后一列,即label为随机变量;
#计算给定数据集的信息熵(经验熵)
def calcEntropy(dataSet):
num=len(dataSet)#统计数据集的大小
labelCounts={}#标签分类及个数
#使用的时map统计个数,如果map中不含该标签,则添加新标签;否则,对新标签的值++
for di in dataSet:
currentlabel=di[-1]#只有子集中含有的标签才会导入到labelCounts中
if currentlabel not in labelCounts.keys():
labelCounts[di[-1]]=1
else:
labelCounts[di[-1]]+=1
h=0#经验熵的初始化
#获取map中的值
for lcount in labelCounts.values():
h-=lcount/num*math.log2(lcount/num)#math中的log是e为底的
return h
计算条件熵:即按照某一特征分类后,对分类后的子集求信息熵,再按照分类的特征的占比求和
#计算条件熵(经验条件熵)
def calcConditionalEntropy(dataSet):
num=len(dataSet)#数据集的大小
htvalue=[]#按照各特征分类后的条件熵
for i in range(len(dataSet[0])-1):
feaFlag={}#记录该特征对应的subDataSet列表的子集下标,便于后面将数据分类到subDataSet的各子集中
flag=0
subDataSet=[]#用于存储按照某一特征分类后的子集
#将数据集按特征分类到subDataSet中
for j in dataSet:
currentFea=j[i]
if currentFea not in feaFlag:
feaFlag[currentFea]=flag
flag+=1
sublist=[]
subDataSet.append(sublist)
subDataSet[feaFlag[currentFea]].append(j)
#计算该特征分类后的条件熵
ht=0
for subi in subDataSet:
subnum=len(subi)
ht+=subnum/num*calcEntropy(subi)
htvalue.append(ht)
return htvalue
计算信息增益,选择最优特征
#计算信息增益,选择最优特征
def chooseBestFeature(htval,h):
k=len(htval)
h=numpy.tile(h,k)
g=(h-htval)
#print(g)
bestG=g[0]
bestF=0
#选择最大值对应的下标
for i in range(k):
if g[i]>bestG:
bestG=g[i]
bestF=i
return bestF
创建决策树:这里返回的决策树形如{‘no’: {‘no’: ‘no’, ‘yes’: ‘yes’}, ‘yes’: ‘yes’}
原理是先按照最优特征划分数据集,生成字典(key值为特征对应的属性,value为数据集去掉该特征后对应key的子集),对字典中的子集继续上述操作,直到只有一列或者只有label列值均相同
#获取最优特征对应的子集
def getBestSub(dataSet,bestF):
subtree={}
for i in dataSet:
currfea=i[bestF]
if currfea not in subtree.keys():
sublist=[]
subtree[currfea]=sublist
del i[bestF]
subtree[currfea].append(i)
#print(bestF,subtree)
return subtree
#将一列的数据集转化为字典,并选取字典中value最大对应的key
def maxCnt(dataSet):
data={}
for d in dataSet:
if d[0] not in data.keys():
data[d[0]]=0
data[d[0]]+=1
return max(data.items(),key=lambda x:x[1])[0]#key接收一个函数,x为传入值(这里值一个条目),x[1]表示返回值(这里返回条目中的第二个值)
#创建决策树,返回决策树和每次获取的最优特征下标集
def createTree(dataSet,bestList):
ctree={}
datali=dataSet[0]#获取第0行
classList=[exp[-1] for exp in dataSet]#获取每一行中的最后一个特征(label)
#如果最后一个特征全部相同,则分类完成
if classList.count(classList[0])==len(classList):
return classList[0],bestList
#如果每一行只剩一列(label),不能再分了,判断label中最多的值为该分类下的标签
if len(datali)==1:
return maxCnt(dataSet),bestList
#否则,继续分解
bestF=chooseBestFeature(calcConditionalEntropy(dataSet),calcEntropy(dataSet))
bestList.append(bestF)
subtree=getBestSub(dataSet,bestF)#获取最优特征下的分类字典
#对字典中对应的子集继续分类
for s in subtree.keys():
ctree[s],bestList=createTree(subtree[s],bestList)
return ctree,bestList
求解树的深度:这个本来是想来实现绘制决策树用的,最后放弃了绘制。。。
#求树的深度
def getTreeDeep(tree,i):
if tree==None:
return 0
smax=1
for sub in tree:
if type(tree[sub]) != type("dsds"):
si=getTreeDeep(tree[sub],i+1)
smax=max(si,smax)
else:
smax=max(smax,i+1)
return smax
小结
上面使用的是ID3算法,还有C4.5,CART
ID3算法的缺点是无法直接处理数值数据
第4章 朴素贝叶斯
基本原理【见统计学习方法】
给定训练数据
要求解
由上面的公式可知,我们只需要求解
这里强调一点,
如果各属性
实例1:对文本进行分类【见机器学习实战】
给定训练集,如下,可以看到这是个二分类问题,1代表侮辱性文字,0代表正常言论
#给定的数据集,其中postingList的每一行对应随机向量x,classVec对应着y
def loadDataSet():
postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
['stop', 'posting', 'stupid', 'worthless', 'garbage'],
['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
classVec = [0,1,0,1,0,1] #1 is abusive, 0 not
return postingList,classVec
设给定的预测变量为['love','my','dalmation']
,需要求出该变量对应的类别
现在需要求解
机器学习实战这本书中是将所有的单词的条件概率计算出来,然后对给定的文本集,选出其单词对应的条件概率求乘积来求解,具体如下:
(1)将所有的训练集中的单词取出,放在set中,确保唯一性
#给定的数据集,其中postingList的每一行对应随机向量x,classVec对应着y
def loadDataSet():
postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
['stop', 'posting', 'stupid', 'worthless', 'garbage'],
['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
classVec = [0,1,0,1,0,1] #1 is abusive, 0 not
return postingList,classVec
#第一步:获取set列表
def creatVocabList(dataSet):
vocabSet=set([])#新建set列表
for document in dataSet:
vocabSet=vocabSet|set(document)#并操作
return list(vocabSet)#将set转为list
(2)求解
#第二步:求解P(Y=ck)的概率
def ckprobability(classVec):
labelMap={}
for ck in classVec:
if ck not in labelMap.keys():
labelMap[ck]=0
labelMap[ck]+=1
for ck in labelMap:
labelMap[ck]=labelMap[ck]/len(classVec)#ck类的个数占总个数的比
return labelMap
(3)统计各
【注】这里避免条件概率为0的情况,将初始值设置为1,整体sum加2
#第三步:求解条件概率
#统计各文档集在set列表中的个数
def setOfWord2Vec(vocabList,dataSet):
dataVec=[]
for dasub in dataSet:
subVec=[0]*len(vocabList)
for word in dasub:
if word in vocabList:
subVec[vocabList.index(word)]=1
dataVec.append(subVec)
return dataVec
#计算各单词的条件概率
#因为求解条件概率中如果有为0的值,最后测试集包含该条件概率,会导致整体为0,为了降低这种影响,下面的初始值pNum设为1,总体sum加2
def trainNB(dataVec,classVec):
pNum={}
for i in range(len(classVec)):
if classVec[i] not in pNum:
pNum[classVec[i]]=np.array([1]*len(dataVec[0]))
pNum[classVec[i]]+=np.array(dataVec[i])
for keys in pNum.keys():
pNum[keys]=pNum[keys]/(sum(pNum[keys])+2)
return pNum
(4)对于给定的预测文档,将其中单词对应(3)的条件概率相乘,再与对应的(2)相乘
#第4步,测试数据
def tesNB(testEntry):
dataSet, classVec = loadDataSet()#文档集合类别
vocabList = creatVocabList(dataSet)#set集
labelMap = ckprobability(classVec)#ck的概率
dataVec = setOfWord2Vec(vocabList, dataSet)#文档集对应set集映射
pNum = trainNB(dataVec, classVec)#获取所有单词的条件概率
plabel={}
for ck in pNum.keys():
if ck not in plabel.keys():
plabel[ck]=0
for word in testEntry:
if word in vocabList:
plabel[ck]+=math.log2(pNum[ck][vocabList.index(word)])#测试集中单词对应的条件概率相加
plabel[ck]+=math.log2(labelMap[ck])#加上先验概率
return plabel
(5)找出(4)中最大的值对应的
#第5步:预测分类
def classifyNB(plabel):
return max(plabel.items(),key=lambda x:x[1])[0]#对字典操作,选取value最大的元素对应的key
朴素贝叶斯分类就到此结束。。。后续再更
Python函数附录
range()中的第三个参数为步进
numpy.tile(inx,k)#表示将inx在列的方向上复制k次
numpy.tile(inx,[k,m])#表示将inx在列的方向上复制m次,在行的方向上复制k次
numpy.transpose() #矩阵的转置
内积:numpy.multiply(x1,x2)
将对应的元素分别相乘
arr.shape[0]#表示读取矩阵arr的第一维长度(行)
*2 #两个表示乘方
plt.scatter()的使用:如下面
plt.scatter(df[‘game’],df[‘ice’],c=li)#其中三个参数都可以是列表,第三个参数表示的时颜色列表,对应于前两个值所表示的点的颜色
ones函数
np.ones(5)
array([ 1., 1., 1., 1., 1.])
>>> np.ones((5,), dtype=np.int)
array([1, 1, 1, 1, 1])
>>> np.ones((2, 1))
array([[ 1.],
[ 1.]])
>>> s = (2,2)
>>> np.ones(s)
array([[ 1., 1.],
[ 1., 1.]])
arange函数,生成指定范围,指定步长的列表
>>>range(1,5)
range(1,5)
>>>tuple(range(1, 5))
(1, 2, 3, 4)
>>>list(range(1, 5))
[1, 2, 3, 4]
>>>r = range(1, 5)
>>>type(r)
<class 'range'>
>>>for i in range(1, 5):
... print(i)
1
2
3
4
>>> np.arange(1, 5)
array([1, 2, 3, 4])
>>>range(1, 5, .1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'float' object cannot be interpreted as an integer
>>>np.arange(1, 5, .5)
array([ 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5])
>>>range(1, 5, 2)
>>>for i in range(1, 5, 2):
... print(i)
1
3
>>for i in np.arange(1, 5):
... print(i)
1
2
3
4
DataFrame操作
对于一列可以使用tolist() 如:dtes[‘ice’].tolist()
对于几列数据而言,使用as_matrix() 如:dtes[[‘ice’,’game’,’fly’]].as_matrix()
排序:labels.sort_values(by=’distance’)#通过某列的值来排序
对DataFrame行列操作:df.iloc[:,1:]#表示获取全部行,第2列到全部列
matplotlib
坐标轴区间限定:
plt.axis([xmin,xmax,ymin,ymax])
或者使用
plt.xlim(xmin,xmax)
plt.ylim(ymin,ymax)
# 设置x轴的标签,第一个参数对应x的位置,第二个参数对应x的位置上的text(这里是日期),第三个参数是旋转度
plt.xticks(x[::30],ti_list[::30],rotation=17)
绘制文字:plt.text(x,y,str,fontdict)
参见http://blog.csdn.net/rumswell/article/details/9862089
列表相加要将list转为numpy.array
正则表达式
\w :表示匹配字母,数字,下划线
\W: 表示匹配除字母,数字,下划线以外的字符
\w*:带 *表示前一个字符的0个或多个
\w+ : +表示至少1个
单词附录
deprecated 弃用
numerical 数字的
textual 文本的
比较好的博客
http://blog.csdn.net/lanchunhui/article/details/53046803
统计学习方法归纳
交叉验证法
1.简单的交叉验证法
将给定的数据分为两部分,一部分作为训练集,另一部分作为测试集;用训练集在各种条件下(如不同的参数)训练模型,从而得到不同的模型;在测试集上评价各种模型的测试误差,选出测试误差最小的模型
2.S折交叉验证
应用最多,方法如下:随机的将数据切为S个互不相交大小相同的子集,然后以S-1个子集的数据为训练集来训练模型,剩下的一个子集为测试集来测试模型,有S种组合方式,取S次测评中平均测试误差最小的模型