Mit Gemma einen Chatbot erstellen

Auf ai.google.dev ansehen In Google Colab ausführen In Vertex AI öffnen Quelle auf GitHub ansehen

Large Language Models (LLMs) wie Gemma liefern besonders informative Antworten und sind daher ideal für die Entwicklung von virtuellen Assistenten und Chatbots.

LLMs sind üblicherweise zustandslos, was bedeutet, dass ihnen ein inhärenter Speicher zum Speichern vergangener Unterhaltungen fehlt. Jede Aufforderung oder Frage wird unabhängig und ohne Berücksichtigung vorheriger Interaktionen verarbeitet. Ein entscheidender Aspekt eines natürlichen Gesprächs ist jedoch die Fähigkeit, den Kontext aus früheren Interaktionen zu bewahren. Um diese Einschränkung zu überwinden und LLMs zu ermöglichen, den Unterhaltungskontext aufrechtzuerhalten, müssen sie in jedem neuen Prompt, der dem LLM präsentiert wird, explizit relevante Informationen wie den Unterhaltungsverlauf (oder entsprechende Teile) erhalten.

In dieser Anleitung erfahren Sie, wie Sie einen Chatbot mit der auf Anweisungen abgestimmten Modellvariante von Gemma entwickeln.

Einrichtung

Gemma-Einrichtung

Um diese Anleitung abzuschließen, müssen Sie zuerst die Schritte unter Gemma-Einrichtung ausführen. In der Anleitung zur Einrichtung von Gemma erfahren Sie, wie Sie Folgendes tun können:

  • Auf kaggle.com erhältst du Zugriff auf Gemma.
  • Colab-Laufzeit mit ausreichenden Ressourcen zum Ausführen auswählen das Modell Gemma 2B.
  • Generieren und konfigurieren Sie einen Kaggle-Nutzernamen und einen API-Schlüssel.

Nachdem Sie die Gemma-Einrichtung abgeschlossen haben, fahren Sie mit dem nächsten Abschnitt fort. Dort legen Sie Umgebungsvariablen für Ihre Colab-Umgebung fest.

Umgebungsvariablen festlegen

Legen Sie Umgebungsvariablen für KAGGLE_USERNAME und KAGGLE_KEY fest.

import os
from google.colab import userdata

# Note: `userdata.get` is a Colab API. If you're not using Colab, set the env
# vars as appropriate for your system.
os.environ["KAGGLE_USERNAME"] = userdata.get('KAGGLE_USERNAME')
os.environ["KAGGLE_KEY"] = userdata.get('KAGGLE_KEY')

Abhängigkeiten installieren

Installieren Sie Keras und KerasNLP.

# Install Keras 3 last. See https://keras.io/getting_started/ for more details.
pip install -q tensorflow-cpu
pip install -q -U keras-nlp tensorflow-hub
pip install -q -U "keras>=3"
pip install -q -U tensorflow-text

Backend auswählen

Keras ist eine Deep-Learning-API auf hoher Ebene mit mehreren Frameworks, die auf einfache und nutzerfreundliche Weise entwickelt wurde. Mit Keras 3 können Sie das Back-End auswählen: TensorFlow, JAX oder PyTorch. Alle drei funktionieren in dieser Anleitung.

import os

# Select JAX as the backend
os.environ["KERAS_BACKEND"] = "jax"

# Pre-allocate 100% of TPU memory to minimize memory fragmentation
os.environ["XLA_PYTHON_CLIENT_MEM_FRACTION"] = "1.0"

Pakete importieren

Importieren Sie Keras und KerasNLP.

import keras
import keras_nlp

# for reproducibility
keras.utils.set_random_seed(42)

Modell instanziieren

KerasNLP bietet Implementierungen vieler beliebter Modellarchitekturen. In dieser Anleitung instanziieren Sie das Modell mit GemmaCausalLM, einem End-to-End-Gemma-Modell für kausale Language Models. Ein kausales Sprachmodell sagt das nächste Token basierend auf vorherigen Tokens voraus.

Instanziieren Sie das Modell mit der Methode from_preset:

gemma_lm = keras_nlp.models.GemmaCausalLM.from_preset("gemma2_instruct_2b_en")

