Accord.Net中決策樹
決策樹介紹
決策樹是一類機器學習算法,可以實現對數據集的分類、預測等。具體請閱讀我另一篇博客(http://www.cnblogs.com/twocold/p/5424517.html)。
Accord.Net
Accord.Net(http://accord-framework.net/)是一個開源的.Net環境下實現的機器學習算法庫。並且還包括了計算機視覺、圖像處理、數據分析等等許多算法,並且基本上都是用C#編寫的,對於.Net程序員十分友好。代碼在Github托管,並且現在仍在維護中。(https://github.com/accord-net/framework)。此處不再具體介紹,有興趣的可以去官網或者Github下載文檔和代碼深入了解。此處只簡單介紹決策樹部分的實現和使用方法。
決策樹結構
決策樹、顧名思義,肯定是一個和樹結構,作為最基礎的數據結構之一,我們深知樹結構的靈活性。那么Accord.Net是如何實現這種結構的呢?看類圖

首先觀察樹結構中最重要的一個結構,Node類的類圖如下:

簡單介紹下主要屬性方法。
| 屬性 |
含義 |
| IsLeaf |
是否為葉子節點 |
| IsRoot |
是否為根節點 |
| Output |
指示結點的類別信息(葉子節點可用) |
| Value |
為非根節點時,表示其父節點分割特征的值 |
| Branches |
為非葉子節點時,表示其子結點的集合 |
還有樹結構:

| 屬性、方法 |
含義 |
| Root |
根節點 |
| Attributes |
標識各個特征的信息(連續、離散、范圍) |
| InputCount |
特征個數 |
| OutputClasses |
輸出類別種數 |
| Compute() |
計算出某一樣本的類別信息 |
| Load(),Save() |
將決策樹存儲到文件或者讀出 |
| ToAssembly() |
存儲到dll程序集中 |
還有其他依賴項就不再逐一介紹了,Accord的官方文檔里都有更加清晰的講解。
主要想要說的是ID3Learning和C45Learning兩個類。這是Accord.Net實現的兩個決策樹學習(訓練)算法,ID3算法和C4.5算法(ID為Iterative Dichotomiser的縮寫,迭代二分器;C是Classifier的縮寫,即第4.5代分類器)。后面會介紹兩者的區別。
決策樹學習算法:
這里以一個經典的打網球的例子,介紹ID3算法的學習過程。要理解下面的代碼可能需要對決策樹的學習過程有個基本的了解,可以參考開頭給出的鏈接學習下決策樹的基本概念。
| Mitchell's Tennis Example |
|||||
| Day |
Outlook |
Temperature |
Humidity |
Wind |
PlayTennis |
| 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 |
首先,為了后面進一步構造決策樹,我們需要把上面的數據簡化一下,以字符串存儲和進行比較會消耗大量的內存空間,並且降低效率。考慮到所有特征都為離散特征,可以直接用最簡單的整型表示就行,只要保存下數字和字符串的對應關系就行。Accord.Net用了CodeBook來實現,這里也就不具體介紹了。然后需要對樹的一些屬性進行初始化,比如特征的個數(InputCount),類別數(OutputClasses)。還有每個特征可能的取值個數。接下來就可以利用上面codebook轉義過的樣本數據進行構造了。
下面貼出ID3算法中遞歸方法的偽代碼,大致講解下其實現邏輯(注:此代碼刪去了很多細節,因此無法運行,只大概了解其實現邏輯。)。
/// <summary>
/// 決策樹學習的分割構造遞歸方法
/// </summary>
/// <param name="root">當前遞歸結點</param>
/// <param name="input">輸入樣本特征</param>
/// <param name="output">樣本對應類別</param>
/// <param name="height">當前結點層數</param>
private void split(DecisionNode root, int[][] input, int[] output, int height)
{
//遞歸return條件
//1.如果output[]都相等,就是說當前所有樣本類別相同,則遞歸結束。結點標記為葉子節點,output值標識為樣本類別值
double entropy = Statistics.Tools.Entropy(output, outputClasses);
if (entropy == 0)
{
if (output.Length > 0)
root.Output = output[0];
return;
}
//2.如果當前路徑上所有特征都用過一次了,也就是說現在所有樣本在所有特征上取值相同,也就沒法划分了;遞歸結束。結點標記為葉子節點,output值標識為樣本類別值最多的那個
//這個變量存儲的是還未使用的特征個數
int candidateCount = attributeUsageCount.Count(x => x < 1);
if (candidateCount == 0)
{
root.Output = Statistics.Tools.Mode(output);
return;
}
// 如果需要繼續分裂,則首先尋找最優分裂特征,
// 存儲剩余所有可以特征的信息增益大小
double[] scores = new double[candidateCount];
// 循環計算每個特征分裂時的信息增益存儲到scores里
Parallel.For(0, scores.Length, i =>
{
scores[i] = computeGainRatio(input, output, candidates[i],
entropy, out partitions[i], out outputSubs[i]);
}
// 獲取到最大信息增益對應的特征
int maxGainIndex = scores.Max();
// 接下來 需要按照特征的值分割當前的dataset,然后傳遞給子節點 遞歸
DecisionNode[] children = new DecisionNode[maxGainPartition.Length];
for (int i = 0; i < children.Length; i++)
{
int[][] inputSubset = input.Submatrix(maxGainPartition[i]);
split(children[i], inputSubset, outputSubset, height + 1); // 遞歸每個子節點
}
root.Branches.AddRange(children);
}
此代碼僅為方便理解,具體實現細節請自行下載Accord源代碼閱讀,相信您會有不少收獲。
C4.5的實現與ID3算法流程基本相同,有幾個不同之處
1) 在選擇最優分割特征時,ID3算法采用的是信息增益,C4.5采用的是增益率。
2) C4.5支持連續型特征,因此,在遞歸進行之前,要采用二分法計算出n-1個候選划分點,將這些划分點當做離散變量處理就和ID3過程一致了。同樣是因為連續型變量,這樣一條路徑下連續型特征可以多次用來分割,而離散型特征每個只能用一次。
3) C4.5支持缺失值的處理,遺憾的是Accord中並沒有加入這一特性。
Accord.Net中還給出了簡單的剪枝算法,有興趣可以自行閱讀。
以上面的打網球例子,這里給出Accord.Net中構造和訓練決策樹的代碼示例。
//數據輸入 存儲為DataTable
DataTable data = new DataTable("Mitchell's Tennis Example");
data.Columns.Add("Day");
data.Columns.Add("Outlook");
data.Columns.Add("Temperature");
data.Columns.Add("Humidity");
data.Columns.Add("Wind");
data.Columns.Add("PlayTennis");
data.Rows.Add("D1", "Sunny", "Hot", "High", "Weak", "No");
data.Rows.Add("D2", "Sunny", "Hot", "High", "Strong", "No");
data.Rows.Add("D3", "Overcast", "Hot", "High", "Weak", "Yes");
data.Rows.Add("D4", "Rain", "Mild", "High", "Weak", "Yes");
data.Rows.Add("D5", "Rain", "Cool", "Normal", "Weak", "Yes");
data.Rows.Add("D6", "Rain", "Cool", "Normal", "Strong", "No");
data.Rows.Add("D7", "Overcast", "Cool", "Normal", "Strong", "Yes");
data.Rows.Add("D8", "Sunny", "Mild", "High", "Weak", "No");
data.Rows.Add("D9", "Sunny", "Cool", "Normal", "Weak", "Yes");
data.Rows.Add("D10", "Rain", "Mild", "Normal", "Weak", "Yes");
data.Rows.Add("D11", "Sunny", "Mild", "Normal", "Strong", "Yes");
data.Rows.Add("D12", "Overcast", "Mild", "High", "Strong", "Yes");
data.Rows.Add("D13", "Overcast", "Hot", "Normal", "Weak", "Yes");
data.Rows.Add("D14", "Rain", "Mild", "High", "Strong", "No");
// 創建一個CodeBook對象,用於將data中的字符串“翻譯”成整型
Codification codebook = new Codification(data,
"Outlook", "Temperature", "Humidity", "Wind", "PlayTennis");
// 將data中的樣本特征數據部分和類別信息分別轉換成數組
DataTable symbols = codebook.Apply(data);
int[][] inputs = Matrix.ToArray<double>(symbols, "Outlook", "Temperature", "Humidity", "Wind");
int[] outputs = Matrix.ToArray<int>(symbols, "PlayTennis");
//分析得出每個特征的信息,如,每個特征的可取值個數。
DecisionVariable[] attributes = DecisionVariable.FromCodebook(codebook, "Outlook", "Temperature", "Humidity", "Wind");
int classCount = 2; //兩種可能的輸出,打網球和不打
//根據參數初始化一個樹結構
DecisionTree tree = new DecisionTree(attributes, classCount);
// 創建一個ID3訓練方法
ID3Learning id3learning = new ID3Learning(tree);
// 訓練該決策樹
id3learning.Run(inputs, outputs);
//現在即可使用訓練完成的決策樹預測一個樣本,並借助codebook“翻譯”回來
string answer = codebook.Translate("PlayTennis",tree.Compute(codebook.Translate("Sunny", "Hot", "High", "Strong")));
貼一張利用決策樹做的小例子。


