Unisci modelli linguistici di grandi dimensioni con mergekit |  di Maxime Labonne |  Gennaio 2024

 | Intelligenza-Artificiale

Crea facilmente i tuoi modelli, non è necessaria alcuna GPU!

Immagine dell’autore

La fusione dei modelli è una tecnica che combina due o più LLM in un unico modello. È un metodo relativamente nuovo e sperimentale per creare nuovi modelli a basso costo (non è richiesta alcuna GPU). La fusione dei modelli funziona sorprendentemente bene e ha prodotto molti modelli all’avanguardia su Apri la classifica LLM.

In questo tutorial lo implementeremo utilizzando il file mergekit biblioteca di Charles Goddard. Più specificamente, esamineremo quattro metodi di unione e forniremo esempi di configurazioni. Quindi, utilizzeremo mergekit per creare il nostro modello, Marcoro14–7B-slerpche è diventato il modello con le migliori prestazioni nella classifica Open LLM (02/01/23).

Il codice è disponibile su GitHub E Google Co. Consiglio di utilizzare il mio notebook automatizzato per eseguire facilmente mergekit: 🥱 LazyMergekit.

Immagine dell’autore

In questa sezione ci concentreremo su quattro metodi attualmente implementati in mergekit. Tieni presente che esistono altri metodi, ad esempio lineare E Aritmetica del compito. Se sei interessato a documenti sulla fusione dei modelli, lo consiglio questa eccellente raccolta sul Volto che abbraccia.

1. SLRP

Interpolazione lineare sferica (SLERP) è un metodo utilizzato per eseguire un’interpolazione uniforme tra due vettori. Mantiene un tasso di cambiamento costante e preserva le proprietà geometriche dello spazio sferico in cui risiedono i vettori.

Esistono diversi motivi per preferire SLERP rispetto a un’interpolazione lineare tradizionale. Ad esempio, negli spazi ad alta dimensione, l’interpolazione lineare può portare a a diminuzione della grandezza del vettore interpolato (cioè riduce la scala dei pesi). Inoltre, spesso rappresenta il cambiamento di direzione dei pesi informazioni più significative (come l’apprendimento e la rappresentazione delle caratteristiche) rispetto all’entità del cambiamento.

SLERP viene implementato utilizzando i seguenti passaggi:

  1. Normalizza i vettori di input alla lunghezza unitaria, assicurando che rappresentino direzioni anziché grandezze
  2. Calcola l’angolo tra questi vettori utilizzando il loro prodotto scalare.
  3. Se i vettori sono quasi collineari, per efficienza viene utilizzata per impostazione predefinita l’interpolazione lineare. Altrimenti, SLERP calcola i fattori di scala in base al fattore di interpolazione t (t=0 = 100% del primo vettore, t=1 = 100% del modello 2) e l’angolo tra i vettori.
  4. Questi fattori vengono utilizzati per pesare i vettori originali, che vengono poi sommati per ottenere il vettore interpolato.

SLERP è attualmente il metodo di fusione più popolare, ma si limita a combinare solo due modelli alla volta. È ancora possibile combinare gerarchicamente più modelli, come mostrato in Mistral-7B-Merge-14-v0.1.

Esempio di configurazione:

slices:
- sources:
- model: OpenPipe/mistral-ft-optimized-1218
layer_range: (0, 32)
- model: mlabonne/NeuralHermes-2.5-Mistral-7B
layer_range: (0, 32)
merge_method: slerp
base_model: OpenPipe/mistral-ft-optimized-1218
parameters:
t:
- filter: self_attn
value: (0, 0.5, 0.3, 0.7, 1)
- filter: mlp
value: (1, 0.5, 0.7, 0.3, 0)
- value: 0.5
dtype: bfloat16

Questa è una classica configurazione SLERP, applicata a ogni livello di entrambi i modelli. Si noti che immettiamo un gradiente di valori per il fattore di interpolazione t. I parametri per gli strati di autoattenzione e MLP utilizzeranno diverse combinazioni di OpenPipe/mistral-ft-ottimizzato-1218 E mlabonne/NeuralHermes-2.5-Mistral-7B. Gli altri strati sono una miscela 50/50 dei due modelli.

Puoi trovare il modello finale su Hugging Face Hub all’indirizzo mlabonne/NeuralPipe-7B-slerp.

2. LEGAMI

Introdotto nel questo articolo di Yadav et al., TIES-Unione è progettato per unire in modo efficiente più modelli specifici per attività in un unico modello multitasking. Affronta due sfide principali nella fusione dei modelli:

  • Ridondanza nei parametri del modello: Identifica ed elimina i parametri ridondanti all’interno di modelli specifici per attività. Ciò si ottiene concentrandosi sulle modifiche apportate durante la messa a punto, identificando le modifiche più significative del primo k% e scartando il resto.
  • Disaccordo tra i segni dei parametri: I conflitti sorgono quando diversi modelli suggeriscono aggiustamenti opposti allo stesso parametro. TIES-Merging risolve questi conflitti creando un vettore di segni unificato che rappresenta la direzione di cambiamento più dominante in tutti i modelli.

TIES-Merging si articola nei seguenti tre passaggi:

  1. Ordinare: Riduce la ridondanza nei modelli specifici per attività conservando solo una frazione dei parametri più significativi (parametro di densità) e reimpostando il resto a zero.
  2. Eleggere il segno: risolve i conflitti di segno tra diversi modelli creando un vettore di segno unificato basato sulla direzione più dominante (positiva o negativa) in termini di grandezza cumulativa.
  3. Unione disgiunta: calcola la media dei valori dei parametri che si allineano con il vettore del segno unificato, esclusi i valori zero.

A differenza di SLERP, TIES può unire più modelli contemporaneamente.

Esempio di configurazione:

models:
- model: mistralai/Mistral-7B-v0.1
# no parameters necessary for base model
- model: OpenPipe/mistral-ft-optimized-1218
parameters:
density: 0.5
weight: 0.5
- model: mlabonne/NeuralHermes-2.5-Mistral-7B
parameters:
density: 0.5
weight: 0.3
merge_method: ties
base_model: mistralai/Mistral-7B-v0.1
parameters:
normalize: true
dtype: float16

Con questa configurazione, utilizziamo Mistral-7B come modello base per calcolare i pesi delta. Uniamo gli stessi due modelli: mistral-ft-ottimizzato-1218 (50%) e NeuralHermes-2.5-Mistral-7B (30%) con normalizzazione. In questo caso, la densità significa che manteniamo solo il 50% dei parametri di ciascun modello (l’altra metà proviene dal modello base).

Si noti che la somma dei pesi non è uguale a 1. Ciò significa che ridurremo la scala dei pesi finali (non consigliato in realtà!). Questa configurazione si ispira ai parametri forniti dall’autore di OpenHermes-2.5-neural-chat-7b-v3–1–7B.

Puoi trovare il modello finale su Hugging Face Hub all’indirizzo mlabonne/NeuralPipe-7B-fascette.

3. OSARE

Introdotto da Yu et al. (2023), OSARE utilizza un approccio simile a TIES con due differenze principali:

  • Potatura: OSARE ripristinare casualmente i pesi ottimizzati ai loro valori originali (quelli del modello base).
  • Ridimensionamento: DARE ridimensiona i pesi per mantenere le aspettative dei risultati del modello approssimativamente invariati. Aggiunge i pesi riscalati di entrambi (o più) modelli ai pesi del modello base con un fattore di scala.

L’implementazione di questo metodo da parte di Mergekit ha due versioni: con la fase di elezione del segno di TIES (dare_ties) o senza (dare_linear).

Esempio di configurazione:

models:
- model: mistralai/Mistral-7B-v0.1
# No parameters necessary for base model
- model: samir-fama/SamirGPT-v1
parameters:
density: 0.53
weight: 0.4
- model: abacusai/Slerp-CM-mist-dpo
parameters:
density: 0.53
weight: 0.3
- model: EmbeddedLLM/Mistral-7B-Merge-14-v0.2
parameters:
density: 0.53
weight: 0.3
merge_method: dare_ties
base_model: mistralai/Mistral-7B-v0.1
parameters:
int8_mask: true
dtype: bfloat16

In questa configurazione, uniamo tre diversi modelli basati sull’utilizzo di Mistral-7B dare_ties. Questa volta ho scelto pesi la cui somma è 1, che sembra funzionare meglio. Il parametro di densità è leggermente superiore a quello consigliato, ma sembra che abbia funzionato bene per questo modello.

Puoi trovarlo su Hugging Face Hub all’indirizzo mlabonne/Daredevil-7B. È anche il miglior modello di fusione in questo articolo, superando anche Marcoro14–7B-slerp.

4. Passaggio

Il metodo passthrough differisce notevolmente dai precedenti. Concatenando strati di diversi LLM, può produrre modelli con un file numero esotico di parametri (ad esempio, 9B con due modelli di parametri 7B). Questi modelli vengono spesso definiti dalla comunità come “frankenmerges” o “modelli Frankenstein”.

Questa tecnica è molto sperimentale, ma è riuscita a creare modelli impressionanti, come goliath-120b utilizzando due modelli Llama 2 70B. Quello recentemente rilasciato SOLARE-10.7B-v1.0 utilizza anche la stessa idea, chiamata ridimensionamento in profondità nel loro giornale.

Esempio di configurazione:

slices:
- sources:
- model: OpenPipe/mistral-ft-optimized-1218
layer_range: (0, 32)
- sources:
- model: mlabonne/NeuralHermes-2.5-Mistral-7B
layer_range: (24, 32)
merge_method: passthrough
dtype: bfloat16

Il frankenmerge risultante avrà tutti i 32 strati del primo modello e 8 strati aggiuntivi del secondo modello. Questo crea un frankenmerge con un totale di 40 strati e parametri 8.99B. Questa configurazione è ispirata a GML-Mistral-fusa-v1.

Puoi trovare il modello finale su Hugging Face Hub all’indirizzo fusione di mlabonne/NeuralPipe-9B.

In questa sezione utilizzeremo mergekit per caricare una configurazione di unione, eseguirla e caricare il modello risultante su Hugging Face Hub.

Prima di tutto installiamo mergekit direttamente dai sorgenti come segue:

!git clone https://github.com/cg123/mergekit.git
!cd mergekit && pip install -q -e .

Nel blocco successivo carichiamo la configurazione di fusione in formato YAML. Specifichiamo inoltre il nome del modello unito per uso futuro. Puoi copiare/incollare qualsiasi configurazione dalla sezione precedente qui.

Questa volta utilizzeremo due modelli diversi: Marcoroni-7B-v3 E Mistral-7B-Merge-14-v0.1 e unirli con il metodo SLERP. Salviamo la configurazione come file yaml da utilizzare come input nel comando merge.

import yaml

MODEL_NAME = "Marcoro14-7B-slerp"
yaml_config = """
slices:
- sources:
- model: AIDC-ai-business/Marcoroni-7B-v3
layer_range: (0, 32)
- model: EmbeddedLLM/Mistral-7B-Merge-14-v0.1
layer_range: (0, 32)
merge_method: slerp
base_model: AIDC-ai-business/Marcoroni-7B-v3
parameters:
t:
- filter: self_attn
value: (0, 0.5, 0.3, 0.7, 1)
- filter: mlp
value: (1, 0.5, 0.7, 0.3, 0)
- value: 0.5
dtype: bfloat16

"""

# Save config as yaml file
with open('config.yaml', 'w', encoding="utf-8") as f:
f.write(yaml_config)

Eseguiamo il comando merge con i seguenti parametri:

  • --copy-tokenizer per copiare il tokenizzatore dal modello base
  • --allow-crimes E --out-shard-size per suddividere i modelli in frammenti più piccoli che possono essere calcolati su una CPU con poca RAM
  • --lazy-unpickle per abilitare il pigro deselezionatore sperimentale per un minore utilizzo della memoria

Inoltre, alcuni modelli possono richiedere il --trust_remote_code bandiera (questo non è il caso di Mistral-7B).

Questo comando scaricherà i pesi di tutti i modelli elencati nella configurazione di unione ed eseguirà il metodo di unione selezionato (dovrebbero essere necessari circa 10 minuti).

# Merge models
!mergekit-yaml config.yaml merge --copy-tokenizer --allow-crimes --out-shard-size 1B --lazy-unpickl

Il modello è ora unito e salvato nella directory “merge”. Prima di caricarlo possiamo creare un file README con tutte le informazioni necessarie per la riproducibilità. Il seguente blocco di codice definisce un modello Jinja e lo riempie automaticamente con i dati dalla configurazione di unione.

!pip install -qU huggingface_hub

from huggingface_hub import ModelCard, ModelCardData
from jinja2 import Template

username = "mlabonne"

template_text = """
---
license: apache-2.0
tags:
- merge
- mergekit
- lazymergekit
{%- for model in models %}
- {{ model }}
{%- endfor %}
---

# {{ model_name }}

{{ model_name }} is a merge of the following models using (mergekit)(https://github.com/cg123/mergekit):

{%- for model in models %}
* ({{ model }})(https://huggingface.co/{{ model }})
{%- endfor %}

## 🧩 Configuration

```yaml
{{- yaml_config -}}
```
"""

# Create a Jinja template object
jinja_template = Template(template_text.strip())

# Get list of models from config
data = yaml.safe_load(yaml_config)
if "models" in data:
models = (data("models")(i)("model") for i in range(len(data("models"))) if "parameters" in data("models")(i))
elif "parameters" in data:
models = (data("slices")(0)("sources")(i)("model") for i in range(len(data("slices")(0)("sources"))))
elif "slices" in data:
models = (data("slices")(i)("sources")(0)("model") for i in range(len(data("slices"))))
else:
raise Exception("No models or slices found in yaml config")

# Fill the template
content = jinja_template.render(
model_name=MODEL_NAME,
models=models,
yaml_config=yaml_config,
username=username,
)

# Save the model card
card = ModelCard(content)
card.save('merge/README.md')

Ora che abbiamo una scheda modello, possiamo inviare l’intera cartella all’Hub.

from google.colab import userdata
from huggingface_hub import HfApi

username = "mlabonne"

# Defined in the secrets tab in Google Colab
api = HfApi(token=userdata.get("HF_TOKEN"))

api.create_repo(
repo_id=f"{username}/{MODEL_NAME}",
repo_type="model"
)
api.upload_folder(
repo_id=f"{username}/{MODEL_NAME}",
folder_path="merge",
)

Il modello è ora disponibile su Hugging Face Hub all’indirizzo mlabonne/Marcoro14–7B-slerp. Su un altro notebook possiamo provare il modello con precisione a 4 bit su una GPU T4 libera utilizzando il seguente codice:

!pip install -qU transformers accelerate

from transformers import AutoTokenizer
import transformers
import torch

model = "mlabonne/Marcoro14-7B-slerp"
messages = ({"role": "user", "content": "What is a large language model?"})

tokenizer = AutoTokenizer.from_pretrained(model)
prompt = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
pipeline = transformers.pipeline(
"text-generation",
model=model,
torch_dtype=torch.float16,
device_map="auto",
)

outputs = pipeline(prompt, max_new_tokens=256, do_sample=True, temperature=0.7, top_k=50, top_p=0.95)

Ci stiamo ponendo la domanda “Che cos’è un modello linguistico di grandi dimensioni?” e ho ricevuto questo output:

Un modello linguistico di grandi dimensioni è un tipo di sistema di intelligenza artificiale (AI) addestrato su grandi quantità di dati di testo. È progettato per comprendere e generare un linguaggio simile a quello umano, facendo previsioni su quali parole o frasi potrebbero essere successive in una frase o in un documento. Questi modelli utilizzano algoritmi complessi e architetture di reti neurali per apprendere dai dati e migliorare le proprie prestazioni nel tempo. Alcuni noti modelli linguistici di grandi dimensioni includono GPT-3 di OpenAI e BERT di Google.

Sembra buono, ma abbiamo bisogno di una valutazione più completa. Per questo tipo di modello generico, ci sono alcuni parametri di riferimento interessanti:

  • Chatbot Arenache compila una classifica LLM basata su Elo basata sui voti umani.
  • Panca MT (stesso collegamento), che utilizza GPT-4 come giudice per valutare le risposte del modello su una serie di domande a più turni.
  • Suite di benchmark NousResearchche aggrega quattro benchmark: AGIEval, GPT4ALL, TruthfulQA e Bigbench. GPT4ALL stesso include HellaSwag, OpenBookQA, Winogrande, ARC-Easy, ARC-Challenge, BoolQ e PIQA.
  • Apri la classifica LLMche aggrega sei benchmark: ARC, HellaSwag, MMLU, Winogrande, GSM8K e TruthfulQA.

Sfortunatamente, non possiamo inviare il nostro modello alla Chatbot Arena. Invece, ho scelto di valutarlo utilizzando la classifica Open LLM e i benchmark NousResearch.

Ho inviato il nostro modello al Apri la classifica LLM (scheda “🚀 Invia qui!”). Come mostrato nell’introduzione, è classificato come il miglior modello con parametri 7B nella classifica. Ecco i risultati completi:

Immagine dell’autore

Il problema con la classifica Open LLM è che questi benchmark sono pubblici. Significa che le persone possono formare LLM sui dati dei test per ottenere risultati migliori. Unendo i migliori modelli, contaminiamo anche i nostri stessi risultati. È lecito presumerlo Marcoro14–7B-slerp è contaminato e alcuni modelli utilizzati in questa fusione sono stati addestrati sul set di test. Se vuoi creare il modello migliore e non compromettere la classifica, ti consiglio di utilizzare solo modelli non unificabili per creare le tue unioni.

Questo è il motivo per cui non vogliamo fare affidamento solo sulla classifica OpenLLM. Per la suite di benchmark NousResearch, ho usato 🧐Valutazione automatica LLM per calcolare i punteggi automaticamente con un semplice notebook Colab. Ecco i risultati rispetto all’ottimo OpenHermes-2.5-Mistral-7B:

Immagine dell’autore

Otteniamo un miglioramento significativo rispetto a questo modello ogni punto di riferimento. Tieni presente che la suite di benchmark NousResearch condivide alcune attività con la classifica Open LLM: ARC-Challenge, TruthfulQA, HellaSwag e Winogrande. Per quanto ne so, Bigbench è l’unico benchmark diverso al 100% (non esitate a contattarmi se non è così). Tuttavia, uno dei modelli utilizzati in questa fusione potrebbe comunque essere stato addestrato su Bigbench.

In questo articolo, abbiamo introdotto il concetto di unire LLM con quattro metodi diversi. Abbiamo dettagliato il funzionamento di SLERP, TIES, DARE e passthrough e fornito esempi di configurazioni. Infine, abbiamo eseguito SLERP con mergekit per creare Marcoro14–7B-slerp e caricalo su Hugging Face Hub. Abbiamo ottenuto ottime performance su due suite di benchmark: Open LLM Leaderboard (modello 7B più performante) e NousResearch. Se vuoi creare le tue fusioni, ti consiglio di utilizzare il mio taccuino automatizzato 🥱 LazyMergekit.

Un altro modo per combinare più modelli è unirli in un’architettura Mixture of Experts (MoE). Nel prossimo articolo discuteremo come farlo in dettaglio e creare il nostro file proprio modello simile a Mixtral. Se ti è piaciuto questo articolo, seguimi su Medium e Twitter @mlabonne.

Fonte: towardsdatascience.com

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *