機械学習実践チュートリアル (6): デシジョン ツリー

デシジョンツリー

決定木とは何ですか? デシジョン ツリーは、基本的な分類および回帰手法です。分かりやすい例で言うと、下図のフローチャートは決定木であり、四角は決定ブロック、楕円は終了ブロックを表しており、結論が得られて操作を終了できることを示しています。判定モジュールから始まる左右の矢印は分岐と呼ばれ、別の判定モジュールや終了モジュールに到達することができます。分類デシジョン ツリー モデルは、インスタンスの分類を記述するツリー構造であることも理解できます。デシジョン ツリーはノードと有向エッジで構成されます。ノードには、内部ノードとリーフ ノードの 2 種類があります。内部ノードは機能または属性を表し、リーフ ノードはクラスを表します。混乱していますか?? 以下の図に示す決定木では、長方形と楕円形がノードです。長方形のノードは内部ノード、楕円形のノードはリーフ ノード、ノードから引かれた左右の矢印は有向エッジです。最上位ノードはデシジョン ツリーのルート ノードです。このように、node文とmodule文が対応していて分かりやすいです。

この記事のテキストの大部分は、https://cuijiahua.com/blog/2017/11/ml_2_decion_tree_1.html、コード、および一部のオリジナルから転載したものです。

ここに画像の説明を挿入します
このフローチャートに戻りましょう。はい、正しくお読みいただけたでしょう。これは架空のデート対象分類システムです。まず、合コンの相手が家を持っているかどうかを検出します。部屋に余裕がある場合は、このブラインド デートでさらに連絡を取ることを検討してください。余地がない場合は、ブラインド デートにやる気があるかどうかを観察します。そうでない場合は、さようならを言うだけです。この時点で、「あなたはとてもいい人ですが、私たちには適していません。」と言うことができます。ブラインドデートを候補者リストに入れてください。候補者リストと呼ぶには良い言い方ですが、やや不完全な言い方をすると、スペアタイヤです。

しかし、これは合コンの単純な分類システム、単なる分類にすぎません。実際の状況はさらに複雑であり、考慮すべき点は多岐にわたる可能性があります。良い気性ですか?料理はできますか?家事をする気はありますか?家族には子供が何人いますか? 親は何をしますか?ああ、なんてことだ、もう言いたくない、考えるだけでとても怖い。

決定木は if-then ルールのセットと考えることができます。決定木を if-then ルールに変換するプロセスは次のとおりです: 決定木のルート ノードからリーフ ノードまでです。各パスがルールを構築します。 ; パス上の内部ノードの特性はルールの条件に対応し、葉ノードのクラスはルールの結論に対応します。デシジョン ツリーのパス、またはそれに対応する if-then ルールのセットには、相互に排他的で完全であるという重要な特性があります。つまり、すべてのインスタンスは 1 つのパスまたはルールによってカバーされ、1 つのパスまたはルールのみがカバーされます。ここで扱われるのは、インスタンスの特性がパス上の特性と一致していること、またはインスタンスがルールの条件を満たすことを意味します。

デシジョン ツリーを使用して予測を行うには、次のプロセスが必要です。

  • データの収集: 任意の方法を使用できます。たとえば、ブラインド デート システムを構築したい場合は、仲人から、またはブラインド デートにインタビューすることでデータを取得できます。彼らが考慮した要素と最終的な選択結果に基づいて、私たちが使用できるデータを得ることができます。
  • データの準備: データを収集した後、それを整理し、収集したすべての情報を特定のルールに従って分類し、その後の処理を容易にするためにフォーマットする必要があります。
  • データの分析: 任意の方法を使用できます。デシジョン ツリーが構築された後、デシジョン ツリー グラフが期待を満たしているかどうかを確認できます。
    学習アルゴリズム: 決定木を構築するプロセスであり、決定木のデータ構造を構築する決定木学習とも言えます。
  • アルゴリズムのテスト: 経験的ツリーを使用したエラー率の計算。誤り率が許容範囲に達すると、決定木を使用できるようになります。
    アルゴリズムを使用する: このステップでは任意の教師あり学習アルゴリズムを使用でき、デシジョン ツリーを使用すると、データの固有の意味をより深く理解できます。

デシジョンツリー構築の準備

デシジョン ツリーを使用して予測を行う各ステップは重要ですが、データ収集が不十分だと、エラー率の低いデシジョン ツリーを構築するための機能が不十分になります。データの特徴は十分ですが、どの特徴を使用すればよいかわからない場合、良好な分類効果を備えたデシジョン ツリー モデルを構築できなくなります。アルゴリズムの観点から見ると、デシジョン ツリーの構築が私たちの中心的なコンテンツです。

決定木を構築するにはどうすればよいでしょうか? 一般に、このプロセスは、特徴選択、デシジョン ツリー生成、デシジョン ツリー プルーニングの 3 つのステップに要約できます。

機能の選択

特徴の選択は、トレーニング データを分類する機能を持つ特徴を選択することで構成されます。これにより、決定木の学習の効率が向上します。特徴を使用した分類の結果がランダムな分類の結果とあまり変わらない場合、その特徴は分類能力がないと言われます。経験的には、そのような特徴を破棄しても、決定木の学習の精度にはほとんど影響がありません。通常、特徴選択の基準は情報利得 (情報利得) または情報利得比率ですが、この記事では簡単のため、特徴選択の基準として情報利得を使用します。では、情報獲得とは何でしょうか?情報の獲得について説明する前に、一連の例であるローン申請サンプル データ テーブルを見てみましょう。

ここに画像の説明を挿入します
ローン申請のデシジョン ツリーを、与えられたトレーニング データを通じて学習し、今後のローン申請を分類するために使用できることが期待されています。つまり、新規顧客がローンを申請するときに、デシジョン ツリーを使用してローンを承認するかどうかを決定します。申請者の特性に基づいてローンの申請を行います。

