1 points par GN⁺ 2 시간 전 | 1 commentaires | Partager sur WhatsApp
  • WASTE est un moteur d’inférence en C qui convertit le modèle open weights complet Kimi K3 de 2,78 billions de paramètres, sans réduction, en un conteneur de 982 Gio exécutable sur un ordinateur portable grand public
  • Il garde uniquement le tronc résident du modèle en mémoire et lit depuis le NVMe environ 4 % des poids d’experts activés à chaque token, le reste de la RAM servant de cache d’experts à taille limitée
  • Kimi K3 peut être ouvert avec un minimum de 29,05 Go de RAM pour un contexte 4K, mais la configuration pratique est un budget de 46 Go sur un MacBook Pro 64 Go, où il atteint 0,45 à 0,62 tok/s
  • En chevauchant lectures d’experts et calculs, il obtient environ 1,6× d’amélioration, et en exécutant le routeur de la couche suivante un residual en avance, il fait passer le taux de cache hit de 14 % à 38 %, sans modifier le volume total lu ni les logits
  • Il permet d’exécuter localement un très grand modèle sans connexion Internet, sans coût par token ni transfert de données externe, mais nécessite un NVMe interne et environ 1 To de stockage ; avec un budget RAM de 52 Go ou plus, le paging de l’OS peut au contraire provoquer un fort ralentissement

Objectif et forme d’implémentation de WASTE

  • WASTE (Weight-Aware Streaming Tensor Engine) est un moteur d’inférence C intégrable, sans dépendance externe à l’exécution
    • Il n’utilise que libwaste.a et l’exécutable waste, et ne nécessite ni BLAS, ni CUDA, ni ONNX, ni Python en dehors de libc et pthreads
    • Python n’est utilisé que pour la conversion du modèle et la validation par rapport à PyTorch, pas dans le chemin d’inférence
    • L’API publique comprend 26 fonctions et prend en charge l’ouverture du modèle, le réglage d’une limite de RAM, la génération, la sauvegarde de session et l’arrêt
  • La cible actuellement validée est le modèle complet Kimi K3 2.78T
    • La source publique fait 1,42 To et le conteneur après conversion fait 982 Gio
    • Ce n’est ni une version distillée, ni élaguée, ni réduite
    • Kimi-Linear 48B atteint aussi, avec le même moteur et le même format, un conteneur de 19 Gio, un minimum de 1,87 Go de RAM et 10,7 tok/s
  • Le nom du projet vient de l’objectif de réduire les situations où l’on exécute dans des datacenters cloud, en consommant à la fois des coûts de tokens et de l’électricité, des modèles qui pourraient tourner sur du matériel de bureau

Architecture de streaming depuis le disque

  • K3 étant une architecture Mixture of Experts, seulement environ 4 % du modèle sont activés par token ; les poids inactifs peuvent donc être disposés pour être accessibles au moment nécessaire, sans rester en RAM
  • Le conteneur .waste se compose d’un manifeste JSON, d’un tronc résident et de banques d’experts par couche
    • Chaque enregistrement d’expert est aligné sur 4 KiB
    • Les matrices gate, up et down sont placées de manière contiguë afin qu’un expert soit lu exactement en un seul pread
    • Le cache de pages est contourné via F_NOCACHE sur macOS, O_DIRECT sur Linux et FILE_FLAG_NO_BUFFERING sur Windows
  • Si le cache de pages n’est pas contourné, un conteneur de test plus petit que la RAM peut entrer dans le cache de l’OS et produire des taux de hit qui ne se reproduisent pas avec le modèle de 982 Gio
  • Lors de la lecture des enregistrements, le magic, l’ID d’expert et la plage d’offsets sont toujours vérifiés pour éviter qu’une banque tronquée ou mal assemblée ne réponde avec les mauvais poids
    • La vérification crc32 du payload s’active avec --verify
    • Le coût de vérification est d’environ 5 % sur Kimi-Linear et d’environ 1 % sur K3 ; elle est désactivée par défaut
    • Il est recommandé de vérifier une fois les conteneurs copiés, téléchargés ou placés sur un disque non fiable
    • Le tronc et les codebooks n’ont pas de checksum

