Ho addestrato un'Intelligenza Artificiale a fare trading di Bitcoin... sarò riuscito a prevedere le variazioni del prezzo? Scopriamolo insieme.
Premessa
Utilizzare un'IA preaddestrata, come ad esempio ChatGPT, è molto diverso dall'addestrare da zero una rete neurale. I parametri da tenere in considerazione sono davvero molti. Mi scuso fin d'ora se il mio linguaggio risulterà impreciso ai puristi, non sono un ricercatore ma un sistemista. Durante un week end piovoso, non potendo girare in pista in moto e non potendo uscire per mare a vela (due delle mie tante passioni) mi è venuta voglia di approfondire l'argomento AI Train.
I dataset finanziari sono molto ben strutturati e si prestano molto bene ad iniziare a muovere i primi passi in materia, quindi sono partito proprio da questi per non perdere troppo tempo nel processo di normalizzazione dei dati. Ovviamente, l'amico intelligente che ho citato all'inizio (ed il suo incredibile modello o-preview), mi ha dato una mano nel capire le logiche, nello scrivere parti di codice in python e nel dipanare molti dubbi.
Per iniziare ho stabilito un obiettivo binario: il prezzo dello slot temporale successivo aumenterà o diminuirà? Se il modello restituisce dati congrui e usabili, allora si passa allo step successivo: il prezzo di che percentuale aumenterà o diminuirà (mi accontento anche di "poco o molto")?
I dataset
Per iniziare ho creato uno script in grado di scaricare i dati di apertura, chiusura, volumi scambiati, prezzo massimo e minimo (le candele finanziarie) con intervallo temporale di 1 minuto dal 2018 al 2023 compresi per circa 15 delle principali e più stabili criptovalute. Per mia esperienza, le dinamiche spesso si assomigliano, quindi ho inserito tutti questi dati sempre come elaborazione base per il processo di apprendimento del modello di Intelligenza Artificiale. Per la parte di test pura, ho invece usato i valori del 2024 (da gennaio a settembre compresi). Nonostante i modelli abbiano una percentuale di Learn e Test, comunque ho voluto fare un "test indipendente" su valori che il modello non aveva mai visto per evitare il fenomeno di Overfitting (che vedremo più avanti).
unix,date,symbol,open,high,low,close,volume,base-volume,quote-volume,tradecount
1577836800,2020-01-01 00:00:00,BTCUSDT,7195.24,7196.25,7183.14000000,7186.68000000,51.64281200,19.59823000,140888.41428273,493
1577836860,2020-01-01 00:01:00,BTCUSDT,7187.67,7188.06,7182.20000000,7184.03000000,7.24814800,2.03177200,14599.21192429,135
La versione 1 - LSTM
Per iniziare sono partito da tensorflow, ma dopo innumerevoli tentativi in cui la GPU (4090 con 24gb di VRAM) non veniva vista, sono passato con successo a torch. Lavorare solo con la CPU (nonostante sia un i9 di ultima generazione) sarebbe stato impossibile sia come tempi sia come risultati.
Superati i problemi tecnici, ho iniziato ad utilizzare una rete neurale di tipo
LSTM ed ho iniziato a giocare con le epochs (i cicli di addestramento) implementando tecniche di resume dai checkpoint e uno script separato per fare tutta la parte di testing. Qui mi sono apparsi chiari i parametri di inferenza (ovvero quanto il modello è in grado di prevedere correttamente): l'accuracy ed il loss. Nel modello l'accuratezza era spesso molto alta (vicina all'1... ci prendeva sempre, quindi sarei diventato finalmente milionario) ma l'accuracy su dati nuovi mai visti dal modello lo era molto meno. Ho scoperto, quindi, il fenomeno dell'overfitting, ovvero all'aumentare dei cicli di apprendimento il modello rischia di imparare a memoria i dati. Questo ovviamente non va bene perchè comunque su dati mai visti prima il modello si comporterà in maniera deludente. Per evitare questo si gioca con diversi parametri tra cui il
patience, ovvero uno stop forzato dell'apprendimento nel momento in cui i valori danno segnali in tal senso o banalmente il modello non apprende più. Sfatiamo quindi subito un mito in cui credevo anche io: più cicli di apprendimento faccio e più imparo. NO, perchè rischio di imparare a memoria e quindi imparo uno schema inutilizzabile.

La versione 2 - Preprocessing
I dati non erano molto incoraggianti, sostanazialmente era come lanciare una monetina e scommettere sul risultato. Quindi ho aggiunto una fase di preprocessing così da dare all'IA anche dei parametri basati su indicatori finanziari normalmente utilizzati, ovvero
- MACD(Moving Average Convergence/Divergence),
- Incrocio delle Medie Mobili,
- OBV (On-Balance Volume),
- VPT (Volume–price trend),
- RSI (Relative Strength Index)
ognuno con diversi parametri. Ho preso i dati in mio possesso ed ho creato uno script per applicare tutti questi indicatori finanziari su molteplici finestre per capire quale, senza IA, comunque era in grado di aggiungere accuratezza di per se. Ho scritto quindi un ulteriore script per ordinare questi dati.
In fase di preprocessing ho quindi passato l'indicatore che mi dava il miglior risultato al modello da addestrare, vedendo finalmente dei risultati migliori della semplice casualità.
La versione 3 - CNN e Transformer
Nella versione 3 ho aggiunto al modello un
CNN (Rete Neurale Convoluzionale). Una CNN viene utilizzata nel contesto delle serie temporali per estrarre automaticamente delle feature rilevanti dai dati.I vantaggi sono sostanzialmente 3:
- Riduzione della dimensionalità: Filtrare i dati temporali attraverso una convoluzione per ridurre le informazioni ridondanti.
- Pattern locali: Identificare pattern locali nei dati, come trend di breve periodo o picchi nei dati temporali. Nel caso dei prezzi, potrebbe identificare movimenti locali di prezzo o anomalie.
- Pre-elaborazione per LSTM: La CNN riduce la complessità dei dati che vengono poi passati all'LSTM per elaborare le relazioni sequenziali a lungo termine.

Oltre al CNN ho aggiunto anche un processo di Transformer. I Transformer sono molto efficaci nel gestire sequenze lunghe e relazioni complesse tra le diverse parti di una sequenza grazie al meccanismo di attenzione, che permette al modello di concentrarsi sulle parti rilevanti della sequenza.
In questo script, il Transformer può:
- Identificare quali parti della sequenza temporale hanno una maggiore influenza su ogni predizione, permettendo al modello di "prestare attenzione" a movimenti di prezzo rilevanti, ignorando i movimenti meno significativi.
- Aiutare a catturare le relazioni temporali a lungo termine più efficacemente di un semplice LSTM, che tende a concentrarsi solo sulle relazioni a breve termine.
La versione 4 - Approfondimento sui parametri e seed
Ad un certo punto è risultato chiaro che alcuni parametri dovevano essere affinati. Mi sono concentrato su:
- Hidden Size: Banalizzando questi sono i neuroni dell'IA. Valori bassi rendono il modello troppo semplice e incapace di imparare pattern complessi, valori alti portano al rischio di Overfitting (il modello impara a memoria) oltre a rendere l'elaborazione molto più lenta
- Number of Layers: Indica il numero di livelli, come per gli Hidden Size troppo pochi o troppi sono controproducenti, tuttavia 1 Layer è sufficiente spesso per molti compiti, ma potrebbe non essere in grado di catturare relazioni complesse nei dati. Se Hidden Size è il numero dei neuroni X Y su una rappresentazione cartesiana, i Layer sono la profondità Z.
- Learning Percentage: Indica la percentuale di dati che viene dedicata all'addestramento e la percentuale che viene dedicata ai test. Percentuali alte dedicano più dati all'addestramento, ma lasciano meno dati per valutare le prestazioni. Percentuali basse dedicano più dati al test, ma riducono la quantità di dati usati per l'addestramento.
- Learning Rate: Si occupa di ponderare il tasso di apprendimento. Valori alti fanno apprendere rapidamente, ma superficialmente. Valori bassi solitamente sono più precisi, ma richiedono più tempo (oppure non fanno apprendere proprio il modello).
- Batch Size: Indica il numero di campioni che vengono elaborati prima che il modello aggiorni i pesi durante l'addestramento. Un valore più grande significa che più dati vengono elaborati contemporaneamente, riducendo il numero di aggiornamenti dei pesi, mentre un valore più piccolo aumenta il numero di aggiornamenti dei pesi, ma con meno dati processati in ogni aggiornamento.
L'equilibrio tra questi valori non è banale, quindi ho scritto due script. Uno che lancia una sequenza di learning dell'IA con diversi parametri magliati tra di loro (sotto un esempio di una configurazione che ha comportato 1536 train differenti), ed uno che prende i log dei risultati, gli fa un test indipendente su un dataset esterno mai visto dal modello (per evitare l'Overfitting) e scrive tutto in un database.
Addestrando lo stesso modello con gli stessi parametri uscivano tuttavia delle accuratezze (ovvero percentuali di previsioni azzeccate) diverse, e la cosa mi ha meravigliato. Perchè identici parametri davano risultati differenti? Poi ho scoperto i "seed". Alcune variabili vengono generati casualmente durante il processo di addestramento, come i pesi iniziali delle connessioni tra i neuroni, lo Shuffle dei dati di addestramento, i batch presi dal Stochastic Gradient Descent (SGD), i livelli neurali Batch Normalization e Dropout, il calcolo dei gradienti usati dalla GPU, i processi di cross-validation o ricerca casuale degli iperparametri (random search), la Random Augmentations nei Dati ed altri valori. Fissando il Seed (seme) la casualità viene contenuta al massimo, rendendo i test ripetibili. Fattore, questo, assolutamente fondamentale per avere dei test comparativi accurati.
Com'è andata a finire?
Sarò diventato milionario? Questo lo scopriremo nella parte 2, anche perchè per ora sto ancora addestrando il modello e sto cercando la combinazione che mi restituisce l'accuratezza migliore!
Qualora non mi doveste vedere più in giro, allora vorrà dire che effettivamente sarò diventato milionario. In caso contrario, bhè... almeno avrò sperimentato un po'. E si sa, la conoscenza non ha prezzo.