[PYTHON] J'ai jeté un œil au contenu de sklearn (scikit-learn) (1) ~ Qu'en est-il de l'implémentation de CountVectorizer? ~

introduction

Dans cet article, j'aimerais jeter un œil au contenu de sklearn. Récemment, de nombreux livres ont été publiés pour vous permettre de mettre en œuvre vous-même des algorithmes d'apprentissage automatique. Je ne l'ai pas lu moi-même, mais je crois que si vous regardez de plus près le contenu de sklearn sans lire ce livre, vous vous y habituerez pour pouvoir le mettre en œuvre vous-même sans acheter de livre. De plus, sklearn est un package gratuit qui est édité quotidiennement par un grand nombre d'utilisateurs, de sorte que le programme est merveilleusement optimisé. C'est donc un programme très poli et il n'y a aucune raison de ne pas l'utiliser pour étudier le programme! !! Cela dit, même si un débutant regarde soudainement le contenu de sklearn, je pense que c'est vrai qu'il ne peut pas le comprendre. Même si je regardais le contenu de sklearn, je ne pouvais pas l'organiser dans mon esprit. Alors, y a-t-il quelqu'un qui peut expliquer le contenu du paquet sklearn? Quand. .. .. .. Ce que je pensais ne figurait pas parmi les bons. Donc, j'ai pensé que je devrais trouver du temps libre et jeter un œil au contenu. !! Je ne vais pas l'expliquer si soigneusement, mais je vais juste jeter un coup d'œil rapide. Comme point de départ, examinons le contenu d'un simple CountVectorizer.

CountVectorizer CountVectorizer est un algorithme qui compte la fréquence d'apparition des mots. La fréquence d'apparition des mots compte le nombre de fois qu'un mot apparaît dans une phrase et est utilisé, et peut être facilement calculée à l'aide du vecteur de décompte de sklearn. La méthode de détermination de la fréquence d'apparition utilise une méthode appelée extraction de quantité de caractéristiques. L'extraction de caractéristiques est une vectorisation des caractéristiques des données d'entraînement, et dans ce cas, la fréquence d'occurrence des mots correspond au vecteur (valeur numérique) quote % E3% 81% AEcountvectorizer% E3% 82% 92% E7% 94% A8% E3% 81% 84% E3% 81% A6% E5% 8D% 98% E8% AA% 9E% E3% 81% AE% E5 % 87% BA% E7% 8F% BE% E9% A0% BB% E5% BA% A6% E3% 82% 92% E6% 95% B0% E3% 81% 88% E3% 81% A6% E3% 81 % BF% E3% 82% 8B /).

Nous allons jeter un coup d'oeil.

class CountVectorizer(_VectorizerMixin, BaseEstimator):
    def __init__(self, input='content', encoding='utf-8',
                 decode_error='strict', strip_accents=None,
                 lowercase=True, preprocessor=None, tokenizer=None,
                 stop_words=None, token_pattern=r"(?u)\b\w\w+\b",
                 ngram_range=(1, 1), analyzer='word',
                 max_df=1.0, min_df=1, max_features=None,
                 vocabulary=None, binary=False, dtype=np.int64):
        self.input = input
        self.encoding = encoding
        self.decode_error = decode_error
        self.strip_accents = strip_accents
        self.preprocessor = preprocessor
        self.tokenizer = tokenizer
        self.analyzer = analyzer
        self.lowercase = lowercase
        self.token_pattern = token_pattern
        self.stop_words = stop_words
        self.max_df = max_df
        self.min_df = min_df
        if max_df < 0 or min_df < 0:
            raise ValueError("negative value for max_df or min_df")
        self.max_features = max_features
        if max_features is not None:
            if (not isinstance(max_features, numbers.Integral) or
                    max_features <= 0):
                raise ValueError(
                    "max_features=%r, neither a positive integer nor None"
                    % max_features)
        self.ngram_range = ngram_range
        self.vocabulary = vocabulary
        self.binary = binary
        self.dtype = dtype

Ça ressemble à ça. Cet objet semble hériter de deux classes, mais fondamentalement fit et fit_transform suffisent pour l'utiliser. De plus, lorsque l'on regarde les valeurs initiales, il semble n'y avoir aucun paramètre requis. Regardons maintenant l'ajustement en premier.

    def fit(self, raw_documents, y=None):
        """Learn a vocabulary dictionary of all tokens in the raw documents.

        Parameters
        ----------
        raw_documents : iterable
            An iterable which yields either str, unicode or file objects.

        Returns
        -------
        self
        """
        self._warn_for_unused_params() ##préoccupation
        self.fit_transform(raw_documents)
        return self

Fait intéressant, il semble que fit_transform soit utilisé dans fit. Il s'avère que ce n'est pas fit, etc. qui est important, mais fit_transform. Jetons également un coup d'œil à celui ci-dessus.

    def _warn_for_unused_params(self):

        if self.tokenizer is not None and self.token_pattern is not None:
            warnings.warn("The parameter 'token_pattern' will not be used"
                          " since 'tokenizer' is not None'")

        if self.preprocessor is not None and callable(self.analyzer):
            warnings.warn("The parameter 'preprocessor' will not be used"
                          " since 'analyzer' is callable'")

        if (self.ngram_range != (1, 1) and self.ngram_range is not None
                and callable(self.analyzer)):
            warnings.warn("The parameter 'ngram_range' will not be used"
                          " since 'analyzer' is callable'")
        if self.analyzer != 'word' or callable(self.analyzer):
            if self.stop_words is not None:
                warnings.warn("The parameter 'stop_words' will not be used"
                              " since 'analyzer' != 'word'")
            if self.token_pattern is not None and \
               self.token_pattern != r"(?u)\b\w\w+\b":
                warnings.warn("The parameter 'token_pattern' will not be used"
                              " since 'analyzer' != 'word'")
            if self.tokenizer is not None:
                warnings.warn("The parameter 'tokenizer' will not be used"
                              " since 'analyzer' != 'word'")

Autant que je puisse voir, il semble que je cherche quelque chose qui ne va pas avec les paramètres. L'important ici est que la méthode ici n'est pas un CountVectorizer, mais un objet de _VectorizerMixin. Il semble donc que cet objet effectue une vérification d'erreur pour les paramètres et plus encore. De plus, lors de l'héritage de plusieurs objets, ajoutez Mixin à la fin comme _VectorizerMixin. Les objets avec ceci sont essentiellement utilisés en combinaison avec d'autres objets! !! Cela indique cela. Voyons maintenant l'importante méthode fit_transform.

    def fit_transform(self, raw_documents, y=None):

        if isinstance(raw_documents, str):  #Raw ici_Exclut les cas où les documents n'apparaissent pas dans le type de liste. type str inutile! !!
            raise ValueError(
                "Iterable over raw text documents expected, "
                "string object received.")

        self._validate_params() #n_Si la gamme de grammes convient
        self._validate_vocabulary()#préoccupation
        max_df = self.max_df #Ceci est un petit point. Puisqu'il est difficile de se décrire plusieurs fois, nous l'utilisons ici comme variable.
        min_df = self.min_df
        max_features = self.max_features

        vocabulary, X = self._count_vocab(raw_documents,
                                          self.fixed_vocabulary_) #préoccupation
 
        if self.binary:
            X.data.fill(1)

        if not self.fixed_vocabulary_:
            X = self._sort_features(X, vocabulary) 

            n_doc = X.shape[0]
            max_doc_count = (max_df
                             if isinstance(max_df, numbers.Integral)
                             else max_df * n_doc)
            min_doc_count = (min_df
                             if isinstance(min_df, numbers.Integral)
                             else min_df * n_doc)
            if max_doc_count < min_doc_count:
                raise ValueError(
                    "max_df corresponds to < documents than min_df")
            X, self.stop_words_ = self._limit_features(X, vocabulary,
                                                       max_doc_count,
                                                       min_doc_count,
                                                       max_features)

            self.vocabulary_ = vocabulary

        return X #Renvoie un vecteur


Jetons un coup d'œil au premier point, la méthode de self._validate_vocabulary ().

    def _validate_vocabulary(self):
        vocabulary = self.vocabulary #dictionnaire
        if vocabulary is not None: #Le dictionnaire est-il entré comme valeur initiale?#Lorsque le dictionnaire n'est pas défini. Ou ceci est exécuté lors du montage une fois
            if isinstance(vocabulary, set): 
                vocabulary = sorted(vocabulary)
            if not isinstance(vocabulary, Mapping): #Le vocabulaire est-il correctement de type dict? Fait l'objet d'une enquête.
                vocab = {}
                for i, t in enumerate(vocabulary):
                    if vocab.setdefault(t, i) != i: #Vérifiez ici les expressions en double dans le dictionnaire
                        msg = "Duplicate term in vocabulary: %r" % t
                        raise ValueError(msg)
                vocabulary = vocab
            else:#Votre dictionnaire n'est pas de type dict, mais est-ce correct? ??
                indices = set(vocabulary.values())
                if len(indices) != len(vocabulary):
                    raise ValueError("Vocabulary contains repeated indices.")
                for i in range(len(vocabulary)):
                    if i not in indices:
                        msg = ("Vocabulary of size %d doesn't contain index "
                               "%d." % (len(vocabulary), i))
                        raise ValueError(msg)
            if not vocabulary:
                raise ValueError("empty vocabulary passed to fit")
            self.fixed_vocabulary_ = True #Le dictionnaire est correctement défini
            self.vocabulary_ = dict(vocabulary) #Formez un dictionnaire.
        else: #Lorsque le dictionnaire n'est pas entré dans les paramètres initiaux.
            self.fixed_vocabulary_ = False #Le dictionnaire n'est pas défini.

Cette méthode est une méthode de vectorizerMixin. Cette méthode forme-t-elle essentiellement un dictionnaire? ?? Cela semble être une méthode pour vérifier. La création d'un dictionnaire est un élément important qui correspond à la colonne de sortie. Si aucun dictionnaire n'est créé pour les paramètres initiaux, self.fixed_vocabulary = False est exécuté. Cette méthode est en fait appelée la deuxième fois, c'est-à-dire lorsqu'elle est transformée. Par conséquent, self.fixed_vocabulary_ = True self.vocabulary_ = dict (vocabulaire) Ces deux sont exécutés.

Maintenant que nous avons confirmé que le dictionnaire a été créé, vérifions self.count_vocab (raw_documents, self.fixed_vocabulary).

    def _count_vocab(self, raw_documents, fixed_vocab):
        """Create sparse feature matrix, and vocabulary where fixed_vocab=False
        """
        if fixed_vocab:#Lorsque le dictionnaire est créé
            vocabulary = self.vocabulary_
        else:#Lorsque le dictionnaire n'a pas été créé
            # Add a new value when a new vocabulary item is seen
            vocabulary = defaultdict()
            vocabulary.default_factory = vocabulary.__len__ #En faisant ces réglages, le vocabulaire[mot]とすることでそのmotに自動でindexが設定されます.結構役たちます.

        analyze = self.build_analyzer() #Ici n_Des paramètres tels que le gramme sont applicables.
        j_indices = []
        indptr = []

        values = _make_int_array()
        indptr.append(0)
        for doc in raw_documents:#Lisez des données unidimensionnelles.
            #doc = ["hoge hogeee hogeeeee"]Se sentir comme
            feature_counter = {}
            for feature in analyze(doc):#mot
                #feature = "hoge"Se sentir comme
                try:
                    feature_idx = vocabulary[feature] #Ici hoge:1 hogee:2 hogeee:C'est comme 3. fonctionnalité_idx est une donnée numérique. Si c'est hoge, 1 est changé.
                    if feature_idx not in feature_counter:
                        feature_counter[feature_idx] = 1 #feature_Si ce n'est pas dans le dictionnaire du compteur.
                    else:
                        feature_counter[feature_idx] += 1 #feature_Si c'est dans le dictionnaire du compteur+1 être
                except KeyError:
                    # Ignore out-of-vocabulary items for fixed_vocab=True
                    continue

            j_indices.extend(feature_counter.keys()) #Mots du dictionnaire (numérique)
            values.extend(feature_counter.values()) #Combien de fois un mot du dictionnaire apparaît
            indptr.append(len(j_indices)) 
            #Les trois méthodes ci-dessus sont les méthodes qui apparaissent lors de la création d'un modèle fragmenté.

        if not fixed_vocab: #Exécuter lorsque le dictionnaire n'a pas été créé
            # disable defaultdict behaviour
            vocabulary = dict(vocabulary)
            if not vocabulary:
                raise ValueError("empty vocabulary; perhaps the documents only"
                                 " contain stop words")

        if indptr[-1] > np.iinfo(np.int32).max:  # = 2**31 - 1
            if _IS_32BIT:
                raise ValueError(('sparse CSR array has {} non-zero '
                                  'elements and requires 64 bit indexing, '
                                  'which is unsupported with 32 bit Python.')
                                 .format(indptr[-1]))
            indices_dtype = np.int64

        else:
            indices_dtype = np.int32
        j_indices = np.asarray(j_indices, dtype=indices_dtype)
        indptr = np.asarray(indptr, dtype=indices_dtype)
        values = np.frombuffer(values, dtype=np.intc)

        X = sp.csr_matrix((values, j_indices, indptr),
                          shape=(len(indptr) - 1, len(vocabulary)),
                          dtype=self.dtype)
        X.sort_indices()
        return vocabulary, X #Dictionnaire et X(Valeur vectorielle de clairsemée)

Je pense que beaucoup de gens connaissent les algorithmes utilisés pour créer des dictionnaires ici. Ce que vous faites n'est pas si difficile. Cependant, utiliser la valeur éparse est un peu compliqué.

Eh bien, c'est la dernière étape.

        if not self.fixed_vocabulary_: #Exécuter lorsque False
            X = self._sort_features(X, vocabulary) #Réorganisez le dictionnaire proprement.

            n_doc = X.shape[0]
            max_doc_count = (max_df
                             if isinstance(max_df, numbers.Integral)
                             else max_df * n_doc)
            min_doc_count = (min_df
                             if isinstance(min_df, numbers.Integral)
                             else min_df * n_doc)
            if max_doc_count < min_doc_count:
                raise ValueError(
                    "max_df corresponds to < documents than min_df")
            X, self.stop_words_ = self._limit_features(X, vocabulary,
                                                       max_doc_count,
                                                       min_doc_count,
                                                       max_features)

            self.vocabulary_ = vocabulary #Définir le dictionnaire ici

        return X

Ce qui précède est fondamentalement étrange si min_doc_count <min_doc_count! Dans self._limit_features (), la dimension est réduite en fonction de la fréquence d'apparition. return X renvoie le vecteur avec scipy.

À la fin

J'ai jeté un coup d'œil au contenu de Count Vectorizer dans sklearn. Le programme n'était pas si compliqué car aucune formule n'est apparue cette fois. Si vous le divisez en parties inattendues, chaque processus est simple. Jetez un œil à cet article et voyez comment il est fabriqué par vous-même. Ensuite, je pense que je vais essayer TF-IDF. Je l'écrirai quand j'en aurai envie. c'est tout

Recommended Posts

J'ai jeté un œil au contenu de sklearn (scikit-learn) (1) ~ Qu'en est-il de l'implémentation de CountVectorizer? ~
Un résumé divers de ce que j'ai recherché sur Ansible
Un mémorandum sur la mise en œuvre des recommandations en Python
Jetons un coup d'œil à la carte des fonctionnalités de YOLO v3
J'ai examiné de plus près pourquoi l'auto Python est nécessaire
J'ai fait réfléchir AI aux paroles de Genshi Yonezu (implémentation)
J'ai vérifié le contenu du volume du docker
J'ai pris un benchmark de compression h5py
L'histoire de l'adresse IPv6 que je souhaite conserver au minimum
J'ai lu l'implémentation de range (Objects / rangeobject.c)
À propos de la vitesse de traitement de SVM (SVC) de scikit-learn
Écrire une note sur la version python de python virtualenv
À propos du contenu de développement de l'apprentissage automatique (exemple)
Ce à quoi j'ai pensé dans la question de l'examen d'entrée de "Bayes Statistics from the Basics"
J'ai réfléchi à la raison pour laquelle Python self est nécessaire avec le sentiment d'un interpréteur Python
Un mémorandum sur les avertissements dans les résultats de sortie de pylint
[GoLang] Définissez un espace au début du commentaire
Jetez un œil au traitement de LightGBM Tuner
Implémentation python de la classe de régression linéaire bayésienne
Regardez le score de Go d'un joueur de Go professionnel
J'ai suivi la mise en place de la commande du (seconde moitié)
Un résumé des choses que j'ai touchées comme un blog
Tâches au démarrage d'un nouveau projet python
Qu'est-ce qu'un moteur de recommandation? Résumé des types
Regardez de plus près le tutoriel Kaggle / Titanic
J'ai recherché le contenu de l'agent CloudWatch Logs
J'ai créé un outil pour obtenir les liens de réponse d'OpenAI Gym en même temps
À propos du contenu de wscript lors de la création d'un environnement en langage D comme celui avec Waf
Ce que j'ai pensé après avoir travaillé pendant un an sur le projet "Ne pas écrire de commentaires"
TensorFlow change-t-il l'image de l'apprentissage profond? Ce que j'ai pensé après avoir touché un peu