Weiter zum Inhalt

Wahrheitsgemäße und irreführende Hotelbewertungen mit Machine Learning erkennen

In diesem Tutorial setzt du einen Machine-Learning-Algorithmus in Python für ein reales Problem ein. Du lernst, mehrere Textdateien in Python einzulesen, Labels zu extrahieren, mit DataFrames zu arbeiten und vieles mehr!
Aktualisiert 18. Sept. 2026  · 11 Min. lesen

Mit KI erkunden

ChatGPTClaudePerplexity

In diesem Tutorial arbeitest du beispielhaft mit dem Textdatensatz Deceptive Opinion Spam Corpus.

Kurz gesagt, du behandelst heute folgende Themen:

  • Zuerst erhältst du eine kurze Einführung in den Hotelbewertungs-Datensatz und importierst alle benötigten Module,
  • dann lernst du den Aufbau der Daten kennen: Du liest alle Textdateien aus einem Pfad ein, extrahierst daraus die Labels, erstellst je ein DataFrame für Labels und Reviews und führst beides zusammen,
  • anschließend bereitest du die Daten vor: Du entfernst Stopwörter aus den Hotelbewertungen mit Hilfe einer Natural-Language-Toolkit-Bibliothek und extrahierst die Wortarten (Parts of Speech) für jedes Wort im Datensatz.
  • Nach dem Preprocessing bereitest du das Training vor: Du teilst die Daten in Training und Test, lernst TF‑IDF kennen, vektorisierst Trainings- und Testdaten, implementierst ein Support-Vector-Machine-Modell, speicherst sowohl den TF‑IDF‑Vektorisierer als auch das Modell und lädst sie wieder.
  • Zum Schluss triffst du Vorhersagen auf den Testdaten: Du visualisierst die Confusion-Matrix, die Genauigkeit und den Klassifikationsbericht, testest dein Modell mit verschiedenen Random States und prüfst es außerdem mit zwei Yelp-Bewertungen.

Das Deceptive Opinion Spam Dataset verstehen

Bevor du die Daten lädst, lohnt sich ein Blick darauf, womit du genau arbeitest. Das Deceptive Opinion Spam Dataset ist ein Korpus mit wahrheitsgemäßen und irreführenden Hotelbewertungen zu 20 Hotels in Chicago. Die Daten werden in zwei Arbeiten je nach Sentiment der Bewertung beschrieben: Positive Bewertungen in 1 und negative Bewertungen in 2. Für tiefere Einblicke kannst du die Paper heranziehen.

Der Korpus enthält:

  • 400 wahrheitsgemäße, positive Bewertungen von TripAdvisor
  • 400 irreführende, positive Bewertungen von Mechanical Turk
  • 400 wahrheitsgemäße, negative Bewertungen von Expedia, Hotels.com, Orbitz, Priceline, TripAdvisor und Yelp
  • 400 irreführende, negative Bewertungen von Mechanical Turk

Insgesamt sind es 1.600 Bewertungen. Deine Aufgabe ist es, wahrheitsgemäße und irreführende Hotelbewertungen mit einem Machine-Learning-Algorithmus zu klassifizieren.

Also, legen wir los!

Alle benötigten Module importieren

Du startest mit dem Import aller benötigten Module wie os, pandas, nltk, regex und vor allem scikit-learn, denn in diesem Tutorial verwendest du den Machine-Learning-Algorithmus Support Vector Machine aus der scikit-learn-Bibliothek.

os brauchst du, um durch Ordner und Unterordner mit den Textdateien zu iterieren, fnmatch hilft dir, gezielt nur Textdateien zu filtern, pandas bringt die Daten in ein tabellarisches Format und ermöglicht vielseitige Transformationen. Regex unterstützt dich beim Extrahieren von Informationen anhand von Mustern. Nltk (Natural Language Toolkit) nutzt du, um Stopwörter zu entfernen, die dein Modell nicht lernen soll. Und mit scikit-learn bekommst du hilfreiche Bibliotheken zum Aufbereiten und Vektorisieren der Daten, zum Trainieren eines Modells zur Trennlinienfindung zwischen Datenpunkten und zum Auswerten der Genauigkeit.

import os
import fnmatch
from textblob import TextBlob
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from nltk.corpus import stopwords
from nltk import pos_tag,pos_tag_sents
import regex as re
import operator
from sklearn.svm import SVC, LinearSVC
from sklearn.metrics import classification_report, accuracy_score, confusion_matrix
from sklearn_cross_validation import train_test_split  
from sklearn import metrics
from sklearn import svm
from sklearn.grid_search import GridSearchCV
import pickle
from nltk.corpus import stopwords

Alle Textdateien einlesen, Labels extrahieren und ein DataFrame mit Labels erstellen

Aufbau der Daten
Abbildung: Datenstruktur


Nach dem Import der Bibliotheken extrahierst du als Nächstes die Labels. Die Labels helfen dem Modell, zwischen Bewertungen zu unterscheiden, also ob eine Bewertung wahrheitsgemäß oder irreführend ist.

Dafür iterierst du zunächst über alle Textdateien, holst dir deren absolute Pfade und leitest daraus die zugehörigen Labels ab.

path = 'op_spam_train/'

label = []

configfiles = [os.path.join(subdir,f)
for subdir, dirs, files in os.walk(path)
    for f in fnmatch.filter(files, '*.txt')]

Es sollten 1.600 Pfade sein, jeweils zu einer Textdatei. Lass uns das schnell prüfen!

len(configfiles)
1600

Drucken wir auch einen Beispielpfad.

configfiles[1]
'op_spam_train/negative_polarity/deceptive_from_MTurk/fold4/d_swissotel_14.txt'

Aus dieser Struktur siehst du: Um die Labels zu extrahieren, brauchen wir einen Filter. Dafür nutzt du Regex, also reguläre Ausdrücke.

for f in configfiles:
    c = re.search('(trut|deceptiv)\w',f)
    label.append(c.group())

Sind die Labels in einer List namens label gesammelt, erstellst du daraus ein DataFrame.

labels = pd.DataFrame(label, columns = ['Labels'])

Lass uns die ersten fünf Zeilen des DataFrames labels ansehen.

labels.head(5)
  Labels
0 deceptive
1 deceptive
2 deceptive
3 deceptive
4 deceptive

Alle Reviews einlesen und in eine Liste schreiben

Nachdem du die Labels extrahiert hast, holst du dir nun die Texte aus den Dateien!

review = []
directory =os.path.join("op_spam_train")
for subdir,dirs ,files in os.walk(directory):
   # print (subdir)
    for file in files:
        if fnmatch.filter(files, '*.txt'):
            f=open(os.path.join(subdir, file),'r')
            a = f.read()
            review.append(a)

Wie zuvor erstellst du daraus ein DataFrame der Liste review.

reviews = pd.DataFrame(review, columns = ['HotelReviews'])

Drucke die ersten fünf Zeilen des DataFrames reviews.

reviews.head(5)
  HotelReviews
0 Grant it, this hotel seems very nice, but I wa...
1 I recently stayed at the Swissotel Chicago wit...
2 I was sorely disappointed with the Sheraton Ch...
3 My family and I stayed at the Sheraton Chicago...
4 I recently stayed at the Homewood Suites by Hi...

Super! Bis hierhin war es recht eingängig, oder?

Jetzt führen wir die DataFrames labels und reviews zusammen.

Review- und Label-DataFrame zusammenführen

result = pd.merge(reviews, labels,right_index=True,left_index = True)


result['HotelReviews'] = result['HotelReviews'].map(lambda x: x.lower())

Nach dem Merge liegt das neue DataFrame result vor. Schau dir ein paar Zeilen an.

result.head()
  HotelReviews Labels
0 grant it, this hotel seems very nice, but i wa... deceptive
1 i recently stayed at the swissotel chicago wit... deceptive
2 i was sorely disappointed with the sheraton ch... deceptive
3 my family and i stayed at the sheraton chicago... deceptive
4 i recently stayed at the homewood suites by hi... deceptive

Stopwörter aus der Spalte „HotelReviews" entfernen

Bei Textdaten ist es wichtig, Stopwörter zu entfernen. Sie sind wenig aussagekräftig und helfen dem Modell nicht dabei, Muster zu lernen.

Du legst eine neue Spalte namens review_without_stopwords an. Die lambda-Funktion verarbeitet alle Zeilen der Spalte HotelReviews. Mit einer List Comprehension speicherst du nur die Wörter, die nicht in der Variablen stop enthalten sind.

import ntlk
nltk.download('stopwords')

Hinweis: Um die folgende Zelle auszuführen, musst du die obigen Zeilen ggf. im Terminal laufen lassen.

stop = stopwords.words('english')

result['review_without_stopwords'] = result['HotelReviews'].apply(lambda x: ' '.join([word for word in x.split() if word not in (stop)]))

Drucken wir schnell das DataFrame ohne Stopwörter!

result.head()
  HotelReviews Labels review_without_stopwords
0 grant it, this hotel seems very nice, but i wa... deceptive grant it, hotel seems nice, pleased stay here....
1 i recently stayed at the swissotel chicago wit... deceptive recently stayed swissotel chicago husband two ...
2 i was sorely disappointed with the sheraton ch... deceptive sorely disappointed sheraton chicago. outside ...
3 my family and i stayed at the sheraton chicago... deceptive family stayed sheraton chicago hotel towers be...
4 i recently stayed at the homewood suites by hi... deceptive recently stayed homewood suites hilton chicago...

Wortarten aus den Hotelbewertungen extrahieren und als Feature ins Modell geben

Laut 1 lassen sich wahrheitsgemäße und irreführende Meinungen grob als informativ bzw. imaginativ einordnen. Zwischen beiden gibt es deutliche Verteilungsunterschiede: Informative Texte enthalten typischerweise mehr Nomen, Adjektive, Präpositionen, Determinierer und nebenordnende Konjunktionen, während imaginative Texte häufiger Verben, Adverbien, Pronomen und Prädeterminierer aufweisen. Dass irreführende Bewertungen mehr Superlative enthalten, ist wenig überraschend, denn übertreibende Sprache ist dort häufig.

Wir extrahieren daher für jedes Wort in einer Zeile seine Wortart und geben diese Information als Feature-Vektor an das Modell weiter.

Für das POS-Tagging verwendest du TextBlob, eine Python-Bibliothek zur Verarbeitung von Textdaten. Sie bietet eine einfache API für gängige NLP-Aufgaben wie Part-of-Speech-Tagging, Extraktion von Nominalphrasen, Sentimentanalyse, Klassifikation, Übersetzung u. v. m.

Hinweis: Um die Zelle mit der pos-Funktion auszuführen, musst du vorher folgende Zeilen im Terminal ausführen.

import nltk
nltk.download('punkt')
nltk.download('averaged_perceptron_tagger')
def pos(review_without_stopwords):
    return TextBlob(review_without_stopwords).tags
os = result.review_without_stopwords.apply(pos)
os1 = pd.DataFrame(os)

Jede Zeile des DataFrames os1 enthält eine Liste aus Wörtern mit ihrer jeweiligen Wortart. Eine Liste kannst du nicht direkt vektorisieren, um sie dem Modell zu übergeben. Deshalb wandelst du die Zeilen in Strings um.

os1.head()
  review_without_stopwords
0 [(grant, NN), (it, PRP), (hotel, NN), (seems, ...
1 [(recently, RB), (stayed, VBN), (swissotel, NN...
2 [(sorely, RB), (disappointed, JJ), (sheraton, ...
3 [(family, NN), (stayed, VBD), (sheraton, JJ), ...
4 [(recently, RB), (stayed, VBN), (homewood, NN)...

Jetzt wandelst du jede Zeile in einen String um, in dem jedes Wort mit seinem POS per Slash verbunden ist und die Wort-Paare durch ein Leerzeichen getrennt sind.

os1['pos'] = os1['review_without_stopwords'].map(lambda x:" ".join(["".join(x) for x in x ]) )

Zum Schluss mergest du die Spalte pos in das Haupt-DataFrame result und gibst die ersten Zeilen aus.

result = result = pd.merge(result, os1,right_index=True,left_index = True)
result.head()
Erste Zeilen der pos-Spalte und des result-DataFrames

Trainingsphase

Die Daten in 80% Training und 20% Test aufteilen

Nachdem die Features extrahiert sind, teilst du die Daten in Training und Test. In diesem Tutorial nimmst du 80% für das Training und 20% für den Test.

review_train, review_test, label_train, label_test = train_test_split(result['pos'],result['Labels'], test_size=0.2,random_state=13)

Trainings- und Testdaten mit dem TfidfVectorizer vektorisieren

Schauen wir uns TfidfVectorizer genauer an.

TF‑IDF besteht aus zwei Teilen:

    • Term Frequency (TF): Wie oft ein bestimmtes Wort in einem Dokument vorkommt. Das Wort „the“ erscheint beispielsweise sehr häufig in allen Dokumenten, trägt aber wenig inhaltliche Information. Ein Wort wie „messi“ liefert hingegen Kontext, etwa Fußball. Seltene Wörter können jedoch ebenso wenig Aussagekraft haben. Beispielhaft: tf("the") = 100, tf("messi") = 5, tf("floccinaucinihilipilification") = 1.
  • Inverse Document Frequency (IDF): Dieses Maß korrigiert den Einfluss sehr häufiger bzw. sehr seltener Wörter. Es ist definiert als Logarithmus aus {Anzahl Dokumente im Korpus geteilt durch die Anzahl Dokumente, in denen der Begriff vorkommt}. Für „the“ ist dieses Verhältnis nahe 1, der Logarithmus also nahe 0. Beispiel: idf("the") = 0, idf("messi") = log(10/3) = 0,52 (angenommen, der Korpus dreht sich um Fußball), idf("floccinaucinihilipilification") = log(10/1) = 1.
  • Letzter Schritt: Wir wollen „messi“ höher gewichten als die anderen beiden Wörter. Daher multiplizieren wir: tf‑idf = tf × idf. "the" = 100 × 0 = 0, "floccinaucinihilipilification" = 1 × 1 = 1, "messi" = 5 × 0,52 = 2,6.

Diese Gewichtung hilft dem ML‑Modell bei der Klassifikation, da wir explizit festlegen, welche Wörter mehr bzw. weniger ins Gewicht fallen.

Jetzt implementieren wir das für Trainings- und Testdaten.

tf_vect = TfidfVectorizer(lowercase = True, use_idf=True, smooth_idf=True, sublinear_tf=False)

X_train_tf = tf_vect.fit_transform(review_train)
X_test_tf = tf_vect.transform(review_test)

Modell implementieren

Als Nächstes setzt du ein Modell vom Typ Support Vector Machines (SVM) ein. Eine gute Einführung findest du hier.

Um die besten Hyperparameter für dein ML‑Modell zu wählen, verwendest du GridSearchCV. Basierend auf Trainingsdaten und Labels ermittelt es aus deinen Vorgabewerten die beste Kombination. Du gibst je fünf Werte für C und gamma vor und erhältst die beste Kombination für deine Daten.

def svc_param_selection(X, y, nfolds):
    Cs = [0.001, 0.01, 0.1, 1, 10]
    gammas = [0.001, 0.01, 0.1, 1]
    param_grid = {'C': Cs, 'gamma' : gammas}
    grid_search = GridSearchCV(svm.SVC(kernel='linear'), param_grid, cv=nfolds)
    grid_search.fit(X, y)
    return grid_search.best_params_
svc_param_selection(X_train_tf,label_train,5)
{'C': 10, 'gamma': 0.001}
clf = svm.SVC(C=10,gamma=0.001,kernel='linear')
clf.fit(X_train_tf,label_train)
pred = clf.predict(X_test_tf)

TF‑IDF‑Vektorisierer und ML‑Modell speichern

Speichere das trainierte Modell zusammen mit dem TF‑IDF‑Vektorisierer mit der Bibliothek pickle, damit du später einfach Daten laden, vektorisieren und mit dem ML‑Modell vorhersagen kannst.

with open('vectorizer.pickle', 'wb') as fin:
    pickle.dump(tf_vect, fin)
with open('mlmodel.pickle','wb') as f:
    pickle.dump(clf,f)

TF‑IDF‑Vektorisierer und ML‑Modell laden

pkl = open('mlmodel.pickle', 'rb')
clf = pickle.load(pkl)   
vec = open('vectorizer.pickle', 'rb')
tf_vect = pickle.load(vec)
/home/naveksha/.local/lib/python3.5/site-packages/sklearn/base.py:311: UserWarning: Trying to unpickle estimator SVC from version pre-0.18 when using version 0.19.1. This might lead to breaking code or invalid results. Use at your own risk.
  UserWarning)
/home/naveksha/.local/lib/python3.5/site-packages/sklearn/base.py:311: UserWarning: Trying to unpickle estimator TfidfTransformer from version pre-0.18 when using version 0.19.1. This might lead to breaking code or invalid results. Use at your own risk.
  UserWarning)
/home/naveksha/.local/lib/python3.5/site-packages/sklearn/base.py:311: UserWarning: Trying to unpickle estimator TfidfVectorizer from version pre-0.18 when using version 0.19.1. This might lead to breaking code or invalid results. Use at your own risk.
  UserWarning)

Vorhersagen auf den Testdaten

X_test_tf = tf_vect.transform(review_test)
pred = clf.predict(X_test_tf)

Confusion-Matrix, Accuracy und Klassifikationsbericht zur Bewertung des Modells ausgeben

print(metrics.accuracy_score(label_test, pred))
0.96875
print (confusion_matrix(label_test, pred))
[[149   6]
 [  4 161]]

Die obige Confusion-Matrix ist 2 × 2 aufgebaut: Erstes Zeilen‑, erstes Spaltenelement ist True Positive (TP), erstes Zeilen‑, zweites Spaltenelement ist False Negative (FN), zweite Zeile, erste Spalte ist False Positive (FP) und zweite Zeile, zweite Spalte ist True Negative (TN).

Daraus lässt sich ablesen, dass nur 10 von 320 Testsamples falsch klassifiziert wurden.

print (classification_report(label_test, pred))
             precision    recall  f1-score   support

  deceptive       0.97      0.96      0.97       155
      truth       0.96      0.98      0.97       165

avg / total       0.97      0.97      0.97       320

Das Modell mit unterschiedlichen Random States testen

  • Random State 1
review_train, review_test, label_train, label_test = train_test_split(result['pos'],result['Labels'], test_size=0.2,random_state=1)
X_test_tf = tf_vect.transform(review_test)
pred = clf.predict(X_test_tf)
print(metrics.accuracy_score(label_test, pred))
0.978125
print (confusion_matrix(label_test, pred))
[[146   5]
 [  2 167]]
print (classification_report(label_test, pred))
             precision    recall  f1-score   support

  deceptive       0.99      0.97      0.98       151
      truth       0.97      0.99      0.98       169

avg / total       0.98      0.98      0.98       320
  • Random State 10
review_train, review_test, label_train, label_test = train_test_split(result['pos'],result['Labels'], test_size=0.2,random_state=10)
X_test_tf = tf_vect.transform(review_test)
pred = clf.predict(X_test_tf)
print(metrics.accuracy_score(label_test, pred))
0.98125
print (confusion_matrix(label_test, pred))
[[155   5]
 [  1 159]]
print (classification_report(label_test, pred))
             precision    recall  f1-score   support

  deceptive       0.99      0.97      0.98       160
      truth       0.97      0.99      0.98       160

avg / total       0.98      0.98      0.98       320
  • Random State 42
review_train, review_test, label_train, label_test = train_test_split(result['pos'],result['Labels'], test_size=0.2,random_state=42)
X_test_tf = tf_vect.transform(review_test)
pred = clf.predict(X_test_tf)
print(metrics.accuracy_score(label_test, pred))
0.984375
print (confusion_matrix(label_test, pred))
[[165   3]
 [  2 150]]
print (classification_report(label_test, pred))
             precision    recall  f1-score   support

  deceptive       0.99      0.98      0.99       168
      truth       0.98      0.99      0.98       152

avg / total       0.98      0.98      0.98       320

Aus den obigen Ergebnissen siehst du: Das Modell leistet hervorragende Arbeit und overfittet nicht, da du es mehrfach mit unterschiedlichen Splits getestet hast.

Das Modell mit zwei Yelp-Reviews testen

def test_string(s):
    X_test_tf = tf_vect.transform([s])
    y_predict = clf.predict(X_test_tf)
    return y_predict
test_string("The hotel was good.The room had a 27-inch Samsung led tv, a microwave.The room had a double bed")
array(['truth'], dtype=object)
test_string("My family and I are huge fans of this place. The staff is super nice, and the food is great. The chicken is very good, and the garlic sauce is perfect. Ice cream topped with fruit is delicious too. Highly recommended!")
array(['deceptive'], dtype=object)

Das Modell hat beide Bewertungen korrekt klassifiziert. Die erste ist wahrheitsgemäß, die zweite ist irreführend.

Hinweis

Die Modellleistung kann auf unterschiedlichen Systemen mit abweichender Hardware- und Softwarekonfiguration variieren – ein ungewöhnliches, beobachtetes Verhalten. Wenn die Community helfen kann, die Ursache zu finden, profitieren viele, inklusive der Autorin bzw. des Autors dieses Beitrags. Mit Community sind hier die Leserinnen und Leser dieses Tutorials gemeint.

Viel Erfolg beim Lernen!

Hoffentlich war dieses Tutorial hilfreich und bringt dich fachlich weiter.

Wenn du mehr über Machine Learning lernen möchtest, schau dir den DataCamp‑Kurs Building Chatbots in Python an.

Themen
Python
Maschinelles Lernen

Mehr über Python und Machine Learning

Kurs

Machine Learning verstehen

2 Std.
308.6K
In diesem Kurs lernst du das spannende Themenfeld des maschinellen Lernens kennen – und du benötigst dafür gar keine Programmierkenntnisse.
Details anzeigenRight Arrow
Kurs Starten
Mehr anzeigenRight Arrow
Verwandt

Tutorial

Python-Anweisungen IF, ELIF und ELSE

In diesem Tutorial lernst du ausschließlich Python if else-Anweisungen kennen.
Sejal Jaiswal's photo

Sejal Jaiswal

9 Min.

Tutorial

Python Switch Case Statement: Ein Leitfaden für Anfänger

Erforsche Pythons match-case: eine Anleitung zu seiner Syntax, Anwendungen in Data Science und ML sowie eine vergleichende Analyse mit dem traditionellen switch-case.
Matt Crabtree's photo

Matt Crabtree

5 Min.

Tutorial

if…elif…else in Python Tutorial

Lerne, wie du in Python if…elif…else-Anweisungen erstellst.
DataCamp Team's photo

DataCamp Team

4 Min.

Tutorial

Python Datenstrukturen Tutorial

Mach dich mit Python-Datenstrukturen vertraut: Lerne mehr über Datentypen und primitive sowie nicht-primitive Datenstrukturen wie Strings, Listen, Stapel usw.
Sejal Jaiswal's photo

Sejal Jaiswal

24 Min.

Tutorial

Python-Arrays

Python-Arrays mit Code-Beispielen. Lerne noch heute, wie du mit Python NumPy Arrays erstellen und ausdrucken kannst!
DataCamp Team's photo

DataCamp Team

3 Min.

Tutorial

Python-Tutorial zum Verknüpfen von Zeichenfolgen

Lerne verschiedene Methoden zum Verknüpfen von Zeichenfolgen in Python kennen, mit Beispielen, die jede Technik zeigen.
DataCamp Team's photo

DataCamp Team

5 Min.

Mehr AnzeigenMehr Anzeigen