1. 程式人生 > >人工智慧演算法—決策樹

人工智慧演算法—決策樹

    文/騰訊soso林世飛

    決策樹方法最早產生於上世紀60年代,到70年代末。由J Ross Quinlan提出了ID3演算法,此演算法的目的在於減少樹的深度。但是忽略了葉子數目的研究。C4.5演算法在ID3演算法的基礎上進行了改進,對於預測變數的缺值處理、剪枝技術、派生規則等方面作了較大改進,既適合於分類問題,又適合於迴歸問題

    這裡 介紹其基本原理 和一個實驗例子。

    先介紹2個演算法:

    演算法一:熵entropy

    熵(entropy)指的是體系的混亂的程度,當我們嘗試把混合集合A={B1,B2,C1,C2…..} (其中Bx表示一個類別的元素,Cx

表示另外一個) 劃分為2個集合 MN(即決策樹的2個分支時候),比較好的劃分是 M 裡面都是 BxN裡面都是Cx,這時候我們需要一個函式對 劃分以後 的 集合進行評估,看看是否純度 夠“純”。如果很純,很有序,熵就是0.

    理解該公式:p(xi) 越平均,系統約混亂,如果系統只有2個元素x1x2x1出現概率是0.5x2出現概率也是0.5,即p(x1) =0.5p(x2) =0.5 ,這時公式計算結果為1p(xi)如果比較不平均,比如p(x2) =1,那就是系統很確定,一點都不混亂,肯定是x2構成,這時熵計算結果就是0.

    這個規律剛剛好是 log 函式特點 過(10)這個點(見下圖),我想這個就是

克勞德·艾爾伍德·夏農設計這個公式選擇log函式的道理。

    用python 實現就是 :

    def entropy(l):

    from math import log

    #函式程式設計語法,定義一個函式

    log2=lambda x:log(x)/log(2)

    total=len(l)

    counts={}

    #統計每個型別出現格式

    for item in l:

    counts.setdefault(item,0)

    counts[item]+=1

    ent=0

    for i in counts:

    p=float(counts[i])/total #

計算概率

    ent-=p*log2(p)#熵計算

    return ent

    演算法二:除了 熵,還有一個衡量一個集合是否混亂的方法叫 Gini Impurity (基尼不純度)方法。

    公式如下:

    公式基本上也符合以上 熵的 規律: 集合越純值越小,如果只有2個元素時候,每個元素出現概率就是0.5,這時 I = 0.5*0.5 +0.5*0.5 =0.5

    0.5*0.5   # 我的理解是  K1(出現概率0.5) 被當做 其他Kx的概率(出現概率0.5

    Python 實現如下:

    # 去重 統計每個出現次數

    def uniquecounts(rows):

       results={}

       for row in rows:

          # The result is the last column

          r=row[len(row)-1]

          if r not in results: results[r]=0

          results[r]+=1

       return results

    def giniimpurity(rows):

      total=len(rows)

      counts=uniquecounts(rows)

      imp=0

      for k1 in counts:

           # k1 的概率

        p1=float(counts[k1])/total

        for k2 in counts:

          if k1==k2: continue

           # k2 的概率

          p2=float(counts[k2])/total

           # 我的理解是  K1 被當做 其他Kx的概率

          imp+=p1*p2

      return imp

    現在開始介紹決策樹:

    決策樹樹節點定義:

    class decisionnode:

      def __init__(self,col=-1,value=None,results=None,tb=None,fb=None):

        self.col=col  #第幾個列 即因子

        self.value=value  #判斷值

        self.results=results   #結果集合

        self.tb=tb  #左右樹

        self.fb=fb

    #構建決策樹的過程,scoref 就是前面衡量 集合混亂 程度的2個演算法的函式之一

    def buildtree(rows,scoref=entropy):

      if len(rows)==0: return decisionnode()

      current_score=scoref(rows)

      # 最佳劃分

      best_gain=0.0

      best_criteria=None

      best_sets=None

      #列數

      column_count=len(rows[0])-1

      for col in range(0,column_count):

        column_values={}

        # 統計每一列可能的值

        for row in rows:

           column_values[row[col]]=1

      #嘗試每一列 每一種值 作為劃分集合

        for value in column_values.keys():

          (set1,set2)=divideset(rows,col,value)

          # Information gain 資訊增益??我的理解是加權計算目前的得分,即純度、混亂度

          p=float(len(set1))/len(rows)

          gain=current_score-p*scoref(set1)-(1-p)*scoref(set2)

          if gain>best_gain and len(set1)>0 and len(set2)>0:

            best_gain=gain

            best_criteria=(col,value)

            best_sets=(set1,set2)

      # 建立子分支

      if best_gain>0:

        trueBranch=buildtree(best_sets[0])

        falseBranch=buildtree(best_sets[1])

        return decisionnode(col=best_criteria[0],value=best_criteria[1],

                            tb=trueBranch,fb=falseBranch)

      else:

        # 如果是葉子節點則統計這個分支的 個數

        return decisionnode(results=uniquecounts(rows))

    #根據某列值 劃分rows 2個 集合

    # or nominal values

    def divideset(rows,column,value):

       # Make a function that tells us if a row is in

       # the first group (true) or the second group (false)

       split_function=None

       if isinstance(value,int) or isinstance(value,float):

          split_function=lambda row:row[column]>=value

       else:

          split_function=lambda row:row[column]==value

       # Divide the rows into two sets and return them

       set1=[row for row in rows if split_function(row)]

       set2=[row for row in rows if not split_function(row)]

       return (set1,set2)

    #利用一個已知樹 決策過程

    def classify(observation,tree):

      if tree.results!=None:

        return tree.results

      else:

        v=observation[tree.col]

        branch=None

        if isinstance(v,int) or isinstance(v,float):

          if v>=tree.value: branch=tree.tb

          else: branch=tree.fb

        else:

          if v==tree.value: branch=tree.tb

          else: branch=tree.fb

        return classify(observation,branch)

    時間抽取是 web 頁面 分類、抽取時候一個很重要的 課題。通常一個頁面將包含多個可能代表 該頁面 發表時間的 字串,如果判斷一個 包含數字的字串是否是一個時間串 ,往往要考慮很多因素,比如 ,整個過程會比較繁瑣。

    這裡嘗試利用1099 頁面 分析處理得到的162個時間串的各個屬性 ,利用決策樹進行學習,最終生成一個決策樹 ,該決策樹可以新的 時間串,根據其屬性進行 判斷。

    以下是實驗效果:

    其中每一列代表 其屬性值,比如第一列含義是該字串是否出現在 連結中,是為ture

    生成決策樹:

    >>> datas=[ line.split('|')[1:] for line in file('result3') ]

    >>> tree= treepredict.buildtree(datas)

    對某個時間 2010-05-27 00:00:00  提取的各個特徵通過這個決策樹 判斷是否是時間

    2010-05-27 00:00:00|false|false|false|false|true|false|false|false|8825|0.971809|2|8|0|false|false|0

    結論是:不是時間 

    >>> treepredict.classify(['false', 'false', 'false', 'false', 'true', 'false', '

    false', 'false', 'false', 'false', 'false', 'false', 'false', '0', '7754','249',

     '0.967919', '0.031082', '0', '0', '0', '0', '0', '2', '-1', '0', '8', '0', '0',

     '0'],tree)

    {'0/n': 30}

    我們可以檢視下 這個機器學習 產生的 決策樹:

    區域性:

    從上圖可以看到1011 列 值對於該問題決策該串是否是時間串 起著關鍵作用,雖然我們可能考慮很多因素、但以下幾列起著關鍵作用,具體含義是 塊開始 、 正文的位置關係 、字串長度等。

    這個從實驗資料集來看也比較正常,因為這個資料集是我的實驗資料,很多列值沒有精確計算,基本雷同,變化不大。換句話說,對最後決策其作用都是那些 變化比較大的列項。

    以上是關於決策樹原理實現和工程利用的一個例子的學習筆記,對於時間抽取是否適合利用決策樹來處理,目前還沒有定論和應用,這裡只是利用他來幫助我們理解 在眾多因素參與決策時候,哪些因素關鍵些,較好解釋了我們決策過程,每個因子起到作用,比如有的因子其實不起作用,至少在我們的資料集中。

    決策樹在工程實際利用時候,可能 還要面臨 樹裁剪 (DecisionTree Pruning)、資料項某些維度資料缺少的問題。

    什麼時候使用決策樹 ,本身就是一個問題。

    更多內容參考