LogoPierre Lepagnol, PhD

Notes on NLP, ML and applied data science

Back to all articles

Cross-LoRA - Partie 1 : la théorie

Transférer un adaptateur LoRA d'un modèle à un autre, sans données et sans ré-entraînement.

·21 min read
Au sommaire

Les modèles de base évoluent vite. Lorsque vous avez investi du temps et des données pour entraîner un LoRA sur un modèle, vous voulez idéalement pouvoir le réutiliser sur un autre modèle, sans avoir à ré-ajuster le LoRA lui-même.

C'est tout l'objet du papier Cross-LoRA: A Data-Free LoRA Transfer Framework across Heterogeneous LLMs.

TL;DR - Cross-LoRA prend un adaptateur LoRA (As,Bs)(\As, \Bs) entraîné sur un modèle source, fait une SVD tronquée des poids des deux modèles, aligne les bases par moindres carrés, puis projette As\As et Bs\Bs côté droit et côté gauche pour obtenir un adaptateur (At,Bt)(\At, \Bt) directement attachable au modèle cible. Aucune donnée et aucun gradient n'est requis, seulement quelques minutes GPU par couple de modèles.

Illustration de Cross-LoRA
Illustration de Cross-LoRA

Cette série est découpée en deux articles :

  1. Partie 1 - la théorie (cet article) : pourquoi le transfert zero-data est possible, et comment il fonctionne sur le papier.
  2. Partie 2 - l'implémentation : comment passer de l'algorithme à un adaptateur PEFT qui se charge réellement dans le modèle cible.

Dans cet article, nous apprendrons:

  1. Pourquoi un adpatateur LoRA n'est pas portable par défaut,
  2. Qu'est ce que Cross-LoRA:
  3. comment ça fonctionne: le contenu exact d'Algorithm 1 du papier
  4. Quels hypothèses à la méthode et comment les vérifier
Vue d'ensemble de Cross-LoRA
Vue d'ensemble de Cross-LoRA

Table des notations

Dans le reste des articles nous utiliserons les notations suivantes:

SymboleSensExemple typique
Ws\Ws, Wt\Wtpoids d'une même couche dans le modèle source / ciblematrice m×nm \times n
ms,nsm_s, n_sdimensions de sortie / entrée côté source2048×20482048 \times 2048
mt,ntm_t, n_tdimensions de sortie / entrée côté cible3072×30723072 \times 3072
ΔW\dWmise à jour LoRA (ΔW=BA\dW = B A)rang rLoRA\rlora
rLoRA\rlorarang LoRA - typiquement petit88, 1616, 3232
AA, BBfacteurs LoRA, ARrLoRA×nA \in \R^{\rlora \times n}, BRm×rLoRAB \in \R^{m \times \rlora}rLoRAmin(m,n)\rlora \ll \min(m, n)
rSVD\rsvdrang de troncature de la SVD - rien à voir avec rLoRA\rlora160160, 320320
U,Σ,VU, \Sigma, VSVD tronquée de WW : WUΣVTW \approx U \Sigma V^TURm×rSVDU \in \R^{m \times \rsvd}
PU,PV\PU, \PVmatrices d'alignement des bases source vers cibleobtenues par lstsq\operatorname{lstsq}
U~s,V~s\widetilde{U}_s, \widetilde{V}_sbases source ré-exprimées dans les dimensions cibleU~s=PUUs\widetilde{U}_s = \PU \Us

Convention : on écrit BTB^T pour la transposée. F\lVert \cdot \rVert_F pour la norme de Frobenius, et argmin\operatorname*{argmin} pour l'opérateur de minimiseur d'un problème de moindres carrés.

LoRA : Rappel et Problème

Le principe

LoRA part d'une observation empirique : quand on fine-tune un grand modèle, la mise à jour ΔW\dW qu'on applique aux poids WW est de rang effectif faible. Autrement dit, on n'a pas besoin d'une matrice pleine pour capturer ce que le fine-tuning veut exprimer - une matrice de rang rLoRA\rlora suffit, avec rLoRAmin(m,n)\rlora \ll \min(m, n).

