22 août 2023

Pourquoi les modèles d'API d'OpenAI ne peuvent pas être forcés à se comporter de manière déterministe

Les contributeurs
Michiel De Koninck
Ingénieur en apprentissage automatique et spécialiste du LLM
Aucun élément trouvé.
S'abonner à la newsletter
Partager cet article

Un âne ne se cogne jamais deux fois la tête sur la même pierre. Il semble que ChatGPT et les ânes aient ceci en commun : ChatGPT n'est pas un imbécile, il ne fait pas toujours la même erreur. Au lieu de cela, il fait une erreur différente à chaque fois. C'est bien ? Essayez de le forcer à faire toujours la même erreur. Mais ce n'est pas possible.

Nous observons que les modèles génératifs d'OpenAI (DALL-E, ChatGPT, ...) ne peuvent pas être contrôlés pour agir de manière déterministe. En d'autres termes, ils produisent des résultats incohérents même lorsque leur température, le paramètre qui contrôle leur "créativité", est réglée sur zéro. Vous trouverez plus d'informations sur la température dans la documentation de l'API OpenAI. Plutôt que de proposer des solutions, cet article de blog vise à expliquer ce comportement. Parce que la connaissance, c'est le pouvoir, n'est-ce pas ?

Mais quelle est la pertinence de cette question ? Cette question revient-elle régulièrement ? Apparemment, oui. Elle est actuellement en tête de la liste des FAQ de l'OpenAI GPT.

Capture d'écran de https://platform.openai.com/docs/guides/gpt/faq

Il est probable que, si vous construisez une application qui s'appuie sur les résultats d'un modèle GPT, vous souhaitiez (au moins dans une phase de test) que le modèle se comporte de manière déterministe afin de pouvoir compter sur un comportement reproductible dans une certaine mesure. A titre d'exemple :

  • (Generative Language) GPT context :
    vous voulez montrer à votre patron que votre "générateur de code Angular guidé" délivre le bon blob de code pour une certaine requête 100% du temps.
  • (Vision générative) Contexte de DALL-E :
    vous voulez montrer à vos collègues que votre message-guide produit exactement la même image merveilleuse de "jus de fruit dans un bol de soupe" qu'hier, lorsque vous travailliez sur le marketing du stand de limonade de votre fils.

Mais trêve d'introduction, parce que nous apprécions à la fois la structure et la démesure, nous allons maintenant analyser le comportement des modèles GPT en vous faisant passer de l'observation à la compréhension , puis à l'explication . Nous devons entrer dans un niveau de détail assez élevé, mais grâce à une visualisation simplifiée, nous espérons vous épargner la nécessité de connaître des concepts mathématiques compliqués. Vive les simplifications !

L'observation

Pour résumer à nouveau, la question à laquelle nous allons répondre ici se résume à :

"pourquoi je n'obtiens pas systématiquement les mêmes réponses lors d'un appel à n'importe quelle API générative OpenAI lorsque la température est de 0 ?

Il convient de noter que, bien que des discussions et des ressources pertinentes sur ce sujet puissent être trouvées ;

Nous avons estimé qu'aucune de ces explications n'était satisfaisante pour apporter une réponse complète et exhaustive. C'est une bonne nouvelle car cela nous permet de combler le vide et de vous offrir cette douce satisfaction.

La compréhension

"Il est difficile de comprendre exactement le fonctionnement d'un système à boîte noire. Mais si la boîte noire a évolué à partir de boîtes qui étaient, vous savez, moins noires et plus transparentes, alors certaines hypothèses de qualité peuvent encore être faites."
- quelqu'un, à un moment donné, peut-être

Comme le montre clairement la fausse citation ci-dessus, nous ne pouvons pas savoir exactement comment les LLM (Large Language Models) ChatGPT ou GPT-4 fonctionnent sous le capot (par exemple, l 'article GPT-4). Mais grâce à leurs prédécesseurs plus transparents (par exemple l'article GPT-2 [2] et le code source ouvert) et à leurs concurrents à code source ouvert (par exemple l'article LLaMA), nous connaissons l'essentiel de l'architecture basée sur les transformateurs.

Ci-dessous, nous nous concentrons sur les parties de l'architecture que nous estimons les plus pertinentes pour l'observation des comportements non déterministes. N'hésitez pas à parcourir rapidement cette partie et à passer à la section "Explication" si vous connaissez les bases de l'architecture des modèles génératifs textuels.

Comment le modèle fournit-il le texte de sortie ?

Premièrement, un passage de l'inférence vers l'avant à travers le réseau LLM fournit un seul "jeton" qui représente "l'unité de texte la plus granulaire que le modèle comprend" (peut être considéré comme une syllabe, mais peut tout aussi bien représenter un mot dans son ensemble). Pour des raisons d'interprétabilité, nous considérons que chaque "jeton" de notre histoire représente un mot entier. Cette simplification n'a aucune incidence sur les conclusions ultérieures.

Représentation simplifiée d'un passage à travers un réseau LLM

Ceci étant dit, un réseau LLM possède un vocabulaire de tokens et grâce à sa compréhension apparemment magique du texte d'entrée, il est très doué pour indiquer quels tokens de son vocabulaire sont les plus susceptibles de suivre l'entrée donnée. Cette indication se fait par l'attribution de probabilités (voir le dessin ci-dessus). Par exemple, P(token_0) représente la probabilité estimée que le mot mâcher (représenté par le token_0) suive la séquence donnée de tokens d'entrée ( la vache est ...). La probabilité du mot bowling (représenté par le token_50.256) est, espérons-le, plus faible que celle du mot chewing ou grazing dans ce contexte.

Nous soulignons que, pour qu'un LLM génère une séquence complète de texte, il doit passer itérativement à travers le réseau plusieurs fois : chaque passage avant sert à sélectionner exactement un jeton qui, à son tour, contribue à déterminer le jeton suivant.

La pièce cruciale du puzzle vient maintenant de la façon dont un seul jeton de sortie est choisi à l'aide de cette liste de probabilités de jetons. Ci-dessous, nous plongerons brièvement dans le monde de l'échantillonnage. Pour une compréhension plus approfondie des méthodes d'échantillonnage, nous vous recommandons de lire cet article du blog Hugging Face.

Échantillonnage de haut niveau : Échantillonnage de haut niveau

Les LLM modernes utilisent (une variante de) l'échantillonnage top-p (c'est-à-dire l'échantillonnage du noyau introduit dans cet article de 2018) pour échantillonner la réponse. Cette méthode ne prend en compte que les jetons dont la probabilité cumulée dépasse la probabilité p et redistribue ensuite la masse de probabilité sur les jetons restants de manière à ce que la somme des probabilités soit égale à 1. Si vous vous dites "attendez quoi", félicitations, vous n'êtes pas statisticien ! N'hésitez pas à relire cette phrase et à passer ensuite à l'explication visuelle plus compréhensible ci-dessous :

Exemple d'échantillonnage top-p (c'est-à-dire d'échantillonnage de noyaux) basé sur l'article du blog Hugging Face.

Supposons que nous ayons fixé p=0,92. Lors de la première passe à partir du seul mot "le", nous avons besoin de 6 jetons pour dépasser cette probabilité de 92 % (ils totalisent 94 %). Nous pouvons échantillonner un jeton parmi ces six mots en considérant leurs probabilités redistribuées (où le mot gentil aura la plus grande chance d'être choisi). Pour la passe suivante, nous constatons que les trois jetons les plus probables dépassent déjà facilement le seuil de 92 % et que le jeton éventuel est donc échantillonné à partir de ces trois jetons seulement.

Ce qui est intéressant, c'est que le nombre de jetons à échantillonner dépend dynamiquement du niveau d'"incertitude" du modèle. Si le modèle considère qu'un petit sous-ensemble de jetons est le plus pertinent (par exemple, parce que les jetons d'entrée se composent de "the", "car", ce qui restreint le contexte), il ne prélèvera que ces jetons.

Anecdote pertinente : il y a quelques années, à ML6, nous avons créé un "résumé des conditionsgénérales" de base qui, par hasard, a généré le mot "lait" dans un résumé et s'est complètement orienté vers la nourriture, simplement parce qu'il n'utilisait pas l'échantillonnage de noyaux.

Alors, que contrôle la température ?

Nous comprenons maintenant à quoi se réfère le paramètre top_p de la documentation de l'API OpenAI. Notez que par défaut, ce paramètre fixe p=100%, ce qui signifie que tous les tokens de sortie sont pris en compte. Si par contre p=0%, le premier token à être vérifié, qui est algorithmiquement celui avec la plus grande probabilité, sera toujours choisi car il dépasse immédiatement le seuil super bas par lui-même.

D'accord, mais quel rôle joue le paramètre de la température ?

Imaginez que vous disposiez d'un ensemble de jetons de sortie (éventuellement avec une probabilité redistribuée si vous jouez avec le paramètre top_p) à échantillonner :

Échantillonnage des jetons de sortie réels en fonction des jetons restants en jeu

Le rôle de la température est de contrôler les poids relatifs dans la distribution des probabilités. Elle contrôle la mesure dans laquelle les différences de probabilité jouent un rôle dans l'échantillonnage. Prenons l'exemple ci-dessus : pour la séquence d'entrée de jetons "Le", nous nous attendrions (par défaut) à ce que le mot "beau" ait 75 % de chances d'être choisi P("beau")=75 %. C'est ce qui se produit à la température t=1. Ce paramètre peut être choisi entre 0 et 2.

À la température t=0, cette technique d'échantillonnage se transforme en ce que nous appelons la recherche avide/l'échantillonnageargmax , où le jeton ayant la probabilité la plus élevée est toujours sélectionné (ici : P("nice")=100% ).

À la température t=2, la différence entre les jetons les plus probables et les moins probables est réduite au moment de l'échantillonnage. Pour l'exemple de gauche ci-dessus, cela se traduirait par : P("gentil")=58% , P("chien")=32% , P("voiture")=10%.

Pour les personnes intéressées, la formule permettant de calculer les probabilités d'échantillonnage affectées par la température t est ajoutée ci-dessous (où K représente le nombre total de jetons pris en compte dans l'échantillonnage) :

Conclusion : finis les secrets

Vous pouvez maintenant vous considérer comme un véritable guerrier des API génératives de l'OpenAI car l'extrait ci-dessous n'a plus de secrets pour vous.
Notez la phrase "we generally recommend altering this or top_p/temperature but not both" (nous recommandons généralement de modifier ceci ou top_p/temperature mais pas les deux) . Il s'agit simplement d'une suggestion visant à rendre vos modifications plus ou moins interprétables au fur et à mesure que vous jouez avec les valeurs. Le fait de fixer l'une ou l'autre de ces valeurs à leurs limites déterministes (c'est-à-dire p=0 ou top_p=0) a le même effet.

Extrait de la documentation de l'API de complétion de l'OpenAI

Rappelons que : par défaut, l'échantillonnage se fait sur l'ensemble du vocabulaire des jetons (top_p=1) et la distribution de probabilité n'est pas affectée (temperature=1).

L'explication

En utilisant les connaissances ci-dessus, nous savons exactement ce qui devrait se passer si nous fixons température=0, à savoir : le jeton ayant la probabilité la plus élevée sera choisi.

Mais que se passe-t-il si, à un moment donné de la génération, la foudre frappe et que deux jetons se voient attribuer exactement la même probabilité ?

Coup de foudre ⚡ : on rencontre deux jetons qui ont exactement la même probabilité.

La foudre peut frapper : un risque non nul

Le cas où au moins deux jetons ont la même probabilité peut sembler improbable, mais certains facteurs contribuent à cette probabilité qui n'est pas si faible :

  • Incertitude du modèle: dans les cas où le modèle est loin d'être certain qu'un jeton est le choix idéal pour le jeton suivant, la probabilité que les jetons qui font partie des favoris aient des probabilités similaires est plus élevée.
  • Précision limitée: la probabilité que deux probabilités soient exactement identiques diminue avec le nombre de bits utilisés pour les représenter. Si vous avez : 1 bit = 2 nombres possibles, 2 bits = 4 nombres possibles, 8 bits = 256 nombres possibles. Si vous ne disposez que de 8 bits pour représenter un nombre dans un réseau (qui est le résultat d'une somme de multiplications à partir d'autres nombres de 8 bits), la probabilité que les résultats soient exactement les mêmes est plus grande que lorsque vous disposez de 32 bits à chaque étape du processus. Nous notons que si des choix de quantification sont faits pour optimiser la vitesse et le coût de l'inférence, la précision des nombres représentés est encore plus limitée.
  • Passes avant Bonanza: avec le nombre de jetons généralement nécessaires pour construire une réponse pertinente, la probabilité que la foudre frappe augmente régulièrement. Si Pᵢ est la probabilité que cela se produise pour une passe avant, la probabilité totale que cela se produise est la somme de cette probabilité pour toutes les passes avant nécessaires pour générer la réponse totale. Pour simplifier, imaginons que la probabilité que la foudre frappe soit fixée à P_i=0,0001%, alors pour une réponse qui nécessite 200 passages dans le réseau, la probabilité que la foudre frappe la totalité de la réponse serait de P=0,02%, ce qui n'est pas si négligeable.

Si un autre jeton est sélectionné une fois, la probabilité de suivre les passes suivantes sera directement affectée, ce qui se traduira par un "chemin de réponse" différent. Vous pouvez imaginer que si le mot "lait" est sélectionné une seule fois, le résumé des conditions générales sera complètement différent par la suite.

D'accord, la foudre peut frapper. Mais que se passe-t-il si elle frappe ?

La question suivante est donc évidente : si deux jetons ont exactement la même probabilité, que se passe-t-il ensuite ?

Lorsque les ordinateurs doivent faire un choix entre plusieurs options également valables/probables, le pouvoir décisif du "pile ou face" est confié à une graine. La graine contrôle la valeur d'un générateur de nombres pseudo-aléatoires. Une simplification plus intuitive est présentée ci-dessous.

Illustration de la façon dont une graine peut être imaginée pour influencer des décisions apparemment aléatoires.

Nous nous attendons donc à ce que ces graines affectent la manière dont les situations ⚡ sont traitées. Généralement, lorsque vous hébergez votre propre algorithme/modèle, vous pouvez fixer cette graine de sorte que les décisions aléatoires "à pile ou face" soient toujours les mêmes.

Pas de contrôle des semences, pas de contrôle des récoltes

Un agriculteur avisé aurait pu dire un jour :

si vous ne contrôlez pas ce que vous semez, comment pouvez-vous contrôler ce que vous récoltez ?"
- agriculteur hypothétique, probablement

Et mon Dieu, cet agriculteur hypothétique aurait mis le doigt sur le proverbial clou sur la tête. Si vous ne pouvez pas corriger la semence utilisée pour déterminer les décisions "à pile ou face" au sein de votre système, alors le contrôle total est inaccessible.

Pas de contrôle des semences, pas de déterminisme.

Nous avons donc expliqué pourquoi le déterminisme reste hors de portée lorsque l'on travaille avec les API les plus populaires de l'OpenAI.

Quod erat demonstrandum ?

Mais pourquoi ? Pourquoi les choses sont-elles ainsi ?

D'accord, donc des graines. C'est tout un programme. Ce n'est pas si surprenant. La question qui subsiste est la suivante :
Pourquoi ne pouvez-vous pas passer une graine ? Comme le dit ce type sur Twitter:

il est assez fou que l'API d'OpenAI n'ait pas de paramètre de "graine aléatoire". Le comportement attendu est d'obtenir des résultats que l'on ne peut jamais reproduire."
- Sasha Rush (professeur associé à Cornell Tech et chercheur sur les visages qui s'étreignent)

Examinons donc les raisons qui peuvent expliquer le choix de ne pas autoriser le passage d'une graine fixe :

  • Peut-être que la quantité de magie vaudou nécessaire pour définir une graine définie par l'utilisateur sur plusieurs GPU est tout simplement trop importante ?
    "Sparks of Artificial General Intelligence" (étincelles d'intelligence artificielle générale) : bien sûr.
    Mais "Glimmers of Deterministic Behaviour" (lueurs d'uncomportement déterministe) : rêvez (cue Aerosmith).
  • Peut-être que les différentes combinaisons de logiciels et de matériel (GPU) qui prennent en charge les calculs en virgule flottante introduisent un degré d'aléatoire qui ne peut pas être entièrement contrôlé en fixant la graine ? Par conséquent, le fait de donner à l'utilisateur la possibilité de fixer la graine susciterait de fausses attentes ?
  • Peut-être que nos amis d'OpenAI (et d'autres fournisseurs d'API) ne veulent pas que vous puissiez échantillonner de manière déterministe le comportement d'un modèle, car cela pourrait potentiellement vous donner plus de moyens de jeter un coup d'œil sous le capot des modèles à source fermée d'aujourd'hui ?

Enveloppez le tout

Au cours de ce voyage, nous avons offert des aperçus sur :

  • Comment les probabilités de jetons sont-elles fournies par un réseau LLM ?
  • Comment les jetons sont échantillonnés à chaque étape de la génération de réponses et comment la température et les paramètres top_p influencent cet échantillonnage.
  • Pourquoi les probabilités de jetons exactement égales ne sont pas aussi inhabituelles qu'on pourrait le penser
  • Le rôle que joue généralement une graine dans les situations de foudroiement

Pour des raisons qui ne nous sont pas immédiatement connues, OpenAI ne nous permet pas de définir la graine qui a un pouvoir concluant lorsque la foudre frappe pendant la génération par jetons d'une réponse.

Nous ne saurons peut-être pas pourquoi le déterminisme et donc la reproductibilité sont empêchés dans une certaine mesure. Mais au moins, le comportement est plus clair maintenant. Et cela nous rassure un peu.
N'est-ce pas ?

Postes connexes

Voir tout le contenu
Aucun résultat n'a été trouvé.
Il n'y a pas de résultats correspondant à ces critères. Essayez de modifier votre recherche.
Grand modèle linguistique
Modèles de fondation
Entreprise
Personnes
Données Structurées
Chat GPT
Durabilité
Voix et son
Développement frontal
Protection des données et sécurité
IA responsable/éthique
Infrastructure
Hardware et capteurs
MLOps
IA générative
Natural Language Processing
Vision par ordinateur