Das Beispiel basiert auf einem offenen Datensat von Newsgroup-Nachtrichten und orientiert sich an diesem offiziellen Tutorial von scikit-learn zur Textanalyse.
Wir nutzen Dokumente von mehreren Newsgroups und trainieren damit einen Classifier, der dann eine Zudordnung von neuen Texten auf eine dieser Gruppen durchführen kann. Sprich die Newsgroups stellen die Klassen/Tags dar, mit denen wir neue Texte klassifizieren. Wie nutzen eine einfachen Bag-of-Word-Ansatz in dem wir (normalisierte) Häufigkeit von Wörtern als Features nutzen.
In diesem Fall liegen die Daten noch nicht als Teil von scikit-learn
vor, es wird aber eine Funktion angeboten, mit die Daten bezogen werden
können.
from sklearn.datasets import fetch_20newsgroups
Wir legen vier Newsgroups, die wir nutzen wollen, fest.
selected_categories = ["sci.crypt", "sci.electronics", "sci.med", "sci.space"]
Wir beziehen die Trainingset- und Testsets-Dokumente.
newsgroup_posts_train = fetch_20newsgroups(
data_home="newsgroup_data",
subset='train',
categories=selected_categories,
shuffle=True, random_state=1)
newsgroup_posts_test = fetch_20newsgroups(
data_home="newsgroup_data",
subset='test',
categories=selected_categories,
shuffle=True, random_state=1)
Die Objekte, die wir erhalten, sind scikit-learn
-Bunches …
type(newsgroup_posts_train)
… und haben die üblichen Attribute von Bunches.
dir(newsgroup_posts_train)
U.a. exitiert die übliche Beschreibung des Datensets im Attribute
DESCR
, die wir uns ansehen können.
print(newsgroup_posts_train.DESCR)
Das Attribut data
enthält in diesem Fall keine Matrix, sondern
Newsgroup-Message-Texte. Ein Beispiel schauen wir uns an:
print(newsgroup_posts_train.data[6])
Die Targets sind die Newsgroup-Namen. Diese Klassen sind wie üblich
für scikit-learn
als Zahlen kodiert, die wir mittels target_names
auflösen können.
print(newsgroup_posts_train.target_names)
Für unsere Beispiel-Message:
newsgroup_posts_train.target_names[newsgroup_posts_train.target[6]]
Um die Wörter zu zählen, aber auch um Stopwörte zu entfernen und zu
Tokenisieren nutzen wir ein Objekt der
CountVectorizer-Klasse
bzw. dessen fit
-Methode
from sklearn.feature_extraction.text import CountVectorizer
count_vect = CountVectorizer()
count_vect.fit(newsgroup_posts_train.data)
Über alle Dokumente bekommen wir die folgende Zusammenstellung der Wörter und ihre Indices (positionen im Array):
len(count_vect.get_feature_names_out())
Wir können uns ein paar Beispiele ansehen …
count_vect.get_feature_names_out()[10000:10050]
… oder sogar das counting-Dictionary mit den Wörtern und ihre Vorkommen-Anzahl betrachten (Achtung: groß!).
print(count_vect.vocabulary_)
Diese Countings müssen wir für den Klassifikator in eine Matrix transformieren:
X_train_counts = count_vect.transform(newsgroup_posts_train.data)
Die Matrix, die wir erhalten, hat folgende Maße:
X_train_counts.shape
Wir normalisieren die Wörtercoutings auf die Anzahl an Wörter im Text (Term Frequency - TF). Dazu nutzen wir eine Objekt der Klasse TfidfTransformer (schalten die idf-Normalisierung (Inverse Document Frequency) dabei ab.)
from sklearn.feature_extraction.text import TfidfTransformer
tf_transformer = TfidfTransformer(use_idf=False)
Die Normalisierung erfolgt mit den Methoden fit
und transform
.
tf_transformer.fit(X_train_counts)
X_train_tf = tf_transformer.transform(X_train_counts)
Die Matrix, die wir erhalten, hat folgende Maße:
X_train_tf.shape
Jetzt können wir einen Klassifikator erstellen. Wir Nutzen hier eine Random-Forest-Klassifikator, könnten aber auch eine andere Methode wählen.
from sklearn.ensemble import RandomForestClassifier
tf_random_forest_classifier = RandomForestClassifier()
Wie bei allen Supervised-Learning-Verfahren trainieren wir den Klassikator mit der Trainingsmatrix.
tf_random_forest_classifier.fit(X_train_tf, newsgroup_posts_train.target)
Um zu testen wie gut der Klassifikator funktioniert, prozessieren wir das Test-Set mit dem CountVectorizer-Objekt und führen die gleiche TF-Transformation durch.
X_test_counts = count_vect.transform(newsgroup_posts_test.data)
X_test_tf = tf_transformer.transform(X_test_counts)
Ein kurze Blick auf die Maße der Matrix, zeigt uns, dass die Anzahl an Spalten (Features) gleich ist wie bei der Trainingsmatrix.
X_test_counts.shape
Jetzt können wir mit der score
-Methods die Güte des Klassikators auf
dem Test-Set prüfen.
tf_random_forest_classifier.score(X_test_tf, newsgroup_posts_test.target)
Der Klassifikator scheint gut genug zu funktionieren. Wir können jetzt Listen von Dokumenten klassifizieren. Wir nehmen zwei Dokumete aus unserem Test-Set und erstellen zusätzlich ein sehr kleines eigene Dokument, das nur aus einem Satz bestehent.
docs_to_classify = [
newsgroup_posts_test.data[1],
newsgroup_posts_test.data[7],
"The sun send a lot of radiation to the planets including earth"]
Werfen wir einen kurzen Blick auf die zwei Dokumente aus dem Testset.
print(newsgroup_posts_test.data[1])
print(newsgroup_posts_test.data[7])
Auch diese neu zu klassifizierenden Dokumente müssen wir wie die Traininsdokumente in Matrizen transformieren:
X_to_classify_counts = count_vect.transform(docs_to_classify)
X_to_classify_tfidf = tf_transformer.transform(X_to_classify_counts)
Jetzt können wir mit dieser Matrix die Klassifikation durchführen …
predicted_classes = tf_random_forest_classifier.predict(X_to_classify_tfidf)
… und uns die Klassen anschauen, mit denen die Dokumente versehen wurden.
for predicted_class in predicted_classes:
print(newsgroup_posts_train.target_names[predicted_class])
Um den Klassifikator zu verbessern, testen wir statt der Term-Frequenz nun die TFIDF (Term Frequency times Inverse Document Frequency) und erstellen damit unsere Matrizen.
tfidf_transformer = TfidfTransformer(use_idf=True).fit(X_train_counts)
Mach mit diesem TFIDF-Ansatz äquivalent zu der Klassifikation mit dem TF-Ansatz weiter. D.h. führe alle nötigen Schritte wie Training, Scoring und Prediction durch. Ist das Ergebnis besser? Gerne kannst Du zusätzlich mit anderen Klassifikator-Typen anstelle von Randeom Forest experiment werden (z.B. SVMs oder neuronale Netzen), um zu testen, ob dies zu einer besseren Klassifikation führt.