Die Funktion GemmaCausalLM.from_preset() instanziiert das Modell aus einer voreingestellten Architektur und Gewichtungen. Im Code oben gibt der String "gemma2_instruct_2b_en" die Voreinstellung für das Modell Gemma 2 2B mit 2 Milliarden Parametern an. Gemma-Modelle mit den Parametern 7B, 9B und 27B sind ebenfalls verfügbar. Sie finden die Codestrings für Gemma-Modelle in der Liste der Modellvarianten auf Kaggle.

Verwenden Sie die Methode summary, um weitere Informationen zum Modell abzurufen:

gemma_lm.summary()

Wie Sie der Zusammenfassung entnehmen können, hat das Modell 2,6 Milliarden trainierbare Parameter.

Formatierungshilfefunktionen definieren

from IPython.display import Markdown
import textwrap

def display_chat(prompt, text):
  formatted_prompt = "<font size='+1' color='brown'>🙋‍♂️<blockquote>" + prompt + "</blockquote></font>"
  text = text.replace('•', '  *')
  text = textwrap.indent(text, '> ', predicate=lambda _: True)
  formatted_text = "<font size='+1' color='teal'>🤖\n\n" + text + "\n</font>"
  return Markdown(formatted_prompt+formatted_text)

def to_markdown(text):
  text = text.replace('•', '  *')
  return Markdown(textwrap.indent(text, '> ', predicate=lambda _: True))

Chatbot erstellen

Das auf die Gemma-Anweisung abgestimmte Modell „gemma2_instruct_2b_en“ wurde optimiert, um die folgenden Rundentokens zu verstehen:

<start_of_turn>user\n  ... <end_of_turn>\n
<start_of_turn>model\n ... <end_of_turn>\n

In dieser Anleitung werden diese Tokens verwendet, um den Chatbot zu erstellen. Weitere Informationen zu Gemma-Kontrolltokens finden Sie unter Formatierungs- und Systemanleitung.

Chatassistent zum Verwalten des Unterhaltungsstatus erstellen

class ChatState():
  """
  Manages the conversation history for a turn-based chatbot
  Follows the turn-based conversation guidelines for the Gemma family of models
  documented at https://ai.google.dev/gemma/docs/formatting
  """

  __START_TURN_USER__ = "<start_of_turn>user\n"
  __START_TURN_MODEL__ = "<start_of_turn>model\n"
  __END_TURN__ = "<end_of_turn>\n"

  def __init__(self, model, system=""):
    """
    Initializes the chat state.

    Args:
        model: The language model to use for generating responses.
        system: (Optional) System instructions or bot description.
    """
    self.model = model
    self.system = system
    self.history = []

  def add_to_history_as_user(self, message):
      """
      Adds a user message to the history with start/end turn markers.
      """
      self.history.append(self.__START_TURN_USER__ + message + self.__END_TURN__)

  def add_to_history_as_model(self, message):
      """
      Adds a model response to the history with start/end turn markers.
      """
      self.history.append(self.__START_TURN_MODEL__ + message)

  def get_history(self):
      """
      Returns the entire chat history as a single string.
      """
      return "".join([*self.history])

  def get_full_prompt(self):
    """
    Builds the prompt for the language model, including history and system description.
    """
    prompt = self.get_history() + self.__START_TURN_MODEL__
    if len(self.system)>0:
      prompt = self.system + "\n" + prompt
    return prompt

  def send_message(self, message):
    """
    Handles sending a user message and getting a model response.

    Args:
        message: The user's message.

    Returns:
        The model's response.
    """
    self.add_to_history_as_user(message)
    prompt = self.get_full_prompt()
    response = self.model.generate(prompt, max_length=2048)
    result = response.replace(prompt, "")  # Extract only the new response
    self.add_to_history_as_model(result)
    return result

Mit dem Modell chatten

Chatten Sie mit dem Modell.

chat = ChatState(gemma_lm)
message = "Tell me, in a few words,  how to compute all prime numbers up to 1000?"
display_chat(message, chat.send_message(message))

🙋‍♂️

Beschreiben Sie mir in wenigen Worten, wie man alle Primzahlen bis 1.000 berechnet.
🤖

Sieve von Eratosthenes.

Setzen Sie die Unterhaltung fort.

message = "Now in Python! No numpy, please!"
display_chat(message, chat.send_message(message))

🙋‍♂️

Jetzt in Python! Kein Schnickschnack, bitte!
🤖

def sieve_of_eratosthenes(n):
  """Returns a list of prime numbers up to n."""
  primes = [True] * (n + 1)
  primes[0] = primes[1] = False
  for i in range(2, int(n**0.5) + 1):
    if primes[i]:
      for j in range(i * i, n + 1, i):
        primes[j] = False
  return [i for i, is_prime in enumerate(primes) if is_prime]

primes = sieve_of_eratosthenes(1000)
print(primes)

Erklärung:

  1. Initialisierung:

    • primes = [True] * (n + 1): Erstellt eine Liste primes mit booleschen Werten, wobei zuerst davon ausgegangen wird, dass alle Zahlen Primzahlen sind.
    • primes[0] = primes[1] = False: Legt 0 und 1 als „nicht für Prime“ fest.
  2. Iteration:

    • for i in range(2, int(n**0.5) + 1):: Iteriert von 2 zur Quadratwurzel aus n. Wir müssen nur die Quadratwurzel prüfen, da jede zusammengesetzte Zahl einen Primfaktor haben muss, der kleiner oder gleich ihrer Quadratwurzel ist.
    • if primes[i]:: Wenn i als Prime gekennzeichnet ist: <ph type="x-smartling-placeholder">
        </ph>
      • for j in range(i * i, n + 1, i):: Kennzeichnet alle Vielfachen von i als nicht Prime.
  3. Ergebnis:

    • return [i for i, is_prime in enumerate(primes) if is_prime]: Erstellt eine Liste von Indexen, bei denen primes[i] „True“ ist und die Primzahlen darstellt.

Wenn du irgendetwas genauer erklären möchtest, kannst du dich jederzeit gern wieder an mich wenden.

message = "Thank you, it works! Can you explain the code in French?"
display_chat(message, chat.send_message(message))

🙋‍♂️

Vielen Dank, es funktioniert! Kannst du den Code auf Französisch erklären?
🤖

Bien sûr ! Voici une explication du code en français :

def sieve_of_eratosthenes(n):
  """Retourne une liste de nombres premiers jusqu'à n."""
  primes = [True] * (n + 1)
  primes[0] = primes[1] = False
  for i in range(2, int(n**0.5) + 1):
    if primes[i]:
      for j in range(i * i, n + 1, i):
        primes[j] = False
  return [i for i, is_prime in enumerate(primes) if is_prime]

primes = sieve_of_eratosthenes(1000)
print(primes)

Erläuterung:

  1. Initialisierung:

    • primes = [True] * (n + 1): Crée une liste primes de valeurs booléennes, initialement supposant que tous les nombres sont premiers.
    • primes[0] = primes[1] = False: Définit 0 et 1 für Nicht-Premieren.
  2. Iterationen:

    • for i in range(2, int(n**0.5) + 1):: Itère de 2 jusqu'à la racine carrée de n. Nous ne devons vérifier que jusqu'à la racine carrée car tout nombre composite doit avoir unfacteur premier inférieur ou égal à sa racine carrée.
    • if primes[i]:: Si i est considéré comme premier: <ph type="x-smartling-placeholder">
        </ph>
      • for j in range(i * i, n + 1, i):: Marquer tous les multiples de i würdigen Nicht-Premieren.
  3. Ergebnis:

    • return [i for i, is_prime in enumerate(primes) if is_prime]: Crée une liste des indices où primes[i] est vrai, représentant les nombres premiers.

N'hésitez pas à me demander si vous avez besoin d'une explication plus détaillée de quelque hat entschieden!

message = "Great! Now add those explanations as comments in the code."
display_chat(message, chat.send_message(message))

🙋‍♂️

Sehr gut! Fügen Sie diese Erklärungen jetzt als Kommentare in den Code ein.
🤖