特徴の選択とは、特徴空間を分割するためにどの特徴を使用するかを決定することです。たとえば、上記のデータ テーブルから 2 つの可能な決定木を取得しました。それぞれの決定木は、異なる特性を持つ 2 つのルート ノードで構成されています。
ここに画像の説明を挿入します
図 (a) に示すルート ノードは年齢によって特徴付けられ、3 つの値を持ち、異なる値に対応する異なる子ノードを持ちます。図 (b) に示すルート ノードはワークによって特徴付けられ、2 つの値を持ち、異なる値に対応する異なる子ノードを持ちます。どちらのデシジョン ツリーもここから続行できます。問題は、どの機能を選択するのが良いかということです。これには、機能を選択するための基準を決定する必要があります。直観的には、特徴がより優れた分類能力を持っている場合、つまり、トレーニング データ セットがこの特徴に従ってサブセットに分割され、各サブセットが現在の条件下で最適な分類を有する場合、この特徴を選択する必要があります。情報利得は、この直感的な基準をよく表しています。

情報獲得とは何ですか?データセットを分割した後の情報の変化を情報ゲインと呼びます 情報ゲインの計算方法がわかれば、データセットを特徴量ごとに分割して得られる情報ゲインを計算できます 情報ゲインが最も高い特徴を取得することが最良の選択です。
情報利得 = データ全体の不確実性 - 特定の特徴条件の不確実性 = この特徴がどの程度確実性を高めるか

データの不確実性を判断する方法は、シャノンのエントロピーの概念につながります。

シャノンのエントロピー

どのデータ パーティションが最適であるかを評価する前に、情報ゲインの計算方法を学ぶ必要があります。集合的な情報の尺度はシャノン エントロピーまたは単にエントロピーと呼ばれ、その名前は情報理論の父であるクロード シャノンに由来しています。

情報ゲインとエントロピーが何なのか理解できなくても、心配しないでください。それらは生まれた時から世界にとって非常に混乱する運命にあるからです。クロード・シャノンが情報理論を書いた後、ジョン・フォン・ノイマンは、それが何を意味するのか誰も知らなかったため、「エントロピー」という用語の使用を提案しました。

情報エントロピーの原理を徹底的に理解したい場合は、導出に使用されるグラフ原理ラグランジュ乗数法を参照し、同時に対数関数の特性を理解してください。

  1. ax =N (a>0、および a≠1) の場合、数値 x は a を底とする N を底とする対数と呼ばれ、次のように記録されます。 N の底は実数と呼ばれます。
  2. Y=logaX の場合、Y a の乗算が X に等しいことを意味します
  3. 基数 a は 0 ~ 1 の間で単調減少し、1 より大きい場合は単調増加します。
  4. a>1 の場合 x が 0 ~ 1 の場合、y は負の数 x=1 y=0 x>1 y は正の数
  5. ln は自然対数、つまり e を底とする対数を求める演算子です。
    e は定数で、2.71828183...
    lnx は ln(x)、つまり e を底とする x の対数、つまり e の何乗が x に等しいか、と理解できます。
    lnx=loge^x、logeE=Ine=1

ここに画像の説明を挿入します
エントロピーは情報の期待値として定義されます。情報理論と確率統計において、エントロピーは確率変数の不確実性の尺度です。分類したいものが複数のカテゴリに分かれる可能性がある場合、記号 xi の情報は次のように定義されます:
ここに画像の説明を挿入します
p(xi) はそのカテゴリを選択する確率 たとえば、10 人のチームの場合、性別は男の子です(3) と女の子 (7) の 2 つのカテゴリでは、上の式の対数は 2 に基づくか、または e を底 (自然対数) にすることができます。

男生熵=-log2 3/10 =  1.7369655941662063  
女生熵=-log2 7/10 = 0.5145731728297583
整个团队的熵=男生熵+女生熵 =2.2515387669959646

