デシジョン ツリー - 理論から実践まで

デシジョンツリー

基本知識

基本的な考え方

定義: デシジョン ツリーは、一般的なタイプの機械学習手法であり、インスタンスの分類を記述するツリー構造です。(特定のインスタンスには一連の特性があり、結果はこれらの特性に従って判断されます)

説明: デシジョン ツリーはノードと有向エッジで構成されます。ノードはルート ノード、内部ノード、リーフ ノードに分割できます。

ルートノード: サンプルの完全なセット

内部ノード: 機能または属性のテスト

リーフノード:判定結果

判定テスト シーケンス: ルート ノードから各リーフ ノードまでのパスが判定テスト シーケンスに対応します。

目的:一般化能力、つまりまだ見たことのない事例への対応力が強い決定木を生成すること。

分析と思考

次に、スイカの本で良いメロンか悪いメロンを判断する方法の決定木を使用して、次のことを理解します。

スイカのデータセット:

ここに画像の説明を挿入

スイカの意思決定ツリー図:

ここに画像の説明を挿入

上記のデータセットと生成された決定木を見ると、次の問題がわかります。

  • これは if-else ではなく、なぜわざわざ行うのでしょうか?
  • なぜスイカの決定木は初めてテクスチャーによって判断されるのでしょうか?
  • なぜスイカ決定木は2層目で触覚を判定し、4層目ツリーでは触覚を判定する特徴があるのでしょうか?
  • データセットにはノック音とおへそという 2 つの特徴がありますが、これらが決定木に反映されないのはなぜですか?
  • スイカ データ セットのサイズはデシジョン ツリーにどのような影響を及ぼしますか?

まず最初の質問について話しましょう。これは実行できる単純な if-else ではないでしょうか?

まず第一に、デシジョン ツリーの起源は非常に単純です。データ構成の分岐構造 if-else から派生しています。ツリー構造を使用する理由は、効率の向上がもたらされ、効果的に次のことが可能になるからです。良い結果が得られるように最適化します。

それでは、上記の質問を続けて見てください。デシジョン ツリーを理解できるよう最善を尽くします。

デシジョン ツリーの前提となる知識ポイント

情報利得、利得率、ジニ指数

上記の 2 番目の質問について:スイカの決定木が初めてテクスチャに従って判断されるのはなぜですか? それは、食感によって良いメロンか悪いメロンかを区別するのに役立つからに違いありません。

意思決定の目的は、より適切な分類を行うためです。すべての特徴によって、良いメロンが 50%、悪いメロンが 50% になるのであれば、意思決定は意味がありません。スイカを買うとき、この 2 種類しかありません。状況 (どちらか一方)良いか悪いか) ので、根ノードから葉ノードまでのデシジョン ツリーの決定クラスは、ますます低くなければなりませんスイカは良いメロンか悪いメロンです。

このことから、決定木学習の鍵は、最も多くの分割属性を選択する方法にあることがわかります。

次に、3 つの古典的な属性分割方法を紹介します。

情報取得(ID3)

情報獲得を理解するには、情報エントロピーとは何かを知る必要があります。

情報エントロピー

定義: サンプルセットの純度を測定するために最も一般的に使用される指標の 1 つ。現在のサンプルセット D におけるクラス K のサンプルの割合が pk (K = 1, 2, 3, 4...|y|) であると仮定すると、D = − ∑ k の情報エントロピー E nt ( D ) は次のようになります
。 = 1 ∣ y ∣ pk log ⁡ 2 pk Ent(D) = -\sum_{k=1}^{|y|} p_{k}\log_{2}{p_{k}}エント( D ) _ _=k = 1ypログ_2p

  • pk は分類結果の数で、二値分類の場合は |y| = 2、スイカの例では良いメロンと悪いメロンです。
  • 単位:ビット
  • Ent(D) の値が小さいほど、D の純度が高くなります。
  • p = 0、その後 plogp = 0 (慣例)
  • Ent(D) の最小値は 0、最大値は log|y| です。

