1. 程式人生 > >數據挖掘 - 算法 - ID3 - 轉自 http://www.cnblogs.com/dztgc/archive/2013/04/22/3036529.html

數據挖掘 - 算法 - ID3 - 轉自 http://www.cnblogs.com/dztgc/archive/2013/04/22/3036529.html

str htm bar c++代碼 度量 進行 初始化 ++ 預測

1 簡介

  決策樹學習是一種逼近離散值目標函數的方法,在這種學習到的函數被表示為一棵決策樹。

2 決策樹表

  決策樹通過把實例從根節點排列到某個葉子結點來分類實例,葉子結點即為實例所屬的分類。樹上的每一個結點指定了對實例的某個屬性的測試,並且該結點的每一個後續分支對應於該屬性的一個可能值。

分類實例的方法是從這棵樹的根節點開始,測試這個結點指定的屬性,然後按照給定實例的該屬性值對應的樹枝向下移動。然後這個過程在以新結點為根的子樹上重復。

畫出了一棵典型的學習到的決策樹。

技術分享

     圖1

這棵決策樹根據天氣情況分類“星期六上午是否適合打網球”。例如,下面的實例:

< Outlook=Sunny,Temperature= Hot,Humidity = High ,Wind= Strong >

將被沿著這棵決策樹的最左分支向下排列,因而被評定為反例(也就是這棵樹預測這個實例Play Tennis = No)。

這棵樹以及表 3-2 中用來演示 ID3 學習算法的例子摘自(Quinlan 1986)。

通常決策樹代表實例屬性值約束的合取(conjunction )的析取式(disjunction)。 從樹根到樹葉的每一條路徑對應一組屬性測試的合取,樹本身對應這些合取的析取。

例如,圖1 表示的決策樹對應於以下表達式:

(Outlook=Sunny ? Humidity= Normal) ?(Outlook=Overcast) ?(Outlook=Rain ? Wind=Weak)

3 決策樹的ID3算法

基本的ID3 算法通過自頂向下構造決策樹來進行學習。

構造過程是從“哪一個屬性將在樹的根結點被測試?”這個問題開始的。

為了回答這個問題,使用統計測試來確定每一個實例屬性單獨分類訓練樣例的能力。

(1)分類能力最好的屬性被選作樹的根結點的測試。

(2)然後為根結點屬性的每個可能值產生一個分支,並把訓練樣例排列到適當的分支(也就是,樣例的該屬性值對應的分支)之下。

(3)然後重復整個過程,用每個分支結點關聯的訓練樣例來選取在該點被測試的最佳屬性。

這形成了對合格決策樹的貪婪搜索(greedy search ),也就是算法從不回溯重新考慮以前的選擇。

偽代碼

技術分享

3.1 哪個屬性是最佳的分類屬性

ID3 算法的核心問題是選取在樹的每個結點要測試的屬性。

我們希望選擇的是最有助於分類實例的屬性。那麽衡量屬性價值的一個好的定量標準是什麽呢?

這裏將定義一個統計屬性,稱為“信息增益(information gain )”,用來衡量給定的屬性區分訓練樣例的能力。

ID3 算法在增長樹的每一步使用這個信息增益標準從候選屬性中選擇屬性。

3.1.1用熵度量樣例的均一性

為了精確地定義信息增益,我們先定義信息論中廣泛使用的一個度量標準,稱為熵(entropy),它刻畫了任意樣例集的純度( purity)。

給定包含關於某個目標概念的正反樣例的樣例集S ,那麽S 相對這個布爾型分類的熵為:

Entropy(S ) =-Plog2P-PΘlog2PΘ

其中P 是在S 中正例的比例,PΘ是在S 中負例的比例。在有關熵的所有計算中我們定義0 log0 為0 。

舉例說明,假設S 是一個關於某布爾概念的有 14 個樣例的集合,它包括 9 個正例和5 個反例(我們采用記號[9+ ,5 -]來概括這樣的數據樣例)。

那麽S 相對於這個布爾分類的熵(Entropy)為:

Entropy([9+,5-]) =-(9/14)log2(9/14) - (5/14)log2(5/14)

至此我們討論了目標分類是布爾型的情況下的熵。更一般的,如果目標屬性具有 c個不同的值,那麽S 相對於 c 個狀態(c-wise )的分類的熵定義為:

Entropy(S )=Σ -Pi*log2Pi(1 ≤ i≤ c)

3.1.2用信息增益度量期望的熵降低

已經有了熵作為衡量訓練樣例集合純度的標準,現在可以定義屬性分類訓練數據的效力的度量標準。這個標準被稱為“信息增益(information gain )”。

