[PYTHON] Analyse du réseau des acteurs vocaux (à l'aide de word2vec et networkx) (1/2)

Le flux brutal introduit dans cet article

・ Gratter et obtenir une liste des noms et des sexes des acteurs de la voix ・ Séparez les informations textuelles de Wikipédia de l'acteur vocal incluses dans la liste avec mecab et apprenez avec word2vec ・ Jouez avec l'analyse de mots en utilisant le modèle appris avec word2vec

Flux d'analyse approximatif à introduire la prochaine fois

・ Construisez et visualisez un réseau d'acteurs féminins ・ Utilisez les informations du réseau pour regrouper et classer les acteurs vocaux similaires

Ravi de vous rencontrer, je m'appelle bamboo-nova. Qiita lancera principalement ** l'analyse des matériaux **. Veuillez consulter le "blog hatena" pour des histoires sérieuses et des analyses. De plus, la PNL n'est pas du tout une spécialité, donc je pense que c'est un code enfantin, mais s'il vous plaît regardez avec des yeux chaleureux ...!

http://yukr.hatenablog.com/

Ceci est le premier article, mais j'ai essayé ** l'analyse du réseau d'acteurs vocaux ** comme première analyse matérielle.

Cette fois, j'ai apporté l'article Wikipédia d'un acteur vocal, créé un réseau d'acteurs vocaux similaires ou apparentés à partir des informations textuelles et j'ai décidé de me regrouper davantage à partir de là.

Cette fois, nous créerons un modèle d'apprentissage et l'analyserons uniquement pour les comédiennes de voix féminines dans leur adolescence et la trentaine.

** Une addition) ** J'ai mis le code source qui résume la série d'étapes jusqu'à présent sur Github, alors veuillez vous y référer si vous le souhaitez!

bamboo-nova/seiyu_network

Tout d'abord, obtenez une liste des acteurs féminins.

Supprimez les sites suivants pour obtenir une liste des noms des acteurs de la voix. Il semble que les acteurs de la voix dont les âges ne sont pas divulgués ne soient pas inclus dans la liste sur ce site, donc même s'ils sont célèbres, ils peuvent ne pas être affichés dans l'analyse.

http://lain.gr.jp/voicedb/individual

Tout d'abord, appelez le module requis.

import MeCab
import codecs
import urllib
import urllib.parse as parser
import urllib.request as request
import requests
from bs4 import BeautifulSoup
import numpy as np
import pandas as pd

from gensim.models import word2vec

import re

# .Je l'ai divisé entre ceux qui font parseToNode et ceux qui ne le font pas.
mecab = MeCab.Tagger('-Owakati -d /usr/local/mecab/lib/mecab/dic/mecab-ipadic-neologd')
title = MeCab.Tagger('-d /usr/local/mecab/lib/mecab/dic/mecab-ipadic-neologd')

Obtient les informations de l'URL spécifiée.

url = "http://lain.gr.jp/voicedb/individual/age/avg/10"
 
response = requests.get(url)
response.encoding = response.apparent_encoding
 
soup10 = BeautifulSoup(response.text, 'html.parser')
 
url = "http://lain.gr.jp/voicedb/individual/age/avg/20"
 
response = requests.get(url)
response.encoding = response.apparent_encoding
 
soup20 = BeautifulSoup(response.text, 'html.parser')

url = "http://lain.gr.jp/voicedb/individual/age/avg/30"
 
response = requests.get(url)
response.encoding = response.apparent_encoding
 
soup30 = BeautifulSoup(response.text, 'html.parser')

Tout d'abord, obtenez le nom de l'acteur vocal. Cette fois, dans l'URL du code source ci-dessus, toutes les balises qui ont l'attribut href incluant "voicedb / profile" seront acquises par les acteurs de la voix dans leur adolescence et 30 ans. L'image ci-dessous est celle où l'une des URL du code source ci-dessus a été effectivement vérifiée avec Chrome, mais le nom de l'acteur vocal est en fait stocké dans un format de liste dans l'attribut href comprenant "voicedb / profile". Je pense que vous pouvez le confirmer.

スクリーンショット 2020-02-07 16.30.48.png

Ci-dessous, le code source pour obtenir le nom

corpus=[]
name10 = soup10.find_all(href=re.compile("voicedb/profile"))
name20 = soup20.find_all(href=re.compile("voicedb/profile"))
name30 = soup30.find_all(href=re.compile("voicedb/profile"))
for p in name10:
            corpus.append(p.text)
for p in name20:
            corpus.append(p.text)
for p in name30:
            corpus.append(p.text)

Ensuite, obtenez le sexe. En regardant l'image vérifiée ci-dessus, il semble que vous pouvez obtenir le src et l'alt dans la balise img, alors obtenez-le comme suit. Au fait, c'est la partie de ʻalt = img.attrs.get ('alt', 'N') `, mais si l'attribut alt existe, sa valeur est définie, et s'il n'existe pas, 'N' est défini.

data = []
for img in soup10.find_all('img'):
    data.append(dict(src=img['src'],
                     alt=img.attrs.get('alt', 'N')))
