Bezserwerowa AI: osadzanie Gemmy w Cloud Run

1. Wprowadzenie

Z tego ćwiczenia w Codelabs dowiesz się, jak wdrożyć EmbeddingGemma, zaawansowany wielojęzyczny model wektora dystrybucyjnego tekstu, w Cloud Run przy użyciu procesorów GPU. Następnie użyjesz wdrożonej usługi do generowania wektorów dystrybucyjnych na potrzeby aplikacji do wyszukiwania semantycznego.

W przeciwieństwie do tradycyjnych dużych modeli językowych (LLM), które generują tekst, modele wektorów dystrybucyjnych przekształcają tekst w wektory liczbowe. Te wektory są kluczowe w przypadku tworzenia systemów generowania wspomaganego wyszukiwaniem (RAG), które umożliwiają znajdowanie najbardziej trafnych dokumentów w odpowiedzi na zapytanie użytkownika.

Jakie zadania wykonasz

  • Umieść model EmbeddingGemma w kontenerze za pomocą Ollamy.
  • Wdróż kontener w Cloud Run z akceleracją GPU.
  • Przetestuj wdrożony model, generując wektory dystrybucyjne dla przykładowego tekstu.
  • Utwórz lekki system wyszukiwania semantycznego za pomocą wdrożonej usługi.

Czego potrzebujesz

  • Projekt Google Cloud z włączonymi płatnościami.
  • Podstawowa znajomość Dockera i wiersza poleceń.

2. Zanim zaczniesz

Konfiguracja projektu

  1. Jeśli nie masz jeszcze konta Google, musisz je utworzyć.
    • Używaj konta osobistego zamiast konta służbowego lub szkolnego. Konta służbowe i szkolne mogą mieć ograniczenia, które uniemożliwiają włączenie interfejsów API potrzebnych do tego ćwiczenia.
  2. Zaloguj się w konsoli Google Cloud.
  3. Włącz płatności w konsoli Google Cloud.
  4. Utwórz nowy projekt lub użyj już istniejącego.
    • Jeśli zobaczysz błąd dotyczący limitu projektu, użyj ponownie istniejącego projektu lub usuń go, aby utworzyć nowy.

Uruchamianie Cloud Shell

Cloud Shell to środowisko wiersza poleceń działające w Google Cloud, które zawiera niezbędne narzędzia.

  1. Kliknij Aktywuj Cloud Shell u góry konsoli Google Cloud.
  2. Po połączeniu z Cloud Shell sprawdź uwierzytelnianie:
    gcloud auth list
    
  3. Sprawdź, czy projekt jest wybrany:
    gcloud config get project
    
  4. W razie potrzeby ustaw:
    gcloud config set project <YOUR_PROJECT_ID>
    

Włącz interfejsy API

Aby włączyć wszystkie wymagane interfejsy API, uruchom to polecenie:

gcloud services enable \
  run.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com

3. Konteneryzacja modelu

Aby uruchomić EmbeddingGemma bezserwerowo, musimy spakować go w kontenerze. Użyjemy Ollamy, lekkiej platformy do uruchamiania LLM, i Dockera.

Tworzenie pliku Dockerfile

W Cloud Shell utwórz nowy katalog dla swojego projektu i przejdź do niego:

mkdir embedding-gemma-codelab
cd embedding-gemma-codelab

Utwórz plik o nazwie Dockerfile z tą zawartością:

FROM ollama/ollama:latest

# Listen on all interfaces, port 8080
ENV OLLAMA_HOST=0.0.0.0:8080

# Store model weight files in /models
ENV OLLAMA_MODELS=/models

# Reduce logging verbosity
ENV OLLAMA_DEBUG=false

# Never unload model weights from the GPU
ENV OLLAMA_KEEP_ALIVE=-1

# Store the model weights in the container image
ENV MODEL=embeddinggemma:latest
RUN ollama serve & sleep 5 && ollama pull $MODEL

# Start Ollama
ENTRYPOINT ["ollama", "serve"]

Ten plik Dockerfile wykonuje te czynności:

  1. Zaczyna się od oficjalnego obrazu podstawowego Ollamy.
  2. Konfiguruje Ollamę tak, aby nasłuchiwała na porcie 8080 (domyślnym porcie Cloud Run).
  3. Polecenie RUN uruchamia ollama serwer i pobiera embeddinggemma model podczas procesu kompilacji, dzięki czemu jest on wbudowany w obraz.
  4. Ustawia OLLAMA_KEEP_ALIVE=-1, aby model był wczytany do pamięci GPU i można było szybciej obsługiwać kolejne żądania.

