pingpang

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            21 Posts :: 0 Stories :: 3 Comments :: 0 Trackbacks

          一、決策樹簡介:

           

          關于決策樹,幾乎是數據挖掘分類算法中最先介紹到的。

          決策樹,顧名思義就是用來做決定的樹,一個分支就是一個決策過程。

           

          每個決策過程中涉及一個數據的屬性,而且只涉及一個。然后遞歸地,貪心地直到滿足決策條件(即可以得到明確的決策結果)。

           

          決策樹的實現首先要有一些先驗(已經知道結果的歷史)數據做訓練,通過分析訓練數據得到每個屬性對結果的影響的大小,這里我們通過一種叫做信息增益的理論去描述它,期間也涉及到的概念。也可參考文章信息增益與熵.

           

          下面我們結合實例說一下決策樹實現過程中的上述關鍵概念:

           

          假設我們有如下數據:

           

          agejobhousecreditclass
          10010
          10020
          11021
          11111
          10010
          20010
          20020
          21121
          20131
          20131
          30131
          30121
          31021
          31031
          30010

          (一)

          我們首先要通過計算找到哪個屬性的所有屬性值能更好地表達class字段的不同。通過計算,我們發現house的屬性值最能表現class字段的不同。這個衡量標準其實就是信息增益。計算方法是:首先計算全部數據的,然后除class之外的其他屬性逐個遍歷,找到最小的那個屬性(house),然后將全部數據的減去按照house屬性劃分數據之后的數據的

           

          這個值如果滿足條件假如(>0.1),我們認為數據應該按照這個節點進行分裂,也就是說這個屬性(house)構成了我們的一次決策過程。

           

          (二)

          然后

          在按照house分裂的每個數據集上,針對其他屬性(house除外)進行與(一)相同的過程,直到信息增益不足以滿足數據分裂的條件。

           

          這樣,我們就得到了一個關于屬性數據劃分的一棵樹。可以作為class字段未知的數據的決策依據。

           

           

          二、決策樹代碼實現:

           

          具體計算代碼如下:---假設上述數據我們保存為descision.dat文件,以及需要bash4.0及以上支持運行。

           

          Bash代碼  收藏代碼
          1. #!/home/admin/bin/bash_bin/bash_4  
          2.   
          3. input=$1;  
          4.   
          5. if [ -z $input ]; then  
          6.     echo "please input the traning file";  
          7.     exit 1;  
          8. fi   
          9.   
          10. ## pre calculate the log2 value for the later calculate operation  
          11. declare -a log2;  
          12. logi=0;  
          13. records=$(cat $input | wc -l);  
          14. for i in `awk -v n=$records 'BEGIN{for(i=1;i<n;i++) print log(i)/log(2);}'`  
          15. do  
          16.     ((logi+=1));  
          17.     log2[$logi]=$i;  
          18. done  
          19.   
          20.   
          21. ## function for calculating the entropy for the given distribution of the class  
          22. function getEntropy {  
          23.     local input=`echo $1`;  
          24.     if [[ $input == *" "* ]]; then  
          25.         local current_entropy=0;  
          26.         local sum=0;  
          27.         local i;  
          28.         for i in $input  
          29.         do  
          30.             ((sum+=$i));  
          31.             current_entropy=$(awk -v n=$i -v l=${log2[$i]} -v o=$current_entropy 'BEGIN{print n*l+o}');  
          32.         done  
          33.         current_entropy=$(awk -v n=$current_entropy -v b=$sum -v l=${log2[$sum]} 'BEGIN{print n/b*-1+l;}')  
          34.         eval $2=$current_entropy;  
          35.     else  
          36.         eval $2=0;  
          37.     fi  
          38. }  
          39.   
          40.   
          41. ### the header title of the input data  
          42. declare -A header_info;  
          43. header=$(head -1 $input);  
          44. headers=(${header//,/ })  
          45. length=${#headers[@]};  
          46. for((i=0;i<length;i++))  
          47. do  
          48.     attr=${headers[$i]};  
          49.     header_info[$attr]=$i;  
          50. done  
          51.   
          52.   
          53.   
          54. ### the data content of the input data  
          55. data=${input}_dat;  
          56. sed -n '2,$p' $input > $data  
          57.   
          58.   
          59.   
          60. ## use an array to store the information of a descision tree  
          61. ## the node structure is {child,slibling,parent,attr,attr_value,leaf,class}  
          62. ## the root is a virtual node with none used attribute  
          63. ## only the leaf node has class flag and the "leaf,class" is meaningfull  
          64. ## the descision_tree  
          65. declare -a descision_tree;  
          66.   
          67. ## the root node with no child\slibing and anythings else  
          68. descision_tree[0]="0:0:0:N:N:0:0";  
          69.   
          70.   
          71. ## use recursive algrithm to build the tree   
          72. ## so we need a trace_stack to record the call level infomation  
          73. declare -a trace_stack;  
          74.   
          75. ## push the root node into the stack  
          76. trace_stack[0]=0;  
          77. stack_deep=1;  
          78.   
          79. ## begin to build the tree until the trace_stack is empty  
          80. while [ $stack_deep -ne 0 ]  
          81. do  
          82.     ((stack_deep-=1));  
          83.     current_node_index=${trace_stack[$stack_deep]};  
          84.     current_node=${descision_tree[$current_node_index]};  
          85.     current_node_struct=(${current_node//:/ });  
          86.   
          87.     ## select the current data set   
          88.     ## get used attr and their values  
          89.     attrs=${current_node_struct[3]};  
          90.     attrv=${current_node_struct[4]};  
          91.   
          92.     declare -a grepstra=();  
          93.   
          94.     if [ $attrs != "N" ];then  
          95.         attr=(${attrs//,/ });  
          96.         attrvs=(${attrv//,/ });  
          97.         attrc=${#attr[@]};  
          98.         for((i=0;i<attrc;i++))  
          99.         do  
          100.             a=${attr[$i]};  
          101.             index=${header_info[$a]};  
          102.             grepstra[$index]=${attrvs[$i]};  
          103.         done  
          104.     fi  
          105.   
          106.     for((i=0;i<length;i++))  
          107.     do  
          108.         if [ -z ${grepstra[$i]} ]; then  
          109.             grepstra[$i]=".*";  
          110.         fi  
          111.     done  
          112.     grepstrt=${grepstra[*]};  
          113.     grepstr=${grepstrt// /,};  
          114.     grep $grepstr $data > current_node_data  
          115.   
          116.     ## calculate the entropy before split the records  
          117.     entropy=0;  
          118.     input=`cat current_node_data | cut -d "," -f 5 | sort | uniq -c | sed 's/^ \+//g' | cut -d " " -f 1`  
          119.     getEntropy "$input" entropy;  
          120.   
          121.     ## calculate the entropy for each of the rest attrs  
          122.     ## and select the min one  
          123.     min_attr_entropy=1;   
          124.     min_attr_name="";  
          125.     min_attr_index=0;  
          126.     for((i=0;i<length-1;i++))  
          127.     do  
          128.         ## just use the rest attrs  
          129.         if [[ "$attrs" != *"${headers[$i]}"* ]]; then  
          130.             ## calculate the entropy for the current attr  
          131.             ### get the different values for the headers[$i]  
          132.             j=$((i+1));  
          133.             cut -d "," -f $j,$length current_node_data > tmp_attr_ds  
          134.             dist_values=`cut -d , -f 1 tmp_attr_ds | sort | uniq -c | sed 's/^ \+//g' | sed 's/ /,/g'`;  
          135.             totle=0;  
          136.             totle_entropy_attr=0;  
          137.             for k in $dist_values  
          138.             do  
          139.                 info=(${k//,/ });  
          140.                 ((totle+=${info[0]}));  
          141.                 cur_class_input=`grep "^${info[1]}," tmp_attr_ds | cut -d "," -f 2 | sort | uniq -c | sed 's/^ \+//g' | cut -d " " -f 1`  
          142.                 cur_attr_value_entropy=0;  
          143.                 getEntropy "$cur_class_input" cur_attr_value_entropy;  
          144.                 totle_entropy_attr=$(awk -v c=${info[0]} -v e=$cur_attr_value_entropy -v o=$totle_entropy_attr 'BEGIN{print c*e+o;}');  
          145.             done  
          146.             attr_entropy=$(awk -v e=$totle_entropy_attr -v c=$totle 'BEGIN{print e/c;}');  
          147.             if [ $(echo "$attr_entropy < $min_attr_entropy" | bc) = 1 ]; then  
          148.                 min_attr_entropy=$attr_entropy;  
          149.                 min_attr_name="${headers[$i]}";  
          150.                 min_attr_index=$j;  
          151.             fi  
          152.         fi  
          153.     done  
          154.   
          155.     ## calculate the gain between the original entropy of the current node   
          156.     ## and the entropy after split by the attribute which has the min_entropy  
          157.     gain=$(awk -v b=$entropy -v a=$min_attr_entropy 'BEGIN{print b-a;}');  
          158.   
          159.     ## when the gain is large than 0.1 and  then put it as a branch  
          160.     ##      and add the child nodes to the current node and push the index to the trace_stack  
          161.     ## otherwise make it as a leaf node and get the class flag  
          162.     ##      and do not push trace_stack  
          163.     if [ $(echo "$gain > 0.1" | bc)  = 1 ]; then  
          164.         ### get the attribute values  
          165.         attr_values_str=`cut -d , -f $min_attr_index current_node_data | sort | uniq`;  
          166.         attr_values=($attr_values_str);  
          167.   
          168.         ### generate the node and add to the tree and add their index to the trace_stack  
          169.         tree_store_length=${#descision_tree[@]};  
          170.         current_node_struct[0]=$tree_store_length;  
          171.         parent_node_index=$current_node_index;  
          172.          
          173.         attr_value_c=${#attr_values[@]};  
          174.         for((i=0;i<attr_value_c;i++))  
          175.         do  
          176.             tree_store_length=${#descision_tree[@]};  
          177.             slibling=0;  
          178.             if [ $i -lt $((attr_value_c-1)) ]; then  
          179.                 slibling=$((tree_store_length+1));  
          180.             fi  
          181.   
          182.             new_attr="";  
          183.             new_attrvalue="";  
          184.             if [ $attrs != "N" ]; then  
          185.                 new_attr="$attrs,$min_attr_name";  
          186.                 new_attrvalue="$attrv,${attr_values[$i]}";  
          187.             else  
          188.                 new_attr="$min_attr_name";  
          189.                 new_attrvalue="${attr_values[$i]}";  
          190.             fi  
          191.             new_node="0:$slibling:$parent_node_index:$new_attr:$new_attr_value:0:0";  
          192.             descision_tree[$tree_store_length]="$new_node";  
          193.             trace_stack[$stack_deep]=$tree_store_length;  
          194.             ((stack_deep+=1));  
          195.         done  
          196.         current_node_update=${current_node_struct[*]};  
          197.         descision_tree[$current_node_index]=${current_node_update// /:};  
          198.     else   ## current node is a leaf node   
          199.         current_node_struct[5]=1;  
          200.         current_node_struct[6]=`cut -d , -f $length current_node_data | sort | uniq -c | sort -n -r | head -1 | sed 's/^ \+[^ ]* //g'`;  
          201.         current_node_update=${current_node_struct[*]};  
          202.         descision_tree[$current_node_index]=${current_node_update// /:};  
          203.     fi   
          204.       
          205.     ## output the descision tree after every step for split or leaf node generater  
          206.     echo ${descision_tree[@]};  
          207. done  
           

          執行代碼:

           

          Bash代碼  收藏代碼
          1. ./descision.sh descision.dat  

           執行結果為:

           

          Java代碼  收藏代碼
          1. 1:0:0:N:N:0:0 0:2:0:house:0:0:0 0:0:0:house:1:0:0  
          2. 1:0:0:N:N:0:0 0:2:0:house:0:0:0 0:0:0:house:1:1:1  
          3. 1:0:0:N:N:0:0 3:2:0:house:0:0:0 0:0:0:house:1:1:1 0:4:1:house,job:0,0:0:0 0:0:1:house,job:0,1:0:0  
          4. 1:0:0:N:N:0:0 3:2:0:house:0:0:0 0:0:0:house:1:1:1 0:4:1:house,job:0,0:0:0 0:0:1:house,job:0,1:1:1  
          5. 1:0:0:N:N:0:0 3:2:0:house:0:0:0 0:0:0:house:1:1:1 0:4:1:house,job:0,0:1:0 0:0:1:house,job:0,1:1:1  

          輸出結果中展示了決策樹結構生成過程的細節,以及生成過程中樹的變化過程

           

          本代碼中使用了一維數組結構來存儲整棵決策樹,輸出的先后順序按照數組下標輸出。

           

          輸出結果中最后一行表示最終的決策樹。它表示的樹形結構其實是:

           

          決策樹結果

          這樣看著是不是就爽多了。

           

          說明:

          關于上述決策樹結果中其實有部分存在誤導:

          默認根節點是放在數組的第一個位置的,即索引值為0,而子節點中的child與sibling為0時并不表示指向跟節點,而是表示無意義,即沒有子節點或兄弟節點。

           

          該決策樹所代表的分類規則:

          根據該決策樹輸出,我們挖掘的規則是這樣的:

          首先根據house屬性判斷,當house屬性為1時,走到索引為2的節點,此時該節點是葉子節點,預測值class為1.

          當house屬性為0時,接著按照job屬性來判斷,當job屬性為0時走到索引為3的節點,預測值class為0。如果job屬性為1時走到索引值為4的節點,此時預測值class為1.

          posted on 2012-07-21 22:21 往事隨風 閱讀(1294) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 普陀区| 上饶市| 蒙山县| 沾益县| 筠连县| 鄂托克前旗| 中方县| 万年县| 霞浦县| 四川省| 汉阴县| 沙田区| 襄垣县| 霍林郭勒市| 郯城县| 翁源县| 甘泉县| 佛冈县| 贵州省| 浠水县| 开封县| 镶黄旗| 图木舒克市| 屯门区| 夏津县| 揭阳市| 公主岭市| 房产| 许昌市| 云林县| 莒南县| 泸水县| 文安县| 铜梁县| 北川| 古交市| 金溪县| 子长县| 格尔木市| 弋阳县| 齐齐哈尔市|