ランダムフォレストで活性予測

以前の記事の続きとなります。

計算した記述子を用いて活性の予測を行います。最初に今回作成したコードを記載します。

def main():
    filename = "AID_504466_datatable_all.csv"
    calc = False
    random_state = 0

    # 記述子の計算
    x, y = calc_descriptor(filename, calc=calc)

    # 欠損値を平均値で補完
    x = x.fillna(x.mean())
    x = drop_low_var_col(x, thres_var=0, plot=False)

    # pd.DataFrame -> np.ndarrayに型変換
    x = x.to_numpy().astype(float)
    y = y.to_numpy().astype(int)

    # train, validation, testに分割
    train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.2)
    train_x, val_x, train_y, val_y = train_test_split(train_x, train_y, test_size=0.3)

    # 学習
    clf = RandomForestClassifier(random_state=random_state)
    clf.fit(train_x, train_y)

    # validationデータで予測
    val_pred = clf.predict(val_x)
    
    # 評価
    acc = accuracy_score(val_y, val_pred)
    balanced_acc = balanced_accuracy_score(val_y, val_pred)
    precision = precision_score(val_y, val_pred)
    recall = recall_score(val_y, val_pred)
    f1 = f1_score(val_y, val_pred)
    print("accuracy: ", acc)
    print("balanced accuracy: ", balanced_acc)
    print("precision: ", precision)
    print("recall: ", recall)
    print("f value: ", f1)

それではそれぞれ解説をしていきます。

    filename = "AID_504466_datatable_all.csv"
    calc = False
    random_state = 0

    # 記述子の計算
    x, y = calc_descriptor(filename, calc=calc)

    # 欠損値を平均値で補完
    x = x.fillna(x.mean())
    x = drop_low_var_col(x, thres_var=0, plot=False)

以前の記事でも説明していますが、calc変数は記述子を計算するかどうかを決定する変数です。以前に実行していれば再度計算する必要はないので、ここではFalseを指定しています。random_stateはscikit-learnのランダム性を制御する数値となります。random_stateを固定することで毎回の実験に再現性を持たせることができます。

記述子を計算した後には欠損値の補完を行っています。欠損値の補完には様々な手法がありますが、最初の検討では簡単に実装できる平均値や中央値などで行うことが多いです。

    # pd.DataFrame -> np.ndarrayに型変換
    x = x.to_numpy().astype(float)
    y = y.to_numpy().astype(int)

    # train, validation, testに分割
    train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.2)
    train_x, val_x, train_y, val_y = train_test_split(train_x, train_y, test_size=0.3)

続いてpandas.DataFrame型からnumpy.ndarray型に変換を行っています。この後使用するscikit-learnの各メソッドはpandas.DataFrameでもnumpy.ndarrayでも対応しているので、実は最初はpandas.DataFrame型のまま処理を続けようと考えていました。しかし、pandas.DataFrameで処理を実行するとエラーが発生してしまいました。詳細なことは判明しませんでしたが、scikit-learnのメソッドが完全にpandas.DataFrameに対応しているわけではないらしいです。このような理由で型変換を行っています。

trainデータ、validationデータ、testデータの分割にはscikit-learnのtrain_test_splitを使用しています。簡単に実装できるので、これも最初の検討にはちょうどいいと考えています。

    # 学習
    clf = RandomForestClassifier(random_state=random_state)
    clf.fit(train_x, train_y)

    # validationデータで予測
    val_pred = clf.predict(val_x)
    
    # 評価
    acc = accuracy_score(val_y, val_pred)
    balanced_acc = balanced_accuracy_score(val_y, val_pred)
    precision = precision_score(val_y, val_pred)
    recall = recall_score(val_y, val_pred)
    f1 = f1_score(val_y, val_pred)
    print("accuracy: ", acc)
    print("balanced accuracy: ", balanced_acc)
    print("precision: ", precision)
    print("recall: ", recall)
    print("f value: ", f1)

上のコードは、学習→予測→評価の流れを示しています。分類器にはランダムフォレストを使用します。パラメータもいろいろとあるのですが、一旦全てデフォルト値で評価してみます。

実行した結果は以下のようになりました。

accuracy: 0.9865044732363992
balanced accuracy: 0.5170835135068126
precision: 0.6363636363636364
recall: 0.03444034440344403
f value: 0.06534422403733955

注目していただきたいのはaccuracyとbalanced accuracyの値です。accuracyは非常に高い値を示していますが、balanced accuracyはaccuracyに比べて低い値にとどまっています。これは今回使用したデータセットがActiveラベルに比べてInactiveラベルが非常に多いことが原因だと考えられます。すなわち、Activeラベルの予測がうまくできていないということです。これはrecallの値が非常に低い値だということからもわかります。モデルの評価をするときはaccuracyだけではなく、その他の手法も用いて検討するということが重要だということがよくわかる結果となりました。

モデルの評価としてはうまく分類できていないということになってしまいました。さらに良い結果を出したいとなった時には、パラメータや欠損値の補完など検討項目はたくさんありそうです。