def sieve_of_eratosthenes(n):
  """Retourne une liste de nombres premiers jusqu'à n."""
  # Initialise une liste de boolean avec True pour tous les nombres de 0 à n
  primes = [True] * (n + 1)
  # Définit 0 et 1 comme non-premiers
  primes[0] = primes[1] = False
  # Itère de 2 à la racine carrée de n
  for i in range(2, int(n**0.5) + 1):
    # Si i est considéré comme premier
    if primes[i]:
      # Itère sur tous les multiples de i
      for j in range(i * i, n + 1, i):
        # Définit les multiples de i comme non-premiers
        primes[j] = False
  # Retourne la liste des indices des nombres premiers
  return [i for i, is_prime in enumerate(primes) if is_prime]

primes = sieve_of_eratosthenes(1000)
print(primes)

Erläuterung:

  • Initialisierung: <ph type="x-smartling-placeholder">
      </ph>
    • primes = [True] * (n + 1): Crée une liste primes de valeurs booléennes, initialement supposant que tous les nombres sont premiers.
    • primes[0] = primes[1] = False: Définit 0 et 1 für Nicht-Premieren.
  • Iterationen: <ph type="x-smartling-placeholder">
      </ph>
    • for i in range(2, int(n**0.5) + 1):: Itère de 2 jusqu'à la racine carrée de n. Nous ne devons vérifier que jusqu'à la racine carrée car tout nombre composite doit avoir unfacteur premier inférieur ou égal à sa racine carrée.
    • if primes[i]:: Si i est considéré comme premier: <ph type="x-smartling-placeholder">
        </ph>
      • for j in range(i * i, n + 1, i):: Marquer tous les multiples de i würdigen Nicht-Premieren.
  • Ergebnis: <ph type="x-smartling-placeholder">
      </ph>
    • return [i for i, is_prime in enumerate(primes) if is_prime]: Crée une liste des indices où primes[i] est vrai, représentant les nombres premiers.

Testen Sie die generierte Antwort, indem Sie den generierten Code ausführen:

def sieve_of_eratosthenes(n):
  """Retourne une liste de nombres premiers jusqu'à n."""
  # Initialise une liste de boolean avec True pour tous les nombres de 0 à n
  primes = [True] * (n + 1)
  # Définit 0 et 1 comme non-premiers
  primes[0] = primes[1] = False
  # Itère de 2 à la racine carrée de n
  for i in range(2, int(n**0.5) + 1):
    # Si i est considéré comme premier
    if primes[i]:
      # Itère sur tous les multiples de i
      for j in range(i * i, n + 1, i):
        # Définit les multiples de i comme non-premiers
        primes[j] = False
  # Retourne la liste des indices des nombres premiers
  return [i for i, is_prime in enumerate(primes) if is_prime]

primes = sieve_of_eratosthenes(1000)
print(primes)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]

Verwenden Sie die Methode get_history, um zu sehen, wie der gesamte Kontext von der Klasse Chat beibehalten wurde.

print(chat.get_history())
<start_of_turn>user
Tell me, in a few words,  how to compute all prime numbers up to 1000?<end_of_turn>
<start_of_turn>model
**Sieve of Eratosthenes.** 
<end_of_turn><start_of_turn>user
Now in Python! No numpy, please!<end_of_turn>
<start_of_turn>model

```python
def sieve_of_eratosthenes(n):
  """Returns a list of prime numbers up to n."""
  primes = [True] * (n + 1)
  primes[0] = primes[1] = False
  for i in range(2, int(n**0.5) + 1):
    if primes[i]:
      for j in range(i * i, n + 1, i):
        primes[j] = False
  return [i for i, is_prime in enumerate(primes) if is_prime]

primes = sieve_of_eratosthenes(1000)
print(primes)
```

**Explanation:**

1. **Initialization:**
   - `primes = [True] * (n + 1)`: Creates a list `primes` of boolean values, initially assuming all numbers are prime.
   - `primes[0] = primes[1] = False`: Sets 0 and 1 as non-prime.

2. **Iteration:**
   - `for i in range(2, int(n**0.5) + 1):`: Iterates from 2 to the square root of `n`. We only need to check up to the square root because any composite number must have a prime factor less than or equal to its square root.
   - `if primes[i]:`: If `i` is marked as prime:
     - `for j in range(i * i, n + 1, i):`: Marks all multiples of `i` as non-prime.