for img in soup20.find_all('img'):
    data.append(dict(src=img['src'],
                     alt=img.attrs.get('alt', 'N')))
for img in soup30.find_all('img'):
    data.append(dict(src=img['src'],
                     alt=img.attrs.get('alt', 'N')))

gender = []
for res in data:
    for k, v in res.items():
        if v == 'Femme' or v == 'Masculin':
            gender.append(v)

Ensuite, enregistrez la trame de données qui intègre le nom et le sexe au format csv.

name = pd.DataFrame(corpus)
gen = pd.DataFrame(gender)
res = pd.concat([name, gen],axis=1)
res.columns = ['Name','Gender']
res.to_csv('seiyu.csv')

Obtenez les informations de profil de Wikipédia de l'acteur de voix féminine inclus dans la liste obtenue.

À présent, sélectionnez uniquement les comédiennes vocales féminines dans les trames de données enregistrées et obtenez les informations de profil de l'acteur vocal cible sur Wikipedia. Les informations obtenues sont enregistrées sous pwiki.txt.

df = pd.read_csv('seiyu.csv')

#Lien Wikipedia
link = "https://ja.wikipedia.org/wiki/"
#Cibler uniquement les acteurs féminins
df_women = df[df.Gender=='Femme']
keyword = df_women.Name
keyword = list(keyword)
corpus = []
for word in keyword:
    #Télécharger l'article sur les doubleurs
    try:
        with request.urlopen(link + parser.quote_plus(word)) as response:
            #la réponse est au format html
            html = response.read().decode('utf-8')
            soup = BeautifulSoup(html, "lxml")
            # <p>Obtenir le tag
            p_tags = soup.find_all('p')
            for p in p_tags:
                corpus.append(p.text.strip())
    except urllib.error.HTTPError as err:
        #Pour les acteurs vocaux qui ne sont pas répertoriés sur Wikipédia, une erreur se produira, donc la gestion des exceptions est ajoutée.
        if err.code == 404:
            continue
        else:
            raise

with codecs.open("pwiki.txt", "w", "utf-8") as f:
    f.write("\n".join(corpus))

Divisez en fait le fichier texte et enregistrez le modèle d'apprentissage sous word2vec.

Maintenant, écrivons les données texte réellement acquises. Comme il existe de nombreux noms d'animations cette fois, nous prenons également en charge la nomenclature appropriée (par exemple, la flotte de lycées est traitée comme une nomenclature et une organisation appropriées, donc si vous parseToNode normalement * Sera). De plus, comme dans l'exemple ci-dessous, il semble que ** pour une raison quelconque, si vous écrivez "precure" séparément avec mecab + neologd, il sera converti en anglais **, alors répondez également à cela avec la branche conditionnelle de l'instruction if afin qu'elle réponde à l'anglais. (Comme il y avait tellement d'acteurs vocaux qui ont affiché leur apparition dans Precure sur leur profil en tant que statut, cela ne pouvait pas être ignoré en tant qu'informations sur la quantité de fonctionnalités, alors j'ai fait en sorte que la division soit correctement reflétée).

node = title.parse('Precure')

node.split(",")[6]
#Résultat de sortie: 'PulCheR'

Vous trouverez ci-dessous le code source réel. Tout d'abord, prétraitez les données texte.

fi = codecs.open('pwiki.txt')
result = []
fo = open('try.csv', 'w')

lines = fi.readlines()
for line in lines:
    line = re.sub('[\n\r]',"",line)
    line = re.sub('[ ]'," ",line) #Certaines nomenclatures ne peuvent pas être extraites en tant que nomenclature composée à moins que l'espace pleine largeur ne soit converti en demi-largeur.
    line = re.sub('(Année|Mois|journée)',"",line)
    line = re.sub('[0-9_]',"",line)
    line = re.sub('[#]',"",line)
    line = re.sub('[!]',"",line)
    line = re.sub('[*]',"",line)
    fo.write(line + '\n')


fi.close()
fo.close()

Ensuite, les données de texte prétraitées sont réellement divisées et apprises avec word2vec. Cette fois, j'ai appris avec les paramètres par défaut. L'explication de word2vec sera omise ici, veuillez donc vous référer à l'URL suivante pour le mécanisme, etc.

Word2Vec: La puissance incroyable du vecteur de mots qui surprend même l'inventeur

fi = open('try.csv', 'r')
fo = open('res.csv', 'w')

#line = fi.readline()
lines = fi.readlines()
result=[]
mecab.parse("")


for line in lines:
    node = mecab.parseToNode(line)
    node_org = title.parse(line)
    
    while node:
        hinshi = node.feature.split(",")[0]
        if hinshi == 'adjectif' or hinshi == 'nom' or hinshi == 'adverbe' or (len(node.feature.split(",")[6])>1):
            fo.write(node.feature.split(",")[6] + ' ')
        if node_org != 'EOS\n' and node_org.split(",")[1] == 'Nomenclature propriétaire':
            fo.write(node_org.split(",")[6] + ' ')
        if node_org != 'EOS\n' and node_org.split(",")[1] == 'Nomenclature propriétaire' and node_org.split(",")[6].isalpha()==True:
            fo.write(node_org.split(",")[7] + ' ')
        node = node.next

fi.close()
fo.close()

print('Wakati phase completed!')

sentences = word2vec.LineSentence('res.csv')
model = word2vec.Word2Vec(sentences,
                          sg=1,
                          size=200,
                          min_count=5,
                          window=5,
                          hs=1,
                          iter=100,
                          negative=0)


#Économisez avec cornichon
import pickle
with open('mecab_word2vec_seiyu.dump', mode='wb') as f:
    pickle.dump(model, f)

Essayez les résultats du modèle word2vec

Jouons avec l'analyse de mots en utilisant le modèle réellement appris avec word2vec. À titre de test, je vais essayer de sortir même un doubleur qui semble ne pas se vendre comme une idole.

ret = model.wv.most_similar(negative=['Idole'],topn=1000) 

for item in ret:
    if len(item[0])>2 and (item[0] in list(df.Name)):
        print(item[0],item[1])

Résultat de sortie

Sumi Nako 0.13453319668769836
Shota Aoi 0.1175239235162735
Aio Toyosaki 0.1002458706498146
Tomoka Tamura 0.08929911255836487
Akane Yamaguchi 0.05830669403076172
Ayaka Mori 0.056574173271656036
Saki Fujita 0.05241834372282028
Yui Kano 0.051871318370103836
Saori Hayami 0.04932212829589844
Mika Kikuchi 0.04044754058122635
Anzu Suzuki 0.034879475831985474
Ryota Osaka 0.029612917453050613
Yuka Iguchi 0.02767171896994114
Aoki Yuki 0.02525651454925537
Chieko Higuchi 0.022603293880820274

Ensuite, je vais sortir un doubleur qui semble avoir une forte couleur d'idole.

ret = model.wv.most_similar(positive=['Idole'],topn=300) 

for item in ret:
    if len(item[0])>2 and (item[0] in list(df.Name)):
        print(item[0],item[1])

Résultat de sortie

Machico 0.1847614347934723
Kaori Fukuhara 0.1714700609445572
Sachika Misawa 0.1615515947341919
Mai Nakahara 0.15694507956504822
Yui Ogura 0.1562490165233612
Shoko Nakagawa 0.1536223590373993
Nao Higashiyama 0.15278896689414978
Yui Sakakibara 0.14662891626358032
Ai Shimizu 0.14592087268829346
Natsuori Ishihara 0.14554426074028015

Ensuite, extrayons les acteurs de la voix qui sont susceptibles d'avoir un lien avec le «prix».

ret = model.wv.most_similar(positive=['Prix'],topn=500) 

for item in ret:
    if len(item[0])>2 and (item[0] in list(df.Name)):
        print(item[0],item[1])

Résultat de sortie


Kido Ibuki 0.19377963244915009
Kaori Fukuhara 0.16889861226081848
Minami Tsuda 0.16868139803409576
Shinrei Uchida 0.1677364706993103
Kaori Nazuka 0.1669023633003235
Ai Chino 0.16403883695602417
Maaya Sakamoto 0.16285887360572815
Yui Makino 0.14633819460868835
Ellie Yamazaki 0.1392231583595276
Eri Kitamura 0.13390754163265228
Kana Asumi 0.13131362199783325
Arisa Noto 0.13084736466407776
Ayaka Ohashi 0.1297467052936554
Ryota Osaka 0.12972146272659302

Ah, je me demande ... j'ai l'impression que c'est vraiment comme ça (** Au fait, même si je dis "mariage", c'est sorti lol **).

Résumé

Cette fois, j'ai obtenu les informations de Wikipedia de l'acteur vocal, j'ai enregistré le modèle d'apprentissage avec word2vec en utilisant les informations textuelles obtenues et j'ai essayé de déplacer le modèle. Il y a une limite aux informations textuelles du profil sur Wikipédia car il n'y a pas d'informations détaillées, il est donc nécessaire de vérifier une grande quantité de données et de modélisation afin de les analyser sérieusement. Cependant, pardonnez-moi car je publie ici uniquement à des fins d'analyse matérielle. ** En fait, la quantité de données textuelles est petite et le résultat changera légèrement en fonction du paramètre de word2vec, il se peut donc que ce ne soit pas exactement le résultat du blog **. Le résultat général en lui-même ne change pas radicalement cela, mais ...

Ensuite, nous le visualiserons comme un réseau en utilisant le modèle réellement appris, et en fait, nous regrouperons le réseau pour catégoriser les acteurs vocaux similaires.

Recommended Posts

Analyse du réseau des acteurs vocaux (à l'aide de word2vec et networkx) (1/2)
Analyse du réseau des acteurs vocaux (utilisant word2vec et networkx) (2/2)
Analyse de réseau avec NetworkX --- Volume de détection de la communauté
Analyse de sensibilité globale à l'aide des bibliothèques d'analyse de sensibilité Salib et Oacis
Estimation de l'auteur à l'aide du réseau neuronal et de Doc2Vec (Aozora Bunko)