log2 p (女子より低いことに
注意してください。

上記の式を通じて、すべてのカテゴリの情報を取得できます。エントロピーを計算するには、すべてのカテゴリのすべての可能な値に含まれる情報の期待値 (数学的期待値) を計算する必要があります。これは次の式で得られます。中間項 n はカテゴリの数です
ここに画像の説明を挿入します
エントロピーが大きいほど、確率変数の不確実性も大きくなります。

エントロピーの確率がデータ推定 (特に最尤推定) から得られる場合、対応するエントロピーは経験的エントロピーと呼ばれます。データからの推定とは何ですか?たとえば、データが 10 個あり、カテゴリ A とカテゴリ B の 2 つのカテゴリがあるとします。そのうち 7 個のデータがカテゴリ A に属しているため、カテゴリ A である確率は 10 分の 7 です。このうち、カテゴリ B に属するデータが 3 つある場合、カテゴリ B の確率は 10 分の 3 です。簡単に説明すると、データに基づいて確率が計算されるということです。ローン申請サンプル データ テーブル内のデータをトレーニング データ セット D として定義すると、トレーニング データ セット D の経験的エントロピーは H(D) となり、|D| はそのサンプル容量とサンプル数を表します。K 個のクラス Ck, = 1,2,3,…,K,|Ck| がクラス Ck に属するサンプルの数であると仮定すると、経験的エントロピーの公式は次のように書くことができます。 これに従って経験的エントロピー H( D) を計算します
ここに画像の説明を挿入します
。式、サンプル ローン申請データ シートのデータを分析します。最終的な分類結果は、「貸付」と「貸付なし」の 2 つのカテゴリのみになります。表のデータ統計によると、15件のデータのうち、9件のデータは貸し出しの結果、6件のデータは貸し出しなしの結果となっています。したがって、データセット D の経験的エントロピー H(D) は次のようになります。
ここに画像の説明を挿入します
計算後、データセット D の経験的エントロピー H(D) の値は 0.971 であることがわかります。

経験的エントロピーを計算するコードを作成する

コードを記述する前に、まずデータセットの属性に注釈を付けます。

  • 年齢: 0 は若者を表し、1 は中年を表し、2 は高齢者を表します。
  • 仕事がある: 0 はいいえ、1 ははいを意味します。
  • 自分の家を所有します。0 はいいえ、1 ははいを意味します。
  • 信用状態: 0 は平均、1 は良好、2 は非常に良好を意味します。
  • カテゴリ (ローンを与えるかどうか): no は「いいえ」を意味し、yes は「はい」を意味します。
  • これらを決定した後、データセットを作成し、経験的エントロピーを計算できます。コードは次のように記述されます。
#%%

#数据集,yes表示放贷,no表示不放贷
'''
具体参考:案例图
 特征1表示年龄 0表示青年,1表示中间,2表示老年
 特征2表示是否有工作  0表示否,1表示有
 特征3表示是否有自己的房子 0表示否 1表示有
 特征4是信贷情况 0表示一般 1表示好 2表示非常好。
'''
import numpy as np
dataSet = np.array([[0, 0, 0, 0, 'no'],         
            [0, 0, 0, 1, 'no'],
            [0, 1, 0, 1, 'yes'],
            [0, 1, 1, 0, 'yes'],
            [0, 0, 0, 0, 'no'],
            [1, 0, 0, 0, 'no'],
            [1, 0, 0, 1, 'no'],
            [1, 1, 1, 1, 'yes'],
            [1, 0, 1, 2, 'yes'],
            [1, 0, 1, 2, 'yes'],
            [2, 0, 1, 2, 'yes'],
            [2, 0, 1, 1, 'yes'],
            [2, 1, 0, 1, 'yes'],
            [2, 1, 0, 2, 'yes'],
            [2, 0, 0, 0, 'no']])
labels = ['不放贷', '放贷']
'''
  计算经验熵
  D代表传入的数据集
'''
def ShannonEnt(D):
    #去除重复元素的结论的分类:[yes,no]
    kArray=np.unique(D[:,4].reshape(1,len(D)))
    #计算出最终分类的个数
    k=len(kArray)
    #获取整个样本集的个数
    D_count=len(D)
    #经验熵
    HD=0;
    #循环多个分类,计算这个分类的熵,最后求和
    for i in range(k):
        #获取等于当前分类的数据行
        ck=[row for row in D if row[4]==kArray[i]]
        HD-=len(ck)/D_count *np.log2(len(ck)/D_count) 
    return HD;
HD_=ShannonEnt(dataSet)
print("整个数据经验熵:",HD_)  

出力結果:
データ全体の経験的エントロピー:0.9709505944546686

条件付きエントロピー

エントロピーが何であるかはわかりますが、条件付きエントロピーとは一体何でしょうか? 条件付きエントロピー H(Y|X) は、既知の確率変数 の条件下での確率変数 Y の不確実性を表します。これは、D
ここに画像の説明を挿入します
の与えられた条件下での Y の条件付き確率分布のD1、D2、・・・、Dn}、|Di|はDiのサンプル数である。部分集合 Di 内の Ck に属するサンプルの集合を Dik とします。つまり、Dik = Di ∩ Ck であり、|Dik| は Dik 内のサンプルの数です。したがって、経験的な条件付きエントロピーの式は次のようになります。
ここに画像の説明を挿入します
実際、合計は次のようになります。 (データ全体の現在の特性を持つ同じデータ セットの確率 * このデータ セットの最終分類のエントロピーを取得します)

'''
  计算条件熵
  H(D|0) 计算某个特征列的条件熵,当年龄特征的情况下,是否房贷不确定的情况,越大越不确定
'''     
def calcConditionShannon(D,index):
    #去除重复元素的index列的数组
    featureType=np.unique(D[:,index].reshape(1,len(D)))
    featureTypeCount=len(featureType)
    #获取整个样本集的个数
    D_count=len(D)
    HDA=0;
    for i in range(featureTypeCount):
        Di=np.array([row for row in D if row[index]==featureType[i]])
        HDA+=len(Di)/D_count*ShannonEnt(Di)
    return HDA;
print("年龄特征条件熵",calcConditionShannon(dataSet,0))

出力: 年齢特徴の条件付きエントロピー 0.8879430945988998

情報獲得

情報ゲイン = データ全体の不確実性 - 特定の特徴条件の不確実性 = この特徴がどの程度確実性を高めるか
情報ゲイン = 経験的エントロピー - 現在の特徴条件のエントロピー
情報ゲインは特徴に相対的です 情報ゲインが大きいほど、最終的な分類結果に対する特徴の影響を考慮するには、最終的な分類結果に最も大きな影響を与える特徴を分類特徴として選択する必要があります。

条件付きエントロピーと経験的条件付きエントロピーの概念が明確になります。次に、情報の獲得について話しましょう。前述したように、情報の獲得は機能に比例します。したがって、トレーニング データ セット D 上の特徴 A の情報ゲイン g(D,A) は、次の条件の下で、セット D の経験的エントロピー H(D) と D の経験的条件付きエントロピー H(D|A) の差として定義されます。機能 A. の条件が与えられている場合、つまり、
ここに画像の説明を挿入します
概念的なことをたくさん話した後、理解できなくても問題ありません。例をいくつか挙げて、戻って概念を見れば理解できるでしょう。

ローン申請サンプル データシートを例として取り上げます。特徴 A1 である年齢列のデータを見ると、若者、中年、高齢者の 3 つのカテゴリがあります。若者の年齢のデータのみを調べます。若者の年齢のデータは 5 つあります。したがって、若者の年齢のデータがトレーニング データセットに表示される確率は 5/15 で、これは次のようになります。 3分の1。同様に、訓練データセットに中高年のデータが現れる確率は 3 分の 1 です。ここでは若者のデータのみを見ていきますが、5 つのデータのうち最終的に融資が受けられたことを示すデータは 2 つだけであるため、最終的に融資を受けられる確率は 5 分の 2 です。最終的に融資を受けられる確率は、それぞれ 5 分の 3 と 5 分の 4 です。したがって、年齢の情報利得を計算するプロセスは次のようになります。
ここに画像の説明を挿入します
同様に、他の特徴 g(D,A2)、g(D,A3)、g(D,A4) の情報利得を計算します。最後に各
ここに画像の説明を挿入します
特徴量の情報利得を比較すると、特徴量 A3 (持ち家がある) の情報利得値が最も大きいため、A3 が最適な特徴量として選択されます。