情報エントロピーを理解した後、情報利得を理解することができます。

情報の獲得:

離散属性 a には V 個の可能な値 { , , ..., } があり a除算すると V 個の分岐ノードが生成され、v 番目の分岐ノードには D の属性 a のすべての値が含まれます。として表されます a^1a^2a^va^vD^v

単刀直入に言うと、どの特徴色が出るか(スイカ判別を例にします)、緑(6/17)、漆黒(6/17)、薄白(5/17)の3種類があり、計算します。これら 3 つのエントロピーの情報を加算し、比例的に合計します。

次に、サンプル集合 D を属性 a で分割することによって得られる情報ゲインを計算できます。 Gain
( D , a ) = E nt ( D ) − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ E nt ( D v ) Gain (D, a) = Ent(D) - \sum_{v=1}^{V}\frac{|D^{v}|}{|D|} Ent(D^{v} )ゲイン( D _ _ __=エント( D ) _ _v = 1VD ∣D _vエント( D _ _v )
一般的に言えば、情報利得が大きいほど、実際の属性 a を分割することによって得られる「純度の向上」も大きくなります。(率直に言うと、得られる情報が多ければ多いほど、最初に分類するためにそれを使用する可能性が高くなります。)

しかし問題がある:

通常、データ セットには「番号」が含まれています (上にスクロールすると、いくつかのスクリーンショットが表示されます)。それを分割候補の属性としても使用すると、その情報ゲインは他の属性よりも大きくなります。

各数値 (1、2、3...|y|) は「数値」グループ |y| 分岐内のインスタンスであるため、各数値の情報エントロピーは次のようになります。 E nt ( D v ) = (
− ( 1 log ⁡ 2 1 + 0 log ⁡ 2 0 ) ) Ent(D^{v}) = (-(1\log_{2}{1} + 0\log_{2}{0}))エント( D _ _v )=( ( 1ログ_21+0ログ_20 ) )

Ent ( D v ) = 0 Ent(D^{v}) = 0エント( D _ _v )=0

(詳細は省略しますが) 情報利得の式から、「Ent(D) - 0」のときが最も情報利得が大きくなることが分かります。

注: ここで 0 は対数関数の領域にありませんが、これは慣例であるため、その数学的意味についてはあまり心配する必要はありません。

情報の取得では、多数の値を持つ属性が優先されることがわかります。

この状況では、ゲイン比と呼ばれる新しい概念が提案されます。

ゲイン比(C4.5)

公式:
Gainratio ( D , a ) = Gain ( D , a ) IV ( a ) Gain_ratio(D, a) = \frac{Gain(D, a)}{IV(a)}ゲイン_ _ _rat i o ( D , __=I V ( a )ゲイン( D _ _ _ _

IV ( a ) = − ∑ V v = 1 ∣ D v ∣ ∣ D ∣ log ⁡ 2 ∣ D v ∣ ∣ D ∣ IV(a) = -\sum_{V}^{v=1}\frac{|D ^{v}|}{|D|} \log_{2}{\frac{|D^{v}|}{|D|}}I V ( a )=Vv = 1D ∣D _vログ_2D ∣D _v

IV(a): 属性 a の固有値と呼ばれ、通常、属性 a の取り得る値が多いほど、IV(a) は大きくなります。

ゲイン レートの式から、ゲイン レート基準では、可能な値が少ない属性が優先されます。

属性の分割方法は、分割候補属性の中から情報利得が平均レベルより高い属性を見つけ出し、最も利得率が高いものを選択する。

ジニ指数 (CART)

ジニ値の定義: D に K クラスがあり、サンプル点が k 番目のクラスに属する確率が pk であると仮定すると、確率分布のジニ値は次のように定義されます。 G ini ( D ) = ∑ k = 1 K
pk ( 1 − pk ) = 1 − ∑ k = 1 K pk 2 Gini(D) = {\textstyle \sum_{k=1}^{K}}p_{k}(1-p_{k}) = 1- {\textstyle \sum_{k =1}^{K}}p_{k}^2ジニ( D ) _ _ _=k = 1Kp( 1p)=1k = 1Kpk2
機能: 2 つのサンプルがランダムに抽出され、それらのカテゴリ ラベルが一致しない確率を反映します。

注: Gini(D) が小さいほど、データセット D の純度は高くなります。

データセット D が与えられた場合、属性 a のジニ インデックスは次のように定義されます
G iniindex ( D , a ) = ∑ v = 1 V ∣ D v ∣ ∣ D ∣ G ini ( D v ) Gini_{index}(D,a ) = {\textstyle \sum_{v=1}^{V}}\frac{|D^v|}{|D|}ジニ(D^v)ジニ_ _ _インデックス_ _ _ _( D _=v = 1VD ∣D _vジニD_ _v )
分割戦略: 候補属性セット A において、分割後のジニ指数が最小となる属性を最適な分割属性として選択します。

これを見て、多くの人は「なぜスイカ決定木は第 2 層のタッチを判定するのに、第 4 層の木でもタッチを判定するのか?」という 3 番目の疑問に対する答えを持っていると思います。

上記3つの分割方法で得られる最適な効果はその都度変わるため、2層目の質感が少しぼやけていると、触感で直接メロンの良し悪しが判断できます。別の決定シーケンスでは、メロンの良し悪しをより正確に判断するために、最初に他のことを判断する必要がある場合があります。

剪定

  • なぜ剪定するのか?

    決定木学習アルゴリズムにおける「過学習」との戦い。

  • 剪定戦略とは何ですか?

    • 事前枝刈り: 決定木の生成プロセスでは、分割前に各ノードが推定されます。現在のノードの分割によって決定木の汎化パフォーマンスが改善できない場合、分割は停止され、現在のノードがリーフ ノードとしてマークされます。
    • 枝刈り後: 最初にトレーニング セットから完全なデシジョン ツリーを生成し、次に非リーフ ノードを下から上に調べます。ノードに対応するサブツリーがリーフ ノードに置き換えられる場合、デシジョン ツリーは一般化できます。パフォーマンスを向上させるには、サブツリーをリーフ ノードに置き換えます。
  • 剪定後の性能はどうやって判断するのですか?

    • ホールドアウト法: データの一部をパフォーマンス評価用の「検証セット」として確保します。

スイカの本でそれがどのように機能するかを見てみましょう。

1つ目はデータの準備で、二重横線が学習セット、二重横線がホールドアウト法の検証セットです。

ここに画像の説明を挿入

上図に従って生成された決定木は次のとおりです。

ここに画像の説明を挿入

剪定前

ここに画像の説明を挿入

この写真を見て、あなたはインスピレーションを受けたと思います。これは、検証セットをテストに使用し、分割前後の分割された検証セットの精度を調べているだけではないでしょうか。彼の歩みは何でしょうか?

  • 根から - - - > 葉
  • まず、決定に従って、子ノードを正確な分類結果に変換します (例: へそくぼみに 10 個のデータがあり、7 個が良好、3 個が不良で、この子ノードは良好なメロンとして分析されます)
  • そこに検証セットのデータを入れて検証するのですが、除算前の精度<除算後の精度の場合は除算が必要です。
    • ルートノードを分割する前の精度は検証セットによって決まり、単純な二値分類の場合はラベルを直接判定します。
    • 子ノードの分割前の精度は、その親ノードの検証された精度です。

効果:

  • 多くの分岐は「拡張」されないため、過剰適合のリスクが軽減され、デシジョン ツリーのトレーニング時間のオーバーヘッドとテスト時間のオーバーヘッドも軽減されます。
  • 事前の剪定に基づいて、その後の分割のパフォーマンスを向上させることが有益です。

短所:フィッティング不足のリスクがある可能性があります

剪定後の

プロセスは次のとおりです。
ここに画像の説明を挿入

ステップ:

  • 葉から - - - > 根
  • まず、内部ノードを葉ノードとし、学習データセットの判定系列の分類結果に応じて、大きい方の分類結果を葉ノードとして使用する。
  • 次に、枝刈り前後の精度を判断し、枝刈り後の精度 > 枝刈り前の精度の場合は枝刈りを行います。
    • 最初のリーフ ノードの検証精度は検証セットによって制御されます。
    • 親ノードの枝刈りされた精度はトレーニング セットによって決まります。

効果:

  • 剪定前よりも多くの枝が保存されます。
  • アンダーフィッティングのリスクは小さく、汎化能力は剪定前よりも優れています。
  • トレーニングのオーバーヘッドは、枝刈りなしおよび枝刈り前のオーバーヘッドよりもはるかに大きくなります。

コード

擬似コード(基本アルゴリズム)

输入:训练集D={
    
    (x1, y1),(x2, y2),.. . , (xm,ym)};
		属性集A= {
    
    a1, a2,.. . , ad}
过程:过程:函数TreeGenerate(D, A)
1:生成结点node;
2: if D中样本全属于同一类别C then
3:      将node标记为C类叶结点;return
4: end if
5: if A = 空集  OR D中样本在A上取值相同 then
6:		将node标记为叶结点,其类别标记为D中样本数最多的类; return
7: end if
8: 从A中选择最优划分属性a*;
9: for a*的每一个值a*' do
10:		为node 生成一个分支;令Dv表示D中在a*上取值为a*'的样本子集;
11:		if Dv为空 then
12:			将分支结点标记为叶结点,其类别标记为D中样本最多的类; return
13:		else
14:			以 TreeGenerate( Dv, A\{
    
    a*})为分支结点
15:		end if
16: end for
输出:以node为根结点的一棵决策树

上の図から、デシジョン ツリーは再帰的なプロセスであり、次の 3 つの状況が再帰的な戻りにつながることがわかります。

  • 1. 現在のノードに含まれるサンプルはすべて同じカテゴリに属しており、分割する必要はありません。
  • 2. 現在の属性セットが空であるか、すべてのサンプルのすべての属性が同じ値を持ち、分割できません。
  • 3. 現在のノードに含まれるサンプル セットは空であり、分割できません。

sklearn を使用したデシジョン ツリーの実装 - アヤメのデータ セットを例にします

# 决策树分类器
sklearn.tree.DecisionTreeClassifier(criterion = , max_depth= , random_state =)
# criterion:默认是gini指数,也可以选择其他的,例如信息熵:"entropy"
# max_depth:树的深度
# random——state:随机数种子
# 决策树可视化,导数DOT格式
sklearn.tree.export_graphviz(estimator, out_file='*.dot', feature_names=[","])
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, export_graphviz
from six import StringIO
import pydotplus
def decisiion_iris():
    iris = load_iris()
    x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22)
    estimator = DecisionTreeClassifier(criterion="entropy")
    estimator.fit(x_train, y_train)

    y_predict = estimator.predict(x_test)
    print("y_predict:\n", y_predict)
    print("直接比较真实值和预测值:\n", y_test == y_predict)

    score = estimator.score(x_test, y_test)
    print("准确率为:\n", score)

    # 可视化
    dot_data = StringIO()
    export_graphviz(estimator, out_file=dot_data)
    graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
    # 将结果存入到png文件中
    graph.write_png('diabetes.png')
    graph.write_pdf('diabetes.pdf')
    # 显示
    Image(graph.create_png())
    return None

.dot ファイルを生成し、.dot ファイルを png 形式に変換して pdf ファイルを生成します

生成された png ファイルは次のとおりです。

ここに画像の説明を挿入

注: PDF ファイルと PNG イメージを生成したい場合は、それらをダウンロードする必要があります。pipwindows_10_cmake_Release_graphviz-install-7.0.1-win64と conda を使用してダウンロードしようとしないでください。これには環境の構成が必要であり、役に立たないからです。ダウンロード リンク: ここをクリック | Graphviz

ダウンロード中に [ヘルプ] を直接クリックしてパスを追加することをお勧めします。インストール プロセスはあまり説明されません。

ダウンロード後は、印刷して入力できます。エラーがある場合は、まず環境変数がインストールされているかどうかを確認し、コマンド ライン操作の cmd を介して .dot/pdf ファイルを入力できます。ドット ファイルを PNG イメージに変換しますdot -Tpng *.dot -o *.png

コードの基礎となる実装

「機械学習の実践」という本を通して、他の人のコードを参考にして、決定木のコードを実行しました。

データセットはコードに組み込まれています。その理由は、すべてのファイルが入力されているためにエラーを報告しているためKeyerror:list indices must be integers or slices, not str です。

データセットには 3 つの特徴があります。

データセットの説明:

「気温」「就寝人数」「お腹が空いているかどうか」の3つの特徴を設定し、そのうちの3つの特徴(気温:暑い3、適度な2、寒い1)、空腹度:(1お腹が空いている、0お腹が空いていない)あなたが望むものを判断してください ベッドに留まらないでください!

import numpy as np
import operator
import math
# 按照给定特征划分数据集
#dataSet:待划分数据集   axis:划分数据集的特征   value:需要返回的特征的值
def splitDataSet(dataSet,axis,value):  
    retDataSet=[]                                  
    for featVec in dataSet:                            #遍历元素
        if featVec[axis] == value:                     #符合条件的,抽取出来
            reducedFeatVec = featVec[:axis]
            reducedFeatVec.extend(featVec[axis+1:])
            retDataSet.append(reducedFeatVec)
    return retDataSet
# 计算给定数据集的信息熵
import math
def calcShannonEnt(dataSet):
    numEntries = len(dataSet)                          #获得数据集的行数
    labelCounts = {
    
    }                                   #用于保存每个标签出现次数的字典
    for featVec in dataSet:
        currentLabel = featVec[-1]                     #提取标签信息
        if currentLabel not in labelCounts.keys():     #如果标签未放入统计次数的字典,则添加进去
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1                 #标签计数
    shannonEnt = 0.0                                   #熵初始化
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntries      #选择该标签的概率
        shannonEnt -= prob*math.log(prob, 2)                #根据信息熵公式计算
    return shannonEnt                                  #返回经验熵
# 选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0]) - 1       #特征数量
    baseEntropy = calcShannonEnt(dataSet)   #计数数据集的香农熵
    bestInfoGain = 0.0                      #信息增益
    bestFeature = -1                        #最优特征的索引值
    for i in range(numFeatures):            #遍历数据集的所有特征
        featList = [example[i] for example in dataSet]          #获取dataSet的第i个所有特征
        uniqueVals = set(featList)                              #创建set集合{},元素不可重复
        newEntropy = 0.0                                        #信息熵
        for value in uniqueVals:                                #循环特征的值
            subDataSet = splitDataSet(dataSet, i, value)        #subDataSet划分后的子集
            prob = len(subDataSet) / float(len(dataSet))        #计算子集的概率
            newEntropy += prob * calcShannonEnt((subDataSet)) 
        infoGain = baseEntropy - newEntropy                     #计算信息增益
        print("第%d个特征的信息增益为%.3f" % (i, infoGain))      #打印每个特征的信息增益
        if (infoGain > bestInfoGain):                           #计算信息增益
            bestInfoGain = infoGain                             #更新信息增益,找到最大的信息增益
            bestFeature = i                                     #记录信息增益最大的特征的索引值
    return bestFeature                                          #返回信息增益最大特征的索引值
# 统计出现次数最多的元素(类标签)
def majorityCnt(classList):
    classCount={
    
    }  #统计classList中每个类标签出现的次数
    for vote in classList:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)    #根据字典的值降序排列
    return sortedClassCount[0][0]  #返回出现次数最多的类标签
# 创建决策树
def createTree(dataSet,labels):
    classList = [example[-1] for example in dataSet]           #取分类标签
    if classList.count(classList[0]) == len(classList):        #如果类别完全相同,则停止继续划分
        return classList[0]
    if len(dataSet[0]) == 1:                                   #遍历完所有特征时返回出现次数最多的类标签
        return majorityCnt(classList)
    bestFeat = chooseBestFeatureToSplit(dataSet)               #选择最优特征
    bestFeatLabel = labels[bestFeat]                           #最优特征的标签
    myTree = {
    
    bestFeatLabel:{
    
    }}                                #根据最优特征的标签生成树
    del(labels[bestFeat])                                      #删除已经使用的特征标签
    featValues = [example[bestFeat] for example in dataSet]    #得到训练集中所有最优特征的属性值
    uniqueVals = set(featValues)                               #去掉重复的属性值
    for value in uniqueVals:                                   #遍历特征,创建决策树
        subLabels = labels[:]                                  #复制所有标签,这样树就不会弄乱现有标签
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
    return myTree
import matplotlib
import matplotlib.pyplot as plt
 
# 定义文本框和箭头格式
decisionNode = dict(boxstyle="square", fc="0.8")  #boxstyle文本框样式、fc=”0.8” 是颜色深度
leafNode = dict(boxstyle="round4", fc="0.8")      #叶子节点
arrow_args = dict(arrowstyle="<-")                #定义箭头
 
# 绘制带箭头的注解
def plotNode(nodeTxt, centerPt, parentPt, nodeType):
    #createPlot.ax1是表示: ax1是函数createPlot的一个属性
    createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction',xytext=centerPt,
                            textcoords='axes fraction',va="center", ha="center", bbox=nodeType, arrowprops=arrow_args)
 
