Connect with us

Le cadre d’inférence de Microsoft amène les modèles de langage à grande échelle 1-bit sur les appareils locaux

Intelligence artificielle

Le cadre d’inférence de Microsoft amène les modèles de langage à grande échelle 1-bit sur les appareils locaux

mm
Understanding 1-bit LLMs and Microsoft's BitNet.cpp Framework

Le 17 octobre 2024, Microsoft a annoncé BitNet.cpp, un cadre d’inférence conçu pour exécuter des modèles de langage à grande échelle (LLM) quantifiés à 1 bit. BitNet.cpp constitue une avancée significative dans le domaine de l’IA générative, permettant le déploiement efficace de LLM 1 bit sur des CPU standard, sans nécessiter de GPU coûteux. Ce développement démocratise l’accès aux LLM, les rendant disponibles sur une large gamme d’appareils et offrant de nouvelles possibilités pour les applications d’IA sur appareil.

Comprendre les modèles de langage à grande échelle 1-bit

Les modèles de langage à grande échelle (LLM) nécessitaient traditionnellement des ressources computationnelles importantes en raison de l’utilisation de nombres à virgule flottante à haute précision (généralement FP16 ou BF16) pour les poids du modèle. Cette nécessité a rendu le déploiement des LLM coûteux et gourmand en énergie.

À leur core, les LLM 1 bit utilisent des techniques de quantification extrêmes pour représenter les poids du modèle à l’aide de trois valeurs possibles seulement : -1, 0 et 1, d’où le terme « 1,58 bit » (car il faut légèrement plus d’un bit pour encoder trois états).

Système de poids ternaire

Le concept

La quantification à 1 bit dans BitNet.cpp est un système de poids ternaire. BitNet fonctionne avec seulement trois valeurs possibles pour chaque paramètre :

  • -1 (négatif)
  • 0 (neutre)
  • 1 (positif)

Cela entraîne une exigence de stockage d’environ 1,58 bit par paramètre, d’où le nom BitNet b1.58. Cette réduction drastique de la largeur de bit des paramètres conduit à une réduction impressionnante de l’utilisation de la mémoire et de la complexité computationnelle, car la plupart des multiplications à virgule flottante sont remplacées par des additions et des soustractions simples.

Fondement mathématique

La quantification à 1 bit implique la transformation des poids et des activations en leur représentation ternaire à l’aide des étapes suivantes :

1. Binarisation des poids

La binarisation des poids implique de les recentrer autour de la moyenne (α), aboutissant à une représentation ternaire. La transformation est exprimée mathématiquement comme :

Wf=Sign(Wα)

Où :

  • W est la matrice de poids d’origine.
  • α est la moyenne des poids.
  • Sign(x) renvoie +1 si x > 0 et -1 sinon.

2. Quantification des activations

La quantification des activations garantit que les entrées sont limitées à une largeur de bit spécifiée :

Où :

  • Qb = 2(b−1)2^{(b-1)} est le niveau de quantification maximum pour une largeur de bit b.
  • γ est la valeur absolue maximale de x (notée ∣∣x∣∣∞).
  • ε est un petit nombre pour éviter les débordements pendant les calculs.

3. Opération BitLinear

La couche BitLinear remplace les multiplications matricielles traditionnelles par une opération simplifiée :

y=Wf×x^e×(Qbβγ)

Où :

  • β est un facteur d’échelle utilisé pour minimiser les erreurs d’approximation.
  • γ met à l’échelle les activations.
  • Q_b est le facteur de quantification.

Cette transformation permet des calculs efficaces tout en préservant les performances du modèle.

Implications de performance

Efficacité de la mémoire

Le système de poids ternaire réduit considérablement les exigences de mémoire :

  • LLM traditionnels : 16 bits par poids
  • BitNet.cpp : 1,58 bit par poids