Lecture anticipée et prédiction du routeur

  • Quand le routeur d’une couche décide de 16 ID d’experts, chaque lecture est demandée dans un thread distinct, et le calcul consomme les données dès leur arrivée
    • Le chevauchement des lectures et des calculs améliore K3 d’environ 1,6×
    • Le travail effectué et les statistiques de cache sont les mêmes avant et après l’activation de la fonctionnalité
  • Avant que le hidden state réel de la couche suivante ne soit produit, le routeur suivant résident est exécuté sur le hidden state actuel pour précharger 6 experts
    • Cette prédiction un residual en avance est correcte à 92 % au rang 1 et à 81 % dans le top 6
    • Le routeur réel décidant des experts finaux, la sortie reste exactement correcte
    • Le demand hit rate passe de 14 % à 38 %, tandis que le nombre total d’octets lus ne change pas
    • Elle peut être désactivée avec WASTE_LOOKAHEAD=0
  • L’implémentation qui appliquait la même technique au prefill a été supprimée
    • Une couche decode occupe 16 slots de cache, alors qu’une couche chunk en occupe environ 550
    • Les enregistrements lus à l’avance étaient évincés avant utilisation, augmentant le volume lu de 6,9 % sans réduire le temps

Quantification et exactitude

  • Les poids d’experts sont stockés avec une quantification vectorielle résiduelle en 3 étapes, avec des codebooks de 256 entrées pour des vecteurs de dimension 8, soit 3,00 bits par poids
    • Sans reconstruire toute la matrice, une table de produits scalaires partiels est construite, puis chaque ligne est traitée avec 3 recherches dans la table et 2 additions
  • Le tronc conserve des poids 4 bits et 8 bits
    • Comme le modèle n’a été entraîné avec prise en compte de la quantification que pour les experts, la sortie s’effondre avec un tronc en 3 bits
    • La prédiction de cache était correcte, mais le débit ne s’améliorait pas non plus, elle a donc été supprimée
  • Toutes les couches sont comparées à l’implémentation de référence PyTorch
    • L’écart final sur les logits est de 3.6e-06
    • La tour vision est à 2.3e-06 de sa propre référence
    • La conversion du cache KV latent conserve des logits identiques à un niveau de 1.2e-05

Budget RAM et fenêtre de performance étroite

  • K3 utilise 16 experts dans chacune de ses 92 couches, créant un working set de 17,0 Go par token
    • Si le cache est plus petit que cette taille, les experts stockés pendant un token sont évincés avant le token suivant et le taux de hit devient 0 %
  • Les mesures sur un système 64 Go montrent qu’allouer plus de RAM n’accélère pas toujours
    • Budget 32 Go, cache 3,32 Go : taux de hit 0 %, 0,50 tok/s
    • Budget 46 Go, cache 17,32 Go : taux de hit classique 17 %, 0,53 à 0,55 tok/s
    • Budget 52 Go, cache 23,32 Go : non reproductible, 0,04 à 0,15 tok/s
    • Budget 58 Go, cache 29,32 Go : 0,02 à 0,03 tok/s
  • Le lookahead du routeur fait passer le taux de hit d’environ 14 % à 38 % avec 46 Go, mais l’effondrement au-delà de 52 Go est dû au paging de l’OS, pas aux cache misses
    • À 58 Go, malgré un taux de hit plus élevé, c’est environ 20 fois plus lent qu’à 46 Go
    • Après avoir mis le système en situation de paging avec un gros budget, même les mesures à 46 Go peuvent tomber à 0,02 tok/s
  • Le budget par défaut est choisi sous les 7/8 de la RAM physique, arrondi vers le bas par unité de working set de token
    • Sur un MacBook Pro 64 Go, il utilise 46,24 Go et alloue 17,56 Go au cache d’experts
    • Si le budget explicite est inférieur au minimum, il refuse de démarrer au lieu de continuer en swap
    • Sur un système 128 Go, on peut utiliser le budget total recommandé correspondant au tronc plus 3 fois le working set

