首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Aggregation Model : Blending , Bagging , Boosting

Aggregation Model : Blending , Bagging , Boosting

作者头像
西红柿炒鸡蛋
发布2018-09-07 17:16:59
5580
发布2018-09-07 17:16:59
举报
文章被收录于专栏:自学笔记自学笔记

⑴Motivation of Aggregation

比如现在有一支股票,你不知道是跌还是涨。你有T个friends,每一个friend对应的建议分别是g1,g2,g3...gn,那么你应该怎么选择建议?

⑵Blending

1.Select the most trust-worthy friend

这其实就是对应validation,我们在所有的friend里面做kfold或者vfoldvalidation,然后比较Ein,小的将拿出来作为你的most trust-worthy friend。和我们之前学的一样,选择应该最好的模型做预测即可。

2.mix their opinion from all your friends uniformly

如果你的friends的水平都差不多,那么不如混合一下他们的想法求平均,把所有的预测结果加载一起进行平均求解。

3.mix their opinion from all your friends non-uniformly

如果你的friends水平参差不齐,那么就可以给予不同的重视程度,比如有专业搞股票的,那就给多点投票权给他,不是就减少点。

4.combine the opinion conditionally

第四种方法与第三种方法类似,但是权重不是固定的,根据不同的条件,给予不同的权重。和第三个相比,不同之处就在于它是动态的。比如如果是传统行业的股票,那么给这方面比较厉害的朋友较高的投票权重,如果是服务行业,那么就给这方面比较厉害的朋友较高的投票权重。

但是第一种方法只是从众多的模型中选择一个最好的,aggregation就是融合,我们需要的是集体的智慧。看一下为什么aggregation可以比普通的模型work better。

一条一条线就是刚刚的validation,aggregation做的就是融合,比如上部分中间的圆圈的点,叉叉一票,圈圈是两票,所以就是圈圈,所以aggregation是可以做到feature transform的。所以是起到了特征转换的效果:

再从另一个方面来看:比如我们使用perceptron learning algorithm:

而如果综合一下投票选择,平均一下是可以得到中间那条最好的直线,这和SVM的目标是一样的。所以aggregation也起到了regularization的效果。综合一下:

The advantages of aggregation model

①feature transform ②regularization

⑶Uniform Blending

对应着第三种方法。我们已经选择好了一些比较好的gt,其实就是model,我们已经从friends里面选择几个很好的朋友出来做aggregation了。uniform意味着就是平均,分类公式:

这种方法对应三种情况:第一种情况是每个候选的矩gt都完全一样,这跟选其中任意一个gt效果相同;第二种情况是每个候选的矩gt都有一些差别,这是最常遇到的,大都可以通过投票的形式使多数意见修正少数意见,从而得到很好的模型,如下图所示;第三种情况是多分类问题,选择投票数最多的那一类即可。

小的差别是可以被大的方向所correct。如果是回归问题:

第一种情况:如果gt都是一样的,那么没有任何区别 第二种情况:如果是都不一样都有差别,那么求平均其实可以减弱这些差别的影响。

对于uniform blending for regression的可行性

所以有:

所以从平均来说,求gt的平均值G比单一的gt要效果更好,可能对于左边单一gt-lost function求平均会混淆单一的gt效果,求平均只是简化计算,配合右边的而已,如果不要平均还是一样的结果,分别求lost相加求平均和先平均再求lost其实就是单一gt和平均G的lost对比。 我们已经知道G是数目为T的gt的平均值。令包含N个数据的样本D独立同分布于PN,每次从新的Dt中学习得到新的gt,在对gt求平均得到G,当做无限多次,即T趋向于无穷大的时候:

