dummyとonehotの違い

dummy encodingは、1列にデータにカテゴリが2つ(A、B)あると、AとBの列を作り、0か1を入れていき、どちらかの列を削除する。1列に3カテゴリであれば、3列(A、B、C)作り、1行削除し2列を作る。カテゴリn個ある場合は、n-1列で表現。

one hot encodingは、1列にデータにカテゴリが2つ(A、B)あると、AとBの列を作り、0か1を入れていく。1列に3カテゴリであれば、3列(A、B、C)作る。カテゴリn個ある場合は、n列で表現。

どちらを使うべきか

regressionから勉強するとdummyを使うべきと学びます。onehotはdummy trapにかかり、(他の列から1列を完璧に推測できてしまい)multicolinaryを起こしてしまう。逆に、neural network系から勉強するとdummyって何?って感じだと思います。

この本によると、linear regression models, logistic regression models, discriminant analysis, and Neural networks that don’t employ weight decayについては、ダミー変数化すべき。そのほかは、onehotで問題ない。ということらしい。

ただネットを調べてみるとneural networkについては両者でやってもあまり差がないので、単純なonehotの方が好まれるようです。weight decayを考慮したモデルはあまり見かけないと思います。

どちらかはっきりさせるために実験します。

① カテゴリをそのまま数字として扱ったケース
② dummy variable化して扱う
③ onehot encoding化して扱う

Autokeras(Neural Network)、Adaboost、Gradient Boost Machine、Random forest、Logistic regression、LDA、lightGBM(チューニング方法3通り)を使って、ACCURACYがどうなるかを見てみます。

データ

心臓病のデータセット。心臓病かどうかを各種データから予測する。

https://www.kaggle.com/cherngs/heart-disease-cleveland-uci
参考:https://archive.ics.uci.edu/ml/datasets/Heart+Disease

Authors:
1. Hungarian Institute of Cardiology. Budapest: Andras Janosi, M.D.
2. University Hospital, Zurich, Switzerland: William Steinbrunn, M.D.
3. University Hospital, Basel, Switzerland: Matthias Pfisterer, M.D.
4. V.A. Medical Center, Long Beach and Cleveland Clinic Foundation:Robert Detrano, M.D., Ph.D.

Outcome:
14列.condition: 0 = 心臓病なし, 1 = 心臓病あり category(nominal)

Predictors:
1列.age:年齢 numeric
2列.sex:性別 (1 = male; 0 = female) category(nominal)
3列.cp: 胸痛のタイプ (1: 典型的狭心症 2: 非典型的狭心症 3: 非狭心症痛 4: 無症状) category(nominal)
4列.trestbps:安静時血圧(入院時のmmHg単位)numeric
5列.chol: 血中コレストロール(mg/dl) numeric
6列.fbs:空腹時血糖値>120mg/dl(1=真、0=偽)category(nominal)
7列.restecg:安静時心電図結果 (0:正常 1:ST-T波異常(T波反転および/または0.05mV以上のST上昇もしくはST下降)を有する。 2:Estesの基準による左心室肥大の可能性が高いまたは確定しているもの)category(nominal)
8列.thalach:最大心拍数 numeric
9列.exang:運動誘発性狭心症(1=有、0=無)category(nominal)
10列.oldpeak:安静時に比べて運動により誘発されるSTの低下 numeric
11列.slope:運動負荷時のSTセグメントの傾き(1: upsloping 2: flat 3: downsloping)category(nominal)
12列.ca: X線透視検査で着色された主要血管の数(0-3)numeric
13列.thal:タリウム負荷試験の結果 0=正常、1=固定欠陥、2=可逆性欠陥 category(nominal)

コード

dummy encoding

pandasのget_dummiesを使う。drop_firstをTrueにする。

    df = pd.read_csv("heart_cleveland_upload.csv")
    
    dummys = pd.get_dummies(df, 
                            drop_first = True,
                            columns=["sex","cp","fbs","restecg","exang","slope","thal","condition"])
    

onehot encoding

pandasのget_dummiesを使う。drop_firstをFalseにする。

    df = pd.read_csv("heart_cleveland_upload.csv")
    
    dummys = pd.get_dummies(df, 
                            drop_first = False,
                            columns=["sex","cp","fbs","restecg","exang","slope","thal","condition"])
    

以下が10回、これらの条件を変化させてそれぞれのモデルでACCを計測した結果である。

① カテゴリをそのまま数字として扱ったケース(Unprocessed)
② dummy variable化して扱う (Dummy)
③ onehot encoding化して扱う (Onehot)

