分类算法(5) ---- 决策树(ID3,C4.5,CTAR)

决策树就是一棵树,内部节点表示一个属性,分支代表属性值,叶子节点表示最终的结果。

训练过程:初始化所有数据分布在根节点上,然后这个根节点通过选择属性进行分裂。

                直到没有多余的可以分裂的属性,或每个叶节点的样本都是一个类别的。

使用算法:通过自顶而下递归分治的做法,将数据样本进行划分

决策过程:从根节点开始,测试相应的属性,并按照属性值选择输出分支,直到到达叶子节点,就可以得到决策结果。

优势:(1)非常直观;(2)效率高,只需要构造一次,就可以不断使用,每一次预测的最大计算次数不超过决策树的深度。

         (3)而且构造过程不需要设置任何参数,贝叶斯就需要

 

决策树的构造:

首先把所有记录看作一个节点

1. 遍历每个变量的每一种分割方式,找到最好的分割点

2. 分割成两个节点

3. 对这两个节点再执行分割,直到每个节点足够“纯”,每个节点下待分类项属于同一个类别

关键在于分割属性,在某个节点处按照某一特征属性的不同划分构造不同的分支。

属性又分为离散型跟连续型,对于离散性属性,可以使用属性划分的一个子集进行测试,根据“属于此子集”和“不属于”分成两个分支。

对于连续型属性,先进行离散化,确定一个值作为分裂点,根据大于这个值和小于这个值分成两个分支。


-------------------------------------------------------------------------------

ID3算法:

    分裂属性的选择:选择信息增益大的

    信息增益: 划分前数据集的熵 - 划分后数据集的条件熵

    

    (其中D为划分前的数据样本,A为本次划分的属性,设A将原来的样本D划分成了n个样本)

    数据集经验熵H(D):

    

    特征A对数据集D的条件熵H(D|A):

    

算法实现:

  输入训练集D与特征集A,输出决策树T
(1)若D中所有实例属于同一类C_k,则T为单节点树,并将此类作为该节点的类标记,返回T;
(2)若A=∅,则T为单节点树,并将D中是隶属最大的类作为该节点的类,返回T;
(3)否则,计算A中各特征对D的信息增益,选择信息增益最大的特征A_best;
(4)对A_best中每一个可能的值继续分割成若干非空子集D_i.
(5)对第i个节点,以D_i 为训练集,以{A-A_best}为特征集递归调用。

ID3缺点:

⑴不能处理连贯属性,只能处理离散型的描述性属性。

⑵用信息增益选择属性时偏向于选择分枝比较多的属性值


------------------------------------------------------------------------------

 C4.5算法:

    分裂属性的选择:选择信息增益率大的

    信息增益率:

    


————————————————————————

CTAR算法:

   分裂属性的选择:选择gini划分系数小的

   gini划分系数:

   


-----------------------------------------------------------------------------

优化:剪枝

剪枝对决策树正确率的影响很大,一般有前向剪枝和后向剪枝。

前向:在构建决策树的过程当中提前停止。所以切分节点的条件会比较苛刻,决策树很短,无法达到最优。

后向:决策树建好后,才开始剪枝。


错误率修正剪枝:(后向)

使用部分测试数据进行修正,用单一叶节点代替整个子树,看看错误率有没有减少。

                                            或者用一个子树替代另外一颗子树。问题:有些节点计算后就被剪掉了,导致浪费。

悲观剪枝:(后向)

使用所有测试数据,递归估算每个内部节点所覆盖样本节点的误判率。

剪枝后该内部节点会变成一个叶子节点,该叶子节点的类别为原内部节点的最优叶子节点所决定。

再比较剪枝前后该节点的错误率。


最后附上CTAR代码实现:

<span style="font-size:12px;">#include<bits/stdc++.h>
#define line_train 27751
#define line 39646
#define item_num 58
#define PLENTY 1.1
using namespace std;
int all_share = 0; 
int leaves = 1; 
set<int> ATT,all;//剩余属性与样例 
struct TEXT{ //文本 
	string Item;
	vector <double> feature; //属性 
	int shares;
}text[40000];
struct attribute{ //属性 
	int index,is_share,att_type;
	double att_value;
};
bool operator < (attribute A, attribute B){
	return A.att_value < B.att_value;
}
struct Node{ //节点 
	int type,trains,leaf_num,more; 
	double error;
	attribute property;
	Node*left,*right;
	Node(int ty){type = ty;}
	void change_point(Node*l,Node*r){
		left = l; right = r;
	}
};

void input(){
	string str;
	double data;
	ifstream fin("Datac_all.csv");
	getline(fin, str);
	for(int i=1;i<line;i++){ 
		getline(fin, str);
		istringstream ss(str);
		vector<double> tmp;
		while (!ss.eof()){
			ss >> data;  //忽略逗号 
			ss.ignore(str.size(), ',');
			text[i].feature.push_back(data);				
		}		
		if(i<=line_train){
			text[i].shares = data;
			all_share += data;
		}
		if(i==line_train) i++;			
	}
	fin.close();
	cout << "Read done.\n";
}

//计算数据集经验熵H(D)
double entropy(int size, int share_num){	
	if(size <= 1)	return 0;
	double p = (double)share_num/size;
	if(p == 0 || p == 1) return 0;
	return - p*(log(p)/log(2)) - (1-p)*(log(1-p)/log(2));
}
//计算划分系数gini index 
double gini_index(int rb, int size, int ls, int sum){	
	int lns = rb - ls, rs = sum - ls;
	int rns = size - rb - rs;
	return (double)((rb/size)*(1-(pow(ls,2)+pow(lns,2))/pow(rb,2))+
	                (1-rb/size)*(1-(pow(rs,2)+pow(rns,2))/pow(size-rb,2))); 	
}

//寻找最佳划分属性
attribute FindBestAttribute(const set<int> &s, const set<int> &A, int share_num){
	attribute RES;
	RES.att_type = -1;
	int size = s.size(),size_A = A.size();
	if(size <= 1 ||size_A == 0) 
	   return RES;	
	double MAX = -100000;
	int type_res;
	double value_res;
	double H = entropy(size, share_num);
	for(set<int>::iterator ite = A.begin(); ite != A.end(); ite++)
	{
		int i = *ite;		
		attribute att_temp[size + 1];
		int count = 0;
		for(set<int>::iterator it = s.begin(); it != s.end(); it++){
			att_temp[count].att_value = text[*it].feature[i];
			att_temp[count].is_share = text[*it].shares;
			att_temp[count++].index = *it;
		}
		sort(att_temp, att_temp + size);
		int min_gini_count = 1, left_share = 0, ls;
		double min_gini = 1;
		for(int j = 1; j < size; j++){
			left_share += att_temp[j-1].is_share;
			double gini_temp = gini_index(j, size, left_share, share_num);
			if(gini_temp < min_gini){
				min_gini = gini_temp;
				min_gini_count = j;
				ls = left_share;
			}
		}
	
		set<int> temp0,temp1;
		for(int j = 0; j < size; j++){
			if(j < min_gini_count)
				temp0.insert(att_temp[j].index);
			else
				temp1.insert(att_temp[j].index);
		}
		double lp = (double)min_gini_count/size;
		double H_cond = (double)(lp)*entropy(min_gini_count, ls) + 
		               (1-lp)*entropy(size-min_gini_count, share_num-ls);
		double g = (H - H_cond) / H; 
		if(g > MAX){
			MAX = g; type_res = i;
			value_res = (double)(att_temp[min_gini_count-1].att_value+
			                     att_temp[min_gini_count].att_value)/2;
		}
	}
	if(MAX == -100000) 	type_res = -1; 
	RES.att_type = type_res;
	RES.att_value = value_res;
	return RES;
}

//DFS构建决策树
//parent为各阶段,s为剩余样例,A为剩余属性,share_num为分享的个数 
void desision(Node *parent, const set<int> &s, const set<int> &A, int share_num)
{
    int s_size = s.size(), A_size = A.size();	
    attribute RES;
	RES.att_type = -1;
	//设置父节点参数 
	parent->change_point(NULL,NULL);
	parent->type = -1;
	parent->property = RES;
	parent->trains = s_size;
	parent->error = (double)(min(share_num, s_size - share_num) + PLENTY);
	parent->more =  (share_num > s_size - share_num); 
	//判断是否搜索到树叶
	if(share_num == s_size){//全为share 
		parent->type = 1; return;
	}
	if(share_num == 0){//全不share 
		parent->type = 0;return;
	}
	//然后判断进来的数据集是否属于同一个类  
	if(A.size() == 0){
		parent->type = (share_num > s_size/2); //使用多数判决法 
		return;
	} 
	//否则选择最佳属性划分数据集,并更新样例集和属性集
	attribute best = FindBestAttribute(s, A, share_num);
	if(best.att_type == -1 || s_size == 0) return;	   
	   
	set<int> t1, t2;
	int ls = 0, rs = 0;
	for(set<int>::iterator it = s.begin(); it != s.end(); it++){
		if(text[*it].feature[best.att_type] < best.att_value){
			t1.insert(*it);
			ls += text[*it].shares;
		}else{
			t2.insert(*it);
			rs += text[*it].shares;
		}
	}
	set<int> A_temp = A;
	A_temp.erase(best.att_type);
	
	Node *Left = new Node(-1);
	Node *Right = new Node(-1);
	parent->change_point(Left,Right);
	parent->property = best;
	desision(Left, t1, A_temp, ls);
	desision(Right, t2, A_temp, rs);
}

void cutting(Node *parent){//悲观剪枝 
	if(parent == NULL)	return;
	double et = parent->error;
	double eT = (double)parent->leaf_num * PLENTY;
	double SE = (double)sqrt(eT * (parent->trains - eT) / parent->trains);
	if(et <= eT + SE){	
		parent->type = parent->more;
		parent->change_point(NULL,NULL);
	}else{ //子树替换叶子节点 
		cutting(parent->left);
		cutting(parent->right);
	}
}

int get_leaf_num(Node *p){ //获取叶子节点数目 
	if(p == NULL){
		p->leaf_num = 0; return 0;
	}
	if(p->type != -1){
		p->leaf_num = 0; return 1;
	}
	p->leaf_num = get_leaf_num(p->left) + get_leaf_num(p->right);
	return p->leaf_num;
}

int find_Node(int t, Node *p){//寻找节点 
	if(p == NULL) return -1;
	if(p->type != -1)	
		return p->type;	
	if(p->property.att_type == -1)
		return -1;
	if(text[t].feature[p->property.att_type]  < p->property.att_value)
		return find_Node(t, p->left);
	else
		return find_Node(t, p->right);
}

int main(){
	input();
	for(int i=1;i<=line_train;i++)
	   all.insert(i); //初始样例 
	for(int j=0;j<item_num;j++)
	    ATT.insert(j); //初始属性 
	    
	Node *top = new Node(-1);
	desision(top, all, ATT, all_share);
	int all_leaves = get_leaf_num(top);
	cutting(top);
	
	ofstream out("CART.txt");
	for(int i = line_train+2; i < line; i++)	
		out << find_Node(i, top) << endl;
	return 0;
}</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值