所以当T是infinite的时候,得到的g杆就是把T个训练好的朋友的结果综合起来,代替G。这T个gt是通过不同的D来训练的,把他们扔进A(Dt)进行训练,最后得到结果。 所以对应上面等式可以得到:avg(Eout(gt))就是演算法A期望的表现(expected performance of A),因为就是通过A(Dt)演算法来挑选出合适的gt来进行平均的,avg(ε(gt - G)^2)就是不同的gt和他们达成的共识相差了多少(expected deviation to consensus,called variance),Eout(G)就是达成的共识(performance of consensus),而blending在求平均的过程中就有减弱上述variance的作用,只是削弱,而不是消除。因为只有T趋于无限的时候才有可能用g杆完全替代G,因为G是所有gt的consensus,而达成共识是要求大方向把小方向给correct过来,如果大方向不够大,那么是没有能力correct的,所以平时我们使用的是近似于G,而不是真正意义上的G,所以上面的variance不能完全消除。

⑷uniform-blending的代码实现

导包准备和读取数据文件:

import numpy as np
import pandas as pd

def read_file(filename):
  '''
  use to read file which you open
  :param filename:
  :return:dataset
  '''
  data = pd.read_csv(filename)
  return data
  pass

def load_data():
  print('Loading data......')
  train = read_file('../../Data/train.csv')
  test = read_file('../../Data/test.csv')
  y_train = train.iloc[: , 0]
  x_train = train.iloc[: , 1:]
  y_test = test.iloc[: , 0]
  x_test = test.iloc[: , 1:]
  return x_train , y_train , x_test , y_test
  pass

if __name__ == '__main__':
  x_train , y_train , x_test , y_test = load_data()
  print('x_train : ',x_train)
  print('y_train : ',y_train)

又导包:

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import MachineLearning.AggregationModel.Blending.load_data as load_data
from sklearn.model_selection import StratifiedKFold
from sklearn.ensemble import RandomForestClassifier , ExtraTreesClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.linear_model import LogisticRegression

主函数部分:

if __name__ == '__main__':

  np.random.seed(0)  # seed to shuffle the train set
  accuracys = []
  verbose = True
  shuffle = False

  X, y, X_test , y_test = load_data.load_data()

  if shuffle:
      idx = np.random.permutation(y.size) #random a sequence
      X = X[idx]
      y = y[idx]
  clfs = [RandomForestClassifier(n_estimators=100, n_jobs=-1, criterion='gini'),
          RandomForestClassifier(n_estimators=100, n_jobs=-1, criterion='entropy'),
          ExtraTreesClassifier(n_estimators=100, n_jobs=-1, criterion='gini'),
          ExtraTreesClassifier(n_estimators=100, n_jobs=-1, criterion='entropy'),
          GradientBoostingClassifier(learning_rate=0.05, subsample=0.5, max_depth=6, n_estimators=50)]

  print('Building the model......')
  dataset_blend_train = np.zeros((X.shape[0], len(clfs)))
  dataset_blend_test = np.zeros((X_test.shape[0], len(clfs)))
  test_matrix = np.zeros((len(y_test) , 5))




  for j, clf in enumerate(clfs):
      print (j, clf)
      clf.fit(X[j*700 : (j+1)*700] , y[j*700 : (j+1)*700])
      predict = clf.predict_proba(X_test)[: , 1]
      predict1 = []
      for i in range(len(predict)):
          if predict[i] > 0.5:
              predict1.append(1)
          else:
              predict1.append(0)
          pass
      accuracys.append(calculate_accuracy(predict1 , y_test))
      test_matrix[: , j] = predict

  predictions = []
  for i in range(len(test_matrix)):
      if test_matrix[i].mean() > 0.5:
          predictions.append(1)
      else:
          predictions.append(0)

  accuracys.append(calculate_accuracy(predictions , y_test))
  indexs_name = ['RandomForest','RandomForest','ExtraTrees_gini','ExtraTrees_entropy' , 'GradientBoosting','Average']
  draw(accuracys , indexs_name)

选择的model分别是randomforest,extraTrees和FradientBoosting,这就是选择gt的步骤,之后就是用不同的训练集去训练他们,5个model那就把数据分成5类,不同的数据训练不同模型,综合结果。 工具函数:

def calculate_accuracy(predictions , y_test):
  sum = 0
  for i in range(len(y_test)):
      if predictions[i] == y_test.tolist()[i]:
         sum += 1
  print("accuracy :",(sum/len(y_test))*100 , '%')
  return (sum/len(y_test))*100

