diff --git a/Dokumentation/3_2_Datenauslagerung_Generator.tex b/Dokumentation/3_2_Datenauslagerung_Generator.tex index 1ae69d0bba5c3fa482204eb949f01ae1d13f86bf..c3c7285fa0f57022e6dfab8d9934ee6a95c487a3 100644 --- a/Dokumentation/3_2_Datenauslagerung_Generator.tex +++ b/Dokumentation/3_2_Datenauslagerung_Generator.tex @@ -19,7 +19,7 @@ Dies könnte mit 8GB RAM etwas knapp werden, jedoch stellt es bei 16GB gar kein Das Problem ist recht deutlich. Viele Personen hätten auf ihrem privaten PC vermutlich nicht einmal genug Festplattenspeicher für diese Datenmenge, geschweige denn Arbeitsspeicher. \\ Die Daten müssen bei der CNN-Methode also nur stückweise in den Arbeitsspeicher geladen werden, dann wird mit diesen trainiert und anschließend werden sie wieder aus dem Arbeitsspeicher gelöscht um Platz für die nächsten Daten zu machen. Dabei gibt es wieder verschiedene Ansätze. -\subsubsection{Erster Generator} +\subsubsection{Erster Generator}\label{subsubsec:simpleGen} %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 model geladen werden. \begin{lstlisting} diff --git a/Dokumentation/3_umsetzung.tex b/Dokumentation/3_umsetzung.tex index 59924074c7295d847553c214d31e42ceb5279a95..a4cc99053023d89b62d83509013bbf3224c7127d 100644 --- a/Dokumentation/3_umsetzung.tex +++ b/Dokumentation/3_umsetzung.tex @@ -9,3 +9,4 @@ \input{silas/gensim.tex} \input{silas/w2vMean.tex} +\input{silas/w2vCNN.tex} diff --git a/Dokumentation/silas/daten_analyse.tex b/Dokumentation/silas/daten_analyse.tex index 95793a6e98aa4f291227618e4f02f95b2b5f4404..58a68c911b91bc5a70f414429edfb627cd83061b 100644 --- a/Dokumentation/silas/daten_analyse.tex +++ b/Dokumentation/silas/daten_analyse.tex @@ -72,7 +72,7 @@ print (nr_of_reviews)) Das Programm liefert \lstinline{8021122} zurück. -\subsubsection{Reviewlänge} +\subsubsection{Reviewlänge}\label{subsubsec:reviewlength} \begin{lstlisting}[inputencoding=latin1,caption={Programm zur ermittlung der Reviewlänge},label={list:rev_len}] import json import numpy as np diff --git a/Dokumentation/silas/gensim.tex b/Dokumentation/silas/gensim.tex index 176b7cf4475c785e4c50b5fd1b1483bea0a30aa0..d29b753f77b6402c7c6fdbaa353af03ce182268c 100644 --- a/Dokumentation/silas/gensim.tex +++ b/Dokumentation/silas/gensim.tex @@ -1,4 +1,4 @@ -\subsection{Word2Vec mit Gensim} +\subsection{Word2Vec mit Gensim}\label{subsection:gensim} Für das Trainieren des Word2Vec-Modells wird in dieser Arbeit die Open-Source-Library Gensim verwendet. diff --git a/Dokumentation/silas/w2vCNN.tex b/Dokumentation/silas/w2vCNN.tex new file mode 100644 index 0000000000000000000000000000000000000000..7f5e12af67731bf458f14ea0e7461c6999dee864 --- /dev/null +++ b/Dokumentation/silas/w2vCNN.tex @@ -0,0 +1,67 @@ +\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 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?} + +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. \ No newline at end of file diff --git a/Dokumentation/silas/w2vMean.tex b/Dokumentation/silas/w2vMean.tex index 3901e51419fc61a0881686b36c556ebca15f61ff..0e5fd3affebec1052f6648006f172f1e8f37f26e 100644 --- a/Dokumentation/silas/w2vMean.tex +++ b/Dokumentation/silas/w2vMean.tex @@ -175,6 +175,7 @@ confusion_matrix(Y_test,y_pred,normalize='true') \end{lstlisting} \wip{Tabelle auswerten} \begin{table}[ht] + \def\arraystretch{1.3} \begin{center} \begin{tabular}{*{4}{R}} - &Negativ & Neutral & Positiv\\ @@ -190,6 +191,7 @@ von $85.7\%$, betrachtet man jedoch die Konfusionsmatrix in Tabelle \ref{tab:con sieht man das dort bloß $27\%$ der neutralen Rezensionen richtig klassifiziert wurden. \wip{Tabellenreferenz ???} \begin{table}[ht] + \def\arraystretch{1.3} \begin{center} \begin{tabular}{*{4}{R}} - &Negativ & Neutral & Positiv\\ diff --git a/Dokumentation/w2v.pdf b/Dokumentation/w2v.pdf index e5ed1a377c5d83ecf0d36afaead7205ef4e69ab4..bd0fc144e6635514c2bbff06aec511fc52aa963e 100644 Binary files a/Dokumentation/w2v.pdf and b/Dokumentation/w2v.pdf differ diff --git a/Dokumentation/w2v.tex b/Dokumentation/w2v.tex index a6bc56043389071d0a5a4981669d3cbe0385b260..5effcf68d318297652f03e5b4e1b6f05abdf4f47 100644 --- a/Dokumentation/w2v.tex +++ b/Dokumentation/w2v.tex @@ -74,7 +74,7 @@ } \newcolumntype{R}{>{\collectcell\ApplyGradient}c<{\endcollectcell}} -\renewcommand{\arraystretch}{1.5} +%\renewcommand{\arraystretch}{1.5} \setlength{\fboxsep}{3mm} % box size \setlength{\tabcolsep}{5pt} %----------------------- diff --git a/python/hdf5.py b/python/hdf5.py index 4acbccfb3acca59fbff54e052f831442218d156b..89c4081c63a1c8d252fc00ed46830d6c888129b4 100644 --- a/python/hdf5.py +++ b/python/hdf5.py @@ -13,6 +13,7 @@ if __name__ == '__main__': pathSilas = "G:\\ml\\" path = pathSilas data_path ="E:\\downloads\\yelp_dataset\\yelp_dataset\\yelp_academic_dataset_review.json" + #data_path = "D:\\ml\\data\\sample1.json" def getSentenceVectorCNN(sentence): split = utils.simple_preprocess(sentence) @@ -34,7 +35,7 @@ if __name__ == '__main__': import json i = 0 with h5py.File(path + "w2vCNN.hdf5", "w") as hf: - chunkSize = 10**4 + chunkSize = 10E3 trainChunk = int(chunkSize * 0.6) valTestChunk = int(chunkSize * 0.2) xTrain = [] @@ -122,40 +123,39 @@ if __name__ == '__main__': i += 1 index +=1 - XTrain.resize(XTrain.shape[0]+trainChunk, axis=0) - XTrain[-trainChunk:] = xTrain + #XTrain.resize(XTrain.shape[0]+trainChunk, axis=0) + #XTrain[-trainChunk:] = xTrain - YTrain.resize(YTrain.shape[0]+trainChunk, axis=0) - YTrain[-trainChunk:] = yTrain + #YTrain.resize(YTrain.shape[0]+trainChunk, axis=0) + #YTrain[-trainChunk:] = yTrain - XVal.resize(XVal.shape[0]+valTestChunk, axis=0) - XVal[-valTestChunk:] = xVal + #XVal.resize(XVal.shape[0]+valTestChunk, axis=0) + #XVal[-valTestChunk:] = xVal - YVal.resize(YVal.shape[0]+valTestChunk, axis=0) - YVal[-valTestChunk:] = yVal + #YVal.resize(YVal.shape[0]+valTestChunk, axis=0) + #YVal[-valTestChunk:] = yVal - XTest.resize(XTest.shape[0]+valTestChunk, axis=0) - XTest[-valTestChunk:] = xTest + #XTest.resize(XTest.shape[0]+valTestChunk, axis=0) + #XTest[-valTestChunk:] = xTest - YTest.resize(YTest.shape[0]+valTestChunk, axis=0) - YTest[-valTestChunk:] = yTest + #YTest.resize(YTest.shape[0]+valTestChunk, axis=0) + #YTest[-valTestChunk:] = yTest #%% import h5py -def hdf5Generator(filePath, batch_size, dataSet): +def hdf5Generator(filePath, batch_size, dataSet,loop=True): with h5py.File(filePath, 'r') as hf: L = len(hf["X" + dataSet]) while True: batch_start = 0 batch_end = batch_size - while batch_start < L: - limit = min(batch_end, L) - X = hf["X" + dataSet][batch_start:limit] - Y = hf["Y" + dataSet][batch_start:limit] - + while batch_end < L: + X = hf["X" + dataSet][batch_start:batch_end] + Y = hf["Y" + dataSet][batch_start:batch_end] yield (X,Y) #a tuple with two numpy arrays with batch_size samples batch_start += batch_size batch_end += batch_size + if not loop: break \ No newline at end of file diff --git a/python/w2v_cnn_gen_hdf5.py b/python/w2v_cnn_gen_hdf5.py index 4ed8165769320f1f5f946e45f55e11087fbe71ad..78ee36eadce83735e26d88866bf8d580dc1ff0a9 100644 --- a/python/w2v_cnn_gen_hdf5.py +++ b/python/w2v_cnn_gen_hdf5.py @@ -1,5 +1,7 @@ #%% CNN +import os +os.environ['TF_FORCE_GPU_ALLOW_GROWTH'] = 'true' import tensorflow as tf import numpy as np from tensorflow.keras.models import Sequential @@ -11,11 +13,10 @@ modelNN = Sequential() modelNN.add(Conv1D(32, 7, activation='relu',input_shape=((72, 100)))) modelNN.add(Conv1D(32, 7, activation='relu')) -#modelNN.add(GlobalMaxPooling1D()) +modelNN.add(GlobalMaxPooling1D()) modelNN.add(Flatten()) modelNN.add(Dense(512,activation='relu')) modelNN.add(Dense(128,activation='relu')) -#modelNN.add(Dense(50,activation='relu',input_dim=X[0].size)) modelNN.add(Dense(10,activation='relu')) modelNN.add(Dense(3,activation='softmax')) modelNN.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=["sparse_categorical_accuracy"]) @@ -23,19 +24,33 @@ modelNN.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics= #%% from hdf5 import hdf5Generator path = "G:\\ml\\" -num_rows = 340000 -batchSize = 512 +num_rows = 4.8E6 +batchSize = 2048 steps = num_rows/batchSize #early stop -earlystop = keras.callbacks.EarlyStopping(monitor='accuracy',patience=10,verbose=False,restore_best_weights=True) +earlystop = keras.callbacks.EarlyStopping(monitor='sparse_categorical_accuracy',patience=10,verbose=False,restore_best_weights=True) cbList = [earlystop] trainData = hdf5Generator(path + "w2vCNN.hdf5", batchSize, "Train") valData = hdf5Generator(path + "w2vCNN.hdf5", batchSize, "Val") -hist = modelNN.fit(trainData, validation_data=valData, epochs=12, steps_per_epoch=steps, validation_steps=steps) - -#hist = modelNN.fit(hdf5Generator("vectors.hdf5", batchSize),epochs=15, steps_per_epoch=steps) -#hist = modelNN.fit(hdf5Generator("vectors.hdf5", batchSize),epochs=15,batch_size=batchSize,callbacks=cbList) +#%% +cW = {0:4.18,1:9.53,2:1.52} +hist = modelNN.fit(trainData, validation_data=valData, epochs=100,class_weight=cW, steps_per_epoch=steps, validation_steps=steps,callbacks=cbList) +modelNN.save("D:\\ml\\CNN-Classfication") #modelNN.fit(train,epochs=12,validation_data=val,batch_size=batchSize,steps_per_epoch= num_rows/batchSize,callbacks=cbList,validation_steps=num_rows/batchSize) +# %%eval +testData = hdf5Generator(path + "w2vCNN.hdf5", batchSize, "Test",loop=False) +modelNN.evaluate(testData) +#%% +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 +from sklearn.metrics import confusion_matrix +confusion_matrix(y_test,y_pred,normalize='true') # %% diff --git a/python/w2v_sentence_cnn.py b/python/w2v_sentence_cnn.py index 67a763e0b6f45459940e7b621b3e0b52cc5676f9..ea82ec3e7290bfef55e1f8b46c38aa558375ce36 100644 --- a/python/w2v_sentence_cnn.py +++ b/python/w2v_sentence_cnn.py @@ -8,6 +8,7 @@ import pandas as pd import json from tensorflow.python.keras.constraints import MaxNorm +from tensorflow.python.keras.layers.core import Dropout model_path = "D:\\ml\\full_yelp_w2v_model" #data_path ="C:\\Users\\sls21\\Documents\\Uni\\word2vec\\sample.json" @@ -28,15 +29,15 @@ def getSentenceVectorCNN(sentence): i += 1 except: pass - #if wordVecs == np.zeros((72,100)): #maybe dont alllow sentences with less than n wordvecotrs - # raise Exception('words not found in w2v model') + if np.all(wordVecs[5:]==0): + raise Exception('not enough words found in w2v model') return wordVecs # %% data import numpy as np try: - X = np.load("./Data/X_cnn.npy") - Y = np.load("./Data/Y_cnn.npy") + X = np.load("D:/ml/data/X_cnn.npy") + Y = np.load("D:/ml/data/Y_cnn.npy") except: X = [] Y = [] @@ -61,8 +62,8 @@ except: #Y.append(1 if y>2 else 0) X = np.array(X) Y = np.array(Y) - #np.save("./Data/X_cnn",X) - #np.save("./Data/Y_cnn",Y) + np.save("D:/ml/data/X_cnn.npy",X) + np.save("D:/ml/data/Y_cnn.npy",Y) # %% CNN import tensorflow as tf import numpy as np @@ -87,35 +88,29 @@ import tensorflow as tf import numpy as np from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Flatten -from tensorflow.keras.layers import Conv1D,MaxPooling1D,GlobalMaxPooling1D +from tensorflow.keras.layers import Conv1D,MaxPooling1D,GlobalMaxPooling1D,Dropout from tensorflow import keras modelNN = Sequential() modelNN.add(Conv1D(32, 7, activation='relu',input_shape=(X[0].shape))) modelNN.add(Conv1D(32, 7, activation='relu')) -#modelNN.add(GlobalMaxPooling1D()) +modelNN.add(GlobalMaxPooling1D()) modelNN.add(Flatten()) modelNN.add(Dense(512,activation='relu')) +modelNN.add(Dropout(0.5)) modelNN.add(Dense(128,activation='relu')) -#modelNN.add(Dense(50,activation='relu',input_dim=X[0].size)) +modelNN.add(Dropout(0.25)) modelNN.add(Dense(10,activation='relu')) +modelNN.add(Dropout(0.1)) modelNN.add(Dense(3,activation='softmax')) modelNN.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=["accuracy"]) # %% fit #early stop -earlystop = keras.callbacks.EarlyStopping(monitor='val_accuracy',patience=10,verbose=False,restore_best_weights=True) +earlystop = keras.callbacks.EarlyStopping(monitor='val_accuracy',patience=5,verbose=False,restore_best_weights=True) cbList = [earlystop] -hist = modelNN.fit(X,Y,epochs=1000,validation_split=0.2,batch_size=128,callbacks=cbList) - -#%% -## Notes -# np.unique(Y,return_counts=True) -# (array([0, 1]), array([ 77051, 272891], dtype=int64)) -# /Y.size -# -> array([0.2201822, 0.7798178]) -# -> Unbalanced dataset +hist = modelNN.fit(X,Y,epochs=50,validation_split=0.2,batch_size=128,callbacks=cbList) -#current val_acc ~ 91.5 % +#%% \ No newline at end of file diff --git a/python/w2v_sentence_cnn_gen.py b/python/w2v_sentence_cnn_gen.py index 14386f319389c9b0df5490328ce629cd3263b5a7..d7ca4c915902963b599e4bbb4181910a85aaa790 100644 --- a/python/w2v_sentence_cnn_gen.py +++ b/python/w2v_sentence_cnn_gen.py @@ -1,11 +1,8 @@ #%% import os -#os.environ["CUDA_VISIBLE_DEVICES"] = "-1" -from math import nan -from gensim.test.utils import datapath +os.environ['TF_FORCE_GPU_ALLOW_GROWTH'] = 'true' from gensim import utils from w2v_yelp_model import getWordVecModel -import pandas as pd import json model_path = "D:\\ml\\full_yelp_w2v_model" @@ -24,14 +21,14 @@ def getSentenceVectorCNN(sentence): i += 1 except: pass - #if wordVecs == np.zeros((72,100)): #maybe dont alllow sentences with less than n wordvecotrs - # raise Exception('words not found in w2v model') + if np.all(wordVecs[5:]==0): + raise Exception('not enough words found in w2v model') return wordVecs #%% Data Generator import numpy as np import json -def generate_arrays_from_file(path, batchsize): +def generate_arrays_from_file(path, batchsize,loop=True): inputs = [] targets = [] batchcount = 0 @@ -51,13 +48,14 @@ def generate_arrays_from_file(path, batchsize): batchcount += 1 except: continue - if batchcount > batchsize: + if batchcount >= batchsize: X = np.array(inputs) y = np.array(targets) yield (X, y) inputs = [] targets = [] batchcount = 0 + if not loop: break #%% CNN import tensorflow as tf @@ -84,12 +82,25 @@ modelNN.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics= #early stop earlystop = keras.callbacks.EarlyStopping(monitor='val_accuracy',patience=25,verbose=False,restore_best_weights=True) cbList = [earlystop] -num_rows = 350000 -batchSize = 512 +#num_rows = 4.8E6 +num_rows = 410000 +batchSize = 2048 #hist = modelNN.fit(generate_arrays_from_file('./sample.json',128),epochs=1000,validation_split=0.2,batch_size=128,callbacks=cbList) train = generate_arrays_from_file('D:\\ml\\data\\train.json',batchSize) val = generate_arrays_from_file('D:\\ml\\data\\val.json',batchSize) -modelNN.fit(train,epochs=12,validation_data=val,batch_size=batchSize,steps_per_epoch= num_rows/batchSize,callbacks=cbList,validation_steps=num_rows/batchSize) +#%% +modelNN.fit(train,epochs=1,validation_data=val,steps_per_epoch= num_rows/batchSize,callbacks=cbList,validation_steps=num_rows/batchSize) + +# %% +modelNN.evaluate(generate_arrays_from_file('D:\\ml\\data\\val.json',16000,False)) +# %% +y_pred = np.argmax(modelNN.predict(generate_arrays_from_file('D:\\ml\\data\\val.json',16000,False)),axis=-1) +# %% +y_t = [] +for a in generate_arrays_from_file('D:\\ml\\data\\val.json',batchSize,False): + y_t.append(a[1]) + + # %%