Sentiment Analysis
Bei der Sentiment Analysis geht es um die Analyse der Stimmung eines Textes. Ein Algorithmus könnte zum Beispiel zur Marktforschung die Stimmungslage zu einem bestimmten Produkt erfassen.
Bereits im Jahr 2020 hab ich einen Sentiment Analysis Algorithmus entwickelt. Damals ging es um die Erfassung der Stimmungslage von Zeitungsartikeln.
Hedonometer
Hedonometer ist ein Projekt, das bereits seit dem Jahr 2009 das Gemüt von Twitter-Posts analysiert.

Durchschnittliches Gemüt von Twitter 🐦 (hedonometer.org)
In meiner Implementation des Algorithmus, verwende ich den Datensatz von Hedonometer, der dort als csv
Tabelle heruntergeladen werden kann.
Kleiner Auszug aus der Tabelle für Englische Wörter:
rank | word | english | score | deviation |
---|---|---|---|---|
0 | laughter | laughter | 8.5 | 0.93 |
1 | happiness | happiness | 8.44 | 0.97 |
2 | love | love | 8.42 | 1.11 |
… | ||||
10174 | epidemic | epidemic | 1.72 | 0.99 |
Der score beschreibt das Gemüt von 1-10.
Die deviation beschreibt das Gewicht eines Wortes.
Um das Sentiment eines Textes zu bestimmen, können zunächst durch Tokenization
alle Wörter des Textes extrahiert werden.
Anschließend werden die Wörter in der Tabelle gesucht und mit dem score und der deviation (Gewicht) das durchschnittliche Gemüt des Textes bestimmt.
Mittlerweile gibt es bessere Algorithmen, um eine Sentiment Analysis durchzuführen. Mit Reinforcement Learning können einige Probleme vermieden werden, die mit diesem simplen Algorithmus auftreten.
Implementation des Algorithmus
Der Ganze Quellcode kann auf GitHub aufgefunden werden: GitHub
export async function getSentiment(text: string, options: GetSentimentOptions): Promise<Sentiment> {
const wordScores = await getDataset();
const datasetInfo = await getDatasetInfo();
const words = (text.match(/\b\w+\b/g) || [])
.map(w => w.toLocaleLowerCase());
// This will find & map the word score for each word. Words with no matching word score will be filtered out!
const mappedWords = words
.map(w => wordScores.find(s => s.word === w))
.filter(ws => ws);
const missingWords = words.filter(w => !wordScores.some(ws => ws.word === w)); // Words that could not be mapped to a word score.
// This will calculate the arithmetic mean of the word scores, while weighting the scores with the deviation.
// Maybe the harmonic mean would be the correct forumula in this case, however my math skills are to bad to know for sure.
// TODO: Maybe find out if the harmonic mean is better suited
const sentiment = Myth.sumBy(mappedWords, ws => ws.score * ws.deviation)
/ Myth.sumBy(mappedWords, ws => ws.deviation);
const neutralScoreRadius = (datasetInfo.scoreMax - datasetInfo.scoreMin) * options.neutralSentimentRange * 0.5;
const neutralScore = options.neutralSentimentAnker === 'mean'
? datasetInfo.scoreMean
: datasetInfo.scoreMiddle;
return {
lostWordCount: missingWords.length,
lostWords: [...new Set(missingWords)],
datasetInfo,
sentiment,
sentimentTxt: (sentiment <= neutralScore - neutralScoreRadius)
? 'negative'
: (sentiment >= neutralScore + neutralScoreRadius) ? 'positive' : 'neutral'
};
}
Evaluation
Warum ich dieses alte Thema nochmal aufgegriffen habe, hat aber eigentlich einen anderen Grund. In meinem KI Studiengang ging es im letzten Kapitel um die Evaluation von KI-Systemen. So kam mir die Idee, zur Übung die Theorie in der Praxis anzuwenden.
Zur Evaluation können die Ergebnisse des KI-Algorithmus in eine Konfusionsmatrix eingetragen werden und anschließend Metriken berechnet werden.
Konfusionsmatrix
Die Konfusionsmatrix stellt die erwarteten Ergebnisse, den tatsächlichen Ergebnissen gegenüber.
Im Fall der Sentiment Analysis gibt es drei mögliche Klassen, in die ein Text eingeordnet werden kann. (positiv
, neutral
& negativ
)
Hier die Konfusionsmatrix meines (unoptimierten) Sentiment Analysis Algorithmus:
-----------------+----------+---------+----------
| | positive | neutral | negative |
-----------------+----------+---------+----------
| konrad positive | 774 | 431 | 87 |
-----------------+----------+---------+----------
| konrad neutral | 319 | 944 | 703 |
-----------------+----------+---------+----------
| konrad negative | 10 | 55 | 211 |
-----------------+----------+---------+----------
konrad ist der Name des Algorithmus.
In diesem Test, hab ich den Algorithmus mit gelabelten Daten von kaggle getestet. Gelabelt heißt, dass die Daten von einem Menschen manuell analysiert und bewertet wurden.
Metriken
Mit unserer Konfusionsmatrix können wir nun einige Metriken berechnen.
Accuracy
Die Accuracy beschreibt das Verhältnis von richtigen zu falschen Ergebnissen. Zur Berechnung werden alle richtigen Ergebnisse addiert und durch die Gesamtzahl der Ergebnisse geteilt.
Die richtigen Ergebnisse sind in der Tabelle markiert:
positive | neutral | negative | |
---|---|---|---|
konrad positive | 774 | 431 | 87 |
konrad neutral | 319 | 944 | 703 |
konrad negative | 10 | 55 | 211 |
TPOS
= True PositiveFPOS
= False Positive
TNEU
= True NeutralFNEU
= False Neutral
TNEG
= True NegativeFNEG
= False Negative
Precision
Die Precision gibt an, wieviel Prozent der Ergebnisse korrekt einer Klasse zugeordnet wurden. Die Metrik kann also für alle drei Klassen berechnet werden.
In diesem Beispiel wird die Precision für die Klasse positive
berechnet.
In der Tabelle ist der TPOS Wert farblich markiert und die FPOS Werte fett gedruckt:
positive | neutral | negative | |
---|---|---|---|
konrad positive | 774 | 431 | 87 |
konrad neutral | 319 | 944 | 703 |
konrad negative | 10 | 55 | 211 |
Recall
Der Recall gibt an, wieviel Prozent der Daten einer Klasse tatsächlich vom Algorithmus dieser Klasse zugeordnet wurden. Auch hier kann die Metrik für die verschiedenen Klassen seperat berechnet werden.
In diesem Beispiel wird der Recall für die Klasse positive
berechnet.
XPOS
= Missing Positives. Bei binären Klassifizierungsaufgaben (Zwei statt drei Klassen), würde man hier stattdessen den FNEG Wert nehmen.
Da dieser Wert bei drei Klassen allerdings auch die Werte einschließt, die eigentlich in der Klasse neutral landen sollten, musste ich hier einen neuen Begriff erfinden.
In der Tabelle ist der TPOS Wert farblich markiert und die XPOS Werte fett gedruckt:
positive | neutral | negative | |
---|---|---|---|
konrad positive | 774 | 431 | 87 |
konrad neutral | 319 | 944 | 703 |
konrad negative | 10 | 55 | 211 |
F-Score
Der F-Score kombiniert die Precision und den Recall, in dem der harmonische Mittelwert gebildet wird. Diese Metrik kann auch wieder für jede Klasse seperat ermittelt werden.
Hier wird der F-Score für die Klasse positive
gebildet.
Alle Metriken
-----------------+----------+---------+----------
| | positive | neutral | negative |
-----------------+----------+---------+----------
| konrad positive | 774 | 431 | 87 |
-----------------+----------+---------+----------
| konrad neutral | 319 | 944 | 703 |
-----------------+----------+---------+----------
| konrad negative | 10 | 55 | 211 |
-----------------+----------+---------+----------
Accuracy: 0.55
Precision (positive): 0.60
Precision (neutral): 0.48
Precision (negative): 0.76
Precision: 0.59
Recall (positive): 0.70
Recall (neutral): 0.66
Recall (negative): 0.21
Recall: 0.39
F-Score (positive): 0.65
F-Score (neutral): 0.56
F-Score (negative): 0.33
F-Score: 0.47