תכנות בסיוע AI עם CodeGemma ו-KerasNLP

להצגה ב-ai.google.dev הפעלה ב-Google Colab הצגת המקור ב-GitHub

סקירה כללית

CodeGemma הוא וריאציה של Gemma שמותאמת למשימות תכנות. המדריך הזה מבוסס על המדריך למתחילים של Keeras CodeGemma ומציג דרכים נוספות שבהן CodeGemma יכול לעזור לכם במשימות התכנות.

הגדרה

קבלת גישה ל-CodeGemma

כדי להשלים את המדריך הזה, תצטרכו קודם לבצע את הוראות ההגדרה בהגדרה של Gemma. בהוראות ההגדרה של Gemma מוסבר איך לבצע את הפעולות הבאות:

  • ניתן לקבל גישה ל-Gemma בכתובת kaggle.com.
  • צריך לבחור זמן ריצה של Colab עם מספיק משאבים כדי להריץ את המודל Gemma 7B.
  • יצירה והגדרה של שם משתמש ומפתח API ב-Kaggle.

אחרי שמשלימים את ההגדרה של Gemma, עוברים לקטע הבא, שבו מגדירים משתני סביבה לסביבת Colab.

בחירת סביבת זמן הריצה

כדי להריץ את דגמי CodeGemma 7B, צריך תוכנית בתשלום של Colab Pro שמספקת סביבת זמן ריצה עם GPU A100.

  1. בפינה השמאלית העליונה של חלון Colab, לוחצים על ▾ (אפשרויות חיבור נוספות).
  2. בוחרים באפשרות Change runtime type (שינוי הסוג של סביבת זמן הריצה).
  3. בקטע Hardware Aacclerator, בוחרים באפשרות A100 GPU.

הגדרה של מפתח ה-API

כדי להשתמש ב-Gemma, עליך לציין את שם המשתמש שלך ב-Kaggle ומפתח Kaggle API.

כדי ליצור מפתח Kaggle API, עוברים לכרטיסייה Account בפרופיל המשתמש ב-Kaggle ובוחרים באפשרות Create New Token. הפעולה הזו תגרום להורדה של קובץ kaggle.json שמכיל את פרטי הכניסה ל-API.

ב-Colab, בוחרים באפשרות Secrets (🔑) בחלונית הימנית ומוסיפים את שם המשתמש של Kaggle ואת מפתח ה-API של Kaggle. שומרים את שם המשתמש בשם KAGGLE_USERNAME ואת מפתח ה-API בשם KAGGLE_KEY.

הגדרה של משתני סביבה

הגדרה של משתני סביבה בשביל KAGGLE_USERNAME ו-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')

יחסי תלות בהתקנות

pip install -q -U keras-nlp

בחירת קצה עורפי

Keras הוא ממשק API ללמידה עמוקה (Deras) ברמה גבוהה של כמה פריימים, שנועד לפשט וקלות שימוש. באמצעות Keras 3, אתם יכולים להריץ תהליכי עבודה באחד משלושה קצוות עורפיים: TensorFlow, JAX או PyTorch.

במדריך הזה צריך להגדיר את הקצה העורפי של JAX.

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

ייבוא חבילות

לייבא את Keras ו-KerasNLP.

import keras_nlp
import keras

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

דוגמאות למודל של CodeGemma 7B

בקטע הזה מפורטות דוגמאות לשימוש במודל 7B CodeGemma שעבר אימון מראש כדי לעזור במשימות תכנות.

טעינת המודל

חברת KerasNLP מספקת הטמעות של כל שלוש הווריאנטים של CodeGemma (2B ו-7B שעברו אימון מראש (PT) ו-7B מכווננים להוראה (IT)) באמצעות GemmaCausalLM, מודל Gemma מקצה לקצה לבניית מודלים של שפה סיבתיים. מודל שפה סיבתי חוזה את האסימון הבא על סמך אסימונים קודמים.

בדוגמה הזו, טוענים את המודל code_gemma_7b_en באמצעות ה-method 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()

ה-method from_preset מייצרת את המודל מתוך ארכיטקטורה ומשקולות מוגדרות מראש.

השלמת קוד באמצעות FIM מרובה שורות

המודלים של PT CodeGemma מאומנים לפי משימות של מילוי קוד. בקטע הזה מוצגות דוגמאות שמשתמשות באפשרות המילוי האוטומטי (FIM) של CodeGemma למילוי אוטומטי של קוד במיקום הסמן שצוין על סמך ההקשר שמסביב.

בשלב הראשון, צריך להגדיר קבועים ופונקציית עזר לעיצוב הנחיות.

# 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}"

דוגמה 1 – הוספת תנאי חסר

הקוד לדוגמה שלמטה ליצירת רצף פיבונאצ'י לא יפעל כראוי אם 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)

בהנחה שהסמן נמצא בתחילת שורה 4 (כאשר התנאי else נמצא), התוכן שלפני ואחרי הסמן הוא:

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|>

מריצים את ההנחיה.

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|>

המודל מכניס את הפונקציה elif הנכונה עבור n=1 במיקום של הסמן.

דוגמה 2 – אלגוריתם מעבר מלא של DFS

השלמה אוטומטית של קוד לאלגוריתם למעבר בעצים בחיפוש מעמיק (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|>

מריצים את ההנחיה.

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|>

יצירת קוד

בנוסף למילוי קוד, המודל CodeGemma 7B PT מאומן גם על קורפוסים של שפה טבעית. תוכלו להשתמש בהנחיה הזו כדי להנחות את המודל ליצור קוד.

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
}

דוגמאות למודלים של IT 7B

בקטע הזה אנחנו משתמשים במודל CodeGemma 7B Instruction-Tuned כדי לבצע משימות תכנות מתקדמות יותר. מודל ה-IT של CodeGemma 7B נגזר ממודל CodeGemma 7B PT באמצעות כוונון עדין של קוד יחד עם למידה מבוקרת עם משוב אנושי. הקטע הזה עוסק בדוגמאות לשימוש במודל הזה ליצירת אפליקציות פתוחות.

טעינת מודל ה-IT

טוענים את המודל code_gemma_instruct_7b_en באמצעות ה-method 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]

מודלים של IT מאומנים באמצעות מעצב ספציפי, שיוצר הערות עם מידע נוסף לכל הדוגמאות לכוונון של הוראות, כדי לציין תפקידים ולתאר פניות בשיחה.

בשלב הראשון, צריך להגדיר קבועים ופונקציית עזר לעיצוב הנחיות.

# 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"

תרגום קוד

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"""

מעצבים את ההנחיה.

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

מריצים את ההנחיה.

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`.

זיהוי נקודות חולשה בקוד

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"""

מעצבים את ההנחיה.

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.

המודל מזהה נקודת חולשה פוטנציאלית בקוד ומספק שינויים בקוד כדי לצמצם אותה.

סיכום

המדריך הזה הסביר לכם איך להשתמש ב-CodeGemma למגוון משימות תכנות. למידע נוסף על CodeGemma: