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:
parent
7778c648c1
commit
ae2cf563e6
7 changed files with 91 additions and 38 deletions
|
@ -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)" />
|
||||||
|
|
89
README.md
89
README.md
|
@ -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
|
|
||||||
|
|
|
@ -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__}")
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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__ = (
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
Loading…
Reference in a new issue