top of page

Vers des modèles capables de décrire le code

Nous avons construit un modèle de langage appelé Gemini qui peut lire le code et décrire ses fonctionnalités en langage naturel.  Notre modèle est capable dans une variété de langages de programmation et il est multilingue. Dans ce blog, nous détaillons ses capacités, ainsi que les impacts potentiels.

2 janvier 2022

Project Gemini (17).png

Nous avons construit un modèle de langage appelé Gemini qui a été formé sur environ 1 million de paires code-description. L'objectif de ce projet était simple : pouvons-nous activer des machines capables de décrire efficacement le code de la même manière qu'un humain ? Bien qu'il s'agisse d'une tâche ardue, les travaux récents sur les modèles de langage ont fait des progrès rapides, rendant les tâches auparavant difficiles beaucoup plus plausibles.

Cependant, la construction de modèles de langage pour le code informatique présente plus de défis que les modèles de langage traditionnels. Premièrement, la fiabilité des données est assez variable et moins abondante. En règle générale, les ensembles de données accessibles au public contiennent des exemples de code de qualité moyenne, mais ne contiennent pas de descriptions de qualité. Cela peut fausser négativement les performances d'un modèle au début, ce qui est difficile à réparer plus tard dans le cycle de développement. Deuxièmement, la réalisation de modèles de langage performants sur n'importe quelle tâche est devenue de plus en plus coûteuse. Il a été bien documenté que plus un modèle de langage est grand dans les paramètres entraînables, mieux il fonctionnera; Cependant, cette stratégie s'accompagne de coûts monétaires et de calcul plus élevés.

Un autre défi majeur lors de la construction d'un modèle de langage est la fiabilité du système dans son ensemble. Des travaux récents d'entreprises comme OpenAI, Google et Facebook ont montré que les modèles de langage, en particulier dans le domaine de la génération de langage, peuvent produire des résultats inattendus. Le contrôle des sorties générées est toujours un problème de recherche ouvert qui reste en grande partie à résoudre. 

Avec tous les défis mentionnés, les questions sont devenues plus claires pour construire un système réussi pour décrire le code :

1.Pouvons-nous conserver suffisamment d'échantillons de données contenant des descriptions de code de qualité avec le code d'origine ?
2. Comment pouvons-nous construire un modèle de langage plus petit qui maximise la cohérence et la fidélité en même temps ?

Construire un modèle pour décrire le code


Modèle
Pour répondre aux questions mentionnées ci-dessus, Gemini utilise l'architecture de transformateur popularisée. Nous avons constaté que la formation avec tout autre type d'architecture (par exemple : RNN ou GRU) ne donnait pas la même qualité qu'un transformateur. 

Tout d'abord, l'un des principaux objectifs de la construction d'un système de description de code consistait à ajouter de la cohérence aux sorties générées. Gemini utilise une structure d'encodeur-décodeur, contrairement à une structure de décodeur uniquement comme GPT-3. Nous avons constaté que l'utilisation d'une structure encodeur-décodeur améliorait considérablement la cohérence des descriptions générées par rapport à l'utilisation de décodeurs uniquement. 

Gemini se compose de 3 tailles de modèles différentes. Le principal se compose de 600 millions de paramètres, une version plus petite "Lambda" avec 230 millions de paramètres et "Plato" à 64 millions de paramètres. Cela peut surprendre certains lecteurs étant donné l'ampleur récente de projets comme GPT-3, qui utilisent jusqu'à 175 milliards de paramètres ; Cependant, nous avons constaté que davantage de paramètres ne donnaient de meilleures performances que lorsqu'ils étaient associés à un réglage fin. 


Base de données
Deuxièmement, trouver des données de qualité était un défi majeur. Notre objectif avec Gemini est de fournir des résumés naturels du code ; Puisqu'il n'y a pas d'ensembles de données publiquement disponibles d'échantillons de code avec une description correspondante naturelle, nous avons dû générer synthétiquement des échantillons. Nous avons commencé par pré-former Gemini sur 885 000 échantillons de paires code-docstring accessibles au public pendant 7 époques. Cela a donné à Gemini une bonne base pour comprendre la sémantique du code lors de la traduction vers l'anglais.

Pour atteindre le naturel, nous avons préparé manuellement 250 échantillons manuscrits avec leur extrait de code correspondant. Ces nouveaux points de données ont été utilisés pour affiner une nouvelle version de Gemini pour 2 époques, ce qui lui a donné la capacité d'écrire naturellement. Notre intention à ce stade était de laisser Gemini ré-annoter notre ensemble de données d'origine, cependant, il ne pouvait écrire qu'environ 3 descriptions sur 10 à un niveau satisfaisant. 

Avec l'aide humaine, Gemini a pu générer synthétiquement 700 descriptions, ce qui nous a permis d'affiner à nouveau le modèle. Cette fois, nous avons constaté que Gemini pouvait décrire le code beaucoup plus clairement. Nous avons répété ce cycle jusqu'à ce que Gemini produise 10 000 échantillons pour Python, Javascript, Go, PHP, Java et Ruby. 

Screenshot 2021-12-30 5.12.02 PM.png

La figure de gauche/ci-dessus est un exemple visuel de notre stratégie de génération de données synthétiques, essentielle pour atteindre les performances de Gemini. La génération de données synthétiques de qualité est également importante pour maintenir la sécurité du code de l'utilisateur. Parce que nous avons un pipeline de génération de données robuste, l'utilisation du code de l'utilisateur dans nos modèles n'est pas nécessaire. 

Nous avons constaté que les modèles n'avaient besoin d'être affinés que pour un maximum de 3 époques, quelle que soit la taille du modèle. À l'avenir, nous espérons n'exiger qu'une formation préalable ou une mise au point pour de nouvelles tâches.  

Échantillons

Les échantillons affichés à droite/ci-dessous sont issus de nos tests internes initiaux de nos trois modèles. Bien que ces échantillons soient triés sur le volet par nos soins, nous pensons qu'ils représentent avec précision les capacités de notre modèle. Pour améliorer les performances, nous utilisons une méthode de recherche "best of" lors de la génération des sorties. Nous constatons que tous les modèles fonctionnent mieux lorsqu'ils choisissent parmi les 3 ou 4 meilleurs résultats. De plus, nous avons constaté que la définition d'une pénalité de répétition de 2,5 à 3 donnait les meilleurs résultats lorsque l'on tentait de maximiser la cohérence. 

Remarque : n désigne le nombre "best of" (par exemple, prendre la meilleure solution parmi 3 sorties générées) et p désigne la pénalité de répétition.

Gémeaux (602 millions de paramètres)
n = 3  p = 3,0

Code d'entrée

demandes d'importation
importer json

def post_url(endpoint, json_data):
    r = requests.post(endpoint, data=json.dumps(json_data))
    return r.text

Description générée :

Ce programme envoie un POST à un point de terminaison donné et renvoie la réponse du serveur JSON.

Expériences

Lors de la construction de Gemini, nous avons eu de nombreuses questions sur ce qui affecterait le plus les performances. Nous considérons un modèle performant comme un modèle capable d'écrire des sorties cohérentes et sensibles qui couvrent la portée du code. Des travaux antérieurs dans ce domaine ont montré que des modèles de langage plus grands (en termes de paramètres) améliorent considérablement les performances, nous avons donc formé une variété de tailles de modèles.

Nos plus grandes questions étaient les suivantes :

 

  • Comment la taille du modèle affecterait-elle les sorties générées ?

  • La pré-formation + ajustement est-elle plus performante que la pré-formation stricte ?

  • Dans quelle mesure chaque modèle se généralise-t-il s'il est seulement pré-formé et affiné sur un langage de programmation ? (c'est-à-dire, un modèle formé uniquement sur Python peut-il être généralisé à Javascript, Java, etc.)

  • Pouvons-nous réduire le nombre d'époques d'entraînement nécessaires ?​

Résultats

Dans nos expériences, nous avons constaté que l'augmentation pure et simple du nombre de paramètres entraînables n'est pas corrélée à une augmentation d'une amélioration significative des performances. Au lieu de cela, nous constatons que la pré-formation et le réglage fin donnent les meilleures performances, ce qui a été bien documenté dans des travaux antérieurs.

À notre grande surprise, nous avons constaté que seul le réglage fin sur un langage de programmation est compétitif avec le réglage fin sur plusieurs langages de programmation. Notre théorie est que Gemini prête attention à la sémantique du code (c'est-à-dire les noms de variables, les noms de fonctions, les noms de bibliothèques, etc.) plutôt qu'à la mémorisation pure. Cela nous amène à croire qu'un modèle plus grand, pré-entraîné et affiné sur un large éventail d'un langage de programmation, pourrait dépasser considérablement la version actuelle de Gemini. 

Les modèles plus grands avaient un avantage dans le nombre d'époques d'entraînement nécessaires. De manière générale, Gemini n'avait besoin que d'environ 5 époques de pré-formation et de 1 à 2 époques de réglage fin pour obtenir des résultats de qualité. 

Code d'entrée

importer * en tant que tf depuis '@tensorflow/tfjs' ;

 

exporter la fonction asynchrone loadModel() {

   const modèle = attendre tf.loadModel('/model/model.json');

   retour modèle ;

}

 

exporter la fonction asynchrone startServer() {

    const modèle = attendre loadModel();

    serveur const = http.createServer(async (req, res) => {

 

    if (req.method === 'POST') {

      corps const = attendre req.json();

      entrée const = tf.tensor2d(body.input, [1, body.input.length]);

      const output = model.predict(input);

      res.json(output.dataSync());

    }

  });

 server.listen(8080);

}

Gemini (602 millions de paramètres, pré-entraînés + affinés)

Ce programme est utilisé pour créer un serveur pour le modèle. Il charge d'abord le modèle, puis démarre un écouteur sur le serveur.

Gemini (602 millions de paramètres, pré-formation sans réglage fin)

Charge un modèle TensorFlow et démarre le serveur pour les requêtes.

Gemini (1,4 milliard de paramètres, pré-formation sans réglage fin)

Ce programme fait ce qui suit : il charge d'abord le modèle. Ensuite, il démarre un écouteur de serveur. Enfin, il démarre un serveur pour les requêtes.

Gemini (662 millions de paramètres, pré-formation sur plusieurs langages, ajustement sur Python uniquement)

Cette fonction est utilisée pour démarrer un serveur pour le modèle. Il charge d'abord le modèle, puis il démarre le serveur.

Zones d'amélioration

Au cours de notre expérimentation, nous avons trouvé certaines lacunes communes de Gemini et des versions plus petites. La plupart des lacunes ont tendance à suivre des travaux antérieurs dans l'espace de modélisation du langage, qui étaient attendus à une certaine capacité. Certaines des lacunes sont les suivantes :
 

Les sorties sont parfoisaussi concis
Lorsqu'il est posé avec des entrées plus longues, Gemini résumera la description en 7 mots ou moins. Généralement, cela ne poserait pas de problème, mais nous constatons que ces descriptions manquent de clarté ou ne couvrent pas toute la portée de l'entrée. 

Les descriptions auront une répétabilité élevée dans certains scénarios 

Un problème courant dans la modélisation du langage est la répétition de mots ou de phrases. Nous constatons que Gemini adoptera ce comportement lorsqu'il sera posé dans un contexte mixte (c'est-à-dire : des entrées contenant différents types de code mélangés).

