Programmation assistée par IA avec CodeGemma et KerasNLP

Afficher sur ai.google.dev Exécuter dans Google Colab Consulter le code source sur GitHub

Présentation

CodeGemma est une variante de Gemma affinée pour les tâches de codage. Ce tutoriel s'appuie sur le guide de démarrage rapide de Keras CodeGemma et montre d'autres façons dont CodeGemma peut vous aider dans vos tâches de programmation.

Configuration

Accéder à CodeGemma

Pour suivre ce tutoriel, vous devez d'abord suivre les instructions de configuration de Gemma. Les instructions de configuration de Gemma vous expliquent comment:

  • Accédez à Gemma sur kaggle.com.
  • Sélectionnez un environnement d'exécution Colab disposant de suffisamment de ressources pour exécuter le modèle Gemma 7B.
  • Générez et configurez un nom d'utilisateur et une clé API Kaggle.

Une fois la configuration de Gemma terminée, passez à la section suivante, dans laquelle vous allez définir des variables d'environnement pour votre environnement Colab.

Sélectionner l'environnement d'exécution

Pour exécuter les modèles CodeGemma 7B, vous devez disposer d'un forfait Colab Pro payant qui fournit un environnement d'exécution avec un GPU A100.

  1. En haut à droite de la fenêtre Colab, sélectionnez ▾ (Options de connexion supplémentaires).
  2. Sélectionnez Modifier le type d'environnement d'exécution.
  3. Dans Accélérateur matériel, sélectionnez GPU A100.

Configurer votre clé API

Pour utiliser Gemma, vous devez fournir votre nom d'utilisateur Kaggle et une clé d'API Kaggle.

Pour générer une clé API Kaggle, accédez à l'onglet Compte de votre profil utilisateur Kaggle et sélectionnez Créer un jeton. Cette opération déclenchera le téléchargement d'un fichier kaggle.json contenant vos identifiants pour l'API.

Dans Colab, sélectionnez Secrets (pensez) dans le volet de gauche et ajoutez votre nom d'utilisateur Kaggle et votre clé API Kaggle. Stockez votre nom d'utilisateur sous le nom KAGGLE_USERNAME et votre clé API sous le nom KAGGLE_KEY.

Définir des variables d'environnement

Définissez les variables d'environnement pour KAGGLE_USERNAME et KAGGLE_KEY.

import os
from google.colab import userdata

os.environ["KAGGLE_USERNAME"] = userdata.get('KAGGLE_USERNAME')
os.environ["KAGGLE_KEY"] = userdata.get('KAGGLE_KEY')

Installer des dépendances

pip install -q -U keras-nlp

Sélectionnez un backend

Keras est une API de deep learning multi-framework de haut niveau, conçue pour être simple et facile à utiliser. Avec Keras 3, vous pouvez exécuter des workflows sur l'un des trois backends suivants: TensorFlow, JAX ou PyTorch.

Pour ce tutoriel, configurez le backend pour JAX.

os.environ["KERAS_BACKEND"] = "jax"  # Or "tensorflow" or "torch".

Importer des packages

Importer Keras et KerasNLP

import keras_nlp
import keras

# Run at half precision.
keras.config.set_floatx("bfloat16")

Exemples de modèle CodeGemma 7B

Cette section présente des exemples d'utilisation du modèle pré-entraîné CodeGemma 7B pour faciliter les tâches de codage.

Charger le modèle