LoRA exploite ça en figeant WW et en n'apprenant que deux petites matrices :

ΔW=BAavecARrLoRA×n,BRm×rLoRA\dW = B A \quad \text{avec} \quad A \in \R^{\rlora \times n}, \quad B \in \R^{m \times \rlora}

À l'inférence, la couche calcule :

h=Wx+αrLoRAB(Ax)h = W x + \frac{\alpha}{\rlora} B(Ax)
Flux d'une couche LoRA
Flux d'une couche LoRA

Trois conséquences utiles pour la suite

  1. ΔW\dW vit dans un sous-espace de dimension rLoRA\rlora. Côté entrée, son image est contrainte par les lignes de AA ; côté sortie, par les colonnes de BB. C'est exactement la structure que Cross-LoRA va exploiter.
  2. Convention gauche / droite. BB agit « à gauche » (espace de sortie de la couche) et AA agit « à droite » (espace d'entrée). Cette dissymétrie n'est pas cosmétique : la projection Cross-LoRA traite AA et BB séparément, parce qu'ils vivent dans deux espaces différents du modèle.
  3. WW est figé. C'est ce qui rend l'idée du transfert plausible : WW n'a pas été tordu par le fine-tuning, ses sous-espaces dominants restent ceux du modèle de base - et donc comparables entre source et cible.

C'est ce dernier point qui ouvre la porte à Cross-LoRA : si Ws\Ws et Wt\Wt partagent une géométrie suffisamment proche dans leurs sous-espaces dominants, alors un ΔWs\dW_s exprimé dans cette géométrie devrait pouvoir être réexprimé dans celle de Wt\Wt.

Notre problème : les LoRA ne sont pas transférables par nature

Un adaptateur LoRA est dépendant du modèle sur lequel il a été entraîné.

Il a été ajusté contre un modèle de base précis : un jeu de poids WW, des dimensions cachées spécifiques, une organisation de couches donnée. La paire (A,B)(A, B) que vous obtenez à la fin du fine-tuning ne décrit pas une « compétence » portable - elle décrit une correction à appliquer à ce modèle de base, et à aucun autre.

Or les modèles de base bougent vite. Une nouvelle version de Qwen sort, LLaMA passe à une variante 3.2, Gemma publie une nouvelle famille. Chaque fois, les LoRA accumulés deviennent inutilisables tels quels.

Les options classiques sont toutes coûteuses :

  • ré-entraîner le LoRA sur le nouveau modèle - il faut redisposer des données, qui ont parfois disparu, été interdites, ou simplement été utilisées sous licence expirée ;
  • distiller - cela suppose des données de référence et un budget de calcul ;
  • maintenir N LoRA en parallèle pour N modèles - l'effort est multiplié.

Cross-LoRA propose un troisième chemin : transférer un LoRA vers un modèle hétérogène, sans données et sans nouvelle phase d'entraînement. Le seul matériau utilisé, ce sont les poids des deux modèles de base et l'adaptateur source.

Pour clarifier ce que ça n'est pas :

  • ce n'est pas une distillation : aucun modèle ne génère de réponses à imiter ;
  • ce n'est pas du model merging : on ne fusionne pas deux modèles, on déplace un delta d'un modèle à l'autre ;
  • ce n'est pas du fine-tuning : il n'y a pas de gradient, pas d'optimiseur, pas de fonction de perte.

C'est, littéralement, un changement de repère algébrique appliqué à ΔW\dW.

L'intuition Cross-LoRA en une image

Voilà le raisonnement, en trois temps.

Premier temps - exposer la géométrie. Une matrice de poids WW se décompose en SVD : W=UΣVTW = U \Sigma V^T. Les colonnes de UU forment une base orthonormée de l'espace de sortie ; celles de VV, une base orthonormée de l'espace d'entrée ; Σ\Sigma dit quelles directions portent l'essentiel du signal. En tronquant à rSVD\rsvd directions, on obtient une description compacte de la géométrie dominante de la couche.

Deuxième temps - aligner. On fait la SVD du modèle source et du modèle cible. On obtient (Us,Vs)(\Us, \Vs) et (Ut,Vt)(\Ut, \Vt). Ces bases ne sont pas alignées : même si elles décrivent des couches qui font « la même chose » sémantiquement, leurs axes ne pointent pas dans le même sens. On cherche donc une transformation PU\PU qui amène Us\Us « le plus près possible » de Ut\Ut, et de même pour VV. C'est un problème de moindres carrés.

Troisième temps - projeter. Une fois les bases alignées, on dispose d'un dictionnaire qui dit comment exprimer une direction de l'espace source dans l'espace cible. On l'applique à ΔWs\dW_s pour obtenir ΔWt\dW_t.

Schéma directeur : SVD \rightarrow alignement \rightarrow projection.

SVD, alignement, projection
SVD, alignement, projection

Le reste de l'article remplit les détails de chaque étape.

Brique 1 - LoRA-Align

LoRA-Align répond à une seule question : comment exprimer le repère du modèle source dans le repère du modèle cible ?

SVD tronquée

Pour chaque couche concernée, on calcule une SVD tronquée à l'ordre rSVD\rsvd :

WsUsΣsVsTavecUsRms×rSVD,VsRns×rSVD\Ws \approx \Us \Sigma_s \Vs^T \quad \text{avec} \quad \Us \in \R^{m_s \times \rsvd}, \quad \Vs \in \R^{n_s \times \rsvd} WtUtΣtVtTavecUtRmt×rSVD,VtRnt×rSVD\Wt \approx \Ut \Sigma_t \Vt^T \quad \text{avec} \quad \Ut \in \R^{m_t \times \rsvd}, \quad \Vt \in \R^{n_t \times \rsvd}

Pourquoi tronquer ?

  • Coût : une SVD pleine sur une matrice de plusieurs milliers de lignes par plusieurs milliers de colonnes est lente et inutile - on ne se sert que des premières directions.
  • Bruit : les directions de très petites valeurs singulières capturent surtout du bruit numérique. Les jeter améliore l'alignement plus qu'il ne le dégrade.
  • Énergie retenue : rSVD=320\rsvd = 320 suffit en pratique à conserver l'essentiel du signal des couches d'attention et MLP des modèles 1.5B–3B testés dans le papier.

Encadré - piège de notation Ce rSVD\rsvd n'a rien à voir avec rLoRA\rlora. Le papier utilise le même symbole rr pour les deux et c'est trompeur. Dans toute la série, on les garde séparés : rLoRA\rlora désigne le rang LoRA (typiquement 8–32), rSVD\rsvd désigne le rang de troncature de la SVD (typiquement 80–320).

Le problème d'alignement

Us\Us et Ut\Ut sont deux bases orthonormées du même type d'espace (sortie de la couche), mais « tournées » différemment, et de surcroît dans des dimensions différentes (msm_s vs mtm_t). On cherche la transformation PU\PU qui les recolle :

PU=argminPPUsUtF2avecPRmt×ms\PU = \operatorname*{argmin}_P \lVert P\Us - \Ut \rVert_F^2 \quad \text{avec} \quad P \in \R^{m_t \times m_s} PV=argminPPVsVtF2avecPRnt×ns\PV = \operatorname*{argmin}_P \lVert P\Vs - \Vt \rVert_F^2 \quad \text{avec} \quad P \in \R^{n_t \times n_s}

C'est un problème classique de moindres carrés. Le papier utilise torch.linalg.lstsq, qui le résout par décomposition QR ou SVD selon le solveur.

Remarque - pourquoi pas Procrustes ? On pourrait être tenté de chercher PP orthogonal et résoudre un problème de Procrustes orthogonal. Cross-LoRA ne le fait pas : il accepte des PP non orthogonaux, ce qui laisse de la flexibilité quand msmtm_s \ne m_t ou nsntn_s \ne n_t - précisément le cas hétérogène qui motive la méthode.

Bases alignées

À la sortie de cette étape, on dispose de :

U~s=PUUsRmt×rSVD\widetilde{U}_s = \PU \Us \in \R^{m_t \times \rsvd} V~s=PVVsRnt×rSVD\widetilde{V}_s = \PV \Vs \in \R^{n_t \times \rsvd}

U~s\widetilde{U}_s est une base du sous-espace dominant source, mais ré-exprimée dans les dimensions du modèle cible. C'est exactement le dictionnaire dont on avait besoin.

Construction de la base alignée
Construction de la base alignée

Brique 2 - LoRA-Shift

LoRA-Align nous a donné les bases alignées. LoRA-Shift les utilise pour déplacer la mise à jour LoRA elle-même.

Le papier propose une équation globale qui projette ΔWs\dW_s « des deux côtés à la fois ». En pratique, dès qu'il y a un mismatch dimensionnel entre source et cible, cette équation devient pénible à manipuler. L'Algorithm 1 du papier est plus propre et c'est lui qu'il faut suivre : il sépare le traitement des poids BB (côté gauche) et AA (côté droit).

Pourquoi séparer gauche et droite

Rappel : dans un module LoRA, BB agit dans l'espace de sortie de la couche, AA dans l'espace d'entrée. Ces deux espaces n'ont aucune raison d'avoir la même dimension - et entre familles hétérogènes, ils ne l'ont en général pas. Forcer une projection symétrique mélangerait deux problèmes d'alignement indépendants. Mieux vaut les traiter séparément, chacun avec sa propre base.

Les deux projections

BtU~s(U~sTBs)projection coˆteˊ gauche, pour les poids B\Bt \leftarrow \widetilde{U}_s(\widetilde{U}_s^T\Bs) \quad \text{projection côté gauche, pour les poids } B At(AsV~s)V~sTprojection coˆteˊ droit, pour les poids A\At \leftarrow (\As \widetilde{V}_s)\widetilde{V}_s^T \quad \text{projection côté droit, pour les poids } A

Lecture intuitive :

  • côté BB, on comprime Bs\Bs dans le sous-espace dominant source via U~sTBs\widetilde{U}_s^T\Bs, puis on le réexprime dans les coordonnées cibles via U~s\widetilde{U}_s. La composition agit comme un projecteur U~sU~sT\widetilde{U}_s\widetilde{U}_s^T, mais exprimé dans le repère cible ;
  • côté AA, c'est la même mécanique appliquée à droite avec V~s\widetilde{V}_s.

Encadré - vérification de dimensions

  • BsRms×rLoRA\Bs \in \R^{m_s \times \rlora} \rightarrow U~sTBsRrSVD×rLoRA\widetilde{U}_s^T\Bs \in \R^{\rsvd \times \rlora} \rightarrow U~s(U~sTBs)Rmt×rLoRA\widetilde{U}_s(\widetilde{U}_s^T\Bs) \in \R^{m_t \times \rlora}
  • AsRrLoRA×ns\As \in \R^{\rlora \times n_s} \rightarrow AsV~sRrLoRA×rSVD\As\widetilde{V}_s \in \R^{\rlora \times \rsvd} \rightarrow (AsV~s)V~sTRrLoRA×nt(\As\widetilde{V}_s)\widetilde{V}_s^T \in \R^{\rlora \times n_t}

On retrouve bien des facteurs (At,Bt)(\At, \Bt) aux dimensions du modèle cible et toujours de rang rLoRA\le \rlora.

Projections gauche et droite
Projections gauche et droite

Ce qu'on obtient

À la fin, on a une nouvelle paire (At,Bt)(A_t, B_t) qui :

  • a les bonnes dimensions pour s'attacher au modèle cible ;
  • conserve la structure rang-rLoRA\rlora de l'adaptateur original (la projection ne l'augmente pas) ;
  • vit dans la géométrie dominante du modèle cible, telle que vue à travers l'alignement avec celle de la source.

