J'ai créé un Wrapper qui appelle KNP depuis Java

Contenu de l'article compréhensible en 3 lignes

Aperçu

Récemment, la vague de Deep se précipite dans la région de la PNL, alors certaines personnes peuvent dire: «Je n'utilise pas KNP de nos jours». Cependant, si vous ne disposez pas de données correctes et que vous ne pouvez pas utiliser de méthodes courantes, ou si vous souhaitez essayer les résultats basés sur des règles avant d'essayer la méthode approfondie, il existe de nombreux résultats d'analyse de cas et diverses fonctionnalités. KNP est toujours utile car il vous donne beaucoup d'informations.

D'un autre côté, il ne peut pas être utilisé comme une bibliothèque comme Sudachi, donc cela peut être un problème à utiliser dans un programme (en particulier dans des langages autres que Python avec pyKNP).

Donc, cette fois, j'ai créé une bibliothèque Wrapper qui appelle KNP (et Human ++) à partir de Java. (https://github.com/Natsuume/knp4j) ~~ Je voulais le publier dans le référentiel Maven, mais je n'ai pas pu le faire à temps, donc je le publierai bientôt dans le référentiel Maven. ~~

~~ De plus, bien que nous ayons confirmé l'opération dans une certaine mesure, il est possible que des problèmes surviennent parce que nous n'avons pas écrit un test approprié ~~

Postscript

Publié dans Maven Central. Maintenant disponible sur Maven, Gradle, etc.

pom.xml


<dependency>
  <groupId>dev.natsuume.knp4j</groupId>
  <artifactId>knp4j</artifactId>
  <version>1.1.3</version>
</dependency>

build.gradle


implementation 'dev.natsuume.knp4j:knp4j:1.1.3'

Comment utiliser

C'est presque comme README.md sur github.

Sample.java


//Builder pour créer KNPWrapper
ResultParser<KnpResult> knpResultParser = new KnpResultParser();
KnpWrapperBuilder<KnpResult> knpWrapperBuilder = new KnpWrapperBuilder<>();
KnpWrapper<KnpResult> wrapper = knpWrapperBuilder
    .setJumanCommand(List.of("bash", "-c", "jumanpp")) //Commande d'exécution Juman
    .setKnpCommand(List.of("bash", "-c", "knp -tab -print-num -anaphora")) //Commande d'exécution KNP(Actuellement"-tab」「-print-num」「-option anaphora "requise)
    .setJumanMaxNum(1) //Nombre maximum de processus humains à démarrer en même temps
    .setJumanStartNum(1) //Nombre de processus humains à démarrer à l'initialisation
    .setKnpMaxNum(1) //Nombre maximum de processus KNP à démarrer en même temps
    .setKnpStartNum(1) //Nombre de processus KNP démarrés à l'initialisation
    .setRetryNum(0) //Nombre de tentatives si l'acquisition des résultats échoue
    .setResultParser(knpResultParser) //Liste des résultats de sortie<String>Définissez Parser pour qu'il se convertisse en n'importe quelle classe
    .start();
var texts = List.of(
    "Texte de test 1",
    "Texte de test 2",
    "Texte de test 3"
);
texts.parallelStream().map(wrapper::analyze)
    .flatMap(List::stream)
    .map(KnpResult::getSurfaceForm)
    .forEach(System.out::println);

Donnez divers paramètres avec KnpWrapperBuilder et générez et démarrez KnpWrapper pour la première fois avec start (). Donnez à setJumanCommand, setKnpCommand les mêmes commandes que vous donnez à ProcessBuilder. Selon l'environnement, il peut être possible d'exécuter uniquement avec les chemins JUMAN et KNP. (Dans mon environnement, j'ai dû appeler JUMANPP, KNP sur WSL, donc j'ai donné une commande comme l'exemple ci-dessus)

Pour les paramètres autres que setResultParser (), le contenu de l'exemple ci-dessus est les valeurs par défaut.

une fonction

Exécuter dans plusieurs processus

Configurez plusieurs processus et réutilisez-les. Le nombre de processus qui peuvent être configurés en même temps peut être librement défini pour chacun des JUMAN et KNP.

JUMAN et KNP ont un mode serveur, mais ce n'est pas actuellement pris en charge (sera pris en charge à l'avenir).

Ré-exécution lorsque l'analyse échoue

Fondamentalement, on suppose qu'il ne sera pas utilisé, mais lorsque ʻIOException ou ʻInterruptedException se produisent dans une série de traitements, le processus dans lequel l'exception s'est produite est arrêté et un autre processus tente d'analyser à nouveau.

Analyseur de résultats

Tout analyseur qui implémente l'interface ResultParser peut être utilisé comme analyseur de sortie. Les deux types de méthodes suivants sont définis pour ResultParser.

ResultParser.java


public interface ResultParser<OutputT> {

  /**
   *Renvoie une instance arbitraire avec le résultat de l'analyse de Knp en entrée.
   *
   * @Liste de paramètres Résultat de l'analyse Knp
   * @return Instance représentant le résultat de l'analyse
   */
  OutputT parse(List<String> list);

  /**
   *Renvoie l'instance à utiliser si l'analyse échoue.
   *
   * @renvoyer l'instance à renvoyer en cas d'échec de l'analyse
   */
  OutputT getInvalidResult();
}

getInvalidResult () est une méthode qui retourne une instance lorsqu'un résultat d'analyse normal ne peut pas être obtenu. Il est utilisé lorsque la réexécution à l'exception ci-dessus échoue, ou lorsque KNP ne parvient pas à analyser (KNP ne parvient pas à analyser si la demi-largeur «+», «*» est incluse).

Vérifiez s'il est plus rapide qu'un processus unique

Changez jumanMaxNum, knpMaxNum avec le code ci-dessous et comparez le temps d'exécution (ms). Dans l'environnement expérimental, le processeur est Ryzen 7 3700x et la taille du tas est de 32 Go.

Dans l'environnement expérimental, JUMAN et KNP de WSL sont appelés. (On dit que WSL est lent IO, donc il peut être un peu plus rapide dans d'autres environnements?)

  public static void main(String[] args) {
    long time = System.currentTimeMillis();

    KnpWrapperBuilder<KnpResult> knpWrapperBuilder = new KnpWrapperBuilder<>();
    int jumanMaxNum = 1;
    int knpMaxNum = 1;
    int textSize = 100;
    KnpWrapper<KnpResult> wrapper =
        knpWrapperBuilder
            .setJumanMaxNum(jumanMaxNum)
            .setKnpMaxNum(knpMaxNum)
            .setResultParser(new KnpResultParser())
            .start();
    var sampleText = "Je me suis inscrit au calendrier de l'Avent avec Nori," 
        + "Je ne vois aucun signe du temps donc aujourd'hui%Vous ne pouvez dormir qu'après avoir travaillé pendant d heures.";
    var texts =
        IntStream.range(0, textSize)
            .mapToObj(i -> String.format(sampleText, i))
            .collect(Collectors.toList());
    var results =
        texts
            .parallelStream()
            .map(wrapper::analyze)
            .flatMap(List::stream)
            .collect(Collectors.toList());

    System.out.println("time: " + (System.currentTimeMillis() - time));
    System.exit(0);
  }

résultat

jumanMaxNum knpMaxNum Première fois Deuxième fois Troisième fois 4e 5ème fois moyenne
1 1 17297 17320 17241 17159 17421 17287.6
1 5 2808 2764 2858 2791 2789 2802
5 1 20334 20211 19974 20037 20189 20149

Pour le moment, j'ai trouvé que JUMAN et KNP sont plus rapides lorsque KNP est exécuté dans plusieurs processus que lorsqu'ils sont exécutés dans un seul processus. En revanche, contrairement à KNP, qui est le goulot d'étranglement, le côté JUMAN semble ralentir s'il est trop augmenté.

Afin de voir à quel point le résultat diffère en fonction du nombre de processus de JUMAN et KNP, le nombre de textes a été augmenté de 100 à 500 et les combinaisons suivantes ont également été mesurées.

jumanMaxNum knpMaxNum Première fois Deuxième fois Troisième fois 4e 5ème fois moyenne
1 5 27953 27590 27674 27999 27669 27777
1 10 15825 16366 15118 15632 14931 15574.4
5 10 18704 17778 17355 16134 17254 17445
10 10 19514 19265 20459 19891 19233 19672.4
1 15 14533 22271 14187 21838 19794 18524.6
5 15 14149 14584 14929 15709 15228 14919.8
10 15 19313 17903 15478 18219 16740 17530.6
1 20 21620 14489 21960 20456 15671 18839.2
5 20 15899 15820 15713 14720 17053 15841
10 20 18850 15850 18461 18200 16357 17543.6

~~ Je ne sais pas. ~~ Pour le moment, la combinaison moyenne la plus rapide dans cet environnement était une combinaison de 5 processus pour JUMAN et 15 processus pour KNP. Cependant, j'estime que les combinaisons autour de [1, 10], [5, 15], [5, 20] sont dans la plage d'erreur.

Essayez également le résultat de la saisie de la commande suivante sur le terminal WSL.

