Cet article est de Kronos Co., Ltd. "~ Printemps 2020 ~ Je le ferai sans autorisation Calendrier de l'Avent" C'est l'article du troisième jour!
J'aimerais essayer quelque chose qui s'appelle API COTOHA (plateforme d'API de traitement du langage naturel / traitement de la voix) utilisé par des ingénieurs de pointe. ・ ・ ・ Voilà pourquoi À propos de l 'API COTOHA, qui contient beaucoup d'articles intéressants, j'ai pensé que je ferais quelque chose d'amusant et de flashy, mais cette fois, je viens d'écrire un article à ce sujet. J'ai écrit !! (contradiction) Quant aux sentiments, que diriez-vous de vous préparer à quelque chose de flashy? J'ai l'intention de le dire.
** Ce que tu sais **
--Utilisation détaillée de l'analyse de correspondance dans l'API COTOHA. --Comportement que vous avez remarqué dans l'essai, qui n'est pas écrit dans la référence de l'API COTOHA --Comment gérer Json renvoyé par la réponse API en classe
** Inconnue **
--Informations dans les versions antérieures à Python 3.7
Tout d'abord, voici une citation de la page officielle sur "Qu'est-ce que l'analyse de correspondance?"
Extraire les précurseurs (y compris les précurseurs constitués de mots multiples) correspondant aux directives telles que «là» et «il», les synonymes tels que «il» et «elle», et les mots correspondants tels que «même 〇〇». C'est une API RESTful qui produit tout comme la même chose.
Hmmm, par exemple? (Citation supplémentaire)
Par exemple, dans l'analyse du journal de dialogue entre le moteur de dialogue et l'utilisateur, en extrayant le mot pointé par le synonyme de la phrase contenant le synonyme et le contexte avant et après celui-ci, ce n'est pas aussi significatif pour l'analyse des journaux tels que «il» et «elle». Il est possible de remplacer les mots manquants par les mots précédents et de réaliser une analyse de log plus précise.
En d'autres termes, (ceci est aussi un exemple de phrase officiel), "** Taro " et " he **" sont analysés en comparant la phrase "Taro est un ami. Il a mangé de la viande grillée." Le résultat sera retourné ensemble.
Vérifiez ici ** "Si vous effectuez un prétraitement avec une analyse de correspondance avant de faire des choses flashy, les résultats des autres traitements du langage naturel changeront (plus de précision)?" ** Ensuite, j'ai décidé de faire le titre «Analyse de correspondance de phrases avec l'API COTOHA et de les enregistrer dans un fichier» (j'essayais de le faire au début, donc je suis parti) Il y a peut-être une demande. C'est aussi un facteur.
Cette fois
-Scraping n'importe quel texte de Aozora Bunko
Considérez la mise en œuvre. Par exemple, dans l'exemple précédent, «Taro est un ami. Il a mangé de la viande grillée.» «Et« Taro est un ami. Taro a mangé de la viande grillée. »« Enregistrez dans un fichier texte.
Voir ici pour la source entière Il comprend également certains traitements qui ne sont pas liés à l'analyse de la correspondance. La structure des dossiers ressemble à ceci.
├── aozora_scraping.py
├── config.ini
├── cotoha_function.py
├── json_to_obj.py
├── main.py
├── respobj
│ ├── __init__.py
│ └── coreference.py
└── result
__Pycache__
et README.md
sont omis. On suppose que le texte résultant sera stocké dans le dossier result
.aozora_scraping.py
# -*- coding:utf-8 -*-
import requests
from bs4 import BeautifulSoup
def get_aocora_sentence(aozora_url):
res = requests.get(aozora_url)
#Belle initialisation de soupe
soup = BeautifulSoup(res.content, 'lxml')
#Obtenir le texte principal de Aozora Bunko
main_text = soup.find("div", class_="main_text")
#Élimination du rubis
for script in main_text(["rp","rt","h4"]):
script.decompose()
sentences = [line.strip() for line in main_text.text.splitlines()]
#Élimination des pièces vides
sentences = [line for line in sentences if line != '']
return sentences
Si vous passez l'URL d'Aozora Bunko à la méthode get_aocora_sentence
, le texte de cette page sera renvoyé sous forme de liste pour chaque saut de ligne, en omettant les rubis et les marges.
main_text = soup.find("div", class_="main_text")
En quelque sorte, c'est un processus après avoir su que le corps du texte d'Aozora Bunko est entouré de <div class =" main_text "> </ div>
.
J'ai évoqué ce qui suit, par exemple comment traiter le texte d'Aozora Bunko.
J'ai essayé d'extraire et d'illustrer l'étape de l'histoire en utilisant COTOHA
cotoha_function.py
# -*- coding:utf-8 -*-
import os
import urllib.request
import json
import configparser
import codecs
#Classe d'opération API COTOHA
class CotohaApi:
#Initialisation
def __init__(self, client_id, client_secret, developer_api_base_url, access_token_publish_url):
self.client_id = client_id
self.client_secret = client_secret
self.developer_api_base_url = developer_api_base_url
self.access_token_publish_url = access_token_publish_url
self.getAccessToken()
#Obtenez un jeton d'accès
def getAccessToken(self):
#Spécification d'URL d'acquisition de jeton d'accès
url = self.access_token_publish_url
#Spécification d'en-tête
headers={
"Content-Type": "application/json;charset=UTF-8"
}
#Demander les spécifications du corps
data = {
"grantType": "client_credentials",
"clientId": self.client_id,
"clientSecret": self.client_secret
}
#Encoder la spécification du corps de la requête en JSON
data = json.dumps(data).encode()
#Demande de génération
req = urllib.request.Request(url, data, headers)
#Envoyez une demande et recevez une réponse
res = urllib.request.urlopen(req)
#Obtenir le corps de la réponse
res_body = res.read()
#Décoder le corps de la réponse à partir de JSON
res_body = json.loads(res_body)
#Obtenir un jeton d'accès à partir du corps de la réponse
self.access_token = res_body["access_token"]
#API d'analyse de correspondance
def coreference(self, document):
#Spécification de l'URL d'acquisition de l'API d'analyse de correspondance
url = self.developer_api_base_url + "v1/coreference"
#Spécification d'en-tête
headers={
"Authorization": "Bearer " + self.access_token,
"Content-Type": "application/json;charset=UTF-8",
}
#Demander les spécifications du corps
data = {
"document": document
}
#Encoder la spécification du corps de la requête en JSON
data = json.dumps(data).encode()
#Demande de génération
req = urllib.request.Request(url, data, headers)
#Envoyez une demande et recevez une réponse
try:
res = urllib.request.urlopen(req)
#Que faire si une erreur se produit dans la demande
except urllib.request.HTTPError as e:
#Si le code d'état est 401 Non autorisé, réacquérir le jeton d'accès et demander à nouveau
if e.code == 401:
print ("get access token")
self.access_token = getAccessToken(self.client_id, self.client_secret)
headers["Authorization"] = "Bearer " + self.access_token
req = urllib.request.Request(url, data, headers)
res = urllib.request.urlopen(req)
#Si l'erreur est différente de 401, la cause est affichée.
else:
print ("<Error> " + e.reason)
#Obtenir le corps de la réponse
res_body = res.read()
#Décoder le corps de la réponse à partir de JSON
res_body = json.loads(res_body)
#Obtenir le résultat de l'analyse à partir du corps de la réponse
return res_body
Pour les fonctions qui utilisent l'API COTOHA, reportez-vous à J'ai essayé d'utiliser l'API COTOHA, qui est censée être facile à gérer le traitement du langage naturel, en Python. J? ai compris. Cependant, veuillez noter que l'URL est passée de «beta / coreference» à «v1 / coreference» pour l'analyse de classement. (Maintenant, la version bêta changera peut-être un jour)
La première moitié du passage du texte à l'analyse de correspondance dans main.py
est la suivante. Je l'ai écrit (je l'ai écrit tel quel car il y a une partie à expliquer)
main.py
# -*- coding:utf-8 -*-
import os
import json
import configparser
import datetime
import codecs
import cotoha_function as cotoha
from aozora_scraping import get_aocora_sentence
from respobj.coreference import Coreference
from json_to_obj import json_to_coreference
if __name__ == '__main__':
#Obtenez l'emplacement du fichier source
APP_ROOT = os.path.dirname(os.path.abspath( __file__)) + "/"
#Obtenir la valeur définie
config = configparser.ConfigParser()
config.read(APP_ROOT + "config.ini")
CLIENT_ID = config.get("COTOHA API", "Developer Client id")
CLIENT_SECRET = config.get("COTOHA API", "Developer Client secret")
DEVELOPER_API_BASE_URL = config.get("COTOHA API", "Developer API Base URL")
ACCESS_TOKEN_PUBLISH_URL = config.get("COTOHA API", "Access Token Publish URL")
#constant
max_word = 1800
max_call_api_count = 150
max_elements_count = 20
#URL d'Aozora Bunko
aozora_html = 'Tout'
#Heure actuelle
now_date = datetime.datetime.today().strftime("%Y%m%d%H%M%S")
#Chemin du fichier pour enregistrer le texte d'origine
origin_txt_path = './result/origin_' + now_date + '.txt'
#Chemin du fichier pour enregistrer les résultats
result_txt_path = './result/converted_' + now_date + '.txt'
#Instanciation de l'API COTOHA
cotoha_api = cotoha.CotohaApi(CLIENT_ID, CLIENT_SECRET, DEVELOPER_API_BASE_URL, ACCESS_TOKEN_PUBLISH_URL)
#Récupérer le texte d'Aozora Bunko
sentences = get_aocora_sentence(aozora_html)
#Enregistrer le texte original pour comparaison
with open(origin_txt_path, mode='a') as f:
for sentence in sentences:
f.write(sentence + '\n')
#valeur initiale
start_index = 0
end_index = 0
call_api_count = 1
temp_sentences = sentences[start_index:end_index]
elements_count = end_index - start_index
limit_index = len(sentences)
result = []
print("Nombre total de listes" + str(limit_index))
while(end_index <= limit_index and call_api_count <= max_call_api_count):
length_sentences = len(''.join(temp_sentences))
if(length_sentences < max_word and elements_count < max_elements_count and end_index < limit_index):
end_index += 1
else:
if end_index == limit_index:
input_sentences = sentences[start_index:end_index]
print('indice: ' + str(start_index) + 'De' + str(end_index) + 'Jusqu'à ce que')
#Conditions de sortie
end_index += 1
else:
input_sentences = sentences[start_index:end_index - 1]
print('indice: ' + str(start_index) + 'De' + str(end_index-1) + 'Jusqu'à ce que')
print(str(call_api_count) + 'Deuxième communication')
response = cotoha_api.coreference(input_sentences)
result.append(json_to_coreference(response))
call_api_count += 1
start_index = end_index - 1
temp_sentences = sentences[start_index:end_index]
elements_count = end_index - start_index
En premier lieu, il n'est pas possible d'envoyer toutes les phrases en une seule requête à la fois (même si cela peut être naturel).
Je n'ai trouvé aucune mention dans la référence,
(Cela n'a pas été vérifié, mais je pense que le traitement des autres API COTOHA peut être similaire ou proche) Après l'instruction while, dans l'instruction if, "Jetez les données de texte emballées dans la liste pour chaque saut de ligne à l'analyse de correspondance autant que possible" est implémenté.
J'étais particulier sur la liste plutôt que sur la simple longueur de la phrase parce que je pensais que l'exactitude pourrait être affectée si l'analyse de la correspondance n'était pas effectuée aux pauses dans la phrase. (Attendu et non vérifié)
call_api_count <= max_call_api_count
Avec le plan gratuit, chaque API représente 1000 appels / jour, j'ai donc fait une mauvaise déclaration selon laquelle je voulais contrôler le nombre d'appels d'API dans une certaine mesure.
Je pense que c'est une question de goût, N'est-il pas plus facile d'attribuer la réponse API à une classe que de l'utiliser telle quelle dans le type de dictionnaire? C'est un service de proposition.
Avec l'API COTOHA, le type de dictionnaire semble être majoritaire dans l'article de Qiita, je vais donc poster une référence sur l'analyse de correspondance.
Tout d'abord, examinons un exemple officiel du type de format json dans lequel la réponse de l'analyse de correspondance entre. (Comme d'habitude, si vous lancez "Taro est un ami. Il a mangé de la viande grillée.")
coreference.json
{
"result" : {
"coreference" : [ {
"representative_id" : 0,
"referents" : [ {
"referent_id" : 0,
"sentence_id" : 0,
"token_id_from" : 0,
"token_id_to" : 0,
"form" : "Taro"
}, {
"referent_id" : 1,
"sentence_id" : 1,
"token_id_from" : 0,
"token_id_to" : 0,
"form" : "il"
} ]
} ],
"tokens" : [ [ "Taro", "Est", "ami", "est" ], [ "il", "Est", "Viande rôtie", "À", "manger", "Ta" ] ]
},
"status" : 0,
"message" : "OK"
}
La définition de la classe à laquelle cela peut être attribué est la suivante. Si vous jetez un œil à la Référence officielle, vous devriez être en mesure de la comprendre. Définissons la hiérarchie json de la partie la plus profonde.
resobj/coreference.py
# -*- coding: utf-8; -*-
from dataclasses import dataclass, field
from typing import List
#Objet d'entité
@dataclass
class Referent:
#ID d'entité
referent_id: int
#Le numéro de l'instruction qui contient l'entité
sentence_id: int
#Formulaire de début d'entité Numéro d'élément
token_id_from: int
#Forme de fin de l'entité Numéro d'élément
token_id_to: int
#Correspondance cible
form: str
#Objet d'information d'analyse de correspondance
@dataclass
class Representative:
#ID des informations d'analyse de correspondance
representative_id: int
#Tableau d'objets d'entité
referents: List[Referent] = field(default_factory=list)
#Objet de résultat de l'analyse des correspondances
@dataclass
class Result:
#Tableau d'objets d'information d'analyse de correspondance
coreference: List[Representative] = field(default_factory=list)
#Disposition de la notation obtenue par analyse morphologique de chaque phrase de la phrase cible d'analyse
tokens: List[List[str]] = field(default_factory=list)
#réponse
@dataclass
class Coreference:
#Objet de résultat de l'analyse des correspondances
result: Result
#Code d'état 0:OK, >0:Erreur
status: int
#Message d'erreur
message: str
Où je me suis fait prendre
Pour une raison quelconque, le champ de formulaire de la classe "Referent" n'a pas été expliqué dans Référence officielle.
Il m'a fallu un certain temps pour remarquer que les tokens
de la classe" Result "étaient List [List [str]]
.
Méthode pour attribuer json à la classe (json_to_coreference
est également décrit dans main.py
)
json_to_obj.py
# -*- coding:utf-8 -*-
import json
import codecs
import marshmallow_dataclass
from respobj.coreference import Coreference
def json_to_coreference(jsonstr):
json_formated = codecs.decode(json.dumps(jsonstr),'unicode-escape')
result = marshmallow_dataclass.class_schema( Coreference )().loads(json_formated)
return result
Il est implémenté en tant que dataclasses
, marshmallow_dataclass
. Le marshmallow_dataclass
peut souvent ne pas être installé. (Page PyPI)
C'est la raison principale pour laquelle Python 3.7 devrait être utilisé cette fois. Je recommande cette méthode car même s'il y a un changement de cahier des charges, il est facile de comprendre les pièces correspondantes et je pense que la correspondance sera rapide. (Je pense que c'est juste que je ne suis pas habitué au type de dictionnaire de Python, alors veuillez l'utiliser comme référence uniquement.)
Site de référence JSON la classe python
La question ici est "quelle correspondance est utilisée pour l'arrondissement". Cette fois, sur la base de la prédiction que ce qui apparaît en premier dans le texte est le corps principal ~~ easy ~~, nous allons l'implémenter en le résumant avec les mots correspondants apparus précédemment.
main.py
#Deuxième partie
for obj in result:
coreferences = obj.result.coreference
tokens = obj.result.tokens
for coreference in coreferences:
anaphor = []
#Basé sur la première correspondance de la coréférence.
anaphor.append(coreference.referents[0].form)
for referent in coreference.referents:
sentence_id = referent.sentence_id
token_id_from = referent.token_id_from
token_id_to = referent.token_id_to
#Réécrivez afin que le nombre d'éléments dans la liste ne soit pas modifié pour un traitement ultérieur.
anaphor_and_empty = anaphor + ['']*(token_id_to - token_id_from)
tokens[sentence_id][token_id_from: (token_id_to + 1)] = anaphor_and_empty
#Enregistrer le texte modifié dans un fichier
with open(result_txt_path, mode='a') as f:
for token in tokens:
line = ''.join(token)
f.write(line + '\n')
Quel élément numérique (phrase) de la phrase dans tokens
est sentence_id
token_id_from
et token_id_to
signifient que le token_id_from
to token_id_to
th de l'élément analysé dans la phrase sentence_id
e correspond à la correspondance.
Trouvez la correspondance à réécrire avec coreference.referents [0] .form
,
Lors de la réécriture
#Réécrivez afin que le nombre d'éléments dans la liste ne soit pas modifié pour un traitement ultérieur.
anaphor_and_empty = anaphor + ['']*(token_id_to - token_id_from)
tokens[sentence_id][token_id_from: (token_id_to + 1)] = anaphor_and_empty
Je vais faire un petit travail comme ça.
(Le nombre d'éléments à réécrire et le nombre d'éléments à réécrire sont forcément mis en correspondance)
Si vous ne le faites pas, les nombres de token_id_from
et token_id_to
seront incorrects. (S'il vous plaît dites-moi s'il y a une meilleure façon)
main.py
# -*- coding:utf-8 -*-
import os
import json
import configparser
import datetime
import codecs
import cotoha_function as cotoha
from aozora_scraping import get_aocora_sentence
from respobj.coreference import Coreference
from json_to_obj import json_to_coreference
if __name__ == '__main__':
#Obtenez l'emplacement du fichier source
APP_ROOT = os.path.dirname(os.path.abspath( __file__)) + "/"
#Obtenir la valeur définie
config = configparser.ConfigParser()
config.read(APP_ROOT + "config.ini")
CLIENT_ID = config.get("COTOHA API", "Developer Client id")
CLIENT_SECRET = config.get("COTOHA API", "Developer Client secret")
DEVELOPER_API_BASE_URL = config.get("COTOHA API", "Developer API Base URL")
ACCESS_TOKEN_PUBLISH_URL = config.get("COTOHA API", "Access Token Publish URL")
#constant
max_word = 1800
max_call_api_count = 150
max_elements_count = 20
#URL d'Aozora Bunko
aozora_html = 'https://www.aozora.gr.jp/cards/000155/files/832_16016.html'
#Heure actuelle
now_date = datetime.datetime.today().strftime("%Y%m%d%H%M%S")
#Chemin du fichier pour enregistrer le texte d'origine
origin_txt_path = './result/origin_' + now_date + '.txt'
#Chemin du fichier pour enregistrer les résultats
result_txt_path = './result/converted_' + now_date + '.txt'
#Instanciation de l'API COTOHA
cotoha_api = cotoha.CotohaApi(CLIENT_ID, CLIENT_SECRET, DEVELOPER_API_BASE_URL, ACCESS_TOKEN_PUBLISH_URL)
#Récupérer le texte d'Aozora Bunko
sentences = get_aocora_sentence(aozora_html)
#Enregistrer le texte original pour comparaison
with open(origin_txt_path, mode='a') as f:
for sentence in sentences:
f.write(sentence + '\n')
#valeur initiale
start_index = 0
end_index = 0
call_api_count = 1
temp_sentences = sentences[start_index:end_index]
elements_count = end_index - start_index
limit_index = len(sentences)
result = []
print("Nombre total de listes" + str(limit_index))
while(end_index <= limit_index and call_api_count <= max_call_api_count):
length_sentences = len(''.join(temp_sentences))
if(length_sentences < max_word and elements_count < max_elements_count and end_index < limit_index):
end_index += 1
else:
if end_index == limit_index:
input_sentences = sentences[start_index:end_index]
print('indice: ' + str(start_index) + 'De' + str(end_index) + 'Jusqu'à ce que')
#Conditions de sortie
end_index += 1
else:
input_sentences = sentences[start_index:end_index - 1]
print('indice: ' + str(start_index) + 'De' + str(end_index-1) + 'Jusqu'à ce que')
print(str(call_api_count) + 'Deuxième communication')
response = cotoha_api.coreference(input_sentences)
result.append(json_to_coreference(response))
call_api_count += 1
start_index = end_index - 1
temp_sentences = sentences[start_index:end_index]
elements_count = end_index - start_index
for obj in result:
coreferences = obj.result.coreference
tokens = obj.result.tokens
for coreference in coreferences:
anaphor = []
#Basé sur la première correspondance de la coréférence.
anaphor.append(coreference.referents[0].form)
for referent in coreference.referents:
sentence_id = referent.sentence_id
token_id_from = referent.token_id_from
token_id_to = referent.token_id_to
#Réécrivez afin que le nombre d'éléments dans la liste ne soit pas modifié pour un traitement ultérieur.
anaphor_and_empty = anaphor + ['']*(token_id_to - token_id_from)
tokens[sentence_id][token_id_from: (token_id_to + 1)] = anaphor_and_empty
#Enregistrer le texte modifié dans un fichier
with open(result_txt_path, mode='a') as f:
for token in tokens:
line = ''.join(token)
f.write(line + '\n')
Image comparant le texte original et une partie du texte converti avec FileMerge (à gauche est l'original, à droite après la conversion)
before:
J'avais un ami de Kagoshima et j'ai appris naturellement en imitant cette personne, donc j'étais douée pour sonner ce sifflet de gazon.
Alors que je continuais à souffler, le professeur détourna les yeux et s'éloigna.
↓ after:
Avec une personne de Kagoshima comme ami, j'ai appris naturellement en imitant une personne de Kagoshima, et j'étais douée pour sonner ce sifflet de gazon.
Alors que je continuais à souffler dans ce sifflet, le professeur détourna les yeux et s'éloigna.
"Cette personne" dit "à une personne de Kagoshima" que "si vous continuez à souffler", cela devient "si vous continuez à souffler ce sifflet".
before:
Je suis immédiatement allé rendre l'argent au professeur. J'ai également apporté l'exemple de champignon Shiitake avec moi.
~Environ 6 phrases omises~
Le professeur savait beaucoup de choses que je ne savais pas sur les maladies rénales.
«La caractéristique de la maladie est que vous êtes seul malade, mais vous ne le remarquez pas.
Un officier que je connaissais en a finalement été tué, mais il est mort comme un mensonge.
~
↓
after:
Je suis immédiatement allé rendre l'argent au professeur. J'ai également apporté l'exemple de champignon Shiitake avec moi.
~Environ 6 phrases omises~
Le professeur savait beaucoup de choses que je ne savais pas sur les maladies rénales.
«La caractéristique de la maladie est que vous êtes seul malade, mais vous ne le remarquez pas.
Un officier que je connaissais a finalement été tué par le champignon Shiitake habituel, mais il est mort comme un mensonge.
~
Exemple Shiitake ... Il y a beaucoup d'autres douleurs. Le symbole n'est pas bien traité.
Je ne pense pas que les textes anciens correspondent à cela. Demi histoire (c'est juste intéressant parce que c'est comme ça)
Les deux sont l'original à gauche et le converti à droite.
En fin de compte, est-ce que l'hypothèse de "" "Si le prétraitement est effectué par analyse de correspondance avant de faire des choses flashy, les résultats d'un autre traitement du langage naturel vont changer (plus de précision)?" "Est-ce vrai ou non? Le résultat était indescriptible. Je pense que plus de vérification est nécessaire.
Afin d'améliorer la précision de la conversion, il semble préférable de réfléchir plus attentivement à la question de "quelle correspondance arrondir", et il peut être important de considérer le cadre de la phrase.
Puisque le japonais est une parade de directives et de synonymes, la perfection semble difficile, mais il y a des endroits où il peut être converti confortablement, donc je pense que l'API COTOHA a un potentiel considérable. c'est tout!
Recommended Posts