文章目录

  • 说明
  • 1. Day63-65 AdaBoosting算法
    • 1 AdaBoostin举例
      • 1.1数据样本
      • 1.2 举例过程
    • 2. 理论知识
    • 3. 总结
  • 2. 代码理解
    • 1. WeightedInstances类
    • 2. 选择基分类器并进行训练(树桩分类器)
    • 3. 计算误差率和误差系数(树桩分类器)
    • 4. 计算精确度
    • 5. 总结
  • 3. java知识
    • 1. 抽象类
    • 2. static关键字
    • 3. 静态代码块

说明

闵老师的文章链接: 日撸 Java 三百行(总述)_minfanphd的博客-CSDN博客
自己也把手敲的代码放在了github上维护:https://github.com/fulisha-ok/sampledata

1. Day63-65 AdaBoosting算法

1 AdaBoostin举例

AdaBoost算法是一种集成学习算法,是Boosting算法中的一种,通过组合多个弱分类器来构建一个强分类器。因为我也是第一次接触这个算法,直接去看一些算法以及公式,感觉还是有点吃力,所以我结合网上看的例子,自以及己也模拟一个例子先手动过一遍这个算法过程,然后再去学习他的理论知识。(其中的计算结果我是通过文章的代码计算所得的),如果遇到一些概念不懂,我们先假装自己懂,看完例子再去看理论,于我而言,蛮有用的。(若有问题,欢迎指正~)

1.1数据样本

我把原来iris.aff文件的150个缩减为12个数据,数据如下:一共有4种特征,3个类别,12个数据集。

@RELATION iris@ATTRIBUTE sepallength   REAL
@ATTRIBUTE sepalwidth  REAL
@ATTRIBUTE petallength     REAL
@ATTRIBUTE petalwidth  REAL
@ATTRIBUTE class   {Iris-setosa,Iris-versicolor,Iris-virginica}@DATA
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
5.4,3.9,1.7,0.4,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.9,3.1,4.9,1.5,Iris-versicolor
5.5,2.3,4.0,1.3,Iris-versicolor
6.5,2.8,4.6,1.5,Iris-versicolor
5.7,2.8,4.5,1.3,Iris-versicolor
6.3,3.3,6.0,2.5,Iris-virginica
5.8,2.7,5.1,1.9,Iris-virginica
7.1,3.0,5.9,2.1,Iris-virginica
6.3,2.9,5.6,1.8,Iris-virginica
6.5,3.0,5.8,2.2,Iris-virginica
7.6,3.0,6.6,2.1,Iris-virginica

1.2 举例过程

先初始化这12个数据集的权重: 1 N \frac{1}{N} N1​

索引 0 1 2 3 4 5 6 7 8 9 10 11
权重 0.083 0.083 0.083 0.083 0.083 0.083 0.083 0.083 0.083 0.083 0.083 0.083

由于我们有三个类别,对于属性的选择我们都是随机选择属性来进行训练。我们的基学习器采用的是树桩分类器。(树桩分类器是决策树的一种特殊形式,它只包含一个根结点和两个叶子结点,相当于把数据按一个阈值一分为二,基类学习器还有我们之前学的KNN,贝叶斯算法等。)
前提:我们在每次学习的时候,基类学习器G(x)的值我们用+1来表示与我们预期相符合(正类别);-1来表示与我们预期不符合(负类别)

  • 第一个弱学习器(基类学习器)
    随机选择的属性为sepallength
索引 0 1 2 3 4 5 6 7 8 9 10 11
属性 5.1 4.9 4.7 4.6 7.0 6.4 6.9 5.5 6.3 5.8 7.1 6.3
权重w1 0.083 0.083 0.083 0.083 0.083 0.083 0.083 0.083 0.083 0.083 0.083 0.083
正确结果 y 1 ( x ) y_{1}(x) y1​(x) 0 0 0 0 1 1 1 1 2 2 2 2
预测结果 G 1 ( x ) G_{1}(x) G1​(x) 0 0 0 0 1 1 1 1 1 1 1 1