time echo "Je l'ai enregistré sur le calendrier de l'Avent, mais je n'en vois aucun signe à temps, je dois donc travailler une heure aujourd'hui avant de pouvoir dormir." | jumanpp | knp -tab -print-num -anaphora
résultat Première fois Deuxième fois Troisième fois 4e 5ème fois moyenne
real 220 223 234 219 234 226
user 78 78 63 109 94 84.4
sys 125 125 141 78 125 118.8

prime

C'est amusant de voir le processeur tourner cpu.png

Recommended Posts

J'ai créé un Wrapper qui appelle KNP depuis Java
J'ai créé une classe qui peut utiliser JUMAN et KNP de Java
J'ai créé une application shopify @java
J'ai créé un MOD qui appelle instantanément un véhicule avec Minecraft
J'ai créé un nouvel outil de déploiement Java
J'ai créé un programme de jugement des nombres premiers en Java
J'ai essayé de frapper une méthode Java d'ABCL
J'ai créé un jeu Janken en Java (CLI)
J'ai créé une application de visualisation qui affiche le PDF
J'ai écrit un code de test (Junit & mockit) pour le code qui appelle l'API AWS (Java)
J'ai fait une roulette à Java.
[Débutant] J'ai créé un programme pour vendre des gâteaux en Java
J'ai créé un Dockerfile pour démarrer Glassfish 5 en utilisant Oracle Java
Création du framework JAVA "numatrix" qui génère facilement des valeurs numériques uniques dans un environnement distribué et multi-thread
J'ai essayé de créer un programme en Java qui résout le problème du voyageur de commerce avec un algorithme génétique
Une histoire que j'ai eu du mal à défier le pro de la concurrence avec Java
J'ai créé un PDF avec Java.
J'ai créé une interface graphique avec Swing
[Java] Précautions lors de la création d'un processus qui appelle une méthode de classe abstraite utilisant DI à partir d'une classe enfant
[Note] Ce que j'ai appris en six mois grâce à des inexpérimentés (Java)
Exécuter le fichier de commandes à partir de Java
Accéder à Teradata depuis une application Java
J'ai écrit une sorte de livre qui ressemble à Java
J'ai créé une source qui génère automatiquement un fichier de classe d'entité JPA
J'ai posé une question qui peut être utilisée pour des entretiens techniques
Création d'une méthode pour demander Premium Friday (version Java 8)
J'ai fait une simple fonction de recommandation.
[Note] Ce que j'ai appris en six mois grâce à des inexpérimentés (Java) (3)
J'ai créé une application correspondante (application Android)
Une histoire que j'ai finalement comprise Java pour une déclaration en tant que non-ingénieur
J'ai créé un outil de génération package.xml.
[Android] J'ai créé une application de podomètre.
J'ai essayé d'apprendre Java avec une série que les débutants peuvent comprendre clairement
[LINE BOT] J'ai créé un Ramen BOT avec Java (Maven) + Heroku + Spring Boot (1)
J'ai créé une classe qui génère automatiquement Json qui spécifie la texture de Minecraft [1.12.2]
J'ai créé un site qui résume les informations sur la restriction du sucre avec Vue.js
[Ruby] J'ai créé un simple client Ping
Un programme qui calcule la puissance de 2 à 100
Essayez d'exécuter Kubernetes Job à partir de Java
J'ai créé un plug-in pour IntelliJ IDEA
J'ai créé une application de calculatrice sur Android
J'ai créé une application Janken avec Android
Ce que j'ai appris du calcul monétaire Java
Si une personne de Java apprend PHP
Je vais exposer la putain d'application que j'ai rendue difficile pour obtenir un emploi d'ingénieur sans expérience.
J'ai créé un programme qui recherche la classe cible à partir du processus surchargé avec Java
J'ai créé un client API THETA qui peut être utilisé pour le développement de plugins
Apprendre Java avec Progate → Je vais vous expliquer parce que j'ai moi-même créé un jeu de base
J'ai eu du mal à faire du multithreading Java à partir de zéro, alors organisez-le
Connectez-vous à Aurora (MySQL) depuis une application Java
04. J'ai fait un frontal avec SpringBoot + Thymeleaf
J'ai fait de l'art de la mosaïque avec des images Pokemon
java j'ai essayé de casser un simple bloc
Pour devenir programmeur VB.net depuis une boutique Java
Je l'ai fait en Java pour toujours rendre (a == 1 && a == 2 && a == 3) vrai
Les débutants en programmation apprennent PHP à partir d'une perspective Java-variables-
J'ai écrit un programme de jugement des nombres premiers en Java
J'ai créé un conteneur Docker pour exécuter Maven