Private Vision: generare immagini in locale — Parte 1

Image

Private Vision: generare immagini in locale — Parte 1

Dopo settimane di sviluppo su Private Mind, la chat IA locale con Gemma, avevo un sistema stabile, testato, e soprattutto offline. Nessuna richiesta HTTP verso OpenAI, Anthropic o chiunque altro. Ma il progetto portava con sé una lezione che molti non ascoltano fino a quando non la pagano, una cosa fatta bene costa risorse, e aggiungere una seconda cosa sopra consuma il margine che non avevi.

Quando ho pensato di aggiungere al sistema la generazione di immagini, il passo naturale dopo un chatbot testuale, mi sono fermato a una decisione che è meno visibile del codice ma più importante, non metterò tutto in un unico software.

Perché non aggiungere la generazione a Private Mind

Private Mind gira con l’intera pipeline Gemma (modello large, 9B parametri) già in VRAM. Durante una conversazione la GPU ha i pesi del modello, qualche cache intermedia, e basta. È un carico prevedibile, la memoria sale durante l’inferenza, scende dopo, e la GPU rimane per il 90% del tempo sottoutilizzata ma occupata.

Aggiungere la generazione di immagini alla stessa GPU avrebbe significato:

  • Scaricare Gemma e caricare un modello di diffusione mentre una conversazione è ancora aperta.
  • Competizione sulla GPU: due runtime Python, due set di thread, due buffer inaspettati.
  • Margine di memoria ridotto a zero: su una RTX 2070 (8GB nominali, ~6GB liberi durante il lavoro) non è uno scenario teorico, è quello che succede.
  • Codice architetturalmente confuso: la stessa applicazione tiene due sistemi completamente diversi, uno conversazionale che riusa lo stato, uno stateless per la generazione, con il risultato che nessuno è ottimizzato per quello che dovrebbe fare.
    La lezione che avevo imparato da settimane di Vibe Coding era questa: il tuo codice è tanto pulito quanto i confini che metti tra le cose. Se metto due giganti in una stanza, non li ho semplificati, li ho solo resi più difficili da sostenere.

Decisione: nuovo software, stesso concetto. Private Vision nasce come gemello di Private Mind, stesso DNA (offline, caricamento asincrono, interfaccia leggera), architettura separata.

Cosa sono i checkpoint

Un checkpoint è il file che contiene i pesi della rete neurale di un modello di diffusione. Se la rete neurale fosse una ricetta per cucinare, i pesi sarebbero le misure esatte di ogni ingrediente, il risultato di miliardi di aggiustamenti durante l’addestramento.

Per Stable Diffusion (che usa SDXL, la versione che ho scelto), un checkpoint è un file .safetensors che pesa tra i 5 e i 7 GB. Non è un’applicazione, non è codice eseguibile: è pura matematica congelata in binario. Quando il software carica il checkpoint, dice alla GPU: “Qui ci sono i numeri che trasformeranno il testo in un’immagine. Memorizzateli.”

Il bello dei checkpoint è che ce ne sono decine di migliaia nella comunità open-source con varianti ottimizzate per stili diversi, checkpoint “distillati” che pesano meno, checkpoint specializzati. Non c’è un unico “Stable Diffusion ufficiale”: ce n’è uno di base, e da lì è partita un’esplosione creativa.

I tre livelli dell’architettura di Private Vision

Appena apri il repository di Private Vision trovi quattro file Python che incapsulano tutto:

app.py: il punto di ingresso. La sua responsabilità è una sola, quella di scegliere quale checkpoint caricare. Questo suona banale, ma è il cardine che tiene fuori gli effetti collaterali dall’import. Se il modulo principale iniziasse a caricare il checkpoint al momento dell’import, avresti subito un processo che scende pesantemente su disco e GPU nel momento in cui scrivi from private_vision import qualcosa in qualunque altro script. Invece, app.py chiede all’utente (in modo interattivo, se necessario) quale file usare, poi avvia tutto il resto dopo che la scelta è stata fatta.

private_vision/engine.py: il cuore. Qui vive tutta la logica di caricamento e generazione. Engine cerca i checkpoint disponibili in models/, li espone come opzioni, carica quello scelto in un thread separato (così l’interfaccia rimane responsiva mentre aspetta), e mette a disposizione il pipeline di diffusione già preparato. Engine è il punto di contatto unico con PyTorch e diffusers, tutto il resto non sa nemmeno che exist ono.

private_vision/web.py: l’interfaccia HTTP. Espone tre route principali:

  • GET /api/status — il frontend la chiama ogni 500ms per sapere se il modello è pronto.
  • POST /api/generate — riceve il prompt, invoca il pipeline, torna l’immagine.
  • POST /api/shutdown — chiude l’intera app (perché un processo Flask rimasto in ascolto è il modo più veloce di perdere ore su un bug che non c’è).

private_vision/config.py: i parametri di configurazione. Niente di eccitante, sono percorsi dei folder, porte del server, step di default per la generazione. Esiste separato apposta — se devi cambiare il comportamento, non tocchi il codice di logica, tocchi la configurazione.

Cos’è PyTorch

Dal sito IBM e ricercando su Wikipedia PyTorch viene definito come un framework open source di machine learning e deep learning basato su Python. Sviluppato principalmente da Meta AI, serve a creare e addestrare reti neurali per compiti come la computer vision e l’elaborazione del linguaggio naturale.

PyTorch definisce una classe chiamata Tensor (torch.Tensor) per memorizzare e operare su array rettangolari multidimensionali omogenei di numeri. I tensori PyTorch sono simili agli array NumPy, ma possono essere gestiti anche da una GPU NVIDIA compatibile con CUDA. PyTorch ha anche sviluppato il supporto per altre piattaforme GPU, ad esempio ROCm di AMD [24] e Metal Framework di Apple.[25]

Il flusso da prompt a immagine

Quando scrivi un prompt nell’interfaccia e clicchi “Genera”:

  1. Il frontend invia il testo al backend via POST a /api/generate.
  2. Il backend verifica che il pipeline sia disponibile. Fino a quel momento poteva ancora stare caricando in background; se non è pronto, la richiesta rimane in coda.
  3. Il prompt va al text encoder, un modello più piccolo (CLIP, integrato già nel checkpoint) che converte il testo in numeri che la rete neurale sa leggere. “Una montagna al tramonto” diventa un vettore di 768 numeri che catturano il concetto.
  4. Il pipeline di diffusione parte. Genera un’immagine partendo da rumore puro, ed ad ogni step la trasforma un po’ di più guidandola verso il significato che hai codificato nel testo. Se chiedi 30 step, sono 30 iterazioni di questo processo.
  5. Il decoder finale (VAE) converte il risultato interno della rete in pixel veri.
  6. L’immagine torna al frontend come base64, niente salvataggi su disco, solo memoria, così da poter mostrare velocemente all’utente quello che hai generato.

Tutto accade sulla GPU. PyTorch gestisce i vettori, la memoria, il parallelismo a basso livello. Tu devi solo pensare al flusso ad alto livello: testo → numeri → immagine.

Perché non uno, ma due software?

Tornando al punto di partenza: se avessi messo tutto insieme, avrei guadagnato “semplicità” solo sulla carta, una repo unica. In pratica avrei perso:

  • Stabilità: se la generazione di immagini ha un bug grave (e i modelli di IA ne hanno), non tira giù la chat.
  • Sviluppo indipendente: posso migliorare Private Vision senza toccare le centinaia di chat di Private Mind.
  • Risorse prevedibili: la GPU ha un proprietario chiaro per ogni momento.

L’architettura di microservizi non è una moda da grandi aziende, è una lezione su come un sistema non crolla quando lo usi per due cose diverse. Qui non sono microservizi nel senso di Kubernetes e load balancer, sono solo due processi: ma la lezione rimane.

La prossima volta che vedi un progetto aggiungere una feature sempre più complessa allo stesso eseguibile, chiediti: sta aggiungendo logica, o sta solo rimandando il momento di creare il nuovo eseguibile che tanto dovrà creare comunque?

Se l’articolo ti è piaciuto restiamo in contatto su linkedin a: https://www.linkedin.com/in/andreatonin/

Releated Posts

Private Vision: i LoRA e la matematica della personalizzazione — Parte 4

La cartella loras/ di Private Vision era piena di roba fatta da altri. Funzionava, per carità: Private Vision…

DiByAndrea Tonin Set 9, 2026

Dalla Skill all’Agente AI: automatizzare le note spese con ChatGPT e Google Workspace

Prima abbiamo distinto Progetti, GPT e Skill. Un Progetto è uno spazio di lavoro, un GPT è un…

DiByAndrea Tonin Ago 23, 2026

Anatomia di una Skill di ChatGPT: quando il prompt diventa una procedura

Negli ultimi mesi abbiamo imparato a scrivere prompt sempre più strutturati. Non più il classico “fammi un testo…

DiByAndrea Tonin Ago 23, 2026

Progetti, GPT e Skill: la differenza spiegata bene

Negli ultimi anni ChatGPT è passato dall’essere “una chat intelligente” a diventare un piccolo ecosistema di strumenti: Progetti,…

DiByAndrea Tonin Ago 23, 2026