3. **Result:**
   - `return [i for i, is_prime in enumerate(primes) if is_prime]`: Creates a list of indices where `primes[i]` is True, representing the prime numbers.


Let me know if you'd like a more detailed explanation of any part! 
<end_of_turn><start_of_turn>user
Thank you, it works! Can you explain the code in French?<end_of_turn>
<start_of_turn>model
Bien sûr ! Voici une explication du code en français :

```python
def sieve_of_eratosthenes(n):
  """Retourne une liste de nombres premiers jusqu'à n."""
  primes = [True] * (n + 1)
  primes[0] = primes[1] = False
  for i in range(2, int(n**0.5) + 1):
    if primes[i]:
      for j in range(i * i, n + 1, i):
        primes[j] = False
  return [i for i, is_prime in enumerate(primes) if is_prime]

primes = sieve_of_eratosthenes(1000)
print(primes)
```

**Explication:**

1. **Initialisation:**
   - `primes = [True] * (n + 1)`: Crée une liste `primes` de valeurs booléennes, initialement supposant que tous les nombres sont premiers.
   - `primes[0] = primes[1] = False`: Définit 0 et 1 comme non-premiers.

2. **Itération:**
   - `for i in range(2, int(n**0.5) + 1):`: Itère de 2 jusqu'à la racine carrée de `n`. Nous ne devons vérifier que jusqu'à la racine carrée car tout nombre composite doit avoir un facteur premier inférieur ou égal à sa racine carrée.
   - `if primes[i]:`: Si `i` est considéré comme premier:
     - `for j in range(i * i, n + 1, i):`:  Marquer tous les multiples de `i` comme non-premiers.

3. **Resultat:**
   - `return [i for i, is_prime in enumerate(primes) if is_prime]`: Crée une liste des indices où `primes[i]` est vrai, représentant les nombres premiers.


N'hésitez pas à me demander si vous avez besoin d'une explication plus détaillée de quelque chose! 
<end_of_turn><start_of_turn>user
Great! Now add those explanations as comments in the code.<end_of_turn>
<start_of_turn>model

```python
def sieve_of_eratosthenes(n):
  """Retourne une liste de nombres premiers jusqu'à n."""
  # Initialise une liste de boolean avec True pour tous les nombres de 0 à n
  primes = [True] * (n + 1)
  # Définit 0 et 1 comme non-premiers
  primes[0] = primes[1] = False
  # Itère de 2 à la racine carrée de n
  for i in range(2, int(n**0.5) + 1):
    # Si i est considéré comme premier
    if primes[i]:
      # Itère sur tous les multiples de i
      for j in range(i * i, n + 1, i):
        # Définit les multiples de i comme non-premiers
        primes[j] = False
  # Retourne la liste des indices des nombres premiers
  return [i for i, is_prime in enumerate(primes) if is_prime]

primes = sieve_of_eratosthenes(1000)
print(primes)
```

**Explication:**

* **Initialisation:**
    * `primes = [True] * (n + 1)`:  Crée une liste `primes` de valeurs booléennes, initialement supposant que tous les nombres sont premiers.
    * `primes[0] = primes[1] = False`: Définit 0 et 1 comme non-premiers.
* **Itération:**
    * `for i in range(2, int(n**0.5) + 1):`: Itère de 2 jusqu'à la racine carrée de `n`. Nous ne devons vérifier que jusqu'à la racine carrée car tout nombre composite doit avoir un facteur premier inférieur ou égal à sa racine carrée.
    * `if primes[i]:`: Si `i` est considéré comme premier:
        * `for j in range(i * i, n + 1, i):`:  Marquer tous les multiples de `i` comme non-premiers.
* **Resultat:**
    * `return [i for i, is_prime in enumerate(primes) if is_prime]`: Crée une liste des indices où `primes[i]` est vrai, représentant les nombres premiers. 



<end_of_turn>

Zusammenfassung und weitere Informationen

In dieser Anleitung haben Sie erfahren, wie Sie mit Keras auf JAX mit dem abgestimmten Modell Gemma 2B Instruction chatten.

In diesen Leitfäden und Tutorials erfahren Sie mehr über Gemma: