Ich habe einen Wrapper erstellt, der KNP von Java aus aufruft

Artikelinhalt, der in 3 Zeilen verstanden werden kann

Überblick

Vor kurzem stürzt die Welle von Deep in den NLP-Bereich, daher sagen einige Leute vielleicht: "Ich benutze KNP heutzutage nicht mehr." Wenn Sie jedoch keine anständigen Daten haben und keine gängigen Methoden verwenden können oder wenn Sie die regelbasierten Ergebnisse vor dem Ausprobieren der Deep-Methode ausprobieren möchten, gibt es zahlreiche Fallanalyseergebnisse und verschiedene Funktionen. KNP ist immer noch nützlich, weil es Ihnen viele Informationen gibt.

Auf der anderen Seite kann es nicht wie Sudachi als Bibliothek verwendet werden, so dass die Verwendung in einem Programm problematisch sein kann (insbesondere in anderen Sprachen als Python mit pyKNP).

Also habe ich dieses Mal eine Wrapper-Bibliothek erstellt, die KNP (und Human ++) von Java aus aufruft. (https://github.com/Natsuume/knp4j) ~~ Ich wollte es im Maven-Repository veröffentlichen, konnte es aber nicht rechtzeitig erstellen, daher werde ich es bald im Maven-Repository veröffentlichen. ~~

~~ Obwohl wir den Vorgang bis zu einem gewissen Grad bestätigt haben, besteht außerdem die Möglichkeit, dass Probleme auftreten, weil wir keinen ordnungsgemäßen Test geschrieben haben ~~

Nachtrag

Veröffentlicht in Maven Central. Jetzt verfügbar bei Maven, Gradle usw.

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'

Wie benutzt man

Es ist fast so wie README.md auf Github.

Sample.java


//Builder zum Erstellen von KNPWrapper
ResultParser<KnpResult> knpResultParser = new KnpResultParser();
KnpWrapperBuilder<KnpResult> knpWrapperBuilder = new KnpWrapperBuilder<>();
KnpWrapper<KnpResult> wrapper = knpWrapperBuilder
    .setJumanCommand(List.of("bash", "-c", "jumanpp")) //Juman Hinrichtungsbefehl
    .setKnpCommand(List.of("bash", "-c", "knp -tab -print-num -anaphora")) //KNP-Ausführungsbefehl(Zur Zeit"-tab」「-print-num」「-Anaphora "Option erforderlich)
    .setJumanMaxNum(1) //Maximale Anzahl menschlicher Prozesse, die gleichzeitig gestartet werden sollen
    .setJumanStartNum(1) //Anzahl der menschlichen Prozesse, die bei der Initialisierung beginnen sollen
    .setKnpMaxNum(1) //Maximale Anzahl von KNP-Prozessen, die gleichzeitig gestartet werden sollen
    .setKnpStartNum(1) //Anzahl der bei der Initialisierung gestarteten KNP-Prozesse
    .setRetryNum(0) //Anzahl der Wiederholungen, wenn die Ergebniserfassung fehlschlägt
    .setResultParser(knpResultParser) //Liste der Ausgabeergebnisse<String>Stellen Sie Parser so ein, dass er in eine beliebige Klasse konvertiert wird
    .start();
var texts = List.of(
    "Testtext 1",
    "Testtext 2",
    "Testtext 3"
);
texts.parallelStream().map(wrapper::analyze)
    .flatMap(List::stream)
    .map(KnpResult::getSurfaceForm)
    .forEach(System.out::println);

Geben Sie mit "KnpWrapperBuilder" verschiedene Einstellungen ein und generieren und starten Sie KnpWrapper zum ersten Mal mit "start ()". Geben Sie setJumanCommand, setKnpCommand die gleichen Befehle, die Sie ProcessBuilder geben. Abhängig von der Umgebung kann möglicherweise nur mit den Pfaden JUMAN und KNP ausgeführt werden. (In meiner Umgebung musste ich JUMANPP, KNP in der WSL aufrufen, also gab ich einen Befehl wie im obigen Beispiel)

Für andere Einstellungen als "setResultParser ()" sind die Inhalte des obigen Beispiels die Standardwerte.

Funktion

In mehreren Prozessen ausführen

Richten Sie mehrere Prozesse ein und verwenden Sie sie erneut. Die Anzahl der Prozesse, die gleichzeitig eingerichtet werden können, kann für JUMAN und KNP frei festgelegt werden.

JUMAN und KNP haben einen Servermodus, der derzeit jedoch nicht unterstützt wird (wird in Zukunft unterstützt).

Erneute Ausführung, wenn die Analyse fehlschlägt

Grundsätzlich wird davon ausgegangen, dass es nicht verwendet wird. Wenn jedoch in einer Reihe von Prozessen eine "IOException" oder "InterruptedException" auftritt, wird der Prozess, in dem die Ausnahme aufgetreten ist, beendet und ein anderer Prozess versucht erneut, sie zu analysieren.

Ergebnis Parser

Jeder Parser, der die ResultParser-Schnittstelle implementiert, kann als Ausgabe-Parser verwendet werden. Die folgenden zwei Arten von Methoden sind für "ResultParser" definiert.

ResultParser.java


public interface ResultParser<OutputT> {

  /**
   *Gibt eine beliebige Instanz mit dem Analyseergebnis von Knp als Eingabe zurück.
   *
   * @Parameterliste Knp-Analyseergebnis
   * @Rückgabeinstanz, die das Analyseergebnis darstellt
   */
  OutputT parse(List<String> list);

  /**
   *Gibt die Instanz zurück, die verwendet werden soll, wenn die Analyse fehlschlägt.
   *
   * @return Instanz, die zurückgegeben wird, wenn die Analyse fehlschlägt
   */
  OutputT getInvalidResult();
}

getInvalidResult () ist eine Methode, die eine Instanz zurückgibt, wenn kein normales Analyseergebnis erhalten werden kann. Es wird verwendet, wenn die erneute Ausführung bei der obigen Ausnahme fehlschlägt oder wenn KNP nicht analysiert (KNP kann nicht analysieren, ob die halbe Breite "+", "*" enthalten ist).

Überprüfen Sie, ob es schneller als ein einzelner Prozess ist

Ändern Sie jumanMaxNum, knpMaxNum mit dem folgenden Code und vergleichen Sie die Ausführungszeit (ms). In der experimentellen Umgebung ist die CPU Ryzen 7 3700x und die Heap-Größe 32 GB.

In der experimentellen Umgebung werden JUMAN und KNP der WSL aufgerufen. (WSL soll langsames E / A sein, daher kann es in anderen Umgebungen etwas schneller sein?)

  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 = "Ich habe mich im Adventskalender bei Nori registriert," 
        + "Ich sehe heute kein Zeichen von Zeit%Sie können erst nach d Stunden arbeiten.";
    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);
  }

