Sprachverarbeitung 100 Schläge 2015 ["Kapitel 5: Abhängigkeitsanalyse"](http: //www.cl.ecei. Es ist eine Aufzeichnung von 49. "Extraktion des Abhängigkeitspfads zwischen Nomenklatur" von tohoku.ac.jp/nlp100/#ch5). .. Es ist endlich der Wendepunkt von 100 Schlägen. Hier ist ** ein bösartiger Schlag, der zu einem Dämonentor wird **. ** Das Problem ist schwer zu verstehen, und selbst wenn Sie es verstehen, ist es schwierig, es zu lösen **. Es fühlt sich an, als hätte ich 100 beendet, aber es ist das schwierigste Problem für meinen eigenen Algorithmus (es gibt andere kompliziertere, aber ich überlasse es im Grunde dem Paket, damit ich keine großen Probleme habe).
Verknüpfung | Bemerkungen |
---|---|
049.Extraktion von Abhängigkeitspfaden zwischen Nomenklaturen.ipynb | Antwortprogramm GitHub Link |
100 Klicks Amateur-Sprachverarbeitung:49 | Kopieren Sie die Quelle vieler Quellteile und fügen Sie sie ein |
CaboCha Beamter | CaboCha Seite zuerst anzuschauen |
Ich habe CRF ++ und CaboCha vor zu langer Zeit installiert und vergessen, wie man sie installiert. Da es sich um ein Paket handelt, das überhaupt nicht aktualisiert wurde, haben wir die Umgebung nicht neu erstellt. Ich erinnere mich nur, dass ich frustriert war, als ich mich für CaboCha unter Windows entschied. Ich glaube, ich konnte es unter 64-Bit-Windows nicht verwenden (ich habe einen vagen Speicher und möglicherweise liegt ein Problem mit meinen technischen Fähigkeiten vor).
Art | Ausführung | Inhalt |
---|---|---|
OS | Ubuntu18.04.01 LTS | Es läuft virtuell |
pyenv | 1.2.16 | Ich benutze pyenv, weil ich manchmal mehrere Python-Umgebungen benutze |
Python | 3.8.1 | python3 auf pyenv.8.Ich benutze 1 Pakete werden mit venv verwaltet |
Mecab | 0.996-5 | apt-Installieren Sie mit get |
CRF++ | 0.58 | Es ist zu alt und ich habe vergessen, wie man es installiert(Vielleichtmake install ) |
CaboCha | 0.69 | Es ist zu alt und ich habe vergessen, wie man es installiert(Vielleichtmake install ) |
Wenden Sie den Abhängigkeitsanalysator CaboCha auf "Ich bin eine Katze" an und erleben Sie die Funktionsweise des Abhängigkeitsbaums und der syntaktischen Analyse.
Klasse, Abhängigkeitsanalyse, CaboCha, Klausel, Abhängigkeit, Groß- / Kleinschreibung, funktionale Verbsyntax, Abhängigkeitspfad, [Graphviz](http: / /www.graphviz.org/)
Verwenden von CaboCha für den Text (neko.txt) von Natsume Sosekis Roman "Ich bin eine Katze" Analysieren Sie die Abhängigkeit und speichern Sie das Ergebnis in einer Datei namens neko.txt.cabocha. Verwenden Sie diese Datei, um ein Programm zu implementieren, das die folgenden Fragen beantwortet.
Extrahieren Sie den kürzesten Abhängigkeitspfad, der alle Nomenklaturpaare im Satz verbindet. Wenn jedoch die Klauselnummern des Nomenklaturpaars * i * und * j * (* i * <* j *) sind, muss der Abhängigkeitspfad die folgenden Spezifikationen erfüllen.
- Ähnlich wie bei Problem 48 wird der Pfad durch Verketten der Ausdrücke (oberflächenmorphologischen Elemente) jeder Phrase von der Startklausel bis zur Endklausel mit "
->
" ausgedrückt.- Ersetzen Sie die in den Abschnitten * i * und * j * enthaltene Nomenklatur durch X bzw. Y.
Darüber hinaus kann die Form des Abhängigkeitspfads auf zwei Arten betrachtet werden.
--Wenn Klausel * j * im Pfad von Klausel * i * zum Stammverzeichnis des Syntaxbaums vorhanden ist: Zeigen Sie den Pfad von Klausel * i * zu Klausel * j * an
- Anders als oben, wenn sich die Phrase * i * und die Klausel * j * an einer gemeinsamen Klausel * k * auf dem Pfad von der Klausel * j * zur Wurzel des Syntaxbaums überschneiden: Der Pfad und die Klausel * unmittelbar vor der Klausel * i * zur Klausel * k * Der Pfad von j * bis kurz vor der Klausel * k * und der Inhalt der Klausel * k * werden mit "
|
" verkettet und angezeigt.Aus dem Satz "Ich habe hier zum ersten Mal einen Menschen gesehen" (8. Satz von neko.txt.cabocha) sollte beispielsweise die folgende Ausgabe erhalten werden.
X ist|In Y.->Beginnen mit->Mensch->Dinge|sah X ist|Genannt Y.->Dinge|sah X ist|Y.|sah In X.->Beginnen mit-> Y In X.->Beginnen mit->Mensch-> Y X genannt-> Y
Der Klopfinhalt ist lang und schwer zu verstehen. Artikel ["Amateur-Sprachverarbeitung 100 Schläge: 49"](https://qiita.com/segavvy/items/dfbf9d5dd5885e807d49#%E5%95%8F%E9%A1%8C%E3%81%AE%E6% Ich habe es endlich verstanden, nachdem ich 84% 8F% E5% 91% B3) gesehen hatte. Ich werde meine Erklärung weglassen ...
Es gibt 150 Zeilen ... Es scheint, dass es ein wenig einfacher gemacht werden kann, wenn es Zeit braucht, aber ich bin ratlos.
import re
#Trennzeichen
separator = re.compile('\t|,')
#Abhängigkeit
dependancy = re.compile(r'''(?:\*\s\d+\s) #Nicht erfassbar
(-?\d+) #Zahlen(Kontakt)
''', re.VERBOSE)
NOUN_REPLACE = '@@noun@@'
SEP1 = ' -> '
SEP2 = ' | '
class Morph:
def __init__(self, line):
#Durch Tabulator und Komma teilen
cols = separator.split(line)
self.surface = cols[0] #Oberflächentyp(surface)
self.base = cols[7] #Grundform(base)
self.pos = cols[1] #Teil(pos)
self.pos1 = cols[2] #Teiltexte Unterklassifizierung 1(pos1)
class Chunk:
def __init__(self, morphs, dst):
self.morphs = morphs
self.dst = dst #Indexnummer der Kontaktklausel
self.dsts = []
self.phrase = ''
self.phrase_noun = ''
self.noun = False
for morph in morphs:
if morph.pos != 'Symbol':
self.phrase += morph.surface #Für Nicht-Symbole
if morph.pos == 'Substantiv':
#Wenn es die erste Nase ist, behandeln Sie sie als Nomenklatur
if self.noun == False:
self.noun = True
self.phrase_noun += NOUN_REPLACE
continue
#Wenn es sich bei der vorherigen um eine Nomenklatur handelt, ersetzen Sie das Oberflächensystem nicht
elif self.noun and self.phrase_noun.endswith(NOUN_REPLACE):
continue
#Fügen Sie das Oberflächensystem mit Ausnahme des Austauschs und des Überspringens so hinzu, wie es ist
self.phrase_noun += morph.surface
morphs = []
chunks = []
sentences = []
with open('./neko.txt.cabocha') as f:
for line in f:
dependancies = dependancy.match(line)
#Wenn es sich nicht um ein EOS- oder Abhängigkeitsanalyseergebnis handelt
if not (line == 'EOS\n' or dependancies):
morphs.append(Morph(line))
#Wenn es ein morphologisches Analyseergebnis im EOS- oder Abhängigkeitsanalyseergebnis gibt
elif len(morphs) > 0:
chunks.append(Chunk(morphs, dst))
morphs = []
#Im Falle eines Abhängigkeitsergebnisses
if dependancies:
dst = int(dependancies.group(1))
#Wenn es eine Abhängigkeit gibt, führt dies zu EOS
if line == 'EOS\n' and len(chunks) > 0:
#Kontakte hinzufügen(Fügen Sie die letzte Zeile für die Effizienz hinzu)
for chunk in chunks[::-1]:
if chunk.dst!= -1:
chunk.dsts.extend([chunk.dst])
if chunks[chunk.dst].dst != -1:
chunk.dsts.extend(chunks[chunk.dst].dsts)
sentences.append(chunks)
chunks = []
def output_result(i, chunk, sentence):
print('\tX:', i, chunk.phrase, chunk.dsts)
#Nomenklatur(X)Schleife, um nach Y zu suchen
for i_y, y_chunk in enumerate(sentence[i+1:], i+1):
#Y für Nomenklatur
if y_chunk.noun:
#Y Informationsausgabe
print('\t\tY:', i_y, y_chunk.phrase)
result = ''
#Y ist die Route(Kontakt)Wenn in enthalten
if i_y in chunk.dsts:
result = '\t\t\tP1:' + '\t' + sentence[i].phrase_noun.replace(NOUN_REPLACE, 'X') + SEP1
#Ausgabe von X nach Y.
for i_path in chunk.dsts:
#Beenden Sie, wenn Y erreicht ist
if i_path == i_y:
result += 'Y'
break
else:
result += sentence[i_path].phrase + SEP1
#Y ist die Route(Kontakt)Wenn nicht enthalten in
else:
result = '\t\t\tP2:' + '\t' + sentence[i].phrase_noun.replace(NOUN_REPLACE, 'X')
#Suchen Sie eine allgemeine Klausel mit dem Mindestwert im Produktsatz
i_goal = min(set(chunk.dsts).intersection(set(sentence[i_y].dsts)))
#Von X nach Y vor der gemeinsamen Phrase
for i_path in chunk.dsts:
if i_path == i_goal:
result += SEP2 + sentence[i_y].phrase_noun.replace(NOUN_REPLACE, 'Y')
break
else:
result += SEP1 + sentence[i_path].phrase
#Von der für Y verantwortlichen Person bis zur allgemeinen Phrase
for i_path in sentence[i_y].dsts:
if i_path == i_goal:
result += SEP2 + sentence[i_goal].phrase
else:
result += SEP1 + sentence[i_path].phrase
print(result)
for i_sentence, sentence in enumerate(sentences):
#Anweisungsausgabe
print(i_sentence, '\t', ''.join([chunk.phrase for chunk in sentence]))
for i_chunk, chunk in enumerate(sentence):
#Wenn der Ausdruck eine Nomenklatur ist
if chunk.noun and chunk.dst != -1:
output_result(i_chunk, chunk, sentence)
#Begrenzt, weil es viele gibt
if i_sentence > 5:
break
Zunächst erweitern wir die Chunk-Klasse erheblich.
Setzen Sie das Flag noun
auf True
, wenn es sich um eine Nomenklatur handelt.
Erstellen Sie dann phrase_nomen
so, dass" X ist "und" Y ist ". Zu diesem Zeitpunkt kann es entweder X oder Y sein, daher habe ich den festen Wert "NOUN_REPLACE" in die X- und Y-Teile eingefügt. In Anbetracht des Falls, in dem die Nomenklatur stetig ist (Beispiel: Taube + Uhr), prüfen wir auch, ob "endet mit" mit "NOUN_REPLACE" endet (stellen Sie sicher, dass "NOUN_REPLACE" nicht stetig ist).
python
class Chunk:
def __init__(self, morphs, dst):
self.morphs = morphs
self.dst = dst #Indexnummer der Kontaktklausel
self.dsts = []
self.phrase = ''
self.phrase_noun = ''
self.noun = False
for morph in morphs:
if morph.pos != 'Symbol':
self.phrase += morph.surface #Für Nicht-Symbole
if morph.pos == 'Substantiv':
#Wenn es die erste Nase ist, behandeln Sie sie als Nomenklatur
if self.noun == False:
self.noun = True
self.phrase_noun += NOUN_REPLACE
continue
#Wenn es sich bei der vorherigen um eine Nomenklatur handelt, ersetzen Sie das Oberflächensystem nicht
elif self.noun and self.phrase_noun.endswith(NOUN_REPLACE):
continue
#Fügen Sie das Oberflächensystem mit Ausnahme des Austauschs und des Überspringens so hinzu, wie es ist
self.phrase_noun += morph.surface
Was sich seit dem letzten Mal geändert hat, ist der letzte bedingte "Wenn" -Zweig "Wenn es in EOS ein Abhängigkeitsergebnis gibt".
Durchlaufen Sie die Klausel chunks
, um eine Liste aller Kontakte zu erstellen. Der Grund für die Rückwärtsschleife von der letzten Zeile ist, dass wir alle Kontakte gleichzeitig erstellen und die Liste der einmal erstellten Kontakte wiederverwenden möchten.
python
for line in f:
dependancies = dependancy.match(line)
#Wenn es sich nicht um ein EOS- oder Abhängigkeitsanalyseergebnis handelt
if not (line == 'EOS\n' or dependancies):
morphs.append(Morph(line))
#Wenn es ein morphologisches Analyseergebnis im EOS- oder Abhängigkeitsanalyseergebnis gibt
elif len(morphs) > 0:
chunks.append(Chunk(morphs, dst))
morphs = []
#Im Falle eines Abhängigkeitsergebnisses
if dependancies:
dst = int(dependancies.group(1))
#Wenn es eine Abhängigkeit gibt, führt dies zu EOS
if line == 'EOS\n' and len(chunks) > 0:
#Kontakte hinzufügen(Aus Effizienzgründen aus der letzten Zeile hinzufügen)
for chunk in chunks[::-1]:
if chunk.dst!= -1:
chunk.dsts.extend([chunk.dst])
if chunks[chunk.dst].dst != -1:
chunk.dsts.extend(chunks[chunk.dst].dsts)
sentences.append(chunks)
chunks = []
Die gesamten Satz-, X- und Y-Informationen werden eingerückt und ausgegeben, obwohl sie nicht in den Aufgabenanweisungen enthalten sind, damit sie später überprüft werden können. Die Ausgabe dieses Mal kann grob in die folgenden zwei Muster unterteilt werden.
P1
setzen
--Pattern 2: X und Y sind durch einen anderen Pfad getrennt: |, Y ersetzt nur die Nomenklatur-> Zum Zeitpunkt der Ausgabe auf "P2" gesetztIm Fall von Muster 1 werden X bis Y der Reihe nach ausgegeben, da X und Y der gleiche Pfad sind. Muster 2 ist kompliziert, weil X und Y unterschiedliche Pfade sind. Ich verwende die Schnittfunktion, um die gemeinsame Terminierung von X und Y zu finden. "Was ich im 6. Kapitel von Kapitel 1 getan habe" Das stimmt.
python
def output_result(i, chunk, sentence):
print('\tX:', i, chunk.phrase, chunk.dsts)
#Nomenklatur(X)Schleife, um nach Y zu suchen
for i_y, y_chunk in enumerate(sentence[i+1:], i+1):
#Y für Nomenklatur
if y_chunk.noun:
#Y Informationsausgabe
print('\t\tY:', i_y, y_chunk.phrase)
result = ''
#Y ist die Route(Kontakt)Wenn in enthalten
if i_y in chunk.dsts:
result = '\t\t\tP1:' + '\t' + sentence[i].phrase_noun.replace(NOUN_REPLACE, 'X') + SEP1
#Ausgabe von X nach Y.
for i_path in chunk.dsts:
#Beenden Sie, wenn Y erreicht ist
if i_path == i_y:
result += 'Y'
break
else:
result += sentence[i_path].phrase + SEP1
#Y ist die Route(Kontakt)Wenn nicht enthalten in
else:
result = '\t\t\tP2:' + '\t' + sentence[i].phrase_noun.replace(NOUN_REPLACE, 'X')
#Suchen Sie eine allgemeine Klausel mit dem Mindestwert im Produktsatz
i_goal = min(set(chunk.dsts).intersection(set(sentence[i_y].dsts)))
#Von X nach Y vor der gemeinsamen Phrase
for i_path in chunk.dsts:
if i_path == i_goal:
result += SEP2 + sentence[i_y].phrase_noun.replace(NOUN_REPLACE, 'Y')
break
else:
result += SEP1 + sentence[i_path].phrase
#Von der für Y verantwortlichen Person bis zur allgemeinen Phrase
for i_path in sentence[i_y].dsts:
if i_path == i_goal:
result += SEP2 + sentence[i_goal].phrase
else:
result += SEP1 + sentence[i_path].phrase
print(result)
Wenn das Programm ausgeführt wird, werden die folgenden Ergebnisse ausgegeben. Vielleicht ist es richtig. Ich habe nicht viel überprüft.
Ausgabeergebnis
0 eins
1 Ich bin eine Katze
2 Noch kein Name
X:0 Der Name ist[2]
3 Ich habe keine Ahnung, wo ich geboren wurde
X:0 wo[1, 2, 4]
Y:2 Katonto
P1:In X.->Geboren-> Y
Y:3 Ich habe eine Ahnung
P2:In X.->Geboren->Katonto|Y.|Verwende nicht
X:2 Katonto[4]
Y:3 Ich habe eine Ahnung
P2:Mit X.|Y.|Verwende nicht
X:3 Ich habe eine Ahnung[4]
4 Ich erinnere mich, dass ich nur an einem dunklen und feuchten Ort geweint habe
X:0 alles[1, 5, 7]
Y:Um 3
P2:Auch mit X.->dim|In Y.|In Tränen->Ich erinnere mich
Y:6 Nur was war da
P2:Auch mit X.->dim->In Tränen|Nur Y.|Ich erinnere mich
Y:Ich erinnere mich
P1:Auch mit X.->dim->In Tränen-> Y
X:Um 3[5, 7]
Y:6 Nur was war da
P2:In X.->In Tränen|Nur Y.|Ich erinnere mich
Y:Ich erinnere mich
P1:In X.->In Tränen-> Y
X:6 Nur was war da[7]
Y:Ich erinnere mich
P1:Nur X.-> Y
5 Ich habe hier zum ersten Mal Menschen gesehen
X:0 Ich bin[5]
Y:1 wo
P2:X ist|In Y.->Beginnen mit->Mensch->Dinge|sah
Y:3 Menschen
P2:X ist|Genannt Y.->Dinge|sah
Y:4 Dinge
P2:X ist|Y.|sah
X:1 wo[2, 3, 4, 5]
Y:3 Menschen
P1:In X.->Beginnen mit-> Y
Y:4 Dinge
P1:In X.->Beginnen mit->Mensch-> Y
X:3 Menschen[4, 5]
Y:4 Dinge
P1:X genannt-> Y
X:4 Dinge[5]
6 Außerdem hörte ich später, dass es die böseste Rasse von Menschen war, die Shosei genannt wurde.
X:1 später[2, 9]
Y:3 ist es
P2:In X.->Wenn du hörst|Y ist|Das ist es
Y:4 Hat einen Studenten angerufen
P2:In X.->Wenn du hörst|Genannt Y.->In Menschen->War ein Rennen|Das ist es
Y:5 beim Menschen
P2:In X.->Wenn du hörst|In Y.->War ein Rennen|Das ist es
Y:6 Ichiban
P2:In X.->Wenn du hörst| Y ->Böse->War ein Rennen|Das ist es
Y:7 böse
P2:In X.->Wenn du hörst|Y.->War ein Rennen|Das ist es
Y:War 8 Rennen
P2:In X.->Wenn du hörst|War Y.|Das ist es
Y:9 Das stimmt
P1:In X.->Wenn du hörst-> Y
X:3 ist es[9]
Y:4 Hat einen Studenten angerufen
P2:X ist|Genannt Y.->In Menschen->War ein Rennen|Das ist es
Y:5 beim Menschen
P2:X ist|In Y.->War ein Rennen|Das ist es
Y:6 Ichiban
P2:X ist| Y ->Böse->War ein Rennen|Das ist es
Y:7 böse
P2:X ist|Y.->War ein Rennen|Das ist es
Y:War 8 Rennen
P2:X ist|War Y.|Das ist es
Y:9 Das stimmt
P1:X ist-> Y
X:4 Hat einen Studenten angerufen[5, 8, 9]
Y:5 beim Menschen
P1:X genannt-> Y
Y:6 Ichiban
P2:X genannt->In Menschen| Y ->Böse|War ein Rennen->Das ist es
Y:7 böse
P2:X genannt->In Menschen|Y.|War ein Rennen->Das ist es
Y:War 8 Rennen
P1:X genannt->In Menschen-> Y
Y:9 Das stimmt
P1:X genannt->In Menschen->War ein Rennen-> Y
X:5 beim Menschen[8, 9]
Y:6 Ichiban
P2:In X.| Y ->Böse|War ein Rennen->Das ist es
Y:7 böse
P2:In X.|Y.|War ein Rennen->Das ist es
Y:War 8 Rennen
P1:In X.-> Y
Y:9 Das stimmt
P1:In X.->War ein Rennen-> Y
X:6 Ichiban[7, 8, 9]
Y:7 böse
P1: X -> Y
Y:War 8 Rennen
P1: X ->Böse-> Y
Y:9 Das stimmt
P1: X ->Böse->War ein Rennen-> Y
X:7 böse[8, 9]
Y:War 8 Rennen
P1:X.-> Y
Y:9 Das stimmt
P1:X.->War ein Rennen-> Y
X:War 8 Rennen[9]
Y:9 Das stimmt
P1:War X.-> Y