Die computergestützte Verarbeitung natürlicher Sprache ist alles andere als trivial. Die Grundlagen dafür wurden über Jahrzehnte vor allem von der Computerlinguistik geschaffen. Die Sprachverarbeitung findet an vielen Stellen ihren Einsatz. Es können etwas grammatische Strukturen identifiziert und analysiert werden. Ein ganz wichtiger Anwendungsbereich liegt in der semantischen Analyse: der Computer soll Text nicht nur als Aneinanderreihung von Zeichen sehen, sondern den Inhalt eines Textes "verstehen". Ein typischer Anwendungsfall davon ist etwa die Named Entity Recognition, wo es darum geht, z.B. Personen- oder Ortsnennungen in einem Text zu finden.
Ein weiterer Bereich ist die Textklassifizierung, wo wir schon in den Bereich des künstlichen Intelligenz kommen. Eine klassische Anwendung dafür ist etwa die Identifizierung von Spam, die Klassifizierung nach Testsorten oder die Sentiment Analysis, bei der versucht wird, die "Stimmung" eines Textes (z.B. eines Online-Kommentars) zu verstehen.
NLP ist ein relativ komplexer Bereich, in dem statistische und wahrscheinlichkeitstheoritsche Aspekte aber auch Wissensrepräsentationen eine große Rolle spielen. Es gibt eine Reihe von Bibliotheken, die über Jahre für diesen Bereich geschrieben wurden. Für Python gibt es beispielsweise
Spacy steht als externe Bibliothek zur Verfügung. Sie kann einfach via
pip install spacy
installiert werden.
Für jede benötigte Sprache müssen dann noch sprachspezifische Dateien nach installiert werden. Für Deutsch geht das so:
python -m spacy download de
Eine Liste der unterstützten Sprachen finden Sie hier: https://spacy.io/models/
Sobald alles installiert ist, können wir erste Experimente starten.
Zuerst laden wir spacy und das (statistische) Modell für Deutsch:
In [ ]:
import spacy
nlp = spacy.load('de')
Dann laden wir den schon bekannten Roman von Jakob Wassermann:
In [ ]:
with open('../data/wassermann/der_mann_von_vierzig_jahren.txt') as fh:
doc = nlp(fh.read())
Beim Erzeugen des Spacy-Dokuments wurden bereits allerlei Aktionen durchgeführt. Beispielsweise wurde der Text tokeniziert, also in einzelne Tokens (Wörter, wenn man so will) zerlegt. Die passiert sprachspezifisch und ist daher deutlich besser als unsere bisherigen Versuche mit split()
.
In [ ]:
for token in doc:
print(token)
Wir können damit zum Beispiel die Zahl der Tokens zählen:
In [ ]:
sum(1 for token in doc)
spaCy speichert für jedes Token eine Reihe weitere Daten, die als Property angesprochen werden können:
In [ ]:
# Gibt Token für Token mit POS-Daten aus.
# Zum nächsten Token gelangen Sie durch Drücken der Enter-Taste
# Abbruch der Schleife durch Eingabe von 'exit' + Enter
for token in doc:
print(('{}\n\tLemma: {}\n\tPOS: {}\n\tTAG: {}\n\tDEP: {}'
'\n\tShape: {}\n\tis_alpha: {}\n\tis_stop: {}'.format(
token.text, token.lemma_, token.pos_, token.tag_, token.dep_,
token.shape_, token.is_alpha, token.is_stop)))
if input() == 'exit':
break
Named Entity Recognition versucht Entitätsnamen (Personen, Orte, Firmen, Datumsangaben, Telefonnummern, Buchtitel, usw.) im Text zu identifizieren. Der Erfolg solcher Bemühungen hängt stark davon ab, wie gut das jeweilige Modell zur Textsorte (aber z.B. auch zur jeweiligen Zeit passt). Da spaCy vor allem auf zeitgenössische Texte mit einem Fokus auf Online-Text spezialisiert ist, ist das Ergebnis nicht berauschend, zeigt aber die grundlegende Idee. Die Ergebnisse sind rein statistisch und lassen sich trainieren. Auf die Schnelle habe ich leider kein gut trainiertes Modell für etwas antiquiertes literarisches Deutsch gefunden.
Mehr zu Named Entities in spaCy finden Sie hier: https://spacy.io/usage/linguistic-features#named-entities
In [ ]:
# Zeige Orte (LOC) es gibt noch: ORG, PERS, MISC
for ent in doc.ents:
if ent.text.strip(): # for some reason we get a lot of empty String Tokens
if ent.label_ == 'LOC':
print(ent.text)
Da Entity-Objekte auch die Positionsangaben innerhalb des Textes bereitstellen, kann man Entities bei Bedarf ziemlich leicht im Text taggen. Ich verwende dazu einen übersichtlicheren String:
In [ ]:
text = ('Anton Maier, geboren 1990, wohnt in Wien und mag Bücher. '
'Seine Frau heißt Anna Huber und interessiert sich für Computer.'
'Aufgewachsen ist er in Graz und sie in St. Pölten.')
doc = nlp(text)
In [ ]:
# Use custom tags for named entities
TAG_MAP = {
'LOC': ('<place>', '</place>'),
'PER': ('<person>', '</person>')
}
# stores the single parts which a joinded to a string in the end
buf = []
last_end = 0 # end position of the last entity found
for ent in doc.ents:
# we are only interested in the tags defined in TAG_MAP, so if ent.label_
# has no key in TAG_MAP, the tag will be an empty string
start_tag, end_tag = TAG_MAP.get(ent.label_, ('', ''))
# extract substring from end pos of last entity to start off this entity
buf.append(text[last_end:ent.start_char])
# add tags and entity string
buf.append(start_tag + ent.text + end_tag)
last_end = ent.end_char
# Add the remaining text after the last substring
buf.append(text[last_end:])
print(''.join(buf))