Cette réduction se traduit par une économie de mémoire d’environ 90% par rapport aux modèles traditionnels à 16 bits, permettant ainsi à des modèles plus importants de tenir dans les mêmes contraintes matérielles.

Efficacité énergétique

Vitesse d’inférence, efficacité énergétique (Apple M2)

 

Vitesse d'inférence : plus rapide sur les deux CPU

Vitesse d’inférence, efficacité énergétique (i7-13700H)

1. Vitesse d’inférence : plus rapide sur les deux CPU

La vitesse d’inférence est représentée par le nombre de jetons traités par seconde. Voici un résumé des observations :

  • Sur Apple M2 Ultra : BitNet.cpp atteint une accélération allant jusqu’à 5,07x pour les modèles plus importants (30B) par rapport à Llama.cpp, avec un pic de vitesse de 593,43 jetons par seconde pour un modèle de 125M, ce qui constitue une accélération de 1,37x. Pour les modèles plus importants comme le 3,8B et le 7B, BitNet.cpp maintient une vitesse supérieure à 84,77 jetons par seconde, montrant son efficacité à toutes les échelles.
  • Sur Intel i7-13700H : BitNet.cpp atteint des améliorations de vitesse encore plus spectaculaires. À la taille de modèle de 7B, BitNet.cpp offre une accélération incroyable de 5,68x par rapport à Llama.cpp. Pour les modèles plus petits comme le 125M, il traite 389,08 jetons par seconde, ce qui est 2,37x plus rapide que Llama.cpp.

2. Efficacité énergétique : un facteur de changement pour les appareils périphériques

Les graphiques fournis comprennent également des comparaisons de coût énergétique, montrant une réduction significative de la consommation d’énergie par jeton traité :

  • Sur Apple M2 Ultra : Les économies d’énergie de BitNet.cpp sont substantielles. Pour le modèle de 700M, il consomme 55,4% moins d’énergie par jeton par rapport à Llama.cpp, passant de 0,314 à 0,140. Cette tendance se poursuit pour les modèles plus importants, le modèle de 70B montrant une réduction de 70,0% de la consommation d’énergie.
  • Sur Intel i7-13700H : BitNet.cpp offre 71,9% d’économie d’énergie pour le modèle de 700M, avec une consommation passant de 1,367 à 0,384. Bien que les données énergétiques pour le modèle de 70B dans Llama.cpp soient indisponibles, BitNet.cpp reste efficace, avec une consommation d’énergie à 17,33 pour le modèle de 70B.

3. Franchir le seuil de vitesse de lecture humaine

L’une des observations les plus intéressantes de ces graphiques est la référence à la vitesse de lecture humaine, indiquée à 5-7 jetons par seconde. Cette ligne rouge montre que les deux implémentations, en particulier BitNet.cpp, peuvent dépasser facilement les vitesses de lecture humaines même pour les modèles les plus importants :

  • Sur Apple M2 Ultra, BitNet.cpp dépasse la vitesse de lecture humaine pour toutes les tailles de modèles, la vitesse la plus basse étant de 8,67 jetons par seconde pour un modèle de 70B.
  • Sur Intel i7-13700H, le modèle de 100B atteint 1,70 jeton par seconde, touchant presque la plage inférieure de la vitesse de lecture humaine, tandis que tous les modèles plus petits dépassent ce seuil.

Considérations de formation

Estimateur de passage direct (STE)

Puisque la quantification à 1 bit introduit des fonctions non différentiables, la formation implique une technique spécialisée appelée Estimateur de passage direct (STE). Dans cette approche, les gradients s’écoulent sans altération à travers les points non différentiables. Voici une mise en œuvre simplifiée en Python :

class StraightThroughEstimator(Function):
@staticmethod
def forward(ctx, input):
return input.sign()

@staticmethod
def backward(ctx, grad_output):
return grad_output

Formation à précision mixte

