[Français] scikit-learn 0.18 Guide de l'utilisateur 4.2 Extraction de fonctionnalités

Google traduit http://scikit-learn.org/0.18/modules/feature_extraction.html [scikit-learn 0.18 User Guide 4. Dataset Conversion](http://qiita.com/nazoking@github/items/267f2371757516f8c168#4-%E3%83%87%E3%83%BC%E3%82%BF À partir de% E3% 82% BB% E3% 83% 83% E3% 83% 88% E5% A4% 89% E6% 8F% 9B)


4.2 Extraction de fonctionnalités

Le module sklearn.feature_extraction peut être utilisé pour extraire des entités dans des formats pris en charge par des algorithmes d'apprentissage automatique à partir d'ensembles de données de formats tels que du texte et des images.

** Remarque ** L'extraction de fonctionnalités est très différente de la sélection de fonctionnalités (http://qiita.com/nazoking@github/items/b9eb61f0c981af2cbdd0) .. Le premier consiste à convertir des données arbitraires telles que du texte et des images en fonctionnalités numériques pouvant être utilisées pour l'apprentissage automatique. Cette dernière est une technique d'apprentissage automatique appliquée à ces valeurs de caractéristiques.

4.2.1. Chargement des fonctionnalités depuis les dictionnaires

La classe DictVectorizer est une quantité de caractéristiques représentée comme une liste d'objets dict Python standard. Il peut être utilisé pour convertir un tableau en représentation NumPy / SciPy utilisée par l'estimateur scikit-learn. Bien que pas particulièrement rapide, le dict de Python a les avantages d'être facile à utiliser, clairsemé (pas besoin de stocker des fonctionnalités inexistantes) et de pouvoir stocker des noms de fonctionnalités en plus des valeurs. DictVectorizer implémente un codage un-à-un ou un codage "one-hot" pour les catégories (ou les valeurs nominales discrètes). Les attributs de catégorie sont des paires «attribut-valeur» qui sont limitées à une liste non ordonnée de valeurs discrètes (identificateurs de rubrique, types d'objets, balises, noms, etc.). Dans ce qui suit, «ville» est un attribut de catégorie et «température» est une quantité caractéristique numérique conventionnelle.

>>> measurements = [
...     {'city': 'Dubai', 'temperature': 33.},
...     {'city': 'London', 'temperature': 12.},
...     {'city': 'San Fransisco', 'temperature': 18.},
... ]

>>> from sklearn.feature_extraction import DictVectorizer
>>> vec = DictVectorizer()

>>> vec.fit_transform(measurements).toarray()
array([[  1.,   0.,   0.,  33.],
       [  0.,   1.,   0.,  12.],
       [  0.,   0.,   1.,  18.]])

>>> vec.get_feature_names()
['city=Dubai', 'city=London', 'city=San Fransisco', 'temperature']

DictVectorizer est une transformation d'expression utile pour la formation des classificateurs de phrases dans les modèles de traitement du langage naturel (généralement en extrayant des caractéristiques autour d'un mot d'intérêt particulier). Par exemple, supposons que vous ayez un premier algorithme qui extrait les balises de mot partiel (PoS) d'une phrase. Le dict suivant est une telle caractéristique extraite du mot «assis» dans la phrase «Le chat était assis sur le tapis».

>>> pos_window = [
...     {
...         'word-2': 'the',
...         'pos-2': 'DT',
...         'word-1': 'cat',
...         'pos-1': 'NN',
...         'word+1': 'on',
...         'pos+1': 'PP',
...     },
...     #Dans une application réelle, vous extrairez de nombreux dicts de ce type
... ]

Ces données peuvent être vectorisées dans une matrice bidimensionnelle clairsemée appropriée pour alimenter le classificateur (text.TfidfTransformer pour la normalisation. Après avoir été redirigé vers modules / generated / sklearn.feature_extraction.text.TfidfTransformer.html # sklearn.feature_extraction.text.TfidfTransformer).

>>> vec = DictVectorizer()
>>> pos_vectorized = vec.fit_transform(pos_window)
>>> pos_vectorized                
<1x6 sparse matrix of type '<... 'numpy.float64'>'
    with 6 stored elements in Compressed Sparse ... format>
>>> pos_vectorized.toarray()
array([[ 1.,  1.,  1.,  1.,  1.,  1.]])
>>> vec.get_feature_names()
['pos+1=PP', 'pos-1=NN', 'pos-2=DT', 'word+1=on', 'word-1=cat', 'word-2=the']

Comme vous pouvez l'imaginer, extraire un tel contexte autour de mots individuels dans le corpus de documents aboutit à une matrice très large (de nombreuses fonctionnalités one-hot), presque nulle. Sera. Pour stocker la structure de données résultante en mémoire, la classe DictVectorizer utilise la matrice scipy.sparse par défaut au lieu de numpy.ndarray.

4.2.2. Hachage des fonctionnalités

La classe FeatureHasher est un hachage de fonctionnalité ou "[astuce de hachage](https: //) en.wikipedia.org/wiki/Feature_hashing) est un vectoriseur rapide et à faible mémoire qui utilise une technologie connue sous le nom de. Au lieu de créer une table de conversion de valeur de fonctionnalité, FeatureHasher applique une fonction de hachage aux valeurs de fonctionnalité pour déterminer directement les numéros de colonne dans l'exemple de matrice. En conséquence, la vitesse est augmentée et l'utilisation de la mémoire est réduite au détriment de l'inspectabilité. Hasher ne se souvient pas de l'apparence des valeurs d'entités d'entrée et n'a pas la méthode ʻinverse_transform. Les fonctions de hachage peuvent provoquer des conflits entre des fonctionnalités (non pertinentes), de sorte que des fonctions de hachage signées sont utilisées et le signe de la valeur de hachage détermine le signe de la valeur stockée dans la matrice de sortie de la valeur de fonctionnalité. De cette manière, les collisions sont susceptibles d'être compensées plutôt que cumulatives, et la moyenne attendue des valeurs caractéristiques de sortie est nulle. Si non_negative = Trueest passé au constructeur, la valeur absolue sera récupérée. Cela annule une partie de la gestion des conflits, mais attend une entrée non négative [sklearn.naive_bayes.MultinomialNB](http://scikit-learn.org/stable/modules/generated/sklearn.naive_bayes.MultinomialNB. html # sklearn.naive_bayes.MultinomialNB) Estimator et [sklearn.feature_selection.chi2](http://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.chi2.html#sklearn.feature_selection.chi2) Fonctionnalités Vous pouvez transmettre la sortie à un sélecteur, etc. FeatureHasher accepte les mappages (tels que les dictées Python et leurs variantes dans le modulecollections), les paires (fonctionnalité, valeur) ou les listes de chaînes, selon le paramètre du constructeur ʻinput_type. Le mappage est traité comme une liste de (fonctionnalité, valeur), où['feat1', 'feat2', 'feat3']est[('feat1', 1), ('feat2', 1), ( Interprété comme «fonction3», 1)] ». Si une valeur de caractéristique apparaît plusieurs fois dans l'échantillon, les valeurs associées sont additionnées (ainsi('feat', 2)et('feat', 3.5)sont('feat', 5.5)). La sortie de FeatureHasher est toujours une matrice scipy.sparse de style CSR. Les hachages de fonctionnalités peuvent être utilisés pour la classification de documents, mais text.CountVectorizer, FeatureHasher n'effectue aucun fractionnement de mot ou autre prétraitement autre que l'encodage Unicode vers UTF-8. Pour la combinaison tokenization / hasher, voir [Vectoring a large text corpus using hash tricks](# 4238-% E3% 83% 8F% E3% 83% 83% E3% 82% B7% E3) % 83% A5% E3% 83% 88% E3% 83% AA% E3% 83% 83% E3% 82% AF% E3% 82% 92% E4% BD% BF% E7% 94% A8% E3% 81 % 97% E3% 81% A6% E5% A4% A7% E3% 81% 8D% E3% 81% AA% E3% 83% 86% E3% 82% AD% E3% 82% B9% E3% 83% 88 % E3% 82% B3% E3% 83% BC% E3% 83% 91% E3% 82% B9% E3% 82% 92% E3% 83% 99% E3% 82% AF% E3% 83% 88% E3 Voir% 83% AB% E5% 8C% 96% E3% 81% 99% E3% 82% 8B). À titre d'exemple, considérons une tâche de traitement du langage naturel au niveau du mot qui nécessite des fonctionnalités extraites d'une paire (token, part_of_speech). Vous pouvez utiliser les fonctions du générateur Python pour extraire des fonctionnalités:

def token_features(token, part_of_speech):
    if token.isdigit():
        yield "numeric"
    else:
        yield "token={}".format(token.lower())
        yield "token,pos={},{}".format(token, part_of_speech)
    if token[0].isupper():
        yield "uppercase_initial"
    if token.isupper():
        yield "all_uppercase"
    yield "pos={}".format(part_of_speech)

Ensuite, le raw_X fourni à FeatureHasher.transform peut être construit en utilisant:

 raw_X = (token_features(tok, pos_tagger(tok)) for tok in corpus)

Il est fourni à la laveuse comme suit.

hasher = FeatureHasher(input_type='string')
X = hasher.transform(raw_X)

Récupérez la matrice scipy.sparse X`. Notez le générateur qui facilite l'extraction des fonctionnalités. Les jetons ne sont traités que sur demande de Hasher.

4.2.2.1. Détails de mise en œuvre

FeatureHasher utilise une version 32 bits signée de MurmurHash3. En conséquence (et en raison des limitations de scipy.sparse), le nombre maximum de fonctionnalités actuellement prises en charge est de 2 $ ^ {31} -1 $. Dans la formulation originale de l'astuce de hachage de Weinberger et al., Deux fonctions de hachage distinctes $ h $ et $ \ xi $ ont été utilisées pour déterminer respectivement le numéro de colonne et le signe des valeurs des caractéristiques. L'implémentation actuelle fonctionne sur l'hypothèse que les bits de code MurmurHash3 sont indépendants des autres bits. Il est recommandé d'utiliser une puissance de 2 comme paramètre n_features, car il utilise simplement le reste de la division pour convertir la fonction de hachage en un numéro de colonne. Sinon, les valeurs des caractéristiques ne seront pas mappées uniformément.

--Référence: --Killian Weinberger, Anirban Dasgupta, John Langford, Alex Smola, Josh Attenberg (2009) Functional Hashing for Large-Scale Multitasking Learning Proc. ICML. - MurmurHash3

4.2.3. Extraction d'entités de texte

4.2.3.1. Représentation par sac de mots

L'analyse de texte est une application majeure des algorithmes d'apprentissage automatique. Cependant, les données brutes ne peuvent pas fournir un ensemble de symboles directement à l'algorithme lui-même. Parce que la plupart d'entre eux attendent des vecteurs d'entités numériques de taille fixe plutôt que des documents texte brut de longueur variable. Pour résoudre ce problème, scicit-learn fournit un utilitaire de la manière la plus courante pour extraire des caractéristiques numériques d'un contenu textuel.

  • Donnez un identifiant entier pour chaque jeton possible, comme la création de jetons d'une chaîne et l'utilisation d'espaces et de ponctuation comme séparateurs de jetons.
  • Comptez l'apparence des jetons dans chaque document.
  • Normaliser et pondérer en utilisant des jetons moins importants qui se produisent dans la majorité des échantillons / documents.

Dans ce schéma, les fonctionnalités et les exemples sont définis comme suit:

  • La fréquence d'apparition des jetons individuels est traitée comme une quantité de caractéristiques (peut ou non être normalisée). --Un vecteur de toutes les fréquences de jeton dans un document donné est considéré comme un échantillon multivarié.

Ainsi, un corpus d'un document peut être représenté par une matrice avec une ligne par document et une colonne pour chaque jeton (par exemple, mot) qui apparaît dans le corpus. La vectorisation est le processus général de conversion d'un ensemble de documents texte en un vecteur de caractéristiques numériques. Cette stratégie particulière (tokenisation, comptage, normalisation) est appelée le Sac de Mots ou représentation «Sac de n-grammes». Le document est décrit par le nombre d'occurrences du mot, ignorant complètement les informations de position relative du mot dans le document.

4.2.3.2. Clairsemé

La plupart des documents n'utilisent que quelques-uns des mots couramment utilisés dans les corpus, de sorte que la matrice résultante contient de nombreuses valeurs de caractéristiques nulles (généralement 99% ou plus). Par exemple, un ensemble de 10 000 documents courts (comme le courrier électronique) utilise un total de 100 000 ordres de vocabulaire et chaque document utilise de 100 à 1 000 mots uniques. En plus de pouvoir stocker de telles matrices en mémoire, les implémentations utilisent généralement une représentation éparse du package scipy.sparse pour accélérer les matrices / vecteurs algébriques.

4.2.3.3. Comment utiliser le vectoriseur commun

CountVectorizer fournit à la fois la tokenisation et le décompte des occurrences. Il est implémenté dans une classe.

>>> from sklearn.feature_extraction.text import CountVectorizer

Il existe de nombreux paramètres dans ce modèle, mais les valeurs par défaut sont très raisonnables (voir la documentation de référence (http://scikit-learn.org/stable/modules/classes.html#text-feature-extraction- pour plus d'informations). ref)) ::

>>> vectorizer = CountVectorizer(min_df=1)
>>> vectorizer                     
CountVectorizer(analyzer=...'word', binary=False, decode_error=...'strict',
        dtype=<... 'numpy.int64'>, encoding=...'utf-8', input=...'content',
        lowercase=True, max_df=1.0, max_features=None, min_df=1,
        ngram_range=(1, 1), preprocessor=None, stop_words=None,
        strip_accents=None, token_pattern=...'(?u)\\b\\w\\w+\\b',
        tokenizer=None, vocabulary=None)

Utilisez-le pour tokeniser et compter l'occurrence de mots dans le corpus minimal de documents texte.

>>> corpus = [
...     'This is the first document.',
...     'This is the second second document.',
...     'And the third one.',
...     'Is this the first document?',
... ]
>>> X = vectorizer.fit_transform(corpus)
>>> X                              
<4x9 sparse matrix of type '<... 'numpy.int64'>'
    with 19 stored elements in Compressed Sparse ... format>

Le paramètre par défaut consiste à extraire les mots de deux caractères ou plus et à tokeniser la chaîne. La fonction spécifique qui effectue cette étape peut être explicitement demandée.

>>> analyze = vectorizer.build_analyzer()
>>> analyze("This is a text document to analyze.") == (
...     ['this', 'is', 'text', 'document', 'to', 'analyze'])
True

Chaque terme détecté par l'analyseur lors de l'ajustement se voit attribuer un index entier unique qui correspond à la colonne de la matrice résultante. Vous pouvez obtenir l'interprétation de cette colonne comme suit:

>>> vectorizer.get_feature_names() == (
...     ['and', 'document', 'first', 'is', 'one',
...      'second', 'the', 'third', 'this'])
True

>>> X.toarray()           
array([[0, 1, 1, 1, 0, 0, 1, 0, 1],
       [0, 1, 0, 1, 0, 2, 1, 0, 1],
       [1, 0, 0, 0, 1, 0, 1, 1, 0],
       [0, 1, 1, 1, 0, 0, 1, 0, 1]]...)

Le mappage inversé des valeurs de caractéristiques aux index de colonne est stocké dans l'attribut vocabulary_ du vectoriseur.

>>> vectorizer.vocabulary_.get('document')
1

Par conséquent, les mots non trouvés dans le corpus d'apprentissage seront complètement ignorés lors des futurs appels à la méthode de conversion.

>>> vectorizer.transform(['Something completely new.']).toarray()
...                           
array([[0, 0, 0, 0, 0, 0, 0, 0, 0]]...)

Notez que dans le corpus précédent, le premier et le dernier documents ont exactement les mêmes mots et sont codés avec un vecteur d'égalité. En particulier, vous perdez l'information selon laquelle le dernier document est au format question. Pour stocker les informations de commande, vous pouvez extraire 2 grammes de mots en plus de 1 gramme (mots individuels).

>>> bigram_vectorizer = CountVectorizer(ngram_range=(1, 2),
...                                     token_pattern=r'\b\w+\b', min_df=1)
>>> analyze = bigram_vectorizer.build_analyzer()
>>> analyze('Bi-grams are cool!') == (
...     ['bi', 'grams', 'are', 'cool', 'bi grams', 'grams are', 'are cool'])
True

Le vocabulaire extrait par ce vectoriseur est beaucoup plus large et peut résoudre l'ambiguïté des informations de position d'ordre des mots une fois codées.

>>>
>>> X_2 = bigram_vectorizer.fit_transform(corpus).toarray()
>>> X_2
...                           
array([[0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0],
       [0, 0, 1, 0, 0, 1, 1, 0, 0, 2, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0],
       [1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0],
       [0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1]]...)

Plus précisément, la phrase de question "Est-ce que c'est" n'existe que dans le dernier document.

>>> feature_index = bigram_vectorizer.vocabulary_.get('is this')
>>> X_2[:, feature_index]     
array([0, 0, 0, 1]...)

4.2.3.4. Pondération Tf-idf

Dans un corpus de texte volumineux, il y a tellement de mots (par exemple, "the", "a", "is" en anglais) qu'ils ont peu d'informations significatives sur le contenu du document. Fournir des informations de fréquence directement au classifieur masque la fréquence des termes rares mais intéressants pour les termes très fréquemment utilisés. Il est très courant d'utiliser la transformation tf-idf pour rendre les informations de fréquence adaptées au classificateur. Tf-idf signifie le nombre de mots apparaissant (tf) et le nombre inverse de phrases apparaissant (idf), et $ \ text {tf-idf (t, d)} = \ text {tf (t, d)} \ fois \ texte {idf (t)} $. Le paramètre par défaut pour TfidfTransformer`` TfidfTransformer (norm = 'l2', use_idf = True, smooth_idf = True, sublinear_tf = False) La fréquence des mots, le nombre de fois qu'un mot apparaît dans un document donné, est multipliée par la composante IDF calculée comme suit:

\text{idf}(t) = log{\frac{1 + n_d}{1+\text{df}(d,t)}} + 1

Où $ n_d $ est le nombre total de documents et $ \ text {df} (d, t) $ est le nombre de documents contenant le mot $ t $. Le vecteur tf-idf résultant est normalisé par la norme euclidienne.

v_{norm} = \frac{v}{||v||_2} = \frac{v}{\sqrt{v{\_1}^2 + v{\_2}^2 + \dots + v{\_n}^2}}

Il s'agissait à l'origine d'une méthode de pondération des mots (fonction de classement des résultats de recherche des moteurs de recherche) développée pour la recherche d'informations, qui est souvent utilisée dans la classification et le regroupement de documents. Dans la section ci-dessous, comment exactement tf-idfs est calculé, [TfidfTransformer] de scikit-learn (http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfTransformer.html Calculé avec # sklearn.feature_extraction.text.TfidfTransformer) et TfidfVectorizer Montre si le tf-idfs donné est un peu différent de la notation standard du manuel qui définit idf comme suit:

\text{idf}(t) = log{\frac{n_d}{1+\text{df}(d,t)}}

Dans TfidfTransformer et TfidfVectorizer, smooth_idf = False ajoute" 1 "à idf au lieu du dénominateur de idf.

\text{idf}(t) = log{\frac{n_d}{\text{df}(d,t)}} + 1

Cette normalisation est implémentée par la classe TfidfTransformer.

>>> from sklearn.feature_extraction.text import TfidfTransformer
>>> transformer = TfidfTransformer(smooth_idf=False)
>>> transformer   
TfidfTransformer(norm=...'l2', smooth_idf=False, sublinear_tf=False,
                 use_idf=True)

Encore une fois, consultez la documentation de référence (http://scikit-learn.org/stable/modules/classes.html#text-feature-extraction-ref) pour plus d'informations sur tous les paramètres. Prenons l'exemple suivant. Le taux d'apparition du premier mot est de 100%, ce qui n'est pas très intéressant. Le taux d'apparition des deux autres fonctionnalités est inférieur à 50%, ce qui représente probablement mieux le contenu du document.

>>> counts = [[3, 0, 1],
...           [2, 0, 0],
...           [3, 0, 0],
...           [4, 0, 0],
...           [3, 2, 0],
...           [3, 0, 2]]
...
>>> tfidf = transformer.fit_transform(counts)
>>> tfidf                         
<6x3 sparse matrix of type '<... 'numpy.float64'>'
    with 9 stored elements in Compressed Sparse ... format>

>>> tfidf.toarray()                        
array([[ 0.81940995,  0.        ,  0.57320793],
       [ 1.        ,  0.        ,  0.        ],
       [ 1.        ,  0.        ,  0.        ],
       [ 1.        ,  0.        ,  0.        ],
       [ 0.47330339,  0.88089948,  0.        ],
       [ 0.58149261,  0.        ,  0.81355169]])

Chaque ligne est normalisée pour avoir une unité standard euclidienne:

v\_{norm} = \frac{v}{||v||\_2} = \frac{v}{\sqrt{v{\_1}^2 + v{\_2}^2 + \dots + v{_n}^2}}

Par exemple, vous pouvez calculer tf-idf dans le premier terme du premier document dans le tableau des nombres comme suit:

n_{d, {\text{term1}}} = 6  \\
\text{df}(d, t)_{\text{term1}} = 6  \\
\text{idf}(d, t)_{\text{term1}} = log \frac{n_d}{\text{df}(d, t)} + 1 = log(1)+1 = 1  \\
\text{tf-idf}_{\text{term1}} = \text{tf} \times \text{idf} = 3 \times 1 = 3

La répétition de ce calcul pour les deux autres termes du document donne:

\text{tf-idf}_{\text{term2}} = 0 \times log(6/1)+1 = 0 \\
\text{tf-idf}_{\text{term3}} = 1 \times log(6/2)+1 \approx 2.0986

Vecteur tf-idfs brut:

\text{tf-idf}_raw = [3, 0, 2.0986]

Ensuite, l'application de la norme euclidienne (L2) donne les tf-idfs suivants pour le document 1.

\frac{[3, 0, 2.0986]}{\sqrt{\big(3^2 + 0^2 + 2.0986^2\big)}}
= [ 0.819,  0,  0.573]

De plus, le paramètre par défaut smooth_idf = True ajoute un" 1 "à la molécule et au dénominateur. Il semble que le document supplémentaire contienne exactement une fois pour chaque terme de la collection. Empêche zéro pour cent.

\text{idf}(t) = log{\frac{1 + n_d}{1+\text{df}(d,t)}} + 1

En utilisant ce changement, le tf-idf dans la section 3 du document 1 est changé en 1.8473.

\text{tf-idf}_{\text{term3}} = 1 \times log(7/3)+1 \approx 1.8473

Et le tf-idf normalisé L2

\frac{[3, 0, 1.8473]}{\sqrt{\big(3^2 + 0^2 + 1.8473^2\big)}} = [0.8515, 0, 0.5243]
>>> transformer = TfidfTransformer()
>>> transformer.fit_transform(counts).toarray()
array([[ 0.85151335,  0.        ,  0.52433293],
       [ 1.        ,  0.        ,  0.        ],
       [ 1.        ,  0.        ,  0.        ],
       [ 1.        ,  0.        ,  0.        ],
       [ 0.55422893,  0.83236428,  0.        ],
       [ 0.63035731,  0.        ,  0.77630514]])

Le poids de chaque entité calculé par l'appel de méthode «fit» est stocké dans l'attribut model.

>>> transformer.idf_                       
array([ 1. ...,  2.25...,  1.84...])

Puisque tf-idf est si populaire pour les fonctions de texte, il existe une autre classe appelée TfidfVectorizer qui combine toutes les options de CountVectorizer et TfidfTransformer dans un seul modèle:

>>> from sklearn.feature_extraction.text import TfidfVectorizer
>>> vectorizer = TfidfVectorizer(min_df=1)
>>> vectorizer.fit_transform(corpus)
...                                
<4x9 sparse matrix of type '<... 'numpy.float64'>'
    with 19 stored elements in Compressed Sparse ... format>

La normalisation TF-IDF est souvent très utile, mais il peut y avoir des exemples où les marqueurs de génération binaire fournissent de meilleures fonctionnalités. Ceci peut être réalisé en utilisant le paramètre binary de CountVectorizer. En particulier, des estimateurs tels que Bernoulli Naive Bayes modélisent explicitement des variables probabilistes booléennes discrètes. .. De plus, un texte très court peut être bruyant avec tf-idf, tandis que les informations de génération binaire sont plus stables. Comme toujours, la meilleure façon d'ajuster les paramètres d'extraction d'entités est d'utiliser une recherche de grille à validation croisée, par exemple, en pipelinant l'extracteur d'entités avec un classificateur.

  • [Exemple de pipeline pour l'extraction et l'évaluation des fonctionnalités de texte](http://scikit-learn.org/stable/auto_examples/model_selection/grid_search_text_feature_extraction.html#sphx-glr-auto-examples-model-selection-grid- search-text-feature-extraction-py)

4.2.3.5. Décodage des fichiers texte

Le texte est composé de lettres, mais le fichier est composé d'octets. Ces octets représentent des caractères selon un certain codage. Pour travailler avec un fichier texte en Python, vous devez décoder cet octet en un jeu de caractères appelé Unicode. Les codages courants sont ASCII, Latin-1 (Europe occidentale), KOI8-R (russe), codages universels UTF-8 et UTF-16. Il y en a beaucoup d'autres.

Remarque: l'encodage est parfois appelé "jeu de caractères", mais ce terme est moins précis. Il peut y avoir plusieurs encodages pour un seul jeu de caractères.

L'extracteur de fonctionnalités de texte scikit-learn sait comment décoder un fichier texte, mais il n'apparaît que si vous spécifiez comment encoder le fichier. À cette fin, CountVectorizer a un paramètre ʻencoding. Je vais le prendre. Pour les fichiers texte modernes, l'encodage correct est probablement UTF-8, qui est la valeur par défaut (ʻencoding = "utf-8" ). Cependant, si le texte que vous chargez n'est pas réellement encodé en UTF-8, vous obtiendrez ʻUnicodeDecodeError. Pour les erreurs de décodage, désactivez le vectoriseur en définissant le paramètre decode_error sur " ignore "ou" replace "". Tapez help (bytes.decode) à l'invite Python pour voir la documentation de la fonction Python bytes.decode. Si vous rencontrez des difficultés pour décoder du texte, essayez-le:

  • Vérifiez quel est le codage réel du texte. Le fichier peut avoir un en-tête ou README indiquant le codage. Il peut également y avoir des encodages standard qui peuvent être supposés en fonction de la source du texte.
  • Vous pouvez utiliser le fichier de commandes UNIX pour découvrir les encodages courants. Le module chardet de Python est livré avec un script appelé chardetect.py qui devine un encodage particulier, mais je ne peux pas deviner qu'il est correct.
  • Vous pouvez également essayer UTF-8 et ignorer l'erreur. Vous pouvez décoder la chaîne d'octets avec bytes.decode (errors = 'replace') et remplacer toutes les erreurs de décodage par des caractères sans signification, ou définir decode_error = 'replace' dans le vectoriseur. .. Cela peut nuire à l'utilité des quantités de caractéristiques.
  • Le texte réel peut provenir de sources différentes qui peuvent utiliser des codages différents, et peut être décodé par inadvertance dans un codage différent de celui codé. Ceci est courant dans le texte récupéré sur le Web. Le package Python ftfy peut trier automatiquement les erreurs de décodage de certaines classes, donc il décode le texte inconnu en latin-1. Vous pouvez utiliser ftfy pour corriger l'erreur.
  • Si vous avez un fouillis d'encodages où le texte est difficile à trier (comme le jeu de données 20 Newsgroups), vous pouvez revenir à un encodage simple à un octet comme latin-1. Certains textes peuvent ne pas s'afficher correctement, mais au moins les octets de la même séquence représentent toujours la même fonction.

Par exemple, l'extrait de code suivant utilise chardet (non fourni avec scicit-learn et doit être installé séparément) pour déterminer l'encodage des trois textes. Puis vectorisez le texte et imprimez le vocabulaire appris. La sortie n'est pas affichée ici.

>>> import chardet    
>>> text1 = b"Sei mir gegr\xc3\xbc\xc3\x9ft mein Sauerkraut"
>>> text2 = b"holdselig sind deine Ger\xfcche"
>>> text3 = b"\xff\xfeA\x00u\x00f\x00 \x00F\x00l\x00\xfc\x00g\x00e\x00l\x00n\x00 \x00d\x00e\x00s\x00 \x00G\x00e\x00s\x00a\x00n\x00g\x00e\x00s\x00,\x00 \x00H\x00e\x00r\x00z\x00l\x00i\x00e\x00b\x00c\x00h\x00e\x00n\x00,\x00 \x00t\x00r\x00a\x00g\x00 \x00i\x00c\x00h\x00 \x00d\x00i\x00c\x00h\x00 \x00f\x00o\x00r\x00t\x00"
>>> decoded = [x.decode(chardet.detect(x)['encoding'])
...            for x in (text1, text2, text3)]        
>>> v = CountVectorizer().fit(decoded).vocabulary_    
>>> for term in v: print(v)                           

(Selon la version de chardet, le premier peut être erroné.) Pour une présentation d'Unicode et de l'encodage de caractères, voir [Minimums absolus que tout développeur de logiciel doit connaître sur Unicode] de Joel Spolsky (http://www.joelonsoftware.com/articles/Unicode.html). S'il vous plaît.

4.2.3.6. Applications et échantillons

La représentation du sac de mots est très simple, mais elle est en fait étonnamment utile. Surtout dans les ** paramètres supervisés **, il peut être joliment combiné avec un modèle linéaire rapide et évolutif pour former le ** classificateur de documents **.

  • [Classification des documents texte à l'aide de fonctionnalités éparses](http://scikit-learn.org/stable/auto_examples/text/document_classification_20newsgroups.html#sphx-glr-auto-examples-text-document-classification-20newsgroups- py)

Dans le cadre non supervisé, des documents similaires sont rassemblés en appliquant un algorithme de clustering tel que K-means. Peut être groupé.

Enfin, en assouplissant les contraintes d'allocation strictes du clustering, par exemple en utilisant la décomposition matricielle non négative (NMF ou NNMF) (http://scikit-learn.org/stable/modules/decomposition.html#nmf). Il est possible de découvrir les principaux thèmes du corpus.

  • [Extraction de sujets à l'aide de la décomposition de matrice non négative et de l'attribution potentielle de jilliclets](http://scikit-learn.org/stable/auto_examples/applications/topics_extraction_with_nmf_lda.html#sphx-glr-auto-examples-applications-topics-extraction -avec-nmf-lda-py)

4.2.3.7. Limitations d'expression dans Sac de mots

L'ensemble unigramme (de quel sac de mots il s'agit) ne peut pas capturer de phrases ou d'expressions à plusieurs mots et ignore efficacement les dépendances d'ordre des mots. De plus, le modèle Bag of Words n'explique pas les éventuelles fautes d'orthographe ou dérivations de mots. N grammes pour le sauvetage! Au lieu de construire un simple ensemble d'unigramme (n = 1), on pourrait préférer un ensemble de bigrammes (n = 2) qui compte l'occurrence de paires de mots consécutives. Alternativement, vous pouvez envisager un ensemble de N-grammes de lettres, une expression flexible pour les fautes d'orthographe et les dérivés. Par exemple, disons que vous avez affaire à un corpus de deux documents ['words', 'wprds']. Le deuxième document contient une faute d'orthographe du mot «mot». Une simple représentation de Bag of Words considère ces deux documents comme des documents très différents et diffère dans les deux caractéristiques possibles. Cependant, la représentation de caractères 2 grammes peut trouver des documents qui correspondent à 4 des 8 caractéristiques, ce qui peut aider le classificateur préféré à prendre de meilleures décisions.

>>> ngram_vectorizer = CountVectorizer(analyzer='char_wb', ngram_range=(2, 2), min_df=1)
>>> counts = ngram_vectorizer.fit_transform(['words', 'wprds'])
>>> ngram_vectorizer.get_feature_names() == (
...     [' w', 'ds', 'or', 'pr', 'rd', 's ', 'wo', 'wp'])
True
>>> counts.toarray().astype(int)
array([[1, 1, 1, 0, 1, 1, 1, 0],
       [1, 1, 0, 1, 1, 1, 0, 1]])

Dans l'exemple ci-dessus, l'analyseur char_wb est utilisé. Il crée n grammes uniquement à partir de caractères dans les limites des mots (avec des espaces intégrés des deux côtés). Au lieu de cela, l'analyseur char crée un n-gramme qui couvre les mots:

>>>
>>> ngram_vectorizer = CountVectorizer(analyzer='char_wb', ngram_range=(5, 5), min_df=1)
>>> ngram_vectorizer.fit_transform(['jumpy fox'])
...                                
<1x4 sparse matrix of type '<... 'numpy.int64'>'
   with 4 stored elements in Compressed Sparse ... format>
>>> ngram_vectorizer.get_feature_names() == (
...     [' fox ', ' jump', 'jumpy', 'umpy '])
True

>>> ngram_vectorizer = CountVectorizer(analyzer='char', ngram_range=(5, 5), min_df=1)
>>> ngram_vectorizer.fit_transform(['jumpy fox'])
...                                
<1x5 sparse matrix of type '<... 'numpy.int64'>'
    with 5 stored elements in Compressed Sparse ... format>
>>> ngram_vectorizer.get_feature_names() == (
...     ['jumpy', 'mpy f', 'py fo', 'umpy ', 'y fox'])
True

La variante sensible aux limites des mots char_wb est particulièrement intéressante dans les langues qui utilisent des espaces pour la séparation des mots. Pour ces langages, la précision prédictive et la vitesse de convergence des classificateurs formés peuvent être augmentées tout en maintenant la robustesse pour les fautes d'orthographe et la dérivation de mots. Vous pouvez enregistrer un certain ordre des mots en extrayant des ngrams au lieu de mots individuels, mais le sac de mots et le sac de n-grammes détruisent la majeure partie de la structure interne d'un document et sa structure interne. Détruisez la plupart des significations véhiculées. Pour relever les défis plus larges de la compréhension du langage naturel, nous devons tenir compte de la structure locale des phrases et des paragraphes. Beaucoup de ces modèles sont convertis en problèmes de "sortie structurée" qui ne sont actuellement pas couverts par scicit-learn.

4.2.3.8. Vectoriser un corpus de texte volumineux en utilisant des astuces de hachage

Le schéma de vectorisation ci-dessus est simple, mais il conserve le mappage chaîne-index (vocabulary_attribute) en mémoire, ce qui pose des problèmes lors du traitement de grands ensembles de données.

«Plus le corpus est grand, plus le vocabulaire est large et plus la mémoire utilisée est importante.

  • L'ajustement nécessite l'attribution d'une structure de données intermédiaire dont la taille est proportionnelle à la taille du jeu de données d'origine.
  • Pour créer un mappage de mots, vous devez charger l'ensemble de données une fois. Par conséquent, il n'est pas possible d'ajuster un classificateur de texte strictement en ligne.
  • La sérialisation / désérialisation des vectoriseurs avec un grand vocabulaire est très lente (généralement beaucoup plus lente que la sérialisation / désérialisation de structures de données plates telles que les tableaux NumPy de même taille).
  • Puisque l'attribut vocabulary_ a une barrière de synchronisation fine, il n'est pas facile de diviser le travail de vectorisation en sous-tâches parallèles. Autrement dit, le mappage de la chaîne de jetons à l'index de fonctionnalité nécessite que les jetons soient partagés en raison de leur premier ordre d'apparition, ce qui peut ralentir les performances de parallélisation par rapport au traitement séquentiel.

sklearn.feature_extraction.FeatureHasher "Astuce de hachage" implémentée par la classe ([fonctionnalité] Hash](http://scikit-learn.org/stable/modules/feature_extraction.html#feature-hashing) et [CountVectorizer](http://scikit-learn.org/stable/modules/generated/sklearn. Vous pouvez surmonter ces limitations en combinant les capacités de prétraitement de texte et de tokenisation de feature_extraction.text.CountVectorizer.html # sklearn.feature_extraction.text.CountVectorizer). Cette combinaison est une transclasse HashingVectorizer qui est principalement compatible avec CountVectorizer. .HashingVectorizer) est implémenté. HashingVectorizer est sans état. Autrement dit, vous n'avez pas à l'appeler.

>>> from sklearn.feature_extraction.text import HashingVectorizer
>>> hv = HashingVectorizer(n_features=10)
>>> hv.transform(corpus)
...                                
<4x10 sparse matrix of type '<... 'numpy.float64'>'
    with 16 stored elements in Compressed Sparse ... format>

Dans la sortie vectorielle, vous pouvez voir que 16 jetons de fonctionnalité non nuls ont été extraits. C'est moins que les 19 non-zéros précédemment extraits par le Count Vectorizer du même corpus de jouets. Cette différence est due à des conflits de fonction de hachage dus à la faible valeur du paramètre n_features. Dans les paramètres du monde réel, le paramètre n_features peut être laissé à sa valeur par défaut de 2 ** 20 (environ 1 million de fonctionnalités possibles). Si la mémoire ou la taille du modèle en aval compte, choisir une valeur inférieure, telle que «2 ^ 18», vous aidera sans introduire trop de conflits supplémentaires dans une tâche de classification de texte typique. .. Le nombre de dimensions n'affecte pas le temps d'apprentissage du processeur des algorithmes qui fonctionnent avec la matrice CSR (LinearSVC (dual = True), Perceptron, SGDClassifier, PassiveAggressive), mais les algorithmes qui fonctionnent avec la matrice CSC ( Notez que cela affecte LinearSVC (dual = False), Lasso (), etc.). Réessayez avec les paramètres par défaut:

>>> hv = HashingVectorizer()
>>> hv.transform(corpus)
...                               
<4x1048576 sparse matrix of type '<... 'numpy.float64'>'
    with 19 stored elements in Compressed Sparse ... format>
>>>

On n'obtient plus de collisions, mais cela se fait au détriment de la plus grande dimensionnalité de l'espace de sortie. Bien entendu, les termes autres que 19 utilisés ici peuvent encore entrer en conflit les uns avec les autres. HashingVectorizer a également les restrictions suivantes:

  • En raison de la nature unidirectionnelle de la fonction de hachage qui effectue le mappage, vous ne pouvez pas inverser le modèle (sans la méthode ʻinverse_transform`) ou accéder à la représentation sous forme de chaîne d'origine de la quantité de caractéristiques.
  • Aucune pondération IDF n'est effectuée pour empêcher le modèle d'avoir un état. TfidfTransformer doit être ajouté dans le pipeline si nécessaire. Je peux.

4.2.3.9. Effectuer une mise à l'échelle hors cœur avec HashingVectorizer

Un développement intéressant utilisant HashingVectorizer est Out-of-Core. La capacité d'effectuer une mise à l'échelle. En d'autres termes, vous pouvez tirer des leçons de données qui ne rentrent pas dans la mémoire principale de votre ordinateur. La stratégie de mise en œuvre de la mise à l'échelle Out-of-Core consiste à diffuser les données dans l'estimateur en mini-lot. Chaque mini-lot est vectorisé à l'aide du HashingVectorizer pour garantir que l'espace d'entrée de l'estimateur a toujours les mêmes dimensions. Par conséquent, la quantité de mémoire utilisée à un moment donné est limitée par la taille du mini-lot. Il n'y a pas de limite à la quantité de données qui peuvent être capturées en utilisant une telle approche, mais d'un point de vue pratique, le temps de formation est souvent limité par le temps CPU consacré à la tâche. Pour un exemple complet de mise à l'échelle Out-of-Core dans une tâche de classification de texte, voir Classification Out-of-Core des documents texte (http://scikit-learn.org/stable/auto_examples/applications/plot_out_of_core_classification.html# Voir sphx-glr-auto-examples-applications-plot-out-of-core-classification-py).

4.2.3.10. Personnalisation de la classe de vectorisation

Vous pouvez personnaliser le comportement en passant l'appelable au constructeur de vectoriseur.

>>> def my_tokenizer(s):
...     return s.split()
...
>>> vectorizer = CountVectorizer(tokenizer=my_tokenizer)
>>> vectorizer.build_analyzer()(u"Some... punctuation!") == (
...     ['some...', 'punctuation!'])
True

En particulier,

--preprocessor: un objet appelable qui peut appeler le document entier en entrée (sous forme de chaîne unique) et renvoyer le document converti. Il peut être utilisé pour supprimer les balises HTML ou pour abaisser tout le document. -- tokenizer: un objet appelable qui divise la sortie du préprocesseur en jetons et en renvoie une liste. --ʻAnalyzer`: un objet appelable qui remplace le préprocesseur et le tokenizer. Tous les analyseurs par défaut appellent le préprocesseur et le tokenizer, mais les analyseurs personnalisés ignorent cela. L'extraction de N-gramme et le filtrage des mots vides sont effectués au niveau de l'analyseur, les analyseurs personnalisés doivent donc reproduire ces étapes.

(Les utilisateurs de Lucene peuvent reconnaître ces noms, mais sachez que le concept scicit-learn peut ne pas avoir de correspondance biunivoque avec le concept Lucene). Au lieu de passer des fonctions personnalisées, vous pouvez dériver de la classe et remplacer les méthodes d'usine build_preprocessor, build_tokenizer et build_analyzer afin que le préprocesseur, le tokenizer et l'analyseur puissent reconnaître les paramètres du modèle.

Quelques trucs et astuces:

  • Si le document est pré-tokenisé par un package externe, stockez les tokens séparés par des blancs dans un fichier (ou une chaîne) et passez ʻanalyzer = str.split`
  • Une excellente analyse au niveau du jeton, telle que la dérivation, la division typographique, la division composée, le filtrage participatif, etc. n'est pas incluse dans la base de code d'apprentissage scicit, mais peut être ajoutée en personnalisant le tokenizer ou l'analyseur. Voici un CountVectorizer avec un tokenizer et un rematizer utilisant NLTK:
>>> from nltk import word_tokenize          
>>> from nltk.stem import WordNetLemmatizer 
>>> class LemmaTokenizer(object):
...     def __init__(self):
...         self.wnl = WordNetLemmatizer()
...     def __call__(self, doc):
...         return [self.wnl.lemmatize(t) for t in word_tokenize(doc)]
...
>>> vect = CountVectorizer(tokenizer=LemmaTokenizer()) 

(Notez que cela n'exclut pas la ponctuation).

La personnalisation du vectoriseur est également utile lorsqu'il s'agit de langues asiatiques qui n'utilisent pas de séparateurs de mots explicites tels que les espaces.

4.2.4. Extraction de caractéristiques d'image

4.2.4.1. Extraction de patch

extract_patches_2 La fonction est une image stockée sous forme de tableau bidimensionnel, Ou extrayez un patch 3D avec des informations de couleur le long du 3ème axe. Pour reconstruire l'image à partir de tous les correctifs, reconstruct_from_patches_2d Utilisez le. Par exemple, il est utilisé pour générer une image de 4 x 4 pixels avec 3 canaux de couleur (par exemple au format RVB).

>>> import numpy as np
>>> from sklearn.feature_extraction import image

>>> one_image = np.arange(4 * 4 * 3).reshape((4, 4, 3))
>>> one_image[:, :, 0]  # R channel of a fake RGB picture
array([[ 0,  3,  6,  9],
       [12, 15, 18, 21],
       [24, 27, 30, 33],
       [36, 39, 42, 45]])

>>> patches = image.extract_patches_2d(one_image, (2, 2), max_patches=2,
...     random_state=0)
>>> patches.shape
(2, 2, 2, 3)
>>> patches[:, :, :, 0]
array([[[ 0,  3],
        [12, 15]],

       [[15, 18],
        [27, 30]]])
>>> patches = image.extract_patches_2d(one_image, (2, 2))
>>> patches.shape
(9, 2, 2, 3)
>>> patches[4, :, :, 0]
array([[15, 18],
       [27, 30]])

Essayons de reconstruire l'image originale à partir du patch en faisant la moyenne des zones qui se chevauchent.

>>> reconstructed = image.reconstruct_from_patches_2d(patches, (4, 4, 3))
>>> np.testing.assert_array_equal(one_image, reconstructed)

PatchExtractor La classe fonctionne de la même manière que extract_patches_2d, mais Prend en charge plusieurs images en entrée. Mis en œuvre en tant qu'estimateur, il peut être utilisé dans les pipelines.

>>> five_images = np.arange(5 * 4 * 4 * 3).reshape(5, 4, 4, 3)
>>> patches = image.PatchExtractor((2, 2)).transform(five_images)
>>> patches.shape
(45, 2, 2, 3)

4.2.4.2. Graphique de connectivité des images

Certaines estimations de scikit-learn peuvent utiliser des fonctionnalités ou des informations de connexion entre les échantillons. Par exemple, le clustering de quartiers (Hierarchical Clustering (http://scikit-learn.org/stable/modules/clustering.html#hierarchical-clustering)) ne peut regrouper que les pixels adjacents dans une image, donc , Un patch continu peut être formé.

../_images/sphx_glr_plot_face_ward_segmentation_0011.png

À cette fin, l'estimateur utilise une matrice de «connectivité» qui indique quels échantillons sont connectés. La fonction img_to_graph est une telle matrice à partir d'une image 2D ou 3D. Retour. De même, grid_to_graph est donné par la forme de ces images. Construisez une matrice concaténée pour les images résultantes. Ces matrices peuvent être utilisées pour imposer la connectivité aux estimateurs qui utilisent des informations de connectivité telles que le regroupement de quartiers (regroupement hiérarchique). Il peut également être utilisé pour construire un noyau pré-calculé ou une matrice de similarité.

--Exemple

  • [Démo de regroupement de mots structurés sur l'image de visage Laclone](http://scikit-learn.org/stable/auto_examples/cluster/plot_face_ward_segmentation.html#sphx-glr-auto-examples-cluster-plot-face-ward] -segmentation-py)
  • Clustering du spectre pour la segmentation d'image
  • [Feature Agglomeration vs. One Variant Selection](http://scikit-learn.org/stable/auto_examples/cluster/plot_feature_agglomeration_vs_univariate_selection.html#sphx-glr-auto-examples-cluster-plot-feature-agglomeration-vs- sélection-univariée-py)

[scikit-learn 0.18 User Guide 4. Dataset Conversion](http://qiita.com/nazoking@github/items/267f2371757516f8c168#4-%E3%83%87%E3%83%BC%E3%82%BF À partir de% E3% 82% BB% E3% 83% 83% E3% 83% 88% E5% A4% 89% E6% 8F% 9B)

© 2010 --2016, développeurs scikit-learn (licence BSD).

Recommended Posts