[PYTHON] fastText est incroyable! Clustering "Yahoo! News"

La dernière fois cet article j'ai essayé de regrouper les livres d'Aozora Bunko avec Doc2Vec. Je me suis demandé si cela fonctionnait un peu, mais honnêtement, le résultat était subtil. Donc, cette fois, au lieu de Doc2Vec, j'utiliserai une bibliothèque appelée fastText pour regrouper les articles de presse Yahoo.

Qu'est-ce que fastText

fastText est une bibliothèque de traitement de langage naturel open source développée par Facebook. Il est hautement fonctionnel, a une bonne précision de prédiction et rend les prédictions encore plus rapides. Les principales fonctions sont la classification par apprentissage supervisé et la génération vectorielle de mots par apprentissage non supervisé.

Cette fois, je vais essayer de prédire la catégorie d'articles en utilisant la fonction de classification par apprentissage supervisé.

Pour plus de détails, rendez-vous sur Référence officielle de fastText! GitHub a été détaillé sur la fonction sur Python!

Environnement de développement

--Docker → ici

Début de la mise en œuvre

import pandas as pd, numpy as np
import re
import MeCab
import fasttext
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn import preprocessing
from sklearn.decomposition import PCA
import japanize_matplotlib

Obtenez des données d'actualité

Chargez les données obtenues à l'aide du code introduit dans Scraping Yahoo News. Il y a beaucoup de nouvelles internationales parce que c'est juste après l'élection présidentielle américaine.

df = pd.read_csv('./YahooNews.csv')
df
title category text
0 Perte? Azato mignon Riho Yoshioka Divertissement Deuxième collection de photos de l'actrice Yoshioka Riho 27 pour la première fois en deux ans Collection Riho par Asami Kiyokawa Shueisha ...
1 Destination touristique hydratante "lieu sacré" du diable Économie La version cinématographique de la lame du diable, qui a enregistré un blockbuster inhabituel malgré l'épave corona, ne se limite pas au film édition train infini ...
2 Dentsu G Corona divise par deux son résultat opérationnel Économie Le Groupe Dentsu a annoncé le 10 que les résultats financiers consolidés de l'exercice clos en décembre 2020 auront 9 revenus équivalents au chiffre d'affaires ...
3 Démocrates de Hong Kong, tous démissionnaires International 12 Selon Beijing Joint Xinhua Electric Co., Ltd., la réunion du Comité permanent chinois pour toutes les personnes sera qualifiée de membre de l'Assemblée législative de Hong Kong 70 le 11 ...
4 Organisation professionnelle Augmentation de l'infection dans tout le pays Domestique La réunion du comité consultatif, une organisation experte du ministère de la Santé, du Travail et du Bien-être, qui conseille sur les mesures contre le nouveau virus corona, se tiendra le 11 ...
... ... ... ...
512 Quatre personnes poignardées autour de la Maison Blanche International Selon US NBC TV et d'autres, les partisans du président Trump et de l'ancien vice-président Byden se sont rassemblés lors de l'élection présidentielle américaine ...
513 Déclaration de victoire unilatérale La réaction des États-Unis est International FNN Prime Online Washington avant 10h00 le 4, une nuit après le jour du vote de l'élection présidentielle américaine ...
514 La croissance des actions de New York se poursuit, temporairement supérieure à 600 $ International Le matin du 4, alors que la bataille serrée entre les deux candidats Trump Byden se poursuit à l'élection présidentielle américaine, où les affaires courantes de New York sont ouvertes ...
515 Retrait de Sega Sammy Gaesen Économie Actions de Sega Sammy Holdings dans Sega Entertainment Tokyo, une filiale consolidée qui exploite des installations de divertissement le 4 ...
516 La Chine utilisera des armes pour la sécurité maritime International L'Assemblée nationale mixte du Congrès national populaire de Chine de Pékin, le 4, stipule l'autorité du Bureau de la police maritime chinoise, qui est en charge de la sécurité maritime ...

517 rows × 3 columns

Définition du dictionnaire et de la fonction

#Spécifiez NEologd dans le dictionnaire MeCab.
#mecab est pour l'analyse des éléments mobiles, wakati est pour le partage
mecab = MeCab.Tagger('-d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd/')
wakati = MeCab.Tagger("-Owakati -d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd/")


#Définir une fonction pour effectuer une analyse morphologique
#Lorsque vous entrez un fichier, il génère le fichier et lorsque vous transmettez une chaîne de caractères, il renvoie une chaîne de caractères. Changez avec le fichier d'arguments.
#Si vous voulez juste séparer, mecab comme argument=Ceci peut être réalisé avec wakati.
def MecabMorphologicalAnalysis(path='./text.txt', output_file='wakati.txt', mecab=mecab, file=False):
    mecab_text = ''
    if file:
        with open(path) as f:
            for line in f:
                mecab_text += mecab.parse(line)
        with open(output_file, 'w') as f:
            print(mecab_text, file=f)
    else:
        for path in path.split('\n'):
            mecab_text += mecab.parse(path)
        return mecab_text


#Produit la similitude cosinus entre v1 et v2.
def cos_sim(v1, v2):
    return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))

Prétraitement des données

Il se transforme en une forme à utiliser avec fastText. fastText peut facilement effectuer un apprentissage supervisé en formatant les données sous la forme suivante. Pour plus de détails, reportez-vous au Tutoriel officiel.

__label__sauce __label__cheese how much does potato starch affect a cheese sauce recipe ? 
__label__food-safety __label__acidity dangerous pathogens capable of growing in acidic environments
__label__cast-iron __label__stove how do i cover up the white spots on my cast iron stove ? 
__label__restaurant michelin three star restaurant; but if the chef is not there

Effectuez le traitement suivant pour lui donner la forme ci-dessus. ① Insérez __label__ avant la catégorie de nouvelles → Store dans la liste (2) Ecrire le texte séparément en utilisant la fonction d'analyse morphologique Mecab définie ci-dessus → Stocker dans la liste ③ Divisez en données de train et données valides en utilisant train_test_split ④ Combinez les catégories et le texte avec train et validez et enregistrez dans un fichier

# ①
cat_lst = ['__label__' + cat for cat in df.category]
print("cat_lst[:5]:", cat_lst[:5]) #Vérifiez le contenu
print("len(cat_lst):", len(cat_lst)) #Vérifiez le nombre d'étiquettes

cat_lst [: 5]: ['__ label__ divertissement', '__ label__ économie', '__ label__ économie', '__ label__ international', '__ label__ national'] len(cat_lst): 517

# ②
text_lst = [MecabMorphologicalAnalysis(text, mecab=wakati) for text in df.text]
print("text_lst[0][:50]:", text_lst[0][:50]) #Vérifiez la première ligne
print("text_lst[1][:50]:", text_lst[1][:50]) #Vérifiez la deuxième ligne
print("len(text_lst):", len(text_lst)) #Vérifiez le nombre d'articles

text_lst [0] [: 50]: Deuxième collection de photos de l'actrice Yoshioka Riho 27 pour la première fois en deux ans Collection Riho par Asami Kiyok text_lst [1] [: 50]: La version cinématographique de Devil's Blade Infinite, qui a enregistré un blockbuster inhabituel malgré le désastre de Corona. len(text_lst): 517

# ③
text_train, text_valid, cat_train, cat_valid = train_test_split(
    text_lst, cat_lst, test_size=0.2, random_state=0, stratify=cat_lst
)


# ④
with open('./news.train', mode='w') as f:
    for i in range(len(text_train)):
        f.write(cat_train[i] + ' '+ text_train[i])
        
with open('./news.valid', mode='w') as f:
    for i in range(len(text_valid)):
        f.write(cat_valid[i] + ' ' + text_valid[i])

Apprentissage et évaluation de modèles

fastText est train_supervised et vous pouvez facilement faire un apprentissage supervisé. Vous pouvez effectuer un traitement de n-gramme en passant un argument à wordNgrams, ou mettre hs en perte et utiliser hiérarchique softmax pour effectuer un traitement à grande vitesse. Quoi qu'il en soit, c'est très fonctionnel! !!

L'apprentissage est excellent, mais je pense que la fonctionnalité de fastText est que vous pouvez évaluer la précision immédiatement en utilisant model.test. Comme indiqué ci-dessous, vous pouvez voir que la précision est assez bonne même pour des données valides.

model = fasttext.train_supervised(input='./news.train', lr=0.5, epoch=500,
                                  wordNgrams=3, loss='ova', dim=300, bucket=200000)

print("TrainData:", model.test('news.train'))
print("Valid", model.test('news.valid'))
TrainData: (413, 1.0, 1.0)
Valid (104, 0.75, 0.75)

Confirmation de l'exactitude à l'aide de données valides

Vérifions l'exactitude du modèle en utilisant des données valides qui ne sont pas utilisées pour l'entraînement. ① Stockez le contenu des données valides dans l_strip (2) Stockez l'étiquette, le texte et la taille dans une liste. label est la catégorie de nouvelles, le texte est le corps et la taille est la probabilité que le modèle soit prédit. La partie nécessaire est extraite à l'aide d'une expression régulière. ③ Sortez les nouvelles une par une et essayez de prédire la catégorie. Les prédictions sont affichées par ordre décroissant de probabilité par le nombre d'arguments k de «prédire». Le tableau suivant montre les probabilités correspondantes. Toutes les questions sont correctes, donc c'est bien.

# ①
with open("news.valid") as f:
    l_strip = [s.strip() for s in f.readlines()] # strip()Supprimer les caractères de saut de ligne à l'aide de
    

# ②    
labels = []
texts = []
sizes = []
for t in l_strip:
    labels.append(re.findall('__label__(.*?) ', t)[0])
    texts.append(re.findall(' (.*)', t)[0])
    sizes.append(model.predict(re.findall(' (.*)', t))[1][0][0])
# ③-1
print("<{}>".format(labels[0]))
print(texts[0])
print(model.predict(texts[0], k=3))
Le groupe Dentsu a annoncé le 10 que les résultats financiers consolidés pour l'exercice se terminant en décembre 2020 seront de 676,3 milliards de yens, soit une baisse de 94 du chiffre d'affaires, ce qui équivaut au chiffre d'affaires. Le bénéfice d'exploitation, qui indique le bénéfice de l'activité principale, a été réduit de moitié à 18,5 milliards de yens. En raison de l'impact de la maladie, la demande de publicités télévisées et Internet a chuté au Japon et à l'étranger. Le bénéfice d'exploitation sera divisé par deux de la fin de l'année à mars de l'année prochaine, ainsi que la création d'une nouvelle société qui sous-traite les opérations aux retraités. Une série de coûts d'acquisition de la fusion de MA ont été inférieurs aux prévisions en raison de l'enregistrement de coûts de réforme structurelle de 25,1 milliards de yens. En conséquence, le résultat net a été multiplié par 22 pour atteindre 10,2 milliards de yens. US Media Storm a décidé d'en faire une filiale en février. La valeur de nombreuses entreprises, y compris des entreprises, a diminué et le coût estimé de l'acquisition d'actions supplémentaires a diminué d'environ 30 milliards de yens. (('__ label__ economy', '__ label__domestic', '__ label__life'), array ([9.88678277e-01, 1.48057193e-01, 3.89984576e-04]))
# ③-2
print("<{}>".format(labels[1]))
print(texts[1])
print(model.predict(texts[1], k=3))
Nintendo a annoncé le 5 que les ventes mondiales cumulées de Nintendo Switch, une machine de jeu à usage domestique, avaient atteint 68,3 millions d'unités à la fin du mois de septembre, dépassant les 61,91 millions d'unités de l'ordinateur familial Famicom pour propager l'infection du nouveau virus corona. La consommation d'accompagnement de la nidification a été un vent arrière, et le changement, qui a été réalisé environ trois ans et demi depuis son lancement en mars 2017, peut être joué comme un jeu même s'il est reporté ou porté, et il a été soutenu par un large éventail de générations. No Mori a été un succès et a stimulé les ventes de commutateurs: rien qu'en septembre 2008, 12,53 millions d'unités ont été vendues. (('__ label__ économie', '__ label__ sports', '__ label__domestic'), tableau ([0,00338661, 0,00206074, 0,00081409]))
# ③-3
print("<{}>".format(labels[2]))
print(texts[2])
print(model.predict(texts[2], k=3))
JERA Seregi Giant 6-2 Yakult 7th Tokyo Dome La cérémonie de départ à la retraite du géant Hisashi Iwakuma 39, qui ne prendra sa retraite que cette saison, s'est déroulée le 7 après la 23e manche du Yakult Tokyo Dome, qui est le plus grand nombre de cette saison. Devant les fans humains, Iwakuma a fermé le rideau sur 21 ans de vie de baseball professionnel aujourd'hui. Il a dit qu'il avait la chance d'avoir de merveilleux coéquipiers pendant 21 ans et qu'il était capable de vivre la meilleure vie de baseball et était plein de gratitude. Et les Giants pendant les deux dernières années. Je n'ai pas pu retourner dans la 1ère armée, mais je suis heureux d'avoir pu porter l'uniforme des Giants à la fin de ma carrière, et je suis heureux d'avoir cette journée. Je veux continuer à pouvoir rendre quelqu'un heureux grâce au baseball. Après cela, j'ai prononcé un discours de remerciement pendant 21 ans, après quoi j'ai reçu un bouquet de Shima, qui a joué avec mon coéquipier Aoki Rakuten à l'époque des Mariners à Yakult, et j'ai également reçu un bouquet de Kanno. Il s'est précipité à Iwakuma lui-même et a pris une photo commémorative avec Nine. Enfin, il a reçu un bouquet de trois enfants et a de nouveau pris une photo commémorative pour dire au revoir aux fans. (('__ label__ sports', '__ label__ entertainment', '__ label__ life'), array ([8.55861187e-01, 1.00888625e-01, 7.65405654e-04]))

Analyse de la représentation vectorielle

Vous pouvez terminer l'analyse jusqu'à ce point, mais utilisons la fonction appelée get_sentence_vector de fastText pour obtenir le vecteur de chaque article et effectuer une analyse plus approfondie. (1) Obtenez un vecteur pour chaque article et enregistrez-le dans la liste. (2) Changez le vecteur, l'étiquette et la taille en un tableau numpy. (Déjà acquis pour l'étiquette et la taille) ③ Standardisez le vecteur en utilisant Standard Scaler ④ Réduction de dimension à l'aide de l'analyseur de composants principaux PCA ⑤ Calculez la similitude pour chaque article en utilisant la fonction cos_sim définie ci-dessus. Les articles de la même catégorie présentent la plus grande similitude. ⑥ Tracé bidimensionnel du vecteur. La taille du point change en fonction de la valeur des tailles. (les tailles stockent la prévisibilité.)

# ①
vectors = []
for t in texts:
    vectors.append(model.get_sentence_vector(t))

    
# ②
vectors = np.array(vectors)
labels = np.array(labels)
sizes = np.array(sizes)


# ③
ss = preprocessing.StandardScaler()
vectors_std = ss.fit_transform(vectors)


