[PYTHON] 100 coups de traitement du langage 2020: Chapitre 4 (analyse morphologique)

Language processing 100 knock 2020 version a été publié, j'ai donc profité de l'occasion pour le résoudre. Puisqu'il s'agit d'une sortie markdown de Jupyter publiée sur GitHub, le package une fois chargé n'est pas chargé après cela. J'espère que vous pourrez l'utiliser comme l'une des références de code.

Étant donné que je suis un débutant dans le traitement du langage naturel, j'aimerais continuer tout en étudiant et enregistrer les matériaux et contenus auxquels j'ai fait référence à ce moment-là.

Le premier chapitre est ici Le troisième chapitre est ici

Chapitre 4: Analyse morphologique

Le texte du roman de Soseki Natsume "Je suis un chat" (neko.txt) a été analysé morphologiquement à l'aide de MeCab, et les résultats ont été analysés. Enregistrez-le dans un fichier appelé neko.txt.mecab. Utilisez ce fichier pour implémenter un programme qui répond aux questions suivantes. Pour les problèmes 37, 38 et 39, utilisez matplotlib ou Gnuplot.

Qu'est-ce que l'analyse morphologique? WikipediaQue,

L'analyse morphologique va de données textuelles (phrases) en langage naturel sans notes d'informations grammaticales à des informations telles que la grammaire de la langue cible et les paroles partielles de mots appelés dictionnaires. À l'origine, il s'agit de diviser en colonnes d'éléments morphologiques (Morphème, en gros, la plus petite unité qui a un sens dans la langue), et de déterminer la partie de chaque élément morphologique.

Principe MeCab

Découvrez les coulisses de l'analyse morphologique japonaise! Comment MeCab analyse morphologiquement.

Comme une simple image Donnez tous les candidats de division possibles pour une phrase. Calculez le coût d'occurrence de chaque nœud (élément) et le coût de connexion des mots de pièce de l'arête. Il semble que le chemin qui minimise le coût cumulé soit calculé par la méthode de planification dynamique. Les mots inconnus sont ajoutés en tant que mots pour chaque type de caractère (comme katakana), et le coût semble être déterminé pour chaque type de caractère.

D'ailleurs, ce coût est déterminé à l'avance lors de l'installation de MeCab, mais il est modélisé par CRF (champ de probabilité conditionnelle) et calculé à l'avance. Il y a une explication détaillée dans l'article ci-dessus, mais j'ai résumé les articles recommandés ci-dessous pour comprendre un peu plus les bases.

À propos de CRF

L'une des caractéristiques est qu'il s'agit d'un «modèle d'identification» et qu'un «apprentissage structurel» est effectué. C'est un vieil article, mais il est basé sur l'histoire du CRF en 30 minutes pour les personnes qui ne comprennent pas le CRF et tombent malades Cela semble bon pour comprendre le contour qui est à la base. Pour comprendre un peu plus en détail Cet article était bon. Aussi, pour plus de contenu mathématique, cette diapositive C'était très facile à comprendre.

Le champ aléatoire conditionnel (abréviation: CRF) est l'un des modèles graphiques probabilistes représentés par des graphes non orientés et est un modèle discriminant. (Wikipédia)

CRF est un champ stochastique de Markov formé de manière caractéristique (un ensemble de variables probabilistes de Markov). (Le champ de probabilité de Markov est un graphique non orienté.)

P({\bf S}|{\bf O})=\frac{1}{Z_o} \exp \left(\sum_{t=1}^{T} \sum_{k=1}^K \lambda_k f_k(s_{t-1},s_t,{\bf o},t)\right)
Z_o=\sum_{s \in {\bf S}} \exp \left(\sum_{t=1}^{T} \sum_{k=1}^K \lambda_k f_k(s_{t-1},s_t,{\bf o},t)\right)