簡單的說,一個屬性的信息增益就是由於使用這個屬性分割樣例而導致的期望熵降低。更精確地講,一個屬性A 相對樣例集合 S 的信息增益Gain(S, A)被定義為:

技術分享

其中 Values (A)是屬性A所有可能值的集合,SV 是S 中屬性A的值為v 的子集(也就是,Sv ={s∈ S |A(s )=v } )。

請註意,等式的第一項就是原來集合S 的熵,第二項是用A分類S 後熵的期望值。

這個第二項描述的期望熵就是每個子集的熵的加權和,權值|Sv|/|S|為屬於S的樣例占原始樣例S 的比例 。

所以Gain(S , A)是由於知道屬性A的值而導致的期望熵減少。換句話來講,Gain (S , A)是由於給定屬性A的值而得到的關於目標函數值的信息。

例如,假定 S 是一套有關天氣的訓練樣例,描述它的屬性包括可能是具有 Weak和Strong 兩個值的 Wind。像前面一樣,假定 S 包含14 個樣例,[9+ ,5 -]。在這 14 個樣例中,假定正例中的 6 個和反例中的2 個有Wind =Weak,其他的有Wind =Strong。由於按照屬性Wind 分類14 個樣例得到的信息增益可以計算如下

技術分享



實驗舉例:

訓練樣本:

技術分享
D1    Sunny        Hot    High        Weak    No
D2    Sunny        Hot    High        Strong    No
D3    Overcast    Hot    High        Weak    Yes
D4    Rain        Mild    High        Weak    Yes
D5    Rain        Cool    Normal        Weak    Yes
D6    Rain        Cool    Normal        Strong    No
D7    Overcast    Cool    Normal        Strong    Yes
D8    Sunny        Mild    High        Weak    No
D9    Sunny        Cool    Normal        Weak    Yes
D10    Rain        Mild    Normal        Weak    Yes
D11    Sunny        Mild    Normal        Strong    Yes
D12    Overcast    Mild    High        Strong    Yes
D13    Overcast    Hot    Normal        Weak    Yes
D14    Rain        Mild    High        Strong    No
技術分享

測試樣本:

技術分享
D1    Sunny        Hot    High        Weak
D2    Sunny        Hot    High        Strong
D3    Overcast    Hot    High        Weak
D4    Rain        Mild    High        Weak
D5    Rain        Cool    Normal        Weak
D6    Rain        Cool    Normal        Strong
D7    Overcast    Cool    Normal        Strong
D8    Sunny        Mild    High        Weak
D9    Sunny        Cool    Normal        Weak
D10    Rain        Mild    Normal        Weak
D11    Sunny        Mild    Normal        Strong
D12    Overcast    Mild    High        Strong
D13    Overcast    Hot    Normal        Weak
D14    Rain        Mild    High        Strong
技術分享 技術分享
頭文件

head.h #ifndef ID3_H_INCLUDED #define ID3_H_INCLUDED #include <map> #include <fstream> #include <vector> #include <set> #include <iostream> #include <cmath> using namespace std; const int DataRow=14; const int DataColumn=6; const int testRow=14; const int testColumn=5; struct Node{ double value;      // 標簽值,1為YES 0為No int attrid;       //屬性標號 int attrvalue;      //屬性值 vector<Node*> childNode; }; #endif // ID3_H_INCLUDED
技術分享

C++代碼

技術分享
#include "id3.h"

