BoccaccioAI

Modello linguistico nativo italiano da 700 milioni di parametri, addestrato da zero.

De Lauretis Tech


Panoramica

BoccaccioAI e' un modello linguistico decoder-only da circa 700 milioni di parametri, addestrato interamente da zero su testo italiano estratto dai dataset CulturaX, Wikipedia IT e mC4 Italian. Il modello e' ottimizzato per compiti di generazione testuale e Question & Answering in lingua italiana.

Caratteristiche principali:

  • Tokenizer BPE custom con vocabolario da 32.000 token, addestrato specificamente su testo italiano.
  • Grouped Query Attention (GQA) con rapporto 3:1 per ridurre la memoria in fase di inferenza.
  • Funzione di attivazione SwiGLU per una migliore efficienza di addestramento.
  • Rotary Position Embeddings (RoPE) per la codifica posizionale relativa.
  • Compatibilita' con Flash Attention 2 per l'accelerazione delle operazioni di attenzione.
  • Architettura scalata secondo le Chinchilla Scaling Laws (ratio token/parametri ~14:1).
  • Fine-tuning con instruction following + refusal training (honesty) su 270k esempi italiani.
  • Fine-tuning V3 con dataset nativi italiani ribilanciato (116k esempi, 5 fonti).
  • Distillazione da teacher Qwen3-235B su 98k esempi SQuAD-IT + instruction generali.
  • RAG (Retrieval-Augmented Generation) con indice FAISS esatto (IndexFlatIP) su 3.2M chunk di Wikipedia IT.
  • Retrieval ibrido: bi-encoder (e5-small) + reranker cross-encoder per disambiguare entita' simili.
  • Deploy serverless su Modal.com (GPU L4) con memory snapshot, scale-to-zero e truecasing automatico.

Architettura

Componente Specifica
Parametri ~700M
Hidden Size 1536
Layer 26
Attention Heads 12 (GQA 3:1, 4 KV heads)
FFN SwiGLU (intermediate 4096)
Posizionale RoPE (theta 10000)
Normalizzazione RMSNorm (eps 1e-5)
Contesto 2048 token
Vocabolario 32.000 token (BPE italiano)
Embedding Condivisi Input embedding = LM head
Dropout 0.0 (standard pre-training LLM)

Struttura del Progetto

boccaccioAI/
|-- configs/
|   |-- model.yaml              # Configurazione architettura modello
|   |-- tokenizer.yaml          # Configurazione tokenizer BPE
|   |-- training.yaml           # Iperparametri pre-training e fine-tuning
|-- src/
|   |-- data/                   # Download, filtro, tokenizzazione, dataset
|   |-- inference/              # Generazione e inferenza
|   |-- model/                  # Architettura: config, attention, layers, transformer
|   |-- tokenizer/              # Addestramento tokenizer BPE
|   |-- training/               # Lightning module, scheduler, callbacks
|-- rag/                        # Pipeline RAG (FAISS + Wikipedia IT)
|   |-- build_index.py          # Costruzione indice FAISS da wiki_it.jsonl
|   |-- retrieve.py             # Retrieval FAISS + lookup SQLite
|   |-- rag_chat.py             # Pipeline RAG completa (retrieval + generazione)
|   |-- download_wiki.py        # Download Wikipedia IT da HuggingFace
|   |-- run_full_rag.sh         # Script end-to-end per build indice
|   |-- requirements.txt        # Dipendenze RAG (faiss, sentence-transformers)
|-- distill/                    # Distillazione da teacher (Qwen3-235B)
|   |-- build_dataset.py        # Costruzione dataset distillazione (SQuAD-IT + instruction)
|   |-- generate_teacher.py     # Generazione risposte teacher via API OpenAI
|   |-- collect_prompts.py      # Raccolta prompt per distillazione
|   |-- filter_quality.py       # Filtraggio risposte per qualita'
|   |-- verify_answers.py       # Verifica risposte teacher
|   |-- check_cost.py           # Stima costi API teacher
|   |-- run_distill.sh          # Script training distillazione
|-- scripts/                    # Script pipeline e utility
|   |-- 01_train_tokenizer.sh   # Fase 1: addestramento tokenizer
|   |-- 02_preprocess_data.sh   # Fase 2: pipeline dati
|   |-- 02_5_smoke_test.sh      # Smoke test su GPU locale (modello nano)
|   |-- 03_pretrain.sh          # Pre-training 700M
|   |-- 04_finetune.sh          # Fine-tuning V1
|   |-- 05_evaluate.sh          # Valutazione
|   |-- 05_finetune_v2.sh       # Fine-tuning V2
|   |-- 06_finetune_v3.sh       # Fine-tuning V3
|   |-- 06_resume_pretrain.sh   # Continued pretraining (Wiki + mC4)
|   |-- 07_finetune_distill.sh  # Fine-tuning con distillazione
|   |-- run_fases_1_2.sh        # Esecuzione Fasi 1-2 su VM
|   |-- auto_upload_distill.sh  # Auto-upload checkpoint distill su HF
|   |-- convert_to_safetensors.py    # Conversione model.pt -> safetensors BF16
|   |-- convert_metadata_to_sqlite.py # Conversione metadata.pkl -> SQLite
|   |-- extract_model_pt.py     # Estrazione model.pt da checkpoint Lightning
|   |-- upload_weights_modal.py # Upload pesi su Modal Volume
|   |-- upload_index_modal.py   # Upload indice FAISS su Modal Volume
|   |-- convert_index_to_flat.py # Conversione IVF -> IndexFlatIP (ricerca esatta)
|   |-- download_ckpt.py        # Download checkpoint da HF Hub
|   |-- get_api_key.py          # Recupera Modal Secret API key
|   |-- benchmark_v3.py         # Benchmark comparativo tra checkpoint
|   |-- chat_distill.py         # Chat interattiva con modello distillato
|   |-- read_tb.py              # Lettura metriche TensorBoard
|-- tests/                      # Test end-to-end
|-- requirements.txt            # Dipendenze complete (training GPU)
|-- requirements-vm.txt         # Dipendenze minime (VM senza GPU)
|-- modal_app.py                # App Modal.com: RAG serverless con memory snapshot
|-- README.md
|-- AGENTS.md

Requisiti

  • Python 3.10+
  • CUDA 12.x con GPU compatibile (H100 consigliata per il training completo)
  • PyTorch 2.3+

Il progetto utilizza due file di dipendenze separati in base all'ambiente di esecuzione:

requirements.txt - Ambiente completo (training GPU)

Include tutte le dipendenze necessarie per il pre-training e fine-tuning del modello su GPU. Richiede una GPU CUDA per l'installazione di flash-attn.

pip install -r requirements.txt

Dipendenze principali: torch, lightning, tokenizers, flash-attn, datasets, wandb, transformers, safetensors.

Per il deploy Modal: modal, fastapi, pydantic. Installazione: pip install modal fastapi pydantic.

requirements-vm.txt - Ambiente VM (Fasi 1-2, solo CPU)

Dipendenze minime per l'esecuzione delle Fasi 1-2 (tokenizer + data pipeline) su VM cloud senza GPU. Esclude torch, lightning, flash-attn e altre librerie GPU-specifiche che non servono per il preprocessing dei dati.

pip install -r requirements-vm.txt

Dipendenze: tokenizers, datasets, datasketch, numpy, tqdm, xxhash, pyyaml.

Questo file viene utilizzato automaticamente dallo script scripts/vm_setup.sh durante il provisioning della VM Hetzner.


Pipeline di Addestramento

L'addestramento completo si articola in 5 fasi sequenziali, ciascuna eseguibile tramite il rispettivo script.

Fase 1 -- Addestramento Tokenizer

Addestra un tokenizer BPE da 32K token su un sottoinsieme di 5GB di CulturaX italiano.

bash scripts/01_train_tokenizer.sh

Fase 2 -- Pipeline Dati

Scarica CulturaX italiano (30GB), filtra e deduplica i documenti, quindi pre-tokenizza il corpus in formato binario per il training.

La pipeline di filtering e' composta da tre stage streaming (shard per shard) per funzionare su VM con RAM limitata (16GB):

  1. Heuristic filtering - rimuove documenti troppo corti/lunghi, con basso ratio alfabetico, punteggiatura eccessiva, o linee ripetute. Output: data/heuristic/
  2. Exact deduplication (xxhash) - rimuove duplicati esatti calcolando xxhash del testo normalizzato. RAM: ~200MB per 10M documenti. Output: data/filtered/
  3. Perplexity filtering (opzionale) - richiede modello KenLM. Skippato se non fornito.

Nota sulla strategia di dedup: Inizialmente era previsto MinHash LSH per catturare anche i near-duplicate, ma su 9.26M documenti con 128 permutazioni richiedeva ~15 ore e ~10GB di RAM, con rischio OOM su VM 16GB. L'exact dedup via xxhash completa in ~10 minuti con ~200MB di RAM. CulturaX e' gia' pre-deduplicato da HuggingFace, quindi i near-duplicate residui sono trascurabili per un modello da 700M parametri.

La pre-tokenizzazione converte il testo filtrato in token IDs binari (uint16) usando il tokenizer BPE addestrato in Fase 1. Anche questo step usa un'architettura streaming: tokenizza uno shard alla volta e scrive i token IDs direttamente su file binario, evitando di caricare l'intero corpus in RAM. Output: data/tokenized/pretrain/train.bin, val.bin, meta.json.

Nota sulla tokenizzazione streaming: La versione originale caricava tutti i 30GB di testo + ~10B token IDs in RAM come liste Python, causando OOM su VM 16GB. La versione streaming usa ~240MB di RAM e completa in ~3 ore.

bash scripts/02_preprocess_data.sh

Fase 2.5 -- Smoke Test su GPU Locale

Verifica che l'intera pipeline di training funzioni correttamente sui dati reali addestrando il modello nano (11M parametri) per 200 step su GPU locale. Questo passo e' gratuito e permette di individuare problemi prima di pagare l'H100.

bash scripts/02_5_smoke_test.sh

Requisiti: 1x GPU con almeno 4GB VRAM (RTX 3060 12GB e' sufficiente). Tempo stimato: ~10-15 minuti.

Fase 3 -- Pre-training

Addestra il modello da 700M parametri su 10 miliardi di token con ottimizzatore AdamW, schedule cosine con warmup e precisione BF16 mista. Il ratio token/parametri (~14:1) e' vicino all'ottimale secondo le Chinchilla Scaling Laws.

bash scripts/03_pretrain.sh

Fase 4 -- Instruction Fine-tuning

Instruction fine-tuning sul modello pre-addestrato per migliorare le capacita' di risposta a domande e istruzioni. Il fine-tuning trasforma il modello da "completatore di testo" a "assistente che risponde a domande".

Prerequisiti:

  • Checkpoint pre-training completato (checkpoints/pretrain/last.ckpt)
  • Dataset instruction tokenizzato (data/tokenized/finetune/train.bin, val.bin)

Dataset: il dataset di fine-tuning e' composto da 3 fonti unite e tokenizzate in formato binario:

Fonte Esempi Tipo Descrizione
anakin87/fine-instructions-ita-70k 69.890 Instruction generali Traduzione LLM-aided con quality filtering (giudice LLM)
raicrits/Orca_ITA_200k 199.922 Instruction + reasoning Orca-style, system prompt + domande con ragionamento implicito
Refusal examples (generati) 1.140 Honesty training Domande su eventi futuri, persone sconosciute, dati sensibili -> "Non ho informazioni sufficienti"
Totale 270.952 ~102M token

Il refusal training insegna al modello a rispondere onestamente "Non ho informazioni sufficienti per rispondere a questa domanda" quando non conosce la risposta, invece di allucinare. Gli esempi coprono 6 categorie: eventi futuri, persone sconosciute/private, dettagli troppo specifici, domande mediche personali, domande legali specifiche, dati sensibili/privacy.

Formato chat: ogni esempio viene formattato come:

### Utente:
<domanda>
### Assistente:
<risposta></s>

Preparazione dataset (in locale, non su GPU):

python src/data/generate_refusals.py     # Genera 1.140 esempi refusal
python src/data/prepare_finetune.py      # Scarica, unisce, tokenizza, salva in binario
python scripts/upload_finetune.py        # Upload su HF Hub

Training: ~45 min su H100 (102M token, 3 epoch, LR 2e-5, cosine decay, effective batch 65k token/step). Il training usa PreTokenizedDataset (formato binario, come il pre-training) invece di InstructionDataset (JSONL) per efficienza.

# Setup completo su Vast.ai (download dati + avvio training + daemon upload)
python scripts/vast_finetune.py --host <ip> --port <porta> --key ~/.ssh/vast_rsa

Il daemon auto_upload_finetune.sh monitora la directory checkpoints/finetune/ e carica automaticamente ogni nuovo checkpoint su HF Hub, mantenendo solo gli ultimi 2 locali.

Dopo il fine-tuning, il modello puo' essere usato in modalita' chat/Q&A e risponde onestamente quando non conosce una risposta.

Fase 5 -- Valutazione

Esegue test di generazione e Q&A per valutare la qualita' del modello addestrato.

bash scripts/05_evaluate.sh

Fase 6 -- Continued Pre-training (Phase 2)

Continua il pre-training aggiungendo Wikipedia IT (1B token) e mC4 Italian filtrato (3B token). Usa LR ridotto (2e-4 vs 4e-4 della fase 1) con cosine decay, caricando i pesi dal checkpoint fase 1 (step 12500). Porta il modello a ~10.8B token totali visti (Chinchilla-optimal).

bash scripts/06_resume_pretrain.sh

Lo script gestisce automaticamente: download dati, tokenizzazione, merge, e lancio training. Tempo stimato: ~5-6 ore totali (1h prep + 4-5h training) su H100.

Fase 7 -- Fine-tuning V2 (dataset nativi italiani)

Secondo fine-tuning con dataset nativi italiani per migliorare la naturalita' delle risposte. Parte dal last.ckpt del fine-tuning V1.

Dataset: 5 fonti italiane native (non tradotte):

  • antoniogr7/italian-sft-curated (50k esempi, reservoir sampled da 1.17M)
  • anakin87/fine-instructions-ita-70k (25k esempi, quality >= 3)
  • DeepMount00/italian_conversations (21k esempi)
  • crux82/squad_it (19k esempi, SQuAD italiano)
  • Mattimax/OpenITA-Conversation (1k esempi)
bash scripts/05_finetune_v2.sh

Checkpoint caricati su HF Hub come checkpoints/finetune_p2/.

Fase 8 -- Fine-tuning V3 (dataset ribilanciato 116k)

Terzo fine-tuning con dataset ribilanciato e ampliato. Parte dal last.ckpt del fine-tuning V1 (non V2).

Dataset: 116.641 esempi totali da 5 fonti, con sampling ribilanciato:

Fonte Esempi Tipo
antoniogr7/italian-sft-curated 50.000 Instruction generali (reservoir da 1.17M)
anakin87/fine-instructions-ita-70k 24.999 Instruction con quality filtering
DeepMount00/italian_conversations 21.109 Conversazioni strutturate multi-turno
crux82/squad_it 19.420 Q&A su testi italiani (SQuAD-IT)
Mattimax/OpenITA-Conversation 1.113 Conversazioni informali
Totale 116.641 ~302MB JSONL

Dopo filtering per sequence_length (1024 token): 98.606 esempi train, 996 val.

bash scripts/06_finetune_v3.sh

Training: 7.300 step (2 epoch), LR 1e-5, warmup 30 step, BF16 mixed precision, ~3 ore su H100. Checkpoint caricati su HF Hub come checkpoints/finetune_p3/ ogni 1000 step. Il training si e' fermato a step 6000 (82%) per stop della VM Vast.ai.

Bug fix critico: durante il V3 e' stato scoperto e risolto un bug in InstructionDataset._estimate_token_length che causava crash a step 759. Il pre-filter tokenizzava la conversazione come un'unica stringa concatenata, mentre _process_example tokenizzava ogni turno separatamente. Poiche' il BPE non e' additivo attraverso i confini dei turni, la stima poteva sottostimare il conteggio reale, lasciando passare esemzi che poi superavano sequence_length in __getitem__. Fix: _estimate_token_length ora replica esattamente la tokenizzazione per-turno di _process_example.

Fase 9 -- Distillazione da Qwen3-235B

Fine-tuning con distillazione da teacher Qwen3-235B (via API OpenAI). Il dataset di distillazione include SQuAD-IT con contesto (stesso formato del RAG) + instruction generali, per un totale di 98k esempi.

Teacher: Qwen3-235B-A22B (accesso via API OpenAI-compatible, ~$0.003 per risposta)

Dataset (distill/build_dataset.py):

  • SQuAD-IT con contesto (19k esempi) -- formato RAG: Leggi il seguente testo... Domanda:
  • Instruction generali italiane (79k esempi) -- da antoniogr7/italian-sft-curated
  • Totale: 98k esempi, filtrati per qualita' (distill/filter_quality.py)

Training: 3 epoch (6.016 step) su RTX 3090 Ti (Vast.ai), BF16 mixed precision, ~1.85 it/s. Checkpoint finale: epoch=2-step=6016.ckpt (= last.ckpt, verificato identico).

bash scripts/07_finetune_distill.sh

Checkpoint caricati su HF Hub lollix91/boccaccio-distill. Il modello distillato e' quello attualmente in produzione su Modal con RAG.


Test Inferenza in Locale

Lo script scripts/test_inference.py permette di testare un checkpoint Lightning (.ckpt) o pesi puri (model.pt) su GPU locale (es. RTX 3060 12GB). Supporta sia checkpoint di PyTorch Lightning che file model.pt generati al termine del training.

Nota: eseguire da progetto root con PYTHONPATH=. (Linux/Mac) o $env:PYTHONPATH="." (PowerShell).

Generazione libera (pre-train)

python scripts/test_inference.py \
    --checkpoint checkpoints/pretrain/last.ckpt \
    --prompt "L'intelligenza artificiale in Italia" \
    --max-new-tokens 256 \
    --temperature 0.7

Modalita' Q&A (dopo fine-tuning)

python scripts/test_inference.py \
    --checkpoint checkpoints/finetune/model.pt \
    --mode qa \
    --question "Qual e' la capitale dell'Italia?" \
    --max-new-tokens 128 \
    --temperature 0.3

Il formato Q&A usa lo stesso template del fine-tuning: user <domanda> assistant . Il parametro --context e' opzionale.

Parametri disponibili

Parametro Default Descrizione
--checkpoint (richiesto) Path al file .ckpt o .pt
--tokenizer-path tokenizer/boccaccio-32k.json Path al tokenizer
--config-path configs/model.yaml Config del modello
--config-variant model Variante (model o nano)
--mode generate generate o qa
--prompt - Prompt per generate
--context - Contesto opzionale per qa
--question - Domanda per qa (richiesto in qa)
--max-new-tokens 256 Token da generare
--temperature 0.7 Temperatura sampling (0 = greedy)
--top-k 50 Top-k filtering
--top-p 0.9 Top-p nucleus sampling
--device auto cuda, cuda:0, cpu

Inferenza

Generazione libera

python -m src.inference.generate \
    --model-dir checkpoints/finetune \
    --tokenizer-path tokenizer/boccaccio-32k.json \
    --config-path configs/model.yaml \
    --mode generate \
    --prompt "L'intelligenza artificiale in Italia" \
    --max-new-tokens 256 \
    --temperature 0.7

Modalita' Q&A

python -m src.inference.generate \
    --model-dir checkpoints/finetune \
    --tokenizer-path tokenizer/boccaccio-32k.json \
    --config-path configs/model.yaml \
    --mode qa \
    --context "Roma e' la capitale della Repubblica Italiana. Si trova nella regione Lazio, nell'Italia centrale." \
    --question "Qual e' la capitale dell'Italia?" \
    --max-new-tokens 128 \
    --temperature 0.3

Parametri di generazione disponibili: --temperature, --top-k, --top-p, --max-new-tokens, --device.


Deploy Serverless su Modal.com

Il modello fine-tunato con distillazione e' deployato come endpoint RAG serverless su Modal.com (GPU L4) con scale-to-zero e memory snapshot per minimizzare costi e cold start.

Architettura

Browser (JS) -> PHP proxy (rate-limit + auth) -> Modal endpoint (GPU L4, RAG) -> risposta
                                                   |
                                                   +-> FAISS retrieval (3.2M chunk Wikipedia IT)
                                                   +-> Generazione BoccaccioAI 700M
  • L'URL di Modal e l'API key non sono mai esposti al client
  • Il proxy PHP gestisce rate-limiting (IP + sessione) e fissa i parametri di generazione
  • Modal autentica le richieste via header X-API-Key (Modal Secret)
  • Il RAG recupera contesto da Wikipedia IT (3.2M chunk) e lo passa al modello per generare risposte fattuali
  • Truecasing automatico: i nomi propri vengono corretti usando i titoli dei chunk recuperati

Configurazione Modal

Parametro Valore
GPU NVIDIA L4
Scale-to-zero Si (scaledown_window=60s)
Timeout 180s
Memory snapshot CPU snapshot (enable_memory_snapshot=True)
Volume boccaccio-rag-data (modello 1.5GB + indice 4.6GB + SQLite 6.5GB)
Auth Modal Secret boccaccio-api-key
Embedder intfloat/multilingual-e5-small (bakato nell'immagine)

Ottimizzazioni cold start

Tecnica Prima Dopo
Modello: safetensors BF16 (zero-copy) 7.9GB .ckpt (torch.load) 1.5GB safetensors (load_file)
Metadata: SQLite su disco 5.1GB pickle (30-60s caricamento) 6.5GB SQLite (0.6ms apertura, 0 RAM)
Memory snapshot CPU Caricamento ad ogni cold start Caricamento snapshottato, restore veloce
Embedder e5-small Download da HF ad ogni avvio Bakato nell'immagine Modal

Risultato: cold start ~19s (con snapshot) invece di 60-90s (senza ottimizzazioni).

Parametri di generazione (default)

Parametro Valore Note
max_new_tokens 256
temperature 0.0 Greedy decoding per massima accuratezza fattuale (QA)
top_k_sampling 40 Usato solo con temperature > 0
repetition_penalty 1.0 Disabilitata: non penalizza la ripetizione di fatti dal contesto
top_k (retrieval) 3 Chunk recuperati dopo reranking
top_n (retrieval largo) 20 Chunk recuperati dal bi-encoder prima del reranking
Indice FAISS IndexFlatIP Ricerca esatta (recall perfetta), ~27ms/query su 3.2M vettori

Pipeline di deploy

# 1. Estrai model.pt dal checkpoint Lightning
python scripts/extract_model_pt.py \
    --ckpt checkpoints/finetune_distill/last.ckpt \
    --output checkpoints/finetune_distill/model.pt

# 2. Converti in safetensors BF16
python scripts/convert_to_safetensors.py \
    --input checkpoints/finetune_distill/model.pt \
    --output checkpoints/finetune_distill/model.safetensors \
    --dtype bf16

# 3. Converti metadata.pkl in SQLite (se non gia' fatto)
python scripts/convert_metadata_to_sqlite.py \
    --input rag/index/faiss/metadata.pkl \
    --output rag/index/faiss/metadata.db

# 4. Upload artefatti su Modal Volume
modal volume put boccaccio-rag-data checkpoints/finetune_distill/model.safetensors /model.safetensors
modal volume put boccaccio-rag-data rag/index/faiss/index.faiss /index.faiss
modal volume put boccaccio-rag-data rag/index/faiss/metadata.db /metadata.db

# 5. Deploy app
modal deploy modal_app.py

Performance

Scenario Tempo
Cold start (con memory snapshot) ~19s
Warm (container attivo) ~3-5s per 200 token

Costo inferenza

  • GPU L4 su Modal: ~$0.80/h
  • Con scale-to-zero (60s idle), il costo e' solo per le richieste effettive
  • Una richiesta da 200 token costa ~$0.001 (warm) o ~$0.004 (cold start)

Endpoint

  • POST /generate -- RAG: recupera contesto e genera risposta (richiede X-API-Key)
  • GET /health -- health check

Test endpoint

curl -X POST https://<modal-url>/generate \
    -H "Content-Type: application/json" \
    -H "X-API-Key: <api-key>" \
    -d '{"question": "Chi e Dante Alighieri?", "use_rag": true}'

Configurazione

Tutti gli iperparametri sono centralizzati in file YAML nella directory configs/:

  • configs/model.yaml -- Architettura del modello (dimensioni, numero di layer, GQA, ecc.). Include anche la variante nano per test locali rapidi.
  • configs/tokenizer.yaml -- Parametri del tokenizer BPE (vocabolario, normalizzazione, corpus sorgente).
  • configs/training.yaml -- Iperparametri di addestramento separati per pre-training e fine-tuning (learning rate, batch size, scheduler, checkpoint, hardware).

Configurazione fine-tuning

Il file configs/training.yaml sezione finetune contiene:

Parametro Valore Descrizione
micro_batch_size 8 Batch per-GPU (H100 80GB)
gradient_accumulation_steps 4 Accumulazione gradienti
Effective batch 65.536 token/step 8 * 4 * 2048
learning_rate 2e-5 LR basso per fine-tuning
min_learning_rate 2e-6 LR finale (cosine decay)
warmup_steps 50 Warmup breve
num_epochs 3 Epoch totali
num_tokens 102M Token dataset (per cosine schedule)
compile_model false torch.compile disabilitato (overhead non giustificato per training breve)
use_flash_attention true Flash Attention 2
checkpoint_every_n_steps 1500 Salvataggio ogni ~1500 step
val_check_interval 200 Validation ogni 200 step

RAG (Retrieval-Augmented Generation)

Il sistema RAG recupera contesto da Wikipedia italiana (3.2M chunk) tramite un indice FAISS esatto (IndexFlatIP), poi lo passa al modello per generare risposte fattuali. Un reranker cross-encoder disambigua entita' simili (es. "Michael Schumacher" vs "Ralf Schumacher") rivalutando ogni chunk insieme alla query completa.

Componenti

Componente Dettagli
Knowledge base Wikipedia IT (1.2M pagine, 3.2M chunk da ~500 caratteri)
Embedding model (bi-encoder) intfloat/multilingual-e5-small (384 dim, 470MB)
Reranker (cross-encoder) BAAI/bge-reranker-v2-m3 (568M, bakato nell'immagine Modal)
Indice FAISS IndexFlatIP (ricerca esatta), 3.2M vettori, 4.9GB
Metadata SQLite (idx -> title, text), 6.5GB, lookup O(1) su disco
Retrieval Bi-encoder top-20 -> reranker top-3

Perche' IndexFlatIP invece di IVFFlat

L'indice IVFFlat originale (nprobe=64 su nlist=2048) esplorava solo il 3% dei cluster, mancando i chunk rilevanti per entita' meno comuni. Questo causava allucinazioni sistemiche: ad esempio "Chi e' Rubens Barrichello?" recuperava "René Barjavel" e "Pieter Paul Rubens" invece della voce corretta (che aveva score piu' alto ma era in un cluster non sondato). IndexFlatIP garantisce recall perfetta con latenza comparabile (~27ms/query) e stessa RAM.

Pipeline di indicizzazione

# 1. Download Wikipedia IT da HuggingFace
python rag/download_wiki.py --output rag/data/wiki_it.jsonl

# 2. Embedding + costruzione indice FAISS
python rag/build_index.py \
    --wiki-path rag/data/wiki_it.jsonl \
    --output-dir rag/index/faiss/ \
    --batch-size 512

# 3. Conversione metadata.pkl -> SQLite (per cold start veloce)
python scripts/convert_metadata_to_sqlite.py \
    --input rag/index/faiss/metadata.pkl \
    --output rag/index/faiss/metadata.db

L'indicizzazione e' stata eseguita su VM H100 (Vast.ai): embedding di 3.2M chunk in ~45 min, training FAISS IVF in ~2 min.

Test RAG in locale

# Confronto RAG vs no-RAG
python rag/rag_chat.py --question "Chi e' Dante Alighieri?" --compare

# Solo RAG
python rag/rag_chat.py --question "Chi e' Dante Alighieri?"

# Mostra il testo completo dei chunk recuperati
python rag/rag_chat.py --question "Chi e' Rubens Barrichello?" --show-sources

# Passa contesto personalizzato (bypassa il retrieval)
python rag/rag_chat.py --question "Quando e' nato Michael Schumacher?" --context "Michael Schumacher (Hürth, 3 gennaio 1969) è un ex pilota..."

# Suite di test (10 domande)
python rag/rag_chat.py --test-suite

Truecasing via retrieval

Il modello e' addestrato su testo con maiuscole corrette e degrada con input tutto minuscolo. Il sistema corregge automaticamente il case della domanda usando i titoli dei chunk recuperati:

  • "chi e dante alighieri?" + chunk titolo "Dante Alighieri" -> "Chi e Dante Alighieri?"
  • Senza RAG: capitalizza solo la prima lettera
  • Zero costo aggiuntivo (sfrutta i chunk gia' recuperati)

Artefatti su HuggingFace

Repo lollix91/boccaccio-rag:

  • index.faiss (4.9GB) -- indice FAISS IndexFlatIP (ricerca esatta)
  • metadata.pkl (5.1GB) -- metadata originale (pickle)
  • embeddings.npy (4.6GB) -- embedding numpy (per rebuild)
  • data/wiki_it.jsonl (4.8GB) -- Wikipedia IT processata

Costi Stimati

Risorsa Stima
Hardware 1x NVIDIA H100 80GB (Vast.ai)
Tempo pre-training fase 1 ~25 ore (12.500 step, 6.6B token CulturaX)
Costo pre-training fase 1 ~$48 (Vast.ai H100 a $1.93/h)
Tempo pre-training fase 2 ~5 ore (8.000 step, 4.2B token Wiki+mC4)
Costo pre-training fase 2 ~$10
Tempo fine-tuning V1 ~45 min (102M token, 3 epoch)
Costo fine-tuning V1 ~$1.5
Tempo fine-tuning V2 ~1 ora
Costo fine-tuning V2 ~$2
Tempo fine-tuning V3 ~3 ore (7.300 step, 2 epoch)
Costo fine-tuning V3 ~$6
Tempo distillazione ~3.5 ore (6.016 step, 3 epoch, RTX 3090 Ti)
Costo distillazione ~$3 (VM + API teacher Qwen3-235B)
Indicizzazione RAG ~45 min (H100, 3.2M chunk)
Costo indicizzazione RAG ~$3 (Vast.ai H100)
Totale training ~$74
Inferenza (Modal L4) ~$0.80/h, scale-to-zero (pay-per-request)

Le stime si riferiscono al training completo su Vast.ai con H100. Il fine-tuning richiede risorse significativamente inferiori rispetto al pre-training.


Stato di Avanzamento

Fase Stato Dettagli
1. Tokenizer Completato BPE 32K su 5GB CulturaX italiano
2. Pipeline dati Completato 30GB CulturaX, filtering + dedup, 6.7B token
2.5. Smoke test Completato Modello nano su RTX 3060 locale
3. Pre-training (fase 1) Completato 12.500 step su H100, ~6.6B token CulturaX
4. Fine-tuning V1 Completato 270k esempi, 3 epoch, step 24.000. Val loss 2.13
5. Pre-training (fase 2) Completato 8.000 step, ~4.2B token Wiki+mC4. Totale 10.8B token
6. Fine-tuning V2 Completato Dataset nativi italiani, checkpoint su HF finetune_p2/
7. Fine-tuning V3 Completato (82%) 116k esempi ribilanciati, fermato a step 6.000/7.300. Val loss 1.85
8. Distillazione Completato 98k esempi da Qwen3-235B, 3 epoch, step 6.016. Checkpoint su HF boccaccio-distill
9. RAG (FAISS) Completato Indice FAISS IndexFlatIP (ricerca esatta) su 3.2M chunk Wikipedia IT, reranker cross-encoder, upload su HF boccaccio-rag
10. Deploy Modal (RAG) Completato RAG + distill su Modal.com (L4, memory snapshot, scale-to-zero, truecasing, greedy decoding)
11. Interfaccia web Completato Frontend PHP/HTML/JS con proxy + rate-limiting, endpoint RAG integrato

Catena di training

Pretrain (CulturaX 6.6B token)
  -> Pretrain P2 (Wiki+mC4, +4.2B token, totale 10.8B)
    -> Fine-tuning V1 (270k esempi SFT, 3 epoch, step 24.000)
      -> Fine-tuning V3 (116k esempi nativi IT, 2 epoch, step 6.000)
        -> Distillazione (98k esempi Qwen3-235B, 3 epoch, step 6.016) [DEPLOYED]
      -> Fine-tuning V2 (dataset nativi IT, checkpoint su HF)

Nota: V2 e V3 partono entrambi dal V1 (finetune/last.ckpt), non l'uno dall'altro. La distillazione parte dal V3. Il modello distillato e' quello in produzione su Modal con RAG.

Risultati benchmark V3 (step 6000) vs V1

Benchmark su 10 domande (temperature 0.3, 150 token max):

  • V3 vince 5-0-5 (5 vittorie, 5 pareggi, 0 sconfitte)
  • V3 risponde meglio su conoscenza fattuale (capitale, fotosintesi)
  • V3 ha meno allucinazioni di V1
  • V3 genera risposte piu' dettagliate e strutturate
  • Entrambi falliscono la matematica (normale per 700M senza training specifico)

Note storiche sul training

  • Pre-training fase 1: fermato a step ~4000, poi riavviato con --resume-from (solo pesi, optimizer resettato). Il checkpoint "step 12500" e' in realta' 4000 + 12500 step totali di forward pass, con reset LR/optimizer al riavvio.
  • Fine-tuning 1° run: partito da checkpoint stale (step 9500). Repetition collapse. Scartato.
  • Fine-tuning V1 (2° run): partito da pretrain_p2/last.ckpt, completato a step 24.000 (3 epoch). Modello funzionale come assistente, risponde correttamente a domande fattuali semplici.
  • Fine-tuning V2: partito da finetune/last.ckpt (V1), dataset nativi italiani. Checkpoint su HF finetune_p2/.
  • Fine-tuning V3: partito da finetune/last.ckpt (V1), dataset ribilanciato 116k. Crash a step 759 per bug in _estimate_token_length (BPE non additivo tra turni). Fixato e riavviato, fermato a step 6.000/7.300 per stop VM. Deployato su Modal.
  • Distillazione: partita da finetune_p3/last.ckpt (V3), dataset di 98k esempi generati da Qwen3-235B via API. Include SQuAD-IT con contesto (formato RAG) + instruction generali. Training completato a step 6.016 (3 epoch) su RTX 3090 Ti. Il modello distillato sostituisce V3 in produzione.
  • RAG: indice FAISS costruito su 3.2M chunk di Wikipedia IT. Inizialmente IVFFlat (nprobe=64), convertito a IndexFlatIP (ricerca esatta) per risolvere il recall crollato su entita' meno comuni. Reranker cross-encoder (bge-reranker-v2-m3) per disambiguare entita' simili. Embedding con e5-small su H100 (~45 min). Metadata convertito da pickle a SQLite per cold start veloce (0.6ms vs 30-60s). Deploy su Modal con memory snapshot CPU (cold start ~19s). Greedy decoding (temperature=0) e repetition penalty disabilitata per massima accuratezza fattuale in QA.

Storage HuggingFace Hub:

  • lollix91/boccaccio-data: checkpoint pre-training + fine-tuning V1/V2/V3, dati tokenizzati, tokenizer
  • lollix91/boccaccio-distill: checkpoint distillazione (last.ckpt + intermedi)
  • lollix91/boccaccio-rag: indice FAISS (4.7GB), metadata (5.1GB), embeddings (4.6GB), wiki_it.jsonl (4.8GB)

Nota spazio HF: il limite e' 100 GB per repo (inclusa git history LFS). I checkpoint intermedi non vengono piu' caricati durante il training per evitare di saturare lo spazio. Solo il model.pt finale viene uploadato.

Repository GitHub: lollix91/boccaccioAI -- codice sorgente, script, configurazione. I token API sono gestiti tramite variabili d'ambiente (HF_TOKEN) e file .hf_token sul server remoto.


Limitazioni note

  • Case sensitivity: il modello e' sensibile alle maiuscole/minuscole. Prompt scritti interamente in minuscolo (es. "chi e' vasco rossi?") vengono normalizzati automaticamente dal sistema RAG: i nomi propri vengono corretti usando i titoli dei chunk recuperati da Wikipedia (truecasing via retrieval). Senza RAG, viene capitalizzata solo la prima lettera.
  • Conoscenza fattuale: 700M parametri non possono memorizzare enciclopedia. Senza RAG, il modello allucina su fatti specifici. Usare sempre contesto per domande fattuali.
  • Comprensione del contesto: anche con il contesto corretto fornito dal RAG, il modello a volte mal-attribuisce fatti (es. confonde il luogo di nascita del nonno con quello del soggetto). Il reranker cross-encoder riduce il rumore nel contesto, ma il limite resta legato alla capacita' di comprensione del modello 700M.
  • Risposte brevi: il teacher (Qwen3-235B) era limitato a 400 token, quindi il modello impara a rispondere in modo conciso (2-10 righe).

Attribuzioni

BoccaccioAI e' stato addestrato su dati derivati dai seguenti dataset pubblici. Ringraziamo gli autori e le community che li hanno resi disponibili.

Dati di pre-training

Dataset Uso Licenza
uonlp/CulturaX (subset it) Corpus principale (~6.6B token) ODC-BY
wikimedia/wikipedia (20231101.it) Continued pre-training (~1B token) CC BY-SA 4.0
mC4 Italian (allenai/c4, config it) Continued pre-training (~3.2B token) ODC-BY

Dati di fine-tuning (SFT)

Dataset Uso Licenza
anakin87/fine-instructions-ita-70k Instruction generali Apache-2.0
raicrits/Orca_ITA_200k Instruction + reasoning Other (derivato da Orca)
antoniogr7/italian-sft-curated Instruction generali Apache-2.0
DeepMount00/italian_conversations Conversazioni multi-turno CC
crux82/squad_it Q&A su testi italiani Derivato da SQuAD (CC BY-SA 4.0)
Mattimax/OpenITA-Conversation Conversazioni informali MIT

Distillazione e RAG

Risorsa Uso Licenza
Qwen3-235B-A22B (via OpenRouter) Teacher per la distillazione Apache-2.0
OpenLLM-France/wikipedia (config it) Knowledge base RAG (5.7M chunk) CC BY-SA 4.0
intfloat/multilingual-e5-small Bi-encoder per retrieval MIT
BAAI/bge-reranker-v2-m3 Cross-encoder reranker Apache-2.0

Le ridistribuzioni dei dataset derivati in questo repository (es. data/tokenized/, distill/, indice FAISS) restano soggette alle licenze dei dataset sorgente indicate sopra, dove applicabile.

Licenza

Codice e pesi del modello sono rilasciati sotto Apache License 2.0 (vedi LICENSE).

Se utilizzi BoccaccioAI -- pesi, codice o opere derivate -- in una pubblicazione, un prodotto o un servizio, sei tenuto a fornire attribuzione come richiesto dalla licenza, mantenendo il copyright notice e il contenuto del file NOTICE.

Citazione suggerita:

BoccaccioAI -- Italian LLM from scratch (700M)
De Lauretis Tech, 2026
https://huggingface.co/lollix91/boccaccioAI
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support