决策树的实现及可视化方法总结
摘要及聲明
1:本文主要介紹決策樹的實現方法及可視化方法;?
2:本文主要為理念的講解,模型也是筆者自建,文中假設與觀點是基于筆者對模型及數據的一孔之見,若有不同見解歡迎隨時留言交流;
3:筆者希望搭建出一套交易體系,原則是只做干貨的分享。后續將更新更多內容,但工作學習之余的閑暇時間有限,更新速度慢還請諒解;
4:本文Python部分的數據通過Tushare金融大數據平臺接口獲取,R語言部分的數據是用筆者之前上學的作業;
5:本文模型實現基于python3.8及R 4.1.2;
?
????????由于筆者后面打算寫的幾篇主線文章會用到決策樹,如果突然出一期主線內容又講金融原理,又講技術原理難免顯得太過雜亂,于是決定先發一篇把技術實現解決,方便后續可以專注于金融原理上。本文同時通過Python和R語言實現決策樹,最后進行了兩種語言實現方式的比較,有特定語言需求的讀者可以從目錄直接跳轉,本文主要內容如下:
目錄
1. 決策的本質其實是對數據集進行切分
2. 回歸、分類樹是路徑依賴問題,線性拆分問題
3. Python實現
3.1 回歸樹
3.2 分類樹
4. R語言實現
4.1 回歸樹
?4.1.1 通過擬合優度確認樹的剪枝參數
?4.1.2 葉節點的含義
4.2 分類樹
5. 總結
1. 決策的本質是對數據集進行切分
? ? ? ? ·根據擬合因變量的類型,決策樹可以分為兩種:回歸樹(連續因變量)和分類樹(離散因變量)。舉個借錢給朋友的栗子,如果我們將收入作為借與不借的唯一標準,那么我們可以生成一顆分類樹:
?圖一:樹的要素:根,葉,決策判斷條件
????????我們把月收入稱作“根節點”,借與不借(樹最下面一行的節點)兩個子節點稱為“葉”,“借”與“不借”則代表了從根到葉的決策路徑。但現實生活中肯定不會那么簡單,我們再加入一個失信次數的條件:
??圖二:簡單的決策樹
?????????于是現在的樹就分裂出四個葉節點。那么這四個節點分別代表:
1:月收入>2W,失信次數>10次,老賴;
2:月收入>2W,失信次數<=10次,還款能力高,信用高;
3:月收入<=2W,失信次數>10次,要錢沒有要命一條;
4:月收入>=2W,失信次數<=10次,還款能力低,但比較誠信;
????????其實我們已經不知不覺將數據集拆分成了4份:
?圖三:決策樹對數據集的劃分
????????這棵樹最大的問題是所有節點的決策閾值都是筆者主觀判斷給出的。換句話說,我們怎么知道月收入2W是最佳的決策標準?失信次數大于10次是最好的判斷依據?可能10次失信里某一次欠了一個億那豈不是要老命了。
????????因此,我們需要借助更強大和更理性的算法找到最理想的的決策依據。
2. 回歸、分類樹是路徑依賴問題,線性拆分問題
? ? ? ?其實網上已經有很多文章寫決策樹,寫得也很不錯。筆者不打算在數學推導上浪費時間,只提一個很容易被忽略的性質:其實決策樹也好,回歸樹也好,都是路徑依賴問題,即下一步的決策取決于上一步已經獲得的信息。這也是傳統技術派的觀點,即過去可以代表未來。
????????為什么路徑依賴這條性質十分重要呢?因為很多金融問題并不是路徑依賴的,如果依靠路徑依賴的模型解決非路徑依賴的問題那就是牛頭不對馬嘴。有沒有非路徑依賴的模型呢?有,蒙特卡洛模擬,隨機游走實驗這類方式就不是路徑依賴問題,即下一步怎么走是完全獨立的,和上一步無關。
????????其次是線性拆分問題,樹的分裂是基于遞歸二分法,即給定某個閾值,每次采取二叉樹的形式分裂。眼尖的人可能發現了,給定的某個閾值導致數據拆分時永遠是方形的(例如圖三)。而如果數據是非線性分割的,樹算法就很容易出現偏差,例如圖四中點A本來屬于2類,但卻被分類算法錯誤的分進4類中。
?圖四:線性切分導致偏差
3. Python實現
????????Python的sklearn就可以實現算法,也比較簡單,但可視化上好一頓折騰:
????????先導入需要模塊:
import tushare as ts import pandas as pd from sklearn import tree import graphviz????????如果電腦上完全沒有graphviz這個模塊,除了pip,還要安裝graphviz的一個插件并且將其配置到環境變量里,不然不能輸出最后的可視化PDF文件。安裝和配置過程可以參考這篇知乎文章:Graphviz安裝及使用-決策樹可視化 - 知乎,筆者親測有效。(graphviz出了很多版本,可以隨便挑個舊版本的EXE文件下載安裝)
? ? ? ? 數據方面筆者通過Tushare金融數據接口拿了今年11月4號的交易數據,下面獲取一下數據:
pro = ts.pro_api("token") df = pro.daily(trade_date='20221104')ts_code trade_date open high low close pre_close change pct_chg vol amount 0 000001.SZ 20221104 10.40 10.85 10.39 10.82 10.44 0.38 3.6398 1776112.23 1903720.944 1 000002.SZ 20221104 13.58 14.09 13.55 14.04 13.64 0.40 2.9326 1077514.38 1500447.963 2 000004.SZ 20221104 8.75 8.92 8.72 8.86 8.76 0.10 1.1416 11770.00 10392.991 3 000005.SZ 20221104 1.69 1.71 1.68 1.71 1.69 0.02 1.1834 78672.00 13352.921 4 000006.SZ 20221104 3.75 3.83 3.73 3.82 3.74 0.08 2.1390 89775.00 34065.087 ... ... ... ... ... ... ... ... ... ... ... ... 4966 873122.BJ 20221104 11.84 11.97 11.71 11.91 11.84 0.07 0.5912 7143.14 8472.178 4967 873169.BJ 20221104 6.74 6.95 6.71 6.85 6.73 0.12 1.7831 7576.40 5188.436 4968 873223.BJ 20221104 3.79 3.83 3.77 3.82 3.79 0.03 0.7916 10879.34 4147.043 4969 873527.BJ 20221104 9.25 9.38 9.22 9.35 9.25 0.10 1.0811 4454.14 4150.398 4970 689009.SH 20221104 33.45 34.35 33.10 34.18 33.20 0.98 2.9518 54280.39 183483.0293.1 回歸樹
????????構建樹的代碼比較簡單,筆者這里回歸了漲跌幅和成交量及成交額, 僅作演示:
mod = tree.DecisionTreeRegressor(max_leaf_nodes=10) result = mod.fit(df.iloc[:,9:], df.iloc[:,8]) # mod.fit(x, y)????????其中, DecisionTreeRegressor()參數設置如下:
DecisionTreeRegressor( criterion='mse', # 'MSE'均方誤差, 'friedman_mse'費爾德曼均方誤差;'mae'絕對離差 max_depth=2, max_features=None, max_leaf_nodes=None, min_impurity_decrease=0.0, # #限制信息增益的大小,信息增益小于設定值分枝不會發生 min_impurity_split=None, # #節點必須含有最小信息增益再劃分 min_samples_leaf=1, # 葉節點最少包含樣本的個數 min_samples_split=2, # #節點必須包含訓練樣本的個數 min_weight_fraction_leaf=0.0, presort=False, random_state=None, splitter='best' )? ? ? ? ?都比較好理解,不過參數的詳細解釋在官網技術文檔:sklearn.tree.DecisionTreeRegressor
????????筆者友情提示,如果數據集很大最好設置一下分裂的最大限制,不然會得到一顆“參天大樹”,筆者看了大概有幾千個葉節點吧,下如圖:
?圖五:不設置分裂限制(部分)
? ? ? ? 筆者設置了分裂節點限制,接下來可視化:
nodes = tree.export_graphviz(result, feature_names=["vol","amount"], filled=True) graph = graphviz.Source(nodes) graph.render(r"tree2") # 可視化生成一個PDF文件?????????得到:
?圖六:設置分裂限制(max_leaf_nodes=10)
????????可以看到,一些葉節點的樣本量非常小(如左三和左四葉節點),說明還是有點過度分裂了,再縮小限制,設max_leaf_nodes=4,則:
??圖七:設置分裂限制(max_leaf_nodes=4)
????????每個根節點的參數分別為:閾值,該根節點數據的MSE,該節點的樣本量,該節點的期望值。
????????每個葉節點的參數為:該節點樣本的MSE,樣本量和期望值。
3.2 分類樹
? ? ? ? 決策樹的數理是對離散因變量進行分類,因此需要將之前回歸的漲跌幅轉換成啞變量:
df["pct_chg"][(df["pct_chg"]>0)]=1 # 上漲 df["pct_chg"][df["pct_chg"]<=0]=0 # 下跌????????和剛剛一樣實例化方法然后傳數據參數進模型擬合:?
mod = tree.DecisionTreeClassifier()(max_leaf_nodes=4) result = mod.fit(df.iloc[:,9:], df.iloc[:,8]) # mod.fit(x, y)?????????其中, DecisionTreeClassifier()參數設置如下:
tree.DecisionTreeClassifier( criterion=’gini’, # gini基尼系數或者entropy信息熵 splitter=’best’, # best or random 前者是在所有特征中找最好的切分點 后者是在部分特征中,默認的”best”適合樣本量不大的時候,而如果樣本數據量非常大,此時決策樹構建推薦”random” max_depth=None, # int or None, optional (default=None) 設置決策隨機森林中的決策樹的最大深度,深度越大,越容易過擬合,推薦樹的深度為:5-20之間 max_feature=None, # None(所有),log2,sqrt,N 特征小于50的時候一般使用所有的 min_samples_split=2, # 設置結點的最小樣本數量,當樣本數量可能小于此值時,節點將不會在劃分 min_samples_leaf=1, # 限制了葉子節點最少的樣本數,如果某葉節點數目小于g該數,則會和兄弟節點一起被剪枝 min_weight_fraction_leaf=0.0, # 這個值限制了葉子節點所有樣本權重和的最小值,如果小于這個值,則會和兄弟節點一起被剪枝默認是0,就是不考慮權重問題 max_features=None, random_state=None, max_leaf_nodes=None, # 最大葉節點數 min_impurity_decrease=0.0, min_impurity_split=None, class_weight=None, presort=False )????????參數的詳細解釋在官網技術文檔:
? ? ? ? 可視化與之前類似,就不再展示圖片了:
nodes = tree.export_graphviz(result, feature_names=["vol","amount"], filled=True) graph = graphviz.Source(nodes) graph.render(r"tree2") # 可視化生成一個PDF文件4. R語言實現
????????筆者不得不說,比起Python,R語言方便不少,尤其可視化這塊,非常友好。接下來筆者用之前上學時的一次作業進行展示,任務主要是對信用卡貸款違約進行分析。
? ? ?R語言的決策樹主要通過rpart模塊實現,可視化通過rpart.plot實現,沒有安裝的需要install.package()一下,下面導入模塊:
library(rpart) library(rpart.plot)????????導入數據,簡單查看變量及其統計數據:
df <- read.csv("CREDIT.csv") summary(df)Customer Default WeeklyIncome EmploymentDurationMin. : 1.00 Length:140 Min. :-0.80700 Min. :-1.83300 1st Qu.: 35.75 Class :character 1st Qu.:-0.61100 1st Qu.:-0.90700 Median : 70.50 Mode :character Median :-0.33200 Median : 0.01900 Mean : 70.50 Mean :-0.02482 Mean : 0.06516 3rd Qu.:105.25 3rd Qu.: 0.15100 3rd Qu.: 0.48200 Max. :140.00 Max. : 5.59500 Max. : 2.79600 WeeklySpend Children Age Education Min. :-1.42100 Min. :-0.498000 Min. :-1.642000 Min. :-2.82600 1st Qu.:-0.62975 1st Qu.:-0.498000 1st Qu.:-0.814250 1st Qu.:-0.68700 Median :-0.15400 Median :-0.498000 Median :-0.028000 Median : 0.13000 Mean :-0.04405 Mean :-0.008943 Mean :-0.003114 Mean : 0.04616 3rd Qu.: 0.26800 3rd Qu.:-0.378250 3rd Qu.: 0.737000 3rd Qu.: 0.69200 Max. : 3.64700 Max. : 4.290000 Max. : 1.756000 Max. : 3.44900????????可以看到,違約情況是非結構化數據,其它所有數據在當時已經被標準化了。
? ? ? ? 將標簽轉換成啞變量:
df$Default[df$Default=="No Default"] <- 0 df$Default[df$Default=="Default"] <- 14.1 回歸樹
? ? ? ? 由于違約狀況轉換回來是離散因變量,因此這里回歸的因變量是個啞變量,也就是說最后計算的結果是違約概率(。
? ? ? ? 就兩行代碼,一行生成擬合回歸樹,一行可視化,非常簡單。筆者想想上面Python的反人類操作簡直快吐血了。。
fit.tree <- rpart(Default ~ ., data = df, method="anova") rpart.plot(fit.tree, type = 1) # type為圖表展示的類型?
?圖八:R語言回歸樹
?4.1.1 通過擬合優度確認樹的剪枝參數
????????通過plotcp可以查看最佳剪枝參數:
printcp(fit.tree) plotcp(fit.tree)????????如圖九, y軸展示了不同規模的樹下進行交叉驗證時error的大小。可以看到,在tree size為5時其誤差最小,擬合優度最佳,此時cp參數(Complexity parameter, 即復雜程度)為0.041:
?
?圖九:回歸樹擬合優度圖
????????于是我們可以在回歸樹內設置復雜參數:
fit.prune <- prune(fit.tree, cp=0.041) rpart.plot(fit.prune, type = 1)????????運行得:
?圖十:違約分析回歸樹(參數cp=0.041)
?4.1.2 葉節點的含義
????????R語言的樹非常簡潔,因此沒有像Python把所有變量是什么一個個標出來。在回歸樹下葉節點會有兩個參數分別代表該節點期望值和概率,以左一葉節點為例,0.059代表落在該節點的數據違約概率的期望值是5.9%,49%指49%的樣本落在這個節點上。
4.2 分類樹
????????R語言分類樹就在回歸樹基礎上加上把anova參數改成class:
fit.tree <- rpart(Default ~ ., data = df, method="class") printcp(fit.tree) plotcp(fit.tree)?????????可以看到,擬合優度圖顯示最佳的cp值為0.03:
?圖十一:分類樹擬合優度圖
?????????采用優化的參數擬合并展示:
fit.prune <- prune(fit.tree, cp=0.03) rpart.plot(fit.prune, type = 1)??圖十二:違約分析分類樹(參數cp=0.03)
? ? ? ? ?簡單說下葉節點含義,可以看到,分類樹比回歸樹多了一個參數,從上到下依次是:節點類別,該節點內樣本中標簽屬于該節點類別的百分比,以及該節點樣本占總體樣本量的百分比。以右一葉節點為例,該節點標簽為違約,該節點內91%的樣本都是違約樣本,該節點樣本占總體樣本量的16%。
5. 總結
????????以后我再也不敢用Python做決策樹了:
?Finally, I'm the clown
????????
?
?
總結
以上是生活随笔為你收集整理的决策树的实现及可视化方法总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android我的世界连接pc,我的世界
- 下一篇: python 中获取数据etree.HT