数式を使って情報ゲインを計算する方法を学習しました。情報ゲインを計算するコードを書いてみましょう。

'''
    计算某个特征的信息增益
    信息增益=整个数据的不确定性-某个特征条件的不确定=这个特征增强了多少确定性
'''
def calaInfoGrain(D,index):
    return ShannonEnt(dataSet)-calcConditionShannon(D,index)
print("年龄的信息增益",HD_-calcConditionShannon(dataSet,0))
print("工作的信息增益",calaInfoGrain(dataSet,1))

feature_count=len(dataSet[0])
for i in range(feature_count-1):
    print("第"+str(i)+"个特征的信息增益",HD_-calcConditionShannon(dataSet,i))

出力:
年齢の情報利得 0.08300749985576883
仕事の情報利得 0.32365019815155627 0
番目の特徴の情報利得 0.08300749985576883 1 番目の特徴の情報利得 0.32365019815155627 2
番目
の特徴の情報利得 0.41997309 4021 97493
番目の特徴の情報ゲインは 0.36298956253708536

私たち自身の計算結果を比較すると、結果は完全に正しいことがわかりました。最適な特徴のインデックス値は 2、つまり特徴 A3 (独自の家がある) です。

決定木の生成

私たちは、経験的エントロピーの計算や最適な特徴の選択など、データ セットから決定木アルゴリズムを構築するために必要なサブ機能モジュールを学習しました。その動作原理は次のとおりです: 元のデータ セットを取得し、データ セットを分割します。最良の属性値に基づくデータ セットでは、2 つ以上の固有値が存在する可能性があるため、2 つの分岐よりも大きいデータ セットのパーティションが存在する可能性があります。最初のパーティションの後、データ セットはツリーのブランチ内の次のノードに渡されます。このノードでは、データを再度分割できます。したがって、再帰原則を使用してデータセットを処理できます。

C4.5、ID3、CART など、デシジョン ツリーを構築するためのアルゴリズムは多数ありますが、これらのアルゴリズムは、実行時にデータをグループに分割するたびに機能を消費するとは限りません。データをグループに分割するたびに特徴の数が減少するわけではないため、これらのアルゴリズムを実際に使用すると、特定の問題が発生する可能性があります。現時点ではこの問題を考慮する必要はありませんが、アルゴリズムが実行を開始する前に列の数を数えて、アルゴリズムがすべての属性を使用しているかどうかを確認する必要があるだけです。

デシジョン ツリー生成アルゴリズムは、継続できなくなるまでデシジョン ツリーを再帰的に生成します。この方法で生成されたツリーは、多くの場合、トレーニング データを非常に正確に分類しますが、未知のテスト データの分類はそれほど正確ではありません。つまり、過剰適合が発生します。過学習の理由は、学習中にトレーニング データの正しい分類を改善する方法を考慮しすぎて、過度に複雑な決定木を構築することです。この問題の解決策は、決定木の複雑さを考慮し、生成される決定木を単純化することです。

ディシジョンツリーの構築

ID3 アルゴリズムの中核は、決定木の各ノードで情報利得基準に対応する特徴を選択し、決定木を再帰的に構築することです。具体的な方法は、ルート ノードから開始して、ノードの考えられるすべての特徴の情報ゲインを計算し、最大の情報ゲインを持つ特徴をノードの特徴として選択し、さまざまな値に基づいてサブノードを確立します。すべての特徴の情報利得が非常に小さくなるか、特徴が選択できなくなるまで、子ノードで上記のメソッドを再帰的に呼び出して決定木を構築します。最後に、決定木が得られます。ID3 は、最尤法を使用して確率モデルを選択することと同じです。

ID3アルゴリズム

ID3 を使用してデシジョン ツリーを構築する前に、データを分析しましょう。
ここに画像の説明を挿入します
3 番目の列でソートされたデータ

print(dataSet[np.argsort(dataSet[:,2])])
输出:
[['0' '0' '0' '0' 'no']
 ['0' '0' '0' '1' 'no']
 ['0' '1' '0' '1' 'yes']
 ['0' '0' '0' '0' 'no']
 ['1' '0' '0' '0' 'no']
 ['1' '0' '0' '1' 'no']
 ['2' '1' '0' '1' 'yes']
 ['2' '1' '0' '2' 'yes']
 ['2' '0' '0' '0' 'no']
 ['0' '1' '1' '0' 'yes']
 ['1' '1' '1' '1' 'yes']
 ['1' '0' '1' '2' 'yes']
 ['1' '0' '1' '2' 'yes']
 ['2' '0' '1' '2' 'yes']
 ['2' '0' '1' '1' 'yes']]

特徴量A3(自分の家がある)の情報利得値が最も大きいため、特徴量A3がルートノードの特徴量として選択される。トレーニング セット D を 2 つのサブセット D1 (A3 の値は「はい」) と D2 (A3 の値は「いいえ」) に分割します。D1 は同じクラスのサンプル点しかないので葉ノードとなり、このノードのクラスマークは「Yes」となります。
その中でもD1は

 ['0' '1' '1' '0' 'yes']
 ['1' '1' '1' '1' 'yes']
 ['1' '0' '1' '2' 'yes']
 ['1' '0' '1' '2' 'yes']
 ['2' '0' '1' '2' 'yes']
 ['2' '0' '1' '1' 'yes']]

D1=1 の場合、分類の結論は 1 つだけであるため、葉ノードになります。分岐がない場合、
D2 は次のようになります。

['0' '0' '0' '0' 'no']
 ['0' '0' '0' '1' 'no']
 ['0' '1' '0' '1' 'yes']
 ['0' '0' '0' '0' 'no']
 ['1' '0' '0' '0' 'no']
 ['1' '0' '0' '1' 'no']
 ['2' '1' '0' '1' 'yes']
 ['2' '1' '0' '2' 'yes']
 ['2' '0' '0' '0' 'no']

