[PYTHON] J'ai essayé "K-Fold Target Encoding"

introduction

De fin 2019 à début 2020? Je me souviens que l'encodage cible est devenu un sujet brûlant.

Le codage cible remplace la variable catégorielle par la valeur moyenne de la variable objectif, mais si vous la traitez simplement, une fuite se produira, vous devez donc la concevoir. Pour éviter les fuites, vous pouvez prendre des mesures telles que la méthode Leave One Out qui utilise la valeur moyenne autre que la ligne à convertir, ou la division en K et le remplacement par la valeur moyenne autre que le pli qui inclut le secteur cible.

Il existe de nombreuses explications de l'encodage cible dans le monde, donc je ne l'expliquerai pas dans cet article (par exemple, ce site out-TS-% E4% BD% BF% E3% 81% A3% E3% 81% A1% E3% 82% 83% E3% 83% 80% E3% 83% A1) est très utile). Dans cet article, j'essaie de voir comment cela fonctionne en utilisant réellement le merveilleux code d'encodage cible de ce site. Je vais essayer.

référence

--Python: Comment utiliser l'encodage cible (https://blog.amedama.jp/entry/target-mean-encoding-types#Leave-one-out-TS-%E4%BD%BF%E3%81%A3% E3% 81% A1% E3% 82% 83% E3% 83% 80% E3% 83% A1)

procédure

Exemple de trame de données

Une fonction qui crée un exemple de bloc de données.

import numpy as np
import pandas as pd

def getRandomDataFrame(data, numCol):
    
    if data== 'train':
    
        key = ["A" if x ==0  else 'B' for x in np.random.randint(2, size=(numCol,))]
        value = np.random.randint(2, size=(numCol,))
        df = pd.DataFrame({'Feature':key, 'Target':value})
        return df
    
    elif data=='test':
        
        key = ["A" if x ==0  else 'B' for x in np.random.randint(2, size=(numCol,))]
        df = pd.DataFrame({'Feature':key})
        return df
    else:
        print(';)')

Vous pouvez générer un bloc de données avec le code suivant. Si test est spécifié comme premier argument, la chaîne de variable objectif ne sera pas sortie. Spécifiez le nombre de lignes dans le deuxième argument.

train = getRandomDataFrame('train', 10)
test = getRandomDataFrame('test', 10)

Le contenu est comme indiqué dans la figure ci-dessous. スクリーンショット 2020-01-22 23.37.22.png スクリーンショット 2020-01-22 23.37.32.png

K-fold Target Encoding Classe d'encodage cible K-fold. Il a fit et transform, donc il peut être utilisé de la même manière que le prétraitement de sklern. L'encodeur Test prend le résultat des données du train comme entrée et ajoute la fonction d'encodage cible. De plus, la partie (1) écrite dans le commentaire est traitée pour remplir la ligne qui devient nan lorsqu'elle est pliée en K avec la valeur moyenne. Nous verrons cela plus tard.

from sklearn import base
from sklearn.model_selection import KFold

class KFoldTargetEncoderTrain(base.BaseEstimator,
                               base.TransformerMixin):
    """How to use.
    targetc = KFoldTargetEncoderTrain('Feature','Target',n_fold=5)
    new_train = targetc.fit_transform(train)
    """
    def __init__(self,colnames,targetName,
                  n_fold=5, verbosity=True,
                  discardOriginal_col=False):
        self.colnames = colnames
        self.targetName = targetName
        self.n_fold = n_fold
        self.verbosity = verbosity
        self.discardOriginal_col = discardOriginal_col
        
    def fit(self, X, y=None):
        return self
    
    def 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.nan       
        
        for 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)  #Remplissez la partie devenue nan avec la valeur moyenne--(1)
            
        if self.verbosity:            
            encoded_feature = X[col_mean_name].values
            print('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
    
    
class TargetEncoderTest(base.BaseEstimator, base.TransformerMixin):
    """How to use.
    test_targetc = TargetEncoderTest(new_train,
                                      'Feature',
                                      'Feature_Kfold_Target_Enc')
    new_test = test_targetc.fit_transform(test)
    """
    
    def __init__(self,train,colNames,encodedName):
        
        self.train = train
        self.colNames = colNames
        self.encodedName = encodedName
        
    def fit(self, X, y=None):
        return self
    
    def transform(self,X):       
        mean =  self.train[[self.colNames, self.encodedName]].groupby(self.colNames).mean().reset_index() 
        
        dd = {}
        for index, row in mean.iterrows():
            dd[row[self.colNames]] = row[self.encodedName]
            X[self.encodedName] = X[self.colNames]
        X = X.replace({self.encodedName: dd})
        return X

Utilisez-le comme suit. Dans le constructeur de KFoldTargetEncoderTrain, spécifiez le nom de la colonne de la variable de catégorie à coder, le nom de la colonne de la variable objectif et le nombre de plis. Dans le constructeur de TargetEncoderTest, spécifiez la trame de données codées, le nom de la colonne de la variable catégorielle codée et le nom de la colonne du montant de la caractéristique codée cible ([nom de la colonne de la variable catégorielle codée] _Kfold_Target_Enc).

targetc = KFoldTargetEncoderTrain('Feature','Target',n_fold=5)
new_train = targetc.fit_transform(train)

test_targetc = TargetEncoderTest(new_train, 'Feature', 'Feature_Kfold_Target_Enc')
new_test = test_targetc.fit_transform(test)

Chacun a le contenu suivant. スクリーンショット 2020-01-22 23.54.52.png スクリーンショット 2020-01-22 23.55.05.png

Vérifions new_train. Puisqu'il s'agit d'un 5 fois, les données sont divisées en 5 plis de 2 lignes chacun. Le premier pli correspond aux première et deuxième lignes à partir du haut. Pour encoder les première et deuxième lignes, regardez les données combinées des quatre autres plis, les enregistrements des lignes 3-10. La valeur moyenne de «Cible» dans chaque groupe de A et B est 3/4 = 0,75 pour A et 1/4 = 0,25 pour B. Utilisez cette valeur pour encoder la valeur du premier repli. Les première et deuxième lignes du premier pli sont toutes les deux A, donc codez avec 0,75. Exécutez la procédure ci-dessus pour tous les plis.

Vérifions new_test. Les données de test sont codées à l'aide de la valeur moyenne des variables catégorielles à coder pour la fonction d'encodage cible des données Train. A est (0,75 + 0,75 + 0,6 + 0,8 + 0,5 + 0,5) / 6 = 0,65 et B est (0,3333333333333333 + 0,33333333333333333 + 0,0 + 0,0) / 4 = 0,166666666666666666.

Ensuite, considérons le cas de devenir nan.

train = getRandomDataFrame('train', 10)
train['Feature'].iloc[0] = "C"
スクリーンショット 2020-01-23 0.12.34.png

Avec ces données, je dois calculer la valeur moyenne du groupe C dans les plis restants pour encoder la première ligne, mais il n'y a pas de C dans les plis restants. Par conséquent, la fonction d'encodage cible sera nan. Par conséquent, remplissez-le avec la valeur moyenne des variables objectives de toutes les lignes. Par conséquent, C est (1 + 0 + 1 + 0 + 0 + 0 + 0 + 1 + 1 + 1) / 10 = 0,5.

スクリーンショット 2020-01-23 0.16.57.png

Au fait, si vous commentez la partie (1), vous pouvez la laisser comme np.nan. Avec LightGBM, vous pouvez apprendre et prédire même avec nan, il peut donc être préférable de ne pas renseigner la valeur moyenne.

Leave-one-out Target Encoding Il est dit que cette méthode ne doit pas être utilisée car elle fuit plus que le codage cible K fois. Cependant, je publierai le code car c'est un gros problème.

class LOOTargetEncoderTrain(base.BaseEstimator,
                               base.TransformerMixin):
    """How to use.
    targetc = LOOTargetEncoderTrain('Feature','Target')
    new_train = targetc.fit_transform(train)
    """
    def __init__(self,colnames,targetName,
                  verbosity=True, discardOriginal_col=False):
        self.colnames = colnames
        self.targetName = targetName
        self.verbosity = verbosity
        self.discardOriginal_col = discardOriginal_col
        
    def fit(self, X, y=None):
        return self
    
    def 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)
        
        col_mean_name = self.colnames + '_' + 'Kfold_Target_Enc'
        X[col_mean_name] = np.nan
        self.agg_X = X.groupby(self.colnames).agg({self.targetName: ['sum', 'count']})
        X[col_mean_name] = X.apply(self._loo_ts, axis=1)
        
        return X
        
    def _loo_ts(self, row):
        group_ts = self.agg_X.loc[row[self.colnames]]
        loo_sum = group_ts.loc[(self.targetName, 'sum')] - row[self.targetName]
        loo_count = group_ts.loc[(self.targetName, 'count')] - 1
        return loo_sum / loo_count

en conclusion

Cette fois, j'ai essayé l'encodage cible K-Fold.

Si la variable objective est binaire, il semble y avoir un moyen d'éviter le surapprentissage, tel que Smoothing.

Recommended Posts

J'ai essayé "K-Fold Target Encoding"
J'ai essayé de gratter
J'ai essayé PyQ
J'ai essayé AutoKeras
J'ai essayé le moulin à papier
J'ai essayé django-slack
J'ai essayé Django
J'ai essayé spleeter
J'ai essayé cgo
J'ai essayé d'utiliser paramétré
J'ai essayé d'utiliser argparse
J'ai essayé d'utiliser la mimesis
J'ai essayé d'utiliser anytree
J'ai essayé d'exécuter pymc
J'ai essayé le spoofing ARP
J'ai essayé d'utiliser aiomysql
J'ai essayé d'utiliser Summpy
J'ai essayé Python> autopep8
J'ai essayé d'utiliser coturn
J'ai essayé d'utiliser Pipenv
J'ai essayé d'utiliser matplotlib
J'ai essayé d'utiliser "Anvil".
J'ai essayé d'utiliser Hubot
J'ai essayé d'utiliser ESPCN
J'ai essayé PyCaret2.0 (pycaret-nightly)
J'ai essayé d'utiliser openpyxl
J'ai essayé le deep learning
J'ai essayé AWS CDK!
J'ai essayé d'utiliser Ipython
J'ai essayé de déboguer.
J'ai essayé d'utiliser PyCaret
J'ai essayé d'utiliser cron
J'ai essayé la mapview de Kivy
J'ai essayé d'utiliser ngrok
J'ai essayé d'utiliser face_recognition
J'ai essayé d'utiliser Jupyter
J'ai essayé de déplacer EfficientDet
J'ai essayé la programmation shell
J'ai essayé d'utiliser doctest
J'ai essayé Python> décorateur
J'ai essayé d'exécuter TensorFlow
J'ai essayé Auto Gluon
J'ai essayé d'utiliser du folium
J'ai essayé d'utiliser jinja2
J'ai essayé AWS Iot
J'ai essayé l'optimisation bayésienne!
J'ai essayé d'utiliser du folium
J'ai essayé d'utiliser la fenêtre de temps
J'ai essayé les réseaux d'itération de valeur
J'ai essayé fp-growth avec python
J'ai essayé de gratter avec Python
J'ai essayé d'apprendre PredNet
J'ai essayé Learning-to-Rank avec Elasticsearch!
[J'ai essayé d'utiliser Pythonista 3] Introduction
J'ai essayé d'utiliser easydict (mémo).
J'ai essayé d'organiser SVM.
J'ai essayé la reconnaissance faciale avec Face ++
J'ai essayé d'utiliser RandomForest