Pour maintenir la stabilité pendant la formation, la précision mixte est employée :

  • Poids et activations : Quantifiés à une précision de 1 bit.
  • Gradients et états de l’optimiseur : Stockés dans une précision plus élevée.
  • Poids latents : Maintenus dans une précision élevée pour faciliter les mises à jour précises pendant la formation.

Stratégie de taux d’apprentissage élevé

Un défi unique avec les modèles à 1 bit est que de petites mises à jour peuvent ne pas affecter les poids binarisés. Pour atténuer cela, le taux d’apprentissage est augmenté, garantissant ainsi une convergence plus rapide et de meilleures performances d’optimisation par rapport aux approches traditionnelles.

Quantification et normalisation de groupe

BitNet.cpp introduit la quantification et la normalisation de groupe pour améliorer le parallélisme du modèle. Au lieu de calculer les paramètres pour l’ensemble de la matrice de poids, BitNet divise les poids et les activations en plusieurs groupes (G).

Cette regroupation permet un traitement parallèle efficace sans communication intergroupe supplémentaire, permettant ainsi la formation et l’inférence de modèles à grande échelle.

Notes et optimisations d’implémentation

Optimisation CPU

BitNet.cpp exploite plusieurs optimisations de bas niveau pour atteindre les performances CPU maximales :

  • Opérations vectorisées : Utilise les instructions SIMD pour effectuer des manipulations de bits de manière efficace.
  • Accès à la mémoire adapté au cache : Structure les données pour minimiser les erreurs de cache.
  • Traitement parallèle : Répartit la charge de travail de manière efficace sur plusieurs cœurs CPU.

Voici un exemple d’une fonction clé qui met en œuvre la quantification et l’inférence dans BitNet :

def bitlinear_forward(input, weight, scale):
# Quantifie l'entrée à l'aide de la quantification absmax
input_q = quantize(input)

# Effectue une multiplication matricielle binaire
output = binary_matmul(input_q, weight)

# Met à l'échelle la sortie pour correspondre à la précision d'origine
return output * scale

def quantize(x):
# Effectue une quantification absmax
scale = torch.max(torch.abs(x))
return torch.clamp(x / scale, -1, 1) * scale

Modèles pris en charge

La version actuelle de BitNet.cpp prend en charge les modèles de langage à grande échelle 1 bit suivants disponibles sur Hugging Face :

  • bitnet_b1_58-large (0,7 milliard de paramètres)
  • bitnet_b1_58-3B (3,3 milliards de paramètres)
  • Llama3-8B-1.58-100B-tokens (8,0 milliards de paramètres)

Ces modèles sont disponibles publiquement pour démontrer les capacités d’inférence du cadre. Bien qu’ils n’aient pas été formés ou publiés officiellement par Microsoft, ils illustrent la polyvalence du cadre.

Guide d’installation

Pour commencer avec BitNet.cpp, suivez les étapes ci-dessous :

Prérequis

  1. Python >= 3.9
  2. CMake >= 3.22
  3. Clang >= 18
  4. Conda (hautement recommandé)

Pour les utilisateurs Windows, Visual Studio doit être installé avec les composants suivants activés :

  • Développement de bureau avec C++
  • Outils C++-CMake pour Windows
  • Git pour Windows
  • Compilateur C++-Clang pour Windows
  • Prise en charge de MS-Build pour l’ensemble d’outils LLVM (Clang)

Pour les utilisateurs Debian/Ubuntu, un script d’installation automatique est disponible :

bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"

Étapes d’installation détaillées

  1. Cloner le référentiel :
    git clone --recursive https://github.com/microsoft/BitNet.git

    cd BitNet
  2. Installer les dépendances :
    # Créer un nouvel environnement Conda (recommandé)
    conda create -n bitnet-cpp python=3.9
    conda activate bitnet-cpp


    pip install -r requirements.txt
  3. Construire et préparer le projet : Vous pouvez télécharger directement un modèle depuis Hugging Face et le convertir en format quantifié :
    python setup_env.py --hf-repo HF1BitLLM/Llama3-8B-1.58-100B-tokens -q i2_s

    Alternativement, vous pouvez télécharger et convertir manuellement le modèle :

    huggingface-cli download HF1BitLLM/Llama3-8B-1.58-100B-tokens --local-dir models/Llama3-8B-1.58-100B-tokens

    python setup_env.py -md models/Llama3-8B-1.58-100B-tokens -q i2_s