D2 の場合、新しい特徴を特徴 A1 (年齢)、A2 (雇用)、および A4 (信用状況) から選択する必要があり、各特徴の情報利得が計算されます。計算によれば、特徴 A2 (雇用) は、最大の情報利得が
ここに画像の説明を挿入します
ノード特性として選択されます。A2 には 2 つの可能な値があるため、このノードから 2 つのサブノードが派生します。「はい」(ジョブがある) に対応するサブノードは 3 つのサンプルを含み、同じカテゴリに属する​​ため、これはリーフ ノード ポイントです。 、クラス マークは「Yes」です。もう 1 つは、「No」(作業なし) に対応する子ノードで、6 つのサンプルが含まれています。これらも同じクラスに属しているため、これもリーフ ノードであり、クラス マークは「」です。いいえ」。
残りのデータは A2 に従ってソートされます。

[['0' '0' '0' '0' 'no']
 ['0' '0' '0' '1' 'no']
 ['0' '0' '0' '0' 'no']
 ['1' '0' '0' '0' 'no']
 ['1' '0' '0' '1' 'no']
 ['2' '0' '0' '0' 'no']
 ['0' '1' '0' '1' 'yes']
 ['2' '1' '0' '1' 'yes']
 ['2' '1' '0' '2' 'yes']]

A2=0の結果はすべてno、A1=1の結果はyesであることがわかり、それ以外の分類はありません。ノードは作業点で終了します。リーフノードがノードであることがわかります。融資するか否かの結論
ここに画像の説明を挿入します
とその分岐点が特徴の価値となります。

コードを記述してデシジョン ツリーを構築する

ディクショナリを使用してディシジョン ツリーの構造を保存します。たとえば、前のセクションで分析したディシジョン ツリーは次のように表現できます。

{'有自己的房子': {0: {'有工作': {0: 'no', 1: 'yes'}}, 1: 'yes'}}

コードは次のように実装されます

#%%

'''
  将数据按照值指定特征列分组,,比如有房子=1的数据行和无房子=0的数据行
  {
     0:[[]]
     1:[[]]
  }
'''
colLabels=["年龄","有工作","有自己的房子","信贷情况"]
def splitData(D,index):
    kArray=np.unique(D[:,index].reshape(1,len(D)))
    #循环多个分类,计算这个分类的熵,最后求和
    returnJSon={};
    for i in range(len(kArray)):
        #获取等于当前分类的数据行
        ck=[row for row in D if row[index]==kArray[i]]
        returnJSon[i]=np.array(ck)
    return returnJSon;
    
def createDecisionTree(D):
    buildTree=None
    #如果传入的D没有数据或者第5列(是否贷款)只有一个分类值,就说明已经是叶子节点了,直接返回结论值
    resultUniqueArray=np.unique(D[:,4].reshape(1,len(D)))
    print(resultUniqueArray,len(D),len(resultUniqueArray))
    if(len(D)==0 or len(resultUniqueArray)==1):
        return resultUniqueArray[0]
    #获取特征数
    feature_count=D.shape[1]
    #算出每个特征的信息增益
    grain=[calaInfoGrain(D,i)for i in range(feature_count-1)]
    #获取信息增益最大的特征值
    maxFeatureIndex=np.argmax(grain);
    #创建一个json对象,里面有个当前特征名称的对象:比如{'有自己的房子': {}}
    buildTree={colLabels[maxFeatureIndex]:{}};
    #循环每个独立的特征值 
    featureGroup=splitData(D,maxFeatureIndex)
    for featureValue in featureGroup:
        buildTree[colLabels[maxFeatureIndex]][featureValue]=createDecisionTree(featureGroup[featureValue])
    return buildTree;
        
    
print(createDecisionTree(dataSet))

デシジョンツリーの視覚化

Graphviz はシンプルで理解しやすいです。ここでは視覚化にgraphviz を使用します。graphviz をダウンロード
、環境を PATH に追加することを選択します。Pythonのインストールコンポーネント

pip install graphviz

コード描画

from graphviz import Digraph
import uuid

def graphDecisionTree(dot,treeNode,parentName,lineName):
    for key in treeNode:
        if type(key)==int:
            if type(treeNode[key])==str or type(treeNode[key])==np.str_:
                #因为会出现两个yes,所以可能不能出现一个分叉而直接指向了,所以名字加上个uuid区分
                node_name=str(treeNode[key])+str(uuid.uuid1())
                dot.node(name=node_name, label=str(treeNode[key]), color='red',fontname="Microsoft YaHei")
                dot.edge(str(parentName),str(node_name), label=str(key), color='red')
            else:
                graphDecisionTree(dot,treeNode[key],parentName,key)
        elif type(treeNode[key])==dict:
            graphDecisionTree(dot,treeNode[key],key,None)
        if type(key)==str or type(treeNode[key])==str:
            dot.node(name=key, label=str(key), color='red',fontname="Microsoft YaHei")
        if parentName is not None and lineName is not None:
            dot.edge(parentName,key, label=str(lineName), color='red')
dot = Digraph(name="pic", comment="测试", format="png")
graphDecisionTree(dot,decisionTreeJson,None,None)
dot.render(filename='my_pic',
               directory='.',  # 当前目录
               view=True)

出力フローチャート
ここに画像の説明を挿入します

デシジョン ツリーを使用して分類を実行する

トレーニング データに基づいてデシジョン ツリーを構築した後、それを使用して実際のデータを分類できます。データ分類を実行する場合、ツリーの構築に使用されるラベル ベクトルとともに決定木が必要です。そして、プログラムはテストデータと決定木の値を比較し、リーフノードに入るまでの処理を再帰的に実行し、最終的にテストデータをリーフノードが属する型として定義します。デシジョン ツリーを構築するコードには、featLabels パラメーターがあることがわかります。何に使われますか? 各分類ノードを記録するために使用され、決定木を使用して予測を行う際に、必要な分類ノードの属性値を順番に入力できます。たとえば、上記でトレーニングしたデシジョン ツリーを分類に使用する場合、冗長な情報を提供する必要はなく、その人に家があるかどうかと仕事があるかどうかという 2 つの情報だけを提供する必要があります。