Gemini est coûteux en calcul à former

La pré-formation de Gemini pour 5 à 7 époques nécessite une journée complète à l'aide d'un GPU Nvidia A100. Cela peut être très long et coûteux pour une startup de le faire systématiquement.

Les ordures à l'entrée sont toujours égales aux ordures à la sortie

Bien que ce comportement ne soit pas surprenant, Gemini ne peut pas bien décrire le code s'il est mal écrit. Nous espérons que nous pourrons combler cet écart avec de nouvelles méthodes ou plus de données sur la route.

La fenêtre contextuelle de Gemini est une limitation

Dans sa version actuelle, Gemini ne peut accepter que 512 jetons en entrée. Cela peut être limitant dans certains scénarios où résumer des programmes complets.

Quelle est la prochaine étape pour Gemini ?

Nous pensons que Gemini pourrait être le début d'une technologie à fort impact, au point d'un changement potentiellement majeur dans la façon dont les logiciels sont développés. Les développeurs de logiciels aiment généralement écrire du code et créer de nouvelles technologies ; Cependant, de nombreuses parties du développement logiciel nuisent à l'écriture de code, comme la documentation, la révision et la correction de bogues. Un système comme Gemini pourrait être utilisé de diverses manières pour automatiser les parties non agréables d'un logiciel d'écriture et laisser plus de temps pour se concentrer de meilleures manières.

Nous sommes incroyablement excités de voir où Gemini pourrait être utilisé. Dans un avenir proche, nous prévoyons que Gemini sera utilisé des manières suivantes :

 

  • Intégration plus rapide pour les équipes logicielles de toute taille(c'est-à-dire augmenter la vitesse de compréhension de la base de code)

  • Automatisation des pratiques logicielles internes(ex. revue de code, correction de bogues, documentation, etc.)

  • Briser les barrières linguistiques pour les équipes multilingues/distantes

  • Les établissements d'enseignement(Augmenter la vitesse à laquelle les étudiants peuvent comprendre le code)

  • Projets Open Source(suppression de l'ambiguïté de la fonctionnalité)

 

Cependant, notre vision de Gemini va bien au-delà d'une simple explication du code. À l'avenir, nous souhaitons étendre les capacités de Gemini pour :

 

  • Rédiger des documents complets, tels que des README ou des rapports sur la fonctionnalité du code

  • Réponse à une question sur le code

  • Créer différents niveaux d'explication(par exemple, une description de code pour un développeur expérimenté peut être différente de celle d'un élève du secondaire)​​

  • Rédaction d'exemples de la façon dont le code pourrait être utilisé dans la pratique(c'est-à-dire: générer des exemples d'appels de fonction)

À partir de ces exemples, on peut commencer à imaginer la vaste portée d'un code général in-text out comme Gemini.

Philosophie de la version

En fin de compte, notre objectif est de distribuer la puissance de solides systèmes d'apprentissage automatique à autant d'individus, de groupes et d'organisations que possible. Parce que les systèmes comme Gemini sont actuellement coûteux à développer, nous le publions en tant que produit payant. Nous permettrons à quiconque d'accéder à Gemini (et à d'autres versions) via :

  • Un produit entièrement géré et sans code

  • API développeur

  • Licence de produit

  • Partenariats


Tout cela servira à financer la prochaine génération de nos moteurs. Dans les années à venir, nous prévoyons de rendre différentes versions de Gemini disponibles via open-source, car nous croyons fermement qu'il est important de permettre à quiconque d'utiliser notre technologie. Cependant, les délais ne sont pas garantis. 

bottom of page