Ici, fk est une fonction de prédisposition et consiste en une prédisposition de transition et une prédisposition d'observation. λ est le poids et le coût est calculé en fonction de cette valeur. Puisque CRF est construit sur le champ stochastique de Markov, la prédisposition transitionnelle se compose uniquement de l'état précédent et de l'état actuel. Le coût d'occurrence de MeCab est calculé à l'aide de la fonction de prédisposition appartenant à la prédisposition observée, et le coût de concaténation est calculé à l'aide de la fonction de prédisposition appartenant à la réanimation de transition. De plus, dans MeCab, puisque l'élément de transition ne traite que de l'état de la partie du discours, il semble que le coût de concaténation ne considère que les informations de la partie du discours.

Prétraitement

Avant de commencer à résoudre le problème, importez matplotlib et effectuez une analyse morphologique avec MeCab. MeCab utilise celui installé par Homebrew.

mecab < ../data/neko.txt > ../data/neko.txt.mecab
import matplotlib.pyplot as plt

30. Lecture des résultats de l'analyse morphologique

Implémentez un programme qui lit les résultats de l'analyse morphologique (neko.txt.mecab). Cependant, chaque élément morphologique est stocké dans un type de mappage avec la clé de la forme de surface (surface), de la forme de base (base), d'une partie du mot (pos) et d'une partie du mot sous-classification 1 (pos1), et une phrase est exprimée sous forme d'une liste d'éléments morphologiques (type de mappage). Faisons le. Pour le reste des problèmes du chapitre 4, utilisez le programme créé ici.

Le type de mappage dans lequel chaque élément de formulaire est stocké est tel que {'surface': 'de', 'base': 'da', 'pos': 'auxiliaire verb', 'pos1': '*'}.

fname = '../data/neko.txt.mecab'

def read_mecab(fname):
    with open(fname, mode='rt', encoding='utf-8') as f:
        #Suppression d'EOS Lors de la suppression, liste le stockage phrase par phrase
        sentence_list = f.read().split('EOS\n')
        sentence_list = list(filter(lambda x: x != '', sentence_list))
    result_list = []
    for sentence in sentence_list:
        morpheme_list = []
        for word in sentence.split('\n'):
            if word != '':
                (surface, attr) = word.split('\t')
                attr = attr.split(',')
                component_dict = {
                    'surface': surface,
                    'base': attr[6],
                    'pos': attr[0],
                    'pos1': attr[1]
                }
                morpheme_list.append(component_dict)
        result_list.append(morpheme_list)
    return result_list

neko_morpheme = read_mecab(fname)
print(neko_morpheme[:4])

[[{'surface': 'one', 'base': 'one', 'pos': 'noun', 'pos1': 'number'}], [{'surface': '\ u3000', 'base ':' \ u3000 ',' pos ':' symbol ',' pos1 ':' blank '}, {' surface ':' my ',' base ':' my ',' pos ':' noun ',' pos1 ':' Synonymes '}, {' surface ':' est ',' base ':' est ',' pos ':' auxiliaire ',' pos1 ':' participant '}, {' surface ':' cat ',' base ':' cat ',' pos ':' noun ',' pos1 ':' general '}, {' surface ':' at ',' base ':' da ',' pos ':' verbe auxiliaire ',' pos1 ':' * '}, {' surface ':' Are ',' base ':' Are ',' pos ':' Verbe auxiliaire ',' pos1 ':' * '}, {' surface ': ». ',' base ':'. ',' pos ':' symbol ',' pos1 ':' ponctuation '}], [{' surface ':' name ',' base ':' name ',' pos ':' nomenclature ',' pos1 ': 'Général'}, {'surface': 'est', 'base': 'est', 'pos': 'auxiliaire', 'pos1': 'participant'}, {'surface': 'encore', ' base ':' encore ',' pos ':' adjunct ',' pos1 ':' connexion auxiliaire '}, {' surface ':' not ',' base ':' not ',' pos ':' adjectif ' , 'pos1': 'Indépendance'}, {'surface': '. ',' base ':'. ',' pos ':' symbol ',' pos1 ':' ponctuation '}], [{' surface ':' \ u3000 ',' base ':' \ u3000 ',' pos ':' symbol ',' pos1 ':' Blank '}, {' surface ':' Where ',' base ':' Where ',' pos ':' Nomenclature ',' pos1 ':' Synonymes '}, {' surface ':' de ',', 'base': 'de', 'pos': 'auxiliaire', 'pos1': 'case assistant'}, {'surface': 'born', 'base': 'born', 'pos': 'verb ',' pos1 ':' Indépendance '}, {' surface ':' ta ',' base ':' ta ',' pos ':' verbe auxiliaire ',' pos1 ':' * '}, {' surface ': 'Ka', 'base': 'Ka', 'pos': 'Auxiliary', 'pos1': 'Sub-addition / Parallel assistant / Final auxiliaire'}, {'surface': 'Tonto', 'base': ' Tonto ',' pos ':' adjunct ',' pos1 ':' general '}, {' surface ':' register ',' base ':' register ',' pos ':' noun ',' pos1 ':' Connexion Sahen '}, {' surface ':' is ',' base ':' is ',' pos ':' auxiliaire ',' pos1 ':' case auxiliaire '}, {' surface ':' tsuka ',' base ':' tsuku ',' pos ':' verbe ',' pos1 ':' indépendance '}, {' surface ':' nu ',' base ':' nu ',' pos ':' verbe auxiliaire ',' pos1 ':' * '}, {' surface ':'. ',' base ':'. ',' pos ':' symbole ',' pos1 ':' ponctuation '}]]

31. verbe

Extraire tous les systèmes de surface de verbes.

pos extrait la surface du verbe. Pour le moment, tout ce que j'avais à faire était d'extraire les verbes, donc j'ai une fois aplati la liste pour chaque phrase et l'ai traitée. La sortie est une liste, mais si vous n'avez pas besoin d'éléments dupliqués, vous pouvez la définir sur set. Faites simplement set (verbose_list).

from itertools import chain

def extract_verbose(target):
    flatten_list = list(chain.from_iterable(target))
    return [word['surface'] for word in flatten_list if word['pos'] == 'verbe']
    
verbose_list = extract_verbose(neko_morpheme)
print(verbose_list[:10])

["Naissance", "Tsuka", "Shi", "Pleurer", "Shi", "Je", "Début", "Voir", "Écouter", "Capturer"]

32. Prototype du verbe

Extraire toutes les formes originales du verbe

def extract_verb_base(target):
    flatten_list = list(chain.from_iterable(target))
    return [word['base'] for word in flatten_list if word['pos'] == 'verbe']

verb_base_list = extract_verb_base(neko_morpheme)
print(verb_base_list[:10])

['Naissance', 'Tsuku', 'Do', 'Cry', 'Do', 'I', 'Start', 'See', 'Listen', 'Catch']

33. «B de A»

Extraire la nomenclature dans laquelle deux nomenclatures sont reliées par "non".

Vous devez traiter chaque phrase. Ajoutez la nomenclature concaténée à la liste sous la forme d'un dictionnaire de la forme {A: B}.

def extract_connected_by_of(target):
    result_list = []
    for dict in target:
        for i in range(1, len(dict)-1):
            if dict[i-1]['pos'] == 'nom' and dict[i]['base'] == 'de' and dict[i+1]['pos'] == 'nom':
                result_list.append({dict[i-1]['surface']: dict[i+1]['surface']})
    return result_list

a_of_b_list = extract_connected_by_of(neko_morpheme)
print(a_of_b_list[:10])

[{'He': 'Palm'}, {'Palm': 'Upper'}, {'Shosei': 'Face'}, {'Should': 'Face'}, {'Face': 'Middle'} , {'Hole': 'Middle'}, {'Shosei': 'Palm'}, {'Palm': 'Back'}, {'What': 'Things'}, {'Important': 'Mother'} ]

34. Concaténation de nomenclature

Extraire la concaténation de la nomenclature (noms qui apparaissent consécutivement) avec la correspondance la plus longue.

Tant que la nomenclature est connectée, toute la nomenclature doit être ajoutée au résultat.

def extract_chain_noun(target_list):
    result_list = []
    temp_list = []
    for d in target_list:
        for word in d:  
            if word['pos'] == 'nom':
                temp_list.append(word['surface'])
            elif len(temp_list) > 1 :
                result_list.append(temp_list)
                temp_list = []
            else:
                temp_list = []
    return result_list

