AdaBoost装袋提升算法

参开资料:http://blog.csdn.net/haidao2009/article/details/7514787

更多挖掘算法:https://github.com/linyiqun/DataMiningAlgorithm

介绍

在介绍AdaBoost算法之前,需要了解一个类似的算法,装袋算法(bagging),bagging是一种提高分类准确率的算法,通过给定组合投票的方式,获得最优解。比如你生病了,去n个医院看了n个医生,每个医生给你开了药方,最后的结果中,哪个药方的出现的次数多,那就说明这个药方就越有可能性是最由解,这个很好理解。而bagging算法就是这个思想。

算法原理

而AdaBoost算法的核心思想还是基于bagging算法,但是他又一点点的改进,上面的每个医生的投票结果都是一样的,说明地位平等,如果在这里加上一个权重,大城市的医生权重高点,小县城的医生权重低,这样通过最终计算权重和的方式,会更加的合理,这就是AdaBoost算法。AdaBoost算法是一种迭代算法,只有最终分类误差率小于阈值算法才能停止,针对同一训练集数据训练不同的分类器,我们称弱分类器,最后按照权重和的形式组合起来,构成一个组合分类器,就是一个强分类器了。算法的只要过程:

1、对D训练集数据训练处一个分类器Ci

2、通过分类器Ci对数据进行分类,计算此时误差率

3、把上步骤中的分错的数据的权重提高,分对的权重降低,以此凸显了分错的数据。为什么这么做呢,后面会做出解释。

完整的adaboost算法如下

最后的sign函数是符号函数,如果最后的值为正,则分为+1类,否则即使-1类。

我们举个例子代入上面的过程,这样能够更好的理解。

adaboost的实现过程:

  图中,“+”和“-”分别表示两种类别,在这个过程中,我们使用水平或者垂直的直线作为分类器,来进行分类。

  第一步:

  根据分类的正确率,得到一个新的样本分布D2-,一个子分类器h1

  其中划圈的样本表示被分错的。在右边的途中,比较大的“+”表示对该样本做了加权。

算法最开始给了一个均匀分布 D 。所以h1 里的每个点的值是0.1。ok,当划分后,有三个点划分错了,根据算法误差表达式得到
误差为分错了的三个点的值之和,所以?1=(0.1+0.1+0.1)=0.3,而ɑ1 根据表达式 的可以算出来为0.42. 然后就根据算法 把分错的点权值变大。如此迭代,最终完成adaboost算法。

  第二步:

  根据分类的正确率,得到一个新的样本分布D3,一个子分类器h2

  第三步:

  得到一个子分类器h3

  整合所有子分类器:

  因此可以得到整合的结果,从结果中看,及时简单的分类器,组合起来也能获得很好的分类效果,在例子中所有的。后面的代码实现时,举出的也是这个例子,可以做对比,这里有一点比较重要,就是点的权重经过大小变化之后,需要进行归一化,确保总和为1.0,这个容易遗忘。

算法的代码实现

输入测试数据,与上图的例子相对应(数据格式:x坐标 y坐标 已分类结果):

1 5 1
2 3 1
3 1 -1
4 5 -1
5 6 1
6 4 -1
6 7 1
7 6 1
8 7 -1
8 2 -1

Point.java

package DataMining_AdaBoost;

/**
 * 坐标点类
 *
 * @author lyq
 *
 */
public class Point {
	// 坐标点x坐标
	private int x;
	// 坐标点y坐标
	private int y;
	// 坐标点的分类类别
	private int classType;
	//如果此节点被划错,他的误差率,不能用个数除以总数,因为不同坐标点的权重不一定相等
	private double probably;

	public Point(int x, int y, int classType){
		this.x = x;
		this.y = y;
		this.classType = classType;
	}

	public Point(String x, String y, String classType){
		this.x = Integer.parseInt(x);
		this.y = Integer.parseInt(y);
		this.classType = Integer.parseInt(classType);
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	public int getClassType() {
		return classType;
	}

	public void setClassType(int classType) {
		this.classType = classType;
	}

	public double getProbably() {
		return probably;
	}

	public void setProbably(double probably) {
		this.probably = probably;
	}
}

AdaBoost.java

package DataMining_AdaBoost;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * AdaBoost提升算法工具类
 *
 * @author lyq
 *
 */
public class AdaBoostTool {
	// 分类的类别,程序默认为正类1和负类-1
	public static final int CLASS_POSITIVE = 1;
	public static final int CLASS_NEGTIVE = -1;

	// 事先假设的3个分类器(理论上应该重新对数据集进行训练得到)
	public static final String CLASSIFICATION1 = "X=2.5";
	public static final String CLASSIFICATION2 = "X=7.5";
	public static final String CLASSIFICATION3 = "Y=5.5";

	// 分类器组
	public static final String[] ClASSIFICATION = new String[] {
			CLASSIFICATION1, CLASSIFICATION2, CLASSIFICATION3 };
	// 分类权重组
	private double[] CLASSIFICATION_WEIGHT;

	// 测试数据文件地址
	private String filePath;
	// 误差率阈值
	private double errorValue;
	// 所有的数据点
	private ArrayList<Point> totalPoint;

	public AdaBoostTool(String filePath, double errorValue) {
		this.filePath = filePath;
		this.errorValue = errorValue;
		readDataFile();
	}

	/**
	 * 从文件中读取数据
	 */
	private void readDataFile() {
		File file = new File(filePath);
		ArrayList<String[]> dataArray = new ArrayList<String[]>();

		try {
			BufferedReader in = new BufferedReader(new FileReader(file));
			String str;
			String[] tempArray;
			while ((str = in.readLine()) != null) {
				tempArray = str.split(" ");
				dataArray.add(tempArray);
			}
			in.close();
		} catch (IOException e) {
			e.getStackTrace();
		}

		Point temp;
		totalPoint = new ArrayList<>();
		for (String[] array : dataArray) {
			temp = new Point(array[0], array[1], array[2]);
			temp.setProbably(1.0 / dataArray.size());
			totalPoint.add(temp);
		}
	}

	/**
	 * 根据当前的误差值算出所得的权重
	 *
	 * @param errorValue
	 *            当前划分的坐标点误差率
	 * @return
	 */
	private double calculateWeight(double errorValue) {
		double alpha = 0;
		double temp = 0;

		temp = (1 - errorValue) / errorValue;
		alpha = 0.5 * Math.log(temp);

		return alpha;
	}

	/**
	 * 计算当前划分的误差率
	 *
	 * @param pointMap
	 *            划分之后的点集
	 * @param weight
	 *            本次划分得到的分类器权重
	 * @return
	 */
	private double calculateErrorValue(
			HashMap<Integer, ArrayList<Point>> pointMap) {
		double resultValue = 0;
		double temp = 0;
		double weight = 0;
		int tempClassType;
		ArrayList<Point> pList;
		for (Map.Entry entry : pointMap.entrySet()) {
			tempClassType = (int) entry.getKey();

			pList = (ArrayList<Point>) entry.getValue();
			for (Point p : pList) {
				temp = p.getProbably();
				// 如果划分类型不相等,代表划错了
				if (tempClassType != p.getClassType()) {
					resultValue += temp;
				}
			}
		}

		weight = calculateWeight(resultValue);
		for (Map.Entry entry : pointMap.entrySet()) {
			tempClassType = (int) entry.getKey();

			pList = (ArrayList<Point>) entry.getValue();
			for (Point p : pList) {
				temp = p.getProbably();
				// 如果划分类型不相等,代表划错了
				if (tempClassType != p.getClassType()) {
					// 划错的点的权重比例变大
					temp *= Math.exp(weight);
					p.setProbably(temp);
				} else {
					// 划对的点的权重比减小
					temp *= Math.exp(-weight);
					p.setProbably(temp);
				}
			}
		}

		// 如果误差率没有小于阈值,继续处理
		dataNormalized();

		return resultValue;
	}

	/**
	 * 概率做归一化处理
	 */
	private void dataNormalized() {
		double sumProbably = 0;
		double temp = 0;

		for (Point p : totalPoint) {
			sumProbably += p.getProbably();
		}

		// 归一化处理
		for (Point p : totalPoint) {
			temp = p.getProbably();
			p.setProbably(temp / sumProbably);
		}
	}

	/**
	 * 用AdaBoost算法得到的组合分类器对数据进行分类
	 *
	 */
	public void adaBoostClassify() {
		double value = 0;
		Point p;

		calculateWeightArray();
		for (int i = 0; i < ClASSIFICATION.length; i++) {
			System.out.println(MessageFormat.format("分类器{0}权重为:{1}", (i+1), CLASSIFICATION_WEIGHT[i]));
		}

		for (int j = 0; j < totalPoint.size(); j++) {
			p = totalPoint.get(j);
			value = 0;

			for (int i = 0; i < ClASSIFICATION.length; i++) {
				value += 1.0 * classifyData(ClASSIFICATION[i], p)
						* CLASSIFICATION_WEIGHT[i];
			}

			//进行符号判断
			if (value > 0) {
				System.out
						.println(MessageFormat.format(
								"点({0}, {1})的组合分类结果为:1,该点的实际分类为{2}", p.getX(), p.getY(),
								p.getClassType()));
			} else {
				System.out.println(MessageFormat.format(
						"点({0}, {1})的组合分类结果为:-1,该点的实际分类为{2}", p.getX(), p.getY(),
						p.getClassType()));
			}
		}
	}

	/**
	 * 计算分类器权重数组
	 */
	private void calculateWeightArray() {
		int tempClassType = 0;
		double errorValue = 0;
		ArrayList<Point> posPointList;
		ArrayList<Point> negPointList;
		HashMap<Integer, ArrayList<Point>> mapList;
		CLASSIFICATION_WEIGHT = new double[ClASSIFICATION.length];

		for (int i = 0; i < CLASSIFICATION_WEIGHT.length; i++) {
			mapList = new HashMap<>();
			posPointList = new ArrayList<>();
			negPointList = new ArrayList<>();

			for (Point p : totalPoint) {
				tempClassType = classifyData(ClASSIFICATION[i], p);

				if (tempClassType == CLASS_POSITIVE) {
					posPointList.add(p);
				} else {
					negPointList.add(p);
				}
			}

			mapList.put(CLASS_POSITIVE, posPointList);
			mapList.put(CLASS_NEGTIVE, negPointList);

			if (i == 0) {
				// 最开始的各个点的权重一样,所以传入0,使得e的0次方等于1
				errorValue = calculateErrorValue(mapList);
			} else {
				// 每次把上次计算所得的权重代入,进行概率的扩大或缩小
				errorValue = calculateErrorValue(mapList);
			}

			// 计算当前分类器的所得权重
			CLASSIFICATION_WEIGHT[i] = calculateWeight(errorValue);
		}
	}

	/**
	 * 用各个子分类器进行分类
	 *
	 * @param classification
	 *            分类器名称
	 * @param p
	 *            待划分坐标点
	 * @return
	 */
	private int classifyData(String classification, Point p) {
		// 分割线所属坐标轴
		String position;
		// 分割线的值
		double value = 0;
		double posProbably = 0;
		double negProbably = 0;
		// 划分是否是大于一边的划分
		boolean isLarger = false;
		String[] array;
		ArrayList<Point> pList = new ArrayList<>();

		array = classification.split("=");
		position = array[0];
		value = Double.parseDouble(array[1]);

		if (position.equals("X")) {
			if (p.getX() > value) {
				isLarger = true;
			}

			// 将训练数据中所有属于这边的点加入
			for (Point point : totalPoint) {
				if (isLarger && point.getX() > value) {
					pList.add(point);
				} else if (!isLarger && point.getX() < value) {
					pList.add(point);
				}
			}
		} else if (position.equals("Y")) {
			if (p.getY() > value) {
				isLarger = true;
			}

			// 将训练数据中所有属于这边的点加入
			for (Point point : totalPoint) {
				if (isLarger && point.getY() > value) {
					pList.add(point);
				} else if (!isLarger && point.getY() < value) {
					pList.add(point);
				}
			}
		}

		for (Point p2 : pList) {
			if (p2.getClassType() == CLASS_POSITIVE) {
				posProbably++;
			} else {
				negProbably++;
			}
		}

		//分类按正负类数量进行划分
		if (posProbably > negProbably) {
			return CLASS_POSITIVE;
		} else {
			return CLASS_NEGTIVE;
		}
	}

}

调用类Client.java:

/**
 * AdaBoost提升算法调用类
 * @author lyq
 *
 */
public class Client {
	public static void main(String[] agrs){
		String filePath = "C:\\Users\\lyq\\Desktop\\icon\\input.txt";
		//误差率阈值
		double errorValue = 0.2;

		AdaBoostTool tool = new AdaBoostTool(filePath, errorValue);
		tool.adaBoostClassify();
	}
}

输出结果:

分类器1权重为:0.424
分类器2权重为:0.65
分类器3权重为:0.923
点(1, 5)的组合分类结果为:1,该点的实际分类为1
点(2, 3)的组合分类结果为:1,该点的实际分类为1
点(3, 1)的组合分类结果为:-1,该点的实际分类为-1
点(4, 5)的组合分类结果为:-1,该点的实际分类为-1
点(5, 6)的组合分类结果为:1,该点的实际分类为1
点(6, 4)的组合分类结果为:-1,该点的实际分类为-1
点(6, 7)的组合分类结果为:1,该点的实际分类为1
点(7, 6)的组合分类结果为:1,该点的实际分类为1
点(8, 7)的组合分类结果为:-1,该点的实际分类为-1
点(8, 2)的组合分类结果为:-1,该点的实际分类为-1

我们可以看到,如果3个分类单独分类,都没有百分百分对,而尽管组合结果之后,全部分类正确。

我对AdaBoost算法的理解

到了算法的末尾,有必要解释一下每次分类自后需要把错的点的权重增大,正确的减少的理由了,加入上次分类之后,(1,5)已经分错了,如果这次又分错,由于上次的权重已经提升,所以误差率更大,则代入公式ln(1-误差率/误差率)所得的权重越小,也就是说,如果同个数据,你分类的次数越多,你的权重越小,所以这就造成整体好的分类器的权重会越大,内部就会同时有各种权重的分类器,形成了一种互补的结果,如果好的分类器结果分错
,可以由若干弱一点的分类器进行弥补。

AdaBoost算法的应用

可以运用在诸如特征识别,二分类的一些应用上,与单个模型相比,组合的形式能显著的提高准确率。

时间: 2024-10-10 11:58:23

AdaBoost装袋提升算法的相关文章

机器学习第5周--炼数成金-----决策树,组合提升算法,bagging和adaboost,随机森林。

决策树decision tree 什么是决策树输入:学习集输出:分类觃则(决策树) 决策树算法概述 70年代后期至80年代初期,Quinlan开发了ID3算法(迭代的二分器)Quinlan改迚了ID3算法,称为C4.5算法1984年,多位统计学家在著名的<Classification and regression tree>书里提出了CART算法ID3和CART几乎同期出现,引起了研究决策树算法的旋风,至今已经有多种算法被提出 算法的核心问题 该按什么样的次序来选择变量(属性)?最佳分离点(连

Adaboost提升算法从原理到实践

1.基本思想: 综合某些专家的判断,往往要比一个专家单独的判断要好.在"强可学习"和"弱科学习"的概念上来说就是我们通过对多个弱可学习的算法进行"组合提升或者说是强化"得到一个性能赶超强可学习算法的算法.如何地这些弱算法进行提升是关键!AdaBoost算法是其中的一个代表. 2.分类算法提升的思路: 1.找到一个弱分类器,分类器简单,快捷,易操作(如果它本身就很复杂,而且效果还不错,那么进行提升无疑是锦上添花,增加复杂度,甚至上性能并没有得到提升

经典提升算法——Adaboost

提升是一个机器学习技术,可以用于回归和分类问题,它每一步产生一个弱预测模型(决策树),并加权累加到总模型中:如果每一步的弱预测模型生成都是依据损失函数的梯度方向,则称之为梯度提升. 梯度提升算法首先给定一个目标损失函数,它的定义域是所有可行的弱函数集合(基函数):提升算法通过迭代的选择一个负梯度方向上的基函数来逐渐逼近局部极小值.这种在函数域的梯度提升观点对机器学习的很多领域有深刻影响.        提升的理论意义:如果一个问题存在弱分类器,则可以通过提升的办法得到强分类器.        A

04-08 梯度提升算法代码(鸢尾花分类)

目录 梯度提升算法代码(鸢尾花分类)+交叉验证调参 一.导入模块 二.导入数据 三.构造决策边界 四.训练模型 4.1 可视化 五.交叉验证训练模型 5.1 找到合适n_estimators 5.2 找到合适max_depth和min_samples_split 5.3 使用最优参数训练模型 5.4 使用非最优参数训练模型 5.5 小结 更新.更全的<机器学习>的更新网站,更有python.go.数据结构与算法.爬虫.人工智能教学等着你:https://www.cnblogs.com/nick

提升算法——Adaboost

思路:通过改变训练样本权重,学习多个分类器,并将这些分类器进行线性组合,提高分类器性能.大多数提升方法都是改变训练数据的概率分布(数据的权值) 强可学习:存在一个多项式的学习算法能够学习他,并且正确率很高 弱可学习:存在一个多项式学习算法学习,正确率比随机猜测要好一些 具体问题: (1)在每一轮如何改变数据权值:提高被前一轮分类器错误分类样本的权值 (2)如何将弱分类器组成强分类器:加权多数表决法,加大正确率高(误差率小)的弱分类器的权值 AdaBoost算法: 原文地址:https://www

提升算法

对于分类问题而言,给定一个训练样本集,求比较粗糙的分类规则(弱分类器)要比求精确的分类规则(强分类器)容易得多,提升法就是从弱学习算法出发,反复学习,得到一系列弱分类器,然后组合弱分类器构成强分类器.大多数提升法都是改变训练数据的概率分布(训练数据的权值分布),针对不同的训练数据分布调用弱学习算法学习一系列弱分类器. 关于AdaBoost的做法是,1.提高前一轮弱分类器错误分类样本的权值,降低正确分类样本的权值,从而以权值增大来获取对弱分类器更大的关注.2.关于分类结果的问题,AdaBoost采

提升算法(2)

提升树: 提升树是以分类树或回归树为基本分类器的提升方法.提升方法实际采用加法模型(即基函数的线性组合)与前向分布算法,以决策树为基函数的提升方法称为提升树,对分类问题决策树是二叉分类树,对回归问题决策树是二叉回归树,其根据特征x<v与x>v将根结点直接连接两个叶结点,以作为决策树桩.提升树模型可以表示为决策树的加法模型: 其中, 表示决策树,为决策树的参数:M为树的个数

scikit-learn的梯度提升算法(Gradient Boosting)使用

前言:本文的目的是记录sklearn包中GBRT的使用,主要是官网各参数的意义:对于理论部分和实际的使用希望在只是给出出处,希望之后有时间能补充完整 摘要: 1.示例 2.模型主要参数 3.模型主要属性变量 内容: 1.示例>>> import numpy as np>>> from sklearn.metrics import mean_squared_error>>> from sklearn.datasets import make_friedm

玩转算法面试 从真题到思维全面提升算法思维

第1章 算法面试到底是什么鬼?一提起算法面试,很多同学就会心有余悸.可其实,大多数企业的算法面试,并没有那么可怕.并不是一定要啃完整本<算法导论>,才能玩儿转算法面试:也并不是只有ACM参赛选手,才能笑傲算法面试.恰恰相反,大多数算法面试关注的算法思维,其实很基础.在这一章,和大家聊一聊,算法面试,到底是什么鬼?... 第2章 面试中的复杂度分析很多同学一提起复杂度分析就头疼,马上想起了<算法导论>中复杂的数学推导.但其实在一般的企业面试中,对复杂度的分析要求并没有那么高,但也是绕