def draw(accuracys , indexs_name):
  fig, ax = plt.subplots()
  colors = ['#B0C4DE','#6495ED','#0000FF','#0000CD','#000080','#191970']
  ax.bar(range(len(accuracys)), accuracys, color=colors, tick_label=indexs_name)
  plt.xlabel('ModelName')
  plt.ylabel('Accuracy')
  ax.set_xticklabels(indexs_name,rotation=30)
  plt.show()
  pass

最后画柱形图:

平均下来的还是很高的,符合上面的avg(Eout(gt)) > E(avg(gt))的结果。

⑸Linear Blending and Any Blending

之前讲的是平均的思想,先要提的是linear Blending,对应的就是 non-uniformly:

那么主要是怎么找到最好的α,可以应用之前的误差最小化的思想:

和之前的线性模型差不多,可以用梯度下降或者牛顿法求解,但是区别就是α有存在限制。所以linear blending = 线性模型 + feature transform + 限制 事实上对于α这个值的正反其实可以不理会:

当α < 0,有|α|(-g(x)),意思就是让我们反着用,对的当错的,错的当对的。α > 0就一切照常使用。 对于Any Blending,其实就是stacking,堆叠模型,一层一层用不同的模型进行训练。

⑹Bagging(Boostrap Aggregation)

对于Blending,无论是Uniform的还是Non-Uniform或者是conditional,都是先得到gt,也就是先用不同的数据训练好gt之后在进行的aggregate,那么我们能否一边做aggregation一边做getting gt呢?

问题来了,如何得到不同的g。在blending中是使用不同的model来得到不同的g,这是一种方法。①使用不同的model。②同一model不同参数。③algorithm本身就带有随机性,比如kmean,EM这种对于初值很敏感的算法,PLA本身这种比较随机的。④数据随机,不同的数据训练模型,可以相同模型也可以不同模型。

回顾一下上一节讲到的一个演算法可以有两部分构成:

那如何利用已有的一份数据集来构造出不同的gt呢?首先,我们回顾一下之前介绍的bias-variance,即一个演算法的平均表现可以被拆成两项,一个是所有gt的共识(bias),一个是不同gt之间的差距是多少(variance)。其中每个gt都是需要新的数据集的。只有一份数据集的情况下,如何构造新的数据集? 其中g杆是在T趋于infinite的时候不同gt计算得到的平均值,为了得到g杆,我们需要构造两个近似条件: ①有限的T ②由已有数据集D构造出Dt PN,独立同分布

第一个条件是可以完成的,第二个条件使用boostrap做法。假设我们有N笔资料,选出一个样本,记录下,再放回去,再选择一个样本,再放回去,重复N次。这样我们就得到了新的一笔资料,这笔资料里面有可能有重复的也有可能没有原数据里面的某些样本。而抽取放回操作不一定是要N次。

有一个实际的例子: 下面举个实际中Bagging Pocket算法的例子。如下图所示,先通过bootstrapping得到25个不同样本集,再使用pocket算法得到25个不同的gt,每个pocket算法迭代1000次。最后,再利用blending,将所有的gt融合起来,得到最终的分类线,如图中黑线所示。可以看出,虽然bootstrapping会得到差别很大的分类线(灰线),但是经过blending后,得到的分类线效果是不错的,则bagging通常能得到最佳的分类模型。

⑺Bagging的代码实现

实现主要的Bagging包: 就是一个类:

class Bagging(object):

所有有关于Bagging的方法都会在这里。 首先是初始化方法:

  def __init__(self ,n_estimators , estimator , rate = 1.0):
      self.n_estimators = n_estimators
      self.estimator = estimator
      self.rate = rate
      pass

有多少个基础模型,模型是哪几个,如果是下采样那么采样率是多少。然后就是投票函数了,因为得到的结果是横着的,横坐标是model纵坐标是prediction,所有要transpose一下,取出每一行看看是1多还是0多,然后取最多的返回即可。

  def Voting(self , data):
      term = np.transpose(data)
      result = list()
      def Vote(df):
          store = defaultdict()
          for kw in df:
              store.setdefault(kw , 0)
              store[kw] += 1
          return max(store , key=store.get)
      result = map(Vote , term)
      return result

