メインコンテンツへスキップ
  1. 第13講 機械学習(2/3)/

決定木(Decision Tree)

教師あり学習 決定木 Scikit-Learn
目次

分類
#

線形回帰では,連続した値を予測する問題を扱いました. しかし,世の中には連続した値ではなく,ある集合の中のいずれかの値を予測する問題もあります. 例えば,性別,病気の有無,天気などが挙げられます. このような問題は,分類(classification)という問題になります.

なお,合格か不合格かや,病気の有無など,予測する値が2つの値の場合は,2値分類(binary classification)と呼び, 3つ以上の値の場合は,多値分類(multiclass classification)と呼びます.

分類にも以下のように多くの手法がありますが,ここではその中でも決定木(Decision Tree)による分類を学びます.

  • 決定木(Decision Tree)
  • ランダムフォレスト(Random Forest)
  • サポートベクターマシン(Support Vector Machine)
  • ニューラルネットワーク(Neural Network)

決定木(Decision Tree)
#

決定木の例として,例題13-1: 最終成績を予測する.を考えます. 例題13-1 では,最終成績を予測しましたが,合格したか否かを予測する問題に変更します.

predict_score.py をコピーし,predict_pass.py として保存します. そして,read_csv 関数を以下のように変更します.

def read_csv(filename):
    x = []
    y = []
    with open(filename, "r") as f:
        f.readline()    # ヘッダ行を読み飛ばす.
        for line in f:
            cols = line.strip().split(",") # 改行を削除し,コンマで分割する.
            score = float(cols[1])
            if score >= 60:
                y.append("合格")   # 目的変数(最終成績)を追加する.
            else:
                y.append("不合格")
            # 説明変数を追加する.
            x.append([float(cols[2]), float(cols[3]), float(cols[4]),\
                float(cols[5]), float(cols[6])]) 
    return x, y

続いて,LinearRegressionDecisionTreeClassifier に変更します. なお,LinearRegressionsklearn.linear_modelモジュールにありますが, DecisionTreeClassifiersklearn.treeモジュールにあります. 加えて,回帰モデルの評価(MAE, MSE, RMSE, R2)は利用できませんので,削除してください.

実行すると,次のような結果が得られます(train_test_splitにランダム要素がありますので必ずしも以下の通りにはなりません).

python3 predict_pass.py score.csv
学習データに対するモデルの精度: 1.0
テストデータに対するモデルの精度: 0.9074074074074074
目的変数がカテゴリ値のとき,線形回帰は用いるとどうなる?

なお,read_csv を上記のように変更し,目的変数をカテゴリ変数に変更したときに LinearRegression を利用すると,実行時に以下のようなエラーが発生します.

Traceback (most recent call last):
  File "predict_pass.py", line 31, in <module>
    model.fit(x_train, y_train)
    ~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.13/site-packages/sklearn/base.py", line 1389, in wrapper
    return fit_method(estimator, *args, **kwargs)
  File "/opt/homebrew/lib/python3.13/site-packages/sklearn/linear_model/_base.py", line 622, in fit
    X, y, X_offset, y_offset, X_scale = _preprocess_data(
                                        ~~~~~~~~~~~~~~~~^
        X,
        ^^
    ...<3 lines>...
        sample_weight=sample_weight,
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/opt/homebrew/lib/python3.13/site-packages/sklearn/linear_model/_base.py", line 169, in _preprocess_data
    y = check_array(y, dtype=X.dtype, copy=copy_y, ensure_2d=False)
  File "/opt/homebrew/lib/python3.13/site-packages/sklearn/utils/validation.py", line 1055, in check_array
    array = _asarray_with_order(array, order=order, dtype=dtype, xp=xp)
  File "/opt/homebrew/lib/python3.13/site-packages/sklearn/utils/_array_api.py", line 832, in _asarray_with_order
    array = numpy.asarray(array, order=order, dtype=dtype)
ValueError: could not convert string to float: np.str_('合格')

逆に,連続変数の目的変数を分類に用いてもエラーは発生しません.ただし,精度は著しく低くなります.

分類モデルの評価
#

分類においても,学習したモデルがどの程度の成果であるのかを評価するための指標があります. 正解率,適合率,再現率,F1値,混同行列などがあり,それぞれ次のように得られます. これらの指標は,sklearn.metrics モジュールに用意されています.

print(f"正解率 {metrics.accuracy_score(y_test, y_pred)}")
print(f"適合率 {metrics.precision_score(y_test, y_pred, pos_label='合格')}")
print(f"再現率 {metrics.recall_score(y_test, y_pred, pos_label='合格')}")
print(f"F1値  {metrics.f1_score(y_test, y_pred, pos_label='合格')}")
print("混同行列")
print(metrics.confusion_matrix(y_test, y_pred, labels=["合格", "不合格"]))

実行結果は次の通りです.

正解率 0.9074074074074074
適合率 0.8555555555555556
再現率 0.8272727272727273
F1値 0.8403311649911295
混同行列
[[42  2]
 [ 3  7]]
  • 正解率(accuracy)
    • 正しく分類された割合です.上の例では,90.74% が正しく分類されています.
  • 適合率(precision)
    • 陽性と予測されたもののうち,実際に陽性である割合です.精度を表す指標です.上の例では,合格と予測されたもののうち,85.56% が実際に合格でした.
  • 再現率(recall)
    • 実際に陽性であるもののうち,陽性と予測された割合です.網羅性を表す指標です.上の例では,実際の合格者のうち,82.72% が合格と予測されました.
  • F1値(F1-score)
    • 適合率と再現率の調和平均です.
  • 混同行列(confusion matrix)
    • 予測結果と実際の結果を表にしたものです.

混同行列(confusion matrix)
#

機械学習はどんなに学習しても,100%の精度を出せるわけではありません. そのため,どのような誤りを犯しているのかを知ることが重要です. 混同行列は,予測結果と実際の結果を表にしたものです.

予測:合格 予測:不合格
実際:合格 TP FN
実際:不合格 FP TN
  • TP(True Positive; 真陽性)
    • 実際に合格で,合格と予測された数です.
  • TN(True Negative; 真陰性)
    • 実際に不合格で,不合格と予測された数です.
  • FP(False Positive; 偽陽性)
    • 実際は不合格で,合格と予測された数です.すなわち誤検出された数です.
  • FN(False Negative; 偽陰性)
    • 実際は合格で,不合格と予測された数です.つまり,検出漏れの数を表します.

上の精度,適合率,再現率,F1値は,これらの値をもとに計算されています.

  • 正解率(accuracy)
    • \(\mathrm{accuracy} = \frac{TP + TN}{TP + FP + FN + TN}\)
  • 適合率(precision)
    • \(\mathrm{precision} = \frac{TP}{TP + FP}\)
  • 再現率(recall)
    • \(\mathrm{recall} = \frac{TP}{TP + FN}\)
  • F1値(F1-score)
    • \(\mathrm{F1} = 2 \times \frac{\mathrm{precision} \times \mathrm{recall}}{\mathrm{precision} + \mathrm{recall}}\)

なお,2値のどちらを正解とするかによって,適合率,再現率の値が変わります. このとき,正解とするクラスを陽性,間違いとするクラスを陰性と呼びます. 混同行列を出力するとき,陽性,陰性を labels オプションで指定すると間違いが少なくなるでしょう.