top of page

A PoC (a-proof-of-concept) con LoRA

Ho già trattato LoRA in questo mio post 🔍 Cos’è LoRA e perché è così importante nella programmazione dell'AI , e oggi voglio presentare un PoC sull'utilizzo di LoRA:


# ===========================================

# PoC: Thought chain completa con LoRA

# Con frecce e commenti visivi per capire il flusso

# ===========================================


from transformers import AutoModelForImageClassification, AutoFeatureExtractor

from peft import LoraConfig, get_peft_model

from PIL import Image, ImageDraw

import torch

import pandas as pd


# ===========================================

# 1️⃣ Creazione immagine "fake"

# ===========================================

# Input → immagine tabella (può essere reale o fake)

width, height = 400, 200

image = Image.new("RGB", (width, height), "white")

draw = ImageDraw.Draw(image)


# Disegno 2x2 griglia per simulare celle

rows, cols = 2, 2

cell_w, cell_h = width // cols, height // rows

for r in range(rows):

for c in range(cols):

x0, y0 = c*cell_w, r*cell_h

x1, y1 = x0+cell_w, y0+cell_h

draw.rectangle([x0, y0, x1, y1], outline="black")

draw.text((x0+10, y0+10), f"R{r}C{c}", fill="black")


# visualizzazione opzionale

# image.show()


# ===========================================

# 2️⃣ Modello ViT + LoRA

# ===========================================

# Modello base → conosce pattern generali (non task specifico)

model_id = "google/vit-base-patch16-224"

model = AutoModelForImageClassification.from_pretrained(model_id)

extractor = AutoFeatureExtractor.from_pretrained(model_id)


# LoRA → aggiunge pesi mirati per task specifico (estrazione tabelle)

config = LoraConfig(

r=4,

lora_alpha=16,

target_modules=["query", "key", "value"],

lora_dropout=0.1,

bias="none",

)

lora_model = get_peft_model(model, config)

lora_model.eval() # modalità inferenza


# ===========================================

# 3️⃣ Preprocess immagine

# ===========================================

# Image → tensore compatibile con ViT

# pixel_values → input del modello

inputs = extractor(images=image, return_tensors="pt")

pixel_values = inputs["pixel_values"]


# Visualizzazione commentata

# Input immagine --> preprocess --> pixel_values --> modello

# 📌 Freccia: Input → Preprocess → Modello


# ===========================================

# 4️⃣ Inferenza con modello + LoRA

# ===========================================

with torch.no_grad():

logits = lora_model(pixel_values) # output grezzo del modello


# Visualizzazione commentata

# pixel_values --> modello + LoRA --> logits

# 📌 Freccia: Modello + LoRA → logits/features


# ===========================================

# 5️⃣ Conversione logits → valori leggibili

# ===========================================

# Questa funzione rappresenta il decoding dei logits in celle/valori

def convert_logits_to_cells(logits_tensor):

"""

Placeholder: in un caso reale decodificherebbe logits → valori delle celle

"""

return [

{"parameter": "R0C0", "date": "2025-11-27", "value": 42, "measurement": "unit"},

{"parameter": "R0C1", "date": "2025-11-27", "value": 17, "measurement": "unit"},

{"parameter": "R1C0", "date": "2025-11-27", "value": 33, "measurement": "unit"},

{"parameter": "R1C1", "date": "2025-11-27", "value": 58, "measurement": "unit"},

]


predictions = convert_logits_to_cells(logits)


# Visualizzazione commentata

# logits → convert_logits_to_cells() → predictions

# 📌 Freccia: Logits → Decoding → Output leggibile


# ===========================================

# 6️⃣ Conversione in DataFrame

# ===========================================

df = pd.DataFrame(predictions)


# Visualizzazione commentata

# predictions → DataFrame → output finale

# 📌 Freccia: Output leggibile → DataFrame → stampa


# ===========================================

# 7️⃣ Output finale

# ===========================================


print("=== Thought chain completa ===")

print(df)




Ecco cosa fa il codice, passo per passo, con il “film mentale” del flusso dati:

Immagine (input)
     │
     ▼
Preprocess (pixel_values)
     │
     ▼
Modello ViT + LoRA
     │
     ▼
Logits/features grezzi
     │
     ▼
Decoding (convert_logits_to_cells)
     │
     ▼
Predictions (JSON/celle)
     │
     ▼
DataFrame / output finale

N.B: La funzione convert_logits_to_cells() è un placeholder logico e rappresenta dove avviene il vero mapping dei logits in valori leggibili.


Spiegazione:


1. Generazione dell’immagine finta

  • Viene creata un’immagine RGB 400×200 bianca con PIL (Image.new).

  • Si disegna una griglia 2×2: si calcolano larghezza/altezza di ogni cella e si tracciano i rettangoli con draw.rectangle.

  • In ogni cella viene scritto un testo tipo R0C0, R0C1, ecc., così l’immagine simula una tabella con 4 celle.


In pratica: si sta costruendo un input di prova che assomiglia a una tabella, senza caricare un file reale.


2. Caricamento ViT e applicazione LoRA

  • model_id = "google/vit-base-patch16-224" carica un Vision Transformer pre-addestrato per classificazione immagini (ImageNet) tramite AutoModelForImageClassification.from_pretrained.​

  • extractor = AutoFeatureExtractor.from_pretrained(model_id) crea il preprocessore che sa come ridimensionare/normalizzare l’immagine per questo ViT.​


Poi entra in gioco LoRA:

  • Si definisce un LoraConfig con rank r=4, lora_alpha=16, lora_dropout=0.1 e target_modules=["query", "key", "value"].

  • get_peft_model(model, config) avvolge il modello con strati LoRA sulle sotto-componenti di attenzione (query/key/value), aggiungendo pesi addestrabili “a basso rango” solo lì.​

  • lora_model.eval() mette il modello in modalità inferenza (no dropout addestrativo, batchnorm fissa, ecc.).


Concettualmente: il ViT pre-addestrato fornisce la “base” di conoscenza visiva, LoRA è lo strato leggero che, se addestrato, specializzerebbe il modello sul task “estrai valori da tabelle”.


3. Preprocess dell’immagine

  • inputs = extractor(images=image, return_tensors="pt") converte l’immagine PIL in tensore PyTorch con le trasformazioni che il ViT si aspetta (resize a 224×224, normalizzazione canali, ecc.).​

  • pixel_values = inputs["pixel_values"] è il tensore che entra nel modello, tipicamente di forma (batch_size, 3, 224, 224).


Qui avviene il passaggio:Immagine PIL → extractorpixel_values (tensore pronto per ViT).


4. Inferenza con ViT + LoRA

  • Con torch.no_grad() si disattiva il calcolo dei gradienti (serve solo per inferenza).

  • logits = lora_model(pixel_values) calcola l’output del modello: per un modello di classificazione, logits sono i punteggi grezzi per ogni classe (prima di softmax).​


Qui la freccia logica è:pixel_values → ViT + LoRA → logits (feature/logits grezzi non ancora leggibili “a livello umano”).


5. Decoding: da logits a celle leggibili

  • La funzione convert_logits_to_cells(logits_tensor) è volutamente un placeholder: nel mondo reale prenderebbe i logits e li trasformerebbe nei valori di tabella (coordinate cella, valore, unità, data, ecc.).

  • Nel PoC, invece, ritorna una lista fissa di 4 dizionari, uno per ogni cella, con campi parameter, date, value, measurement.


Quindi, ricapitolando :logitsconvert_logits_to_cells()predictions (lista di dict, tipo JSON, che rappresentano le celle della tabella).


L’importante è che il punto concettuale in cui “leggi” davvero la tabella è proprio questa funzione: qui si fa il mapping logits → semantica (celle e valori).


6. Conversione in DataFrame

  • df = pd.DataFrame(predictions) prende la lista di dizionari e la trasforma in un DataFrame pandas, con colonne parameter, date, value, measurement.

  • Il DataFrame è la forma “standard” per lavorare poi analiticamente sulla tabella (filtri, groupby, esport in CSV, ecc.).


Freccia concettuale: predictions (JSON-like) → DataFrame → tabella strutturata per analisi downstream.


7. Output finale e “thought chain”

  • print("=== Thought chain completa ===") e print(df) mostrano in console il risultato finale leggibile.

  • Il diagramma testuale sotto riassume il chain of thought del dato:


    Immagine→ Preprocess (pixel_values)→ Modello ViT + LoRA→ Logits grezzi→ Decoding (convert_logits_to_cells)→ Predictions (celle in JSON)→ DataFrame / output finale


Il PoC quindi è una pipeline completa, anche se:

  • la parte “intelligente” di estrazione tabellare (decoding dei logits) è simulata;

  • tutti gli altri passaggi (creazione immagine, preprocess, inferenza, strutturazione in DataFrame) sono reali e funzionanti, solo che i dati di uscita sono finti.


E come si potrebbe integrere la parte intelligente che è ora solo simulata ?


Per integrare la parte “intelligente” bisogna sostituire il placeholder convert_logits_to_cells() con un vero modello addestrato a fare table extraction (o con una testa addestrata sopra ViT) e una logica di decoding dei suoi output.


Opzione 1: usare direttamente Table Transformer


La strada più “realistica” oggi è usare un modello già specializzato come Microsoft Table Transformer (DETR per tabelle), che esiste sia per detection che per structure recognition.​In pratica:


  1. Si sostituisce il ViT di classificazione con un modello Table Transformer per object detection/structure recognition:


    from transformers import TableTransformerForObjectDetection, AutoImageProcessor processor = AutoImageProcessor.from_pretrained(     "microsoft/table-transformer-structure-recognition" ) model = TableTransformerForObjectDetection.from_pretrained(     "microsoft/table-transformer-structure-recognition" )


  2. Si Preprocessa l’immagine con il processor:

    inputs = processor(images=image, return_tensors="pt") outputs = model(**inputs)

    Gli output contengono bounding box e classi per righe, colonne, celle, ecc.​


  3. Si implementa un convert_logits_to_cells() che:

    • filtra le predizioni per oggetti di tipo “table cell / row / column”;

    • converte le bbox in coordinate immagine;

    • per ogni cella croppa il pezzetto di immagine corrispondente e lo passa a un OCR (es. Tesseract) per ottenere il testo;

    • costruisce la lista di dict con parameter (posizione cella), value (testo OCR), altri metadati (data, unità, ecc.).​​


In questo schema, la “parte intelligente” è quindi:Table Transformer → strutture (celle) + OCR → valori di cella → JSON/DataFrame.


Opzione 2: usare il tuo ViT + LoRA come extractor


Se si vuole tenere il ViT + LoRA, bisogna:


  1. Cambiare modello in AutoModel (non solo classification head) e prendi le feature intermedie:


    from transformers import AutoModel, AutoFeatureExtractor base_model = AutoModel.from_pretrained("google/vit-base-patch16-224") extractor = AutoFeatureExtractor.from_pretrained("google/vit-base-patch16-224") lora_model = get_peft_model(base_model, config)


    Così si può usare outputs.last_hidden_state o outputs.pooler_output come embedding.​


  2. Quindi decidere che cosa vuoi predire:

    • caso semplice: “classe” della tabella (es. tipo di parametro in tutto il foglio);

    • caso complesso: coordinate di celle o valori numerici.


  3. Aggiungere una piccola testa sopra le feature ViT:

    • per classificazione: Linear(768, num_labels) e addestri con cross-entropy;

    • per regressione di bounding box: Linear(768, 4) + loss L1/IoU;

    • per valori di cella: o fai OCR + piccola rete che classifica il tipo di cella, oppure tratti la rete come regressore sui valori se l’immagine è sintetica.​


  4. Sostituire convert_logits_to_cells() con:

    • una funzione che prende outputs dal modello (logits o hidden states),

    • applica softmax/sigmoid o inverse scaling,

    • mappa output → struttura tabella (righe/colonne/valori) secondo il formato che ti sei scelto.


Opzione 3: pipeline ibrida molto pratica


Tuttavia, spesso il flusso più robusto è:


  1. Modello di table detection/structure (es. Table Transformer) per avere bbox di celle.​

  2. OCR per leggere il contenuto di ogni cella.

  3. (Opzionale) Il tuo ViT+LoRA sopra le immagini di singole celle o righe per:

    • classificare il tipo di parametro,

    • normalizzare l’unità di misura,

    • stimare la colonna “giusta” se l’OCR è rumoroso.

In questo caso, convert_logits_to_cells() diventerebbe un orchestratore di:

  • chiamata al modello di struttura,

  • loop sulle celle per OCR,

  • eventuale passo ViT+LoRA per arricchire/normalizzare,

  • costruzione finale della lista di dict che finisce nel DataFrame.



 
 
 

Commenti


© 2024 texservice.tech   -  facilitatore informatico  -   mail: texservice13@gmail.com Tel: 353-468-73-15

bottom of page