一、集成学习
集成学习通过构建并结合多个学习器来完成学习任务,有时也被称为多分类器系统、基于委员会的学习等。集成学习通过将多个学习器进行结合,常可获得比单一学习器显著优越的泛化性能,也就是平常所说的“三个臭皮匠顶个诸葛亮”的道理。下面从两个方面对集成学习进行简要介绍。
它有两个大种类:根据个体学习器的生成方式,目前的集成学习方法大致可以分为两大类,即个体学习器间存在强依赖关系、必须串行生成的序列化方法,代表算法是Boosting系列的算法;以及个体学习器间不存在强依赖关系、可同时生成的并行化方法,代表算法是Bagging和随机森林。
最后在结合多个弱学习器的时候,采用如下方式:
1.平均法
常用于数值型的输出,也是非常常见的结合策略了
其中T是基本的单个个体弱分类器的个数,H(x)是最终学习器的结果,hi(x)则是第 i全单个弱分类器。
2.加权平均法
跟平均法很像,只不过对每个分类器都是有一个结合权重
其中wi是第 i个弱分类器的hi(x)的结合权重,通常要求wi大于等于0,且
所以平均法只是加权平均法的特例而已。·
3.投票法
适用于分类的场景,对多个分类的分类结果进行投票,对于拥有多数投票结果的分类,将其作为最强分类的最终分类输出。,
4.学习法
当训练数据很多时,可以通过另一个学习器来对所有基学习器产生结果的结合方法进行学习,这时候个体学习器称为初级学习器,用于结合的学习器成为次级学习器或元学习器.
二:AdaBoosting
从图中我们可以看出来,算法是将多个基本弱的分类器结合在一起,每个分类器对应于一个权重参数(训练而来),而每个弱分类器对最终分类结果的作用也不同,是通过加权平均输出的,权值见上图中三角形里面的数值。那么这些弱分类器和其对应的权值是怎样训练出来的呢?
AdaBoost算法是Adaptive Boost的简称,Boosting通过将一系列弱学习器组合起来,通过集成这些弱学习器的学习能力,得到一个强学习器。具体到AdaBoost算法,AdaBoost在之前学习器的基础上改变样本的权重,增加那些之前被分类错误的样本的比重,降低分类正确样本的比重,这样之后的学习器将重点关注那些被分类错误的样本。最后通过将这些学习器通过加权组合成一个强学习器,具体的,分类正确率高的学习器权重较高,分类正确率低的学习器权重较低。
开始时,所有样本的权重相同,训练得到第一个基分类器。从第二轮开始,每轮开始前都先根据上一轮基分类器的分类效果调整每个样本的权重,上一轮分错的样本权重提高,分对的样本权重降低。之后根据新得到样本的权重指导本轮中的基分类器训练,即在考虑样本不同权重的情况下得到本轮错误率最低的基分类器。重复以上步骤直至训练到约定的轮数结束,每一轮训练得到一个基分类器。
具体算法过程:
假设我们存在N个样本{(x1,y1),(x2,y2),(x3,y3),…,(xN,yN)},希望最终输出一个最强的尝习器G(X)。
G(X)是由T个弱分类器加权得到,第t 个弱分类是Gt(X)。
假设我们正在训练第t个弱分类器,需要得到它的权重。
1:得到每个样本的错误权重,初始时候每个样本是平等的,都是1/N。得到:
Wt= {wt.1, wt.2,wt.3,… , wt.N]
2:计算该第t 个弱分类器的分类误差率,就是把所有分类错误样本统计一下,看看多少几率是错误的,某个样本被分类错误了他的错误率就是该样本的错误权重,分类正确了就是0。
其中:
3:计算该第t个弱分类器的权重系数
这就得到看我们想要的分类器权重了。
接下来我们需要更新下每个样本的错误权重,因为增加那些之前被分类错误的样本的比重,降低分类正确样本的比重,这样之后的学习器将重点关注那些被分类错误的样本。
如果后面的分类器不够重视之前分类错的样本,插科打诨,那么它得到的权重系数将变低,也就是说在最终分类器的影响力就变低。
4:更新每个样本的错误权重。
分两种情况哈,
当样本xi被正确分类的时候:
当样本xi被错误分类的时候:
可以看出来,正确分类的时候,样本的错误权重值会被降低,而被错误分类的时候,样本的错误权重值会被提高。
然后再做一步就是将权重归一化:
重复以上步骤直至训练到约定的轮数结束,每一轮训练得到一个基学习器。
5:得到最强的学习器
完整代码:
package machinelearning.adaboosting;
import java.io.FileReader;
import java.util.Arrays;
import weka.core.Instances;
/**
* Weighted instances.<br>
*
* @author Rui Chen 1369097405@qq.com.
*/
public class WeightedInstances extends Instances {
/**
* Just the requirement of some classes, any number is ok.
*/
private static final long serialVersionUID = 11087456L;
/**
* Weights.
*/
private double[] weights;
/**
******************
* The first constructor.
*
* @param paraFileReader
* The given reader to read data from file.
******************
*/
public WeightedInstances(FileReader paraFileReader) throws Exception {
super(paraFileReader);
setClassIndex(numAttributes() - 1);
// Initialize weights
weights = new double[numInstances()];
double tempAverage = 1.0 / numInstances();
for (int i = 0; i < weights.length; i++) {
weights[i] = tempAverage;
} // Of for i
System.out.println("Instances weights are: " + Arrays.toString(weights));
} // Of the first constructor
/**
******************
* The second constructor.
*
* @param paraInstances
* The given instance.
******************
*/
public WeightedInstances(Instances paraInstances) {
super(paraInstances);
setClassIndex(numAttributes() - 1);
// Initialize weights
weights = new double[numInstances()];
double tempAverage = 1.0 / numInstances();
for (int i = 0; i < weights.length; i++) {
weights[i] = tempAverage;
} // Of for i
System.out.println("Instances weights are: " + Arrays.toString(weights));
} // Of the second constructor
/**
******************
* Getter.
*
* @param paraIndex
* The given index.
* @return The weight of the given index.
******************
*/
public double getWeight(int paraIndex) {
return weights[paraIndex];
} // Of getWeight
/**
******************
* Adjust the weights.
*
* @param paraCorrectArray
* Indicate which instances have been correctly classified.
* @param paraAlpha
* The weight of the last classifier.
******************
*/
public void adjustWeights(boolean[] paraCorrectArray, double paraAlpha) {
// Step 1. Calculate alpha.
double tempIncrease = Math.exp(paraAlpha);
// Step 2. Adjust.
double tempWeightsSum = 0; // For normalization.
for (int i = 0; i < weights.length; i++) {
if (paraCorrectArray[i]) {
weights[i] /= tempIncrease;
} else {
weights[i] *= tempIncrease;
} // Of if
tempWeightsSum += weights[i];
} // Of for i
// Step 3. Normalize.
for (int i = 0; i < weights.length; i++) {
weights[i] /= tempWeightsSum;
} // Of for i
System.out.println("After adjusting, instances weights are: " + Arrays.toString(weights));
} // Of adjustWeights
/**
******************
* Test the method.
******************
*/
public void adjustWeightsTest() {
boolean[] tempCorrectArray = new boolean[numInstances()];
for (int i = 0; i < tempCorrectArray.length / 2; i++) {
tempCorrectArray[i] = true;
} // Of for i
double tempWeightedError = 0.3;
adjustWeights(tempCorrectArray, tempWeightedError);
System.out.println("After adjusting");
System.out.println(toString());
} // Of adjustWeightsTest
/**
******************
* For display.
******************
*/
public String toString() {
String resultString = "I am a weighted Instances object.\r\n" + "I have " + numInstances() + " instances and "
+ (numAttributes() - 1) + " conditional attributes.\r\n" + "My weights are: " + Arrays.toString(weights)
+ "\r\n" + "My data are: \r\n" + super.toString();
return resultString;
} // Of toString
/**
******************
* For unit test.
*
* @param args
* Not provided.
******************
*/
public static void main(String args[]) {
WeightedInstances tempWeightedInstances = null;
String tempFilename = "d:/data/iris.arff";
try {
FileReader tempFileReader = new FileReader(tempFilename);
tempWeightedInstances = new WeightedInstances(tempFileReader);
tempFileReader.close();
} catch (Exception exception1) {
System.out.println("Cannot read the file: " + tempFilename + "\r\n" + exception1);
System.exit(0);
} // Of try
System.out.println(tempWeightedInstances.toString());
tempWeightedInstances.adjustWeightsTest();
} // Of main
} // Of class WeightedInstances