C'est tout ce qu'on a besoin de sérialiser comme adaptateur PEFT cible.

Pourquoi ça marche (et quand ça casse)

Cross-LoRA repose sur une hypothèse forte mais raisonnable : les sous-espaces dominants des poids WW de deux LLM modernes pré-entraînés sur des corpus comparables capturent des structures linguistiques voisines. Pas identiques - voisines. Assez voisines pour qu'une transformation linéaire PP les recolle.

Cette hypothèse n'est pas gratuite. Plusieurs travaux antérieurs sur la représentation des LLM montrent que les couches d'attention et de feed-forward, malgré des initialisations et des familles différentes, convergent vers des sous-espaces fonctionnellement proches. Cross-LoRA en tire les conséquences pratiques.

Mais l'hypothèse a des limites prévisibles :

  • Familles trop éloignées : entre une famille decoder-only récente et un modèle d'architecture significativement différente, la géométrie partagée s'effrite.
  • Profondeurs différentes : si la source a 24 couches et la cible 32, le matching par index de couche est forcément approximatif. Le papier reste discret sur ce cas.
  • Modules absents ou renommés : entre LLaMA et Gemma, les noms et le découpage des blocs MLP diffèrent. Tout module non matché est un trou dans le transfert.
  • Petits rangs LoRA, grands rSVD\rsvd : si rLoRA\rlora est très petit, l'information utile tient dans très peu de directions. Les directions choisies par la SVD ne sont pas garanties d'être les bonnes.

Vis-à-vis des alternatives :

  • l'interpolation naïve (copier les poids LoRA tels quels en zéro-paddant) fonctionne uniquement quand les dimensions coïncident, et ignore complètement la rotation entre repères ;
  • le ré-entraînement reste la baseline imbattable en qualité, mais coûte des données et du calcul - exactement ce que Cross-LoRA cherche à éviter.

Cross-LoRA se positionne donc comme un compromis : moins fidèle qu'un ré-entraînement, beaucoup plus solide qu'une copie naïve, et utilisable quand on n'a plus accès aux données.

Qualité du transfert vs divergence architecturale
Qualité du transfert vs divergence architecturale

Une hypothèse à l'épreuve des faits

L'argument central - « deux LLM modernes partagent une géométrie dominante » - n'est pas un coup de chance. Plusieurs lignes de travaux antérieurs convergent :

  • Représentations linéairement comparables : on sait depuis un bon moment que des modèles entraînés indépendamment sur des corpus comparables apprennent des représentations qu'une transformation linéaire suffit souvent à recoller. C'est précisément la classe de transformations que lstsq\operatorname{lstsq} produit.
  • Sous-espaces dominants stables : sur les couches d'attention et de feed-forward des LLM modernes, l'essentiel de l'« énergie » des poids tient dans une fraction des directions singulières - typiquement quelques centaines sur des matrices de plusieurs milliers de lignes. Cross-LoRA fait l'observation duale côté WW que LoRA fait côté ΔW\dW.
  • Hypothèse de bas rang du fine-tuning : LoRA lui-même repose sur l'idée que ΔW\dW est de faible rang. Cross-LoRA y ajoute que la base de ce sous-espace peut elle-même être réexprimée d'un modèle à l'autre, par projection sur la géométrie dominante.

