三种Target Encoding方式总结
目前看到的大多數特征工程方法都是針對數值特征的。本文介紹的Target Encoding是用于類別特征的。這是一種將類別編碼為數字的方法,就像One-hot或Label-encoding一樣,但和這種兩種方法不同的地方在于target encoding還使用目標來創建編碼,這就是我們所說的有監督特征工程方法。
Target Encoding是任何一種可以從目標中派生出數字替換特征類別的編碼方式。這種目標編碼有時被稱為平均編碼。應用于二進制目標時,也被稱為bin counting。(可能會遇到的其他名稱包括:likelihood encoding, impact encoding, and leave-one-out encoding。)
每種方法都有其缺點,target encoding的缺點主要有:
- 未知類別,會產生過擬合風險;
- 空值,采用填充的方法不能很好的進行評估;
- 長尾類別,對長尾類別這種少量數據的編碼會導致過擬合;
鑒于以上缺點的存在,一般會加入平滑來進行處理。
encoding = weight * in_category + (1 - weight) * overall weight = n / (n + m)說了半天它的缺點和如何解決這些缺點,該方式的優點有哪些呢?
- 高維數據特征:具有大量類別的可能很難編碼:One-hot會生成太多維度,而替代方案(如標簽編碼)可能不適合該類型。Target encoding在此處就很好的解決了這個問題;
- 領域經驗特征:根據之前的經驗,即使某項數據它在特征度量方面得分很低,你也可能會覺得一個分類特征應該很重要。Target encoding有助于揭示特征的真實信息。
在kaggle競賽寶典中,有一篇《Kaggle Master分享編碼神技-Beta Target Encoding》,很好的介紹了Beta Target Encoding,該編碼方案來源于kaggle曾經的競賽Avito Demand Prediction Challenge 第14名solution。從作者開源出來的代碼,我們發現該編碼和傳統Target Encoding不一樣。
- Beta Target Encoding可以提取更多的特征,不僅僅是均值,還可以是方差等等;
- 從作者的開源中,是沒有進行N Fold提取特征的,所以可能在時間上提取會更快一些;
從作者的對比上我們可以看到,使用Beta Target Encoding相較于直接使用LightGBM建模的效果可以得到大幅提升。
class BetaTargetEncoder(object):def __init__(self, group):self.group = groupself.stats = Noneself.whoami = "DOTA"# get counts from dfdef fit(self, df, target_col):# 先驗均值self.prior_mean = np.mean(df[target_col]) stats = df[[target_col, self.group]].groupby(self.group)# count和sumstats = stats.agg(['sum', 'count'])[target_col] stats.rename(columns={'sum': 'n', 'count': 'N'}, inplace=True)stats.reset_index(level=0, inplace=True) self.stats = stats# extract posterior statisticsdef transform(self, df, stat_type, N_min=1):df_stats = pd.merge(df[[self.group]], self.stats, how='left')n = df_stats['n'].copy()N = df_stats['N'].copy()# fill in missingnan_indexs = np.isnan(n)n[nan_indexs] = self.prior_meanN[nan_indexs] = 1.0# prior parametersN_prior = np.maximum(N_min-N, 0)alpha_prior = self.prior_mean*N_priorbeta_prior = (1-self.prior_mean)*N_prior# posterior parametersalpha = alpha_prior + nbeta = beta_prior + N-n# calculate statisticsif stat_type=='mean':num = alphadem = alpha+betaelif stat_type=='mode':num = alpha-1dem = alpha+beta-2elif stat_type=='median':num = alpha-1/3dem = alpha+beta-2/3elif stat_type=='var':num = alpha*betadem = (alpha+beta)**2*(alpha+beta+1)elif stat_type=='skewness':num = 2*(beta-alpha)*np.sqrt(alpha+beta+1)dem = (alpha+beta+2)*np.sqrt(alpha*beta)elif stat_type=='kurtosis':num = 6*(alpha-beta)**2*(alpha+beta+1) - alpha*beta*(alpha+beta+2)dem = alpha*beta*(alpha+beta+2)*(alpha+beta+3)# replace missingvalue = num/demvalue[np.isnan(value)] = np.nanmedian(value)return value在Target Encoding的基礎上,K-Flod 目標編碼的基本思想源自均值目標編碼,在均值目標編碼中,分類變量由對應于它們的目標均值替換。
class KFoldTargetEncoderTrain(base.BaseEstimator, base.TransformerMixin):def __init__(self,colnames,targetName,n_fold=5, verbosity=True,discardOriginal_col=False):self.colnames = colnamesself.targetName = targetNameself.n_fold = n_foldself.verbosity = verbosityself.discardOriginal_col = discardOriginal_colself.whoami = "DOTA"def fit(self, X, y=None):return selfdef transform(self,X):assert(type(self.targetName) == str)assert(type(self.colnames) == str)assert(self.colnames in X.columns)assert(self.targetName in X.columns)mean_of_target = X[self.targetName].mean()kf = KFold(n_splits = self.n_fold,shuffle = False, random_state=2019)col_mean_name = self.colnames + '_' + 'Kfold_Target_Enc'X[col_mean_name] = np.nanfor tr_ind, val_ind in kf.split(X):X_tr, X_val = X.iloc[tr_ind], X.iloc[val_ind]X.loc[X.index[val_ind], col_mean_name] = X_val[self.colnames].map(X_tr.groupby(self.colnames)[self.targetName].mean())X[col_mean_name].fillna(mean_of_target, inplace = True)if self.verbosity:encoded_feature = X[col_mean_name].valuesprint('Correlation between the new feature, {} and, {} is {}.'.format(col_mean_name,self.targetName, np.corrcoef(X[self.targetName].values,encoded_feature)[0][1]))if self.discardOriginal_col:X = X.drop(self.targetName, axis=1)return X總結
以上是生活随笔為你收集整理的三种Target Encoding方式总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何解决高维稀疏的user-item矩阵
- 下一篇: 2021年Kaggle所有赛事TOP方案