[PYTHON] J'ai fait une bibliothèque pour bien séparer les phrases japonaises

introduction

Ces dernières années, le développement de la technologie de traitement du langage naturel a été remarquable et son application est encouragée dans divers domaines. Je fais souvent un travail qui utilise la technologie de traitement du langage naturel et l'IA, mais le travail le plus gênant (mais important) est lié à divers prétraitements.

Certains des principaux prétraitements que vous effectuerez pour la plupart des tâches comprennent:

J'utilise principalement Python, mais je n'avais pas de bibliothèque appropriée pour les ** sauts de phrase japonais **, donc j'ai fini par écrire du code similaire à chaque fois. Je suis sûr qu'il y a environ 100 personnes dans le monde qui ont des problèmes similaires, j'ai donc décidé d'écrire ma propre bibliothèque et de la publier en tant que OSS, mais c'était au début de 2019. Il était temps. Cependant, je n'ai pas pu obtenir suffisamment de temps et de motivation, et cela a été retardé, mais j'ai finalement pu commencer par fixer la limite d'écriture d'articles sur le calendrier de l'Avent.

Problèmes spécifiques de rupture de phrase

Je pense que les éléments suivants sont plus couramment utilisés comme de simples délimiteurs de phrases.

Cependant, il existe de nombreux documents réels qui ne peuvent pas être bien séparés par les règles simples ci-dessus.

Il y a des signes de ponctuation et des points d'exclamation entre "" et ()

Par exemple, «J'ai répondu:« Oui, c'est vrai ». Si vous séparez simplement le texte comme `par ponctuation, il sera divisé comme suit:

Je pense qu'il y a de bonnes situations, mais j'ai répondu: "Oui, c'est vrai." Vous voudrez peut-être le traiter comme une phrase, `.

Il y a une pause au milieu de la phrase

Par exemple, vous pouvez avoir une pause au milieu d'une phrase comme indiqué ci-dessous car elle ne tient pas sur un seul écran (en particulier pour les documents d'une entreprise).

Dans le traitement du langage naturel, ~ omis ~
Il est couramment utilisé.

Si cela est séparé par un saut de ligne, il sera divisé en deux phrases, mais dans le traitement du langage naturel, il est courant d'utiliser ~ omis ~. Vous pouvez le séparer en une seule phrase, `.

Dans l'exemple ci-dessus, si vous supprimez les sauts de ligne, puis que vous les séparez par des signes de ponctuation, vous pouvez faire quelque chose, mais ** contient des phrases qui n'ont pas de signes de ponctuation **, ce qui le rend beaucoup plus gênant. (~~ Veuillez ajouter quelques signes de ponctuation ... ~~)

Bloc de devis pour les e-mails, etc.

>>J'avais l'intention d'aller au salon de coiffure demain, mais "pressé
>>Modifiez l'horaire. Par conséquent, de la réunion
>>Permettez-moi de changer le programme. ".

J'ai reconnu.

Cas où il y a des symboles inutiles au début des lignes et des sauts de ligne au milieu d'une phrase. Il existe une théorie selon laquelle il s'agit du cas le plus courant dans les documents d'entreprise (subjectif). L'approche la plus simple consiste à supprimer d'abord les symboles et les sauts de ligne, puis à les traiter. Cependant, il est rare que vous souhaitiez les combiner tout en supprimant les symboles inutiles et en laissant l'information qu'ils sont un bloc de guillemets.

Techniques connexes

GiNZA GiNZA est une bibliothèque qui peut être utilisée pour séparer des phrases japonaises en Python. Les sauts de phrase à l'aide de GiNZA peuvent être effectués comme suit.

import spacy
nlp = spacy.load('ja_ginza')
doc = nlp('On m'a dit: "Je ne peux pas répondre à vos pensées. Je veux que vous frappiez les autres." Étourdi\n Je n'avais pas d'autre choix que de rester là, mais je veux toujours y croire!')
for sent in doc.sents:
  print(sent)
J'ai dit: "Je ne peux pas répondre à vos pensées.
Je veux que tu frappes l'autre.
"Ils ont dit!
J'étais abasourdi et n'avais pas d'autre choix que de rester là
Je veux toujours croire!

L'avantage de l'utilisation de GiNZA est qu'il peut détecter les sauts de phrase avec une grande précision même s'il y a un saut de ligne au milieu d'une phrase ou si un signe de ponctuation est omis car l'analyse des dépendances est effectuée correctement. .. Bien que ce soit une classe lourde, je pense que c'est une bonne option si vous utilisez également d'autres fonctions de GiNZA.

sentence-splitter C'est un outil créé par Node.js, mais il y a aussi sentence-splitter.

echo -e "Il a dit: "Je ne peux pas répondre à vos pensées. Je veux que vous frappiez les autres." Étourdi\n Je n'avais pas d'autre choix que de rester là, mais je veux toujours y croire!" | sentence-splitter 
Sentence 0:On m'a dit: "Je ne peux pas répondre à vos pensées. Je veux que vous frappiez les autres."
Sentence 1:Étourdi
Je n'avais pas d'autre choix que de rester là, mais je veux toujours y croire!

Cet outil utilise également l'analyseur utilisé à l'intérieur de textlint pour une analyse avancée, il est donc précis même s'il y a une pause au milieu de la phrase. Il est divisé haut. De plus, il est intéressant que j'aime la manipulation de "" etc. et que les performances de traitement soient assez rapides. (Si ce n'était pas Node.js, je l'aurais adopté)

Pragmatic Segmenter Bien qu'il s'agisse d'une bibliothèque Ruby, il existe un Pragmatic Segmenter. Il s'agit d'une bibliothèque de délimiteurs de phrases basée sur des règles, et son principal avantage est qu'elle prend en charge ** plusieurs langues **. Il est également attrayant car il n'effectue pas d'analyse compliquée et est rapide à traiter.

Puisque les règles de saut de phrase japonaises sont proches de mon goût, le but de cet outil de développement est "de pouvoir casser des phrases japonaises égales ou meilleures que le Segmenteur Pragmatique".

Une démo en direct (https://www.tm-town.com/natural-language-processing) est disponible pour cet outil, et les résultats que j'ai essayés là-bas sont indiqués ci-dessous.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wrapper>
<s>On m'a dit: "Je ne peux pas répondre à vos pensées. Je veux que vous frappiez les autres."</s>
<s>Étourdi</s>
<s>Je n'avais pas d'autre choix que de rester là, mais je veux toujours y croire!</s>
</wrapper>

Au fait, un portage Python de ce Segmenteur Pragmatique a été développé sous le nom de pySBD. Malheureusement, il semble que les règles pour le japonais n'aient pas encore été portées.

Ce que j'ai fait

Donc, la bibliothèque que j'ai créée cette fois est ouverte au public à ↓. https://github.com/wwwcojp/ja_sentence_segmenter

En créant la bibliothèque, je l'ai développée avec les objectifs suivants.

Comment utiliser

Installation

Il s'agit de Publish to PyPI, vous pouvez donc facilement l'installer avec pip. Il prend en charge Python 3.6 et supérieur, et il n'y a pas de bibliothèques dépendantes jusqu'à présent.

$ pip install ja-sentence-segmenter

Courir

Il y a des signes de ponctuation et des points d'exclamation entre "" et () et des sauts de ligne au milieu de la phrase

import functools

from ja_sentence_segmenter.common.pipeline import make_pipeline
from ja_sentence_segmenter.concatenate.simple_concatenator import concatenate_matching
from ja_sentence_segmenter.normalize.neologd_normalizer import normalize
from ja_sentence_segmenter.split.simple_splitter import split_newline, split_punctuation

split_punc2 = functools.partial(split_punctuation, punctuations=r"。!?")
concat_tail_te = functools.partial(concatenate_matching, former_matching_rule=r"^(?P<result>.+)(main)$", remove_former_matched=False)
segmenter = make_pipeline(normalize, split_newline, concat_tail_te, split_punc2)

text1 = """
On m'a dit: "Je ne peux pas répondre à vos pensées. Je veux que vous frappiez les autres." Étourdi
Je n'avais pas d'autre choix que de rester là, mais je veux toujours y croire!
"""
print(list(segmenter(text1)))
['On m'a dit: "Je ne peux pas répondre à vos pensées. Je veux que vous frappiez les autres."!', 'J'étais abasourdi et je n'avais pas d'autre choix que de rester là, mais je veux toujours croire!']

Bloc de devis par e-mail

import functools

from ja_sentence_segmenter.common.pipeline import make_pipeline
from ja_sentence_segmenter.concatenate.simple_concatenator import concatenate_matching
from ja_sentence_segmenter.normalize.neologd_normalizer import normalize
from ja_sentence_segmenter.split.simple_splitter import split_newline, split_punctuation

split_punc2 = functools.partial(split_punctuation, punctuations=r"。!?")
concat_mail_quote = functools.partial(concatenate_matching,
  former_matching_rule=r"^(\s*[>]+\s*)(?P<result>.+)$",
  latter_matching_rule=r"^(\s*[>]+\s*)(?P<result>.+)$",
  remove_former_matched=False,
  remove_latter_matched=True)
segmenter = make_pipeline(normalize, split_newline, concat_mail_quote, split_punc2)

text2 = """
>>J'avais l'intention d'aller au salon de coiffure demain, mais "pressé
>>Modifiez l'horaire. Par conséquent, de la réunion
>>Permettez-moi de changer le programme. ".

J'ai reconnu.
"""

print(list(segmenter(text2)))
['>>J'avais l'intention d'aller au salon de coiffure demain, mais il a dit: "Je vais changer mon horaire rapidement. S'il vous plaît laissez-moi changer le calendrier de la réunion."', 'J'ai reconnu.']

Tâches futures

Je suis presque épuisé, je terminerai donc en exposant les prochains numéros.

Impressions

~~ Pourquoi avez-vous fait une chose aussi sobre dans l'article du Calendrier de l'Avent ... ~~

Recommended Posts

J'ai fait une bibliothèque pour bien séparer les phrases japonaises
J'ai créé une bibliothèque python qui fait rouler le rang
J'ai créé une bibliothèque qui lit facilement les fichiers de configuration avec Python
J'ai fait un script pour afficher des pictogrammes
J'ai fait une bibliothèque pour l'assurance actuarielle
Début de PyPi J'ai essayé de rendre possible l'installation d'une bibliothèque pour vérifier les vacances japonaises
J'ai créé un konoha de bibliothèque qui fait passer le tokenizer à une belle sensation
J'ai créé un outil pour compiler nativement Hy
J'ai créé un outil pour obtenir de nouveaux articles
J'ai créé une bibliothèque pour faire fonctionner la pile AWS CloudFormation à partir de CUI (Python Fabric)
J'ai fait un script pour mettre un extrait dans README.md
J'ai créé un module Python pour traduire les commentaires
J'ai créé un code pour convertir illustration2vec en modèle Keras
J'ai fait une commande pour marquer le clip de la table
J'ai fait un texte Python
J'ai fait un robot discord
J'ai créé un package pour filtrer les séries chronologiques avec python
J'ai fait une boîte pour me reposer avant que Pepper ne se fatigue
J'ai fait une commande pour générer un commentaire pour une table dans Django
J'ai créé un outil pour créer un nuage de mots à partir de wikipedia
J'ai fait une fonction pour vérifier le modèle de DCGAN
[Titan Craft] J'ai créé un outil pour invoquer un géant sur Minecraft
Je vous ai fait exécuter des commandes depuis un navigateur WEB
J'ai fait un script pour dire bonjour à mon Koshien
J'ai créé un site d'apprentissage C ++
J'ai essayé de faire un programme pour résoudre (indice) la recherche d'erreur de Saiseriya
J'ai fait un Line-bot avec Python!
J'ai créé un script de traduction basé sur CUI (2)
J'ai fait un wikipedia gacha bot
J'ai créé un serveur Web avec Razpai pour regarder des anime
J'ai créé une bibliothèque Python pour appeler l'API de LINE WORKS
J'ai créé ma propre bibliothèque Python
J'ai fait une loterie avec Python.
J'ai créé un script de traduction basé sur CUI
J'ai créé un générateur brouillé qui encode vos phrases préférées de UTF-8 à Shift-JIS (cp932) en Python
J'ai fait une commande pour afficher un calendrier coloré dans le terminal
J'ai créé un démon avec Python
J'ai créé une bibliothèque de wrapper Python pour l'API de reconnaissance d'images docomo.
J'ai créé un conteneur Docker pour utiliser JUMAN ++, KNP, python (pour pyKNP).
[Python] J'ai fait un décorateur qui ne semble pas avoir d'utilité.
J'ai fait un générateur de mot de passe pour enseigner Python3 aux enfants (bonus) * Complètement refait
J'ai créé un outil pour parcourir automatiquement plusieurs sites avec Selenium (Python)
[Mise à jour Ver1.3.1] J'ai créé une bibliothèque de prétraitement de données DataLiner pour l'apprentissage automatique
J'ai créé une application Web en Python qui convertit Markdown en HTML
Création de la bibliothèque Go nzargv qui organise bien les arguments de ligne de commande
[Django] a créé un champ pour saisir des dates avec des nombres à 4 chiffres
J'ai fait une minuterie de cuisine à afficher sur la barre d'état!
J'ai créé un outil CLI pour convertir les images de chaque répertoire en PDF
J'ai créé un programme pour vous avertir par LINE lorsque les commutateurs arrivent
J'ai créé un réseau pour convertir des images noir et blanc en images couleur (pix2pix)
J'ai créé un script en python pour convertir des fichiers .md au format Scrapbox
J'ai créé un programme pour saisir ce que j'ai mangé et afficher les calories et les sucres
J'ai créé un outil pour convertir Jupyter py en ipynb avec VS Code
J'ai fait un programme pour vérifier la taille d'un fichier avec Python
J'ai créé une fonction pour voir le mouvement d'un tableau à deux dimensions (Python)
J'ai créé un docset de tableau de bord pour Holoviews
Je veux imprimer dans la notation d'inclusion
J'ai essayé de créer un linebot (préparation)