3 puntos por GN⁺ 2023-08-21 | 1 comentarios | Compartir por WhatsApp
  • Es una herramienta para ejecutar inferencia de modelos Diffusion, incluidas las familias SD, Flux y Wan, en C/C++ puro, con el objetivo de una implementación ligera sin dependencias externas
  • La implementación está basada en ggml y tiene una estructura Plain C/C++ que funciona de forma similar a llama.cpp
  • El rango de modelos soportados se divide en modelos de imagen, modelos de edición de imagen y modelos de video, y apunta a SD1.x, SD2.x, SDXL, SD3/SD3.5, FLUX, Qwen Image, Wan2.1/Wan2.2, LTX-2.3, entre otros
  • El alcance de funciones incluye PhotoMaker, Control Net para SD 1.5, LoRA al estilo de stable-diffusion-webui, LCM/LCM-LoRA, decodificación latente basada en TAESD, escalado con ESRGAN, negative prompt y soporte de tokenizer con pesos de tokens
  • Los backends de ejecución son CPU, CUDA, Vulkan, Metal, OpenCL y SYCL; en CPU incluye soporte para AVX, AVX2 y AVX512 en la arquitectura x86
  • Las plataformas soportadas son Linux, Mac OS, Windows y Android; en Android se ejecuta mediante Termux y Local Diffusion
  • Los formatos de pesos soportados son .ckpt, .pth, .pt, .safetensors y .gguf; el modo de conversión transforma los pesos del modelo a .gguf o .safetensors
  • El flujo de uso básico consiste en descargar un binario precompilado desde la página de releases o compilar desde el código fuente, luego descargar los pesos del modelo y ejecutar la generación de imágenes con un comando como ./bin/sd-cli -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat"
  • Ofrece Flash Attention y VAE tiling processing como funciones de optimización de uso de memoria; la distribución por backend del runtime y los parámetros, así como las mejoras de rendimiento, se tratan en guías separadas
  • Las opciones de reproducibilidad se dividen en --rng cuda y --rng cpu, con el objetivo de mantener consistencia con el RNG de GPU de stable-diffusion-webui y el RNG de ComfyUI, respectivamente
  • En la salida PNG inserta los parámetros de generación como una cadena de texto compatible con webui
  • Hay proyectos wrapper para Golang, C#, Python, Rust y Flutter/Dart, y Jellybox, Local Diffusion, LocalAI, KoboldCpp, entre otros, usan stable-diffusion.cpp como backend de generación de imágenes
  • El proyecto está en desarrollo activo, por lo que la API y las opciones de línea de comandos pueden cambiar con frecuencia

1 comentarios

 
GN⁺ 2023-08-21
Opiniones de Hacker News
  • Llama.cpp/ggml encaja excepcionalmente bien con los LLM
    Tienen grandes requisitos de memoria, la cuantización es efectiva, la generación de tokens es sorprendentemente serial y está limitada por el ancho de banda de memoria, así que encajan bien con CPU, y aún mejor con la inferencia del particular pipeline CPU/GPU de ggml
    Pero Stable Diffusion es distinto. La cuantización no funciona tan bien, UNet tiene una carga de cómputo muy grande, y la generación de imágenes por lotes es efectiva y útil incluso para un solo usuario. Por eso encaja mejor con GPU/GPU integrada y se beneficia mucho de la hackeabilidad de la implementación en Python
    Para Stable Diffusion, creo que el camino correcto es generar ejecutables mediante compilación de machine learning. AITemplate ya es muy rápido https://github.com/VoltaML/voltaML-fast-stable-diffusion, y TVM Vulkan también es muy prometedor si alguien termina bien una implementación demo https://github.com/mlc-ai/web-stable-diffusion
    Además, se conserva la mayor parte de la hackeabilidad de una implementación en PyTorch puro

    • Ese proyecto también ofrece cierto soporte de GPU si se pasan los flags de compilación de GGML correctos
      Por ejemplo, GGML_CUBLAS está soportado al compilar, y se obtiene una mejora de velocidad bastante decente frente a C/C++ puro
    • En cambio, es bueno para quienes no tienen una GPU NVIDIA con 6 GB o más de VRAM, pero quieren experimentar con estas redes neuronales en local
      Aunque tarde un poco, se puede ejecutar en una laptop vieja
    • Si mal no recuerdo, con torch.compile también vi una mejora de velocidad bastante buena, y recuerdo haber trabajado directamente en eso
      Voy a ver si encuentro las cifras
  • Genial que hayan implementado incluso CLIP
    Estaría buenísimo extraer solo eso y compilarlo como una implementación en WebAssembly
    Edición: parece que alguien ya hizo https://github.com/monatis/clip.cpp. Ahora solo falta llevarlo a WebAssembly

    • Ya que salió el tema de CLIP, siempre me preocupa que, ahora que OpenAI y Google entraron en modo competencia, quizá no se publique el próximo modelo de nivel CLIP
      Da lástima pensar que en algún lugar, dentro de una bóveda secreta, quizá ya exista un modelo de nivel CLIP más avanzado
      Edición: no me refiero a CLIP-2, sino a un avance de una importancia comparable a la de CLIP
  • La configuración fue tan increíblemente fácil que lo probé de inmediato por primera vez
    Me pregunto qué velocidad debería considerarse normal
    Lo ejecuté en Linux con cmake .. -DGGML_OPENBLAS=ON en un AMD Ryzen 7 5700G, sin GPU dedicada, solo gráficos integrados
    Al ejecutar ./bin/sd -m ../models/sd-v1-4-ggml-model-f32.bin -p "a lovely cat", cada paso de muestreo tardó unos 12 segundos, y todo el muestreo tomó 246.40 segundos
    Me pregunto si ese es el rendimiento esperado
    Edición: no tenía OpenBLAS instalado, así que ese flag no tuvo efecto

    • Esto está bueno. Básicamente hace lo que yo quería hace un año[0]
      En ese momento, casi todas las soluciones exigían un montón de dependencias de Python, tardaban demasiado en instalarse y al final fallaban porque se acababa el espacio en disco
      De verdad, reemplaza literalmente varios gigabytes de espacio en disco por un solo binario de 799 KB. Como extra, si se usa el formato Q8_0, que parece ser el más rápido, también se ahorran unos 2.3 GB de datos
      Sin embargo, fuera del tamaño de imagen predeterminado de 512x512, parece tener bugs. Algunos tamaños como 544x544 tienden a provocar fallas de assert, los tamaños menores que 512x512 a veces generan imágenes basura, y los menores que 384x384 casi siempre lo hacen
      [0] https://news.ycombinator.com/item?id=32555608
    • Hay que cuantizar el modelo, pero unos 12 segundos por iteración parece razonable
    • Solo CPU, cuantización de 8 bits, Intel Core i7 4770S, 16 GB de RAM DDR3, en una PC fanless de hace 10 años: tardó 32 segundos por paso de muestreo y la salida es correcta
  • Hay algo especialmente atractivo en las implementaciones de IA en C/C++
    El código se siente limpio e intuitivo, y hace que todo el campo de la IA parezca algo tangible y posible de aprender
    ¿Será porque el ecosistema de Python es demasiado desordenado?

    • Las reescrituras en general mejoran la calidad del código, y reemplazar dependencias por código a medida que solo hace lo necesario también mejora la calidad del código
      La versión en Python también usa código C y C++ por velocidad, pero aquí todo está en un solo lenguaje
      Es decir, tres factores que permiten un código limpio funcionando juntos
  • Es bueno ver que la gente de machine learning se aleje de Python y use lenguajes que aprovechan el hardware de forma óptima y que no requieren preparar entornos especiales para compilar y ejecutar

    • Es una comparación bastante rara.
      Para empezar, el proyecto original no usa GPU, igual que llama.cpp, mientras que la mayoría del código de machine learning en Python sí usa GPU. No es difícil escribir código Python que aproveche de forma óptima la GPU. Se podría decir que una GPU es un entorno especial para compilar y ejecutar, pero para este problema se puede considerar que la GPU encaja mucho mejor.
      En segundo lugar, el proyecto original, al igual que llama.cpp, creó código eficiente y altamente especializado después de comprobar que ciertos modelos específicos como Stable Diffusion/LLaMA funcionaban bien. En cambio, donde Python brilla es en la etapa de prototipado, cuando todavía no se ha encontrado el modelo adecuado. Todavía no he visto un prototipado tan fácil y cómodo en C++.
      No pretendo desmerecer el excelente trabajo que está haciendo la gente de llama.cpp en el ámbito del machine learning sobre CPU. Pero los problemas que resuelven son completamente distintos.
    • Sería mucho mejor si todos los modelos de machine learning tuvieran una API de inferencia en C sencilla, para poder llamarlos directamente desde casi cualquier lenguaje o plataforma sin el caos de dependencias y configuración de entorno.
    • En el stack de machine learning, los componentes críticos para el rendimiento en realidad no están implementados en Python.
      Por dentro, desde hace tiempo todo ha sido CUDA, C y C++.
      Python no es más que un pegamento muy efectivo que une todo eso.
    • De verdad agradezco a la gente que hace este tipo de trabajo.
      Es la única forma en que he podido ejecutar estos modelos sin problemas molestos. La diferencia es enorme. La combinación CUDA y Linux tampoco es buena, y AMD con Windows es miserable. Probablemente no sea el único al que le pasa.
    • Me parece interesante que mi CPU pueda ejecutar algunos de estos en forma cuantizada a una velocidad casi parecida a la GPU.
      ¿Al final todo era un problema de ancho de banda de memoria?
      La arquitectura de GPU no solo trata de capacidad de cómputo, sino también de colocar la memoria de trabajo cerca de las unidades de cómputo. Cada unidad tiene memoria local que se sincroniza con la memoria global. ¿Será esa una de las grandes razones por las que las GPU son fuertes en este tipo de tareas?
  • Parece C++, ¿por qué lo describen como C/C++?

    • Según entiendo, la dependencia base, ggml, está escrita en C.
  • Hoy vi este repositorio, lo descargué y compilé un .dylib en Mac, y generé bindings a partir del archivo de encabezado provisto con la herramienta ffi-gen de Dart.
    Estoy experimentando con Flutter y usando FFI para no lanzar un subproceso.
    El resultado fue un fuerte dolor de cabeza y una app rota. Mañana lo intentaré de nuevo con la mente despejada.
    Aun así, el repositorio en sí es excelente, y en un M1 incluso llegó a ejecutarse en menos de 10 minutos con f16.

  • Ver ejemplos de varios niveles de cuantización es bastante impresionante.
    El cambio de f16 a q8_0 parece más un cambio de dirección que una pérdida de calidad. El resultado de q5_1 parece difícil de distinguir de q8_0.
    En modelos de alta precisión se pierde determinismo, pero en la práctica es posible que sea bastante utilizable.

  • ¿Hay benchmarks?

    • Algunas personas midieron los tiempos aquí, y según la cuantización y el hardware parece tomar alrededor de 15 a 20 segundos por iteración.
      https://github.com/leejet/stable-diffusion.cpp/issues/1
    • Lo compilé con el comando cmake .. -DGGML_CUBLAS=ON -DCMAKE_CUDA_COMPILER=/opt/cuda/bin/nvcc y usé una NVIDIA GeForce RTX 2060 SUPER.
      Convertí el modelo a FP16.
      Con esta opción, el tiempo por iteración está entre 8.5 y 9 segundos, y el tiempo total para generar una imagen es de unos 200 segundos.