L'utilisation d'une API REST telle que API COTOHA nécessite trois processus.
Parmi ceux-ci, dans l'article précédent (Essayez d'utiliser l'analyse de syntaxe de l'API COTOHA en Java) Je faisais le traitement équivalent à 1. (Créer) et 2. (Envoyer), mais comme la réponse a été laissée sous forme d'un simple JSON, je vais la mapper à une classe Java.
DefaultKeywordWithDependency qui est un modèle qui implémente la dépendance dans NLP4J que je crée avec DIY /nlp4j/blob/master/nlp4j/nlp4j-core/src/main/java/nlp4j/impl/DefaultKeywordWithDependency.java) J'ai une classe, donc je vais la mapper ici. (Parce que c'est →, il n'est pas simplement mappé à la classe POJO.)
DefaultKeywordWithDependency https://github.com/oyahiroki/nlp4j/blob/master/nlp4j/nlp4j-core/src/main/java/nlp4j/impl/DefaultKeywordWithDependency.java
Parser
Le JSON du résultat de l'analyse syntaxique ressemble à ce qui suit. Le résultat de l'analyse syntaxique est des données arborescentes, mais on peut lire qu'il ne s'agit pas d'un arbre en termes de JSON.
{
"result": [
{
"chunk_info": {"id": 0,"head": 2,"dep": "D","chunk_head": 0,"chunk_func": 1,
"links": []
},
"tokens": [
{
"id": 0,"form": "aujourd'hui","kana": "aujourd'hui","lemma": "aujourd'hui","pos": "nom",
"features": ["Date et l'heure"],
"dependency_labels": [{"token_id": 1,"label": "case"}],
"attributes": {}
},
{
"id": 1,"form": "Est","kana": "C","lemma": "Est","pos": "Mots auxiliaires consécutifs",
"features": [],
"attributes": {}
}
]
},
{...(Abréviation)...},
{...(Abréviation)...}
]
}
],
"status": 0,
"message": ""
}
Voici la classe pour Perth. (Tout le code sera publié sur Maven Repository et Github)
package nlp4j.cotoha;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.HashMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import nlp4j.Keyword;
import nlp4j.impl.DefaultKeyword;
import nlp4j.impl.DefaultKeywordWithDependency;
/**
*API COTOHA analysant la réponse V1 JSON
*
* @author Hiroki Oya
* @since 1.0.0.0
*
*/
public class CotohaNlpV1ResponseHandler {
static private final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass());
/**
*Mots clés extraits comme racine de la syntaxe
*/
ArrayList<DefaultKeywordWithDependency> roots = new ArrayList<>();
/**
*Liste de mots-clés
*/
ArrayList<Keyword> keywords = new ArrayList<>();
/**
*Mot clé d'origine pour la phrase
*/
ArrayList<Keyword> chunkLinkKeywords = new ArrayList<>();
/**
* @clause de retour Mot clé d'origine
*/
public ArrayList<Keyword> getChunkLinkKeywords() {
return chunkLinkKeywords;
}
/**
*La source
*/
ArrayList<String> chunkLinks = new ArrayList<>();
/**
*La source
*/
JsonArray arrChunkLinks = new JsonArray();
/**
* Map: token_id --> Keyword
*/
HashMap<String, DefaultKeywordWithDependency> mapTokenidKwd = new HashMap<>();
/**
* Map: id --> Keyword
*/
HashMap<String, DefaultKeywordWithDependency> mapIdKwd = new HashMap<>();
/**
* token id --> sentence
*/
HashMap<Integer, Integer> idSentenceMap = new HashMap<>();
/**
*Mots clés dépendants
*/
ArrayList<DefaultKeyword> patternKeywords = new ArrayList<>();
/**
* @retour Kakemoto
*/
public JsonArray getArrChunkLinks() {
return arrChunkLinks;
}
/**
* @retour Kakemoto
*/
public ArrayList<String> getChunkLinks() {
return chunkLinks;
}
/**
* @carte de l'identifiant de retour et des mots clés
*/
public HashMap<String, DefaultKeywordWithDependency> getIdMapKwd() {
return mapIdKwd;
}
/**
* @return Mappage de l'ID d'élément de formulaire et du numéro de phrase
*/
public HashMap<Integer, Integer> getIdSentenceMap() {
return idSentenceMap;
}
/**
* @retour de la séquence de mots
*/
public ArrayList<Keyword> getKeywords() {
return keywords;
}
/**
* @carte de retour TOKEN ID et mots-clés
*/
public HashMap<String, DefaultKeywordWithDependency> getMapKwd() {
return mapTokenidKwd;
}
/**
* @renvoyer le mot-clé Kakeke
*/
public ArrayList<DefaultKeyword> getPatternKeywords() {
return patternKeywords;
}
/**
* @return Mot-clé de route de dépendance extrait
*/
public ArrayList<DefaultKeywordWithDependency> getRoots() {
return roots;
}
/**
* @param json COTOHA API Syntaxe analyse de la réponse JSON
*/
public void parse(String json) {
// JSON Parser
Gson gson = new Gson();
// COTOHA API RESPONSE
JsonObject result = gson.fromJson(json, JsonObject.class);
//L'ordre dans lequel ils apparaissent dans la phrase
int sequence = 0;
// {
// "result":[
// _{"chunk_info":{...},"tokens"[{...},{...},{...}]},
// _{"chunk_info":{...},"tokens"[{...},{...},{...}]},
// _{"chunk_info":{...},"tokens"[{...},{...},{...}]}
// ]
// }
// chunk_Un objet qui combine des informations et des jetons
JsonArray arrChunkTokens = result.getAsJsonArray("result");
int idxBegin = 0;
int idxSentence = 0;
// FOR EACH(chunk_tokens)
for (int idxChunkTokens = 0; idxChunkTokens < arrChunkTokens.size(); idxChunkTokens++) {
JsonObject chunk_token = arrChunkTokens.get(idxChunkTokens).getAsJsonObject();
// 1. chunk_objet d'information de clause info
// https://api.ce-cotoha.com/contents/reference/apireference.html#parsing_response_chunk
JsonObject chunk_info = chunk_token.get("chunk_info").getAsJsonObject();
logger.debug("chunk_info: " + chunk_info);
int chunk_head = -1;
{
//Numéro d'élément morphologique (0 origine)
String chunk_id = "" + chunk_info.get("id").getAsInt();
//Numéro de phrase de contact
chunk_head = chunk_info.get("head").getAsInt();
//Organisation des informations sources
// https://api.ce-cotoha.com/contents/reference/apireference.html#parsing_response_links
JsonArray links = chunk_info.get("links").getAsJsonArray();
for (int n = 0; n < links.size(); n++) {
JsonObject link = links.get(n).getAsJsonObject();
int link_link = link.get("link").getAsInt();
String link_label = link.get("label").getAsString();
chunkLinks.add(chunk_id + "/" + link_label + "/" + link_link);
arrChunkLinks.add(link);
}
}
// 2.jetons Objet d'information morphologique
// https://api.ce-cotoha.com/contents/reference/apireference.html#parsing_response_morpheme
JsonArray tokens = chunk_token.get("tokens").getAsJsonArray();
//POUR CHAQUE JETONS Objet d'information morphologique
for (int idxTokens = 0; idxTokens < tokens.size(); idxTokens++) {
JsonObject token = tokens.get(idxTokens).getAsJsonObject();
logger.debug("token: " + token);
// X-ID de style Y Quel élément morphologique dans une clause
String token_id = idxChunkTokens + "-" + idxTokens;
logger.debug("token_id: " + token_id);
String token_pos = token.get("pos") != null ? token.get("pos").getAsString() : null;
String token_lemma = token.get("lemma") != null ? token.get("lemma").getAsString() : null;
String token_form = token.get("form") != null ? token.get("form").getAsString() : null;
String token_kana = token.get("kana") != null ? token.get("kana").getAsString() : null;
//Est-ce le dernier des jetons? Si vrai, la destination de la dépendance est le jeton suivant
boolean isLastOfTokens = (idxTokens == tokens.size() - 1);
if (isLastOfTokens) {
logger.debug("Dernier jeton: chunk_head:" + chunk_head);
}
//Mots clés dépendants(Défini dans nlp4j)
DefaultKeywordWithDependency kw = new DefaultKeywordWithDependency();
//Numéros de série dans l'ordre dans lequel ils apparaissent dans le texte
kw.setSequence(sequence);
sequence++;
//Position de départ
kw.setBegin(idxBegin);
// lemma:Headword:Prototype
if (token_lemma != null) {
kw.setLex(token_lemma);
} else {
logger.warn("lemma is null");
}
int intId = token.get("id").getAsInt();
String id = "" + token.get("id").getAsInt();
idSentenceMap.put(intId, idxSentence);
//Que ce soit la fin d'une phrase
boolean isLastOfSentence = (chunk_head == -1 && idxTokens == tokens.size() - 1) //
|| (token_pos != null && token_pos.equals("Phrase"));
// IF(Fin de phrase)
if (isLastOfSentence) {
//numéro de l'instruction d'incrément
idxSentence++;
}
//set facet part paroles
kw.setFacet(token_pos);
//définir le type d'expression str
kw.setStr(token_form);
kw.setEnd(idxBegin + kw.getStr().length());
idxBegin += kw.getStr().length();
//définir la lecture de la lecture
kw.setReading(token_kana);
mapTokenidKwd.put(token_id, kw);
mapIdKwd.put(id, kw);
keywords.add(kw);
//étiquettes de dépendance Un tableau d'informations sur les dépendances
if (token.get("dependency_labels") != null) {
//Tableau d'informations dépendantes
JsonArray arrDependency = token.get("dependency_labels").getAsJsonArray();
for (int n = 0; n < arrDependency.size(); n++) {
//Informations sur les dépendances
JsonObject objDependency = arrDependency.get(n).getAsJsonObject();
String dependency_token_id = "" + objDependency.get("token_id").getAsInt();
//Définir les informations de dépendance pour les mots clés
kw.setDependencyKey(dependency_token_id);
}
}
} // END OF FOR EACH TOKENS
} // END OF FOR EACH (chunk_tokens)
// <Assembler l'arbre>
// FOR EACH(chunk_tokens)
for (int idxChunkTokens = 0; idxChunkTokens < arrChunkTokens.size(); idxChunkTokens++) {
JsonObject chunk_token = arrChunkTokens.get(idxChunkTokens).getAsJsonObject();
// 2. tokens
JsonArray tokens = chunk_token.get("tokens").getAsJsonArray();
// FOR (EACH TOKEN)
for (int idxTokens = 0; idxTokens < tokens.size(); idxTokens++) {
JsonObject token = tokens.get(idxTokens).getAsJsonObject();
String id = "" + token.get("id").getAsInt();
DefaultKeywordWithDependency kw = mapIdKwd.get(id);
// dependency labels
if (token.get("dependency_labels") != null) {
JsonArray arr_dependency_labels = token.get("dependency_labels").getAsJsonArray();
for (int n = 0; n < arr_dependency_labels.size(); n++) {
JsonObject dependency_label = arr_dependency_labels.get(n).getAsJsonObject();
String childID = "" + dependency_label.get("token_id").getAsInt();
String labelDependency = dependency_label.get("label").getAsString();
//Vérifiez s'il chevauche des phrases
int sentence1 = idSentenceMap.get(token.get("id").getAsInt());
int sentence2 = idSentenceMap.get(dependency_label.get("token_id").getAsInt());
//Ne chevauchez pas les phrases
if (mapIdKwd.get(childID) != null && (sentence1 == sentence2)) {
//Le parent et l'enfant sont inversés en japonais et en anglais
DefaultKeywordWithDependency kw1Child = mapIdKwd.get(childID);
DefaultKeywordWithDependency kw2Parent = kw;
kw2Parent.addChild(kw1Child);
kw1Child.setRelation(labelDependency);
if (kw1Child.getBegin() < kw2Parent.getBegin()) {
DefaultKeyword kwd = new DefaultKeyword();
kwd.setBegin(kw1Child.getBegin());
kwd.setEnd(kw2Parent.getEnd());
kwd.setLex(kw1Child.getLex() + " ... " + kw2Parent.getLex());
kwd.setFacet(labelDependency);
patternKeywords.add(kwd);
} else {
DefaultKeyword kwd = new DefaultKeyword();
kwd.setBegin(kw2Parent.getBegin());
kwd.setEnd(kw1Child.getEnd());
kwd.setLex(kw2Parent.getLex() + " ... " + kw1Child.getLex());
kwd.setFacet(labelDependency);
patternKeywords.add(kwd);
}
} //
}
}
} // END OF FOR EACH TOKEN
} // END OF FOR EACH (chunk_tokens)
for (String link : chunkLinks) {
String id1 = link.split("/")[0];
String relation = link.split("/")[1];
String id2 = link.split("/")[2];
Keyword kwd1 = mapTokenidKwd.get(id1 + "-0");
Keyword kwd2 = mapTokenidKwd.get(id2 + "-0");
String lex1 = kwd1.getLex();
String lex2 = kwd2.getLex();
DefaultKeyword kwd = new DefaultKeyword();
kwd.setBegin(kwd1.getBegin());
kwd.setEnd(kwd2.getEnd());
kwd.setLex(lex2 + " ... " + lex1);
kwd.setStr(kwd.getLex());
kwd.setFacet(relation);
chunkLinkKeywords.add(kwd);
}
// </Assembler l'arbre>
for (String key : mapIdKwd.keySet()) {
DefaultKeywordWithDependency kw = mapIdKwd.get(key);
// IF(S'il s'agit d'un mot-clé racine)
if (kw.getParent() == null) {
roots.add(kw);
}
}
} // end of parse()
}
Concernant l'analyse syntaxique de l'API COTOHA, si vous analysez deux phrases telles que «C'est une belle journée aujourd'hui. Je vais à l'école demain». (Veuillez préciser si la reconnaissance est erronée)
Sur la page de démonstration d'analyse, deux phrases sont divisées, mais cela semble traiter l'analyse syntaxique après avoir séparé les phrases avec des signes de ponctuation à l'avance.
Par conséquent, cet analyseur compte à l'avance le "nombre de phrases" comme suit.
J'essaye d'ignorer la dépendance entre les phrases.
En tant que TestCase, j'analyserai le JSON qui a enregistré le résultat de l'API d'analyse syntaxique COTOHA et je le sortirai sous forme de caractère. (Sortie prévue sur Github à une date ultérieure)
File file = new File("src/test/resources/nlp_v1_parse_002.json");
String json = FileUtils.readFileToString(file, "UTF-8");
CotohaNlpV1ResponseHandler handler = new CotohaNlpV1ResponseHandler();
handler.parse(json);
for (DefaultKeywordWithDependency root : handler.getRoots()) {
System.err.println(root.toStringAsDependencyTree());
}
System.err.println("---");
for (Keyword kwd : handler.getKeywords()) {
System.err.println(kwd.getLex() + " (" + "word." + kwd.getFacet() + ")");
System.err.println("\t" + kwd);
}
System.err.println("---");
for (Keyword kwd : handler.getPatternKeywords()) {
System.err.println(kwd.getLex() + " (" + "pattern." + kwd.getFacet() + ")");
System.err.println("\t" + kwd);
}
System.err.println("---");
for (Keyword kwd : handler.getChunkLinkKeywords()) {
System.err.println(kwd.getLex() + " (" + "pattern." + kwd.getFacet() + ")");
System.err.println("\t" + kwd);
}
Cela ressemble à ce qui suit. Il est difficile de lire s'il s'agit de JSON brut, mais j'ai essayé de le sortir sous forme d'arbre. L'état de la dépendance est devenu plus facile à comprendre.
-sequence=11,lex=aller,str=ligne,relation=null
-sequence=7,lex=demain,str=demain,relation=nmod
-sequence=8,lex=Est,str=Est,relation=case
-sequence=9,lex=école,str=école,relation=nmod
-sequence=10,lex=À,str=À,relation=case
-sequence=12,lex=Ki,str=Ki,relation=aux
-sequence=13,lex=Masu,str=Masu,relation=aux
-sequence=14,lex=。,str=。,relation=punct
-sequence=4,lex=Météo,str=Météo,relation=null
-sequence=0,lex=aujourd'hui,str=aujourd'hui,relation=nmod
-sequence=1,lex=Est,str=Est,relation=case
-sequence=2,lex=Bien,str=je,relation=amod
-sequence=3,lex=je,str=je,relation=aux
-sequence=5,lex=est,str=est,relation=cop
-sequence=6,lex=。,str=。,relation=punct
---
aujourd'hui(word.nom)
aujourd'hui[relation=nmod, sequence=0, dependencyKey=1, hasChildren=true, hasParent=false, facet=nom, lex=aujourd'hui, str=aujourd'hui, reading=aujourd'hui, begin=0, end=2]
Est(word.Mots auxiliaires consécutifs)
Est[relation=case, sequence=1, dependencyKey=null, hasChildren=false, hasParent=false, facet=Mots auxiliaires consécutifs, lex=Est, str=Est, reading=C, begin=2, end=3]
Bien(word.Adjectif radical)
Bien[relation=amod, sequence=2, dependencyKey=3, hasChildren=true, hasParent=false, facet=Adjectif radical, lex=Bien, str=je, reading=je, begin=3, end=4]
je(word.Suffixe d'adjectif)
je[relation=aux, sequence=3, dependencyKey=null, hasChildren=false, hasParent=false, facet=Suffixe d'adjectif, lex=je, str=je, reading=je, begin=4, end=5]
Météo(word.nom)
Météo[relation=null, sequence=4, dependencyKey=6, hasChildren=true, hasParent=true, facet=nom, lex=Météo, str=Météo, reading=Météo, begin=5, end=7]
est(word.Jugement)
est[relation=cop, sequence=5, dependencyKey=null, hasChildren=false, hasParent=false, facet=Jugement, lex=est, str=est, reading=mort, begin=7, end=9]
。 (word.Phrase)
。 [relation=punct, sequence=6, dependencyKey=null, hasChildren=false, hasParent=false, facet=Phrase, lex=。, str=。, reading=, begin=9, end=10]
demain(word.nom)
demain[relation=nmod, sequence=7, dependencyKey=8, hasChildren=true, hasParent=false, facet=nom, lex=demain, str=demain, reading=Cul, begin=10, end=12]
Est(word.Mots auxiliaires consécutifs)
Est[relation=case, sequence=8, dependencyKey=null, hasChildren=false, hasParent=false, facet=Mots auxiliaires consécutifs, lex=Est, str=Est, reading=C, begin=12, end=13]
école(word.nom)
école[relation=nmod, sequence=9, dependencyKey=10, hasChildren=true, hasParent=false, facet=nom, lex=école, str=école, reading=Gakkou, begin=13, end=15]
À(word.Assistant de cas)
À[relation=case, sequence=10, dependencyKey=null, hasChildren=false, hasParent=false, facet=Assistant de cas, lex=À, str=À, reading=ré, begin=15, end=16]
aller(word.Tronc de verbe)
aller[relation=null, sequence=11, dependencyKey=14, hasChildren=true, hasParent=true, facet=Tronc de verbe, lex=aller, str=ligne, reading=je, begin=16, end=17]
Ki(word.Fin d'utilisation verbale)
Ki[relation=aux, sequence=12, dependencyKey=null, hasChildren=false, hasParent=false, facet=Fin d'utilisation verbale, lex=Ki, str=Ki, reading=Ki, begin=17, end=18]
Masu(word.Suffixe de verbe)
Masu[relation=aux, sequence=13, dependencyKey=null, hasChildren=false, hasParent=false, facet=Suffixe de verbe, lex=Masu, str=Masu, reading=truite, begin=18, end=20]
。 (word.Phrase)
。 [relation=punct, sequence=14, dependencyKey=null, hasChildren=false, hasParent=false, facet=Phrase, lex=。, str=。, reading=, begin=20, end=21]
---
aujourd'hui...Est(pattern.case)
aujourd'hui...Est[sequence=-1, facet=case, lex=aujourd'hui...Est, str=null, reading=null, count=-1, begin=0, end=3, correlation=0.0]
Bien...je(pattern.aux)
Bien...je[sequence=-1, facet=aux, lex=Bien...je, str=null, reading=null, count=-1, begin=3, end=5, correlation=0.0]
aujourd'hui...Météo(pattern.nmod)
aujourd'hui...Météo[sequence=-1, facet=nmod, lex=aujourd'hui...Météo, str=null, reading=null, count=-1, begin=0, end=7, correlation=0.0]
Bien...Météo(pattern.amod)
Bien...Météo[sequence=-1, facet=amod, lex=Bien...Météo, str=null, reading=null, count=-1, begin=3, end=7, correlation=0.0]
Météo...est(pattern.cop)
Météo...est[sequence=-1, facet=cop, lex=Météo...est, str=null, reading=null, count=-1, begin=5, end=9, correlation=0.0]
Météo... 。 (pattern.punct)
Météo... 。 [sequence=-1, facet=punct, lex=Météo... 。, str=null, reading=null, count=-1, begin=5, end=10, correlation=0.0]
demain...Est(pattern.case)
demain...Est[sequence=-1, facet=case, lex=demain...Est, str=null, reading=null, count=-1, begin=10, end=13, correlation=0.0]
école...À(pattern.case)
école...À[sequence=-1, facet=case, lex=école...À, str=null, reading=null, count=-1, begin=13, end=16, correlation=0.0]
demain...aller(pattern.nmod)
demain...aller[sequence=-1, facet=nmod, lex=demain...aller, str=null, reading=null, count=-1, begin=10, end=17, correlation=0.0]
école...aller(pattern.nmod)
école...aller[sequence=-1, facet=nmod, lex=école...aller, str=null, reading=null, count=-1, begin=13, end=17, correlation=0.0]
aller...Ki(pattern.aux)
aller...Ki[sequence=-1, facet=aux, lex=aller...Ki, str=null, reading=null, count=-1, begin=16, end=18, correlation=0.0]
aller...Masu(pattern.aux)
aller...Masu[sequence=-1, facet=aux, lex=aller...Masu, str=null, reading=null, count=-1, begin=16, end=20, correlation=0.0]
aller... 。 (pattern.punct)
aller... 。 [sequence=-1, facet=punct, lex=aller... 。, str=null, reading=null, count=-1, begin=16, end=21, correlation=0.0]
---
aujourd'hui...Météo(pattern.time)
aujourd'hui...Météo[sequence=-1, facet=time, lex=aujourd'hui...Météo, str=aujourd'hui...Météo, reading=null, count=-1, begin=5, end=2, correlation=0.0]
Bien...Météo(pattern.adjectivals)
Bien...Météo[sequence=-1, facet=adjectivals, lex=Bien...Météo, str=Bien...Météo, reading=null, count=-1, begin=5, end=4, correlation=0.0]
Météo...aller(pattern.manner)
Météo...aller[sequence=-1, facet=manner, lex=Météo...aller, str=Météo...aller, reading=null, count=-1, begin=16, end=7, correlation=0.0]
demain...aller(pattern.time)
demain...aller[sequence=-1, facet=time, lex=demain...aller, str=demain...aller, reading=null, count=-1, begin=16, end=12, correlation=0.0]
école...aller(pattern.goal)
école...aller[sequence=-1, facet=goal, lex=école...aller, str=école...aller, reading=null, count=-1, begin=16, end=15, correlation=0.0]
Être facile à gérer en tant que classe Java signifie qu'il est également facile à gérer pour une utilisation professionnelle. Analyser le résultat de l'analyse syntaxique est un problème, mais dans le monde des affaires, ** voici le jeu **.
Recommended Posts