【機械学習】決定木(実戦)

決定木(実戦)



実践的な部分は、理論的な実践的な操作を理解して強化するのに役立ちます (次のコードは、jupyter ノートブックに基づいています)。




1. 準備作業(jupyter notebook でのフォントサイズスタイルの設定等)

import numpy as np
import os
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
plt.rcParams['axes.labelsize'] = 14
plt.rcParams['xtick.labelsize'] = 12
plt.rcParams['ytick.labelsize'] = 12
import warnings
warnings.filterwarnings('ignore')



2.樹木モデルのビジュアル表示


デシジョン ツリーは理論的に理解しやすいだけでなく (機械学習の「最も使いやすい」アルゴリズム)、構築プロセスを視覚化するために実装することもできます (ニューラル ネットワークなどのアルゴリズムは、それ自体がブラック ボックス モデルであり、より複雑です)。モデルの構築を視覚化することは困難です)。したがって、デシジョン ツリーのもう 1 つの大きな利点は、関連するパッケージを使用して、構築されたツリー モデルを表示できることです。決定木を視覚化できるパッケージは次のとおりです。

ダウンロードリンク: Graphviz .

注: cmd でコマンドを呼び出すには、インストール中に環境変数を構成する必要があります (exe ファイルをダウンロードし、インストール プロセス中に [Add PATH] をオンにします)。


1. アイリスのデータセットから決定木モデルを構築する

# 导入鸢尾花数据集 和 决策树的相关包
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier

# 加载鸢尾花数据集
iris = load_iris()

# 选用鸢尾花数据集的特征
# 尾花数据集的 4 个特征分别为:sepal length:、sepal width、petal length:、petal width
# 下面选用 petal length、petal width 作为实验用的特征
X= iris.data[:,2:]

# 取出标签
y = iris.target

# 设置决策树的最大深度为 2(也可以限制其他属性)
tree_clf = DecisionTreeClassifier(max_depth = 2)

# 训练分类器
tree_clf.fit(X, y)

ここに画像の説明を挿入


2. デシジョン ツリーを視覚化するための具体的な手順

# 导入对决策树进行可视化展示的相关包
from sklearn.tree import export_graphviz

export_graphviz(
    # 传入构建好的决策树模型
    tree_clf,
    
    # 设置输出文件(需要设置为 .dot 文件,之后再转换为 jpg 或 png 文件)
    out_file="iris_tree.dot",
    
    # 设置特征的名称
    feature_names=iris.feature_names[2:],
    
    # 设置不同分类的名称(标签)
    class_names=iris.target_names,
    
    rounded = True,
    filled = True
)

# 该代码执行完毕后,此 .ipython 文件存放路径下将生成一个 .dot 文件(名字由 out_file 设置,此处我设置的文件名为 iris_tree.dot)

次に、cmd を開き、graphviz パッケージの dot コマンドを使用して、.dot ファイルを .png や .jpg などのさまざまな形式に変換できます。コマンドは次のとおりです。
$ dot -T ['jpg' などの画像形式] ['iris_tree.dot' などの対象ドット ファイル] -o ['iris_tree.jpg' などの変換後のファイル名] 例: dot -Tjpg
iris_tree .dot -o iris_tree.jpg

ドットコマンド形式

コマンドを入力して Enter キーを押した後、応答がない場合 (上の図のように)、実行が成功したことを意味します。iris_tree.dot ファイルが保存されているディレクトリを開くと、画像ファイル iris_tree.jpg が表示されます。クリックすると、次のように表示されます。

iris_tree


画像解釈

  • 上図では、ルートノードの1行目のデータが分割特徴であり、ルートノードは分割特徴として「花弁の長さ」という特徴を利用しており、判定条件は「花弁の長さ」であることがわかります。幅は 2.45 インチ以下です。
  • ルート ノードの 2 行目のデータはジニ係数で、ルート ノードのジニ係数が 0.667 であることを示しています。
  • ルート ノードの 3 行目のデータは、現在のデータ セットの分類であり、初期状態では 3 種類の花 (各種類 50 個) があることがわかります。
  • ルート ノードの 4 行目のデータは、データ セットが現在のノードに分類されるカテゴリを示します。

3.確率推定

推定クラス確率: 入力データを次のように仮定します: 花弁の長さ 5cm、幅 1.5cm。上の図からわかるように、それは癜風の花に分類されるべきです。その分類確率を確認するために、デシジョン ツリーの出力は次のようになります。

  • iris-Setosa 为 0% (0/54);
  • アイリス-バーシカラーの 90.7% (49/54);
  • iris-Virginica は 9.3% (5/54) でした。

関数を呼び出して、各確率値を表示しましょう。

# 调用函数查看概率估计
tree_clf.predict_proba([[5,1.5]])
Out
	array([[0.        , 0.90740741, 0.09259259]])

関数によって計算された推定値は、基本的に私たちが計算した結果と一致していることがわかります。


# 调用函数查看预测结果
tree_clf.predict([[5,1.5]])
Out
	array([1])

3 種類の花とそのインデックスは次のとおりです。

0 —— アイリスセトサ;
1 —— アイリス-バーシカラー;
2 —— iris-virginica。

したがって、出力は次のとおりです。array([1])。




3. 判定境界表示


決定境界を描画する関数を定義します。

from matplotlib.colors import ListedColormap

# 定义绘制决策边界的函数
def plot_decision_boundary(clf,X, y, axes=[0,7,0,3], iris=True,legend=False,plot_training=True):
    
    # 构建坐标棋盘
    # 等距选 100 个居于 axes[0],axes[1] 之间的点
    x1s = np.linspace(axes[0],axes[1],100)
    # x1s.shape = (100,)
    
    # 等距选 100 个居于 axes[2],axes[3] 之间的点
    x2s = np.linspace (axes[2],axes[3],100)
    # x2s.shape = (100,)
    
    # 构建棋盘数据
    x1,x2 = np.meshgrid(x1s,x2s)
    # x1.shape = x2.shape = (100,100)
    
    # 将构建好的两个棋盘数据分别作为一个坐标轴上的数据(从而构成新的测试数据)
    # x1.ravel() 将拉平数据(得到的是个列向量(矩阵)),此时 x1.shape = (10000,)
    # 将 x1 和 x2 拉平后分别作为两条坐标轴
    # 这里用到 numpy.c_() 函数,以将两个矩阵合并为一个矩阵
    X_new = np.c_[x1.ravel(),x2.ravel()]
    # 此时 X_new.shape = (10000,2)
    
    # 对构建好的新测试数据进行预测
    y_pred = clf.predict(X_new).reshape(x1.shape)
    
    # 选用背景颜色
    custom_cmap = ListedColormap(['#fafab0', '#9898ff', '#a0faa0'])
    
    # 执行填充
    plt.contourf(x1,x2,y_pred,alpha=0.3,cmap=custom_cmap)
    
    if not iris:
        custom_cmap2 = ListedColormap(['#7d7d58','#4c4c7f','#507d50'])
        plt.contour(x1,x2, y_pred,cmap=custom_cmap2,alpha=0.8)
    if plot_training:
        plt.plot(X[:,0][y==0],X[:,1][y==0],"yo", label="Iris-Setosa")
        plt.plot(X[:,0][y==1],X[:,1][y==1],"bs", label="Iris-Versicolor")
        plt.plot(X[:,0][y==2],X[:,1][y==2],"g^", label="Iris-Virginica")
        plt.axis(axes)
    if iris:
        plt.xlabel("Petal length", fontsize=14)
        plt.ylabel("Petal width", fontsize=14)
    else:
        plt.xlabel(r"$x_1$", fontsize=18)
        plt.ylabel (r"$x_2$", fontsize=18,rotation = 0)
    if legend:
        plt.legend(loc="lower right", fontsize=14)

決定境界を描画します。

# 绘制决策边界
plt.figure(figsize=(8,4))
plot_decision_boundary(tree_clf, X, y)

# 为了便于理解,这里还绘制了决策边界的切割线(基于前面得到的图片)
plt.plot([2.45, 2.45], [0, 3], "k-", linewidth=2)
plt.plot([2.45, 7.5], [1.75,1.75], "k--", linewidth=2)
plt.plot([4.95, 4.95], [0, 1.75], "k:", linewidth=2)
plt.plot([4.85, 4.85], [1.75, 3], "k:", linewidth=2)

# 绘制决策边界划分出的类别所处深度
plt.text(1, 1.0, "Depth=1", fontsize=15)
plt.text(3.2, 2, "Depth=2", fontsize=13)
plt.text(3.2,0.5, "Depth=2", fontsize=13)
plt.title('Decision Tree decision boundaries')
          
plt.show()

ここに画像の説明を挿入




4. 決定木の正則化(プレプルーニング)


DecisionTreeClassfilter クラスには、デシジョン ツリーの形状を制限する次のパラメーターがあります (プレプ​​ルーニング操作に使用できます)。

  • max_depth (決定木の最大深さ)
  • min_samples_split (ノードが分割する前に必要なサンプルの最小数)
  • min_samples_leaf (ノードが分割後に葉ノードのために持つ必要があるサンプルの最小数)
  • max_leaf_nodes (葉ノードの最大数)
  • max_features (各ノードで分割するために評価する機能の最大数。通常、このパラメーターに制限はありません)

次に、実験を使用して、ツリー モデルに対する「min_samples_leaf 属性の制限」の正則化効果をテストします (他のパラメーターも同じ方法でテストできるため、ここでは 1 つずつ実行しません)。

# 这里选用一个难度稍微大一点的数据集
from sklearn.datasets import make_moons

# 构建数据集:X.shape = (100,2)  y.shape = (100,)
X,y = make_moons(n_samples = 100, noise = 0.25, random_state = 43)

# 构建决策树
tree_clf1 = DecisionTreeClassifier(random_state = 6)
tree_clf2 = DecisionTreeClassifier(min_samples_leaf = 5, random_state = 16)
tree_clf1.fit(X,y)
tree_clf2.fit(X,y)

# 画图展示:绘制决策边界
plt.figure(figsize=(12,4))
plt.subplot(121)
plot_decision_boundary(tree_clf1,X,y,axes = [-1.5, 2.5, -1, 1.5], iris = False)
plt.title('No Restrictions')
plt.subplot(122)
plot_decision_boundary(tree_clf2,X,y,axes = [-1.5, 2.5, -1, 1.5], iris = False)
plt.title('min_samples_leaf = 5')

その出力は次のとおりです。

ここに画像の説明を挿入

上の図から、次の結論を導き出すことができます。

  • 左の図は、決定木を構築する際に制限がなく、すべてのデータ ポイントを分類できる (外れ値が表示されない) ため、得られる決定木モデルは非常に複雑になりますが、深刻なオーバーシュートがあります。
  • 右の図の制約により、それによって構築される決定木モデルはそれほど複雑にはなりませんが (オッカムのカミソリの原理: 単純であればあるほど良い)、その分類効果は当然左の図ほど高くはありません。



5. 実験: データに対するツリー モデルの感度を調べる


以下は、サンプル データが変更されたときに、構築されたモデルがまだ安定しているかどうかを調査するための実験です。

# 构建随机测试数据
np.random.seed(6)
Xs = np.random.rand(100,2) - 0.5
ys =(Xs[:, 0]> 0).astype(np.float32)*2

# 定义数据的旋转角度
angle = np.pi/ 4

# 旋转原始测试数据矩阵
rotation_matrix = np.array([[np.cos(angle),-np.sin(angle)],[np.sin(angle),np.cos(angle)]])
Xsr = Xs.dot(rotation_matrix)

# 构建分类器 tree_clf_s 对原始数据进行训练
tree_clf_s = DecisionTreeClassifier(random_state=42)
tree_clf_s.fit(Xs,ys)

# 构建分类器 tree_clf_sr 对处理后的数据进行训练
tree_clf_sr = DecisionTreeClassifier(random_state=42)
tree_clf_sr.fit(Xsr,ys)

# 绘图展示这两个分类器的测试效果
plt.figure(figsize=(11,4))

plt.subplot(121)
plot_decision_boundary(tree_clf_s,Xs,ys,axes=[-0.7,0.7,-0.7,0.7], iris=False)
plt.title('Sensitivity to training set rotation')

plt.subplot(122)
plot_decision_boundary(tree_clf_sr,Xsr,ys,axes=[-0.7,0.7,-0.7,0.7],iris=False)
plt.title('Sensitivity to training set rotation')
plt.show()

その出力は次のとおりです。

ここに画像の説明を挿入

上の図の左側は、データセットを切り取った後に得られる決定境界を示しています。次に、データセット内のデータを45°「回転」させます.論理的には、その決定境界も45°「回転」します.しかし、上図の右側の結果によると、決定境界は回転に追従しませんが、右に示す方法で再描画が行われます。

デシジョン ツリー モデルがデータに非常に敏感であることがわかります。




6. 実験: 決定木による回帰問題の解決


決定木が分類タスクまたは回帰タスクに使用される場合、対応するデータセットはそれぞれ離散的および連続的であるため、その評価にはさまざまな数学的方法を使用する必要があります。分類タスクではエントロピーまたはジニ係数を使用します (計算の観点
からより一般的に使用されるジニ係数を見てください);回帰タスクは分散を使用します (分散は、データセット間の類似性を測定する最も簡単な指標です)。

# 导入相关库函数
from sklearn.tree import DecisionTreeRegressor

# 构造数据(一维数据)
m = 200
X = np.random.rand(m,1)
y = 4*(X-0.5)**2 + np.random.randn(m,1)/10

# 训练分类器
tree_reg = DecisionTreeRegressor(max_depth = 2)
tree_reg.fit(X,y)

ここに画像の説明を挿入

# 树模型的展示
export_graphviz(
    tree_reg,
    out_file=("regression_tree.dot"),
    feature_names=["x"],
    rounded=True,
    filled=True
)

次に、graphviz パッケージの dot コマンドを使用して、次のディシジョン ツリー図を取得します。

回帰木

注: Sklearn では、デフォルトで生成される決定木は二分木 (CART) です。




7. 実験: フィッティング能力に対する決定木の深さの影響を調べる


次の実験では、深さの異なる 2 つの決定木のフィッティング能力の違いを調べて、変数を制御してデータをサンプリングします。

深さだけが異なる 2 つのツリーを設定します。

# 构建两棵深度不一致的决策树
tree_reg1 = DecisionTreeRegressor(random_state=42,max_depth=2)
tree_reg2 = DecisionTreeRegressor(random_state=42,max_depth=3)

# 训练分类器
tree_reg1.fit(X, y)
tree_reg2.fit(X, y)

関連する関数を定義します。

# 定义预测回归值并绘制的函数
def plot_regression_predictions(tree_reg, X, y, axes=[0,1,-0.2,1],ylabel="$y$"):
    # 获取棋盘坐标(这里的对象是一维坐标)
    # reshape() 函数的 -1 表示对全部数据执行此函数执行后,数据由 (100,) 变为 (100,1)
    x1 = np.linspace(axes[0], axes[1], 500).reshape(-1, 1)
    
    # 得到预测值
    y_pred = tree_reg.predict(x1)
    
    # 绘图
    plt.axis(axes)
    plt.xlabel("$x_1$", fontsize=18)
    if ylabel:
        plt.ylabel(ylabel, fontsize=18, rotation=0)
    plt.plot(X, y, "b.")
    plt.plot(x1, y_pred,"r.-", linewidth=2, label = r"$\hat{y}$")

違いを表示します。

# 开始绘制两个分类器的回归效果
plt.figure(figsize=(11,4))

plt.subplot(121)
plot_regression_predictions(tree_reg1, X, y)
for split,style in ((0.1973, "k-"),(0.0917,"k--"),(0.7718,"k--")):
    plt.plot([split,split],[-0.2,1],style,linewidth=2)
plt.text(0.21,0.65,"Depth=0", fontsize=15)
plt.text(0.01,0.2,"Depth=l", fontsize=13)
plt.text(0.65,0.8,"Depth=1", fontsize=13)
plt.legend(loc="upper center",fontsize=18)
plt.title("max_depth=2",fontsize=14)

plt.subplot(122)
plot_regression_predictions(tree_reg2,X, y,ylabel=None)
for split,style in ((0.1973,"k-"),(0.0917,"k--"),(0.7718,"k--")):
    plt.plot([split,split],[-0.2,1], style,linewidth=2)
for split in (0.0458,0.1298,0.2873,0.9040):
    plt.plot([split,split],[-0.2,1],"k:",linewidth=1)
plt.text(0.3,0.5,"Depth=2", fontsize=13)
plt.title("max_depth=3",fontsize=14)

plt.show()

結果の出力は次のとおりです。

ここに画像の説明を挿入

上図から、max_depth を大きく設定するほど、得られる決定木モデルの分割が細かくなり、フィッティング効果が高くなることがわかります。

次に、深さを増やして何が起こるかを確認します。

# 不限制深度
tree_reg1 = DecisionTreeRegressor(random_state=42)

# 深度限制为 10
tree_reg2 = DecisionTreeRegressor(random_state=42,min_samples_leaf=10)

# 训练分类器
tree_reg1.fit(X,y)
tree_reg2.fit(X,y)

# 设置坐标数据(作为测试数据)
x1 = np.linspace(0,1,500).reshape(-1,1)

# 得到模型的预测值
y_pred1 = tree_reg1.predict(x1)
y_pred2 = tree_reg2.predict(x1)

描画表示効果:

# 绘制两个分类器的回归效果
plt.figure(figsize=(11,4))

plt.subplot(121)
plt.plot(X, y,"b.")
plt.plot(x1,y_pred1,"r.-",linewidth=2,label=r"$\hat{y}$")
plt.axis([0,1,-0.2,1.1])
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", fontsize=18, rotation=0)
plt.legend(loc="upper center", fontsize=18)
plt.title("No restrictions", fontsize=14)

plt.subplot(122)
plt.plot(X, y,"b.")
plt.plot(x1,y_pred2,"r.-",linewidth=2,label=r"$ \hat{y}$")
plt.axis([0,1,-0.2,1.1])
plt.xlabel("$x_1$",fontsize=18)
plt.title("min_samples_leaf={}".format(tree_reg2.min_samples_leaf),fontsize=14)

plt.show ()

結果の出力は次のとおりです。

ここに画像の説明を挿入

上図の左側は制約がないため深刻なオーバーフィッティング現象が発生しており(各点を満たすために、モデルはかなり複雑に設定されています)、右図は「最小サンプル数」によるものです。リーフノード」. 制限、オーバーフィッティングのリスクが回避されます。


終わり


おすすめ

転載: blog.csdn.net/the_ZED/article/details/130089515