0x0B 菩提決策樹,姻緣算法求

摘要:要找到有情人,通常是一個漫長的過程。在scikit-learn中,如何模擬人類的決策過程?決策樹的目的是構建一顆最優的二叉分割樹,因此從根節點往下,每一個結點的條件選擇,都是為了讓樹結構最簡潔。

0x0B.jpg
00 緣起

姻緣天注定,一段婚姻一段緣,是報恩報怨,還是討債還債,在一開始,誰都不知道。但可以確定一點,今生定會在一起,無論是長相廝守,還是短暫快樂。

佛曰:和有情人,做快樂事,別問是劫還是緣。

要找到有情人,通常是一個漫長的過程,也許會經歷很多場相親活動。時間久了,對相親也有更多的認識,并且積累和記錄一些數據,如下表:

序號 身高 房子 車子 長相 工作 約否
1 1.80 6.5 fact
2 1.62 5.5 IT
3 1.71 8.5 bank
4 1.58 6.3 bank
5 1.68 5.1 IT 不約
6 1.63 5.3 bank 不約
7 1.78 4.5 IT 不約
8 1.64 7.8 fact 不約
9 1.65 6.6 bank 約嗎?

前面8條數據為過往的相親記錄,最后字段為是否愿意繼續約會的記錄。如前4條數據,會愿意繼續約會,后面四條,直接pass。將這八條記錄作為訓練集,第9條是一條新數據,作為預測,決斷是否要繼續約會呢?

對數據進行一些處理,將其中的離散變量(類別變量)進行替換,保存成csv格式如下表。1表示有,0表示無,目標值的-1表示沒有意義,待預測。對于屬性“工作”列,使用字典:{'IT': 0, 'bank': 1, 'fact':2}進行映射。

height,house,car,handsome,job,is_date
1.80,1,0,6.5,2,1
1.62,1,0,5.5,0,1
1.71,0,1,8.5,1,1
1.58,1,1,6.3,1,1
1.68,0,1,5.1,0,0
1.63,1,0,5.3,1,0
1.78,0,0,4.5,0,0
1.64,0,0,7.8,2,0
1.65,0,1,6.6,0,-1

01 scikit-learn模擬

知識星球.jpeg

程序員喜歡用代碼說話,喜歡用代碼模擬,那么在scikit-learn中,如何模擬人類的決策過程呢?

# sklearn_dt.py
import pandas as pd
from sklearn.tree import DecisionTreeClassifier,export_graphviz

df = pd.read_csv('sklearn_data.csv')
train,test = df.query("is_date != -1"), df.query("is_date == -1")
y_train, X_train = train['is_date'],train.drop(['is_date'], axis=1)
X_test = test.drop(['is_date'], axis=1)

model = DecisionTreeClassifier(criterion='gini')
model.fit(X_train, y_train)
print model.predict(X_test)

上面代碼,使用pandas加載數據集,然后選擇出訓練集與測試集,倒數第三行構建了一個決策樹模型,全部使用默認參數,最后對測試數據進行預測。

需要注意一個參數,criterion='gini',這是決策樹構建的核心算法,參數“gini”指示了程序使用基尼不純度來進行構建決策樹。對于分類問題,scikit-learn還支持使用參數值為"entropy",表示使用熵的信息增益來構建決策樹。對于回歸問題,scikit-learn使用mse(均方誤差)來構建。

多次運行上面的程序,程序會在某些時刻輸出0,某些時間輸出1。

02 信息增益,基尼不純度

上面雖然只是用一行代碼便構建了一個決策樹,但實際上這一行代碼后面,做了很多事情,也就是決策樹構建的過程。

由前面的圖片可以看出,決策樹是一顆二叉樹,左右兩個分支,中間一個條件判斷,條件滿足走左分支,條件不滿足,走右分支。一層層往下,因此可以用遞歸過程來構建。唯一的問題,在當前的結點,應該選擇哪個條件來進行分割。

決策樹的目的是構建一顆最優的二叉分割樹,因此從根節點往下,每一個結點的條件選擇,都是為了讓樹結構最簡潔。換一句簡單的說,就是盡量的找出能使當前數據集盡量分開的條件。

專業術語來說,是使得數據集的不純度越來小,即純度越來越大,使得數據盡可能的分開;不純度也可以理解成數據集的混亂程度(熵),數據越混亂,不純度越高,熵越大,也即不確定性越大。比如數據集里面,約會和不約會的概率都是0.5,那么表示不確定性很多,即不純度很大,純度很小。

我們的目的,就是找到一個條件進行分割,使得這種不確定性減小,如長相小于等于5.4的數據,一定不約,其不純度為0,表示完全純凈了,都是不約,沒有不確定性。

決策樹常用的算法有ID3,C4.5,C5.0,CART,其中前面三種,都是用的熵的信息增益(率)來表示。最后一個CART(Classification And Regression Tree),即分類回歸樹,Scikit-learn實現的便是這種算法,從名字知道,既能用于分類,也用于回歸問題,其中分類問題可以使用基尼(Gini)不純度和熵的信息增益,回歸問題使用了方差降低(Variance Reduction,同mse的均方誤差)的算法。

