Llama CPP è un nuovo strumento progettato per eseguire modelli di linguaggio direttamente in C/C++. Questo strumento è ottimizzato per i processori Apple Silicon, grazie all'utilizzo della tecnologia ARM NEON e del framework Accelerate. Offre inoltre compatibilità AVX2 per sistemi basati su architetture x86. Funzionando prevalentemente sulla CPU, Llama CPP integra anche la capacità di quantizzazione a 4 bit, aumentando così la sua efficienza.

Il vantaggio è che permette di eseguire un modello di linguaggio direttamente sul proprio computer personale senza richiedere una GPU con una grande quantità di VRAM. In poche parole, non c'è bisogno di vendere la propria auto per testare gli ultimi modelli trendy. Siate chiari, anche se lo strumento permette di immergersi a capofitto e a costi contenuti nel mondo dei LLM, servirà armarsi di pazienza. Infatti, i tempi di elaborazione possono essere a volte notevoli. Proviamo insieme? Vedrete che non è raro aspettare 5 minuti per ottenere una risposta.

E non importa, l'idea qui è di far girare il sistema sulla propria postazione di lavoro. Capirete perché le stelle dell'hosting vi chiedono qualche centinaio/migliaia di euro per far girare i vostri modelli nel cloud.

Ad oggi, ecco l'elenco dei modelli compatibili:

  • LLaMA 🦙
  • LLaMA 2 🦙🦙
  • Falcon
  • Alpaca
  • GPT4All
  • Chinese LLaMA / Alpaca and Chinese LLaMA-2 / Alpaca-2
  • Vigogne (French)
  • Vicuna
  • Koala
  • OpenBuddy 🐶 (Multilingual)
  • Pygmalion/Metharme
  • WizardLM
  • Baichuan 1 & 2 + derivations
  • Aquila 1 & 2
  • Starcoder models
  • Mistral AI v0.1
  • Refact
  • Persimmon 8B
  • MPT
  • Bloom
  • StableLM-3b-4e1t

Partiamo dal presupposto che siete sviluppatori, e come ogni buon sviluppatore, utilizzate Linux. Non è così? Non importa, utilizzate Docker per simulare una macchina che gira sullo stesso OS di questo articolo, ovvero Ubuntu 22.04. Potete usare, ad esempio, questo docker-compose.yml:

version: '3.8'
services:
  ubuntu:
    image: ubuntu:22.04
    command: /bin/bash
    stdin_open: true
    tty: true
    working_dir: /workspace
Docker compose up
Docker compose exec ubuntu bash

Ci siamo? Proseguiamo. Prerequisiti: installare gli strumenti necessari per eseguire il programma. Avrete bisogno degli strumenti di compilazione, ma anche degli strumenti che vi permettono di clonare i progetti GitHub e di un token che vi dia accesso a Huggingface, il noto deposito dei LLM open source.

apt-get update
apt-get install -y git  cmake g++ make curl gh python3-pip

curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg 

sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg 
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.g
pg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null 


pip install numpy
pip install sentencepiece
pip install -U "huggingface_hub[cli]"

Per creare un token di accesso Hugging Face, ecco il link (può essere un po' complicato da trovare nell'interfaccia): https://huggingface.co/settings/tokens Se non avete ancora un token su GitHub, ecco anche un link: https://github.com/settings/tokens/new

Ora, cloneremo e compileremo llama.cpp.

# Cloner le dépôt Git de llama-cpp
gh repo clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp

# Compiler le projet
mkdir build
cd build
cmake ..
cmake --build . --config Release

Ora, scaricheremo l'ultimo modello preparato da MistralAi.

Potete trovare il modello su Huggingface qui: https://huggingface.co/mistralai Per maggiore chiarezza, potete aggiungere direttamente llama.cpp al vostro ambiente. Questo ci permetterà di concentrarci sui comandi senza inquinare la nostra console con percorsi che non ci porteranno nulla.

Ad esempio:

export PATH="/Data/Projets/Llm/src/Llama-cpp/llama.cpp/build/bin:$PATH"

Download del Modello: sostituite $HUGGINGFACE_TOKEN con il vostro token.

mkdir /Data/Projets/Llm/Models/mistralai 

huggingface-cli login --token $HUGGINGFACE_TOKEN
huggingface-cli download mistralai/Mistral-7B-v0.1 --local-dir /Data/Projets/Llm/Models/mistralai 

Servitevi un caffè, ci sono 15 giga di dati da scaricare. Alcuni file grandi saranno solo link simbolici alla directory cache. Potete specificare --cache-dir per scriverli su un'altra partizione se necessario. Potrebbe essere qualcosa del genere:

huggingface-cli download mistralai/Mistral-7B-v0.1 --local-dir /Data/Projets/Llm/Models/mistralai --cache-dir /Data/Projets/Llm/Models/huggingface_cache/

``` Una volta che abbiamo scaricato i nostri file in formato PyTorch, dobbiamo procedere con una piccola conversione. Ritorniamo nella nostra directory llama.cpp e eseguiamo il seguente comando: ```Basch
python3 llama.cpp/convert.py /Data/Projets/Llm/Models/mistralai 
  --outfile /Data/Projets/Llm/Models/mistralai/Mistral-7B-v0.1.gguf 
  --outtype f16

Che cos'è questo parametro --outtype q8_0?? Si tratta della Quantizzazione (quantization in inglese).

La quantizzazione è un processo che mira a ridurre la dimensione e la complessità dei modelli. Immaginate uno scultore che inizia con un enorme blocco di marmo e lo scolpisce per creare una statua. La quantizzazione è un po' come togliere pezzi dal blocco per renderlo più leggero e più facile da muovere, facendo attenzione a mantenere la bellezza e la forma della statua.

Nel contesto dei LLM, la quantizzazione implica semplificare la rappresentazione dei pesi del modello. I pesi in un LLM sono valori numerici che determinano come il modello reagisce a diverse entrate. Riducendo la precisione di questi pesi (ad esempio, passando da valori in virgola mobile di 32 bit a valori di 8 bit), si riduce la dimensione complessiva del modello e la quantità di calcolo necessaria per eseguirlo.

Ciò ha diversi vantaggi. In primo luogo, rende il modello più veloce e meno energivoro, il che è fondamentale per applicazioni su dispositivi mobili o server a bassa capacità. In secondo luogo, rende l'IA più accessibile, perché può funzionare su un'ampia gamma di dispositivi, compresi quelli senza hardware di calcolo all'avanguardia. Tuttavia, la quantizzazione deve essere gestita con attenzione. Una riduzione eccessiva della precisione può portare a una perdita di performance, in cui il modello diventa meno preciso o meno affidabile. È quindi una questione di trovare il giusto equilibrio tra efficienza e performance.

  1. Quantizzazione a 2 bit (2-Bit Quantization)

    • q2_k: Utilizza Q4_K per i tensori attention.vw e feed_forward.w2, e Q2_K per gli altri tensori. Questo significa che alcuni tensori chiave hanno una precisione leggermente superiore (4 bit) per mantenere la performance, mentre gli altri sono più compressi (2 bit).
  2. Quantizzazione a 3 bit (3-Bit Quantization)

    • q3_k_l: Impiega Q5_K per attention.wv, attention.wo, e feed_forward.w2, e Q3_K per gli altri. Ciò indica una precisione più alta (5 bit) per alcuni tensori importanti, mentre gli altri sono a 3 bit.
    • q3_k_m: Utilizza Q4_K per gli stessi tensori di q3_k_l, ma con una precisione leggermente inferiore (4 bit), e Q3_K per gli altri.
    • q3_k_s: Applica una quantizzazione Q3_K a tutti i tensori, offrendo uniformità ma con una precisione minore.
  3. Quantizzazione a 4 bit (4-Bit Quantization)

    • q4_0: Metodo originale di quantizzazione a 4 bit.
    • q4_1: Offre precisione più alta rispetto a q4_0, ma inferiore a q5_0, con inferenza più veloce rispetto ai modelli q5.
    • q4_k_m: Utilizza Q6_K per metà dei tensori attention.wv e feed_forward.w2, e Q4_K per gli altri.
    • q4_k_s: Applica Q4_K a tutti i tensori.
  4. Quantizzazione a 5 bit (5-Bit Quantization)

    • q5_0: Fornisce precisione più alta ma richiede più risorse e rallenta l'inferenza.
    • q5_1: Offre una precisione ancora più elevata al costo di un uso aumentato delle risorse e di un'inferenza più lenta.
    • q5_k_m: Come q4_k_m, ma con Q6_K per alcuni tensori e Q5_K per gli altri. - q5_k_s: Usa Q5_K per tutti i tensori.
  5. Quantizzazione a 6 bit (6-Bit Quantization)

    • q6_k: Impiega una quantizzazione Q8_K per tutti i tensori, offrendo una precisione relativamente elevata.
  6. Quantizzazione a 8 bit (8-Bit Quantization)

    • q8_0: Questo metodo raggiunge una quantizzazione quasi indistinguibile dal formato float16, ma richiede molte risorse e rallenta l'inferenza.

La precisione di un metodo di quantizzazione dipende quindi dalla quantità di bit utilizzata per rappresentare i pesi del modello.

  1. La più precisa : Il metodo q6_k (Quantizzazione a 6 bit), che utilizza Q8_K per tutti i tensori, sarebbe probabilmente il più preciso nella nostra lista. Utilizza 8 bit per ogni peso, consentendo una rappresentazione più dettagliata e più vicina ai valori originali. Ciò si traduce in una migliore conservazione delle sfumature e dei dettagli del modello, ma al prezzo di un maggiore utilizzo delle risorse e un tempo di inferenza potenzialmente più lungo.

  2. La meno precisa: Il metodo q2_k (Quantizzazione a 2 bit) sarebbe il meno preciso. Questo metodo utilizza solo 2 bit per la maggior parte dei tensori, con alcune eccezioni a 4 bit (Q4_K) per i tensori attention.vw e feed_forward.w2. La rappresentazione a 2 bit è molto compatta, ma è anche molto più suscettibile di perdere informazioni importanti, perché può rappresentare solo quattro valori diversi per peso, rispetto ai 256 valori in una quantizzazione a 8 bit.

Questa è la teoria, il nostro strumento ci offre tre tipi di conversioni: --outtype {f32,f16,q8_0}

  1. f32 (Float 32-bit): È la rappresentazione standard di alta precisione per i numeri in virgola mobile in molti calcoli informatici, inclusi nei LLM. Ogni peso è memorizzato con una precisione di 32 bit, che offre un'alta precisione ma anche un'elevata dimensione del modello e uso delle risorse. Questa opzione non è una forma di quantizzazione ma piuttosto il formato standard senza quantizzazione.

  2. f16 (Float 16-bit): Si tratta di una versione più compatta di f32, dove ogni peso è memorizzato con una precisione di 16 bit. Ciò riduce la dimensione del modello e la quantità di calcolo necessaria, con una leggera perdita di precisione rispetto a f32. Anche se è una forma di compressione, non è tecnicamente quantizzazione nel senso in cui l'abbiamo discussa precedentemente.

  3. q8_0 (8-Bit Quantization): Questa si riferisce direttamente al metodo di quantizzazione a 8 bit menzionato in precedenza. In questo metodo, ogni peso è rappresentato con 8 bit. È la quantizzazione più alta tra le opzioni date e si avvicina quasi alla precisione di f16.

Offre un buon equilibrio tra la riduzione della dimensione del modello e la conservazione della precisione. In sintesi, f32 offre la precisione più alta ma con la dimensione più grande, f16 riduce questa dimensione di metà con una leggera perdita di precisione, e q8_0 riduce ulteriormente la dimensione mantenendo una precisione generalmente sufficiente per molte applicazioni. Queste opzioni riflettono compromessi diversi tra precisione ed efficienza in termini di dimensione e calcolo.

  • f32: vi darà un file di 27 gigabyte. Dovrete assicurarvi di avere abbastanza RAM per questo. Infatti, Llama.cpp caricherà tutto il file in RAM. L'esecuzione è semplicemente fallita sul mio computer.
  • f16: vi darà un file di 14 gigabyte. L'esecuzione di un'istruzione darà questo:
main -m  /Data/Projets/Llm/Models/mistralai/Mistral-7B-v0.1.gguf -i --interactive-first -r "### Human:" --temp 0 -c 2048 -n -1 --ignore-eos --repeat_penalty 1.2 --instruct

Avete la possibilità di usare direttamente un server. Il seguente comando vi permette di installare llama-cpp e un server compatibile con OpenAPI.

pip install llama-cpp-python[server]
python3 -m llama_cpp.server --model  /Data/Projets/Llm/Models/mistralai/Mistral-7B-v0.1.gguf  --host 0.0.0.0 --port 8000

53f749c772a10b3f17be12bcf1389ef2f9a316ce.png 39b0388cde190ffbae906d2e630c74e9e147ff91.png Ora installeremo un'interfaccia client che dialogherà con la nostra API.

gh repo clone https://github.com/mckaywrigley/chatbot-ui.git
cd chatbot-ui/
docker compose up
``` Ho un po' modificato il docker-compose per adattarlo alla mia configurazione. 

```YAML
version: '3.6'

services:
  chatgpt:
    build: .
    ports:
      - 3000:3000
    restart: on-failure
    environment:
      - 'OPENAI_API_KEY=sk-XXXXXXXXXXXXXXXXXXXX'
      - 'OPENAI_API_HOST=http://host.docker.internal:8000'
      - 'DEFAULT_MODEL=/Data/Projets/Llm/Models/mistralai/Mistral-7B-v0.1.gguf'
      - 'NEXT_PUBLIC_DEFAULT_SYSTEM_PROMPT=You are a helpful and friendly AI assistant. Respond very concisely.'
      - 'WAIT_HOSTS=http://host.docker.internal:8000/'
      - 'WAIT_TIMEOUT=${WAIT_TIMEOUT:-3600}'
    extra_hosts:
      - "host.docker.internal:host-gateway"

Il programma non è davvero progettato per interagire direttamente con MistralAi. Non ho necessariamente scelto l'esempio più semplice. Sapete che con i modelli specificati come compatibili sul repository ufficiale, l'integrazione è migliore. Ecco come appare la mia piccola domanda sulla ricetta della maionese attraverso Chat-ui.

Ora vediamo cosa succede aumentando la quantizzazione a 8 bit con il tipo q8_0.

python3 llama.cpp/convert.py /Data/Projets/Llm/Models/mistralai  --outfile /Data/Projets/Llm/Models/mistralai/Mistral-7B-v0.1_q8.gguf  --outtype q8_0


main -m  /Data/Projets/Llm/Models/mistralai/Mistral-7B-v0.1_q8.gguf -i --interactive-first -r "### Human:" --temp 0 -c 2048 -n -1 --ignore-eos --repeat_penalty 1.2 --instruct

Come potete vedere, è decisamente più veloce! E assolutamente utilizzabile. Questo conclude questa breve presentazione di uno stack auto-ospitato. Come vedete, con pochissimi mezzi, funziona. Immaginate con dell'hardware progettato per! Siate consapevoli che MistralAi è un'azienda francese. Oltre all'uso su un computer portatile economico, sappiate che l'intero pacchetto di strumenti aziendali è disponibile per questo modello.