Connect with us

Marco de inferencia de Microsoft lleva modelos de lenguaje grande de 1 bit a dispositivos locales

Inteligencia artificial

Marco de inferencia de Microsoft lleva modelos de lenguaje grande de 1 bit a dispositivos locales

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

El 17 de octubre de 2024, Microsoft anunció BitNet.cpp, un marco de inferencia diseñado para ejecutar modelos de lenguaje grande (LLM) cuantizados de 1 bit. BitNet.cpp es un progreso significativo en Gen AI, que permite la implementación eficiente de LLM de 1 bit en CPUs estándar, sin requerir GPUs 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 inteligencia artificial en dispositivos.

Entendiendo los modelos de lenguaje grande de 1 bit

Los modelos de lenguaje grande (LLM) tradicionalmente han requerido recursos computacionales significativos debido a su uso de números de punto flotante de alta precisión (típicamente FP16 o BF16) para los pesos del modelo. Esta necesidad ha hecho que la implementación de LLM sea costosa y consume mucha energía.

En su núcleo, los LLM de 1 bit utilizan técnicas de cuantizació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 ligeramente más de un bit para codificar tres estados).

Sistema de pesos ternarios

El concepto

La cuantización de 1 bit en BitNet.cpp es un sistema de pesos ternarios. BitNet opera con solo 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 reducción drástica 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 simples sumas y restas.

Fundamento matemático

La cuantización de 1 bit implica transformar los pesos y las activaciones en su representación ternaria a través de los siguientes pasos:

1. Cuantización de pesos

La cuantización de los pesos implica centralizarlos alrededor de la media (α), lo que da como resultado una representación ternaria. La transformación se expresa matemáticamente como:

Wf=Sign(Wα)

Donde:

  • W es la matriz de pesos original.
  • α es la media de los pesos.
  • Sign(x) devuelve +1 si x > 0 y -1 de lo contrario.

2. Cuantización de activaciones

La cuantización de las activaciones garantiza que las entradas estén limitadas a un ancho de bits especificado:

Donde:

  • Qb = 2(b−1)2^{(b-1)} es el nivel de cuantización máximo para el ancho de bits b.
  • γ es el valor absoluto máximo de x (denotado como ∣∣x∣∣∞).
  • ε es un número pequeño para prevenir desbordamientos durante los cálculos.

3. Operación BitLinear

La capa BitLinear reemplaza las multiplicaciones de matrices tradicionales con una operación simplificada:

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

Donde:

  • β es un factor de escala utilizado para minimizar errores de aproximación.
  • γ escala las activaciones.
  • Q_b es el factor de cuantización.

Esta transformación permite cálculos eficientes mientras se mantiene el rendimiento del modelo.

Implicaciones de rendimiento

Eficiencia de memoria

El sistema de pesos ternarios 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 quepan dentro de las mismas restricciones de hardware.

Eficiencia energética

Velocidad de inferencia, eficiencia energética (Apple M2)

 

Velocidad de inferencia: más rápida en ambas CPUs

Velocidad de inferencia, eficiencia energética (i7-13700H)

1. Velocidad de inferencia: más rápida en ambas CPUs

La velocidad de inferencia se representa como la cantidad de tokens procesados por segundo. A continuación, se presenta un resumen de las observaciones:

  • En Apple M2 Ultra: BitNet.cpp logra una aceleración de hasta 5,07x para modelos más grandes (30B) en comparación con Llama.cpp, con una velocidad máxima de 593,43 tokens por segundo para un modelo de 125M, lo que es una aceleración de 1,37x. 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 muestra su eficiencia en diferentes escalas.
  • En Intel i7-13700H: BitNet.cpp logra mejoras de velocidad aún más dramáticas. En el tamaño de modelo de 7B, BitNet.cpp ofrece una aceleración increíble de 5,68x en comparación con Llama.cpp. Para modelos más pequeños como el 125M, procesa 389,08 tokens por segundo, lo que es 2,37x más rápido que Llama.cpp.

2. Eficiencia energética: un juego cambiador para dispositivos de borde

Los gráficos proporcionados también incluyen comparaciones de costos energéticos, lo que 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 de 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 modelos más grandes, con el modelo de 70B mostrando una reducción del 70,0% en el consumo de energía.
  • En Intel i7-13700H: BitNet.cpp ofrece ahorros de energía del 71,9% para el modelo de 700M, con un consumo que pasa de 1,367 a 0,384. Aunque no se dispone de datos de energía para el modelo de 70B en Llama.cpp, BitNet.cpp sigue siendo eficiente, con un consumo de energía de 17,33 para el modelo de 70B.

3. Superar el límite de velocidad de lectura humana

Una de las observaciones más interesantes de estos gráficos es la referencia a la velocidad de lectura humana, marcada en 5-7 tokens por segundo. Esta línea roja muestra que ambas implementaciones, especialmente BitNet.cpp, pueden superar cómodamente las velocidades de lectura humanas incluso para los modelos más grandes:

  • En Apple M2 Ultra, BitNet.cpp supera la velocidad de lectura humana para todos los tamaños de modelo, con la velocidad más baja siendo 8,67 tokens por segundo para un modelo de 70B.
  • En Intel i7-13700H, el modelo de 100B aún alcanza 1,70 tokens por segundo, casi tocando el rango inferior de la velocidad de lectura humana, mientras que todos los modelos más pequeños superan este límite.

Consideraciones de entrenamiento

Estimador de paso directo (STE)

Dado que la cuantización de 1 bit introduce funciones no diferenciables, el entrenamiento implica una técnica especializada conocida como Estimador de paso directo (STE). En este enfoque, los gradientes fluyen sin alteraciones a través de los puntos no diferenciables. A continuación, se muestra 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 mixta

Para mantener la estabilidad durante el entrenamiento, se emplea precisión mixta:

  • Pesos y activaciones: Cuantizados a precisión de 1 bit.
  • Gradientes y estados del optimizador: Almacenados en precisión más alta.
  • Pesos latentes: Mantenidos en alta precisión para facilitar actualizaciones precisas durante el entrenamiento.

Estrategia de tasa de aprendizaje grande

Un desafío único con los modelos de 1 bit es que las actualizaciones pequeñas pueden no afectar los pesos binarizados. 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 introduce cuantización y normalización de grupos para mejorar la paralelización del modelo. En lugar de calcular parámetros para la matriz de pesos completa, BitNet divide los pesos y las activaciones en múltiples grupos (G).

Esta agrupación permite el procesamiento paralelo eficiente sin comunicación adicional entre grupos, lo que permite el entrenamiento y la inferencia de modelos a gran escala.

Notas de implementación y optimizaciones

Optimización de CPU

BitNet.cpp aprovecha varias optimizaciones de bajo nivel para lograr un rendimiento de CPU máximo:

  • Operaciones vectorizadas: Utiliza instrucciones SIMD para realizar manipulaciones de bits de manera eficiente.
  • Acceso a memoria amigable con la 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 cuantización y la inferencia en BitNet:

def bitlinear_forward(input, weight, scale):
# Cuantiza la entrada utilizando cuantización de valor absoluto máximo
input_q = quantize(input)

# Realiza la multiplicación de matrices binaria
output = binary_matmul(input_q, weight)

# Escala la salida para coincidir con la precisión original
return output * scale

def quantize(x):
# Realiza la cuantización de valor absoluto máximo
scale = torch.max(torch.abs(x))
return torch.clamp(x / scale, -1, 1) * scale

Modelos compatibles

La versión actual de BitNet.cpp es compatible con los siguientes modelos LLM de 1 bit disponibles en Hugging Face:

  • bitnet_b1_58-large (0,7 mil millones de parámetros)
  • bitnet_b1_58-3B (3,3 mil millones de parámetros)
  • Llama3-8B-1.58-100B-tokens (8,0 mil millones de parámetros)

Estos modelos están disponibles públicamente para demostrar las capacidades de inferencia del marco. Aunque no fueron entrenados ni lanzados oficialmente por Microsoft, ilustran la versatilidad del marco.

Guía de instalación

Para empezar a trabajar con BitNet.cpp, siga los pasos a continuación:

Requisitos previos

  1. Python >= 3.9
  2. CMake >= 3.22
  3. Clang >= 18
  4. Conda (altamente recomendado)

Para los usuarios de Windows, se debe instalar Visual Studio con los siguientes componentes habilitados:

  • Desarrollo de escritorio con C++
  • Herramientas de CMake para Windows
  • Git para Windows
  • Compilador Clang para Windows
  • Soporte de MS-Build para el conjunto de herramientas LLVM (Clang)

Para los usuarios de Debian/Ubuntu, hay un script de instalación automática disponible:

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

Pasos para la instalación

  1. Clonar el repositorio:
    git clone --recursive https://github.com/microsoft/BitNet.git

    cd BitNet
  2. Instalar dependencias:
    # Crear un nuevo entorno de Conda (recomendado)
    conda create -n bitnet-cpp python=3.9
    conda activate bitnet-cpp


    pip install -r requirements.txt
  3. Compilar y preparar el proyecto: Puede descargar un modelo directamente desde Hugging Face y convertirlo a un formato cuantizado:
    python setup_env.py --hf-repo HF1BitLLM/Llama3-8B-1.58-100B-tokens -q i2_s

    Alternativamente, puede descargar y convertir el modelo manualmente:

    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

Ejecutar la inferencia con BitNet.cpp

Para ejecutar la inferencia utilizando el marco, utilice el siguiente comando:

python run_inference.py -m models/Llama3-8B-1.58-100B-tokens/ggml-model-i2_s.gguf -p "Sandra viajó a la cocina. ¿Dónde está Sandra?" -n 6 -temp 0.7

Explicación:

  • -m especifica la ruta del archivo del modelo.
  • -p define el texto del prompt.
  • -n establece el número de tokens para predecir.
  • -temp ajusta la aleatoriedad de muestreo durante la inferencia.

Ejemplo de salida

Sandra viajó a la cocina. ¿Dónde está Sandra?

Respuesta: Sandra está en la cocina.

Detalles técnicos de BitNet.cpp

Capa BitLinear

BitNet.cpp implementa una arquitectura de transformador modificada, sustituyendo las multiplicaciones de matrices estándar con operaciones BitLinear. Este enfoque centraliza los pesos en cero antes de la cuantización y los escala para reducir los errores de aproximación. La función de transformación clave se ve así:


# Función de binarización para pesos de 1 bit
def binarize_weights(W):
alpha = W.mean()
W_binarized = np.sign(W - alpha)
return W_binarized

La combinación de pesos centralizados y escalados garantiza que el error de cuantización permanezca mínimo, preservando así el rendimiento.

Impacto en 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 IA poderosa.
  • Eficiencia de costos: Reduce la necesidad de GPUs costosas, lo que reduce la barrera para la adopción.
  • Eficiencia energética: Ahorra energía al aprovechar la inferencia basada en CPU.
  • Innovación: Abre nuevas posibilidades para la IA en dispositivos, como la traducción de lenguaje en tiempo real, asistentes de voz y aplicaciones enfocadas en la privacidad sin dependencia de la nube.

Desafíos y direcciones futuras

Aunque los LLM de 1 bit tienen un gran potencial, quedan varios desafíos. Estos incluyen el desarrollo de modelos de 1 bit robustos para diversas tareas, la optimización del hardware para cálculos de 1 bit y el fomento de la adopción de este nuevo paradigma entre los desarrolladores. Además, explorar la cuantización de 1 bit para tareas de visión por computadora o audio representa una dirección futura emocionante.

Conclusión

El lanzamiento de BitNet.cpp por parte de Microsoft es un avance significativo. Al permitir la inferencia eficiente de 1 bit en CPUs estándar, BitNet.cpp crea la accesibilidad y la sostenibilidad de la IA. Este marco senta las bases para LLM más portátiles y rentables, empujando los límites de lo que es posible con la IA en dispositivos.

He pasado los últimos cinco años sumergiéndome en el fascinante mundo del Aprendizaje Automático y el Aprendizaje Profundo. Mi pasión y experiencia me han llevado a contribuir a más de 50 proyectos de ingeniería de software diversos, con un enfoque particular en AI/ML. Mi curiosidad continua también me ha llevado hacia el Procesamiento de Lenguaje Natural, un campo que estoy ansioso por explorar más a fondo.