維基上面有各種算法的數學公式,對分類問題,都是基于各個類別的概率的簡單計算。在構建樹的時候,算法會嘗試在當前數據集的所有特征上進行切分,找到概率計算出來的最優切分,并將當前條件做為切分點。

以上面的數據為例,使用gini不純度進行分割,最開始數據集的不純度為0.5(根結點的impurity=0.5),在嘗試了所有將數據切分一分為二(比如,切分按是否有房切分,長相大于7劃分,長相小于5劃分)的條件后,發現handsome<=5.4的劃分,是最優的劃分。

因此,決策樹的構建過程,主要為兩個步驟,一是數據二叉劃分,不同的實現方法,有不同的數據劃分,對離散變量,通常是按集合的方法,將數據集劃分成兩類,比如{'bank', 'IT', 'fact'}這個集合,通常會劃分成{'bank'}, {'IT','fact'};{'IT'},{'bank','fact'};{'bank','IT'},{'fact'}這三個。而對于連續型數據,{3.8, 4.5, 7.8}這樣的集體,則會按兩個數的平均值進行劃分。

數據分割完后,使用一種度量方法,來計算當前結點應該選擇哪一個條件進行最優切分,選中的條件,即為當前的決策。

03 決策之樹,決策過程

從上面的算法中,可以簡單理解為,決策樹就是找到當前最優的條件進行將數據一分為二,最優的條件即為當前的決策。

使用圖表,來分析具體的決策過程,在scikit-learn程序中,添加如下代碼:

export_graphviz(model.tree_,
                out_file='tree.dot',
                feature_names=df.columns,
                max_depth=None,
                # 下面幾個參數,需要使用最新的scikit-learn 0.17版本才能用
                class_names=["is_date:no", "is_date:yes"],
                rounded=True,
                filled=True,
            )

對生成的決策樹圖片的說明:

  • 第一行為決策條件(非葉子結點),比如根節點handsome<=5.4,條件為真走左邊,為假走右邊。
  • impurity表示當前數據集的基尼不純度
  • samples表示當前數據集的樣本數
  • value表示當前數據集各個類別(按類別順序從小到大排序,第0類,第1類……)的數量,如果value中的值相等,當前節點沒有著色,為白色。
  • class表示當前數據集中的多數類別,如果value中各個值相等,則應該是按順序取值。

運行上面的代碼,會輸出一個tree.dot的文件,再使用如下的命令,可以生成一個圖片(開篇圖片左邊部分):

$ sudo apt-get install graphviz   # 需要安裝程序
$ dot -Tpng tree.dot -o tree.png

分析圖片的數據,總結出決策規則如下:

  • 長相小于等于5.4的,一定不約;
  • 不滿足前面條件,但:有房子的,一定約;
  • 不滿足前面條件,但:有車,約,沒有車的,不約;


    知識星球.jpeg

    對待測數據,使用上面的規則:

  • 長相大于5.4,不滿足規則1,繼續下一條;
  • 沒有房子,不滿足規則2,繼續下一條規則;
  • 有車,符合第3條規則,約。

04 spark模擬

上面的決策過程,換成簡單的程序思維來表達,就是if-else的條件判斷,使用spark的實現的決策樹,可以打印出來if-else的條件決策過程:

# spark_dt.py
from pprint import pprint
from pyspark import SparkContext
from pyspark.mllib.tree import DecisionTree
from pyspark.mllib.regression import LabeledPoint

sc = SparkContext()
data = sc.textFile('spark_data.csv').map(lambda x: x.split(',')).map(lambda x: (float(x[0]), int(x[1]), int(x[2]), float(x[3]), int(x[4]), int(x[5])))
train = data.filter(lambda x: x[5]!=-1).map(lambda v: LabeledPoint(v[-1], v[:-1]))
test = data.filter(lambda x: x[5]==-1)#.map(lambda v: LabeledPoint(v[-1], v[:-1]))
model = DecisionTree.trainClassifier(train,
                                     numClasses=2,
                                     categoricalFeaturesInfo={1:2, 2:2, 4:3},
                                     impurity='gini',
                                     maxDepth=5,
                                 )

print 'The predict is:', model.predict(test).collect()
print 'The Decision tree is:', model.toDebugString()

通過上面的model.toDebugString(),打印出決策的過程,見開篇圖片右邊部分。

05 結尾

上面演示了兩種不同的決策樹實現,scikit-learn和spark,在演示數據上,輸出可能會不同,因為各自的實現是有差別的。主要在于對離散變量(工作屬性)的處理和數據的分割上面。

決策樹還會涉及剪枝的問題,完全生成的決策樹會伴隨著數據的噪聲導致過擬合,實際應用通常使用隨機森林來防止過擬合。關于隨機森林,請持續關注。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,996評論 2 374

推薦閱讀更多精彩內容