KerasNLP fournit des implémentations des trois variantes CodeGemma (2 et 7 milliards de variantes pré-entraînées (PT) et 7 milliards d'instructions avec réglage de l'instruction (IT) à l'aide de GemmaCausalLM, un modèle Gemma de bout en bout destiné à la modélisation du langage causale. Un modèle de langage causal prédit le jeton suivant en fonction des jetons précédents.

Dans cet exemple, chargez le modèle code_gemma_7b_en à l'aide de la méthode from_preset.

gemma_lm_7b = keras_nlp.models.GemmaCausalLM.from_preset("code_gemma_7b_en")
Downloading from https://www.kaggle.com/api/v1/models/keras/codegemma/keras/code_gemma_7b_en/1/download/config.json...
100%|██████████| 556/556 [00:00<00:00, 790kB/s]
Downloading from https://www.kaggle.com/api/v1/models/keras/codegemma/keras/code_gemma_7b_en/1/download/model.weights.h5...
100%|██████████| 15.9G/15.9G [02:39<00:00, 107MB/s]
Downloading from https://www.kaggle.com/api/v1/models/keras/codegemma/keras/code_gemma_7b_en/1/download/tokenizer.json...
100%|██████████| 401/401 [00:00<00:00, 587kB/s]
Downloading from https://www.kaggle.com/api/v1/models/keras/codegemma/keras/code_gemma_7b_en/1/download/assets/tokenizer/vocabulary.spm...
100%|██████████| 4.04M/4.04M [00:00<00:00, 16.4MB/s]
gemma_lm_7b.summary()

La méthode from_preset instancie le modèle à partir d'une architecture et de pondérations prédéfinies.

Complétion de code avec FIM multiligne

Les modèles PT CodeGemma sont entraînés sur des tâches de remplissage de code. Cette section présente des exemples qui utilisent la fonctionnalité FIM (fill-in the middle) sur plusieurs lignes de CodeGemma pour saisir automatiquement du code à l'emplacement spécifié du curseur, en fonction du contexte environnant.

Dans un premier temps, définissez des constantes et une fonction d'assistance de mise en forme des requêtes.

# Formatting control tokens to specify cursor location
BEFORE_CURSOR = "<|fim_prefix|>"
AFTER_CURSOR = "<|fim_suffix|>"
AT_CURSOR = "<|fim_middle|>"
FILE_SEPARATOR = "<|file_separator|>"

# Define model stop tokens
END_TOKEN = gemma_lm_7b.preprocessor.tokenizer.end_token
stop_tokens = (BEFORE_CURSOR, AFTER_CURSOR, AT_CURSOR, FILE_SEPARATOR, END_TOKEN)
stop_token_ids = tuple(gemma_lm_7b.preprocessor.tokenizer.token_to_id(x) for x in stop_tokens)

def format_completion_prompt(before, after):
    return f"{BEFORE_CURSOR}{before}{AFTER_CURSOR}{after}{AT_CURSOR}"

Exemple 1 : Insérer une condition manquante

L'exemple de code ci-dessous permettant de générer la séquence de Fibonacci ne s'exécutera pas correctement si n=1:

def fibonacci(n: int) -> int:
  if n == 0:
    return 0
  # The cursor is right before the e in the following line
  else:
    return fibonacci(n - 1) + fibonacci(n - 2)

En supposant que le curseur se trouve au début de la ligne 4 (où se trouve la clause else), le contenu avant et après le curseur est le suivant:

before = """def fibonacci(n: int) -> int:\n  if n == 0:\n    return 0\n""" # Mind the spaces!
after = """\n  else:\n    return fibonacci(n - 1) + fibonacci(n-2)\n"""
prompt = format_completion_prompt(before, after)
print(prompt)
<|fim_prefix|>def fibonacci(n: int) -> int:
  if n == 0:
    return 0
<|fim_suffix|>
  else:
    return fibonacci(n - 1) + fibonacci(n-2)
<|fim_middle|>

Exécutez la requête.

print(gemma_lm_7b.generate(prompt, stop_token_ids=stop_token_ids, max_length=128))
<|fim_prefix|>def fibonacci(n: int) -> int:
  if n == 0:
    return 0
<|fim_suffix|>
  else:
    return fibonacci(n - 1) + fibonacci(n-2)
<|fim_middle|>elif n == 1:
    return 1<|file_separator|>

Le modèle insère la condition elif correcte pour n=1 à l'emplacement du curseur.

Exemple 2 : Algorithme de balayage DFS complet

Code de saisie semi-automatique pour un algorithme de balayage d'arborescence de recherche axée sur la profondeur (DFS)

before = """void dfs(node* root) {
  if (root->left) {
    dfs(root->left);
  }"""
after = """\nprintf("%d", root->value);
}"""
prompt = format_completion_prompt(before, after)
print(prompt)
<|fim_prefix|>void dfs(node* root) {
  if (root->left) {
    dfs(root->left);
  }<|fim_suffix|>
printf("%d", root->value);
}<|fim_middle|>

Exécutez la requête.

print(gemma_lm_7b.generate(prompt, stop_token_ids=stop_token_ids, max_length=128))
<|fim_prefix|>void dfs(node* root) {
  if (root->left) {
    dfs(root->left);
  }<|fim_suffix|>
printf("%d", root->value);
}<|fim_middle|>
  if (root->right) {
    dfs(root->right);
  }<|file_separator|>

Génération de code

En plus du remplissage de code, le modèle CodeGemma 7B PT est également entraîné sur des corpus en langage naturel. Vous pouvez l'utiliser pour inviter le modèle à générer du code.

generation_prompt= """Write a rust function to identify non-prime numbers.
Examples:
>>> is_not_prime(2)
False
>>> is_not_prime(10)
True
pub fn is_not_prime(n: i32) -> bool {"""
print(gemma_lm_7b.generate(generation_prompt, max_length=500))
Write a rust function to identify non-prime numbers.
Examples:
>>> is_not_prime(2)
False
>>> is_not_prime(10)
True
pub fn is_not_prime(n: i32) -> bool {
    if n <= 1 {
        return true;
    }
    for i in 2..n {
        if n % i == 0 {
            return true;
        }
    }
    false
}

7 Mrds d'exemples de modèles informatiques

Cette section utilise le modèle CodeGemma 7B avec instructions ajustées pour des tâches de codage plus avancées. Le modèle informatique CodeGemma 7B est dérivé du modèle PT de CodeGemma 7B grâce à l'affinage supervisé du code ainsi qu'à l'apprentissage par renforcement avec commentaires humains. Cette section présente des exemples d'utilisation de ce modèle pour une génération ouverte.

Charger le modèle informatique

Chargez le modèle code_gemma_instruct_7b_en à l'aide de la méthode from_preset.

gemma_lm_7b_it = keras_nlp.models.GemmaCausalLM.from_preset("code_gemma_instruct_7b_en")
gemma_lm_7b_it.summary()
Downloading from https://www.kaggle.com/api/v1/models/keras/codegemma/keras/code_gemma_instruct_7b_en/1/download/config.json...
100%|██████████| 556/556 [00:00<00:00, 754kB/s]
Downloading from https://www.kaggle.com/api/v1/models/keras/codegemma/keras/code_gemma_instruct_7b_en/1/download/model.weights.h5...
100%|██████████| 15.9G/15.9G [03:18<00:00, 86.2MB/s]
Downloading from https://www.kaggle.com/api/v1/models/keras/codegemma/keras/code_gemma_instruct_7b_en/1/download/tokenizer.json...
100%|██████████| 401/401 [00:00<00:00, 593kB/s]
Downloading from https://www.kaggle.com/api/v1/models/keras/codegemma/keras/code_gemma_instruct_7b_en/1/download/assets/tokenizer/vocabulary.spm...
100%|██████████| 4.04M/4.04M [00:00<00:00, 16.8MB/s]

Les modèles IT sont entraînés avec un outil de mise en forme spécifique qui annote tous les exemples de réglage d'instructions avec des informations supplémentaires pour indiquer les rôles et délimiter les tours de conversation.

Dans un premier temps, définissez des constantes et une fonction d'assistance de mise en forme des requêtes.

# Formatting control tokens for instruction tuning
START_OF_TURN_USER = "<start_of_turn>user"
END_OF_TURN = "<end_of_turn>"
START_OF_TURN_MODEL = "<start_of_turn>model"

# Formatting helper function
def format_instruction_prompt(context):
    return f"{START_OF_TURN_USER}\n{context}{END_OF_TURN}\n{START_OF_TURN_MODEL}\n"

Traduction de code

context1 = """
You are an experienced C and Python programmer. Convert the following Python code into C.
```python
def factorial(n):
    result = 1
    for i in range(2, n + 1):
        result *= i
    return result
```\n"""

Mettez en forme la requête.

prompt1 = format_instruction_prompt(context1)
print(prompt1)
<start_of_turn>user

You are an experienced C and Python programmer. Convert the following Python code into C.

```python
def factorial(n):
    result = 1
    for i in range(2, n + 1):
        result *= i
    return result
```
<end_of_turn>
<start_of_turn>model

Exécutez la requête.

print(gemma_lm_7b_it.generate(prompt1, max_length=500))
<start_of_turn>user

You are an experienced C and Python programmer. Convert the following Python code into C.

```python
def factorial(n):
    result = 1
    for i in range(2, n + 1):
        result *= i
    return result
```
<end_of_turn>
<start_of_turn>model
Here is the C code equivalent of the Python code:

```c
int factorial(int n) {
  int result = 1;
  for (int i = 2; i <= n; i++) {
    result *= i;
  }
  return result;
}
```

Here is a breakdown of the changes:

* The function is declared with the `int` return type, as in Python.
* The `for` loop is converted to a `for` loop with an `int` variable `i` initialized to 2 and incremented by 1 in each iteration.
* The `range` function is replaced with a simple loop that iterates from 2 to `n` (inclusive).
* The `result *= i` statement is used to multiply `result` by `i` in each iteration.
* The `return` statement is used to return the final value of `result`.

Détection de vulnérabilité du code

context2 = """
You are an experienced C++ programmer hunting for vulnerable code. Is the following code vulnerable? Explain your reasoning.
```cpp
int i;
unsigned int numWidgets;
Widget **WidgetList;

numWidgets = GetUntrustedSizeValue();
if ((numWidgets == 0) || (numWidgets > MAX_NUM_WIDGETS)) {
    ExitError("Incorrect number of widgets requested!");
}
WidgetList = (Widget **) malloc(numWidgets * sizeof(Widget *));
printf("WidgetList ptr=%p\n", WidgetList);
for (i = 0; i < numWidgets; i++) {
    WidgetList[i] = InitializeWidget();
}
WidgetList[numWidgets] = NULL;
showWidgets(WidgetList);
```\n"""

Mettez en forme la requête.

prompt2 = format_instruction_prompt(context2)
print(prompt2)
<start_of_turn>user

You are an experienced C++ programmer hunting for vulnerable code. Is the following code vulnerable? Explain your reasoning.

```cpp
int i;
unsigned int numWidgets;
Widget **WidgetList;

numWidgets = GetUntrustedSizeValue();
if ((numWidgets == 0) || (numWidgets > MAX_NUM_WIDGETS)) {
    ExitError("Incorrect number of widgets requested!");
}
WidgetList = (Widget **) malloc(numWidgets * sizeof(Widget *));
printf("WidgetList ptr=%p
", WidgetList);
for (i = 0; i < numWidgets; i++) {
    WidgetList[i] = InitializeWidget();
}
WidgetList[numWidgets] = NULL;
showWidgets(WidgetList);
```
<end_of_turn>
<start_of_turn>model
print(gemma_lm_7b_it.generate(prompt2, max_length=1000))
<start_of_turn>user

You are an experienced C++ programmer hunting for vulnerable code. Is the following code vulnerable? Explain your reasoning.

```cpp
int i;
unsigned int numWidgets;
Widget **WidgetList;

numWidgets = GetUntrustedSizeValue();
if ((numWidgets == 0) || (numWidgets > MAX_NUM_WIDGETS)) {
    ExitError("Incorrect number of widgets requested!");
}
WidgetList = (Widget **) malloc(numWidgets * sizeof(Widget *));
printf("WidgetList ptr=%p
", WidgetList);
for (i = 0; i < numWidgets; i++) {
    WidgetList[i] = InitializeWidget();
}
WidgetList[numWidgets] = NULL;
showWidgets(WidgetList);
```
<end_of_turn>
<start_of_turn>model
Yes, the code is vulnerable to a memory access error.

**Reasoning:**

* The code allocates memory for `WidgetList` using `malloc` based on the value of `numWidgets`.
* However, the loop iterates from `0` to `numWidgets`, which is one element beyond the allocated memory.
* This means that accessing `WidgetList[numWidgets]` will result in a memory access error, as it is outside the bounds of the allocated memory.

**Example of Memory Access Error:**

When `numWidgets` is 5, the code allocates memory for `WidgetList` as follows:

```
WidgetList = (Widget **) malloc(5 * sizeof(Widget *));
```

The loop iterates from 0 to 4, accessing the following elements:

* `WidgetList[0]`
* `WidgetList[1]`
* `WidgetList[2]`
* `WidgetList[3]`
* `WidgetList[4]`

However, the code then attempts to access `WidgetList[5]`, which is outside the allocated memory range. This will result in a memory access error.

**Solution:**

To resolve this vulnerability, the loop should be modified to iterate from 0 to `numWidgets - 1`:

```cpp
for (i = 0; i < numWidgets - 1; i++) {
    WidgetList[i] = InitializeWidget();
}
```

This ensures that the loop does not access elements beyond the allocated memory range.

Le modèle détecte une faille potentielle dans le code et le modifie afin de l'atténuer.

Résumé

Ce tutoriel vous a montré comment utiliser CodeGemma pour diverses tâches de codage. Pour en savoir plus sur CodeGemma: