1
Fork 0
mirror of https://github.com/Steffo99/unimore-bda-6.git synced 2024-11-25 17:24:20 +00:00

more things

This commit is contained in:
Steffo 2023-02-11 05:57:14 +01:00
parent 7778c648c1
commit ae2cf563e6
Signed by: steffo
GPG key ID: 2A24051445686895
7 changed files with 91 additions and 38 deletions

View file

@ -5,14 +5,16 @@
<option name="PARENT_ENVS" value="true" /> <option name="PARENT_ENVS" value="true" />
<envs> <envs>
<env name="CONFIRM_OVERWRITE" value="False" /> <env name="CONFIRM_OVERWRITE" value="False" />
<env name="EVALUATION_SET_SIZE" value="100" />
<env name="NLTK_DATA" value="./data/nltk" /> <env name="NLTK_DATA" value="./data/nltk" />
<env name="PYTHONUNBUFFERED" value="1" /> <env name="PYTHONUNBUFFERED" value="1" />
<env name="TENSORFLOW_EMBEDDING_SIZE" value="64" />
<env name="TENSORFLOW_MAX_FEATURES" value="1000000" />
<env name="TF_CPP_MIN_LOG_LEVEL" value="2" /> <env name="TF_CPP_MIN_LOG_LEVEL" value="2" />
<env name="TRAINING_SET_SIZE" value="2000" />
<env name="VALIDATION_SET_SIZE" value="25" />
<env name="WORKING_SET_SIZE" value="100000" /> <env name="WORKING_SET_SIZE" value="100000" />
<env name="XLA_FLAGS" value="--xla_gpu_cuda_data_dir=/opt/cuda" /> <env name="XLA_FLAGS" value="--xla_gpu_cuda_data_dir=/opt/cuda" />
<env name="TRAINING_SET_SIZE" value="100" />
<env name="VALIDATION_SET_SIZE" value="25" />
<env name="EVALUATION_SET_SIZE" value="100" />
</envs> </envs>
<option name="SDK_HOME" value="$PROJECT_DIR$/.venv/bin/python" /> <option name="SDK_HOME" value="$PROJECT_DIR$/.venv/bin/python" />
<option name="SDK_NAME" value="Poetry (unimore-bda-6)" /> <option name="SDK_NAME" value="Poetry (unimore-bda-6)" />

View file

@ -26,13 +26,23 @@
> (https://jupyter.org/) invece di codice .py e relativi commenti separati su PDF (per comodità di consultazione, > (https://jupyter.org/) invece di codice .py e relativi commenti separati su PDF (per comodità di consultazione,
> consegnare comunque anche una stampa PDF del notebook oltre al notebook stesso). > consegnare comunque anche una stampa PDF del notebook oltre al notebook stesso).
## Sinossi
In questo progetto si è realizzato una struttura che permettesse di mettere a confronto diversi modi per effettuare sentiment analysis, e poi si sono realizzati su di essa alcuni modelli di sentiment analysis con caratteristiche diverse per confrontarli.
## Premessa ## Premessa
### Codice ### Packaging
Il codice dell'attività è incluso come package Python 3.10 compatibile con PEP518. Il codice dell'attività è incluso come package Python 3.10 compatibile con PEP518.
Per installare il package, è sufficiente eseguire i seguenti comandi dall'interno della directory del progetto: > **Warning:**
>
> Il progetto non supporta Python 3.11 per via del mancato supporto di Tensorflow a quest'ultimo.
#### Installazione del package
Per installare il package, è necessario eseguire i seguenti comandi dall'interno della directory del progetto:
```console ```console
$ python3.10 -m venv .venv $ python3.10 -m venv .venv
@ -40,11 +50,7 @@ $ source venv/bin/activate
$ pip install . $ pip install .
``` ```
> **Note:** ##### NLTK
>
> Per via di requisiti particolari di Tensorflow, Python 3.11 non è supportato.
#### NLTK
NLTK richiede dipendenze aggiuntive per funzionare, che possono essere scaricate eseguendo il seguente comando su console: NLTK richiede dipendenze aggiuntive per funzionare, che possono essere scaricate eseguendo il seguente comando su console:
@ -52,11 +58,38 @@ NLTK richiede dipendenze aggiuntive per funzionare, che possono essere scaricate
$ ./scripts/download-nltk.sh $ ./scripts/download-nltk.sh
``` ```
### Dataset ##### Tensorflow
Il codice dell'attività richiede la connessione a un server MongoDB 6 contenente il dataset di recensioni Amazon fornito a lezione. L'accelerazione hardware di Tensorflow richiede che una scheda grafica NVIDIA con supporto a CUDA sia disponibile sul dispositivo, e che gli strumenti di sviluppo di CUDA siano installati sul sistema operativo.
Si forniscono alcuni script nella cartella `./data/scripts` per facilitare la configurazione e l'esecuzione di quest'ultimo. Per indicare a Tensorflow il percorso degli strumenti di sviluppo di CUDA, è necessario impostare la seguente variabile d'ambiente, sostituendo a `/opt/cuda` il percorso in cui gli strumenti sono installati sul dispositivo:
```console
$ export XLA_FLAGS=--xla_gpu_cuda_data_dir\=/opt/cuda
```
Per più informazioni, si suggerisce di consultare la pagina [Install Tensorflow 2](https://www.tensorflow.org/install) della documentazione di Tensorflow.
#### Esecuzione del programma
Per eseguire il programma principale, è possibile eseguire i seguenti comandi dall'interno della directory del progetto:
```console
$ source venv/bin/activate
$ python3.10 -m unimore_bda_6
```
### Dati
Il codice dell'attività richiede la connessione a un server MongoDB 6 contenente la collezione di recensioni Amazon fornita a lezione.
> **Warning:**
>
> La collezione non è inclusa con il repository, in quanto occupa 21 GB!
Si forniscono alcuni script nella cartella `./data/scripts` per facilitare la configurazione e l'esecuzione di quest'ultima.
#### Esecuzione del database
Per eseguire il database MongoDB come processo utente, salvando i dati nella cartella `./data/db`: Per eseguire il database MongoDB come processo utente, salvando i dati nella cartella `./data/db`:
@ -64,30 +97,46 @@ Per eseguire il database MongoDB come processo utente, salvando i dati nella car
$ ./data/scripts/run-db.sh $ ./data/scripts/run-db.sh
``` ```
#### Importazione dei dati da JSON
Per importare il dataset `./data/raw/reviewsexport.json` fornito a lezione nel database MongoDB: Per importare il dataset `./data/raw/reviewsexport.json` fornito a lezione nel database MongoDB:
```console ```console
$ ./data/scripts/import-db.sh $ ./data/scripts/import-db.sh
``` ```
Per creare indici MongoDB utili al funzionamento efficiente del codice: #### Creazione indici
Per creare indici MongoDB potenzialmente utili al funzionamento efficiente del codice:
```console ```console
$ mongosh < ./data/scripts/index-db.js $ mongosh < ./data/scripts/index-db.js
``` ```
## Introduzione ## Struttura per il confronto
### Configurazione ambiente e iperparametri - `.config`
### Recupero dati dal database - `.database`
### Tokenizzatore astratto - `.tokenizer.base`
### Analizzatore astratto - `.analysis.base`
### Logging - `.log`
### Tester - `.__main__`
<!-- TODO --> ## Ri-implementazione dell'esercizio con NLTK - `.analysis.nltk_sentiment`
### Wrapping del tokenizzatore di NLTK - `.tokenizer.nltk_word_tokenize`
### Ri-creazione del tokenizer di Christopher Potts - `.tokenizer.potts`
### Problemi di memoria
## `.analysis.base`: Costruzione dell'impalcatura necessaria al confronto ## Ottimizzazione di memoria
### Caching - `.database.cache` e `.gathering`
<!-- TODO --> ## Implementazione di modelli con Tensorflow - `.analysis.tf_text`
### Creazione di tokenizzatori compatibili con Tensorflow - `.tokenizer.plain` e `.tokenizer.lower`
### Creazione di un modello di regressione - `.analysis.tf_text.TensorflowPolarSentimentAnalyzer`
### Creazione di un modello di categorizzazione - `.analysis.tf_text.TensorflowCategorySentimentAnalyzer`
#### Esplosione del gradiente
## `.analysis.nltk_sentiment`: Ricostruzione e ottimizzazione del modello basato su `nltk.sentiment` realizzato a lezione ## Implementazione di tokenizzatori di HuggingFace - `.tokenizer.hugging`
Per avere un modello baseline con cui effettuare un confronto, si è ricostruito un modello basato su `nltk.sentiment` ispirato a quello realizzato a lezione. ## Confronto dei modelli
<!-- TODO --> ## Conclusione
## TODO

View file

@ -38,9 +38,9 @@ def main():
slog.debug("Selected sample_func: %s", sample_func.__name__) slog.debug("Selected sample_func: %s", sample_func.__name__)
for SentimentAnalyzer in [ for SentimentAnalyzer in [
NLTKSentimentAnalyzer,
TensorflowPolarSentimentAnalyzer, TensorflowPolarSentimentAnalyzer,
TensorflowCategorySentimentAnalyzer, TensorflowCategorySentimentAnalyzer,
# NLTKSentimentAnalyzer,
]: ]:
slog = logging.getLogger(f"{__name__}.{sample_func.__name__}.{SentimentAnalyzer.__name__}") slog = logging.getLogger(f"{__name__}.{sample_func.__name__}.{SentimentAnalyzer.__name__}")

View file

@ -49,9 +49,10 @@ class BaseSentimentAnalyzer(metaclass=abc.ABCMeta):
for review in evaluation_dataset_func(): for review in evaluation_dataset_func():
resulting_category = self.use(review.text) resulting_category = self.use(review.text)
log.debug("Evaluation step: expected %d, received %d, review was %s", review.category, resulting_category, review.text[:80])
evaluated += 1 evaluated += 1
try: try:
correct += 1 if round(resulting_category) == round(review.category) else 0 correct += 1 if resulting_category == review.category else 0
score += 1 - (abs(resulting_category - review.category) / 4) score += 1 - (abs(resulting_category - review.category) / 4)
except ValueError: except ValueError:
log.warning("Model execution on %s resulted in a NaN value: %s", review, resulting_category) log.warning("Model execution on %s resulted in a NaN value: %s", review, resulting_category)

View file

@ -199,7 +199,7 @@ class TensorflowCategorySentimentAnalyzer(TensorflowSentimentAnalyzer):
log.debug("Compiling model: %s", model) log.debug("Compiling model: %s", model)
model.compile( model.compile(
optimizer=tensorflow.keras.optimizers.Adam(global_clipnorm=1.0), optimizer=tensorflow.keras.optimizers.Adam(clipnorm=1.0),
loss=tensorflow.keras.losses.CategoricalCrossentropy(), loss=tensorflow.keras.losses.CategoricalCrossentropy(),
metrics=[ metrics=[
tensorflow.keras.metrics.CategoricalAccuracy(), tensorflow.keras.metrics.CategoricalAccuracy(),
@ -217,7 +217,7 @@ class TensorflowCategorySentimentAnalyzer(TensorflowSentimentAnalyzer):
max_i = i max_i = i
max_p = p max_p = p
result = float(max_i) + 1.0 result = float(max_i) + 1.0
return result return float(round(result))
class TensorflowPolarSentimentAnalyzer(TensorflowSentimentAnalyzer): class TensorflowPolarSentimentAnalyzer(TensorflowSentimentAnalyzer):
@ -245,25 +245,23 @@ class TensorflowPolarSentimentAnalyzer(TensorflowSentimentAnalyzer):
tensorflow.keras.layers.Dropout(0.25), tensorflow.keras.layers.Dropout(0.25),
tensorflow.keras.layers.GlobalAveragePooling1D(), tensorflow.keras.layers.GlobalAveragePooling1D(),
tensorflow.keras.layers.Dropout(0.25), tensorflow.keras.layers.Dropout(0.25),
tensorflow.keras.layers.Dense(8), tensorflow.keras.layers.Dense(1, activation="sigmoid"),
tensorflow.keras.layers.Dropout(0.25),
tensorflow.keras.layers.Dense(1, activation="relu"),
]) ])
log.debug("Compiling model: %s", model) log.debug("Compiling model: %s", model)
model.compile( model.compile(
optimizer=tensorflow.keras.optimizers.Adam(clipnorm=2.0), optimizer=tensorflow.keras.optimizers.Adam(clipnorm=1.0),
loss=tensorflow.keras.losses.MeanAbsoluteError(), loss=tensorflow.keras.losses.MeanAbsoluteError(),
metrics=[
# tensorflow.keras.metrics.MeanAbsoluteError(),
]
) )
log.debug("Compiled model: %s", model) log.debug("Compiled model: %s", model)
return model return model
def _translate_prediction(self, a: numpy.array) -> Category: def _translate_prediction(self, a: numpy.array) -> Category:
return 1 + (a[0, 0] + 0.5) * 4 a: float = a[0, 0]
a = a * 2 + 1
a = float(round(a))
return a
__all__ = ( __all__ = (

View file

@ -124,10 +124,10 @@ def TENSORFLOW_EPOCHS(val: str | None) -> int:
""" """
The number of epochs to train Tensorflow models for. The number of epochs to train Tensorflow models for.
Defaults to `5`. Defaults to `3`.
""" """
if val is None: if val is None:
return 5 return 3
try: try:
return int(val) return int(val)
except ValueError: except ValueError:

View file

@ -37,11 +37,14 @@ class Review:
else: else:
raise KeyError(item) raise KeyError(item)
def normvalue(self) -> float:
return (self.category - 1) / 2
def to_tensor_text(self) -> tensorflow.Tensor: def to_tensor_text(self) -> tensorflow.Tensor:
return tensorflow.convert_to_tensor(self.text, dtype=tensorflow.string) return tensorflow.convert_to_tensor(self.text, dtype=tensorflow.string)
def to_tensor_normvalue(self) -> tensorflow.Tensor: def to_tensor_normvalue(self) -> tensorflow.Tensor:
return tensorflow.convert_to_tensor([(self.category - 1) / 4 - 0.5], dtype=tensorflow.float32) return tensorflow.convert_to_tensor([self.normvalue()], dtype=tensorflow.float32)
def to_tensor_tuple_normvalue(self) -> tuple[tensorflow.Tensor, tensorflow.Tensor]: def to_tensor_tuple_normvalue(self) -> tuple[tensorflow.Tensor, tensorflow.Tensor]:
return ( return (