# ④
pca = PCA()
pca.fit(vectors_std)
feature = pca.transform(vectors_std)
feature = feature[:, :2]
# ⑤-1
print("<{}><{}>".format(labels[0], labels[1]))
cos_sim(vectors[0], vectors[1])

0.9514279

# ⑤-2
print("<{}><{}>".format(labels[1], labels[2]))
cos_sim(vectors[1], vectors[2])

0.9299138

# ⑤-3
print("<{}><{}>".format(labels[0], labels[2]))
cos_sim(vectors[0], vectors[2])

0.79527444

# ⑥
x0, y0, z0 = feature[labels=='Divertissement', 0], feature[labels=='Divertissement', 1], sizes[labels=='Divertissement']*1000
x1, y1, z1 = feature[labels=='Des sports', 0], feature[labels=='Des sports', 1], sizes[labels=='Des sports']*1000
x2, y2, z2 = feature[labels=='la vie', 0], feature[labels=='la vie', 1], sizes[labels=='la vie']*1000
x3, y3, z3 = feature[labels=='National', 0], feature[labels=='National', 1], sizes[labels=='National']*1000
x4, y4, z4 = feature[labels=='international', 0], feature[labels=='international', 1], sizes[labels=='international']*1000
x5, y5, z5 = feature[labels=='surface', 0], feature[labels=='surface', 1], sizes[labels=='surface']*1000
x6, y6, z6 = feature[labels=='Économie', 0], feature[labels=='Économie', 1], sizes[labels=='Économie']*1000


plt.figure(figsize=(14, 10))
plt.rcParams["font.size"]=20
plt.scatter(x0, y0, label="Divertissement", s=z0)
plt.scatter(x1, y1, label="Des sports", s=z1)
plt.scatter(x2, y2, label="la vie", s=z2)
plt.scatter(x3, y3, label="National", s=z3)
plt.scatter(x4, y4, label="international", s=z4)
plt.scatter(x5, y5, label="surface", s=z5)
plt.scatter(x6, y6, label="Économie", s=z6)
plt.title("Yahoo Actualités")
plt.xlabel('1st dimension')
plt.ylabel('2nd dimension')
plt.legend(title="category")
plt.show()

output_22_2.png

Considération

―― «Divertissement» et «Sports» sont très proches l'un de l'autre. Il y a de nombreuses parties qui se chevauchent dans la première dimension, mais elles sont clairement séparées dans la deuxième dimension. C'est logique. ―― «International» et «domestique» sont bien séparés, et l'économie se situe entre les deux. C'est également convaincant. ―― «Sports» et «domestique» sont proches, mais est-ce parce que Yahoo News couvre plus d'articles de sport nationaux qu'à l'étranger?

  • "Région" est tracée près de "Domestique", mais la probabilité est faible car elle est tracée petite. Certes, il peut être difficile de lire l'article «Région» et de déterminer s'il est «domestique» ou «régional».

Je pense qu'ils se regroupent bien dans l'ensemble.

Cette fois, j'ai obtenu une intrigue qui a été soigneusement classée par catégorie, ce qui peut être dû au fait que j'ai fait un apprentissage supervisé en utilisant des étiquettes de catégorie. Je pense qu'il est utile de pouvoir créer un modèle qui peut regrouper proprement des données valides qui ne sont pas utilisées pour la formation. Cependant, si l'apprentissage non supervisé produit des résultats similaires, je pense que c'est intéressant. C'est pourquoi j'aimerais essayer le clustering en utilisant l'apprentissage non supervisé la prochaine fois! → * Suite Apprendre sans enseignant

Références

Yahoo! News Regroupement des livres d'Aozora Bunko avec Doc2Vec fastText GitHub (fastText/python) Création d'un environnement mecab (dictionnaire NEologd) avec Docker (ubuntu) Scraping Yahoo News fastText tutorial(Text classification) [Python NumPy] Comment trouver la similitude cosinus Comprendre l'analyse des composants principaux en Python matplotlib Scatter plots with a legend