Performances de K3 et exigences matérielles

  • Le système de mesure est un MacBook Pro M5 Pro 64 Go avec SSD interne
    • RAM minimale pour un contexte 4K : 29,05 Go
    • 32K : 30,54 Go, 128K : 35,63 Go, 1M : 83,21 Go
    • Tronc résident : 27,28 Go
    • Chargement du modèle : 20 secondes
    • Decode : 0,45 à 0,62 tok/s avec le budget par défaut
    • Prefill : 0,47 tok/s en chunked, 0,29 tok/s en séquentiel
  • Même s’il est possible d’ouvrir le modèle avec un minimum de 29,05 Go, un système 32 Go peut paginer sévèrement ; 64 Go est donc la configuration réellement recommandée
  • À froid, 17,0 Go d’experts sont lus par token ; avec un taux de hit de 38 % grâce au lookahead, 10,5 Go sont lus
  • Le SSD interne est mesuré à 12,78 Go/s, contre 0,94 Go/s pour un boîtier USB externe
    • Comme un token lit 17 Go d’experts, le même traitement prend environ 13 secondes sur un stockage externe
    • Le téléchargement d’origine peut être placé sur un disque externe, mais le conteneur converti doit être sur le NVMe interne
  • Il faut 982 Gio pour le conteneur converti et 1,42 To pour le staging des shards d’origine ; l’espace de staging peut être libéré après conversion

Attention et traitement multimodal

  • L’attention de K3 combine Kimi Delta Attention et gated multi-head latent attention dans un ratio 3:1
    • KDA maintient un état récurrent de taille fixe au lieu d’un cache KV croissant
    • MLA met en cache un latent de largeur 512 sans étendre les keys/values par tête
  • En absorbant kv_b_proj dans la query et l’output, le cache pour un contexte 4K passe de 11,25 Go à 0,21 Go
    • C’est une réduction de 53× par rapport à l’approche précédente
    • En 128K, le layout étendu demanderait 360 Go, contre 7,2 Go pour le layout latent
  • Le chemin multimodal prend en charge un ViT de 401 M de paramètres, 27 couches et des patches de 14
    • L’encodage d’une image de 1024 patches prend 15,7 secondes
    • Une image 896×896 occupe 256 positions de séquence avec les réglages par défaut
    • Comme les embeddings d’image passent aussi par les 92 couches MoE, la majeure partie du coût est similaire au text prefill, plus qu’à la tour vision
    • Réduire de moitié max_patches dans vision.json réduit aussi de moitié le nombre de positions de prompt
  • PNG, JPEG, GIF, BMP, TGA et PSD sont pris en charge, et les images peuvent être utilisées avec run, chat et eval
    • Dans une conversation, les positions des images encodées restent dans l’état d’attention et ne sont pas réencodées au tour suivant
    • La tour vision n’est chargée que lorsqu’il y a une image ; elle utilise 434 Mo de poids et 1,12 Go de mémoire réservée au total

Conversion, exécution et serveur

  • La compilation ne nécessite qu’un compilateur C11 et make
    • make check passe 23 tests et en saute 11 avec un conteneur synthétique, sans modèle réel
    • Avec les deux conteneurs réels, la suite complète compte 36 tests
  • La conversion de K3 utilise directement les 96 shards safetensors publics de moonshotai/Kimi-K3
    • Elle prend environ 4,7 heures sur M5 Pro avec 3 processus
    • L’encoder PyTorch pur prend 23,7 heures
    • Elle peut reprendre couche par couche ; en cas d’interruption, seule la couche en cours est retraitée
    • Le downloader prend en charge la reprise des fichiers partiels, l’exponential backoff avec jitter, la vérification de Content-Length et l’enregistrement de l’état des shards terminés
  • La CLI fournit notamment run, chat, eval et plan, et --json permet de produire des résultats lisibles par machine pour eval, tokenize, plan, info et bench
  • serve/ est un serveur HTTP compatible OpenAI qui appelle l’API C publique via ctypes
    • Il fournit /v1/chat/completions, /v1/completions, /v1/models et /health
    • Il gère le streaming, les définitions et résultats d’outils, les arguments d’appel typés, les schémas de réponse JSON, tool_choice, le think channel, thinking_effort et les images
    • Le renderer de prompts est un portage de encoding_k3.py de la release K3 ; si un répertoire de poids est présent, 38 conversations sont comparées segment par segment

