El 17 de octubre de 2024, Microsoft anunció BitNet.cpp, un marco de inferencia diseñado para ejecutar modelos de lenguaje grande (LLM) cuantificados de 1 bit. BitNet.cpp es un progreso significativo en Gen AI, ya que permite la implementación eficiente de LLM de 1 bit en CPU estándar, sin requerir GPU costosas. Este desarrollo democratiza el acceso a los LLM, haciéndolos disponibles en una amplia gama de dispositivos y brindando nuevas posibilidades en aplicaciones de IA en los dispositivos.
Comprensión de los modelos de lenguaje grande de 1 bit
Los modelos de lenguajes grandes (LLM) tradicionalmente han requerido importantes recursos computacionales debido al uso de números de punto flotante de alta precisión (normalmente FP16 o BF16) para los pesos de los modelos. Esta necesidad ha hecho que la implementación de LLM sea costosa y consuma mucha energía.
En esencia, los LLM de 1 bit utilizan técnicas de cuantificación extremas para representar los pesos del modelo utilizando solo tres valores posibles: -1, 0 y 1, de ahí el término “1,58 bits” (ya que requiere un poco más de un bit para codificar tres estados).
Sistema de peso ternario
El concepto
La cuantificación de 1 bit en BitNet.cpp es un sistema de ponderación ternario. BitNet opera con sólo tres valores posibles para cada parámetro:
- -1 (negativo)
- 0 (neutral)
- 1 (positivo)
Esto da como resultado un requisito de almacenamiento de alrededor de 1,58 bits por parámetro, de ahí el nombre BitNet b1.58. Esta drástica reducción en el ancho de bits de los parámetros conduce a una reducción impresionante en el uso de memoria y la complejidad computacional, ya que la mayoría de las multiplicaciones de punto flotante se reemplazan con sumas y restas simples.
Fundamento Matemático
La cuantificación de 1 bit implica transformar pesos y activaciones en su representación ternaria mediante los siguientes pasos:
1. Binarización de peso
Binarizar los pesos implica centralizarlos alrededor de la media (α
), resultando en una representación ternaria. La transformación se expresa matemáticamente como:
W.F=Firmar(W.−a)
Dónde:
- W. es la matriz de peso original.
- a es la media de los pesos.
- Signo(x) regresa +1 si x > 0 y -1 de lo contrario.
2. Cuantización de activación
Las activaciones de cuantificación garantizan que las entradas estén restringidas a un ancho de bits específico:
Dónde:
- qb = 2(b-1)2^{(b-1)} es el nivel máximo de cuantificación para b-ancho de bits.
- do es el valor absoluto máximo de incógnita (denotado como ∣∣x∣∣∞).
- mi es un número pequeño para evitar el desbordamiento durante los cálculos.
3. Operación bitlineal
La capa BitLinear reemplaza las multiplicaciones de matrices tradicionales con una operación simplificada:
y=W.F×incógnita^mi×(qbbdo)
Dónde:
- b es un factor de escala utilizado para minimizar los errores de aproximación.
- do escala las activaciones.
- Q_b es el factor de cuantificación.
Esta transformación permite cálculos eficientes al tiempo que preserva el rendimiento del modelo.
Implicaciones de rendimiento
Eficiencia de la memoria
El sistema de peso ternario reduce significativamente los requisitos de memoria:
- LLM tradicionales: 16 bits por peso
- BitNet.cpp: 1,58 bits por peso
Esta reducción se traduce en un ahorro de memoria de aproximadamente 90% en comparación con los modelos tradicionales de 16 bits, lo que permite que los modelos más grandes se ajusten a las mismas limitaciones de hardware.
1. Velocidad de inferencia: más rápida en ambas CPU
Velocidad de inferencia se representa como el número de tokens procesados por segundo. Aquí hay un desglose de las observaciones:
- En Apple M2 Ultra: BitNet.cpp alcanza hasta 5.07x aceleración para modelos más grandes (30B) en comparación con Llama.cpp, con una velocidad máxima de 593,43 fichas por segundo para un modelo 125M, que es un 1,37x aceleración. Para modelos más grandes como el 3.8B y el 7B, BitNet.cpp mantiene una velocidad superior a 84,77 tokens por segundo, lo que demuestra su eficiencia en todas las escalas.
- En Intel i7-13700H: BitNet.cpp logra mejoras de velocidad aún más espectaculares. En el tamaño del modelo 7B, BitNet.cpp ofrece una increíble aceleración de 5.68x comparado con Llama.cpp. Para modelos más pequeños como el 125M, procesa 389,08 tokens por segundoque es 2,37x más rápido que Llama.cpp.
2. Eficiencia energética: un punto de inflexión para los dispositivos perimetrales
Los gráficos proporcionados también incluyen comparaciones de costos de energíaque muestra una reducción significativa en el consumo de energía por token procesado:
- En Apple M2 Ultra: Los ahorros de energía de BitNet.cpp son sustanciales. Para el modelo 700M, consume 55,4% menos energía por token en comparación con Llama.cpp, pasando de 0,314 a 0,140. Esta tendencia continúa para los modelos más grandes, con el modelo 70B mostrando un Reducción del 70,0% en el consumo de energía.
- En Intel i7-13700H: BitNet.cpp ofrece 71,9% de ahorro de energía para el modelo 700M, con un consumo que baja de 1.367 a 0.384. Aunque los datos de energía para el modelo 70B en Llama.cpp no están disponibles, BitNet.cpp sigue siendo eficiente, con un consumo de energía de 17.33 para el modelo 70B.
3. Cruzar el punto de referencia de velocidad de lectura humana
Una de las ideas más interesantes de estos gráficos es la referencia a velocidad de lectura humanamarcado en 5-7 fichas por segundo. Esta línea roja muestra que ambas implementaciones, especialmente BitNet.cpp, pueden superar cómodamente las velocidades de lectura humana incluso para los modelos más grandes:
- En Apple M2 UltraBitNet.cpp supera la velocidad de lectura humana para todos los tamaños de modelo, siendo la velocidad más baja 8,67 fichas por segundo para un modelo 70B.
- En Intel i7-13700Hel modelo 100B aún logra 1,70 fichas por segundocasi tocando el rango más bajo de velocidad de lectura humana, mientras que todos los modelos más pequeños superan este punto de referencia.
Consideraciones de capacitación
Estimador directo (STE)
Dado que la cuantificación de 1 bit introduce funciones no diferenciables, el entrenamiento implica una técnica especializada conocida como Estimador directo (STE). En este enfoque, los gradientes fluyen inalterados a través de puntos no diferenciables. Aquí hay una implementación simplificada en Python:
class StraightThroughEstimator(Function): @staticmethod def forward(ctx, input): return input.sign() @staticmethod def backward(ctx, grad_output): return grad_output
Entrenamiento de precisión mixto
Para mantener la estabilidad durante el entrenamiento, precisión mixta se emplea:
- Pesos y Activaciones: Cuantizado con una precisión de 1 bit.
- Gradientes y estados del optimizador: Almacenado con mayor precisión.
- Pesos latentes: Se mantiene con alta precisión para facilitar actualizaciones precisas durante el entrenamiento.
Estrategia de gran tasa de aprendizaje
Un desafío único con los modelos de 1 bit es que las pequeñas actualizaciones pueden no afectar los pesos binarios. Para mitigar esto, se aumenta la tasa de aprendizaje, lo que garantiza una convergencia más rápida y una mejor optimización en comparación con los enfoques tradicionales.
Cuantización y normalización de grupos
BitNet.cpp presenta Cuantización y normalización de grupos para mejorar el paralelismo del modelo. En lugar de calcular parámetros para toda la matriz de pesos, BitNet divide los pesos y las activaciones en múltiples grupos (G
).
Esta agrupación permite un procesamiento paralelo eficiente sin comunicación adicional entre grupos, lo que permite la inferencia y el entrenamiento de modelos a gran escala.
Notas de implementación y optimizaciones
Optimización de la CPU
BitNet.cpp aprovecha varias optimizaciones de bajo nivel para lograr el máximo rendimiento de la CPU:
- Operaciones vectorizadas: Utiliza instrucciones SIMD para realizar manipulaciones de bits de manera eficiente.
- Acceso a memoria compatible con caché: estructura los datos para minimizar los errores de caché.
- Procesamiento paralelo: Distribuye la carga de trabajo entre múltiples núcleos de CPU de manera efectiva.
A continuación se muestra un ejemplo de una función clave que implementa la cuantificación y la inferencia en BitNet:
Modelos compatibles
La versión actual de BitNet.cpp admite los siguientes LLM de 1 bit disponibles en Hugging Face:
- bitnet_b1_58-grande (0,7 mil millones de parámetros)
- bitnet_b1_58-3B (3.3B parámetros)
- Llama3-8B-1.58-100B-tokens (8.0B parámetros)
Estos modelos están disponibles públicamente para demostrar las capacidades de inferencia del marco. Aunque Microsoft no los ha entrenado ni publicado oficialmente, ilustran la versatilidad del marco.
Guía de instalación
Para comenzar con BitNet.cpp, siga los pasos a continuación:
Requisitos previos
- Pitón >= 3.9
- Chacer >= 3,22
- Sonido metálico >= 18
- conda (muy recomendable)
Para ventanas Para los usuarios, Visual Studio debe instalarse con los siguientes componentes habilitados:
- Desarrollo de escritorio con C++
- Herramientas C++-CMake para Windows
- Git para Windows
- Compilador C++-Clang para Windows
- Soporte de MS-Build para el conjunto de herramientas LLVM (Clang)
Para Debian/Ubuntu usuarios, hay disponible un script de instalación automática:
Instalación paso a paso
- Clonar el repositorio:
- Instalar dependencias:
- Construir y preparar el proyecto: Puedes descargar un modelo directamente desde Hugging Face y convertirlo a un formato cuantificado:
Alternativamente, descargue y convierta manualmente el modelo:
Ejecutando inferencia con BitNet.cpp
Para ejecutar la inferencia usando el marco, use el siguiente comando:
Explicación:
-m
especifica la ruta del archivo del modelo.-p
define el texto del mensaje.-n
establece el número de tokens a predecir.-temp
ajusta la aleatoriedad del muestreo (temperatura) durante la inferencia.
Ejemplo de salida
Detalles técnicos de BitNet.cpp
Capa lineal de bits
BitNet.cpp implementa una arquitectura Transformer modificada, sustituyendo las multiplicaciones de matrices estándar por BitLinear
operaciones. Este enfoque centraliza los pesos a cero antes de la cuantificación y los escala para reducir los errores de aproximación. La función de transformación clave se ve así:
# Binarization function for 1-bit weights def binarize_weights(W): alpha = W.mean() W_binarized = np.sign(W - alpha) return W_binarized
La combinación de pesos centralizados y escalamiento garantiza que el error de cuantificación sea mínimo, preservando así el rendimiento.
Impacto de la industria
BitNet.cpp podría tener implicaciones de gran alcance para la implementación de LLM:
- Accesibilidad: Permite que los LLM se ejecuten en dispositivos estándar, democratizando el acceso a una potente IA.
- Rentabilidad: Reduce la necesidad de GPU costosas, lo que reduce la barrera para la adopción.
- Eficiencia Energética: Ahorra energía aprovechando la inferencia estándar basada en CPU.
- Innovación: Abre nuevas posibilidades para la IA en el dispositivo, como traducción de idiomas en tiempo real, asistentes de voz y aplicaciones centradas en la privacidad sin dependencias de la nube.
Desafíos y direcciones futuras
Si bien los LLM de 1 bit son prometedores, persisten varios desafíos. Estos incluyen el desarrollo de modelos robustos de 1 bit para diversas tareas, la optimización del hardware para la computación de 1 bit y alentar a los desarrolladores a adoptar este nuevo paradigma. Además, explorar la cuantificación de 1 bit para tareas de audio o visión por computadora representa una dirección futura emocionante.
Conclusión
El lanzamiento de BitNet.cpp por parte de Microsoft es un avance significativo. Al permitir una inferencia eficiente de 1 bit en CPU estándar, BitNet.cpp crea la accesibilidad y la sostenibilidad de la IA. Este marco prepara el escenario para LLM más portátiles y rentables, impulsando lo que es posible con la IA en el dispositivo.