我极度槽糕的心情!!!
今天早上我突然发现了一件让我很伤心的事情,前天写的一篇技术博文《统计学习方法笔记八—-提升方法》竟然没有发表成功!!!而且又被后来的草稿给覆盖了!!!What?!
前言
上一节我们已经讲过一次代码实现了,主要是调用sklearn中的接口,让大家有个整体上的认识。本节主要是利用Python一步一步的来实现。注意,这只是一个简单的实现,步骤还是相对清晰,有助于对算法的加深理解,但是本算法不保证效率。
代码实现
我们先引入两个Python 代码包:sign.py和weakclass.py。sign.py实现了符号函数,很简单。weakclass.py实现了弱分类器,在AdaBoost算法中没迭代一次就训练一个弱分类器,那么在这里就是没迭代一次,就需要调用一次WEAK类,并且在WEAK类中实现了一个train方法。train方法主要是用来训练每一个弱分类器:找到最小的误差分裂点以及分类属性、分裂类别。
#sign.py
# -*- encoding:utf-8 -*-
#符号函数
import numpy as np
import scipy as sp
import warnings
def sign(x):
q=np.zeros(np.array(x).shape)
q[x>=0]=1
q[x<0]=-1
return q
#weakclass.py
# -*- encoding:utf-8 -*-
#弱分类器
from __future__ import division
import numpy as np
import scipy as sp
class WEAKC:
def __init__(self,X,y):
'''
param X: N*M matrix
param y: is a length M vector
M is the number of traincase 训练样本个数
N is the number of feature 特征个数
this weak calssifier is a decision Stump
it's just a basic example from <统计学习方法>
'''
self.X=np.array(X)
#print self.X.shape
self.y=np.array(y)
self.N=self.X.shape[0]
def train(self,W,steps=100):
'''
W is a N length vector#权重向量
'''
min=10000000000.0
t_val=0
t_point=0
t_b=0
self.W=np.array(W)
for i in range(self.N):#对于每一个特征
q,err=self.findmin(i,1,steps)#1:类别为1
#判断划分后的错误率是否小于当前的错误率
if err<min:
min=err
t_val=q
t_point=i#分裂点为第i个特征
t_b=1
#按照类别-1进行划分
for i in range(self.N):
q,err=self.findmin(i,-1,steps)#;类别为-1
if err<min:
min=err
t_val=q
t_point=i
t_b=-1
self.t_val=t_val#得到最终决策树
self.t_point=t_point#得到最终的分裂特征为
self.t_b=t_b#最终的分裂类别
print self.t_val,self.t_point,self.t_b
return min
def findmin(self,i,b,steps):#第i个特征上切分,找到最小误差
t=0
now=self.predintrain(self.X,i,t,b).transpose()
err=np.sum((now!=self.y)*self.W)
pgd=0
buttom=np.min(self.X[i,:])
up=np.max(self.X[i,:])
mins=1000000
minst=0
st=(up-buttom)/steps
for t in np.arange(buttom,up,st):
now=self.predintrain(self.X,i,t,b).transpose()
err=np.sum((now!=self.y)*self.W)
if err<mins:
mins=err
minst=t
return minst,mins#寻找最小分裂,和最小分裂误差
def predintrain(self,test_set,i,t,b):
test_set=np.array(test_set).reshape(self.N,-1)#行数为:特征数。列数为:样本数
gt=np.ones((np.array(test_set).shape[1],1))#行数为:样本数,列数为:1
gt[test_set[i,:]*b<t*b]=-1
return gt
def pred(self,test_set):
test_set=np.array(test_set).reshape(self.N,-1)#行数为:特征数。列数为:样本数
t=np.ones((np.array(test_set).shape[1],1))#行数为:样本数,列数为:1
t[test_set[self.t_point,:]*self.t_b<self.t_val*self.t_b]=-1
return t
最后就是我们的集成算法:AdaBoost.
# -*- encoding:utf-8 -*-
from __future__ import division
import numpy as np
import scipy as cp
from weakclassify import WEAKC
from sign import *
class ADABST:
def __init__(self,X,y,Weaker=WEAKC):
'''
:param X: 是一个N*M的矩阵,N:代表feature_numbers M:代表sample_numbers
:param y:
:param Weaker:一个弱分类器
:return:
'''
#初始化数据
#X是样本点
self.X=np.array(X)
#列优先排列 Y是样本对应的类别 排成一个行向量
self.y=np.array(y).flatten(1)
#print self.y
#判断数据维度的列数是否一样(数据是否对应)
assert self.X.shape[1]==self.y.size
self.Weaker=Weaker
self.sums=np.zeros(self.y.shape)
#print self.sums
#初始化权值,每个样本的权值为1/样本数
self.W=np.ones((self.X.shape[1],1)).flatten(1)/self.X.shape[1]
#print self.W
self.Q=0#统计迭代次数
def train(self,M=4):
'''
M是最大类别(第M个)弱分类器的下标,其实就是迭代的次数
'''
#初始化弱分类器字典(空)
self.G={}
#弱分类器的话语权
self.alpha={}
print '总共迭代次数为:',M
for i in range(M):
self.G.setdefault(i)#{0:None,...,i:None}
self.alpha.setdefault(i)#{0:None,...,i:none}
for i in range(M):
print '第%d次迭代:'%i
#用样本初始化弱分类器
self.G[i]=self.Weaker(self.X,self.y)
#调用train方法,训练弱分类器,同时计算最小误差率
e=self.G[i].train(self.W)
print '第%d次迭代的错误率为:%.2f'%(i,e)
#计算弱分类器Gi的话语权(根据公式)
self.alpha[i]=1/2*np.log((1-e)/e)#求得第i个分类器的系数
#计算弱分类器Gi对样本的分类结果
sg=self.G[i].pred(self.X)
#计算归一化因子(计算样本权值时,保证权值之和为1)
Z=self.W*np.exp(-self.alpha[i]*self.y*sg.transpose())
#更新样本的权重
self.W=(Z/Z.sum()).flatten(1)
#记录迭代次数(从0开始)
self.Q=i
#判断组合起来的强分类器的效果,如果没有分错,不再迭代
if self.finalclassifer(i)==0:
print i+1,"weak classifier is enough to make the error to 0"
break
def finalclassifer(self,t):
'''
将第1个到第t个弱分类器组合起来(跟踪adaboost强分类器组合公式)
'''
#组合成强分类器,并直接计算出其对样本的分类结果(没有用sign函数计算前)
self.sums=self.sums+self.G[t].pred(self.X).flatten(1)*self.alpha[t]
#用sign对分类器计算出的值进行判别
pre_y=sign(self.sums)
t=(pre_y!=self.y).sum()
return t
def pred(self,test_set):
test_set=np.array(test_set)
#判断数据大小是否一样
assert test_set.shape[0]==self.X.shape[0]
sums=np.zeros((test_set.shape[1],1)).flatten(1)
#计算分类器训练样本的结果
for i in range(self.Q+1):
sums=sums+self.G[i].pred(test_set).flatten(1)*self.alpha[i]
#print sums
#甩sign函数判断类别
pre_y=sign(sums)
return pre_y
if __name__ == '__main__':
#我们利用书上的例子来试一下
X = np.array([0,1,2,3,4,5,6,7,8,9]).reshape(1,-1)#1行10列
#print X
#print X.shape
y = np.array([1,1,1,-1,-1,-1,1,1,1,-1]).transpose()#10行
#print y
#print y.shape
a= ADABST(X,y)
a.train(5)
print a.G
print a.pred([[0.55,4.5]])
参考文献:
http://blog.csdn.net/zxc024000/article/details/51577324
http://blog.csdn.net/Best_Coder/article/details/42127033
http://blog.csdn.net/google19890102/article/details/46376603