Einführung in Bibliotheken für die Analyse von Textdaten#
Für die Analyse von Textdaten existieren verschiedene Python-Bibliotheken, welche gängige Textverarbeitungs- und Analyseverfahren sowie hilfreiche Werkzeuge und Datensätze bereitstellen. Im Rahmen dieser Schulung wollen wir uns zwei davon anschauen und nutzen:
NLTK (Natural Language Toolkit) mit Fokus auf klassischen NLP-Verfahren bei moderaten Datenmengen
Gensim (Semantic Modelling) mit Fokus auf Textrepräsentationen insb. für KI/ML sowie der Handhabung großer Datenmengen
NLTK#
NLTK benötigt für viele der verfügbaren NLP-Verfahren spezifische Datensätze oder vortrainierte Modelle, z.B. für das Erkennen von Stopwörtern in der englischen Sprache. Für den Download dieser Daten wird z.B. die Methode download()
bereitgestellt. Wird hier kein Parameter (z.B. Name eines Datensatzes) angegeben, erscheint ein Dialog für die Auswahl der zu ladenden Daten.
Die Collection popular
umfasst dabei z.B. gängige Korpora, Datensätze, vortrainierte Modelle, etc.
# Import von NLTK
import nltk
# Bekanntgabe eines individuellen Ordners für den Download
nltk.data.path.append('/Users/matthias/.cache/nltk_data')
# Potenziell doppelte Einträge entfernen
nltk.data.path = list(set(nltk.data.path))
# Download Dialog anzeigen
# Der individuelle Ordner für den Download kann dort im Feld "Download Directory" angegeben werden
# Daten für den Download, z.b. die Collection "popular", können mit den Pfeiltasten (hoch/runter) ausgewählt werden
nltk.download()
# Test des erfolgreichen Downloads, Zugriff auf geladenen Korpus "brown"
from nltk.corpus import brown
brown.words()
['The', 'Fulton', 'County', 'Grand', 'Jury', 'said', ...]
Mit den jetzt verfügbaren Datensätzen lassen sich umfangreiche Textverarbeitungs- und Analyseverfahren verwenden. Einen Überblick dazu gibt einerseits das NLTK HowTo, aber auch andere Online-Quellen wie RealPython. Sehen wir uns Tokenization, das Stopword-Removal und Stemming an einem Beispiel an.
# Erstellen eines Beispielsatzes
doc = "Life is like riding a bicycle. To keep your balance, you must keep moving."
Tokenization#
from nltk.tokenize import sent_tokenize, word_tokenize
import string
# Tokenization für Sätze
sentences = sent_tokenize(doc)
print(sentences)
['Life is like riding a bicycle.', 'To keep your balance, you must keep moving.']
# Tokenization für Wörter, Entfernen von Satzzeichen
tokens = word_tokenize(doc)
tokens = [token for token in tokens if token not in string.punctuation]
print(tokens)
['Life', 'is', 'like', 'riding', 'a', 'bicycle', 'To', 'keep', 'your', 'balance', 'you', 'must', 'keep', 'moving']
Stopword removal#
from nltk.corpus import stopwords
# Laden der englischen Stopwort-Liste
eng_stop_words = set(stopwords.words('english'))
print('Anzahl der englischen Stopwords: ', len(eng_stop_words))
Anzahl der englischen Stopwords: 198
filtered_tokens = [token for token in tokens if token.casefold() not in eng_stop_words]
print(filtered_tokens)
['Life', 'like', 'riding', 'bicycle', 'keep', 'balance', 'must', 'keep', 'moving']
Stemming#
from nltk.stem import PorterStemmer
stemmer = PorterStemmer()
stemmed_tokens = [stemmer.stem(token) for token in filtered_tokens]
print(stemmed_tokens)
['life', 'like', 'ride', 'bicycl', 'keep', 'balanc', 'must', 'keep', 'move']
Gensim#
Gensim baut auf vier grundlegenden Konzepten auf:
Dokument: Sammlung von Text
Korpus: Sammlung von Dokumenten
Vektoren: Numerische Repräsentation eines Dokuments
Modell: Algorithmus zur Transformation von einer Repräsentation in eine andere
Für die grundlegende Vorverarbeitung von Text (z.b. Tokenization, Stopwords, POS, etc) bieten sich Verfahren aus NLTK an.
ACHTUNG:
Bei der Installation und dem nachfolgenden Import von Gensim kann es unter Umständen zu folgendem Fehler kommen:
"ImportError: cannot import name 'triu' from 'scipy.linalg'"
Dabei wird auf eine fehlende von Gensim genutzte Bibliothek (Abhängigkeit) verwiesen, die in der aktuellsten Version von scipy nicht mehr vorhanden ist. Dies lässt sich mit conda reparieren:
Öffne ein Terminal in Jupyter Lab (“File -> New -> Terminal”)
Führe in diesem Terminal diesen Befehl aus, um die korrekten Abhängigkeiten für Gensim zu installieren:
conda update scipy gensim
Lade den Python-Kernel für dieses Notbook neu (Kreisförmiger Pfeil in den Notebook-Funktionen mit “Restart the kernel”)
Führe alle Codezellen des Notebooks danach wieder aus
import gensim
from gensim import corpora, models, similarities
# Erstellen von Beispieldaten
docs = [
"Life is like riding a bicycle. To keep your balance, you must keep moving.",
"If you want something said, ask a man; if you want something done, ask a woman.",
"If you want something done right, do it yourself.",
"You must be the change you wish to see in the world.",
"Two roads diverged in a wood, and I, I took the one less travelled by, and that has made all the difference.",
"Not all those who wander are lost."
]
Vorverarbeitung mit NLTK#
# Definition einer Funktion für Vorverarbeitung mit Tokenization, Stemming
def nltk_preprocess(doc):
tokens = word_tokenize(doc)
tokens = [token for token in tokens if token not in string.punctuation]
stemmed_tokens = [stemmer.stem(token) for token in tokens]
return stemmed_tokens
processed_docs = [nltk_preprocess(doc) for doc in docs]
print(processed_docs)
[['life', 'is', 'like', 'ride', 'a', 'bicycl', 'to', 'keep', 'your', 'balanc', 'you', 'must', 'keep', 'move'], ['if', 'you', 'want', 'someth', 'said', 'ask', 'a', 'man', 'if', 'you', 'want', 'someth', 'done', 'ask', 'a', 'woman'], ['if', 'you', 'want', 'someth', 'done', 'right', 'do', 'it', 'yourself'], ['you', 'must', 'be', 'the', 'chang', 'you', 'wish', 'to', 'see', 'in', 'the', 'world'], ['two', 'road', 'diverg', 'in', 'a', 'wood', 'and', 'i', 'i', 'took', 'the', 'one', 'less', 'travel', 'by', 'and', 'that', 'ha', 'made', 'all', 'the', 'differ'], ['not', 'all', 'those', 'who', 'wander', 'are', 'lost']]
Überführung in Gensim Corpora#
Weist jedem Token eine eindeutige ID zu
Erlaubt das Verarbeiten sehr großer Datenmengen via Streaming
dictionary = corpora.Dictionary(processed_docs)
print(dictionary.token2id)
{'a': 0, 'balanc': 1, 'bicycl': 2, 'is': 3, 'keep': 4, 'life': 5, 'like': 6, 'move': 7, 'must': 8, 'ride': 9, 'to': 10, 'you': 11, 'your': 12, 'ask': 13, 'done': 14, 'if': 15, 'man': 16, 'said': 17, 'someth': 18, 'want': 19, 'woman': 20, 'do': 21, 'it': 22, 'right': 23, 'yourself': 24, 'be': 25, 'chang': 26, 'in': 27, 'see': 28, 'the': 29, 'wish': 30, 'world': 31, 'all': 32, 'and': 33, 'by': 34, 'differ': 35, 'diverg': 36, 'ha': 37, 'i': 38, 'less': 39, 'made': 40, 'one': 41, 'road': 42, 'that': 43, 'took': 44, 'travel': 45, 'two': 46, 'wood': 47, 'are': 48, 'lost': 49, 'not': 50, 'those': 51, 'wander': 52, 'who': 53}
Transformation zu Bag-of-Words Corpus#
corpus_bow = [dictionary.doc2bow(doc) for doc in processed_docs]
print('Eindeutige Token im Corpus: ', len(dictionary))
print('Anzahl der Dokumente: ', len(corpus_bow))
Eindeutige Token im Corpus: 54
Anzahl der Dokumente: 6
Ähnlichkeitssuche in Dokumenten mit Latent Semantic Indexing (LSI)#
# Erstellen des LSI-Modells - transformiert den Corpus in einen 2-dimensionalen Vektorraum
lsi = models.LsiModel(corpus_bow, id2word=dictionary, num_topics=2)
# Wir können Anfragen für die Ähnlichkeitssuche stellen und ebenfalls in den LSI Vektorraum transformieren
query = "Keep moving"
# Überführung der Anfrage in BOW Vektor
query_bow = dictionary.doc2bow(query.lower().split())
# Transformation des BOW Vektors in LSI Vektorraum
query_lsi = lsi[query_bow]
print(query_lsi)
[(0, 0.08483995231446129), (1, 0.02144278615233461)]
# Erstellen eines Suchindex für unseren Corpus
index = similarities.MatrixSimilarity(lsi[corpus_bow])
# Übergabe der Anfrage an den Suchindex
sims = index[query_lsi]
# Ausgabe des Sucherergebnis - Ähnlichkeitswert mit Dokument
for doc_position, doc_score in enumerate(sims):
print(doc_score, docs[doc_position])
0.9985177 Life is like riding a bicycle. To keep your balance, you must keep moving.
0.96638083 If you want something said, ask a man; if you want something done, ask a woman.
0.9440288 If you want something done right, do it yourself.
0.8221928 You must be the change you wish to see in the world.
0.28360677 Two roads diverged in a wood, and I, I took the one less travelled by, and that has made all the difference.
0.15459536 Not all those who wander are lost.