我们的最佳分割点bestCut=5.3; leftLeafLabel = 0 ; rightLeafLabel = 1;
误差率 e 1 = 0.083 + 0.083 + 0.083 + 0.083 = 0.3333 e1=0.083 + 0.083 + 0.083 + 0.083 = 0.3333 e1=0.083+0.083+0.083+0.083=0.3333
误差系数: α 1 = 1 2 log ⁡ 1 − e 1 e 1 = 0.3465 \alpha _{1}=\frac{1}{2}\log \frac{1-e_{1}}{e_{1}}=0.3465 α1​=21​loge1​1−e1​​=0.3465
训练数据的准确率:0.6667
弱学习器 G 1 ( x ) G_{1}(x) G1​(x): G 1 ( x ) = { 0 , x < 5.3 1 , x > 5.3 G_{1}(x)= \begin{cases} 0, & \text {x < 5.3} \\ 1, & \text{x > 5.3} \end{cases} G1​(x)={0,1,​x < 5.3x > 5.3​

  • 第二个弱学习器(基类学习器)
    随机选择的属性为petallength
索引 0 1 2 3 4 5 6 7 8 9 10 11
属性 1.4 1.4 1.3 1.5 4.7 4.5 4.9 4.0 6.0 5.1 5.9 5.6
权重w2 0.062 0.062 0.062 0.062 0.062 0.062 0.062 0.062 0.125 0.125 0.125 0.125
正确结果 y 2 ( x ) y_{2}(x) y2​(x) 0 0 0 0 1 1 1 1 2 2 2 2
预测结果 G 2 ( x ) G_{2}(x) G2​(x) 0 0 0 0 2 2 2 2 2 2 2 2

我们的阈值取值为: 2.75; leftLeafLabel = 0 ; rightLeafLabel = 2;
误差权重之和 e 2 = 0.062 + 0.062 + 0.062 + 0.062 = 0.249 e2=0.062 + 0.062 + 0.062 + 0.062 = 0.249 e2=0.062+0.062+0.062+0.062=0.249
误差系数: α 2 = 1 2 log ⁡ 1 − e 2 e 2 = 0.5493 \alpha _{2}=\frac{1}{2}\log \frac{1-e_{2}}{e_{2}}=0.5493 α2​=21​loge2​1−e2​​=0.5493
训练数据的准确率:0.6666
弱学习器 G 2 ( x ) G_{2}(x) G2​(x): G 2 ( x ) = { 0 , x < 2.75 2 , x >2.75 G_{2}(x)= \begin{cases} 0, & \text {x < 2.75} \\ 2, & \text{x >2.75} \end{cases} G2​(x)={0,2,​x < 2.75x >2.75​

  • 第三个弱学习器(基类学习器)
    随机选择的属性为petalwidth
索引 0 1 2 3 4 5 6 7 8 9 10 11
属性 0.2 0.2 0.2 0.2 1.4 1.5 1.5 1.3 2.5 1.9 2.1 1.8
权重w3 0.041 0.041 0.041 0.041 0.125 0.125 0.125 0.125 0.083 0.083 0.083 0.083
正确结果 y 3 ( x ) y_{3}(x) y3​(x) 0 0 0 0 1 1 1 1 2 2 2 2
预测结果 G 3 ( x ) G_{3}(x) G3​(x) 1 1 1 1 1 1 1 1 2 2 2 2

我们的阈值取值为:1.65; leftLeafLabel = 1 ; rightLeafLabel = 2;
误差权重之和 e 3 = 0.041 + 0.041 + 0.041 + 0.041 = 0.166 e3=0.041 + 0.041 + 0.041 + 0.041 = 0.166 e3=0.041+0.041+0.041+0.041=0.166
误差系数: α 3 = 1 2 log ⁡ 1 − e 3 e 3 = 0.8047 \alpha _{3}=\frac{1}{2}\log \frac{1-e_{3}}{e_{3}}=0.8047 α3​=21​loge3​1−e3​​=0.8047
训练数据的准确率:1.0
弱学习器 G 3 ( x ) G_{3}(x) G3​(x): G 3 ( x ) = { 1 , x < 1.65 2 , x > 1.65 G_{3}(x)= \begin{cases} 1, & \text {x < 1.65} \\ 2, & \text{x > 1.65} \end{cases} G3​(x)={1,2,​x < 1.65x > 1.65​

我针对第三次的学习来计算他们的准确率:
假设给出的实例:5.1,3.5,1.4,0.2,Iris-setosa
他在第一次基类学习器中预测类别为0: 则第一个类别累加误差系数 0.3465
他在第二次基类学习器中预测类别为0: 则第一个类别累加0.5493后为0.8958
他在第一次基类学习器中预测类别为1: 则第二个类别为:0.8047
故第一个实例的预测类别概率为:[0.8958797346140277, 0.8047189562170501, 0.0]可知预测的为类别0

其他11个实例依次预测的类别为
[0.8958797346140277, 0.8047189562170501, 0.0]
[0.8958797346140277, 0.8047189562170501, 0.0]
[0.8958797346140277, 0.8047189562170501, 0.0]
[0.0, 1.1512925464970227, 0.549306144334055]
[0.0, 1.1512925464970227, 0.549306144334055]
[0.0, 1.1512925464970227, 0.549306144334055]
[0.0, 1.1512925464970227, 0.549306144334055]
[0.0, 0.34657359027997264, 1.3540251005511053]
[0.0, 0.34657359027997264, 1.3540251005511053]
[0.0, 0.34657359027997264, 1.3540251005511053]
[0.0, 0.34657359027997264, 1.3540251005511053]

结合上面的例子,就可以来学习理论知识了:

2. 理论知识

算法步骤(我参考了网上的帖子,这里梳理出来是为了方便以后回顾)

  • 1. 假设训练集样本是这样的:
    T = ( x 1 , y 1 ) , ( x 2 , y 2 ) . . . . . ( x m , y m ) T={({x_{1}, y_{1}}), ({x_{2}, y_{2}})..... ({x_{m}, y_{m}})} T=(x1​,y1​),(x2​,y2​).....(xm​,ym​)

y i {y_{i}} yi​ 的取值如果在一个二分类问题中,可以取值+1和-1,在多类别问题中,要根据情况而定。如我们在上面的例子中,表示样本 x i x_{i} xi​所属的类别

  • 2. 第m个弱学习器的各个数据的权重是
    D k = ( w m 1 , w m 2 , w m 3 . . . . w m i ) D_{k}=(w_{m1},w_{m2},w_{m3}....w_{mi}) Dk​=(wm1​,wm2​,wm3​....wmi​)
    其中初始化的权重为如下:N为样本数量
    D 1 = ( w 11 , w 12 , w 13 . . . . ) , w 1 i = 1 N , i = 1 , 2 , 3 , 4... N D_{1}=(w_{11},w_{12},w_{13}....), w_{1i}=\frac{1}{N}, i=1,2,3,4...N D1​=(w11​,w12​,w13​....),w1i​=N1​,i=1,2,3,4...N
  • 3. 第m个学习器 G m ( x ) G_{m}(x) Gm​(x)

G m ( x ) G_{m}(x) Gm​(x)其取值只能有两个(正类别+1和负类别-1),而在多类别问题中,也依情况而定,我在上面例子中去的是具体的类别。

  • 4. G m ( x ) G_{m}(x) Gm​(x)在训练数据集上的误差率(即失败样本数*样本权重之和):
    e m = P ( G m ( x i ) ≠ y i ) = ∑ i = 1 m w m i I ( G m ( x i ) ≠ y i ) e_{m}=P(G_{m}(x_{i})\neq y_{i})=\sum_{i=1}^{m}w_{mi}I(G_{m}(x_{i})\neq y_{i}) em​=P(Gm​(xi​)=yi​)=i=1∑m​wmi​I(Gm​(xi​)=yi​)
    其中 I ( G m ( x i ) ≠ y i ) { I(G_m(x_i) ≠ y_i) } I(Gm​(xi​)=yi​)的值为1,即预测的结果与实际结果不符合

  • 5.计算 G m ( x ) G_{m}(x) Gm​(x)的权重系数:
    (由公式可以看出,当误差率越小的弱分类器,在最后的强分类器中贡献越大)
    α m = 1 2 log ⁡ 1 − e m e m \alpha _{m}=\frac{1}{2}\log \frac{1-e_{m}}{e_{m}} αm​=21​logem​1−em​​
    权重系数在最终分类器中会发挥作用,当值越大对最终的影响就较大;同时权重系数还会影响下一次权重更新。

  • 6.更新权重的公式:
    已知: D m = ( w m 1 , w m 2 , w m 3 . . . . w m i ) D_{m}=(w_{m1},w_{m2},w_{m3}....w_{mi}) Dm​=(wm1​,wm2​,wm3​....wmi​)的权重,现在计算第m+1个样本集的权重

w m + 1 , i = w m i Z m e x p ( − α m y i G m ( x i ) ) w_{m+1,i}=\frac{w_{mi}}{Z_{m}}exp(-\alpha _{m}y_{i}G_{m}(x_{i})) wm+1,i​=Zm​wmi​​exp(−αm​yi​Gm​(xi​))
其中我们可以简化公式:
w m + 1 , i = { w m i Z m 1 e α m , G m ( x i ) = y i → ( G m ( x i ) ∗ y i = 1 ) w m i Z m e α m , G m ( x i ) ≠ y i → ( G m ( x i ) ∗ y i = − 1 ) w_{m+1,i} = \begin{cases} \frac{w_{mi}}{Z_{m}}\frac{1}{e^{\alpha_{m}}}, & G_{m}(x_{i})=y_{i} \rightarrow (G_{m}(x_{i})*y_{i}=1)\\ \frac{w_{mi}}{Z_{m}}e^{\alpha_{m}}, & G_{m}(x_{i})\neq y_{i} \rightarrow (G_{m}(x_{i})*y_{i}=-1)\\ \end{cases} wm+1,i​={Zm​wmi​​eαm​1​,Zm​wmi​​eαm​,​Gm​(xi​)=yi​→(Gm​(xi​)∗yi​=1)Gm​(xi​)=yi​→(Gm​(xi​)∗yi​=−1)​
( G m ( x i ) ∗ y i = 1 ) (G_{m}(x_{i})*y_{i}=1) (Gm​(xi​)∗yi​=1)的前提是 G m ( x i ) 和 y i G_{m}(x_{i})和y_{i} Gm​(xi​)和yi​取值在+1和-1,而实际上我觉得这个公式要描述的意思就可以理解为:如果预测结果一致就为1,预测结果不一致为-1。就如例子上面我们有3个类别,并没有按+1,-1去计算,所以要依情况而定,但核心思想不变!其中 Z k Z_{k} Zk​是规范化因子
Z k = ∑ i = 1 m w k i e x p ( − α k y i G k ( x i ) ) Z_{k}=\sum_{i=1}^{m}w_{ki}exp(-\alpha_{k}y_{i}G_{k}(x_{i})) Zk​=i=1∑m​wki​exp(−αk​yi​Gk​(xi​))
从上面的是公式,我们可以知道在计算 w m + 1 , i w_{m+1,i} wm+1,i​ 的值时, α m \alpha _{m} αm​的值肯定是大于0的 则 e α m e^{\alpha _{m}} eαm​的值一定是大于1,当我们在第m+1次更新权重时,如果在第m次样本被正确分类( ( G m ( x i ) = y i (G_{m}(x_{i})=y_{i} (Gm​(xi​)=yi​),则他在第m+1次时权重就会变小,而若被错误分类( G m ( x i ) ≠ y i G_{m}(x_{i})\neq y_{i} Gm​(xi​)=yi​),则在第m+1次时权重就会变大 (因为在上一次已经被错误分类了,那么我在这一次的分类中我需要更重视错误分类的,所以就把权重要调大!)

3. 总结

结合上面的例子和公式,去看这个图就非常的生动,我们训练多个弱分类器(串行的),每一次弱分类器中数据的权重都是根据上一次的权重,误差概率和误差系数来更新,经过多次训练后我们最终形成一个最终的强分类器,我们预测类别是就通过计算多个弱分类器的一个加权和来预测那个类别概率最大(并行的)

AdaBoosting算法在每一次的弱分类器的训练中,实际上是一个二分类的问题。但是我们在上面的例子中,他实际上是一个多类别问题,那我们也可以通过这个算法来实现。我们的实现是对每个基本分类器都选择两个类别。在预测阶段,通过对这些基本分类器的预测结果进行投票,选择概率最大的。

2. 代码理解

我从Booster类去理解代码。下面是Booster的main方法,而最核心的东西就是train()方法,其中我们设置了训练的次数是20个。

下面是train的核心代码

public void train() {// Step 1. Initialize.WeightedInstances tempWeightedInstances = null;double tempError;numClassifiers = 0;SimpleTools.processTrackingOutput("Booster.train() Step 1\r\n");// Step 2. Build other classifiers.for (int i = 0; i < classifiers.length; i++) {Common.runSteps ++;// Step 2.1 Construct or adjust the weightedInstancesif (i == 0) {tempWeightedInstances = new WeightedInstances(trainingData);} else {// Adjust the weights of the data.tempWeightedInstances.adjustWeights(classifiers[i - 1].computeCorrectnessArray(),classifierWeights[i - 1]);}SimpleTools.processTrackingOutput("Booster.train() Step 2.1\r\n");// Step 2.2 Train the next classifier.switch (baseClassifierType) {case STUMP_CLASSIFIER:classifiers[i] = new StumpClassifier(tempWeightedInstances);break;case BAYES_CLASSIFIER:classifiers[i] = new BayesClassifier(tempWeightedInstances);break;case Gaussian_CLASSIFIER:classifiers[i] = new GaussianClassifier(tempWeightedInstances);break;default:System.out.println("Internal error. Unsupported base classifier type: " + baseClassifierType);System.exit(0);}classifiers[i].train();SimpleTools.processTrackingOutput("Booster.train() Step 2.2\r\n");// tempAccuracy = classifiers[i].computeTrainingAccuracy();//计算加权错误率tempError = classifiers[i].computeWeightedError();// Set the classifier weight. 弱分类器的权重classifierWeights[i] = 0.5 * Math.log(1 / tempError - 1);if (classifierWeights[i] < 1e-6) {classifierWeights[i] = 0;}// SimpleTools.variableTrackingOutput("Booster.train()");SimpleTools.variableTrackingOutput("Classifier #" + i + " , weighted error = "+ tempError + ", weight = " + classifierWeights[i] + "\r\n");numClassifiers++;// The accuracy is enough. 记录当前训练轮次(迭代)中集成分类器在训练数据上的准确率if (stopAfterConverge) {double tempTrainingAccuracy = computeTrainingAccuray();SimpleTools.variableTrackingOutput("The accuracy of the booster is: " + tempTrainingAccuracy + "\r\n");if (tempTrainingAccuracy > 0.999999) {SimpleTools.processTrackingOutput("Stop at the round: " + i + " due to converge.\r\n");break;}}}}

其中的for循环则是训练的次数,我就以一次训练过程来学习整个内容。通过Booster类中的train方法为入口,去了解这个方法中调用其他类的一些方法。

1. WeightedInstances类

在WeightedInstances类中最重要的方法就是adjustWeights,调整数据样本的权重。
下面这个是Booster方法中train()中调用WeightedInstances类的方法:

  • new WeightedInstances(trainingData)
    这里的方法是更新数据集样本中每个数据的权重,其中new WeightedInstances(trainingData)方法是在第一次训练时,初始化权重的值。初始化权重为 1 N \frac{1}{N} N1​,N为样本的个数
 public WeightedInstances(Instances paraInstances) {super(paraInstances);setClassIndex(numAttributes() - 1);// Initialize weightsweights = new double[numInstances()];double tempAverage = 1.0 / numInstances();for (int i = 0; i < weights.length; i++) {Common.runSteps ++;weights[i] = tempAverage;}SimpleTools.variableTrackingOutput("Instances weights are: " + Arrays.toString(weights));}
  • adjustWeights方法

    • 入参paraCorrectArray是上一次训练的预测结果(bool值:true or false)
    • 入参paraAlpha 上一次训练的误差系数 α m \alpha _{m} αm​
      循环数据样本,更新每个数据的权重值(weights[i]的值):
      若在上一次训练中预测结果为true,则weights[i] = weights[i] * e α m e^{\alpha _{m}} eαm​;
      若预测结果为false,则weights[i] = weights[i] * 1 e α m \frac{1}{e^{\alpha _{m}}} eαm​1​
      最后的循环中进行归一化操作。
public void adjustWeights(boolean[] paraCorrectArray, double paraAlpha) {// Step 2. Calculate alpha.double tempIncrease = Math.exp(paraAlpha);// Step 3. Adjust.double tempWeightsSum = 0; // For normalization.for (int i = 0; i < weights.length; i++) {Common.runSteps ++;if (paraCorrectArray[i]) {weights[i] /= tempIncrease;} else {weights[i] *= tempIncrease;} // Of iftempWeightsSum += weights[i];}// Step 4. Normalize.for (int i = 0; i < weights.length; i++) {Common.runSteps ++;weights[i] /= tempWeightsSum;}SimpleTools.variableTrackingOutput("After adjusting, instances weights are: " + Arrays.toString(weights));}

2. 选择基分类器并进行训练(树桩分类器)

这里选择基分类器为树桩分类器(StumpClassifier类),如下是StumpClassifier类的train()方法。
具体的实现步骤是:

  • 因为特征值较多,这里采用随机的方法,随机选择。(java的Random方法)
  • tempValuesArray记录所选择特征值的取值并进行排序。(Arrays.sort方法)
  • 迭代所有的数据,找出最佳的分割点bestCut,以及左右子结点的类别取值(leftLeafLabel,rightLeafLabel)
    每一次训练,遍历所有的特征值,寻找可能取得的分割点进行计算,以获取最佳的分割点(选取分割点的方法有很多,在文章中的分割点是选取的前后两个特征值的平均值,但分割点的选取方式有很多,可以依情况而定) 选择最佳分割点是使划分的两个子集数据误差最小。
@Overridepublic void train() {// Step 1. Randomly choose an attribute.selectedAttribute = Common.random.nextInt(numConditions);// Step 2. Find all attribute values and sort.double[] tempValuesArray = new double[numInstances];for (int i = 0; i < tempValuesArray.length; i++) {tempValuesArray[i] = weightedInstances.instance(i).value(selectedAttribute);}Arrays.sort(tempValuesArray);Common.runSteps += (long)(numInstances * Math.log(numInstances) / Math.log(2));// Step 3. Initialize, classify all instances as the same with the// original cut.int tempNumLabels = numClasses;double[] tempLabelCountArray = new double[tempNumLabels];int tempCurrentLabel;// Step 3.1 Scan all labels to obtain their counts.for (int i = 0; i < numInstances; i++) {Common.runSteps ++;// The label of the ith instancetempCurrentLabel = (int) weightedInstances.instance(i).classValue();tempLabelCountArray[tempCurrentLabel] += weightedInstances.getWeight(i);}// Step 3.2 Find the label with the maximal count.double tempMaxCorrect = 0;int tempBestLabel = -1;for (int i = 0; i < tempLabelCountArray.length; i++) {if (tempMaxCorrect < tempLabelCountArray[i]) {tempMaxCorrect = tempLabelCountArray[i];tempBestLabel = i;}}// Step 3.3 The cut is a little bit smaller than the minimal value.bestCut = tempValuesArray[0] - 0.1;leftLeafLabel = tempBestLabel;rightLeafLabel = tempBestLabel;// Step 4. Check candidate cuts one by one.// Step 4.1 To handle multi-class data, left and right.double tempCut;double[][] tempLabelCountMatrix = new double[2][tempNumLabels];for (int i = 0; i < tempValuesArray.length - 1; i++) {// Step 4.1 Some attribute values are identical, ignore them.if (tempValuesArray[i] == tempValuesArray[i + 1]) {continue;}tempCut = (tempValuesArray[i] + tempValuesArray[i + 1]) / 2;// Step 4.2 Scan all labels to obtain their counts wrt. the cut.// Initialize again since it is used many times.for (int j = 0; j < 2; j++) {for (int k = 0; k < tempNumLabels; k++) {Common.runSteps ++;tempLabelCountMatrix[j][k] = 0;}}for (int j = 0; j < numInstances; j++) {Common.runSteps ++;// The label of the jth instancetempCurrentLabel = (int) weightedInstances.instance(j).classValue();if (weightedInstances.instance(j).value(selectedAttribute) < tempCut) {tempLabelCountMatrix[0][tempCurrentLabel] += weightedInstances.getWeight(j);} else {tempLabelCountMatrix[1][tempCurrentLabel] += weightedInstances.getWeight(j);}}// Step 4.3 Left leaf. 记录左叶子结点的数据double tempLeftMaxCorrect = 0;int tempLeftBestLabel = 0;for (int j = 0; j < tempLabelCountMatrix[0].length; j++) {Common.runSteps ++;if (tempLeftMaxCorrect < tempLabelCountMatrix[0][j]) {tempLeftMaxCorrect = tempLabelCountMatrix[0][j];tempLeftBestLabel = j;}}// Step 4.4 Right leaf.double tempRightMaxCorrect = 0;int tempRightBestLabel = 0;for (int j = 0; j < tempLabelCountMatrix[1].length; j++) {Common.runSteps ++;if (tempRightMaxCorrect < tempLabelCountMatrix[1][j]) {tempRightMaxCorrect = tempLabelCountMatrix[1][j];tempRightBestLabel = j;}}// Step 4.5 Compare with the current best.if (tempMaxCorrect < tempLeftMaxCorrect + tempRightMaxCorrect) {Common.runSteps ++;tempMaxCorrect = tempLeftMaxCorrect + tempRightMaxCorrect;bestCut = tempCut;leftLeafLabel = tempLeftBestLabel;rightLeafLabel = tempRightBestLabel;}}SimpleTools.variableTrackingOutput("Attribute = " + selectedAttribute + ", cut = " + bestCut+ ", leftLeafLabel = " + leftLeafLabel + ", rightLeafLabel = " + rightLeafLabel);}

3. 计算误差率和误差系数(树桩分类器)


调用StumpClassifier类的computeWeightedError方法。(实际上这个computeWeightedError方法是公用的,使用的是StumpClassifier的父类SimpleClassifier提供的实现方法)

  • 在这个方法中,调用computeCorrectnessArray()获取训练样本在这一次训练中的预测结果和实际结果是否一样,例如下:
  • 计算误差率:失败样本数*样本权重之和
    resultError += weightedInstances.getWeight(i);
  • 计算误差系数(弱分类器的权重) α m = 1 2 log ⁡ 1 − e m e m \alpha _{m}=\frac{1}{2}\log \frac{1-e_{m}}{e_{m}} αm​=21​logem​1−em​​
    classifierWeights[i] = 0.5 * Math.log(1 / tempError - 1);
public double computeWeightedError() {double resultError = 0;boolean[] tempCorrectnessArray = computeCorrectnessArray();for (int i = 0; i < tempCorrectnessArray.length; i++) {Common.runSteps ++;if (!tempCorrectnessArray[i]) {resultError += weightedInstances.getWeight(i);}}if (resultError < 1e-6) {resultError = 1e-6;}return resultError;}

4. 计算精确度


随着训练的次数越来越多,精确度会越来越好,若精确度足够大,就可以跳出训练。

  • classify方法
    入参为传入的数据实例。 如 5.1,3.5,1.4,0.2,Iris-setosa
    第一个for循环累加已经训练的基分类器中的权重系数,第二个for循环选择所有类别中概率最大的即为预测的类别
    计算所有训练样本的预测结果与实际结果对比,计算准确率。
    从这里也可以看出,当你训练次数越多(即numClassifiers越大),那预测正确的概率也会越大。
public double computeTrainingAccuray() {double tempCorrect = 0;for (int i = 0; i < trainingData.numInstances(); i++) {Common.runSteps ++;if (classify(trainingData.instance(i)) == (int) trainingData.instance(i).classValue()) {tempCorrect++;}}double tempAccuracy = tempCorrect / trainingData.numInstances();return tempAccuracy;}public int classify(Instance paraInstance) {double[] tempLabelsCountArray = new double[trainingData.classAttribute().numValues()];for (int i = 0; i < numClassifiers; i++) {Common.runSteps ++;int tempLabel = classifiers[i].classify(paraInstance);tempLabelsCountArray[tempLabel] += classifierWeights[i];}SimpleTools.variableTrackingOutput(Arrays.toString(tempLabelsCountArray));int resultLabel = -1;double tempMax = -1;for (int i = 0; i < tempLabelsCountArray.length; i++) {Common.runSteps ++;if (tempMax < tempLabelsCountArray[i]) {tempMax = tempLabelsCountArray[i];resultLabel = i;}}return resultLabel;}

5. 总结

因为这个AdaBosting算法的实现,涉及到不同类中方法的调用,所以需要知道这个算法的一个大致思想以及他的一些理论公式,再去看代码会更容易接受。同时,基分类器这里选择了树桩分类器,还有其他分类器可选。每个分类器的大致思想都是:

  • 初始化新训练样本的权重或根据上一次训练结果更新权重
  • 选择分类器,并根据分类器进行训练并计算在这一次训练中的错误率以及分类器的权重系数

3. java知识

在这个代码的实现中,用了一些java的基础知识

1. 抽象类

SimpleClassifier类。方法包含的成员变量和方法


抽象类不能被实例只能被继承。抽象类定义的是一组通用相关的属性和方法,并可以提供一些默认的实现,但SimpleClassifier这个类是无法实例化的(即不能被new)他主要的特点有:

  • 1.无法被实例化
  • 2.可以包含抽象方法:抽象方法是没有实现的,且要用abstract关键字声明,子类继承SimpleClassifier必须要实现这两个方法。如SimpleClassifier类的这两个方法。StumpClassifier类集成了SimpleClassifier类,且实现了这两个方法、
 /*** Train the classifier.*/public abstract void train();/*** Classify an instance.* @param paraInstance The given instance.* @return Predicted label.*/public abstract int classify(Instance paraInstance);

  • 可以包含非抽象方法:这些方法包含具体的实现,子类可以继承这些方法,当然也可以重写。例如SimpleClassifier类中的computeCorrectnessArray(),computeTrainingAccuracy(),computeWeightedError()方法
  • 可以包含成员变量和构造方法。

2. static关键字

在今天的代码中,有Common类和SimpleTools类两个公共类,其中的成员变量和方法都是用static关键字修饰的,将在类的所有实例之间共享,即使没有创建类的实例对象,也可以直接通过类名来访问该成员变量。静态变量在内存中只有一份拷贝,被所有实例共享。通常用于表示类的共享数据或常量。
如代码中:

3. 静态代码块

在今天的代码中,Common类中有静态代码块,如下:

静态代码块在类加载的过程中执行,且只执行一次。他的作用主要是在类加载时执行一些初始化操作。(所以一些初始化的操作或变量可以放在这个里面执行)

日撸java三百行day63-65相关推荐

  1. 心得体会day52(日撸 Java 三百行)

    文章链接:日撸 Java 三百行(总述)_minfanphd的博客-CSDN博客 day52 kNN 分类器 (续) 52.1 重新实现 computeNearests, 仅需要扫描一遍训练集, 即可 ...

  2. 日撸 Java 三百行day51-53

    文章目录 说明 Day51-52 KNN 分类器 1.KNN 2.代码 1.aff内容解读 2.代码理解 Day53 knn补充 1.加权思路 2.加权代码 3.leave-one-out 测试思路 ...

  3. 日撸 Java 三百行: DAY1 AND DAY2

    0.前言 由闵帆老师发布的教程:日撸 Java 三百行,有助于培养基本的java程序设计能力与良好的代码规范,同时逐渐深入,能够有助于对数据结构.机器学习相关知识的掌握与理解.我从今日开始,跟随此教程 ...

  4. 日撸 Java 三百行(20 天: 过去10日总结)

    注意:这里是JAVA自学与了解的同步笔记与记录,如有问题欢迎指正说明 目录 · 前言 一.面向对象与面向过程相比, 有哪些优势? 二.比较顺序表和链表的异同 三.分析顺序表和链表的优缺点 四.分析调拭 ...

  5. 日撸java三百行day61-62

    文章目录 说明 Day61 决策树 1.什么是决策树 2.什么是熵 3.什么是信息增益 4.详细例子 1. weather样本 2.第一次决策 3.第二次决策 4.最终决策树 4. 代码理解 4.1 ...

  6. 日撸java三百行day58-59

    文章目录 说明 Day58 符号型数据的 NB 算法 1.基础理论知识 1.1 条件概率 1.2 独立性假设 1.3 Laplacian 平滑 2. 符号型数据的预测算法跟踪 2.1 testNomi ...

  7. 日撸 Java 三百行(特殊训练:关键路径)

    日撸 Java 三百行(特殊训练:关键路径) 注意:这里是JAVA自学与了解的同步笔记与记录,如有问题欢迎指正说明 日撸 Java 三百行(特殊训练:关键路径) 前言 一.关于AOE网 (Activi ...

  8. 日撸 Java 三百行(51-60天,kNN 与 NB)

    目录 总述 01-10天,基本语法 11-20天,线性数据结构 21-30天,树与二叉树 31-40天,图 41-50天,查找与排序 51-60天,kNN 与 NB 61-70天,决策树与集成学习 7 ...

  9. 日撸 Java 三百行(81-90天,CNN 卷积神经网络)

    目录 总述 01-10天,基本语法 11-20天,线性数据结构 21-30天,树与二叉树 31-40天,图 41-50天,查找与排序 51-60天,kNN 与 NB 61-70天,决策树与集成学习 7 ...

最新文章

  1. 新视角带你认识Python可视化库(附代码资源)
  2. 基于mvc三层架构和ajax技术实现最简单的文件上传
  3. [zz]GMM-HMM语音识别模型 原理篇
  4. 理解基本包装类型Number,String,Boolean
  5. linux命令快速手记 — 让手指跟上思考的速度(四)
  6. WinForm:API
  7. 使用邮件合并批量制作工资条并进行发送邮件
  8. Android WIFI的管理方法
  9. 高清车牌识别系统无法连接服务器,智能停车场高清车牌识别系统,常见的故障及解决方法!...
  10. 资产证券化ABS+区块链
  11. PP实施经验分享(11)——SAP订单信息系统COOIS应用*增强版*
  12. 穷苦人民如何用移动固态配置unbutu22.04
  13. 如何解决用户出差,脱域的问题
  14. 【RuTracker使用教程】
  15. 原理+代码|深入浅出Python随机森林预测实战
  16. 爱奇艺html搜索栏代码,如何找到视频的通用代码?
  17. eDP V1.2_硬件相关
  18. 学生成绩录入及查询 (15 分)
  19. webpack 源码泄露
  20. 传感器原理与检测技术复习笔记第四章-电感式传感器

热门文章

  1. 航空公司客户价值分析(附完整代码)
  2. flash特效原理 标签云
  3. java实现kfc点餐系统
  4. 涛思数据完成4700万美元B轮融资, 经纬中国领投,红杉资本中国基金、GGV纪源资本、指数资本跟投...
  5. 线性代数与数据学习:MIT教授Gilbert Strang帮你打下坚实的数学基础
  6. 服务器千兆网络显示10,win10系统如何查看网卡是千兆还是百兆
  7. NCBI 基因组数据下载:法二
  8. 年近35+,人到中年,创业还是打工
  9. 筑牢合规营销“防火墙”,助力四环医药实现合规营销管理能力全面进阶 | 案例研究
  10. 《程序员必读之软件架构》 [豆瓣评分 7.30] 强调实践、注重实效、轻量级、面向开发者的软件架构指南...