Un exemple de l'approche RWKV des modèles de langue écrite en rouille par quelqu'un qui connaît très peu les réseaux mathématiques ou neuronaux. La version initiale était très, très fortement basée sur les informations incroyables et l'exemple de Python ici: https://johanwind.github.io/2023/03/23/rwkv_details.html
Voir également le référentiel du Rwkv Creator: https://github.com/blinkdl/chatrwkv/
Si le chargement en mode 32 bits, il utilise beaucoup de mémoire. Le modèle 3B utilise environ 11 Go de RAM et le 7B peut simplement tenir sur une machine de 32 Go que vous êtes prêt à fermer d'autres applications ou à gérer un échange. Même le chargement en mode 8 bits utilise une bonne quantité de mémoire, mais il se déroulera une fois le chargement terminé.
Vous aurez besoin d'une configuration de rouille et de cargaison: https://www.rust-lang.org/learn/get-started
Vous devrez télécharger un modèle RWKV. Voici un lien pour vous lancer (environ 820 Mo): https://huggingface.co/blinkdl/rwkv-4-pile-430m/resolol/main/rwkv-4-pile-430m-20220808-8066.pth
Aussi le tokenizer ici: https://github.com/blinkdl/chatrwkv/blob/main/20b_tokenizer.json
Les fichiers de modèle Pytorch peuvent être chargés directement. Si les fichiers se terminent par .pt ou .pth il sera chargé en tant que modèle Pytorch. S'il se termine par .st ou .safetensors , il sera chargé en tant que Safettenseurs. Remarque : le support Pytorch est actuellement expérimental et peut ne pas fonctionner correctement. Vous obtiendrez probablement immédiatement une erreur en cas de problème, il ne devrait donc pas être dangereux d'essayer cette approche. Si vous le souhaitez, vous pouvez désactiver la fonctionnalité torch et ne créer que la prise en charge des fichiers de format SafeTtenseurs.
Après cela, vous devriez simplement pouvoir cargo run --release . Vous pouvez essayer de compiler sans --release , mais il est probable que tout sera incroyablement lent. Essayez également cargo run --release -- --help pour voir les options de ligne de commande.
Remarque : La valeur par défaut consiste à utiliser tous les noyaux logiques, voir les options de ligne de commande.
Vous pouvez éventuellement convertir le fichier de modèle .pth au format Safettenseurs. Regardez utils/pth_to_safetensors.py pour un exemple. Pour ce faire, vous aurez besoin de la configuration des packages safetensors ET torch PYTHON. Je suggère de le faire dans un environnement virtuel. Actuellement, il n'y a pas beaucoup d'avantages à cette étape car les fichiers de torche peuvent être chargés directement dans la version actuelle.
La prise en charge de GGML a actuellement besoin d'une version patchée de ggml et ggml-sys du projet llama-rs .
Le Cargo.toml est configuré pour pointer la branche correcte de ma fourche, mais cela disparaîtra une fois que les modifications nécessaires seront fusionnées en GGML. Naturellement, ce dépôt sera mis à jour, mais gardez à l'esprit que vos compléments peuvent commencer à échouer si vous essayez d'utiliser une ancienne version, car finalement cette branche sera supprimée.
Remarque: cette partie est en quelque sorte obsolète maintenant. Je recommande toujours de lire les liens ci-dessous. Notez également que cette description est basée sur une version plus simple du modèle RWKV avec seulement quatre états par couche. La version complète en a cinq.
Voici une description (peut-être erronée) de haut niveau des étapes impliquées dans l'évaluation du modèle. Vous devrez vous référer à la source de smolrwkv/src/simple/model.rs pour que cela ait un sens.
Pensez également fortement à les lire en premier:
Soit dit en passant, un fait amusant: "Tensor" semble vraiment fantaisiste, mais c'est simplement juste un tableau. Un tenseur unidimensionnel n'est qu'un réseau unidimensionnel, un tenseur dimensionnel bidimensionnel est un réseau en deux dimensions. Ils peuvent avoir des propriétés spéciales (comme être immuables), mais cela n'a pas d'importance pour comprendre le concept en général. Si vous connaissez les tableaux, vous avez déjà l'idée générale des tenseurs.
Pour évaluer un jeton:
x à partir de ln0 .x à chaque couche séquentiellement, en utilisant le x la couche générée pour la suivante.x qui s'est nourri.ln1 sur x et alimentez-le au mélange de temps. Cela utilise le tenseur de la partie FFN du modèle.tm_state à partir de l'état de la couche et appelez- last_x . (Pourquoi? Qui sait!)tm_num et tm_den AS last_num , last_den .tm_[state,num,den] mettent donc à jour votre état de couche avec ceux-ci.x résultant des calculs.x du mélange de temps à x ( x += time_mixing_x ).ln2 sur x et alimentez-le sur le mélange de canaux. Cela utilise les tenseurs de la partie réseau de flux de flux du modèle.cm_state à partir de l'état de la couche et appelez- last_x .cm_state afin de mettre à jour l'état de la couche.x résultant du calcul de mélange de canaux.x du mélange de canal à x .x qui a été le résultat après avoir évalué la dernière couche.Le modèle a une liste de jetons qu'il "sait". Parfois, un jeton est égal à un mot, parfois cela fait juste partie d'un mot. Il y a généralement un grand nombre de jetons, entre 30 000 et 60 000. Je crois que les modèles RWKV actuels ont 50 277 jetons. Quoi qu'il en soit, vous obtiendrez une liste de 50 277 numéros de points flottants après avoir exécuté le modèle.
La valeur la plus élevée de cette liste est le jeton prédit que le modèle est la continuation la plus probable, etc. Si vous avez généré une liste triée des 10 à 40 premières probabilités de jeton environ et en sélectionnez une au hasard, vous obtiendrez une sortie assez raisonnable, relativement parlant. Juste à dire qu'un minuscule modèle de 430m ne produit pas la sortie la plus raisonnable en général.
Bonne explication de la façon de gérer l'étape suivante une fois que vous avez la liste des probabilités: https://huggingface.co/blog/how-to-generate
Il y a diverses choses compliquées en mathématiques impliquées dans l'évaluation du modèle, mais la seule chose qui compte vraiment est la multiplication matricielle ( pardot dans la source). Dans le cas de RWKV, c'est la multiplication matricielle-vector (un tableau 2D multiplié par un tableau 1D). > 90% du temps passé à évaluer le modèle se trouve dans ces appels de multiplication matricielle.
En mode non GGML, la gestion des mathématiques / tableau ici utilise la caisse ndarray . Il fournit une fonction .dot , mais cela ne calculera jamais réellement une multiplication de vecteur matriciel en parallèle même si la caisse prétend le support de filetage. Parce que ce calcul est si critique pour les performances, j'ai fini par écrire ma propre fonction pour diviser le calcul en morceaux et l'exécuter en parallèle. Voir les fonctions du module dumdot dans smolrwkv/src/util.rs
Le fait que vous obteniez une liste de probabilités en arrière et et aucune «réponse» définie du modèle ne semble être un contre-argument décent à l'idée que les LLM sont ou pourraient être conscients d'une manière ou d'une autre. Lorsque vous regardez la sortie d'un LLM, la plupart du temps, vous ne verrez même pas le jeton le plus probable. En outre, un fait amusant: lorsque vous alimentez une invite à un modèle, il figure avec une liste de probabilités comme lorsque vous la demandez pour une réponse. Cependant, ces probabilités sont simplement jetées, sauf pour le résultat après le traitement du tout dernier jeton rapide.
Invite en gras. Alors, les dragons sont-ils des serpents ou des chiens? Le monde ne le saura peut-être jamais!
* Loading tokenizer from: ./20B_tokenizer.json
* Loading model from: ./RWKV-4-Pile-430M-20220808-8066.safetensors
* Discovering model structure.
- Loading layer 1/24
[...]
- Loading layer 24/24
* Loading non-layer tensors.
* Loaded: layers=24, embed=1024, vocab=50277
Dans une découverte choquante, le scientifique a découvert un troupeau de dragons vivant dans une vallée éloignée et inattendue, au Tibet. Encore plus surprenant pour les chercheurs était le fait que les Dragons parlaient un chinois parfait.
Ces dragons parlaient tous des dialectes différents et ces dialectes ne correspondaient pas à la langue maternelle des chiens.
Dans une tentative de déchiffrer ce que ces dragons ont dit, ils ont appelé les dragons et ont constaté que leur langue était différente de l'homme.
"Les dragons ont compris les mots humains et plus précisément les langues humaines. Les dragons ont parlé le langage humain. Ils ont également compris les règles pour le chinois", a déclaré l'équipe de recherche à Mongabay.
En menant la recherche, ils espèrent faire la lumière sur l'histoire mystérieuse des dragons dans les régions éloignées et éloignées du monde, en particulier au Tibet.
Le projet de recherche, publié dans la revue Open Science, montre également que les dragons sont en fait des reptiles ou des serpents d'arbres alias.
Dragon, pas de serpent
Selon l'équipe de recherche, les dragons trouvés dans le Tibet sont une race de chiens, pas un reptile.
Bien que l'équipe de recherche ne soit toujours pas en mesure de trouver des explications sur les raisons pour lesquelles ces dragons vivent au Tibet, on croyait auparavant qu'ils étaient probablement présents sur des terres près du plateau tibétain.
"Les dragons y vivent dans le cadre du grand plateau de Qinghai-Tibet qui n'est presque pas perturbé et l'ensemble du plateau de Qinghai-Tibet a été progressivement converti en un état agricole. Par conséquent, ils ont un schéma distinctif de mastication sur les arbres, et probablement les animaux ne sont pas trop grands pour être maintenus dans la nature", expliquent les chercheurs.