Ergebnis

jumanMaxNum knpMaxNum Erstes Mal Zweites Mal Drittes Mal 4 .. 5. Mal durchschnittlich
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

Vorerst stellte ich fest, dass sowohl JUMAN als auch KNP schneller sind, wenn KNP in mehreren Prozessen ausgeführt wird, als wenn sie in einem einzelnen Prozess ausgeführt werden. Im Gegensatz zu KNP, dem Engpass, scheint sich die JUMAN-Seite zu verlangsamen, wenn sie zu stark erhöht wird.

Um zu sehen, wie stark sich das Ergebnis in Abhängigkeit von der Anzahl der Prozesse von JUMAN und KNP unterscheidet, wurde die Anzahl der Texte von 100 auf 500 erhöht und die folgenden Kombinationen zusätzlich gemessen.

jumanMaxNum knpMaxNum Erstes Mal Zweites Mal Drittes Mal 4 .. 5. Mal durchschnittlich
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

~~ Ich weiß es nicht. ~~ Derzeit war die schnellste durchschnittliche Kombination in dieser Umgebung eine Kombination aus 5 Prozessen für JUMAN und 15 Prozessen für KNP. Ich bin jedoch der Meinung, dass die Kombinationen um [1, 10], [5, 15], [5, 20] im Fehlerbereich liegen.

Versuchen Sie auch das Ergebnis der Eingabe des folgenden Befehls auf dem WSL-Terminal.

time echo "Ich habe es im Adventskalender registriert, aber ich sehe keine Anzeichen dafür rechtzeitig, deshalb muss ich heute eine Stunde arbeiten, bevor ich schlafen kann." | jumanpp | knp -tab -print-num -anaphora
Ergebnis Erstes Mal Zweites Mal Drittes Mal 4 .. 5. Mal durchschnittlich
real 220 223 234 219 234 226
user 78 78 63 109 94 84.4
sys 125 125 141 78 125 118.8

Bonus

Es macht Spaß zu sehen, wie sich die CPU dreht cpu.png

Recommended Posts