下采样,如果是用下采样的话就可以调用这个函数:

  def UnderSampling(self,data):
      data = np.array(data)
      np.random.shuffle(data)
      newdata = data[0:int(data.shape[0]*self.rate),:]
      return newdata
      pass

刚刚采样的rate就有用了。 训练预测,这些别人已经写好了,不用多写: 重心是在于Bagging模型本身,这些算法在后面讲到的时候会详细说到。

  def TrainPredict(self , train , test):
      clf = self.estimator.fit(train[: , 0:-1] , train[: , -1])
      result = clf.predict(test[: , 0:-1])
      return result
      pass

然后就是boostrap采样了:

  def RepetitionRandomSampling(self , data , number):
      samples = []
      for i in range(int(self.rate * number)):
          samples.append(data[random.randint(0, len(data) - 1)])
          pass
      return samples
      pass

这个就是一直随机基于好了。

  def Metrics(self, predict_data , test):
      score = predict_data
      pre = np.matrix(test[: , -1])
      score = list(score)
      score = np.matrix(score)
      recall = recall_score(pre.T , score.T, average=None)
      precision = accuracy_score(pre.T, score.T)
      return recall , precision
      pass

估计模型效果,recall,metric,F1,accuracy,precision这些以后会有详细的篇章会讲到。

  def MutModel_clf(self , train , test , sample_type = 'RepetitionRandomSampling'):
      result = list()
      sample_function = self.RepetitionRandomSampling
      num_estimators = len(self.estimator)
      if sample_type == "RepetitionRandomSampling":
          print('Sample type : ' , sample_type)
      elif sample_type == "UnderSampling":
          print('Sample type : ' , sample_type)
          sample_function = self.UnderSampling
          print ("sampling frequency : ",self.rate)
      for estimator in self.estimator:
          print (estimator)
          for i in range(int(self.n_estimators/num_estimators)):
              sample=np.array(sample_function(train,len(train)))
              clf = estimator.fit(sample[:,0:-1],sample[:,-1])
              result.append(clf.predict(test[:,0:-1]))

      score = self.Voting(result)
      recall,precosoion = self.Metrics(score,test)
      return recall,precosoion

只会就是主函数了。

之后就到了数据处理了,因为使用的数据还是上一次blending使用的数据,所以要把label放到最后,做一下数据处理:

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

def MoveActivity(fileName , location , saveName):
  dataframe = pd.read_csv(location + fileName)
  activity = dataframe['Activity']
  dataframe.drop(labels=['Activity'], axis=1,inplace = True)
  dataframe.insert(len(dataframe.columns.values) , 'Activity', activity)
  dataframe.to_csv(location + saveName,index=False)
  pass