決定木を使用した分類のコードは非常に単純で、次のように記述されます。

#%%
'''
    在决策树中判断传入的特征是否贷款
'''
def classfiy(decisionTreeJson,featureLabel,vecTest,index):
    if type(decisionTreeJson)==str or type(decisionTreeJson)==np.str_:
        return decisionTreeJson
    elif type(decisionTreeJson[featureLabel[index]])==dict :
        return classfiy(decisionTreeJson[featureLabel[index]][vecTest[index]],featureLabel,vecTest,index+1)
    else :
        return decisionTreeJson

print("是" if classfiy(decisionTreeJson,featureLabel,[1,0],0)=='yes' else "否")

デシジョンツリーの保存

シリアル化を使用するだけです

  import pickle
  #写入
  with open(filename, 'wb') as fw:
        pickle.dump(inputTree, fw)
#读取
 fr = open(filename, 'rb')
    json=pickle.load(fr)

Sklearn は決定木を使用してコンタクトアイのタイプを予測します

実践的な背景

この記事の本題に入りましょう。眼科医は患者が着用する必要があるコンタクトレンズの種類をどのように判断するのでしょうか? デシジョンツリーがどのように機能するかを理解すれば、人々が着用する必要があるレンズの種類を決定するのを支援することもできます。

コンタクト レンズ データセットは、目の状態を変える観察条件や医師が推奨するコンタクト レンズの種類を多数含む非常に有名なデータセットです。コンタクトレンズの種類には、ハードレンズ、ソフトレンズ、レンズなしなどがあります。データ ソースと UCI データベース、データ セットのダウンロード アドレス: https://github.com/lzeqian/machinelearntry/blob/master/sklearn_deciontree/lenses.txt

合計 24 セットのデータがあります。データのラベルは、年齢、処方箋、乱視、涙率、クラスです。つまり、1 列目は年齢、2 列目は症状、3 列目は乱視の有無です。現在、4 列目は涙の数、4 列目は涙の数、5 列が最終的な分類ラベルです。データは以下の図に示されています。
ここに画像の説明を挿入します
すでに作成された Python プログラムを使用してデシジョン ツリーを構築することもできますが、学習を継続する目的で、この記事では Sklearn を使用します。

Sklearn を使用してデシジョン ツリーを構築する

公式英語ドキュメントのアドレス: http://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html

sklearn.tree モジュールは、分類および回帰の問題を解決するためのデシジョン ツリー モデルを提供します。この実践コンテンツでは、
ここに画像の説明を挿入します
DecisionTreeClassifierとexport_graphvizを使用しており、前者は決定木の構築に、後者は決定木の可視化に使用されます。

DecisionTreeClassifier はデシジョン ツリーを構築します。

まず、合計 12 個のパラメーターを持つ DecisionTreeClassifier 関数を見てみましょう。

ここに画像の説明を挿入します