Conclusion opérationnelle : si l'on observe un transfert qui casse, ce n'est en général pas l'algèbre qui est en cause, mais l'une des conditions précédentes qui n'est pas satisfaite (familles trop éloignées, modules sans correspondance, profondeurs très différentes).

Un mini exemple chiffré

Reprenons le cas concret Qwen2.5-1.5B \rightarrow LLaMA-3.2-3B qui sert de fil rouge à la Partie 2. Pour une couche d'attention q_proj typique :

QuantitéSource (Qwen 1.5B)Cible (LLaMA 3.2 3B)
Nb couches2828
mm15363072
nn15363072
rLoRA\rlora1616
rSVD\rsvd320320
UU1536×3201536 \times 3203072×3203072 \times 320
VV1536×3201536 \times 3203072×3203072 \times 320
PU\PU3072×15363072 \times 1536-
U~s\widetilde{U}_s3072×3203072 \times 320-
Bs\Bs1536×161536 \times 16-
Bt\Bt3072×163072 \times 16-
As\As16×153616 \times 1536-
At\At16×307216 \times 3072-

Pour ce couple précis, la recolle dimensionnelle est asymétrique (msmtm_s \ne m_t) ce qui justifie pleinement le recours à des PU\PU, PV\PV non orthogonaux : un Procrustes orthogonal ne peut tout simplement pas opérer entre R1536\R^{1536} et R3072\R^{3072}.