Exécution de l’inférence avec BitNet.cpp

Pour exécuter l’inférence en utilisant le cadre, utilisez la commande suivante :

python run_inference.py -m models/Llama3-8B-1.58-100B-tokens/ggml-model-i2_s.gguf -p "Sandra a voyagé jusqu'à la cuisine. Où est Sandra ?" -n 6 -temp 0.7

Explication :

  • -m spécifie le chemin du fichier du modèle.
  • -p définit le texte de l’invite.
  • -n définit le nombre de jetons à prédire.
  • -temp ajuste l’aléatoire de l’échantillonnage (température) pendant l’inférence.

Exemple de sortie

Sandra a voyagé jusqu'à la cuisine. Où est Sandra ?

Réponse : Sandra est dans la cuisine.

Détails techniques de BitNet.cpp

Couche BitLinear

BitNet.cpp met en œuvre une architecture de transformateur modifiée, remplaçant les multiplications matricielles standard par des opérations BitLinear. Cette approche recentre les poids sur zéro avant la quantification et les met à l’échelle pour réduire les erreurs d’approximation. La fonction de transformation clé ressemble à ceci :


# Fonction de binarisation pour les poids à 1 bit
def binarize_weights(W):
alpha = W.mean()
W_binarized = np.sign(W - alpha)
return W_binarized

La combinaison de poids recentrés et de mise à l’échelle garantit que l’erreur de quantification reste minime, préservant ainsi les performances.

Impact sur l’industrie

BitNet.cpp pourrait avoir des implications considérables pour le déploiement des modèles de langage à grande échelle :

  • Accessibilité : Permet aux modèles de langage à grande échelle de fonctionner sur des appareils standard, démocratisant ainsi l’accès à un puissant IA.
  • Rentabilité : Réduit le besoin de GPU coûteux, abaissant ainsi la barrière à l’adoption.
  • Efficacité énergétique : Économise de l’énergie en exploitant l’inférence basée sur le CPU.
  • Innovation : Ouvre de nouvelles possibilités pour les applications d’IA sur appareil, comme la traduction en temps réel, les assistants vocaux et les applications axées sur la confidentialité sans dépendance du cloud.

Défis et orientations futures

Bien que les modèles de langage à grande échelle 1 bit montrent des promesses, plusieurs défis restent. Ceux-ci incluent le développement de modèles robustes à 1 bit pour diverses tâches, l’optimisation du matériel pour le calcul à 1 bit et l’encouragement des développeurs à adopter ce nouveau paradigme. De plus, l’exploration de la quantification à 1 bit pour les tâches de vision par ordinateur ou audio représente une direction passionnante pour l’avenir.

Conclusion

Le lancement de BitNet.cpp par Microsoft constitue une avancée significative. En permettant une inférence efficace à 1 bit sur les CPU standard, BitNet.cpp crée l’accessibilité et la durabilité de l’IA. Ce cadre ouvre la voie à des modèles de langage à grande échelle plus portables et plus rentables, repoussant les limites de ce qui est possible avec l’IA sur appareil.

J'ai passé les cinq dernières années à me plonger dans le monde fascinant de l'apprentissage automatique et de l'apprentissage profond. Ma passion et mon expertise m'ont conduit à contribuer à plus de 50 projets de génie logiciel divers, avec un accent particulier sur l'IA/ML. Ma curiosité permanente m'a également attiré vers le traitement automatique des langues, un domaine que je suis impatient d'explorer plus en détail.