パラメータの説明は次のとおりです。

  • criterion: 特徴選択基準、オプションのパラメーター。デフォルトは gini で、エントロピーに設定できます。ジニとはジニ不純度のことで、集合から特定の結果を特定のデータ項目にランダムに適用した場合の期待誤差率であり、統計に基づいた考え方です。エントロピーとは、前回の記事で取り上げたシャノンエントロピーのことで、情報理論に基づいた考え方です。Sklearn ではデフォルトパラメータとして gini が設定されていますが、それに合わせて考慮すべきだったのですが、もしかしたら精度が高くなっているのではないでしょうか?ID3 アルゴリズムはエントロピーを使用し、CART アルゴリズムは gini を使用します。
  • スプリッタ: フィーチャ分割点選択の標準、オプションのパラメータ。デフォルトが最適で、ランダムに設定できます。各ノードの選択戦略。最適なパラメーターは、ジニやエントロピーなどのアルゴリズムに基づいて最適なセグメンテーション機能を選択します。Random は、いくつかの分割点の中から局所的な最適な分割点をランダムに見つけます。デフォルトの「best」はサンプル サイズが小さい場合に適しており、サンプル データ サイズが非常に大きい場合は、デシジョン ツリーの構築に「random」が推奨されます。
    -max_features: 分割時に考慮される特徴の最大数 (オプションのパラメーター)、デフォルトは None です。最適なセグメンテーションを探すときに考慮される特徴の最大数 (n_features は特徴の合計数) には、次の 6 つの状況があります。
  1. max_features が整数の場合、max_features の特徴が考慮されます。
  2. max_features が浮動小数点数の場合は、int(max_features * n_features) の特徴を考慮します。
  3. max_features が auto に設定されている場合、max_features = sqrt(n_features);
  4. max_features が sqrt に設定されている場合、max_featrues = sqrt(n_features) となり、auto と同じになります。
  5. max_features が log2 に設定されている場合、max_features = log2(n_features);
  6. max_features が None に設定されている場合、max_features = n_features、つまりすべての機能が使用されます。
  7. 一般に、サンプルの特徴の数が 50 未満など、それほど多くない場合は、デフォルトの「なし」を使用できますが、特徴の数が非常に多い場合は、今説明した他の値を柔軟に使用して、分割時に考慮される最大の特徴。決定木の生成時間を制御する数値。
  • max_ Depth: デシジョン ツリーの最大の深さ。オプションのパラメータ。デフォルトは None です。このパラメータはツリーのレベル数です。レベルの概念は、たとえばローンの例では、デシジョン ツリーのレベル数は 2 レベルであるということです。このパラメーターが None に設定されている場合、デシジョン ツリーはサブツリーの構築時にサブツリーの深さを制限しません。一般に、データが少ない場合や特徴が少ない場合は、この値を無視できます。または、min_samples_lipt パラメーターが設定されている場合は、サンプル数が min_smaples_split より少なくなるまで。モデルのサンプル サイズが大きく、多くの特徴がある場合は、データの分布に応じて最大深度を制限することをお勧めします。一般的に使用される値の範囲は 10 ~ 100 です。
  • min_samples_split: 内部ノードの再分割に必要なサンプルの最小数 (オプションのパラメーター)、デフォルトは 2 です。この値は、サブツリーの分割を継続できる条件を制限します。min_samples_split が整数の場合、内部ノードを分割するときに min_samples_split が最小サンプル数として使用されます。つまり、サンプルが min_samples_split サンプルより小さい場合は、分割を停止します。min_samples_split が浮動小数点数の場合、min_samples_split はパーセンテージ ceil(min_samples_split * n_samples) となり、数値は切り上げられます。サンプルサイズが大きくない場合は、この値を気にする必要はありません。サンプル サイズが非常に大きい場合は、この値を増やすことをお勧めします。
  • min_samples_leaf: リーフ ノードのサンプルの最小数、オプションのパラメーター、デフォルトは 1 です。この値は、リーフ ノードの最小サンプル数を制限します。リーフ ノードの数がサンプル数よりも少ない場合、そのノードは兄弟ノードとともに枝刈りされます。リーフ ノードには最小サンプル数が必要です。つまり、最終的にリーフ ノードに到達してリーフ ノードとしてカウントされるまでに必要なサンプル数です。1 に設定すると、このカテゴリにサンプルが 1 つしかない場合でも、デシジョン ツリーが構築されます。min_samples_leaf が整数の場合、min_samples_leaf がサンプルの最小数として使用されます。浮動小数点数の場合、min_samples_leaf はパーセンテージであり、上記と同様に celi(min_samples_leaf * n_samples) となり、数値は切り上げられます。サンプルサイズが大きくない場合は、この値を気にする必要はありません。サンプル サイズが非常に大きい場合は、この値を増やすことをお勧めします。
  • min_weight_fraction_leaf: リーフ ノードの最小サンプル重み合計 (オプションのパラメーター)、デフォルトは 0 です。この値は、リーフ ノードのすべてのサンプルの重み合計の最小値を制限します。この値より小さい場合は、その兄弟ノードとともに枝刈りされます。一般に、欠損値のあるサンプルが多数ある場合、または分類ツリー サンプルの分布カテゴリの偏差が大きい場合、サンプルの重みが導入されるため、この値に注意する必要があります。
  • max_leaf_nodes: リーフ ノードの最大数。オプションのパラメータ。デフォルトは None です。リーフ ノードの最大数を制限することで、過剰適合を防ぐことができます。制限が追加された場合、アルゴリズムはリーフ ノードの最大数内で最適なデシジョン ツリーを構築します。特徴量がそれほど多くない場合、この値は考慮する必要はありませんが、特徴量が多い場合は制限し、相互検証によって特定の値を取得できます。
  • class_weight: カテゴリの重み、オプションのパラメータ。デフォルトは None です。辞書、辞書リスト、またはバランスをとることもできます。サンプルの各カテゴリの重みを指定するのは、主に、トレーニング セットに特定のカテゴリのサンプルが多すぎて、トレーニングされたデシジョン ツリーがこれらのカテゴリに偏りすぎることを防ぐためです。カテゴリの重みは、{class_label:weight} の形式で指定できます。ここでは、各サンプルの重みを自分で指定することも、バランスを使用することもできます。バランスを使用すると、アルゴリズムが独自に重みを計算します。対応するサンプルの重みサンプルサイズが小さいカテゴリの評価は高くなります。もちろん、サンプル カテゴリの分布に明らかな偏りがない場合は、このパラメータを無視して、デフォルトの [なし] を選択できます。
  • random_state: オプションのパラメータ。デフォルトは None です。乱数のシード。証明書の場合、random_state は乱数生成器の乱数シードとして使用されます。乱数シード: 乱数が設定されていない場合、乱数は現在のシステム時間に関連付けられ、その時々で異なります。乱数シードを設定すると、異なるタイミングで生成された同じ乱数は、同じ乱数シードで同じになります。RandomState インスタンスの場合、random_state は乱数生成器です。None の場合、乱数ジェネレーターは np.random を使用します。
  • min_impurity_split: ノード分割の最小不純度、オプションのパラメータ、デフォルトは 1e-7 です。これは決定木の成長を制限するしきい値であり、ノードの不純物 (ジニ係数、情報利得、平均二乗誤差、絶対差) がこのしきい値より小さい場合、そのノードは子ノードを生成しなくなります。それがリーフノードです。
    presort: データが事前に並べ替えられているかどうか。オプションのパラメーター。デフォルトは False です。この値はブール値です。並べ替えない場合、デフォルトは False です。一般に、サンプル サイズが小さい場合、または深さが浅いデシジョン ツリーが制限されている場合、これを true に設定すると、分割点の選択が速くなり、デシジョン ツリーの構築が速くなります。サンプルサイズが大きすぎるとメリットがありません。問題は、サンプルサイズが小さい場合、速度が遅くならないことです。したがって、この値は通常無視されます。

これらのパラメータに注意することに加えて、パラメータを調整するときに注意すべき点は次のとおりです。

  • サンプル数が少なくてもサンプルの特徴が非常に大きい場合、決定木は過学習しやすくなります。一般に、サンプルの数が特徴の数よりも大きい場合、堅牢なモデルを構築するのが容易になります。
  • サンプル数は少ないが、サンプルの特徴が非常に大きい場合は、デシジョン ツリー モデルをフィッティングする前に、主成分分析 (PCA)、特徴選択 (Losso)、または独立成分分析 (ICA) などの次元削減を実行することをお勧めします。 )。このような特徴の次元は大幅に削減されます。そうすれば、デシジョン ツリー モデルを当てはめた方が良いでしょう。
  • デシジョン ツリーの視覚化を使用すると同時に、最初にデシジョン ツリーの深さを制限して、最初に生成されたデシジョン ツリー内のデータの予備フィッティングを観察してから、デシジョン ツリーの深さを増やすかどうかを決定することをお勧めします。深さ。
  • モデルをトレーニングするときは、サンプルのカテゴリ ステータス (主に分類ツリーを参照) に注意してください。カテゴリ分布が非常に不均一な場合は、class_weight を使用して、モデルがサンプル数の多いカテゴリに偏りすぎないように制限することを検討してください。
  • デシジョン ツリーの配列は numpy の float32 型を使用します。トレーニング データがこの形式でない場合、アルゴリズムはまずデータをコピーしてから実行します。
  • 入力サンプル行列がスパースである場合は、フィッティング前に csc_matrix sparse を呼び出し、予測前に csr_matrix sparse を呼び出すことをお勧めします。

sklearn.tree.DecisionTreeClassifier() は、以下の図に示すように、使用できるメソッドをいくつか提供します。
ここに画像の説明を挿入します
これを理解すれば、コードを書くことができます。
注: fit() 関数は文字列型のデータを受け取ることができないため、出力された情報から、データがすべて文字列型であることがわかります。fit() 関数を使用する前に、データ セットをエンコードする必要があります。ここでは 2 つの方法を使用できます。

  • LabelEncoder: 文字列を増分値に変換する
  • OneHotEncoder: One-of-K アルゴリズムを使用して文字列を整数に変換する

文字列型データをシリアル化するには、最初にパンダ データを生成する必要があります。これにより、シリアル化作業が容易になります。ここで使用する方法は、元データ -> 辞書 -> pandas データであり、コードは次のとおりです。

#%%

import numpy as np
import pandas as pd
fr = open('lenses.txt')
lenses = np.array([inst.strip().split('\t') for inst in fr.readlines()])
print(lenses)
#四个特征一列是:年龄,第二列是症状,第三列是是否散光,第四列是眼泪数量
#,第五列是最终的分类标签,隐形眼镜类型包括硬材质(hard)、软材质(soft)以及不适合佩戴隐形眼镜(no lenses)
lensesLabels = ['age', 'prescript', 'astigmatic', 'tearRate']
#最终分类在最后一列
lenses_target = [each[-1] for each in lenses]                                                        
print(lenses_target)

#%%

#组装成带有表头的数据格式
lensesDataFrame=np.concatenate((np.array([lensesLabels]),lenses[:,0:4]))
'''
注意dataframe的用法
df['a']#取a列
df[['a','b']]#取a、b列
默认的表头是0,1,2这样的序号,如果需要自定义表头需要定义json
{
   "age":[young,pre],
   "prescript":["myope","myope"]
}
'''
jsonData= {l:lenses[:,i]for i,l in enumerate(lensesLabels)}
lenses_pd = pd.DataFrame(jsonData)                                    #生成pandas.DataFrame
print(lenses_pd)


#%%

from sklearn.preprocessing import LabelEncoder
# 将所有的label 比如young转换成0,pre转为成1这样的数字编码
le = LabelEncoder()          
#传入一个一维的数字,在这个数组里,相同的字符串转换为相同的数字
for i in lenses_pd.columns:
    lenses_pd[i]=le.fit_transform(lenses_pd[i])
print(lenses_pd)

ここに画像の説明を挿入します

Graphviz を使用したデシジョン ツリーの視覚化

graphviz は以前にインストールされているため、pydotplus ライブラリをインストールします

pip3 install pydotplus

コードを書く


#使用sklearn决策树
from sklearn import tree
import pydotplus
from io import StringIO
clf = tree.DecisionTreeClassifier(max_depth = 4)                        #创建DecisionTreeClassifier()类
clf = clf.fit(lenses_pd.values.tolist(), lenses_target)                    #使用数据,构建决策树
dot_data = StringIO()
tree.export_graphviz(clf, out_file = dot_data,                            #绘制决策树
                    feature_names = lenses_pd.keys(),
                    class_names = clf.classes_,
                    filled=True, rounded=True,
                    special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
graph.write_pdf("tree.pdf")     

コードを実行すると、python ファイルが保存されているディレクトリと同じディレクトリに、tree という名前の PDF ファイルが生成されます。ファイルを開くと、デシジョン ツリーが視覚化されていることがわかります。
ここに画像の説明を挿入します
決定木を決定したら、予測を行うことができます。あなたの目の状態や年齢などから、どのようなコンタクトレンズの素材が適しているのかをご確認いただけます。予測結果を確認するには、次のコードを使用します。

print(clf.predict([[1,1,1,0]]))   

要約する

デシジョン ツリーには次のような利点があります。

  • 分かりやすく説明しやすい。ディシジョンツリーを視覚化できます。
  • データの前処理はほとんど必要ありません。他の方法では、多くの場合、ダミー変数を作成し、欠損値を削除するなど、データの標準化が必要になります。デシジョン ツリーはまだ欠損値をサポートしていません。
  • ツリーを使用するコスト (データの予測など) は、トレーニング データ ポイントの数の対数です。
  • 数値変数とカテゴリ変数の両方を処理できます。他のほとんどの方法は、一連の変数の分析に適しています。
  • 複数値の出力変数の問題を処理できます。
  • ホワイトボックスモデルを使用します。このルールは、状況を観察すれば、論理的判断を使用して簡単に表現できます。対照的に、ブラックボックス モデル (人工ニューラル ネットワークなど) の場合、結果の解釈は非常に困難になります。
  • 実際のモデルでも、仮定が無効な場合でも十分に適用できます。

デシジョン ツリーの欠点は次のとおりです。

  • デシジョン ツリー学習では、複雑すぎてデータを適切に予測できないツリーが作成される場合があります。それは過学習です。枝刈りメカニズム (現在はサポートされていません) を使用して、リーフ ノードに必要なサンプルの最小数、または数値の最大深さを設定すると、過剰適合を回避できます。
  • 非常に小さな突然変異でも完全に異なるツリーが生成される可能性があるため、決定木は不安定になる可能性があります。この問題は、アンサンブルを使用したデシジョン ツリーによって軽減されます。
  • XOR、パリティ、マルチプレクサの問題など、デシジョン ツリーでは概念がうまく説明されないため、概念を学習するのは困難です。
  • 特定の分類が優勢な場合、デシジョン ツリーは偏ったツリーを作成します。したがって、トレーニング前にサンプルのバランスをとるためにサンプリングすることをお勧めします。
    他の:

おすすめ

転載: blog.csdn.net/liaomin416100569/article/details/129089200