string DataTable[DataRow][DataColumn];          //保存訓練樣例
string TestTable[testRow][DataColumn];          //保存測試樣例
map<string ,int> string2int;
set<int> S;
set<int> Attributes;
string attrName[DataColumn]=
{"Day","OutLook","Temperature","Humidity","Wind","PlayTennis"};
string attrValue[DataColumn][DataColumn]=
{
    {},
    {"Sunny","Overcast","Rain"},// sunny 1,overcast 2,rain3
    {"Hot","Mild","Cool"},//hot 1,mild 2, cool 3
    {"High","Normal"},//High 1,normal 2
    {"Weak","Strong"}, // weak 1,strong 2
    {"Yes","No"}  //yes 1,no 2

};
int attrCount[DataColumn]={14,3,3,2,2,2};
double lg2(double n)
{
    return log(n)/log(2);
}
void Init()                 //初始化
{
    ifstream fin("dataset.txt");
    for(int i=0;i<DataRow;++i)
    {
        for(int j=0;j<DataColumn;++j)
        {
            fin>>DataTable[i][j];
        }
    }
    fin.close();
    for(int i=1;i<=DataColumn-1;++i)
    {
        string2int[attrName[i]]=i;
        for(int j=0;j<attrCount[i];++j)
            string2int[attrValue[i][j]]=j;
    }
    for(int i=0;i<DataRow;i++)
        S.insert(i);
    for(int i=1;i<=DataColumn-2;i++)
        Attributes.insert(i);
}
double Entropy(const set<int> &s)               //計算熵值
{
    double yes=0,no=0;
    for(set<int>::size_type i=1;i<s.size();i++)
    {
        if(string2int[DataTable[i][DataColumn-1]]==0)
            yes++;
        else
            no++;
    }
    if(no==0||yes==0)
        return 0;
    double Py=yes/s.size();
    double Pn=no/s.size();
    double ans=-1*Py*lg2(Py)+-1*Pn*lg2(Pn);
    return ans;
}
double Gain(const set<int> &s,int attrid)           //計算信息增益值
{
    double ans=0;
    int attrcount = attrCount[attrid];
    double sumEntropy = Entropy(s);
    set<int> *pset = new set<int>[attrcount];
    for(set<int>::const_iterator iter=s.begin();iter!=s.end();iter++)
    {

        pset[string2int[DataTable[*iter][attrid]]].insert(*iter);
    }
    for(int i=0;i<attrcount;i++)
    {
        ans-=(double)pset[i].size()/(double)s.size()*Entropy(pset[i]);
    }
    return sumEntropy-ans;
}
int FinderBestAttribute(const set<int> &s,const set<int> &attr)      //找到最佳分類屬性
{
    double maxg=0;
    int k=-1;
    for(set<int>::const_iterator iter=attr.begin();iter!=attr.end();++iter)
    {
        double tem=Gain(s,*iter);
        if(tem>maxg)
        {
            maxg=tem;
            k=*iter;
        }
    }
    int sum=s.size();
    sum=attr.size();
    if(k==-1)
        cout<<"FinderBestAttribute Error!"<<endl;
    return k;
}

Node * Id3_solution(set<int> s,set<int> attr)
{
    Node * now = new Node();
    now->value=-1;
    if(attr.empty())     
        return NULL;
    int yes=0,no=0,sum=s.size();
    for(set<int>::iterator iter=s.begin();iter!=s.end();iter++)
    {
        if(DataTable[*iter][DataColumn-1]=="Yes")
            yes++;
        else
            no++;
    }
    if(yes==sum||no==sum)
    {
     now->value=yes/sum;
     return now;
    }

    int bestattrid = FinderBestAttribute(s,attr);       //找到最佳的分類屬性
    now->attrid=bestattrid;
    attr.erase(attr.find(bestattrid));
    vector<set<int> > child=vector<set<int> >(attrCount[bestattrid]);
    for(set<int>::iterator iter=s.begin();iter!=s.end();iter++)             //插入孩子結點
    {
        int id = string2int[DataTable[*iter][bestattrid]];
        child[id].insert(*iter);
    }
    for(int i=0;i<child.size();i++)         //對孩子結點進行遞歸調用
    {
        Node *rel = Id3_solution(child[i],attr);
        rel->attrvalue=i;
        now->childNode.push_back(rel);
    }
    return now;
}
void test(Node * Root)          //用測試樣例進行測試
{
    Node* pnow=Root;
    ifstream fin("test.txt");
    for(int i=0;i<testRow;i++)
    {
        for(int j=0;j<testColumn;j++)
            fin>>TestTable[i][j];
    }
    fin.close();
    for(int i=0;i<testRow;i++)
    {
        pnow=Root;
        while(true)
        {
            if(pnow->value==1)
                {TestTable[i][DataColumn-1]="yes";break;}
            else if(pnow->value==0)
                {TestTable[i][DataColumn-1]="no";break;}
            for(vector<Node*>::iterator iter=pnow->childNode.begin();iter!=pnow->childNode.end();++iter)
            {
                if((*iter)->attrvalue==string2int[TestTable[i][pnow->attrid]])
                {pnow=*iter;
                break;}
            }
        }
    }
}
int main()
{
    Init();
    Node * Root = Id3_solution(S,Attributes);
    test(Root);
    for(int i=0;i<testRow;i++)
    {
        for(int j=0;j<DataColumn;j++)
            cout<<TestTable[i][j]<<" ";
        cout<<endl;
    }
    return 0;
}
技術分享

結果:

技術分享

數據挖掘 - 算法 - ID3 - 轉自 http://www.cnblogs.com/dztgc/archive/2013/04/22/3036529.html