kaggle符号化categorical featureまとめ
32327 ワード
kaggleコンテストは本質的にコースのコンテストです.この文章はkaggleコンテストにおけるcategorical featureの一般的な処理方法について述べ,主にツリーモデル(lightgbm,xgboost,etc.)に基づいている.ポイントはtarget encodingとbeta target encodingです.
まとめ: label encoding の特徴は内在順序 に存在する.
one hot encoding 特徴内在順序なしcategory数<4 target encoding (mean encoding, likelihood encoding, impact encoding) フィーチャーに内在的な順序はありません.category数>4 beta target encoding 特徴内在順序なし、category数>4、K-fold cross validation 処理しない(モデル自動符号化) CatBoost,lightgbm
\1. Label encoding
m個のcategoryの特徴がある場合、label encodingを通過すると、各categoryは0からm-1の間の数にマッピングされます.Label encodingはordinal feature(特徴に内在的な順序がある)に適している.
コード:
\2. One-hot encoding (OHE)
m個のcategoryを有する特徴については、独熱符号化(OHE)処理後、m個の二元特徴となり、各特徴は1個のcategoryに対応する.このm個の二元特性は反発し,毎回1つだけ活性化した.
独熱符号化は、元の特徴に内在的な順序が欠けているという問題を解決するが、欠点はhigh-cardinality categorical feature(categoryの数が多い)に対して、符号化後の特徴空間が大きすぎる(ここではPCAの次元ダウンを考慮することができる)、one-hot featureがunbalancedと比較されるため、ツリーモデルでは毎回の切り分け利得が小さく、ツリーモデルは通常grow very deepを必要とし、良好な精度を得ることができる.従ってOHEはcategory数<4の場合に一般的に用いられる.
参考:Using Categorical Data with One Hot Encoding
コード:
\3. Target encoding (or likelihood encoding, impact encoding, mean encoding)
Target encodingはtarget mean value(among each category)を用いてcategorical featureを符号化する.target variable leakを減らすために、主流の方法は2 levels of cross-validationを用いてtarget meanを求めることであり、構想は以下の通りである. train dataを20-folds(例:infold:fold 2-20,out of fold:fold 1) に分割する. は、各infold(fold 2-20)を再び10-folds(例:inner_infold:fold 2-10、Inner_oof:fold 1) に分割する. 10-foldsのinner out of folds値(例:inner_infold#2-10のtargetの平均値をinner_oof#1の予測値として使用する) を計算する. 10個のinner out of folds値を平均してinner_を得るoof_mean
計算oof_mean(例:infold#2-20のinner_oof_meanを使用してout of fold#1のoof_mean を予測する.
train dataのoof_meanマッピングtest data完了符号化 参考:Likelihood encoding of categorical features
open source package category_encoders: scikit-learn-contrib/categorical-encoding
コード:
\4. beta target encoding
初めてこの方法を見たのはkaggleコンテストAvito Demand Prediction Challenge 14位のsolution分かち合い:14 th Place Solution:The Almost Golden Defenders
target encodingと同様に、beta target encodingもtarget mean value(among each category)を用いてcategorical featureを符号化する.異なる点は、target variable leakをさらに低減するために、beta target encodingは、5-fold CVの前ではなく、5-fold CVの内部で発生することである. train dataを5-folds(5-fold cross validation) に分割する. target encoding based on infold data train model get out of fold prediction
同時にbeta target encodingはsmoothing termに加入し、meanの代わりにbayesian meanを使用した.Bayesian mean(Bayesian average)の考え方:あるcategoryがデータ量が少ない場合(
参考:Beta Target Encoding
コード:
また、target encodingおよびbeta target encodingでは、target mean(or bayesian mean)を使用する必要はありません.medium、frqequency、mode、variance、skewness、and kurtosis–またはtargetとcorrelationのある集計値を含む他の統計値を使用することもできます.
5.何も処理しない(モデル自動符号化) XgBoostとRandom Forestは、categorical featureを直接処理することはできません.numerical featureに符号化する必要があります. lightgbmとCatBoostは、categorical featureを直接処理することができます. lightgbm:label encodingを先にする必要があります.特定のアルゴリズム(On Group for Maximum Homogeneity)でoptimal splitを見つけ、ONEよりも効果的です.one-hot encodingを採用することもできます.Features - LightGBM documentation CatBoost:label encodingを先にする必要はありません.one-hot encoding,target encoding(with regularization)を選択できます.CatBoost — Transforming categorical features to numerical features — Yandex Technologies
参照先:https://towardsdatascience.com/catboost-vs-light-gbm-vs-xgboost-5f93620723db
まとめ:
\1. Label encoding
m個のcategoryの特徴がある場合、label encodingを通過すると、各categoryは0からm-1の間の数にマッピングされます.Label encodingはordinal feature(特徴に内在的な順序がある)に適している.
コード:
# train -> training dataframe
# test -> test dataframe
# cat_cols -> categorical columns
for col in cat_cols:
le = LabelEncoder()
le.fit(np.concatenate([train[col], test[col]]))
train[col] = le.transform(train[col])
test[col] = le.transform(test[col])
\2. One-hot encoding (OHE)
m個のcategoryを有する特徴については、独熱符号化(OHE)処理後、m個の二元特徴となり、各特徴は1個のcategoryに対応する.このm個の二元特性は反発し,毎回1つだけ活性化した.
独熱符号化は、元の特徴に内在的な順序が欠けているという問題を解決するが、欠点はhigh-cardinality categorical feature(categoryの数が多い)に対して、符号化後の特徴空間が大きすぎる(ここではPCAの次元ダウンを考慮することができる)、one-hot featureがunbalancedと比較されるため、ツリーモデルでは毎回の切り分け利得が小さく、ツリーモデルは通常grow very deepを必要とし、良好な精度を得ることができる.従ってOHEはcategory数<4の場合に一般的に用いられる.
参考:Using Categorical Data with One Hot Encoding
コード:
# train -> training dataframe
# test -> test dataframe
# cat_cols -> categorical columns
df = train.append(test).reset_index()
original_column = list(df.columns)
df = pd.get_dummies(df, columns = cat_cols, dummy_na = True)
new_column = [c for c in df.columns if c not in original_column ]
\3. Target encoding (or likelihood encoding, impact encoding, mean encoding)
Target encodingはtarget mean value(among each category)を用いてcategorical featureを符号化する.target variable leakを減らすために、主流の方法は2 levels of cross-validationを用いてtarget meanを求めることであり、構想は以下の通りである.
open source package category_encoders: scikit-learn-contrib/categorical-encoding
コード:
# train -> training dataframe
# test -> test dataframe
n_folds = 20
n_inner_folds = 10
likelihood_encoded = pd.Series()
likelihood_coding_map = {}
oof_default_mean = train[target].mean() # global prior mean
kf = KFold(n_splits=n_folds, shuffle=True)
oof_mean_cv = pd.DataFrame()
split = 0
for infold, oof in kf.split(train[feature]):
print ('==============level 1 encoding..., fold %s ============' % split)
inner_kf = KFold(n_splits=n_inner_folds, shuffle=True)
inner_oof_default_mean = train.iloc[infold][target].mean()
inner_split = 0
inner_oof_mean_cv = pd.DataFrame()
likelihood_encoded_cv = pd.Series()
for inner_infold, inner_oof in inner_kf.split(train.iloc[infold]):
print ('==============level 2 encoding..., inner fold %s ============' % inner_split)
# inner out of fold mean
oof_mean = train.iloc[inner_infold].groupby(by=feature)[target].mean()
# assign oof_mean to the infold
likelihood_encoded_cv = likelihood_encoded_cv.append(train.iloc[infold].apply(
lambda x : oof_mean[x[feature]]
if x[feature] in oof_mean.index
else inner_oof_default_mean, axis = 1))
inner_oof_mean_cv = inner_oof_mean_cv.join(pd.DataFrame(oof_mean), rsuffix=inner_split, how='outer')
inner_oof_mean_cv.fillna(inner_oof_default_mean, inplace=True)
inner_split += 1
oof_mean_cv = oof_mean_cv.join(pd.DataFrame(inner_oof_mean_cv), rsuffix=split, how='outer')
oof_mean_cv.fillna(value=oof_default_mean, inplace=True)
split += 1
print ('============final mapping...===========')
likelihood_encoded = likelihood_encoded.append(train.iloc[oof].apply(
lambda x: np.mean(inner_oof_mean_cv.loc[x[feature]].values)
if x[feature] in inner_oof_mean_cv.index
else oof_default_mean, axis=1))
######################################### map into test dataframe
train[feature] = likelihood_encoded
likelihood_coding_mapping = oof_mean_cv.mean(axis = 1)
default_coding = oof_default_mean
likelihood_coding_map[feature] = (likelihood_coding_mapping, default_coding)
mapping, default_mean = likelihood_coding_map[feature]
test[feature] = test.apply(lambda x : mapping[x[feature]]
if x[feature] in mapping
else default_mean,axis = 1)
\4. beta target encoding
初めてこの方法を見たのはkaggleコンテストAvito Demand Prediction Challenge 14位のsolution分かち合い:14 th Place Solution:The Almost Golden Defenders
target encodingと同様に、beta target encodingもtarget mean value(among each category)を用いてcategorical featureを符号化する.異なる点は、target variable leakをさらに低減するために、beta target encodingは、5-fold CVの前ではなく、5-fold CVの内部で発生することである.
同時にbeta target encodingはsmoothing termに加入し、meanの代わりにbayesian meanを使用した.Bayesian mean(Bayesian average)の考え方:あるcategoryがデータ量が少ない場合(
参考:Beta Target Encoding
コード:
# train -> training dataframe
# test -> test dataframe
# N_min -> smoothing term, minimum sample size, if sample size is less than N_min, add up to N_min
# target_col -> target column
# cat_cols -> categorical colums
# Step 1: fill NA in train and test dataframe
# Step 2: 5-fold CV (beta target encoding within each fold)
kf = KFold(n_splits=5, shuffle=True, random_state=0)
for i, (dev_index, val_index) in enumerate(kf.split(train.index.values)):
# split data into dev set and validation set
dev = train.loc[dev_index].reset_index(drop=True)
val = train.loc[val_index].reset_index(drop=True)
feature_cols = []
for var_name in cat_cols:
feature_name = f'{var_name}_mean'
feature_cols.append(feature_name)
prior_mean = np.mean(dev[target_col])
stats = dev[[target_col, var_name]].groupby(var_name).agg(['sum', 'count'])[target_col].reset_index()
### beta target encoding by Bayesian average for dev set
df_stats = pd.merge(dev[[var_name]], stats, how='left')
df_stats['sum'].fillna(value = prior_mean, inplace = True)
df_stats['count'].fillna(value = 1.0, inplace = True)
N_prior = np.maximum(N_min - df_stats['count'].values, 0) # prior parameters
dev[feature_name] = (prior_mean * N_prior + df_stats['sum']) / (N_prior + df_stats['count']) # Bayesian mean
### beta target encoding by Bayesian average for val set
df_stats = pd.merge(val[[var_name]], stats, how='left')
df_stats['sum'].fillna(value = prior_mean, inplace = True)
df_stats['count'].fillna(value = 1.0, inplace = True)
N_prior = np.maximum(N_min - df_stats['count'].values, 0) # prior parameters
val[feature_name] = (prior_mean * N_prior + df_stats['sum']) / (N_prior + df_stats['count']) # Bayesian mean
### beta target encoding by Bayesian average for test set
df_stats = pd.merge(test[[var_name]], stats, how='left')
df_stats['sum'].fillna(value = prior_mean, inplace = True)
df_stats['count'].fillna(value = 1.0, inplace = True)
N_prior = np.maximum(N_min - df_stats['count'].values, 0) # prior parameters
test[feature_name] = (prior_mean * N_prior + df_stats['sum']) / (N_prior + df_stats['count']) # Bayesian mean
# Bayesian mean is equivalent to adding N_prior data points of value prior_mean to the data set.
del df_stats, stats
# Step 3: train model (K-fold CV), get oof prediction
また、target encodingおよびbeta target encodingでは、target mean(or bayesian mean)を使用する必要はありません.medium、frqequency、mode、variance、skewness、and kurtosis–またはtargetとcorrelationのある集計値を含む他の統計値を使用することもできます.
5.何も処理しない(モデル自動符号化)
参照先:https://towardsdatascience.com/catboost-vs-light-gbm-vs-xgboost-5f93620723db