chain_noun_list = extract_chain_noun(neko_morpheme)

for i in chain_noun_list[:10]:
    print(i)

["Humain", "Moyen"] ['Ichiban', 'Mal'] ['Time', 'Mysterious'] ['One', 'Hair'] ['Après', 'Chat'] ['une fois'] ['Puuputo', 'Fumée'] ['Maison', 'Intérieur'] ['Trois', 'Cheveux'] ['Shosei', autre que ']

35. Fréquence d'occurrence des mots

Trouvez les mots qui apparaissent dans la phrase et leur fréquence d'apparition et classez-les par ordre décroissant de fréquence d'apparition.

Utilisez le compteur des collections de la bibliothèque standard Python` pour obtenir une liste de mots et d'occurrences de tapples.

#from itertools import chain
from collections import Counter

def extract_word_frequency(target):
    flatten_list = list(chain.from_iterable(target))
    word_list = [word['surface'] for word in flatten_list]
    counted_list = Counter(word_list).most_common()
    return counted_list

word_counted_list = extract_word_frequency(neko_morpheme)
word, counts = zip(*word_counted_list)
print(word[:20])

('', '.', 'Te', ',', 'est', 'à', 'à', 'et', 'est', 'wa', 'à', ' "',' Aussi ',' pas ',' da ',' shi ',' from ',' are ',' na ')

36. Top 10 des mots les plus fréquents

Affichez les 10 mots les plus fréquents et leur fréquence d'apparition dans un graphique (par exemple, un graphique à barres).

# import matplotlib.pyplot as plt
import japanize_matplotlib

plt.figure(figsize=(12, 8))
plt.barh(word[:10], counts[:10])
plt.title('Top 10 des mots par fréquence')
plt.xlabel('Nombre d'apparitions')
plt.ylabel('mot')
plt.show()

output_15_0.png

37. Top 10 des mots qui coïncident fréquemment avec "chat"

Afficher 10 mots qui cohabitent souvent avec "chat" (fréquence élevée de cooccurrence) et leur fréquence d'apparition dans un graphique (par exemple, un graphique à barres).

Tout d'abord, nous devons réfléchir à la manière de définir la fréquence de cooccurrence, mais ici nous l'avons interprétée comme l'extraction de mots qui apparaissent dans la même phrase.

Cela se fait en 3 étapes.

def extract_cooccurrence_list(target):
    cooccurrence_list = []
    for sentence in target:
        #Si la surface contient des chats, ajoutez-les à la liste.
        word_list = [word['surface'] for word in sentence]
        if "Chat" in word_list:
            cooccurrence_list.extend([word for word in word_list if word != "Chat"])
    return cooccurrence_list

def caluculate_word_frequence(target):
    counted_list = Counter(target).most_common()
    word, counts = zip(*counted_list)
    return word, counts


def main():
    cat_frequence_list = extract_cooccurrence_list(neko_morpheme)
    neko_word, neko_count = caluculate_word_frequence(cat_frequence_list)
    plt.figure(figsize=(12, 8))
    plt.barh(neko_word[:10], neko_count[:10])
    plt.title('Top 10 des mots qui cohabitent fréquemment avec les chats')
    plt.xlabel('Nombre d'apparitions')
    plt.ylabel('mot')
    plt.show()

if __name__ == '__main__':
    main()

output_17_0.png

38. histogramme

Tracez un histogramme de la fréquence d'occurrence des mots (l'axe horizontal représente la fréquence d'occurrence et l'axe vertical représente le nombre de types de mots qui prennent la fréquence d'occurrence sous forme de graphique à barres).

Utilisez la liste des nombres calculés en 35. Si vous vous y référez plus tard comme ceci, vous devriez lui donner un nom légèrement meilleur. Cette fois, nous avons tracé jusqu'à 100 fréquences d'apparition.

plt.figure(figsize = (12,8))
plt.hist(counts, bins=20, range=(1,100))
plt.show()

output_19_0.png

39. Loi de Zipf

Tracez les deux graphiques logarithmiques avec la fréquence d'occurrence des mots sur l'axe horizontal et la fréquence d'occurrence sur l'axe vertical.

La liste des dénombrements acquise en 35 est classée par ordre de rang.

#Créer une liste de classement d'apparence
rank_list = [i+1 for i in range(len(counts))]

plt.figure(figsize = (12,8))
plt.scatter(rank_list, counts)
plt.xscale('log')
plt.yscale('log')
plt.grid(which='major',color='black')

plt.show()

output_21_0.png

Recommended Posts

100 coups de traitement du langage 2020: Chapitre 4 (analyse morphologique)
[Traitement du langage 100 coups 2020] Chapitre 4: Analyse morphologique
Traitement du langage 100 coups Chapitre 4: Analyse morphologique 31. Verbes
100 traitements du langage frappent l'analyse morphologique apprise au chapitre 4
100 Language Processing Knock 2020 Chapitre 4: Analyse morphologique
[Traitement du langage 100 coups 2020] Chapitre 5: Analyse des dépendances
100 Traitement du langage Knock Chapitre 4: Analyse morphologique
100 Language Processing Knock 2015 Chapitre 4 Analyse morphologique (30-39)
100 traitements du langage naturel frappent Chapitre 4 Analyse morphologique (seconde moitié)
100 coups de traitement du langage ~ Chapitre 1
Le traitement de 100 langues frappe le chapitre 2 (10 ~ 19)
Traitement du langage naturel 1 Analyse morphologique
100 traitements linguistiques frappent 03 ~ 05
100 coups de traitement linguistique (2020): 32
100 Language Processing Knock 2015 Chapitre 5 Analyse des dépendances (40-49)
100 coups de traitement linguistique (2020): 35
[Traitement du langage 100 coups 2020] Chapitre 3: Expressions régulières
100 coups de traitement linguistique (2020): 47
100 coups de traitement linguistique (2020): 39
100 traitements du langage naturel frappent le chapitre 4 Commentaire
[Traitement du langage 100 coups 2020] Chapitre 6: Machine learning
100 coups de traitement linguistique (2020): 22
100 coups de traitement linguistique (2020): 26
100 coups de traitement linguistique (2020): 34
100 coups de traitement linguistique (2020): 42
100 coups de traitement linguistique (2020): 29
100 coups de traitement linguistique (2020): 49
Le traitement de 100 langues frappe 06 ~ 09
100 coups de traitement linguistique (2020): 43
100 coups de traitement linguistique (2020): 24
[Traitement du langage 100 coups 2020] Chapitre 1: Mouvement préparatoire
100 coups de traitement linguistique (2020): 45
100 coups de traitement linguistique (2020): 10-19
[Traitement du langage 100 coups 2020] Chapitre 7: Vecteur Word
100 coups de traitement linguistique (2020): 30
100 Language Processing Knock 2020 Chapitre 5: Analyse des dépendances
100 coups de traitement linguistique (2020): 00-09
100 Language Processing Knock 2020: Chapitre 3 (expression régulière)
100 coups de traitement linguistique (2020): 31
[Traitement du langage 100 coups 2020] Chapitre 8: Réseau neuronal
100 coups de traitement linguistique (2020): 48
[Traitement du langage 100 coups 2020] Chapitre 2: Commandes UNIX
100 coups de traitement linguistique (2020): 44
100 coups de traitement linguistique (2020): 41
100 coups de traitement linguistique (2020): 37
[Traitement du langage 100 coups 2020] Chapitre 9: RNN, CNN
100 coups de traitement linguistique (2020): 25
100 coups de traitement linguistique (2020): 23
100 coups de traitement linguistique (2020): 33
100 coups de traitement linguistique (2020): 20
100 coups de traitement linguistique (2020): 27
Le traitement du langage naturel à 100 coups
100 coups de traitement linguistique (2020): 46
100 coups de traitement linguistique (2020): 21
100 points de traitement du langage naturel Chapitre 5 Analyse des dépendances (premier semestre)
100 coups de traitement linguistique (2020): 36
100 coups de traitement du langage amateur: 41
100 coups de traitement du langage amateur: 71
100 coups de traitement du langage amateur: 56
100 coups de traitement du langage amateur: 24
100 coups de traitement du langage amateur: 50