生活随笔
收集整理的這篇文章主要介紹了
决策树-熵计算-ID3算法(转)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
今天,我來講解的是決策樹。對于決策樹來說,主要有兩種算法:ID3算法和C4.5算法。C4.5算法是
對ID3算法的改進。今天主要先講ID3算法,之后會講C4.5算法和隨機森林等。
?
Contents
?
???? 1. 決策樹的基本認識
???? 2. ID3算法介紹
???? 3. 信息熵與信息增益
???? 4. ID3算法的C++實現
?
?
1. 決策樹的基本認識
?
???決策樹是一種依托決策而建立起來的一種樹。在機器學習中,決策樹是一種預測模型,代表的是一種對
?? 象屬性與對象值之間的一種映射關系,每一個節點代表某個對象,樹中的每一個分叉路徑代表某個可能
?? 的屬性值,而每一個葉子節點則對應從根節點到該葉子節點所經歷的路徑所表示的對象的值。決策樹僅
?? 有單一輸出,如果有多個輸出,可以分別建立獨立的決策樹以處理不同的輸出。接下來講解ID3算法。
?
?
2. ID3算法介紹
?
???ID3算法是決策樹的一種,它是基于奧卡姆剃刀原理的,即用盡量用較少的東西做更多的事。ID3算法,
?? 即Iterative Dichotomiser 3,迭代二叉樹3代,是Ross Quinlan發明的一種決策樹算法,這個
?? 算法的基礎就是上面提到的奧卡姆剃刀原理,越是小型的決策樹越優于大的決策樹,盡管如此,也不總
?? 是生成最小的樹型結構,而是一個啟發式算法。
?
?? 在信息論中,期望信息越小,那么信息增益就越大,從而純度就越高。ID3算法的核心思想就是以信息
?? 增益來度量屬性的選擇,選擇分裂后信息增益最大的屬性進行分裂。該算法采用自頂向下的貪婪搜索遍
?? 歷可能的決策空間。
?
?
3. 信息熵與信息增益
?
?? 在信息增益中,重要性的衡量標準就是看特征能夠為分類系統帶來多少信息,帶來的信息越多,該特征越
?? 重要。在認識信息增益之前,先來看看信息熵的定義
?
???熵這個概念最早起源于物理學,在物理學中是用來度量一個熱力學系統的無序程度,而在信息學里面,熵
?? 是對不確定性的度量。在1948年,香農引入了信息熵,將其定義為離散隨機事件出現的概率,一個系統越
?? 是有序,信息熵就越低,反之一個系統越是混亂,它的信息熵就越高。所以信息熵可以被認為是系統有序
???化程度的一個度量。
?
???假如一個隨機變量的取值為,每一種取到的概率分別是,那么
???的熵定義為
?
?????????????
?
???意思是一個變量的變化情況可能越多,那么它攜帶的信息量就越大。
?
???對于分類系統來說,類別是變量,它的取值是,而每一個類別出現的概率分別是
?
?????????????
?
?? 而這里的就是類別的總數,此時分類系統的熵就可以表示為
?
?????????????
?
???以上就是信息熵的定義,接下來介紹信息增益。
?
?? 信息增益是針對一個一個特征而言的,就是看一個特征,系統有它和沒有它時的信息量各是多少,兩者
?? 的差值就是這個特征給系統帶來的信息量,即信息增益。
?
???接下來以天氣預報的例子來說明。下面是描述天氣數據表,學習目標是play或者not play。
?
???
?
???可以看出,一共14個樣例,包括9個正例和5個負例。那么當前信息的熵計算如下
?
???
?
???在決策樹分類問題中,信息增益就是決策樹在進行屬性選擇劃分前和劃分后信息的差值。假設利用
?? 屬性Outlook來分類,那么如下圖
?
???
?
????? 劃分后,數據被分為三部分了,那么各個分支的信息熵計算如下
?
???????
?
????? ?那么劃分后的信息熵為(下面的T是數據屬性)
?
?????? ?
?
????????代表在特征屬性的條件下樣本的條件熵。那么最終得到特征屬性帶來的信息增益為
?
????????
?
???信息增益的計算公式如下
?
???
?
???其中為全部樣本集合,是屬性所有取值的集合,是的其中一個屬性值,是中屬性的
?? 值為的樣例集合,為中所含樣例數。
?
?? 在決策樹的每一個非葉子結點劃分之前,先計算每一個屬性所帶來的信息增益,選擇最大信息增益的屬性來劃
?? 分,因為信息增益越大,區分樣本的能力就越強,越具有代表性,很顯然這是一種自頂向下的貪心策略。以上
?? 就是ID3算法的核心思想。
?
?
4. ID3算法的C++實現
?
???接下來開始用C++實現ID3算法,包括以下文件
?
???
?
ID3.h
[cpp]?view plain?copy
??
#ifndef?_ID3_H_??#define?_ID3_H_?????#include?<utility>??#include?<list>??#include?<map>?????#define?Type?int???//樣本數據類型?????#define???Map1????????std::map<?int,?Type?>????//定義一維map??#define???Map2????????std::map<?int,?Map1?>????//定義二維map??#define???Map3????????std::map<?int,?Map2?>????//定義三維map??#define???Pair????????std::pair<int,?Type>??#define???List????????std::list<?Pair?>????????//一維list??#define???SampleSpace?std::list<?List?>????????//二維list?用于存放樣本數據??#define???Child???????std::map<?int,?Node*?>???//定義后繼節點集合??#define???CI??????????const_iterator?????/*??*???在ID3算法中,用二維鏈表存放樣本,結構為list<?list<?pair<int,?int>?>?>,簡記為SampleSpace,取名樣本空間??*???樣本數據從根節點開始往下遍歷。每一個節點的定義如下結構體??*/?????struct?Node??{??????int?index;????????????????????//當前節點樣本最大增益對應第index個屬性,根據這個進行分類的??????int?type;?????????????????????//當前節點的類型??????Child?next;???????????????????//當前節點的后繼節點集合??????SampleSpace?sample;???????????//未分類的樣本集合??};?????class?ID3{?????public:?????????ID3(int?);??????????~ID3();?????????void?PushData(const?Type*,?const?Type);???//將樣本數據Push給二維鏈表??????void?Build();?????????????????????????????//構建決策樹??????int??Match(const?Type*);??????????????????//根據新的樣本預測結果??????void?Print();?????????????????????????????//打印決策樹的節點的值?????private:?????????void???_clear(Node*);??????void???_build(Node*,?int);??????int????_match(const?int*,?Node*);??????void???_work(Node*);??????double?_entropy(const?Map1&,?double);??????int????_get_max_gain(const?SampleSpace&);??????void???_split(Node*,?int);??????void???_get_data(const?SampleSpace&,?Map1&,?Map2&,?Map3&);??????double?_info_gain(Map1&,?Map2&,?double,?double);??????int????_same_class(const?SampleSpace&);??????void???_print(Node*);?????private:?????????int?dimension;??????Node?*root;??};?????#endif?//?_ID3_H_??
ID3.cpp
[cpp]?view plain?copy
??
#include?<iostream>??#include?<cassert>??#include?<cmath>?????#include?"ID3.h"?????using?namespace?std;?????//初始化ID3的數據成員??ID3::ID3(int?dimension)??{??????this->dimension?=?dimension;?????????root?=?new?Node();??????root->index?=?-1;??????root->type?=?-1;??????root->next.clear();??????root->sample.clear();??}?????//清空整個決策樹??ID3::~ID3()??{??????this->dimension?=?0;??????_clear(root);??}?????//x為dimension維的屬性向量,y為向量x對應的值??void?ID3::PushData(const?Type?*x,?const?Type?y)??{??????List?single;??????single.clear();??????for(int?i?=?0;?i?<?dimension;?i++)??????????single.push_back(make_pair(i?+?1,?x[i]));??????single.push_back(make_pair(0,?y));??????root->sample.push_back(single);??}?????void?ID3::_clear(Node?*node)??{??????Child?&next?=?node->next;??????Child::iterator?it;??????for(it?=?next.begin();?it?!=?next.end();?it++)??????????_clear(it->second);??????next.clear();??????delete?node;??}?????void?ID3::Build()??{??????_build(root,?dimension);??}?????void?ID3::_build(Node?*node,?int?dimension)??{??????//獲取當前節點未分類的樣本數據??????SampleSpace?&sample?=?node->sample;?????????//判斷當前所有樣本是否是同一類,如果不是則返回-1??????int?y?=?_same_class(sample);?????????//如果所有樣本是屬于同一類??????if(y?>=?0)??????{??????????node->index?=?-1;??????????node->type?=?y;??????????return;??????}?????????//在_max_gain()函數中計算出當前節點的最大增益對應的屬性,并根據這個屬性對數據進行劃分??????_work(node);?????????//Split完成后清空當前節點的所有數據,以免占用太多內存??????sample.clear();?????????Child?&next?=?node->next;??????for(Child::iterator?it?=?next.begin();?it?!=?next.end();?it++)??????????_build(it->second,?dimension?-?1);??}?????//判斷當前所有樣本是否是同一類,如果不是則返回-1??int?ID3::_same_class(const?SampleSpace?&ss)??{??????//取出當前樣本數據的一個Sample??????const?List?&f?=?ss.front();?????????//如果沒有x屬性,而只有y,直接返回y??????if(f.size()?==?1)??????????return?f.front().second;?????????Type?y?=?0;??????//取出第一個樣本數據y的結果值??????for(List::CI?it?=?f.begin();?it?!=?f.end();?it++)??????{??????????if(!it->first)??????????{??????????????y?=?it->second;??????????????break;??????????}??????}?????????//接下來進行判斷,因為list是有序的,所以從前往后遍歷,發現有一對不一樣,則所有樣本不是同一類??????for(SampleSpace::CI?it?=?ss.begin();?it?!=?ss.end();?it++)??????{??????????const?List?&single?=?*it;??????????for(List::CI?i?=?single.begin();?i?!=?single.end();?i++)??????????{??????????????if(!i->first)??????????????{??????????????????if(y?!=?i->second)??????????????????????return?-1;?????????//發現不是同一類則返回-1??????????????????else??????????????????????break;??????????????}??????????}??????}??????return?y;?????//比較完所有樣本的輸出值y后,發現是同一類,返回y值。??}?????void?ID3::_work(Node?*node)??{??????int?mai?=?_get_max_gain(node->sample);??????assert(mai?>=?0);??????node->index?=?mai;??????_split(node,?mai);??}?????//獲取最大的信息增益對應的屬性??int?ID3::_get_max_gain(const?SampleSpace?&ss)??{??????Map1?y;??????Map2?x;??????Map3?xy;?????????_get_data(ss,?y,?x,?xy);??????double?s?=?ss.size();??????double?entropy?=?_entropy(y,?s);???//計算熵值?????????int?mai?=?-1;??????double?mag?=?-1;?????????for(Map2::iterator?it?=?x.begin();?it?!=?x.end();?it++)??????{??????????double?g?=?_info_gain(it->second,?xy[it->first],?s,?entropy);????//計算信息增益值??????????if(g?>?mag)??????????{??????????????mag?=?g;??????????????mai?=?it->first;??????????}??????}?????????if(!x.size()?&&?!xy.size()?&&?y.size())???//如果只有y數據??????????return?0;??????return?mai;??}?????//獲取數據,提取出所有樣本的y值,x[]屬性值,以及屬性值和結果值xy。??void?ID3::_get_data(const?SampleSpace?&ss,?Map1?&y,?Map2?&x,?Map3?&xy)??{??????for(SampleSpace::CI?it?=?ss.begin();?it?!=?ss.end();?it++)??????{??????int?c?=?0;??????????const?List?&v?=?*it;??????????for(List::CI?p?=?v.begin();?p?!=?v.end();?p++)??????????{??????????????if(!p->first)??????????????{??????????????????c?=?p->second;??????????????????break;??????????????}??????????}??????????++y[c];??????????for(List::CI?p?=?v.begin();?p?!=?v.end();?p++)??????????{??????????????if(p->first)??????????????{??????????????????++x[p->first][p->second];??????????????????++xy[p->first][p->second][c];??????????????}??????????}??????}??}?????//計算熵值??double?ID3::_entropy(const?Map1?&x,?double?s)??{??????double?ans?=?0;??????for(Map1::CI?it?=?x.begin();?it?!=?x.end();?it++)??????{??????????double?t?=?it->second?/?s;??????????ans?+=?t?*?log2(t);??????}??????return?-ans;??}?????//計算信息增益??double?ID3::_info_gain(Map1?&att_val,?Map2?&val_cls,?double?s,?double?entropy)??{??????double?gain?=?entropy;??????for(Map1::CI?it?=?att_val.begin();?it?!=?att_val.end();?it++)??????{??????????double?r?=?it->second?/?s;??????????double?e?=?_entropy(val_cls[it->first],?it->second);??????????gain?-=?r?*?e;??????}??????return?gain;??}?????//對當前節點的sample進行劃分??void?ID3::_split(Node?*node,?int?idx)??{??????Child?&next?=?node->next;??????SampleSpace?&sample?=?node->sample;?????????for(SampleSpace::iterator?it?=?sample.begin();?it?!=?sample.end();?it++)??????{??????????List?&v?=?*it;??????????for(List::iterator?p?=?v.begin();?p?!=?v.end();?p++)??????????{??????????????if(p->first?==?idx)??????????????{??????????????????Node?*tmp?=?next[p->second];??????????????????if(!tmp)??????????????????{??????????????????????tmp?=?new?Node();??????????????????????tmp->index?=?-1;??????????????????????tmp->type?=?-1;??????????????????????next[p->second]?=?tmp;??????????????????}??????????????????v.erase(p);??????????????????tmp->sample.push_back(v);??????????????????break;??????????????}??????????}??????}??}?????int?ID3::Match(const?Type?*x)??{??????return?_match(x,?root);??}???????int?ID3::_match(const?Type?*v,?Node?*node)??{??????if(node->index?<?0)??????????return?node->type;?????????Child?&next?=?node->next;??????Child::iterator?p?=?next.find(v[node->index?-?1]);??????if(p?==?next.end())??????????return?-1;?????????return?_match(v,?p->second);??}?????void?ID3::Print()??{??????_print(root);??}?????void?ID3::_print(Node?*node)??{??????cout?<<?"Index????=?"?<<?node->index?<<?endl;??????cout?<<?"Type?????=?"?<<?node->type?<<?endl;??????cout?<<?"NextSize?=?"?<<?node->next.size()?<<?endl;??????cout?<<?endl;?????????Child?&next?=?node->next;??????Child::iterator?p;??????for(p?=?next.begin();?p?!=?next.end();?++p)??????????_print(p->second);??}??
main.cpp
[cpp]?view plain?copy
??
#include?<iostream>??#include?"ID3.h"?????using?namespace?std;?????enum?outlook?{SUNNY,?OVERCAST,?RAIN?};??enum?temp????{HOT,???MILD,?????COOL?};??enum?hum?????{HIGH,??NORMAL?????????};??enum?windy???{WEAK,??STRONG?????????};?????int?samples[14][4]?=??{??????{SUNNY???,???????HOT?,??????HIGH??,???????WEAK??},??????{SUNNY???,???????HOT?,??????HIGH??,???????STRONG},??????{OVERCAST,???????HOT?,??????HIGH??,???????WEAK??},??????{RAIN????,???????MILD,??????HIGH??,???????WEAK??},??????{RAIN????,???????COOL,??????NORMAL,???????WEAK??},??????{RAIN????,???????COOL,??????NORMAL,???????STRONG},??????{OVERCAST,???????COOL,??????NORMAL,???????STRONG},??????{SUNNY???,???????MILD,??????HIGH??,???????WEAK??},??????{SUNNY???,???????COOL,??????NORMAL,???????WEAK??},??????{RAIN????,???????MILD,??????NORMAL,???????WEAK??},??????{SUNNY???,???????MILD,??????NORMAL,???????STRONG},??????{OVERCAST,???????MILD,??????HIGH??,???????STRONG},??????{OVERCAST,???????HOT?,??????NORMAL,???????WEAK??},??????{RAIN????,???????MILD,??????HIGH??,???????STRONG}??};?????int?main()??{??????ID3?Tree(4);??????Tree.PushData((int?*)&samples[0],?0);??????Tree.PushData((int?*)&samples[1],?0);??????Tree.PushData((int?*)&samples[2],?1);??????Tree.PushData((int?*)&samples[3],?1);??????Tree.PushData((int?*)&samples[4],?1);??????Tree.PushData((int?*)&samples[5],?0);??????Tree.PushData((int?*)&samples[6],?1);??????Tree.PushData((int?*)&samples[7],?0);??????Tree.PushData((int?*)&samples[8],?1);??????Tree.PushData((int?*)&samples[9],?1);??????Tree.PushData((int?*)&samples[10],?1);??????Tree.PushData((int?*)&samples[11],?1);??????Tree.PushData((int?*)&samples[12],?1);??????Tree.PushData((int?*)&samples[13],?0);?????????Tree.Build();??????Tree.Print();??????cout?<<?endl;??????for(int?i?=?0;?i?<?14;?++i)??????????cout?<<?"predict?value?:????"?<<Tree.Match(?(int?*)&samples[i]?)?<<?endl;??????return?0;??}??
Makefile
[cpp]?view plain?copy
??
Test:?main.cpp?ID3.h?ID3.cpp??????g++?-o?Test?ID3.cpp?main.cpp?????clean:??????rm?Test??
總結
以上是生活随笔為你收集整理的决策树-熵计算-ID3算法(转)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。