Plateformes et limites actuelles

  • macOS arm64, Linux arm64 et Linux x86_64 enregistrent 23 pass et 11 skip sur les mêmes tests indépendants du modèle, et passent aussi les sanitizers et 400 fuzz tests
  • Windows x86_64 a été compilé en cross-compilation avec MinGW-w64 et validé sur un conteneur synthétique, la CLI et un forward pass, mais pas exécuté avec un vrai conteneur de modèle
    • MSVC et Windows ARM64 ne sont pas pris en charge
    • Le contournement du page cache sous Windows n’a été confirmé que sur le système de fichiers de la CI, pas sous la charge d’un vrai conteneur plus grand que la RAM
  • Le SIMD x86 choisit AVX-512 ou AVX2 selon CPUID, mais le chemin AVX-512 n’a pas encore été exécuté sur un CPU réellement compatible
  • Le backend Metal est exact, mais il est 22 % plus lent que le CPU et désactivé par défaut en raison de la forme de la charge de travail, qui déclenche des centaines de petits matvec dépendants
  • L’API n’est pas encore stabilisée, et la conversion automatique du chat format ne prend actuellement en charge que K3
    • Kimi-Linear s’exécute en mode raw sans essayer de deviner le template
  • L’allocation non uniforme de bits par expert ne sera pas introduite
    • La valeur du troisième bit ne varie que jusqu’à 1,15× entre experts d’une même couche et 1,01× entre couches, ce qui n’apporte pas de gain avec une allocation optimale
    • L’allocation fondée sur la fréquence de routing réduit aussi l’espace de stockage, mais réduit très peu l’I/O, qui est le goulot d’étranglement
  • La licence est Apache 2.0