if __name__ == '__main__':
  MoveActivity(fileName='train.csv' , location='../../Data/' , saveName='newtrain.csv')
  MoveActivity(file

生成一个新的表格CSV。

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import MachineLearning.AggregationModel.Bagging.bagging as bagging
from sklearn.model_selection import StratifiedKFold
from sklearn.ensemble import RandomForestClassifier , ExtraTreesClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
def loadData():
  train = pd.read_csv('../../Data/newtrain.csv')
  test = pd.read_csv('../../Data/newtest.csv')
  train = train.as_matrix()
  test = test.as_matrix()
  return train , test
  pass

def Running():
  train , test = loadData()
  clfs = [RandomForestClassifier(n_estimators=100, n_jobs=-1, criterion='gini'),
          RandomForestClassifier(n_estimators=100, n_jobs=-1, criterion='entropy'),
          ExtraTreesClassifier(n_estimators=100, n_jobs=-1, criterion='gini'),
          ExtraTreesClassifier(n_estimators=100, n_jobs=-1, criterion='entropy'),
          GradientBoostingClassifier(learning_rate=0.05, subsample=0.5, max_depth=6, n_estimators=50)]
  bag = bagging.Bagging(n_estimators=5 , estimator=clfs)
  recall , precision = bag.MutModel_clf(train , test)
  print(recall , precision)
  pass

if __name__ == '__main__':
  Running()

使用的模型和上一个blending的是一样的,便于对比。最后做了一下对比:

if __name__ == '__main__':
  pres1 = []
  pres2 = []

  for i in range(10):
      pre = Running(sample='UnderSampling')
      pres2.append(pre)

  for i in range(10):
      pre = Running()
      pres1.append(pre)


  plt.plot([x for x in range(10)] , pres1 , c = 'r')
  plt.plot([x for x in range(10)] , pres2 , c = 'b')
  plt.show()

效果对比:

红色的线是boostrap采样,蓝色的线是下采样,其实也不是下采样,因为刚刚忘记设置rate了,所以默认是1.......。总体效果还是比blending要好的。 GitHub上代码:

https://github.com/GreenArrow2017/MachineLearning/tree/master/MachineLearning/AggregationModel/Bagging

⑻Adaptive Boosting

继续Aggregation Model,最后一个集成算法——Adaptive Boosting,和前面一样都是监督式的学习。举一个简单的例子:老师要求学生来识别一些苹果,上面的是苹果下面的是其他东西。一个学生说,圆形的是苹果,于是成功的识别了一些,但是也有错误的。于是有了下图:

既然有正确分类的了,那么应该要把注意力放在错误的地方上,其他算法也是这样,logistics linear SVM都是minimize lost function。于是另一个学生又提出来了,红色的才是苹果,于是有:

但是错误的几个里面,绿色的也可以是苹果,于是另一个学生又说绿色的是苹果,然后又有:

还是有错,于是另一个同学又说有梗的才是苹果,然后:

这下就正确了,经过这几个同学的推论,苹果就是园的,红色或者绿色,有梗,虽然可能不是非常准确,但是要比单一的条件要好得多。也就是说把所有学生对苹果的定义融合起来,最终得到一个比较好的对苹果的总体定义。这个过程就是boosting,一开始的单个分类器,也就是一个同学是弱分类器,然后boosting主要就是集中多个弱分类器把它变成强的分类器。 在上面的例子里面,老师就像是演算法,学生就像hypothesis 的g,苹果的总定义就是G,老师的作用有两个,一个是选择学生,第二个就是指导方向,使得它们的注意力集中在错误上面。

①Diversity by Re-weighting

介绍这个algorithm之前先来看一下之前的bagging,bagging的抽样方法是boostrap抽样得到一个和原始数据类似的数据D1,然后训练gt,之后进行aggregation操作。假设我们现在有D个样本,进行了一次boostrap抽样操作:

那么对于一个新的抽样得到的数据D1,把他交给一个算法,让他计算:

可以看到抽样出来x1有两个,既然是两个的x1的,那就说明x1只要犯了错误那么就相当于是两个x1犯了错误,说明它做错的程度要更深一些,于是改进一下,增加一个权值:

所以当D1里面出现次数越多的样本,他对应的u就是越大,也就是说他的惩罚会越厉害,其实就是通过bootstrap的方式,来得到这些ui值,作为犯错样本的权重因子,再用 algorithn最小化包含ui的error function,得到不同的gt。这个error function被称为bootstrap-weighted error。

这种算法形式就和之前的SVM差不多,在最后的soft margin里面,我们转换过ξ,他其实就是犯错误的多少,所以:

和之前的SVM不一样的是,这里的u相当于每个犯错的样本的惩罚因子,并会反映到α的范围限定上。 同样在logistics中也是一样的:

所以,知道了u的概念之后,我们是可以通过u来选择不同的gt的,意味我们是可以在相同的模型中通过参数不同来选择不同的gt。结果是要使得gt互相之间都很不相同,因为之前讲过越不同的gt想要找到他们的consensus就越难,不同的gt也会涉及不同的分类方式,所以效果也会越好,相当于是regularization和feature transform了。

问题来了,如何得到不同的g(t)和g(t+1)?按照上面的讲述:

g(t)是u(t+1)产生的,而g(t+1)是u(t+1)产生的,所以如果当g(t)用u(t+1)的时候效果很差时,就表明g(t)和g(t+1)是很差的了。于是我们利用随机效果,比如一枚硬币,随机的时候两面都是0.5,如果有:

那么就表明相差很大,因为gt对于预测分类是没有什么影响的了,已经是随机了,所以这个时候差异是最大的。 对于上面那个式子不太好求解,变换一下:

犯错误的点和没有犯错误的点用橙色和绿色表示,要让这个fraction是0.5,错误和正确的一样就可以了,所以,错误的都乘上正确的数量,正确的都乘上错误的数量,600张是对的,200张错的,那么错的都乘600对的都乘200就可以了。或者我们可以乘上分数,3/4和1/4。所以计算u(t+1)就可以乘上1-ε和ε了。

②Adaptive Boosting Algorithm

现在进入了真正的Adaboost。对于1-ε和ε这些还是有点麻烦,引入一个新的尺度:

对于正确的类别将会除以◇t,对于错误的将会乘上◇t。效果是一样的,之所以这样做是因为它代表了更多的physical significance;当ε < 1/2,◇t > 1,所以正确的除去◇t,就是在削弱正确类别的重要性,错误类别乘上就是增加它的权值,增加它的重要性,对于minimize影响这么大algorithm肯定会重视,于是就会花更多的力气来处理权值大的,这就是像刚刚的例子里面,老师指导方向的效果了。

从这里开始,我们就可以得到一个初步的演算法了:

初始值暂时不知道,①通过u(t)得到gt②利用gt更新u(t)到u(t+1)。 对于u的初始值,一开始可以都是平均的,或者可以用刚刚的boostrap抽样来决定。G(x)可以使用之前的linear blending算法,设置一个参数α作为gt的权重:

对于α,构造一个和上面◇t 相关的一个式子:

于是算法完整了:

α这样取值是有物理意义的,例如当ϵ=1/2时,error很大,跟掷骰子这样的随机过程没什么两样,此时对应的⋄t=1,α=0,即此gt对G没有什么贡献,权重应该设为零。而当ϵt=0时,没有error,表示该gt预测非常准,此时对应的⋄t=∞,α=∞,即此gt对G贡献非常大,权重应该设为无穷大。

由三部分构成:base learning algorithm A,re-weighting factor ⋄t和linear aggregation αt。这三部分分别对应于我们在本节课开始介绍的例子中的Student,Teacher和Class。

base algorithm:对应基础算法模型,比如决策树,SVM,logistics这些。 re-weighting factor :对应的就是u的更新了。 linear aggregation:就是最后的G,综合gt的意见。 完整的算法流程:


③对于AdaBoost可行性的理解:

对这个VC bound中的第一项Ein(G)来说,有一个很好的性质:如果满足ϵt ≤ ϵ < 12,则经过T=O(log N)次迭代之后,Ein(G)能减小到 = 0的程度。而当N很大的时候,其中第二项也能变得很小。因为这两项都能变得很小,那么整个Eout(G)就能被限定在一个有限的上界中。dvc(H)这部分是base algorithm的复杂度,自然是不会太大了,基础算法就是弱分类器就行了,boost要做正是把弱分类器变强。T(logT)是迭代次数,这个是实践的时候得到的结论。logN/N就是样本数目了。迭代次数通常不会太久,数据大小也可以控制,所以综上来看,AdaBoost可以做到很好的效果缺不会太过拟合,这也就印证了之前regularization和feature transform的理解。

所以只要每一次ϵ ∈(ϵt , 1/2),也就是每一次都可以比随机好一点点,那么多次迭代就可以得到很好的结果了。Ein很小,Eout也不会差到哪里去。

⑼Adaptive Boosting in Action

看一下实际上的效果,待会会有代码的实现。 首先拿一些样本做切割:

迭代多几次之后就可以分开了。

⑽AdaBoost代码实现

⒈实现弱分类器decision-stump

实现Adaboost需要用到弱分类器,这个弱分类器使用的是decision-stump。这是一个单分类器,也叫单决策树,它会遍历所有数据的维度,然后找到最小的Ein在哪个维度上,那么哪个维度的那个线就是最好的分类。和上面的几个图是一样的,只能是垂直或者是竖直分,是一直弱分类器。它主要的过程就是遍历所有的数据维度,然后寻找一个合适的thresh作为分割,再看看是左边的作为positive好还是右边的作为positive好。从实际意义来看,decision stump 根据一个属性的一个判断就决定了最终的分类结果,比如根据水果是否是圆形判断水果是否为苹果,这体现的是单一简单的规则(或叫特征)在起作用。显然 decision stump 仅可作为一个 weak base learning algorithm(它会比瞎猜 1/2 稍好一点点,但好的程度十分有限),常用作集成学习中的 base algorithm,而不会单独作为分类器。 所以要优化的function:

所以要先实现一个decision-stump:

def decision_stump(dataMat , Labels , u):
  dataMat = np.mat(dataMat)
  Labels = np.mat(Labels).T
  n , d = dataMat.shape
  numSteps = 10.0
  bestSump = {}
  bestClasEst = np.mat(np.zeros((n , 1)))
  minError = np.inf
  for i in range(d):
      rangeMin = dataMat[: , i].min()
      rangeMax = dataMat[: , i].max()
      stepSize = (rangeMax - rangeMin) / numSteps
      for j in range(-1 , int(numSteps) + 1):
          for inequal in ['lt' , 'gt']:
              threshVal = (rangeMin + np.float32(j) * stepSize)
              predictedVals = stumpClassify(dataMat , i , threshVal , inequal)
              errArr = np.mat(np.ones((n , 1)))
              errArr[predictedVals == Labels] = 0
              weightedError = u.T * errArr

              if weightedError < minError:
                  minError = weightedError
                  bestClasEst = predictedVals.copy()
                  bestSump['dim'] = i
                  bestSump['thresh'] = threshVal
                  bestSump['inseq'] = inequal
  return bestSump, minError, bestClasEst

传入u参数是为了后面的兼容Adaboost的weightError,错误需要增加权重。按照上面的function解释一下功能:

①确定thresh,也就是确定阈值的范围,这里设置的是10个阈值,也就是每一个维度我按照10个来划分,迭代12次,找到最好的一个阈值。12次是因为最左和最右边各有一次。 ②迭代dimension,开始寻找每一个维度是否是最小的Ein。 ③迭代thresh,实际上就是迭代每一个维度上的12个thresh值,看看哪一个达到最小。 ④如果thresh的positive和negative是反过来的也有可能造成很大的错误,所以看看哪边是positive或者是negative可以达到最好的效果,所以迭代lt和gt。 ⑤最后得到是所有分类的1,-1标签,乘上每一个样本的权值u,就得到最终的error了。

thresh的decision-stump分类function:

def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):  # just classify the data
  retArray = np.ones((np.shape(dataMatrix)[0], 1))
  if threshIneq == 'lt':
      retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
  else:
      retArray[dataMatrix[:, dimen] > threshVal] = -1.0
  return retArray

其实就是调换一下positive和negative的表示方式。

⒉实现Adaboost

接下来就是主要的running function,Adaboost了:

def Running(Number , numIt):
  dataX , dataY = getData.get_Data(Number)
  weakClassArr = []
  n = dataX.shape[0]
  u = np.mat(np.ones((n , 1)) / n)
  aggClassEst = np.mat(np.zeros((n , 1)))
  getData.draw(dataX , dataY , Number , [])
  for i in range(numIt):
      bestSump, error, classEst = decision_stump(dataX, dataY, u)
      alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))
      bestSump['alpha'] = alpha
      weakClassArr.append(bestSump)
      expon = np.multiply(-1 * alpha * np.mat(dataY).T, classEst)
      u = np.multiply(u , np.exp(expon)) / np.sum(u) #if miss the normalization,the u will be bigger than bigger , and the thresh is unusable.
      aggClassEst += alpha * classEst
      aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(dataY).T, np.ones((n, 1)))
      errorRate = aggErrors.sum() / n
      precision = np.multiply(np.sign(aggClassEst) == np.mat(dataY).T, np.ones((n, 1)))
      precision = (sum(precision) / n) * 100

      if i % 10 == 0:
          if precision == 100.0:
              break
          print('precision : ',precision)
          getData.draw(dataX , np.sign(aggClassEst) ,200, weakClassArr)
  return weakClassArr, aggClassEst

其实就是按照上面所讲的不找来写的。 实现步骤:

①首先是得到数据了,初始化一些参数等等,比如u,n还有多个弱分类器分类结果相加得到的aggClassEst。 ②迭代,每一个迭代就会产生一个弱分类器,首先先用刚刚写的decision-stump得到一个弱分类器,α的更新公式:

把1/2提出来也是一样的。 ③这个分类器算是完成了,把它加入到集合里面。 ④对于u的更新:

整合一下,其实就是ue^(-αf(x)h(x)),上面的公式是可以化简成这样的。最后还有进行一个u的归一化,林轩宇老师上面的是没有进行归一化的,如果不进行归一化也可以,但是可能错误的权值会增加的很快,正确的权值就会增加的很慢,因为后面的分类结果是要multiple u然后相加的,这样就是会导致,可能你这个点在上一次分类是正确的,u有点小,但是这一次错了,一乘上结果u就很大了,一加起来就很大,会导致有些个别的点波动幅度会很大,最后不一定用-1 / 1这些阈值可以分开了,分类效果很受影响。归一化的一个好处就是可以把错误都限制在-1 / 1之间,最后还是可以用1.-1区分。 ⑤最后就是一些错误的相加了,计算准确率和错误率。

⒊最后一个就是数据的准备了