AutoKeras、logisticに関してはdummy encodingの方がよいスコアを出せそうです。(0.05で有意差はでませんでした)

逆に、Gradient Boost MachineのときはOnehotの方がよさそうです。

正式名Dummy mean ACCOnehot mean ACCUnprocessed
mean ACC
adaAdaboost0.8350.8230.816
akAutokeras – Nerural Network0.8550.8450.848
gbcGradient Boost Machine0.7830.7950.793
ldaLDA0.840.840.83
logiLogistic regression0.8510.8410.836
m1LightGBM optuna tuning method10.7910.790.778
m2LightGBM optuna tuning method20.790.7860.791
m3LightGBM without tuning0.8160.7660.766
rfRandom Forest0.8210.8060.808

encodingの種類別のfor loop毎のACCのランク(ACCが高いほうがランク1となる)

3群の平均ランキングの差 one-way ANOVA

9.54e-08 有意差あり

3群の平均ランキングの差 pairwise-t-test

DummyOnehot
Onehot0.00017
Unprocessed8.8e-080.08564

参考:コード

Onehotの時のコード

import seaborn as sns
import pandas as pd

acc_autokeras = []
acc_ada = []
acc_gbc = []
acc_rf = []
acc_logi = []
acc_lda = []
acc_lightgbm_m1 = []
acc_lightgbm_m2 = []
acc_lightgbm_m3 = []

for i in range(10): 
    import pandas as pd
    from sklearn.model_selection import train_test_split as tts
    
    df = pd.read_csv("heart_cleveland_upload.csv")
    
    dummys = pd.get_dummies(df, 
                            drop_first = False,
                            columns=["sex","cp","fbs","restecg","exang","slope","thal"])
    
    train,test = tts(dummys,test_size=0.2,random_state=i)
    
    x_train = train.drop("condition", axis=1).to_numpy()
    y_train = train.loc[:,["condition"]].to_numpy().ravel()
    
    x_test = test.drop("condition", axis=1).to_numpy()
    y_test = test.loc[:,["condition"]].to_numpy().ravel()
    
    import autokeras as ak
    
    maxacc = 0
    for i in range(5):
    
      clf = ak.StructuredDataClassifier(overwrite=False, max_trials=30)
    
      history = clf.fit(x_train, y_train, epochs=30,validation_split=0.2)
    
      result = clf.evaluate(x_test, y_test)
      print(result)
    
      if result[1] > maxacc:
        model_autokeras = clf.export_model()
        model_autokeras.save("autokeras",save_format="tf")
        maxacc = result[1]
        print("saved",result)
      else:
        continue
    
    from tensorflow.keras.models import load_model
    from sklearn.metrics import accuracy_score
    import numpy as np
    
    # 重みの復元
    manual = load_model("autokeras")
    print("manual",manual.evaluate(x_test, y_test))
    
    t = (manual.predict(x_test)>0.5)*1
    
    flat_list = []
    for sublist in t:
        for item in sublist:
            flat_list.append(item)
    
    print(accuracy_score(np.array(flat_list),y_test))
    acc_autokeras.append(accuracy_score(np.array(flat_list),y_test))
    
    from sklearn.ensemble import RandomForestClassifier
    clf_rf = RandomForestClassifier()
    clf_rf.fit(x_train, y_train)
    acc_rf.append(accuracy_score(clf_rf.predict(x_test),y_test))
    
    from sklearn.ensemble import GradientBoostingClassifier
    clf_gbc = GradientBoostingClassifier()
    clf_gbc.fit(x_train, y_train)
    acc_gbc.append(accuracy_score(clf_gbc.predict(x_test),y_test))
    
    from sklearn.ensemble import AdaBoostClassifier
    clf_ada = AdaBoostClassifier()
    clf_ada.fit(x_train, y_train)
    acc_ada.append(accuracy_score(clf_ada.predict(x_test),y_test))
    
    from sklearn.linear_model import LogisticRegression
    clf_logi = LogisticRegression(random_state=0,solver='liblinear')
    clf_logi.fit(x_train, y_train)
    acc_logi.append(accuracy_score(clf_logi.predict(x_test),y_test))
    
    from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
    clf_lda = LinearDiscriminantAnalysis()
    clf_lda.fit(x_train, y_train)
    acc_lda.append(accuracy_score(clf_lda.predict(x_test),y_test))    
    
    ############## creation of data for lightgbm ##################
    from sklearn.model_selection import train_test_split as tts
    import lightgbm as lgb
    import pandas as pd
       
    train_validation,test = tts(dummys,test_size=0.2,random_state=i)
    train,validation = tts(train_validation,test_size=0.2,random_state=i)
    
    x_train_validation = train_validation.drop("condition",axis=1).to_numpy()
    y_train_validation = train_validation.loc[:,["condition"]].to_numpy().ravel()
    
    x_train = train.drop("condition",axis=1).to_numpy()
    y_train = train.loc[:,["condition"]].to_numpy().ravel()
    
    x_val = validation.drop("condition",axis=1).to_numpy()
    y_val = validation.loc[:,["condition"]].to_numpy().ravel()
    
    x_test = test.drop("condition",axis=1).to_numpy()
    y_test = test.loc[:,["condition"]].to_numpy().ravel()
    
    lgb_train_validation = lgb.Dataset(x_train_validation,y_train_validation,free_raw_data=False)
    lgb_train = lgb.Dataset(x_train, y_train,free_raw_data=False)
    lgb_eval = lgb.Dataset(x_val, y_val,free_raw_data=False)
    
    ############ method 1 ##############
    import optuna
    import numpy as np
    import sklearn
    
    def objective(trial):
    
        param = {
            'objective': 'binary',
            'metric': trial.suggest_categorical('metric', ['binary_error',"binary_logloss"]),
            'lambda_l1': trial.suggest_loguniform('lambda_l1', 1e-8, 10.0),
            'lambda_l2': trial.suggest_loguniform('lambda_l2', 1e-8, 10.0),
            'num_leaves': trial.suggest_int('num_leaves', 2, 256),
            'feature_fraction': trial.suggest_uniform('feature_fraction', 0.4, 1.0),
            'bagging_fraction': trial.suggest_uniform('bagging_fraction', 0.4, 1.0),
            'bagging_freq': trial.suggest_int('bagging_freq', 1, 7),
            'min_child_samples': trial.suggest_int('min_child_samples', 5, 100),
        }
        
        gbm = lgb.train(param, lgb_train,valid_sets=lgb_eval)
        preds = gbm.predict(x_val)
        pred_labels = np.rint(preds)
        accuracy = sklearn.metrics.accuracy_score(y_val, pred_labels)
        return accuracy
    
    study = optuna.create_study(direction='maximize')
    study.optimize(objective, n_trials=1000)
        
    bestparams = study.best_trial.params
    bestparams["objective"]="binary"
    
    model = lgb.train(bestparams, lgb_train_validation)
    pred = model.predict(x_test)
    pred = (pred > 0.5) * 1
    print("method1",bestparams)
    print(sklearn.metrics.accuracy_score(pred,y_test))
    acc_lightgbm_m1.append(sklearn.metrics.accuracy_score(pred,y_test))
    
    ############ method 1 ##############
    
    ############ method 2 ##############
    import optuna.integration.lightgbm as LGB_optuna
    
    param = {
            'objective': 'binary',
            'metric': 'binary_error',
        }
    
    lgb_train = lgb.Dataset(x_train, y_train,free_raw_data=False)
    lgb_eval = lgb.Dataset(x_val, y_val,free_raw_data=False)
    
    best = LGB_optuna.train(param, lgb_train,valid_sets=lgb_eval)
    
    model = lgb.train(best.params, lgb_train_validation)
    pred = model.predict(x_test)
    pred = (pred > 0.5) * 1
    print("method2",best.params )
    print( sklearn.metrics.accuracy_score(pred,y_test))
    
    acc_lightgbm_m2.append(sklearn.metrics.accuracy_score(pred,y_test))
    ################ method 2 ######################
    
    ################ method 3 ######################
    param = {
            'objective': 'binary',
            'metric': 'binary_error',
        }
    model = lgb.train(param, lgb_train,valid_sets=lgb_eval)
    pred = model.predict(x_test)
    pred = (pred > 0.5) * 1
    print("method3",model.params)
    print(sklearn.metrics.accuracy_score(pred,y_test))
    
    acc_lightgbm_m3.append(sklearn.metrics.accuracy_score(pred,y_test))

pd.DataFrame({"ak":acc_autokeras,
              "ada":acc_ada,
              "gbc":acc_gbc,
              "rf":acc_rf,
              "logi":acc_logi,
              "lda":acc_lda,
              "m1":acc_lightgbm_m1,
              "m2":acc_lightgbm_m2,
              "m3":acc_lightgbm_m3,
              }).to_csv("data_with_onehot.csv")

Categories:

category