機械学習による決定木分析でクラシフィケーションツリーを洗練させる

機械学習の手法の一つである決定木分析を使うと、入出力データから、対象の内部ロジックをある程度推測できるようになります。これはデバッグやテストの洗練に活用できる余地があります。

今回はその一例として、決定木分析の主要なアルゴリズムであるCART法を使って、クラシフィケーションツリー法でのクラシフィケーションツリーを洗練させるアプローチを説明します。

題材

イメージとしては、特定条件でログにワーニングやエラーが記録されるかの確認を行うような、ログ機能のテストを想定します。
テストはクラシフィケーションツリー法を使って作成します。作成したクラシフィケーションツリーは以下の通りです(処理をシンプルにするため簡略化した例を用います)。

f:id:goyoki:20200705230642p:plain

今回は、この作成したクラシフィケーションツリーが妥当かチェックするため、テスト実行時に決定木分析を使う場面を扱います。目的としては、例えばテスト設計の妥当性の確認や、リグレッションテストの洗練などを想定します。

データの取得

まず実際にテスト対象を動かして、なるべく多くの入出力データを取得します。

取得対象ですが、クラシフィケーションを取得対象データとします。また取得データはCART法を適用できるように、クラスを参考にして数値化します(例えばクラスが真偽なら1、0で記録します)。

データ取得範囲については、実行空間を全網羅する入力の全組み合わせを実現するのが理想です。ただ一般的に実現不可能なので、QuickCheckのように全体を大まかに網羅するランダムデータを生成してデータ取得します。

例えば以下のようなデータを取得します。

# インプットデータ:
input_name = ["input1", "input2", "input3", "input4"]
inputs = [
    [0, 0, 0, 88],
    [1, 1, 0, 2],
    [0, 1, 1, 26],
    [0, 0, 1, 55],
    [1, 0, 0, 29],
    [1, 1, 0, 12],
    ・・・
]
# 計測したアウトプットデータ:
# (0:"non-error", 1:"error1", 2:"error2", 3:"error3", 4:"warning1"):
output_name = ["non-error", "error1", "error2", "error3", "warning1"]
outputs = [
    0,
    4,
    0,
    4,
    4,
    3,
    ・・・
]

決定木の生成

次に取得したデータからCART法で決定木を生成します。そしてクラシフィケーションツリーの評価のため、決定木のイメージと正解率を求めます。
scikit-learnでの実装は次のようになります。

from io import StringIO
from sklearn import tree
import pydotplus
from sklearn.metrics import accuracy_score
 
def create_tree(inputs, input_name, outputs, output_name):
    clf = tree.DecisionTreeClassifier(max_depth=5) #仕様規模に応じてmax_depthを制限する。
    clf = clf.fit(inputs, outputs)

    dot_data = StringIO()
    tree.export_graphviz(clf, out_file=dot_data,
        feature_names=input_name, class_names=output_name,
        filled=True, rounded=True)
    graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
    graph.write_png('./tree.png')

    predicted_outputs = clf.predict(inputs)
    accuracy = accuracy_score(outputs, predicted_outputs)
    print(f'accuracy:{accuracy}')

前述したデータで決定木を生成すると次が得られます。

f:id:goyoki:20200705230808p:plain

また得られた正解率は今回は0.73となりました。

クラシフィケーションツリーの評価と改善

そして次に、得られた決定木と正解率から、クラシフィケーションツリーを評価します。

以下に該当する場合、不具合の可能性(あくまでこの決定木は既存実装から機械学習で生成したものであり、正しい仕様に基づいたものではない点には注意が必要です)があるか、クラシフィケーション・クラスの抽出漏れの可能性があります。

  • 仕様と比べて決定木が複雑
  • 正解率が低い

 
また、以下に該当する場合、不具合あるいはデータ不足の可能性があるか、クラスの分け方に問題がある可能性があります。

  • 決定木の条件と、クラスの境界が異なる

 
また、以下に該当する場合、不具合あるいはデータ不足の可能性があるか、冗長なクラシフィケーションがある可能性があります(ただ不具合・データ不足の可能性の存在から、冗長であるとしてクラシフィケーションを削除するのは安易に行なえません)。

 

今回は決定木が複雑すぎるのと、正解率が低すぎる点が見受けられます。すなわちクラシフィケーションの抽出漏れの可能性があります。そこで、クラシフィケーションツリーを見直し、次のようにクラシフィケーションを追加します。

f:id:goyoki:20200705230933p:plain

再評価

このクラシフィケーションツリーに基づいて入出力データを再取得し、前述のコードで決定木を生成した結果が以下になります。

f:id:goyoki:20200705230953p:plain

また正解率は1.00となりました。
決定木がシンプルになったのと、正解率が高まった点から、クラシフィケーションの抽出漏れがあり、改善によりそれが是正されたと推測できます。