生成一个sin的数据,就是要生成这种百分之80都是对的,看看剩下那20能不能分对,做到feature transform的效果。

import numpy as np
import random
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
def get_Data(Number):
  x = np.zeros((2*Number , 2),dtype = np.float32)
  y = np.zeros(2*Number , dtype = np.float32)
  t = np.linspace(0 , np.pi * 2 , Number)
  for i in range(Number):
      x[i] = np.c_[t[i] , np.sin(t[i]) - np.random.random() * 3]
      y[i] = 1
      pass
  for i in range(Number):
      x[Number+i] = np.c_[t[i] , np.sin(t[i]) + np.random.random() * 3]
      y[Number+i] = -1
  return x , y
  pass

def draw(X, Y, Number , line):
  for i in range(2*Number):
      if Y[i] == 1:
          plt.scatter(X[i,0], X[i, 1], color='r', marker='x')
      else:
          plt.scatter(X[i, 0], X[i, 1], color='b', marker='<')

      pass

  for l in line:
      if l['dim'] == 0:
          plt.plot(8*[l['thresh']] , np.linspace(0, 8, 8) , color = 'gray' , alpha = 0.5)
      else:
          plt.plot(np.linspace(0, 6.5, 6), 6 * [l['thresh']], color = 'gray', alpha=0.5)
      pass
  plt.title('DataSet')
  plt.xlabel('First Dimension')
  plt.ylabel('Second Dimension')
  plt.show()
  pass

if __name__ == '__main__':
  x , y = get_Data(200)
  draw(x, y, 200 , [])

效果200个点:

4.效果对比

讲道理,这东西应该是可以做到正确率百分之100的。但是:

看看效果:

一个弱分类器的时候

10个的时候

20个的时候

30个的时候

我后面又迭代了几次,讲道理,这我就有点虚了。 后来我又找了一下原因,numSize = 10,是不是这个decision stump分类能力太弱了,Adaboost虽然点名要弱的,但是这个散打比赛太弱了。于是改到40,然后就行了...... 起最后的效果图(事实上改到40还是不行,到99.75%就停了,改到60之后就OK了):

这样效果就有100%了,当然实际上这样会过拟合,但是这只是测试而已。 aggregation model完结!

所有代码在GitHub上: https://github.com/GreenArrow2017/MachineLearning/tree/master/MachineLearning/AggregationModel/Adaboost

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.06.26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ⑴Motivation of Aggregation
  • ⑵Blending
  • ⑶Uniform Blending
  • ⑷uniform-blending的代码实现
  • ⑸Linear Blending and Any Blending
  • ⑹Bagging(Boostrap Aggregation)
  • ⑺Bagging的代码实现
  • ⑻Adaptive Boosting
  • ⑼Adaptive Boosting in Action
  • ⑽AdaBoost代码实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档