# 获取叶节点的数目和树的层数
def getNumLeafs(myTree):
    numLeafs = 0                                      # 初始化
    firstStr = list(myTree.keys())[0]                 # 获得第一个key值(根节点)
    secondDict = myTree[firstStr]                     # 获得value值
    for key in secondDict.keys():
        if type(secondDict[key]).__name__ == 'dict':  # 测试节点的数据类型是否为字典
            numLeafs += getNumLeafs(secondDict[key])  # 递归调用
        else:
            numLeafs += 1
    return numLeafs
 
# 获取树的深度
def getTreeDepth(myTree):
    maxDepth = 0                                           # 初始化
    firstStr = list(myTree.keys())[0]                      # 获得第一个key值(根节点)
    secondDict = myTree[firstStr]                          # 获得value值
    for key in secondDict.keys():
        if type(secondDict[key]).__name__ == 'dict':       # 测试节点的数据类型是否为字典
            thisDepth = 1 + getTreeDepth(secondDict[key])  # 递归调用
        else:
            thisDepth = 1
        if thisDepth > maxDepth:
            maxDepth = thisDepth
    return maxDepth
 
# 在父子节点间填充文本信息
def plotMidText(cntrPt, parentPt, txtString):
    xMid = (parentPt[0] - cntrPt[0]) / 2.0 + cntrPt[0]
    yMid = (parentPt[1] - cntrPt[1]) / 2.0 + cntrPt[1]
    createPlot.ax1.text(xMid, yMid, txtString, va="center", ha="center", rotation=30)
 