FAQ

Pourquoi pas de Procrustes orthogonal partout ?: Parce qu'il impose PP carrée orthogonale, ce qui exige ms=mtm_s = m_t. Cross-LoRA est explicitement conçu pour le cas hétérogène - donc lstsq\operatorname{lstsq} non contraint.

Que se passe-t-il si rSVD>min(m,n)\rsvd > \min(m, n) ? La SVD ne renvoie que min(m,n)\min(m, n) directions ; au-delà on ne gagne rien. En pratique on prend rSVD=min(rSVD,demandeˊ,min(m,n))\rsvd = \min(r_{\mathrm{SVD, demandé}}, \min(m, n)).

L'opération est-elle déterministe ? Oui à un signe près : la SVD a une ambiguïté de signe par direction singulière. Comme U~sU~sT\widetilde{U}_s\widetilde{U}_s^T est insensible à ce signe (il intervient deux fois), la projection finale est bien déterministe.

Et si l'adaptateur source utilise du rank-stabilized LoRA ou du DoRA ? Le squelette de l'algorithme reste valable, mais il faut adapter le facteur d'échelle (α/r\alpha / r vs α/r\alpha / \sqrt{r}) et, pour DoRA, traiter en plus la composante de magnitude. Cross-LoRA tel qu'écrit cible le LoRA standard.

Ce que dit la pratique

Le papier teste Cross-LoRA sur quatre modèles et quatre benchmarks de raisonnement en QCM :

  • modèles de base : LLaMA-3.2-3B, Qwen2.5-1.5B, Qwen2.5-3B, Gemma-2-2B ;
  • benchmarks : ARC-C, ARC-E, OpenBookQA, HellaSwag.

La recette est la suivante : on entraîne un LoRA sur le modèle source, puis on applique Cross-LoRA pour produire un adaptateur attaché au modèle cible, et on évalue ce dernier sur les benchmarks - sans aucun ré-entraînement entre les deux modèles.

Trois points de lecture sont essentiels :

  1. La bonne baseline n'est pas le LoRA original, qui vit sur le modèle source et ne peut pas tourner sur la cible. Les comparaisons utiles sont :
    • le modèle cible nu (sans aucun adaptateur) ;
    • une interpolation naïve des poids LoRA quand les dimensions le permettent.
  2. Cross-LoRA n'égalera pas un fine-tuning natif sur la cible - ce n'est pas son objectif. Il vise à préserver une part du gain du fine-tuning source.
  3. L'écart base-vs-transfert dépend fortement du couple source/cible. Les paires intra-famille (Qwen\rightarrowQwen) transfèrent mieux que les pairesinter-famille (Qwen\rightarrowLLaMA, LLaMA\rightarrowGemma). C'est cohérent avec l'hypothèse desous-espaces partagés.

L'article 2 reproduira Qwen2.5-1.5B \rightarrow LLaMA-3.2-3B comme cas concret.

Conclusion

Récapitulons ce qu'on a posé :

  • les LoRA ne sont pas portables par nature, mais leur structure de bas rang offre une prise ;
  • les poids WW de deux LLM modernes partagent une géométrie dominante qu'on peut exposer via SVD tronquée ;
  • on aligne les bases par moindres carrés (LoRA-Align) puis on projette ΔW\dW côté gauche et côté droit séparément (LoRA-Shift, Algorithm 1) ;
  • l'opération est zero-data, zero-training, et coûte essentiellement quelques SVD plus une résolution de lstsq\operatorname{lstsq} par couche.

Mais entre cette algèbre et un checkpoint PEFT qui se recharge réellement dans le modèle cible, il reste un fossé. Les vraies questions sont d'ordre pratique :

  • comment matcher les modules q_proj, k_proj, v_proj, … entre LLaMA, Qwen et Gemma quand les noms et les conventions diffèrent ?
  • comment gérer les couples de modèles à profondeurs différentes ?
  • comment cacher les SVD pour ne pas les recalculer pour chaque module AA et BB ?
  • comment valider qu'un transfert n'est pas en silence cassé ?

C'est tout l'objet de la Partie 2 - l'implémentation.