diff --git a/Dokumentation/4_ergebnisse.tex b/Dokumentation/4_ergebnisse.tex index 6a1ed8bd273fba6a9b9f2c1a278f20d4735dcb97..b9983d66923d3affa1d43ec3f2e5ece390d0cdac 100644 --- a/Dokumentation/4_ergebnisse.tex +++ b/Dokumentation/4_ergebnisse.tex @@ -1,8 +1,4 @@ \section{Ergebnisse \label{sec:Ergebnisse}} -\input{Auswertung} - -\subsection{Vorhersage und Analyse} - -\subsection{Visualisierung} \ No newline at end of file +\input{Auswertung} \ No newline at end of file diff --git a/Dokumentation/Auswertung.tex b/Dokumentation/Auswertung.tex index 27e6433b8092329acbb18e252c708a0fdbc1be37..4a7be04abebd295c26603580e0e37860d6b439fd 100644 --- a/Dokumentation/Auswertung.tex +++ b/Dokumentation/Auswertung.tex @@ -7,8 +7,42 @@ Die in dieser Arbeit verwendete Methode "`Word2Vec"' ist besonders anfällig fü % Die Aufgabe an sich ist bereits subjektiv, da Sprache subjektiv ist und diese % auch noch in einem meinungsbehafteten Raum genutzt wird -Gleichzeitig sind diese Subjektivitäten aber auch in vielen Fällen wichtig. Sprache ist in der Regel meinungsbehaftet und viele machine learning Projekte beschäftigen sich damit. So auch dieses Projekt zu einem gewissen Grad. Um reviews in entsprechende Kategorien einzuteilen müssen die Wörter anhand ihrer gebräuchliche Nutzung eingeordnet werden, im Idealfall speziell in diesem Szenario um kontextspezifische Unterschiede zu vermeiden. Wenn ein Wort also entgegengesetzt der ursprünglichen Bedeutung in einem völlig anderen Kontext verwendet wird so stellt dies eine wichtige Information dar, welche für die Auswertung beachtet werden muss. \wip{Ich hab das Gefühl das endet abrupt und man könnte hier noch ein paar Worte zu sagen, aber ich tu mich schwer damit} +Gleichzeitig sind diese Subjektivitäten aber auch in vielen Fällen wichtig. Sprache ist in der Regel meinungsbehaftet und viele machine learning Projekte beschäftigen sich damit. So auch dieses Projekt zu einem gewissen Grad. Um Reviews in entsprechende Kategorien einzuteilen müssen die Wörter anhand ihrer gebräuchliche Nutzung eingeordnet werden, im Idealfall speziell in diesem Szenario, um kontextspezifische Unterschiede zu vermeiden. Wenn ein Wort also entgegengesetzt der ursprünglichen Bedeutung in einem völlig anderen Kontext verwendet wird, stellt dies eine wichtige Information dar, welche für die Auswertung beachtet werden muss. \wip{Ich hab das Gefühl das endet abrupt und man könnte hier noch ein paar Worte zu sagen, aber ich tu mich schwer damit} % Gleicher Schreibstil wie Leute welche häufig negativ gewertet haben: Automatisch negativ -\wip{Nicht sicher ob dies das richtige Kapitel für diesen Punkt ist, aber wüsste auch nicht wo sonst hin damit} -Schreibstyle unterscheiden sich von Person zu Person. Besonders in einem informellen Raum wie es das Internet häufig ist zeigt sich dies. Gewisse Wortwahl und Zeichensetzung ist für die einen ein eindeutiges Zeichen für Sarkasmus, während andere diese Aussagen ernst nehmen. Auch Akzente und Dialekte spiegeln sich teilweise durch Slangwörter oder sonstige Besonderheiten im schriftlichen wieder. Dies kann verschiedene Auswirkungen auf die Kategorisierung von Texten haben. Eine mögliches Szenario wäre, dass ein spezieller Dialekt häufig mit negativen reviews vorkommt. Die Ursache dafür könnte dabei einen spezifischen Grund haben oder auch rein zufällig sein. Unabhängig davon kann dies jedoch dazu führen, dass das Programm diese \emph{Korrelation} entdeckt und eine \emph{Kausalität} herstellt. Folglich werden alle reviews, welche diesen Dialekt enthalten als negativ kategorisiert, darunter auch solche, welche ursprünglich positiv waren. +Schreibstile unterscheiden sich von Person zu Person. Besonders in einem informellen Raum wie es das Internet häufig ist zeigt sich dies. Gewisse Wortwahl und Zeichensetzung ist für die einen ein eindeutiges Zeichen für Sarkasmus, während andere diese Aussagen ernst nehmen. Auch Akzente und Dialekte spiegeln sich teilweise durch Slangwörter oder sonstige Besonderheiten im schriftlichen wieder. Dies kann verschiedene Auswirkungen auf die Kategorisierung von Texten haben. Eine mögliches Szenario wäre, dass ein spezieller Dialekt häufig mit negativen Reviews vorkommt. Die Ursache dafür könnte dabei einen spezifischen Grund haben oder auch rein zufällig sein. Unabhängig davon kann dies jedoch dazu führen, dass das Programm diese \emph{Korrelation} entdeckt und eine \emph{Kausalität} herstellt. Folglich werden alle Reviews, welche diesen Dialekt enthalten als negativ kategorisiert, darunter auch solche, welche ursprünglich positiv waren. + +\subsection{Auswertung} +%%Gegenüberstellung Konfusionsmatrix & ergebnisse +%tab:m_w +%tab:conf_no_w +%tab:conf_w_cnn +Im Folgenden werden die Ergebnisse der Mean- und der CNN-Methode ausgewertet und gegenübergestellt. Zunächst wird jedoch der Einfluss der Trainingsgewichte untersucht. + +\subsubsection{Gewichte} +Das Mean-Vektor-Klassifikationsmodell wurde sowohl mit, als auch ohne Trainingsgewichte getestet. Dabei hat die Variante ohne extra Gewichte ein allgemein besseres Ergebnis mit $85.70\%$ gegenüber $80.02\%$ ohne Gewichte. Die Konfusionsmatrizen aus den Tabellen \ref{tab:m_w} und \ref{tab:conf_no_w} zeigen jedoch, dass die Verteilung der richtig bestimmten Reviews mit Gewichten gleichmäßiger ist. Ohne Gewichte werden mehr negative und positive Reviews richtig klassifiziert, aber die Anzahl der richtig bestimmten neutralen Reviews beträgt gerade einmal knapp über $27\%$ gegenüber $68\%$ mit Gewichten. Außerdem werden mehr als dreimal so viele neutrale Reviews als positiv identifiziert, wenn die Gewichte weg gelassen werden. +Unter Berücksichtigung der ursprünglichen Distribution des Datensatzes, wie zuvor in Bild \ref{fig::DST} gezeigt, wird auch deutlich, wieso diese 'Entscheidung' für den Algorithmus vom Vorteil ist. Es gibt eine hohe Anzahl an positiven Reviews, während es verhältnismäßig wenig negative, dafür aber nur einen Bruchteil an neutralen Reviews gibt. Wie bereits in Kapitel \ref{subsubsec:Dist} angesprochen entsteht dadurch unweigerlich ein bias zu einer Identifizierung als positive Review, da dies im Gesamtergebnis zu einem größeren Erfolg führt. +Es lässt sich also erkennen, dass die Gewichtung der Daten durchaus den gewünschten Effekt erzielt, jedoch darunter das Gesamtergebnis etwas gelitten hat. + +\subsubsection{Mean vs. CNN +\label{subsub:MvsC}} +Für die Gegenüberstellung der verschiedenen Methoden werden nur die Ergebnisse der Mean Methode mit Gewichten berücksichtigt, um so ein ausgeglicheneres Ausgangsszenario zu erhalten. Hier lässt sich zwar erkennen, dass die CNN Methode geringfügig besser abgeschnitten hat, mit $81.44\%$ gegenüber $80.02\%$, jedoch ist dieser Unterschied wirklich minimal. Die Methoden können daher als gleichwertig betrachtet werden. Auch die Konfusionsmatrizen \ref{tab:m_w} und \ref{tab:conf_w_cnn} sind annähernd identisch. Das CNN Modell hat lediglich wieder weniger Wert auf neutrale Reviews gelegt und stattdessen eine positive und negative Kategorisierung bevorzugt. + +%Mean: 10s epoche, 4ms pro Step; 2048 batchsize +%CNN, groß: 7min epoche, ?? pro Step;256 batchsize +%CNN, RAM: 17s epoche, 21ms step;128 batchsize +Aufgrund der ähnlichen Ergebnisse ist jedoch die Mean Methode deutlich zu bevorzugen. Zum einen benötigt diese in der Regel weniger Vorbereitung, hier in Form der Generatoren. Das liegt daran, dass die Mean Methode schwächer skalierende Anforderungen an die Hardware hat. Dadurch ist auch die Trainingszeit um ein vielfaches schneller mit ca.\@ 10s pro Epoche gegenüber ungefähr 7min. Der Großteil dieses Unterschieds entsteht dabei durch das Lesen von der Festplatte durch die Generatoren anstelle des Arbeitsspeichers. Aber auch Konfigurationsunterschiede sind dabei zu beachten, z.\,.B\@ nutzt der Mean eine wesentlich größere batchsize von 2048. Im Vergleich arbeitet der CNN nur mit einer Größe von 256. +%Aber auch in kleineren Beispielen, bei denen der CNN auch ausschließlich den Arbeitsspeicher verwenden kann, schneidet der Mean besser ab. Hier benötigt der CNN immer noch ca.\@ 17s pro Epoche. Jedoch sind hier zusätzlich unterschiedliche batchsizes von 2048 + +Allerdings bietet das CNN Modell mehr Möglichkeiten an der Struktur zu arbeiten als die Mean Methode. Während am Mean nur wenige Änderungen vorgenommen werden könnten bietet der CNN ein wesentlich größeres Potenzial. Es besteht also durchaus die Möglichkeit, dass ein ausführlicheres Experimentieren mit dem Aufbau des CNN zu konstant besseren Ergebnissen führt. \wip{Dies konnte in dieser Arbeit aufgrund von Zeitproblemen leider nicht geprüft werden. (Von Zeitproblemen bei ner Arbeit zu reden, welche n halbes Jahr ging is vlt n bissl dreist xD)} + +\subsubsection{Problemstellung} +Die relativ geringe Erfolgsquote der Modelle, aber z.\,B.\@ auch die Schwierigkeiten mit neutralen Reviews zeigen sicherlich, dass es noch Optimierungsmöglichkeiten gibt. Dennoch liegt ein großer Teil der Herausforderung auch an der eigentlichen Problemstellung. Die meisten Menschen hätten vermutlich auch Schwierigkeiten dabei eine 2 Sterne Review, sprich einer negativen, von einer 3 Sterne, also neutralen, zu unterscheiden. Zum einen sind die Übergänge recht fließend. Zum Anderen sind Menschen aber auch einfach unterschiedlich: Der gleiche Text mit den identischen Argumenten könnte für eine Person eine 3 Sterne und für die nächste eine 4 Sterne Review sein. Aus der Sicht des Modells gäbe es also 2 Einträge mit identischen Merkmalen, welche jedoch unterschiedlichen Kategorien zugeordnet werden. + +\subsubsection{Fazit} +Zusammenfassend lässt sich sagen, dass Word2Vec ein valider erster Schritt zur Klassifizierung von Reviews ist. Die Ergebnisse zeigen dabei jedoch, dass noch Optimierungsbedarf herrscht. Dabei ist es aber unklar ob für die genutzten Modelle idealere Strukturen möglich sind oder ob diese Werte eine Obergrenze für diese Problemstellung in Kombination mit ebendiesen Modellen darstellen. +Davon ausgehend, dass die Ergebnisse eine Obergrenze darstellen ist die Mean Methode aufgrund der in Kapitel \ref{subsub:MvsC} besprochenen Punkte deutlich dem CNN Ansatz vorzuziehen. + +Das deutliche Hauptproblem der Klassifizierung stellen die neutralen Reviews dar. Ein möglicher Ansatz wäre die Fragestellung auf eine Kategorisierung in positiv und negativ zu beschränken. Dadurch wird das Problem jedoch nicht gelöst sondern lediglich verschoben. Eine weitere Alternative wäre das Ignorieren von neutralen Reviews, sodass nur mit 1 und 2 sowie 4 und 5 Sterne Reviews gearbeitet wird. Dies wäre aber nur sinnvoll, wenn der geplante Nutzen dieser Klassifizierung das zulässt. Dadurch, dass die Anzahl der neutralen Reviews jedoch ohnehin verhältnismäßig sehr gering ist könnte dies z.\,B.\@ in der Marketinganalyse durchaus eine Option sein. + +%\wip{Ein Vergleich mit einem anderen Projekt mit ähnlicher Problemstellung zeigt, dass sich die hier erarbeiteten Ergebnisse im oberen Bereich bewegen. Dabei wurde im erwähnten Report jedoch weder eins der hier genutzten Modelle noch eine Vorverarbeitung durch Word2Vec verwendet. (Quelle vorhanden, aber unsicher)} \ No newline at end of file diff --git a/Dokumentation/Generatoren.tex b/Dokumentation/Generatoren.tex index 2d71b8132297aa08b9f6ea35ac84ed806081a960..aabcb4106a4154871fceccb421661795b8056591 100644 --- a/Dokumentation/Generatoren.tex +++ b/Dokumentation/Generatoren.tex @@ -23,8 +23,9 @@ Die Daten müssen bei der CNN-Methode also nur stückweise in den Arbeitsspeiche %Erster Ansatz, sehr lange validierungszeit Ein möglicher Ansatz ist die rohen Daten nur schrittweise einzulesen und diese dann erst dem word2vec model zu übergeben, um daraus einen Vektor zu machen. Dafür wird eine Funktion geschrieben, welche dann der fit Methode des eigentlichen machine learning models als Datensatz-parameter übergeben wird. Dabei wird in der Funktion die batchsize berücksichtigt, sodass stets die gewünschte Menge an Daten gleichzeitig in das Modell geladen werden. +%% Aufteilung der Daten Um dies jedoch zu erfüllen müssen die Daten nach ihrem Nutzen in verschiedene Dateien aufgeteilt werden. Dementsprechend eine Trainings-, Validierungs- und Test-Datei. -\begin{lstlisting}[label={lst:Aufteilung}] +\begin{lstlisting}[label={lst:Aufteilung}, caption=Datenaufteilung] val = open('val.json','w',encoding='utf8') test = open('test.json','w',encoding='utf8') train = open('train.json','w',encoding='utf8') @@ -43,12 +44,13 @@ test.close() train.close() \end{lstlisting} Das Aufteilen wird durch ein einfaches Zählen und Sortieren gelöst. Dabei zählt eine Variable, hier \lstinline{i}{}, hoch und je nachdem welchen Wert diese Variable hat werden die Daten in die entsprechende Datei sortiert. Das Ziel ist eine 60-20-20 Aufteilung der Daten. Dafür werden bei den Werten \lstinline{0, 1}{} und \lstinline{2}{} die Daten in die Trainingsdatei geschrieben und dementsprechend bei den Werten \lstinline{3}{} und \lstinline{4}{} in die Validierungs-, bzw.\@ Testdatei. -Wenn in die Testdatei geschrieben wird, wird auch gleichzeitig die Zählervariable zurückgesetzt, sodass mit den gleichen Werten von vorne begonnen werden kann. Hier wurde sich bewusst für ein Zurücksetzen der Variable entschieden und gegen ein kontinuierliches Hochzählen und der Sortierung durch Modulo: Auf diese Weise werden bedenkenlos Overflow- und Signifikanzprobleme verhindert, ohne einen deutlichen Verlust der Lesbarkeit des Codes hinnehmen zu müssen. \wip{Da der genutzte Datensatz nur etwas über 8 Millionen Einträge besitzt und mit Ganzzahlen gearbeitet wird wären beide Probleme zu vernachlässigen gewesen.} +Wenn in die Testdatei geschrieben wird, wird auch gleichzeitig die Zählervariable zurückgesetzt, sodass mit den gleichen Werten von vorne begonnen werden kann. Hier wurde sich bewusst für ein Zurücksetzen der Variable entschieden und gegen ein kontinuierliches Hochzählen und der Sortierung durch Modulo: Auf diese Weise werden bedenkenlos Overflow- und Signifikanzprobleme verhindert, ohne einen deutlichen Verlust der Lesbarkeit des Codes hinnehmen zu müssen. Da der genutzte Datensatz nur etwas über 8 Millionen Einträge besitzt und mit Ganzzahlen gearbeitet wird wären beide Probleme zu vernachlässigen gewesen. Diese Art der Aufteilung ist jedoch nur bedenkenlos einzusetzen in diesem Projekt, da die Daten ohnehin unabhängig sind und ebenfalls unsortiert vorliegen. Aus diesen Gründen sind die drei Datensätze auch ohne extra shuffle zufällig genug. -\wip{Überleitung zum Generator? Oder das Aufteilen der Daten in eigenes Kapitel?} +%%Start eigentlicher Generator +Um diese Dateien nun während des Trainings schrittweise auszulesen muss ein sogenannter Generator erstellt werden. Dieser liest die Daten zum Teil aus, überträgt sie in das Word2Vec Modell und stellt sie anschließend dem Modell zur Verfügung. -\begin{lstlisting} +\begin{lstlisting}[caption=Overhead einfacher Generator] import numpy as np import json def generate_arrays_from_file(path, batchsize): @@ -61,22 +63,22 @@ def generate_arrays_from_file(path, batchsize): json_line = json.loads(line) \end{lstlisting} Zunächst ein wenig overhead, bevor wir dann die entsprechende Datei zeilenweise durchgehen. Die Zeile 7 mit \lstinline{while True:}{} wird dabei vom Keras Modell erwartet, denn das Trainieren soll nicht mitten in einer Epoche enden, weil das Ende der Trainingsdatendatei erreicht wurde. -\begin{lstlisting} +\begin{lstlisting}[caption=Sortierung einfacher Generator, firstnumber=10,label={list:Gen_Sort}] try: inputs.append(getSentenceVectorCNN(json_line["text"])) y = float(json_line["stars"]) if(y <3): - targets.append([0,0,1]) + targets.append(0) elif(y==3): - targets.append([0,1,0]) + targets.append(1) else: - targets.append([1,0,0]) + targets.append(2) batchcount += 1 except: continue \end{lstlisting} -Anschließend wird aus dem eigentlichen Text der Review ein Wortvektor für das CNN gemacht, \wip{wie zuvor in dem Kapitel X.X (Referenz) beschrieben}. Das Ergebnis ob diese review als positiv, negativ oder neutral gelten soll wird in diesem Beispiel One-Hot-Encoded abgespeichert. Sollte eine Exception geschmissen werden, weil z.\,B.\@ kein einziges Wort, welches in der Review vorkommt im word2vec model vertreten sein, wird diese Review einfach übersprungen. -\begin{lstlisting} +Anschließend wird aus dem eigentlichen Text der Review ein Wortvektor für das CNN gemacht. Das Vorgehen dafür wird in Kapitel \ref{subsubsec:CNN_Impl} im Listing \ref{list:getSentenceVector} beschrieben. Das Ergebnis ob diese Review als positiv, negativ oder neutral gelten soll wird in diesem Beispiel als Index Kategorie gespeichert. Sollte eine Exception geschmissen werden, weil z.\,B.\@ kein einziges Wort, welches in der Review vorkommt im Word2Vec Modell vertreten sein, wird diese Review einfach übersprungen. +\begin{lstlisting}[caption=Yield der Daten,firstnumber=22] if batchcount > batchsize: X = np.array(inputs) y = np.array(targets) @@ -89,13 +91,13 @@ Abschließend werden die beiden Listen returned und die entsprechenden Variablen \begin{equation} 512 \times 72 \times 100 \times 8 \approx 28\text{ MB} \end{equation} -Ein deutlicher Nachteil bei diesem Vorgehen ist jedoch, dass dies sehr zeitaufwändig ist. Nicht nur müssen die Daten während des Trainings von der Festplatte anstelle des Arbeitsspeichers gelesen werden. Sondern diese Daten müssen zunächst auch noch in das word2vec Modell überführt werden. Beides sind verhältnismäßig langsame Prozesse, wodurch die Trainingszeit enorm steigt. Aber auch die Validierung benötigt nun in etwa genau so viel Zeit wie das Training an sich. +Ein deutlicher Nachteil bei diesem Vorgehen ist jedoch, dass dies sehr zeitaufwändig ist. Nicht nur müssen die Daten während des Trainings von der Festplatte anstelle des Arbeitsspeichers gelesen werden. Sondern diese Daten müssen zunächst auch noch in das Word2Vec Modell überführt werden. Beides sind verhältnismäßig langsame Prozesse, wodurch die Trainingszeit enorm steigt. Aber auch die Validierung benötigt nun in etwa genau so viel Zeit wie das Training an sich. Dafür ist diese Methode recht simpel und sie benötigt keinen zusätzlichen Festplattenspeicher. Jedoch muss hierfür der Datensatz für Trainings-, Validierungs- und Testmenge in verschiedene Dateien aufgeteilt werden. Sollte also der Festplattenspeicher und nicht so sehr die Zeit ein begrenzender Faktor sein, ist dies ein durchaus valider Ansatz. \subsubsection{hdf5} %Bessere Zeiten, aber hoher Speicherplatz Ein weiterer Ansatz ist die bereits in das Word2Vec Modell überführten Wörter, also die Vektoren an sich abzuspeichern. Dafür eignen sich z.\,B.\@ hdf5 Dateien, welche einen weiteren Vorteil besitzen auf den später eingegangen wird. Das Prinzip ist bei dieser Methode nahezu identisch zu der zuvor besprochenen, auch hier wird der Fit-Methode ein Generator gegeben, dieser liest jedoch nur die entsprechende Datei. Zunächst muss aber der Datensatz in das Word2Vec Modell überführt und entsprechend abgespeichert werden. Dies beginnt auch hier wieder mit ein wenig overhead: -\begin{lstlisting} +\begin{lstlisting}[caption=Overhead hdf5 Generator] import h5py path = "path/to/my/workspace/" @@ -114,7 +116,7 @@ with h5py.File(path + "w2vCNN.hdf5", "w") as hf: yTest = [] \end{lstlisting} Zu Beginn initialisieren wir eine Zähler-Variable auf 0, welche wir später für unsere Aufteilung in Trainings-, Validierungs- und Testmenge nutzen. Für diese Mengen und ihre jeweiligen Input- und Outputdaten bereiten wir auch ein paar Listen vor. In hdf5 Dateien kann man die Daten in sogenannte Chunks aufteilen, welche das schrittweise speichern möglich macht, aber auch das laden verbessern soll. Die Größe dieser Chunks legen wir zunächst auf 10.000 fest. Da wir einen 60-20-20 split unseres Datensatzes haben wollen speichern wir uns ebenfalls die Größe dieser einzelnen Chunks. -\begin{lstlisting} +\begin{lstlisting}[caption=Sortierung hdf5 Generator, firstnumber=16] for index, line in enumerate(open(corpus_path,encoding="utf8")): json_line = json.loads(line) #X Data @@ -131,8 +133,8 @@ Zu Beginn initialisieren wir eine Zähler-Variable auf 0, welche wir später fü else: yData = 2 \end{lstlisting} -Anschließend wird quasi identisch \wip{zu der anderen Methode (Ref?)}der rohe Datensatz ausgelesen und entsprechend in nutzbare x und y Daten umgewandelt. -\begin{lstlisting} +Anschließend wird quasi identisch zu dem Vorgehen im einfachen Generator aus Listing \ref{list:Gen_Sort} der rohe Datensatz ausgelesen und entsprechend in nutzbare x und y Daten umgewandelt. +\begin{lstlisting}[caption=Zuordnung hdf5 Generator, firstnumber=31] if i < 3: xTrain.append(xData) yTrain.append(yData) @@ -146,7 +148,7 @@ Anschließend wird quasi identisch \wip{zu der anderen Methode (Ref?)}der rohe D i += 1 \end{lstlisting} Als nächstes werden die Daten den entsprechenden Listen angehangen. Das Vorgehen hier orientiert sich dabei an der zuvor besprochenen Aufteilung aus dem Code-Schnipsel \ref{lst:Aufteilung}. -\begin{lstlisting} +\begin{lstlisting}[caption=Erstellen der hdf5 Datasets, firstnumber=42] if index == chunkSize - 1: XTrain = hf.create_dataset("XTrain", data=xTrain, maxshape=(None, 72, 100), chunks=(trainChunk, 72, 100)) YTrain = hf.create_dataset("YTrain", data=yTrain, maxshape=(None, 3), chunks=(trainChunk, 3)) @@ -165,9 +167,9 @@ Als nächstes werden die Daten den entsprechenden Listen angehangen. Das Vorgehe xTest = [] yTest = [] \end{lstlisting} -Wenn das erste mal genug Daten in den buffer-listen liegen können diese noch nirgends appended werden, da in der entsprechenden Datei noch nichts vorliegt wo diese appended werden können. Damit kommen wir auch zu einem weiteren Vorteil der hdf5 Dateien: Wir können verschiedene Datasets, mit entsprechenden Labeln, in der gleichen Datei speichern und diese später auch einfach wieder auslesen. \\ +Wenn das erste Mal genug Daten in den buffer-listen liegen können diese noch nirgends appended werden, da in der entsprechenden Datei noch nichts vorliegt wo diese appended werden können. Damit kommen wir auch zu einem weiteren Vorteil der hdf5 Dateien: Wir können verschiedene Datasets, mit entsprechenden Labeln, in der gleichen Datei speichern und diese später auch einfach wieder auslesen. \\ Diese Datasets müssen aber zunächst erstellt werden. Dafür geben wir zum einen das Label des Datasets an; die Dimensionen der maxshape, welche als Anzahl der Datensätze den Eintrag None bekommt, da wir einfach alle Daten die wir haben speichern wollen; die Dimensionen der chunks, wofür wir die eingangs berechneten Chunkgrößen nun nutzen; und zu guter Letzt natürlich die tatsächlichen Daten, welche stets die gleichen Dimensionen haben müssen wie die Chunks. Abschließend resetten wir die buffer-listen um keine Daten mehrfach zu speichern. -\begin{lstlisting} +\begin{lstlisting}[caption=Nachfüllen der hdf5 Datasets, firstnumber=59] if (index+1) % chunkSize == 0 and index > chunkSize: XTrain.resize(XTrain.shape[0]+trainChunk, axis=0) XTrain[-trainChunk:] = xTrain @@ -197,7 +199,7 @@ Diese Datasets müssen aber zunächst erstellt werden. Dafür geben wir zum eine \end{lstlisting} Für das appenden der Daten im folgenden Verlauf müssen die Datasets stets um die entsprechende Größe resized werden. Anschließend werden die neuen Felder entsprechend aufgefüllt und zu guter Letzt wieder die buffer-listen resetted.\\ Das Auslesen der Daten erfolgt letztlich ähnlich wie bei dem anderen vorgestellten Generator: -\begin{lstlisting} +\begin{lstlisting}[caption=hdf5 Generator] def hdf5Generator(filePath, batch_size, dataSet, loop=True): with h5py.File(filePath, 'r') as hf: L = len(hf["X" + dataSet]) diff --git a/Dokumentation/silas/w2vCNN.tex b/Dokumentation/silas/w2vCNN.tex index 5ff1aee0be19ba007316b5b3680e02ce1734f0a8..4b52e5d0bf261fe2c059a996d9fb9a4b6a57de98 100644 --- a/Dokumentation/silas/w2vCNN.tex +++ b/Dokumentation/silas/w2vCNN.tex @@ -1,235 +1,236 @@ -\newpage -\subsection{Word2Vec-CNN-Modell} -Ein \noteable{convolutional neural network}, kurz CNN, ist in der Lage -lokale Muster in einer Sequenz zu erlernen und diese später -wiederzuerkennen, auch wenn diese an einer anderen Stelle auftreten. - -In diesem Modell wird eine Reihe an Wortvektoren an das CNN übergeben, das Extrahieren der Merkmale wird somit Teil des Optimierungsproblems. -\subsubsection{Implementierung} -Aufgrund des sehr hohen Speicherbedarfs wird der im Kapitel \ref{subsubsec:simpleGen} geschriebene Generator -verwendet. - -Hierfür muss jedoch zunächst die Funktion \lstinline{getSentenceVectorCNN} implementiert werden. - -\begin{lstlisting}[caption={Datentransformation},label={list:getSentenceVector}] -import numpy as np -from gensim import utils -from w2v_yelp_model import getWordVecMode - -model_path = "full_yelp_w2v_model" -modelW2V = getWordVecModel(model_path) - -def getSentenceVectorCNN(sentence): - split = utils.simple_preprocess(sentence) - wordVecs = np.zeros((72,100)) - i=0 - for word in split: - if i == 72: break - try: - wordVecs[i] = modelW2V.wv[word] - i += 1 - except: - pass - if np.all(wordVecs[5:]==0): - raise Exception('not enough words found in w2v model') - return wordVecs -\end{lstlisting} - -Zuerst wird in Zeile 5 die im Listing \ref{list:gw2v} geschriebene Funktion -\lstinline{getWordVecMode} aufgerufen, um das Word2Vec-Modell zu erhalten. - -Die Funktion \lstinline{getSentenceVectorCNN} in Zeile 7 nimmt eine Rezension entgegen und -transformiert diese in eine für das CNN verarbeitbare Form. -\wip{genauer erklären?} - -\wip{komischer Satz...} -Dafür wird die Rezension in Zeile 8 tokenisiert, sodass in der Variable -\lstinline{split} eine Liste der, durch die Funktion \lstinline{utils.simple_preprocess} -vorverarbeiteten, Wörter gespeichert ist. - -Danach wird in Zeile 9 das Numpy-Array wordVecs mit Nullen initialisiert. -Dieses Numpy-Array speichert die ersten 72 Wortvektoren der Rezension, -die Zahl 72 wurde aufgrund des im Kapitel \ref{subsubsec:reviewlength} berechneten -Median der Reviewlänge gewählt. - -Die for-Schleife in Zeile 11 durchläuft alle Wörter der Liste split und fügt diese, -falls sie im word2Vec-Modell vertreten sind, in das \lstinline{wordVecs} Array ein. - -Sind mehr als 72 Wörter in der Liste \lstinline{Split} wird die Schleife abgebrochen, -wenn das Array vollständig gefüllt ist. - -Sollten sich weniger als 72 Wörter in der Liste befinden, -so ist das Array bereits mit Nullen initialisiert. - -Dies kann als \noteable{Truncating} und \noteable{Padding} bezeichnet werden und -ist notwendig, -da das CNN eine feste Dimension der Eingangsdaten benötigt. - -Anschließend wird in Zeile 18 geprüft, ob weniger als 5 Wortvektoren in dem Array -stehen, ist dies der Fall, wird eine Exception geworfen. - -Nach dem implementieren der Funktion \lstinline{getSentenceVectorCNN} muss das CNN erstellt werden. -\begin{lstlisting}[caption={CNN},label={list:modelCNN}] -import numpy as np -from tensorflow.keras.models import Sequential -from tensorflow.keras.layers import Dense, Flatten -from tensorflow.keras.layers import Conv1D,MaxPooling1D -from tensorflow import keras - -modelNN = Sequential() - -modelNN.add(Conv1D(150,kernel_size=5, activation='relu',input_shape=((72, 100)))) -modelNN.add(MaxPooling1D(pool_size=4)) -modelNN.add(Conv1D(100,kernel_size=3, activation='relu')) -modelNN.add(MaxPooling1D(pool_size=4)) -modelNN.add(Flatten()) -modelNN.add(Dense(300,activation='relu')) -modelNN.add(Dense(100,activation='relu')) -modelNN.add(Dense(50,activation='relu')) -modelNN.add(Dense(10,activation='relu')) -modelNN.add(Dense(3,activation='softmax')) -modelNN.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=["sparse_categorical_accuracy"]) -modelNN.summary() -\end{lstlisting} -Hier wurden dem Netzwerk zwei \noteable{Conv1D-Schichten} hinzugefügt, mit der -Aktivierungsfunktion -\lstinline{relu}. Die erste Schicht hat hier 150 Filter und eine -Kernelgröße von fünf. Die zweite Schicht hat hingegen 100 Filter und eine -Kernelgröße von drei. - -Die \noteable{MaxPooling1D-Schichten} dienen dazu die Informationen nach den -Faltungen zu verdichten. - -Die Matrix, die als Ergebnis der zweiten \noteable{MaxPooling1D-Schicht} entsteht, -wird anschließend in Zeile 12 in einen Vektor umgewandelt, -dieser Vektor wird als Eingang des dichten neuronalen Netzes benutzt. -Für das dichte neuronale Netz wird hier wieder der Ansatz verfolgt, -die Schichten inkrementell zu verkleinern, um eine Verdichtung der -Information zu erzwingen. - -Zuletzt muss das neuronale Netz noch kompiliert werden, dies geschieht in -Zeile 18. Hierbei wird wie gehabt der optimizer \lstinline{adam} gewählt und die -Fehlerfunktion \lstinline{sparse_categorical_crossentropy} sowie die Metrik -\lstinline{sparse_categorical_accuracy} verwendet. - -Die Zusammenfassung des Modells, die durch Zeile -19 erzeugt wurde, befindet sich im Listing \ref{list:modelCNNSummary}. - -\begin{lstlisting}[caption={CNN - Summary},label={list:modelCNNSummary},numbers=none, basicstyle=\normalsize] -Model: "sequential" -______________________________________________________________ -Layer (type) Output Shape Param # -============================================================== -conv1d (Conv1D) (None, 68, 150) 75150 -______________________________________________________________ -max_pooling1d (MaxPooling1D) (None, 17, 150) 0 -______________________________________________________________ -conv1d_1 (Conv1D) (None, 15, 100) 45100 -______________________________________________________________ -max_pooling1d_1 (MaxPooling1 (None, 3, 100) 0 -______________________________________________________________ -flatten (Flatten) (None, 300) 0 -______________________________________________________________ -dense (Dense) (None, 300) 90300 -______________________________________________________________ -dense_1 (Dense) (None, 100) 30100 -______________________________________________________________ -dense_2 (Dense) (None, 50) 5050 -______________________________________________________________ -dense_3 (Dense) (None, 10) 510 -\end{lstlisting} - - -Um das Modell mit Generatoren zu trainieren, muss -aufgrund der für das Netzwerk unbekannten Größe der Trainingsmenge -angeben werden, wie viele Schritte pro Epoche erfolgen sollen. - - -\begin{lstlisting}[caption={CNN - Fitting},label={list:modelCNNFit},firstnumber=20] -from hdf5 import hdf5Generator -num_rows = 4.8E6 -batchSize = 256 -steps = num_rows/batchSize -#early stop -earlystop = keras.callbacks.EarlyStopping(monitor='val_sparse_categorical_accuracy',patience=8,verbose=False,restore_best_weights=True) -cbList = [earlystop] -trainData = hdf5Generator("w2vCNN.hdf5", batchSize, "Train") -valData = hdf5Generator("w2vCNN.hdf5", batchSize, "Val") - -#%%fit -cW = {0:4.18,1:9.51,2:1.52} -hist = modelNN.fit(trainData, validation_data=valData, epochs=50,class_weight=cW, steps_per_epoch=steps, validation_steps=int(steps/3),callbacks=cbList - -testData = hdf5Generator(path + "w2vCNN.hdf5", batchSize, "Test",loop=False) -modelNN.evaluate(testData) -\end{lstlisting} - -Hierfür wird die Größe der Trainingsmenge durch die BatchSize geteilt (Zeile 24). -Um ein Overfitting zu verhindern, wird, wie beim Mean-Modell auch schon in -Zeile 25 ein Earlystop definiert. -Dieser unterbricht das Training vorzeitig, falls acht Epochen lang keine -Verbesserung auf der Validierungsmenge erzielt wird. - -Die Generatoren für die Trainings- und Testmenge werden in -Zeile 27 und 28 variablen gespeichert. - -Die Klassengewichte in Zeile 31 wurden im Listing \ref{list:modelCNNWeights} berechnet. -Mit allen Anforderungen erfüllt kann jetzt in Zeile 32 das Modell trainiert werden. -\wip{schwer zu versteheneder Satz}Dabei ist zu beachten, dass die Anzahl der Schritte pro -Epoche für die Validierungsmenge gedrittelt wird, da -die Trainingsmenge $\frac{3}{5}$ des Datensatzes ausmacht die Validierungsmenge -hingegen nur $\frac{1}{5}$ wird -trotzdem nach jeder Epoche die gesamte Validierungsmenge evaluiert. - -Nach dem Training des Netzes ist es an der Zeit das Modell zu evaluieren. -Dafür wird in Zeile 34 zunächst wieder ein Generator angelegt. Mit dem -Parameter \lstinline{loop=False} wird gewährleistet, dass der Generator den Datensatz nur -einmal durchläuft. Dieser Generator kann nun in -Zeile 35 an die Methode \lstinline{evaluate} übergeben werden. -Das Evaluieren zeigt, dass dieses Modell zu $81.44\%$ die Klassen der Testmenge richtig klassifiziert. - -\begin{lstlisting}[caption={CNN - Klassengewichte},label={list:modelCNNWeights},numbers=none] -Y_train=[] -gen = hdf5Generator(path + "w2vCNN.hdf5", batchSize, "Test",loop=False) -for (x,y) in gen: - Y_train.append(y) -Y_train = np.array(Y_train).flatten() -count = np.unique(Y_train,return_counts=True)[1] -cWeight = 1/(count/Y_train.size) -\end{lstlisting} -\subsubsection{Konfusionsmatrix} -Um einen besseren Eindruck über die Qualität des -Netzes zu erhalten, wird auch bei diesem -Modell wieder eine Konfusionsmatrix erstellt. -\begin{lstlisting}[caption={CNN - Konfusionsmatrix},label={list:modelCNNKonf},firstnumber=36] -from sklearn.metrics import confusion_matrix -tD = hdf5Generator(path + "w2vCNN.hdf5", batchSize, "Test",loop=False) -y_pred = np.argmax(modelNN.predict(tD),axis=-1) -y_test=[] -for (x,y) in hdf5Generator(path + "w2vCNN.hdf5", batchSize, "Test",loop=False): - y_test.append(y) -y_test = np.array(y_test).flatten() - -confusion_matrix(y_test,y_pred,normalize='true') -\end{lstlisting} -Dafür werden die vom Netz vorhergesagten Klassen der Testmenge in dem -Vektor \lstinline{y_pred} gespeichert und die tatsächlichen Klassen der Testmenge in dem -Vektor \lstinline{y_test}. Beide Vektoren werden der Funktion -\lstinline{confusion_matrix} übergeben, -welche die in der Tabelle \ref{tab:conf_w_cnn} dargestellte Konfusionsmatrix -zurückliefert. - -\begin{table}[ht] - \def\arraystretch{1.3} - \begin{center} - \begin{tabular}{*{4}{R}} - - &Negativ & Neutral & Positiv\\ - Negativ &0.8314& 0.1377& 0.0308\\ - Neutral &0.1970& 0.6354& 0.1676\\ - Positiv &0.0316& 0.1316& 0.8367\\ - \end{tabular} - \end{center} - \label{tab:conf_w_cnn} - \caption{Konfusionsmatrix mit Klassengewichtung} -\end{table} -\wip{Tabelle auswerten!} +\newpage +\subsection{Word2Vec-CNN-Modell} +Ein \noteable{convolutional neural network}, kurz CNN, ist in der Lage +lokale Muster in einer Sequenz zu erlernen und diese später +wiederzuerkennen, auch wenn diese an einer anderen Stelle auftreten. + +In diesem Modell wird eine Reihe an Wortvektoren an das CNN übergeben, das Extrahieren der Merkmale wird somit Teil des Optimierungsproblems. +\subsubsection{Implementierung +\label{subsub:CNN_Impl}} +Aufgrund des sehr hohen Speicherbedarfs wird der im Kapitel \ref{subsubsec:simpleGen} geschriebene Generator +verwendet. + +Hierfür muss jedoch zunächst die Funktion \lstinline{getSentenceVectorCNN} implementiert werden. + +\begin{lstlisting}[caption={Datentransformation},label={list:getSentenceVector}] +import numpy as np +from gensim import utils +from w2v_yelp_model import getWordVecMode + +model_path = "full_yelp_w2v_model" +modelW2V = getWordVecModel(model_path) + +def getSentenceVectorCNN(sentence): + split = utils.simple_preprocess(sentence) + wordVecs = np.zeros((72,100)) + i=0 + for word in split: + if i == 72: break + try: + wordVecs[i] = modelW2V.wv[word] + i += 1 + except: + pass + if np.all(wordVecs[5:]==0): + raise Exception('not enough words found in w2v model') + return wordVecs +\end{lstlisting} + +Zuerst wird in Zeile 5 die im Listing \ref{list:gw2v} geschriebene Funktion +\lstinline{getWordVecMode} aufgerufen, um das Word2Vec-Modell zu erhalten. + +Die Funktion \lstinline{getSentenceVectorCNN} in Zeile 7 nimmt eine Rezension entgegen und +transformiert diese in eine für das CNN verarbeitbare Form. +\wip{genauer erklären?} + +\wip{komischer Satz...} +Dafür wird die Rezension in Zeile 8 tokenisiert, sodass in der Variable +\lstinline{split} eine Liste der, durch die Funktion \lstinline{utils.simple_preprocess} +vorverarbeiteten, Wörter gespeichert ist. + +Danach wird in Zeile 9 das Numpy-Array wordVecs mit Nullen initialisiert. +Dieses Numpy-Array speichert die ersten 72 Wortvektoren der Rezension, +die Zahl 72 wurde aufgrund des im Kapitel \ref{subsubsec:reviewlength} berechneten +Median der Reviewlänge gewählt. + +Die for-Schleife in Zeile 11 durchläuft alle Wörter der Liste split und fügt diese, +falls sie im word2Vec-Modell vertreten sind, in das \lstinline{wordVecs} Array ein. + +Sind mehr als 72 Wörter in der Liste \lstinline{Split} wird die Schleife abgebrochen, +wenn das Array vollständig gefüllt ist. + +Sollten sich weniger als 72 Wörter in der Liste befinden, +so ist das Array bereits mit Nullen initialisiert. + +Dies kann als \noteable{Truncating} und \noteable{Padding} bezeichnet werden und +ist notwendig, +da das CNN eine feste Dimension der Eingangsdaten benötigt. + +Anschließend wird in Zeile 18 geprüft, ob weniger als 5 Wortvektoren in dem Array +stehen, ist dies der Fall, wird eine Exception geworfen. + +Nach dem implementieren der Funktion \lstinline{getSentenceVectorCNN} muss das CNN erstellt werden. +\begin{lstlisting}[caption={CNN},label={list:modelCNN}] +import numpy as np +from tensorflow.keras.models import Sequential +from tensorflow.keras.layers import Dense, Flatten +from tensorflow.keras.layers import Conv1D,MaxPooling1D +from tensorflow import keras + +modelNN = Sequential() + +modelNN.add(Conv1D(150,kernel_size=5, activation='relu',input_shape=((72, 100)))) +modelNN.add(MaxPooling1D(pool_size=4)) +modelNN.add(Conv1D(100,kernel_size=3, activation='relu')) +modelNN.add(MaxPooling1D(pool_size=4)) +modelNN.add(Flatten()) +modelNN.add(Dense(300,activation='relu')) +modelNN.add(Dense(100,activation='relu')) +modelNN.add(Dense(50,activation='relu')) +modelNN.add(Dense(10,activation='relu')) +modelNN.add(Dense(3,activation='softmax')) +modelNN.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=["sparse_categorical_accuracy"]) +modelNN.summary() +\end{lstlisting} +Hier wurden dem Netzwerk zwei \noteable{Conv1D-Schichten} hinzugefügt, mit der +Aktivierungsfunktion +\lstinline{relu}. Die erste Schicht hat hier 150 Filter und eine +Kernelgröße von fünf. Die zweite Schicht hat hingegen 100 Filter und eine +Kernelgröße von drei. + +Die \noteable{MaxPooling1D-Schichten} dienen dazu die Informationen nach den +Faltungen zu verdichten. + +Die Matrix, die als Ergebnis der zweiten \noteable{MaxPooling1D-Schicht} entsteht, +wird anschließend in Zeile 12 in einen Vektor umgewandelt, +dieser Vektor wird als Eingang des dichten neuronalen Netzes benutzt. +Für das dichte neuronale Netz wird hier wieder der Ansatz verfolgt, +die Schichten inkrementell zu verkleinern, um eine Verdichtung der +Information zu erzwingen. + +Zuletzt muss das neuronale Netz noch kompiliert werden, dies geschieht in +Zeile 18. Hierbei wird wie gehabt der optimizer \lstinline{adam} gewählt und die +Fehlerfunktion \lstinline{sparse_categorical_crossentropy} sowie die Metrik +\lstinline{sparse_categorical_accuracy} verwendet. + +Die Zusammenfassung des Modells, die durch Zeile +19 erzeugt wurde, befindet sich im Listing \ref{list:modelCNNSummary}. + +\begin{lstlisting}[caption={CNN - Summary},label={list:modelCNNSummary},numbers=none, basicstyle=\normalsize] +Model: "sequential" +______________________________________________________________ +Layer (type) Output Shape Param # +============================================================== +conv1d (Conv1D) (None, 68, 150) 75150 +______________________________________________________________ +max_pooling1d (MaxPooling1D) (None, 17, 150) 0 +______________________________________________________________ +conv1d_1 (Conv1D) (None, 15, 100) 45100 +______________________________________________________________ +max_pooling1d_1 (MaxPooling1 (None, 3, 100) 0 +______________________________________________________________ +flatten (Flatten) (None, 300) 0 +______________________________________________________________ +dense (Dense) (None, 300) 90300 +______________________________________________________________ +dense_1 (Dense) (None, 100) 30100 +______________________________________________________________ +dense_2 (Dense) (None, 50) 5050 +______________________________________________________________ +dense_3 (Dense) (None, 10) 510 +\end{lstlisting} + + +Um das Modell mit Generatoren zu trainieren, muss +aufgrund der für das Netzwerk unbekannten Größe der Trainingsmenge +angeben werden, wie viele Schritte pro Epoche erfolgen sollen. + + +\begin{lstlisting}[caption={CNN - Fitting},label={list:modelCNNFit},firstnumber=20] +from hdf5 import hdf5Generator +num_rows = 4.8E6 +batchSize = 256 +steps = num_rows/batchSize +#early stop +earlystop = keras.callbacks.EarlyStopping(monitor='val_sparse_categorical_accuracy',patience=8,verbose=False,restore_best_weights=True) +cbList = [earlystop] +trainData = hdf5Generator("w2vCNN.hdf5", batchSize, "Train") +valData = hdf5Generator("w2vCNN.hdf5", batchSize, "Val") + +#%%fit +cW = {0:4.18,1:9.51,2:1.52} +hist = modelNN.fit(trainData, validation_data=valData, epochs=50,class_weight=cW, steps_per_epoch=steps, validation_steps=int(steps/3),callbacks=cbList + +testData = hdf5Generator(path + "w2vCNN.hdf5", batchSize, "Test",loop=False) +modelNN.evaluate(testData) +\end{lstlisting} + +Hierfür wird die Größe der Trainingsmenge durch die BatchSize geteilt (Zeile 24). +Um ein Overfitting zu verhindern, wird, wie beim Mean-Modell auch schon in +Zeile 25 ein Earlystop definiert. +Dieser unterbricht das Training vorzeitig, falls acht Epochen lang keine +Verbesserung auf der Validierungsmenge erzielt wird. + +Die Generatoren für die Trainings- und Testmenge werden in +Zeile 27 und 28 variablen gespeichert. + +Die Klassengewichte in Zeile 31 wurden im Listing \ref{list:modelCNNWeights} berechnet. +Mit allen Anforderungen erfüllt kann jetzt in Zeile 32 das Modell trainiert werden. +\wip{schwer zu versteheneder Satz}Dabei ist zu beachten, dass die Anzahl der Schritte pro +Epoche für die Validierungsmenge gedrittelt wird, da +die Trainingsmenge $\frac{3}{5}$ des Datensatzes ausmacht die Validierungsmenge +hingegen nur $\frac{1}{5}$ wird +trotzdem nach jeder Epoche die gesamte Validierungsmenge evaluiert. + +Nach dem Training des Netzes ist es an der Zeit das Modell zu evaluieren. +Dafür wird in Zeile 34 zunächst wieder ein Generator angelegt. Mit dem +Parameter \lstinline{loop=False} wird gewährleistet, dass der Generator den Datensatz nur +einmal durchläuft. Dieser Generator kann nun in +Zeile 35 an die Methode \lstinline{evaluate} übergeben werden. +Das Evaluieren zeigt, dass dieses Modell zu $81.44\%$ die Klassen der Testmenge richtig klassifiziert. + +\begin{lstlisting}[caption={CNN - Klassengewichte},label={list:modelCNNWeights},numbers=none] +Y_train=[] +gen = hdf5Generator(path + "w2vCNN.hdf5", batchSize, "Test",loop=False) +for (x,y) in gen: + Y_train.append(y) +Y_train = np.array(Y_train).flatten() +count = np.unique(Y_train,return_counts=True)[1] +cWeight = 1/(count/Y_train.size) +\end{lstlisting} +\subsubsection{Konfusionsmatrix} +Um einen besseren Eindruck über die Qualität des +Netzes zu erhalten, wird auch bei diesem +Modell wieder eine Konfusionsmatrix erstellt. +\begin{lstlisting}[caption={CNN - Konfusionsmatrix},label={list:modelCNNKonf},firstnumber=36] +from sklearn.metrics import confusion_matrix +tD = hdf5Generator(path + "w2vCNN.hdf5", batchSize, "Test",loop=False) +y_pred = np.argmax(modelNN.predict(tD),axis=-1) +y_test=[] +for (x,y) in hdf5Generator(path + "w2vCNN.hdf5", batchSize, "Test",loop=False): + y_test.append(y) +y_test = np.array(y_test).flatten() + +confusion_matrix(y_test,y_pred,normalize='true') +\end{lstlisting} +Dafür werden die vom Netz vorhergesagten Klassen der Testmenge in dem +Vektor \lstinline{y_pred} gespeichert und die tatsächlichen Klassen der Testmenge in dem +Vektor \lstinline{y_test}. Beide Vektoren werden der Funktion +\lstinline{confusion_matrix} übergeben, +welche die in der Tabelle \ref{tab:conf_w_cnn} dargestellte Konfusionsmatrix +zurückliefert. + +\begin{table}[ht] + \def\arraystretch{1.3} + \begin{center} + \begin{tabular}{*{4}{R}} + - &Negativ & Neutral & Positiv\\ + Negativ &0.8314& 0.1377& 0.0308\\ + Neutral &0.1970& 0.6354& 0.1676\\ + Positiv &0.0316& 0.1316& 0.8367\\ + \end{tabular} + \end{center} + \label{tab:conf_w_cnn} + \caption{Konfusionsmatrix mit Klassengewichtung} +\end{table} +\wip{Tabelle auswerten!}