# 画树
def plotTree(myTree, parentPt, nodeTxt):
    numLeafs = getNumLeafs(myTree)        # 获取树高
    depth = getTreeDepth(myTree)          # 获取树深度
    firstStr = list(myTree.keys())[0]     # 这个节点的文本标签
    cntrPt = (plotTree.xOff + (1.0 + float(numLeafs)) / 2.0 / plotTree.totalW, plotTree.yOff) #plotTree.totalW, plotTree.yOff全局变量,追踪已经绘制的节点,以及放置下一个节点的恰当位置
    plotMidText(cntrPt, parentPt, nodeTxt)                #标记子节点属性
    plotNode(firstStr, cntrPt, parentPt, decisionNode)
    secondDict = myTree[firstStr]
    plotTree.yOff = plotTree.yOff - 1.0 / plotTree.totalD  #减少y偏移
    for key in secondDict.keys():
        if type(secondDict[key]).__name__ == 'dict':
            plotTree(secondDict[key], cntrPt, str(key))
        else:
            plotTree.xOff = plotTree.xOff + 1.0 / plotTree.totalW
            plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)
            plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))
    plotTree.yOff = plotTree.yOff + 1.0 / plotTree.totalD
 
# 绘制决策树
def createPlot(inTree):
    fig = plt.figure(1, facecolor='white')                          # 创建一个新图形
    fig.clf()                                                       # 清空绘图区
    font = {
    
    'family': 'MicroSoft YaHei'}
    matplotlib.rc("font", **font)
    axprops = dict(xticks=[], yticks=[])
    createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)
    plotTree.totalW = float(getNumLeafs(inTree))
    plotTree.totalD = float(getTreeDepth(inTree))
    plotTree.xOff = -0.5 / plotTree.totalW;
    plotTree.yOff = 1.0;
    plotTree(inTree, (0.5, 1.0), '')
    plt.show()