4. Kompilacja i wdrożenie

Do utworzenia i wdrożenia kontenera w jednym kroku użyjemy wdrożenia źródła w Cloud Run. To polecenie tworzy obraz za pomocą Cloud Build, zapisuje go w Artifact Registry i wdraża w Cloud Run.

Aby wdrożyć aplikację, uruchom to polecenie:

gcloud run deploy embedding-gemma \
  --source . \
  --region europe-west1 \
  --concurrency 4 \
  --cpu 8 \
  --set-env-vars OLLAMA_NUM_PARALLEL=4 \
  --gpu 1 \
  --gpu-type nvidia-l4 \
  --max-instances 1 \
  --memory 32Gi \
  --no-allow-unauthenticated \
  --no-cpu-throttling \
  --no-gpu-zonal-redundancy \
  --timeout=600 \
  --labels dev-tutorial=codelab-embedding-gemma

Informacje o konfiguracji

  • --source . określa bieżący katalog jako źródło kompilacji.
  • --region europe-west1 używamy regionu, który obsługuje GPU w Cloud Run.
  • --concurrency 4 jest ustawiona tak, aby pasowała do wartości zmiennej środowiskowej OLLAMA_NUM_PARALLEL.
  • --gpu 1--gpu-type nvidia-l4 przypisuje 1 GPU NVIDIA L4 do każdej instancji Cloud Run w usłudze.
  • --max-instances 1 określa maksymalną liczbę instancji, do której ma nastąpić skalowanie. Musi być równa limitowi GPU NVIDIA L4 w Twoim projekcie lub od niego mniejsza.
  • --no-allow-unauthenticated ogranicza dostęp do usługi bez uwierzytelniania. Utrzymując prywatność usługi, możesz polegać na wbudowanym uwierzytelnianiu Identity and Access Management (IAM) w Cloud Run w przypadku komunikacji między usługami.
  • Do włączenia GPU wymagana jest --no-cpu-throttling.
  • --no-gpu-zonal-redundancy skonfiguruj opcje nadmiarowości strefowej w zależności od wymagań dotyczących przełączania awaryjnego strefy i dostępnego limitu.

Kwestie dotyczące regionu

Procesory graficzne w Cloud Run są dostępne w określonych regionach. Listę obsługiwanych regionów znajdziesz w dokumentacji.

Dane wyjściowe wdrożenia

Po kilku minutach wdrożenie zostanie zakończone i zobaczysz komunikat podobny do tego:

Service [embedding-gemma] revision [embedding-gemma-12345-abc] has been deployed and is serving 100 percent of traffic.
Service URL: https://embedding-gemma-123456789012.europe-west1.run.app

5. Testowanie wdrożenia

Usługę wdrożyliśmy za pomocą --no-allow-unauthenticated, więc nie możemy po prostu curl publicznego adresu URL. Najpierw musimy przyznać sobie uprawnienia dostępu do usługi i użyć tokena uwierzytelniania w żądaniu.

  1. Przyznaj kontu użytkownika uprawnienia do wywoływania usługi:
    gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
        --member=user:$(gcloud config get-value account) \
        --role='roles/run.invoker'
    
  2. Zapisz dane logowania do Google Cloud i numer projektu w zmiennych środowiskowych, aby użyć ich w żądaniu:
    export PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format="value(projectNumber)")
    export ID_TOKEN=$(gcloud auth print-identity-token)
    
  3. Aby wygenerować osadzanie dla tekstu „Sample text”, uruchom to polecenie:
    curl -X POST "https://embedding-gemma-$PROJECT_NUMBER.europe-west1.run.app/api/embed" \
        -H "Authorization: Bearer $ID_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{
            "model": "embeddinggemma",
            "input": "Sample text"
        }'
    

Powinna pojawić się odpowiedź JSON zawierająca wektor (długą listę liczb) w polu embedding. Potwierdza to, że model osadzania bezserwerowego z obsługą GPU działa.

Odpowiedź będzie wyglądać mniej więcej tak: Dane wyjściowe polecenia curl EmbeddingGemma

Klient Python

Możesz też używać Pythona do interakcji z usługą. Utwórz plik o nazwie test_client.py:

import urllib.request
import urllib.parse
import json
import os

# 1. Setup the URL and Payload
url = f"https://embedding-gemma-{os.environ['PROJECT_NUMBER']}.europe-west1.run.app/api/embed"
payload = {
    "model": "embeddinggemma",
    "input": "Sample text"
}

# 2. Create the Request object
# Note: Providing 'data' automatically makes this a POST request
req = urllib.request.Request(
    url,
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {os.environ['ID_TOKEN']}",
        "Content-Type": "application/json"
    }
)

# 3. Execute and print the response
response = urllib.request.urlopen(req)
result = json.loads(response.read().decode("utf-8"))
print(result)

Stosuj:

python test_client.py

6. Tworzenie aplikacji do wyszukiwania semantycznego

Mamy już działającą usługę osadzania, więc teraz zbudujmy prostą aplikację wyszukiwania semantycznego. Wygenerowane wektory osadzeń wykorzystamy do znalezienia najbardziej odpowiedniego dokumentu dla danego zapytania.

Zależności

Jako bazy danych wektorowych użyjemy chromadb, a jako biblioteki klienta – ollama.

uv init semantic-search --description "Semantic Search Application"
cd semantic-search
uv add chromadb ollama

Tworzenie aplikacji do wyszukiwania

Utwórz plik o nazwie semantic_search.py z tym kodem:

import ollama
import chromadb
import os

# 1. Define our knowledge base
documents = [
    "Poland is a country located in Central Europe.",
    "The capital and largest city of Poland is Warsaw.",
    "Poland's official language is Polish, which is a West Slavic language.",
    "Marie Curie, the pioneering scientist who conducted groundbreaking research on radioactivity, was born in Warsaw, Poland.",
    "Poland is famous for its traditional dish called pierogi, which are filled dumplings.",
    "The Białowieża Forest in Poland is one of the last and largest remaining parts of the immense primeval forest that once stretched across the European Plain.",
]

print("Initializing Vector Database...")
client = chromadb.Client()
collection = client.create_collection(name="docs")

# Configure the client to point to our Cloud Run proxy
ollama_client = ollama.Client(
    host=f"https://embedding-gemma-{os.environ['PROJECT_NUMBER']}.europe-west1.run.app",
    headers={'Authorization': 'Bearer ' + os.environ['ID_TOKEN']}
)

print("Generating embeddings and indexing documents...")
# 2. Store each document in the vector database
for i, d in enumerate(documents):
    # This calls our Cloud Run service to get the embedding
    response = ollama_client.embed(model="embeddinggemma", input=d)
    embeddings = response["embeddings"]
    collection.add(ids=[str(i)], embeddings=embeddings, documents=[d])

print("Indexing complete.\n")

# 3. Perform a Semantic Search
question = "What is Poland's official language?"
print(f"Query: {question}")

# Generate an embedding for the question
response = ollama_client.embed(model="embeddinggemma", input=question)

# Query the database for the most similar document
results = collection.query(
    query_embeddings=[response["embeddings"][0]],
    n_results=1
)

best_match = results["documents"][0][0]
print(f"Best Match Document: {best_match}")

Uruchamianie aplikacji

Uruchom skrypt:

uv run semantic_search.py

Dane wyjściowe powinny być podobne do tych:

Initializing Vector Database...
Generating embeddings and indexing documents...
Indexing complete.

Query: What is Poland's official language?
Best Match Document: Poland's official language is Polish, which is a West Slavic language.

Ten skrypt przedstawia podstawy systemu RAG: używa usługi EmbeddingGemma bezserwerowej do przekształcania dokumentów i zapytań w wektory, co umożliwia znajdowanie dokładnych informacji potrzebnych do udzielenia odpowiedzi na pytanie użytkownika.

7. Czyszczenie danych

Aby uniknąć obciążenia konta Google Cloud bieżącymi opłatami, usuń zasoby utworzone podczas tego ćwiczenia.

Usuwanie usługi Cloud Run

gcloud run services delete embedding-gemma --region europe-west1 --quiet

Usuwanie obrazu kontenera

gcloud artifacts docker images delete \
    europe-west1-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/cloud-run-source-deploy/embedding-gemma \
    --quiet

8. Gratulacje

Gratulacje! Udało Ci się wdrożyć model EmbeddingGemma w usłudze Cloud Run z GPU i użyć go do obsługi aplikacji do wyszukiwania semantycznego.

Masz teraz skalowalną, bezserwerową platformę do tworzenia aplikacji AI, które wymagają rozumienia znaczenia tekstu.

Czego się dowiedziałeś(-aś)

  • Jak umieścić model Ollama w kontenerze za pomocą Dockera.
  • Jak wdrożyć usługę z obsługą GPU w Cloud Run.
  • Jak używać wdrożonego modelu do wyszukiwania semantycznego (RAG).

Dalsze kroki

Dokumentacja