1 commentaires

 
GN⁺ 2 시간 전
Commentaires sur Hacker News
  • Vraiment impressionnant. Ce n’est pas un projet qui cherche à être plus pratique que les fournisseurs cloud dès maintenant, mais un projet qui montre les limites du possible
    Si les gains d’efficacité des modèles et les progrès du matériel local se rejoignent, il pourrait devenir économiquement viable de faire tourner un jour des modèles locaux de haute qualité

  • 0,5 token par seconde, ça me semble inutile pour des tâches longues. Je préférerais dépenser l’argent pour deux 4060 Ti 16 Go avec parallélisme tensoriel
    Dans 20 ans, ça conviendrait peut-être à des robots lents au style cyberpunk, alimentés au solaire, qui tondent la pelouse ou nettoient les trottoirs, ou à un robot de jardin qui suit tout juste la croissance d’un bonsaï pour le tailler

    • C’est presque inutilisable aujourd’hui, mais je suis content que ce genre de projet continue à s’améliorer, car c’est comme ça qu’on finit par atteindre une version pratique
  • Dire qu’il est absurde de payer le coût au token pendant que le fournisseur d’inférence paie l’électricité, je ne vois pas en quoi c’est différent du fait d’acheter des concombres alors que l’agriculteur paie l’eau et l’engrais. J’espère que cette logique plaquée après coup ne s’applique qu’aux LLM
    L’idée en elle-même est intéressante, et j’aimerais l’essayer avec un plus petit modèle. Si on génère 0,5 token par seconde tout en lisant plusieurs Go par seconde depuis le SSD, c’est encore trop gros pour un portable grand public, mais ça pourrait au contraire devenir pratique pour des modèles de 250 à 500 Gio

    • Si vous cultivez vos tomates vous-même, vous obtenez des tomates gratuites. Pas assez pour faire un BLT, mais comme elles ne viennent pas du supermarché, vous avez un peu sauvé le monde /s
  • En supposant une consommation continue de 42 W et un prix de l’électricité de 0,20 $/kWh, cela fait environ 5 dollars par million de tokens, hors autres coûts comme le matériel

    • Un mois, c’est environ 2,6 millions de secondes, et à 0,5 token par seconde on génère 1,3 million de tokens par mois. En tenant compte des coûts annexes, considérer que le coût mensuel d’exploitation de la machine correspond au coût par million de tokens semble à peu près correct
    • Je me demande comment le calcul change avec du solaire
  • Le llama.cpp standard peut aussi mmap des GGUF, donc ce qui ne tient pas en mémoire reste sur disque, et le cache de pages du noyau conserve les chunks résidents les plus utilisés. Je me demande quel est l’avantage d’une implémentation maison

    • La même question avait été posée sur un projet similaire il y a quelques jours : ils ont d’abord essayé mmap, puis ont fait leur propre implémentation et c’était 10 fois plus rapide
      C’est la même raison pour laquelle les moteurs de base de données implémentent leur propre cache. La pagination du noyau est généraliste et pilotée par la demande, mais si on connaît les vrais schémas d’accès, on peut précharger les données nécessaires et les pipeline
    • À cette échelle, utiliser le SSD comme espace de swap risque d’épuiser l’endurance en écriture cumulée en quelques mois seulement. J’aimerais voir les statistiques SMART sur les écritures cumulées et l’usure après un fonctionnement au-delà d’un simple test de courte durée
      Pour un modèle qui tient entièrement en RAM, il valait mieux lancer llama-server avec --no-mmap. Bien sûr, pour charger l’ensemble de Kimi K3 et un contexte d’un million de tokens, il faudrait un serveur de 2 To
  • Le README donne fortement l’impression d’avoir été écrit par un LLM, et je me demande si la base de code aussi a été écrite par un LLM

    • Je ne veux pas le dénigrer superficiellement, mais la documentation se contredit sur le fait que le modèle tourne vraiment à sa précision d’origine. La quantification 3 bits annoncée peut être intéressante, mais K3 fait déjà environ 115 Go en paramètres denses à la précision d’origine, avec environ 25 Go d’experts creux activés par token, auxquels s’ajoute le cache KV, donc l’affirmation de 2 secondes par token avec 29 Go de RAM me paraît difficile à comprendre
    • J’ai beaucoup écrit de logiciels moi-même et j’ai aussi créé un langage de programmation : https://github.com/marcobambini/gravity
      Maintenant, j’utilise mes compétences pour orchestrer des LLM et des agents afin d’écrire bien meilleur code, beaucoup plus vite. Les développeurs doivent soit s’adapter aux nouvelles technologies, soit disparaître
    • J’aimerais qu’au minimum les auteurs relisent eux-mêmes le README généré par le LLM. Les LLM comprennent mal le point de vue du lecteur et partent du principe qu’un lecteur externe connaît déjà tout le contexte du projet et le processus de décision
      On y retrouve tel quel des décisions internes importantes pour l’utilisateur mais sans intérêt pour quelqu’un qui regarde seulement le résultat final, ainsi que le jargon obscur propre à Claude. J’utilise souvent les LLM et je reconnais qu’ils sont très utiles pour écrire du code complexe, mais la qualité des premiers jets rédigés est médiocre
    • Il y a claude dans la liste des contributeurs, donc inutile de spéculer. Si on va jusqu’à laisser Claude faire les commits, il semble peu probable que le code ait été relu manuellement
    • C’est dans le README que le style propre à Claude se sent le plus. Comme on reconnaît la manière d’écrire des personnes, j’ai l’impression que le style court, haché et excessivement rythmé que Claude produit par défaut s’est désormais installé dans ma tête comme une catégorie à part
  • Cela pourrait gagner en valeur si la technologie progresse au point de permettre de choisir précisément le bon modèle pour la bonne tâche. On peut imaginer un futur où, dans un processus d’exploration automatique, on n’active un gros modèle qu’une trentaine de minutes par jour, et on utilise des petits modèles le reste du temps

    • Je ne pense pas que ce futur arrivera. On ne connaît souvent le temps nécessaire qu’une fois l’élément du backlog terminé, et il n’y a aucun moyen d’estimer à l’avance la complexité d’une tâche sans réellement l’exécuter