# 定义文本框和箭头格式
decisionNode = dict(boxstyle="square", fc="0.8")  #boxstyle文本框样式、fc=”0.8” 是颜色深度
leafNode = dict(boxstyle="round4", fc="0.8")      #叶子节点
arrow_args = dict(arrowstyle="<-")                #定义箭头
 
# 绘制带箭头的注解
def plotNode(nodeTxt, centerPt, parentPt, nodeType):
    createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction',xytext=centerPt,
                            textcoords='axes fraction',va="center", ha="center", bbox=nodeType, arrowprops=arrow_args)
def createDataSet():
    dataSet = [[3, 2, 1,'yes'],                        #数据集
            [3, 1, 1, 'yes'],
            [3, 0, 1, 'no'],
            [3, 2, 0, 'yes'],
            [3, 1, 0, 'yes'],
            [2, 2, 1, 'yes'],
            [2, 2, 0, 'yes'],
            [2, 1, 0, 'no'],
            [2, 1, 1, 'yes'],
            [2, 0, 1, 'no'],
            [2, 0, 0, 'no'],
            [1, 0, 0, 'no'],
            [1, 1, 0, 'no'],
            [1, 0, 1, 'no'],
            [2, 3, 1, 'no'],
            [3, 3, 0, 'yes'],
            [1, 2, 0, 'yes'],
            [1, 2, 1, 'yes'],]
 
    # 气温:3热,2适中, 1冷; 1饿, 0不饿
    labels = ['气温', '赖床人数', '饿不饿']        #特征标签
    return dataSet, labels                             #返回数据集和分类属性
 
 
if __name__ == '__main__':
    dataSet, labels = createDataSet()
    myTree = createTree(dataSet, labels)
    print(myTree)
    createPlot(myTree)

結果として得られる決定木は次のとおりです。

ここに画像の説明を挿入

エラー解決:

Error:'dict' object has no attribute 'iteritems'
问题:应该数据集有问题,有重复的了,特征一样,但是标签不同,需要回去修改数据集。

Error:UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xc8 in position 0: invalid continuation byte
将csv读取时设置:encoding="unicode_escape"

要約する

長所と短所

アドバンテージ 欠点がある
効果の可視化で意思決定プロセスを直感的に把握できる デシジョン ツリーはあまり複雑に構築しないでください。そうしないと、簡単にオーバーフィットしてしまいます。
計算の複雑さは高くなく、出力結果は理解しやすく、中間値の欠落の影響を受けず、無関係な特徴データを処理できます。

デシジョン ツリーは、人生から人生へと伝わる機械学習アルゴリズムです。誰もが独自のデシジョン ツリーを持ち、独自の除算を通じてさまざまな条件の重みを計算します。より大きな重みが、私たちが選択するバイアスです。

おすすめ

転載: blog.csdn.net/weixin_51961968/article/details/127839889