Ich habe einen Wrapper erstellt, der KNP von Java aus aufruft
Ich habe eine Klasse erstellt, die JUMAN und KNP aus Java verwenden kann
Ich habe eine shopify App @java erstellt
Ich habe einen MOD erstellt, der sofort ein Fahrzeug mit Minecraft anruft
Ich habe ein neues Java-Bereitstellungstool erstellt
Ich habe ein Programm zur Beurteilung von Primzahlen in Java erstellt
Ich habe versucht, eine Java-Methode von ABCL zu verwenden
Ich habe ein Janken-Spiel in Java (CLI) gemacht.
Ich habe eine Viewer-App erstellt, die PDF anzeigt
Ich habe einen Testcode (Junit & mockit) für den Code geschrieben, der die AWS-API (Java) aufruft.
Ich habe ein Roulette in Java gemacht.
[Anfänger] Ich habe ein Programm zum Verkauf von Kuchen in Java erstellt
Ich habe eine Docker-Datei erstellt, um Glassfish 5 mit Oracle Java zu starten
Erstellt das JAVA-Framework "numatrix", das auf einfache Weise eindeutige numerische Werte in einer verteilten Umgebung und mit mehreren Threads generiert
Ich habe versucht, ein Programm in Java zu erstellen, das das Problem des Handlungsreisenden mit einem genetischen Algorithmus löst
Eine Geschichte, die ich mit Java nur schwer herausfordern konnte
Ich habe ein PDF mit Java erstellt.
Ich habe mit Swing eine GUI erstellt
[Java] Vorsichtsmaßnahmen beim Erstellen eines Prozesses, der eine Methode der Abstract-Klasse mit DI aus einer untergeordneten Klasse aufruft
[Anmerkung] Was ich in einem halben Jahr von unerfahrenen (Java) gelernt habe
Führen Sie eine Batchdatei von Java aus
Greifen Sie über eine Java-Anwendung auf Teradata zu
Ich habe eine Sterling-Sorte geschrieben, die sich wie in Java anfühlt
Ich habe eine Quelle erstellt, die automatisch eine JPA-Entitätsklassendatei generiert
Erstellt eine Methode zum Anfordern von Premium Friday (Java 8-Version)
Ich habe eine einfache Empfehlungsfunktion erstellt.
[Anmerkung] Was ich in einem halben Jahr von unerfahrenen (Java) gelernt habe (3)
Ich habe eine passende App erstellt (Android App)
Eine Geschichte, die ich als Nicht-Ingenieur endlich verstanden habe
Ich habe ein Tool zur Generierung von package.xml erstellt.
[Android] Ich habe eine Schrittzähler-App erstellt.
Ich habe versucht, Java mit einer Reihe zu lernen, die Anfänger klar verstehen können
[LINE BOT] Ich habe einen Ramen BOT mit Java (Maven) + Heroku + Spring Boot (1) gemacht.
Ich habe eine Klasse erstellt, die automatisch Json generiert, die die Textur von Minecraft angibt [1.12.2]
Ich habe mit Vue.js eine Seite erstellt, die Informationen zur Zuckereinschränkung zusammenfasst
[Ruby] Ich habe einen einfachen Ping-Client erstellt
Ein Programm, das die Leistung von 2 bis 100 berechnet
Versuchen Sie, Kubernetes Job von Java aus auszuführen
Ich habe ein Plug-In für IntelliJ IDEA erstellt
Ich habe eine Taschenrechner-App für Android erstellt
Ich habe eine Janken App mit Android gemacht
Was ich aus der Java-Geldberechnung gelernt habe
Wenn eine Person aus Java PHP lernt
Ich werde die verdammte App enthüllen, die ich mir schwer gemacht habe, einen Job als Ingenieur von Unerfahrenen zu bekommen.
Ich habe ein Programm erstellt, das aus dem mit Java überladenen Prozess nach der Zielklasse sucht
Ich habe einen THETA API-Client erstellt, der für die Plug-Entwicklung verwendet werden kann
Lerne Java mit Progate → Ich werde es erklären, weil ich selbst ein einfaches Spiel gemacht habe
Es fiel mir schwer, Java-Multithreading von Grund auf neu zu erstellen. Organisieren Sie es also
Stellen Sie über eine Java-Anwendung eine Verbindung zu Aurora (MySQL) her
04. Ich habe mit SpringBoot + Thymeleaf ein Frontend gemacht
Ich habe Mosaikkunst mit Pokemon-Bildern gemacht
Java Ich habe versucht, einen einfachen Block zu brechen
Um ein VB.net-Programmierer aus einem Java-Shop zu werden
Ich habe Java gemacht, um (a == 1 && a == 2 && a == 3) immer wahr zu machen
Programmieranfänger lernen PHP aus einer Java-Perspektive.
Ich habe ein Programm zur Beurteilung von Primzahlen in Java geschrieben
Ich habe einen Docker-Container erstellt, um Maven auszuführen