Wykorzystanie potencjału Pythona w BigQuery dzięki zarządzanym funkcjom zdefiniowanym przez użytkownika

1. Wprowadzenie

SQL (Structured Query Language) to standardowy język używany w branży do analizy hurtowni danych. Wyrażanie złożonej logiki proceduralnej, obliczeń matematycznych, czyszczenia tekstu lub przepływów pracy związanych z przygotowywaniem danych do uczenia maszynowego w czystym SQL może być jednak bardzo trudne.

Gdy wymagane było złożone niestandardowe przetwarzanie w Pythonie, zespoły zajmujące się danymi zwykle wyodrębniały ogromne zbiory danych z BigQuery, przetwarzały je na zewnętrznych niestandardowych maszynach wirtualnych lub w klastrach i wczytywały wyniki z powrotem. Takie podejście powoduje duże opóźnienia w sieci, zwiększa ryzyko niezgodności z przepisami przez przenoszenie danych i generuje dodatkowe koszty związane z zarządzaniem infrastrukturą.

Zarządzane przez BigQuery funkcje zdefiniowane przez użytkownika (UDF) w Pythonie rozwiązują te problemy, ponieważ uruchamiają niestandardowy kod na zasobach bezserwerowych, które automatycznie skalują się do milionów wierszy. Google Cloud zarządza kompilacją, tworzeniem obrazów, stosowaniem poprawek zabezpieczeń i wykonywaniem kodu, co pozwala uruchamiać niestandardowe obliczenia bezpośrednio w miejscu przechowywania danych.

W tym ćwiczeniu utworzysz potok analizy i wstępnego przetwarzania tekstu na podstawie danych społeczności StackOverflow, przygotowując je do raportowania i uczenia maszynowego.

Wymagania wstępne

  • Projekt Google Cloud z włączonymi płatnościami.
  • Podstawowa wiedza na temat SQL, IAM i BigQuery.

Czego się nauczysz

  • Jak wywoływać wstępnie skompilowaną publiczną funkcję UDF w Pythonie w publicznym zbiorze danych, aby analizować rozkłady danych.
  • Jak wdrożyć własną niestandardową funkcję UDF w Pythonie za pomocą biblioteki beautifulsoup4 do czyszczenia danych nieustrukturyzowanych.
  • Jak skonfigurować połączenie z zasobem Cloud BigQuery, aby bezpiecznie pobierać zasoby uczenia maszynowego i wykonywać lokalną tokenizację ML za pomocą biblioteki Hugging Face Transformers z użyciem pamięci podręcznej kontenera.
  • Jak połączyć te kroki w jeden wydajny potok SQL.

2. Konfiguracja i wymagania

Uruchamianie Cloud Shell

Chociaż Google Cloud można obsługiwać zdalnie z laptopa, w tym ćwiczeniu będziesz używać Google Cloud Shell, czyli środowiska wiersza poleceń działającego w chmurze.

  1. Otwórz konsolę Google Cloud, a potem wybierz lub utwórz projekt w chmurze Google Cloud.
  2. ⚠️ Zapisz identyfikator projektu. Będziesz go używać w tym ćwiczeniu.

39b6a5563d69ccfb.png

  1. Otwórz Cloud Shell na nowej karcie: https://shell.cloud.google.com/.
  2. Jeśli pojawi się pytanie o autoryzację, kliknij Autoryzuj.
  3. Zastąp PROJECT_ID i wklej to polecenie w terminalu:
cat << 'EOF' > env.sh
#!/bin/bash
# env.sh: Environment variables for BigQuery Python UDFs codelab

# ⚠️ Replace 'YOUR_PROJECT_ID' with your actual Google Cloud Project ID
export PROJECT_ID="YOUR_PROJECT_ID"
export REGION="us"
export BQ_DATASET="python_udfs"
export BQ_RESOURCE_CONN="external_api_connection"
EOF

Zastosuj zmienne do aktywnej sesji:

source ./env.sh

Włączanie interfejsów API i tworzenie zbioru danych BigQuery

Włącz w projekcie niezbędne usługi Google Cloud i utwórz docelowy zbiór danych:

# Enable API Services
gcloud services enable \
  bigquery.googleapis.com \
  bigqueryconnection.googleapis.com --quiet

# Create BigQuery Dataset
bq mk --location=${REGION} --dataset ${PROJECT_ID}:${BQ_DATASET}

3. Analizowanie rozkładów danych za pomocą publicznej funkcji UDF w Pythonie

Zanim wdrożysz niestandardowy kod, warto zapoznać się ze zbiorem danych i odfiltrować szumy o niskiej jakości. W tym kroku przeanalizujesz pytania na StackOverflow, aby znaleźć aktywnych użytkowników i poznać statystyczny rozkład ich wyników.

Dlaczego warto użyć do tego funkcji UDF w Pythonie?

Obliczanie wielu dokładnych percentyli (np. 25., 50., 75. i 95.) w pogrupowanych tablicach danych jest złożone i wymaga dużych zasobów w czystym SQL. Standardowe funkcje analityczne standardowej wersji SQL, takie jak PERCENTILE_CONT, oczekują płaskich kolumn wierszy, a nie zagnieżdżonych tablic. Aby obliczyć dokładne percentyle wstępnie zagregowanych tablic w każdym wierszu, musisz napisać obszerne podzapytania, które rozpakowują, sortują i ponownie agregują dane dla każdego wskaźnika percentyla, co jest nieefektywne.

Używając NumPy, czyli wysoce zoptymalizowanej biblioteki naukowej Pythona w funkcji UDF, możesz obliczyć dokładne percentyle matematyczne w tablicy liczb za pomocą jednego wiersza kodu.

Wykonanie

Google Cloud hostuje kilka wstępnie skompilowanych publicznych funkcji UDF (kliknij kartę Procedury). Ponieważ BigQuery wymaga dokładnego dopasowania typów, użyjemy wyrażenia CTE (Common Table Expression), aby wstępnie zagregować dane i przekształcić tablice liczb całkowitych w tablice reprezentacji zmiennoprzecinkowej za pomocą wyrażenia UNNEST.

Uruchom to zapytanie w konsoli BigQuery Studio:

WITH raw_user_scores AS (
  -- 1. Pre-aggregate user scores into an array
  SELECT 
    owner_user_id, 
    ARRAY_AGG(score) AS scores
  FROM 
    `bigquery-public-data.stackoverflow.posts_questions`
  WHERE 
    owner_user_id IS NOT NULL
  GROUP BY 
    owner_user_id
  HAVING 
    ARRAY_LENGTH(scores) >= 5
  LIMIT 5
)
SELECT 
  owner_user_id,
  scores,
  -- 2. Cast arrays to FLOAT64 and call the public percentile Python UDF
  `bigquery-public-data.python_udfs.percentiles`(
    ARRAY(SELECT CAST(s AS FLOAT64) FROM UNNEST(scores) AS s), 
    [25.0, 50.0, 75.0, 95.0]
  ) AS score_percentiles
FROM 
  raw_user_scores;

Dzięki temu możesz od razu poznać wydajność użytkowników bez konieczności konfigurowania uprawnień ani pisania niestandardowego kodu w Pythonie.

Sprawdzanie wyników

Ponieważ to zapytanie zwraca zagnieżdżone typy tablic (scores i score_percentiles), domyślna tabela Wyniki w BigQuery Studio może wyświetlać spłaszczone lub obcięte dane wyjściowe, co utrudnia sprawdzanie elementów tablicy.

Aby wyświetlić uporządkowane, zagnieżdżone dane wyjściowe:

  1. W panelu wyników zapytania znajdź pasek kart (domyślnie jest to Wyniki).
  2. Kliknij kartę JSON.

Powinna się wyświetlić uporządkowana tablica JSON reprezentująca wiersze, podobna do tej:

[{
  "owner_user_id": "533463",
  "scores": ["0", "0", "-1", "0", "0", "2", "-1", "1", "0", "0", "-1", "0", "-3", "1", "1", "0", "1", "2", "3", "1", "0", "0", "1", "0", "0", "3", "6", "11", "0", "1", "0", "0", "3", "17", "0", "1", "1", "3", "5", "-2", "1", "-1", "-1", "2", "3", "0", "0", "0", "5", "0", "4", "0", "0", "0", "3", "3", "0", "140", "0", "1", "3", "0", "0", "-2", "-1", "0", "0", "2", "0", "9", "9", "0", "0", "1", "0", "0", "1", "-1", "0", "0", "0", "0"],
  "score_percentiles": ["0.0", "0.0", "1.75", "8.8500000000000085"]
}, {
  "owner_user_id": "13502536",
  "scores": ["0", "1", "0", "-5", "0", "1", "0", "1", "0", "0", "-2", "0", "1", "0", "1", "0", "0", "1", "0", "1", "0", "0"],
  "score_percentiles": ["0.0", "0.0", "1.0", "1.0"]
}, {
  "owner_user_id": "1170153",
  "scores": ["1", "0", "1", "0", "1", "0", "2", "0", "0", "0", "10", "5", "1", "0", "0", "2", "0", "2", "3", "-1", "1", "0", "1", "0", "0", "1", "0", "2", "0", "4", "0", "3", "0", "0", "2", "0", "0", "1", "0"],
  "score_percentiles": ["0.0", "0.0", "1.5", "4.1000000000000014"]
}, {
  "owner_user_id": "8558174",
  "scores": ["0", "0", "-1", "1", "2", "0"],
  "score_percentiles": ["0.0", "0.0", "0.75", "1.75"]
}, {
  "owner_user_id": "1073044",
  "scores": ["0", "1", "0", "0", "2", "2", "2", "1", "1", "1", "2", "1", "0", "2", "3", "1"],
  "score_percentiles": ["0.75", "1.0", "2.0", "2.25"]
}]

Interpretowanie danych wyjściowych

  • scores: pełna tablica surowych wyników pytań opublikowanych przez każdego unikalnego użytkownika.
  • score_percentiles: tablica zawierająca 4 obliczone wartości zmiennoprzecinkowe. Odpowiadają one dokładnie żądanym percentylom: [25th, 50th, 75th, and 95th]. Na przykład w przypadku użytkownika 533463 95. percentyl wyniku jego pytań wynosi około 8.85, co oznacza, że jego najlepsze pytania mają wysokie wyniki.

4. Natywne czyszczenie tekstu przez utworzenie niestandardowej funkcji UDF

Gdy zidentyfikujemy docelowych użytkowników, chcemy przeanalizować treść ich postów. Surowe posty na forum często zawierają jednak nieuporządkowane tagi i encje HTML. Musimy je usunąć, aby poprawić czytelność i zmniejszyć koszty modelu.

Aby zrozumieć, dlaczego jest to konieczne, najpierw sprawdźmy, jak wygląda surowa, niesformatowana treść posta na Stack Overflow. Uruchom to zapytanie w konsoli BigQuery Studio:

SELECT
  id,
  title,
  body AS raw_html_body
FROM
  `bigquery-public-data.stackoverflow.posts_questions`
  -- Check specific questions that we will use in our final pipeline
WHERE
  id IN (9, 17, 33969)
ORDER BY
  id ASC;

Jeśli przyjrzysz się danym wyjściowym, zobaczysz w tekście tagi formatowania, takie jak <p>, <b>, <code> i inne. Przetwarzanie ich bezpośrednio za pomocą tokenizatorów uczenia maszynowego spowoduje niepotrzebne szumy i sztucznie zwiększy koszty pozyskiwania tokenów.

Dlaczego warto użyć do tego funkcji UDF w Pythonie?

Niezawodne analizowanie kodu HTML za pomocą wyrażeń regularnych w czystym SQL jest trudne i podatne na błędy. Uruchamianie niezawodnej biblioteki Pythona, takiej jak beautifulsoup4, bezpośrednio w zapytaniach zapewnia niezawodny sposób usuwania tagów.

Aby wdrożyć trwałą funkcję clean_html w zbiorze danych, uruchom to zapytanie DDL:

CREATE OR REPLACE FUNCTION `YOUR_PROJECT_ID.python_udfs.clean_html`(html_content STRING)
RETURNS STRING
LANGUAGE python
OPTIONS (
  runtime_version = 'python-3.11',
  entry_point = 'strip_tags',
  packages = ['beautifulsoup4>=4.12.0']
) AS r'''
from bs4 import BeautifulSoup

def strip_tags(html_content):
    if not html_content:
        return ""
    soup = BeautifulSoup(html_content, "html.parser")
    return soup.get_text(separator=" ")
''';

Sprawdź dane wyjściowe funkcji za pomocą prostego zapytania:

SELECT `YOUR_PROJECT_ID.python_udfs.clean_html`('<p>Hello <b>world</b>!</p>') AS cleaned_text;

Powinien się wyświetlić tekst bez elementów HTML:

+----------------+
| cleaned_text   |
+----------------+
| Hello  world ! |
+----------------+

5. Bezpieczne integracje zewnętrzne i zaawansowane przetwarzanie ML

Gdy mamy już czysty tekst, musimy go przygotować do modeli uczenia maszynowego lub dużych modeli językowych (LLM), takich jak Gemma. LLM nie mogą bezpośrednio odczytywać surowego tekstu. Przetwarzają numeryczne identyfikatory tokenów.

Aby przekonwertować czysty tekst na tokeny, zaimportujemy bibliotekę transformers Hugging Face i wczytamy wstępnie wytrenowany tokenizator Google T5 bezpośrednio do bazy danych.

Tworzenie połączenia z zasobem Cloud

Aby nawiązać bezpieczne połączenie, uruchom to zapytanie w konsoli BigQuery Studio:

CREATE CONNECTION IF NOT EXISTS `YOUR_PROJECT_ID.us.external_api_connection`
OPTIONS (
  connection_type = "CLOUD_RESOURCE",
  friendly_name = "Hugging Face Hub Egress Connection",
  description = "Connection used to securely download model configs from public ML hubs"
);

Tworzenie funkcji UDF tokenizatora

Teraz wdróż niestandardową funkcję UDF tokenizatora. Zwróć uwagę, jak funkcja pomocnicza get_tokenizer() sprawdza, czy zmienna globalna tokenizer jest już zainicjowana, zanim spróbuje pobrać:

CREATE OR REPLACE FUNCTION `YOUR_PROJECT_ID.python_udfs.tokenize`(text STRING)
RETURNS ARRAY<INT64>
LANGUAGE python
WITH CONNECTION `YOUR_PROJECT_ID.us.external_api_connection`
OPTIONS (
  runtime_version = 'python-3.11',
  entry_point = 'tokenize',
  packages = ['transformers', 'sentencepiece']
) AS r'''
from transformers import T5TokenizerFast

# Initialize global variable for in-memory container caching
tokenizer = None

def get_tokenizer():
    global tokenizer
    if tokenizer is None:
        # Securely download T5 tokenizer config from Hugging Face Hub (runs once per warm container)
        tokenizer = T5TokenizerFast.from_pretrained("t5-base")
    return tokenizer

def tokenize(text):
    if not text:
        return []
    try:
        t = get_tokenizer()
        # Convert raw clean text into integer token IDs
        return [int(x) for x in t.encode(text)]
    except Exception:
        return []
''';

Aby sprawdzić, czy tokenizator prawidłowo pobiera zasób i zwraca tablicę identyfikatorów liczb całkowitych, przetestuj go za pomocą prostego zapytania:

SELECT `YOUR_PROJECT_ID.python_udfs.tokenize`('Hello world!') AS token_ids;

Aby zobaczyć uporządkowaną tablicę, przełącz się na kartę JSON w panelu wyników zapytania:

[
  {
    "token_ids": ["8774", "296", "55", "1"]
  }
]

6. Uruchamianie kompleksowego potoku wstępnego przetwarzania

Gdy wszystkie 3 kroki potoku są gotowe, możemy połączyć je w jedno zapytanie SQL za pomocą wyrażeń CTE (Common Table Expressions).

Ten potok reprezentuje nowoczesny proces inżynierii danych:

  1. Izoluj aktywnych użytkowników i ich pytania z najwyższymi wynikami za pomocą publicznej funkcji UDF percentyla.
  2. Usuń surowe formatowanie HTML z tekstu lokalnie za pomocą funkcji UDF clean_html.
  3. Przekonwertuj oczyszczony tekst na tablice tokenów za pomocą naszej funkcji UDF tokenize z pamięci podręcznej.

Uruchom to zapytanie potoku w konsoli BigQuery Studio:

WITH raw_user_scores AS (
  -- Step 1: Pre-aggregate scores to safely run percentiles with deterministic ordering
  SELECT 
    owner_user_id, 
    ARRAY_AGG(score ORDER BY id ASC) AS scores
  FROM 
    `bigquery-public-data.stackoverflow.posts_questions`
  WHERE 
    owner_user_id IS NOT NULL
  GROUP BY 
    owner_user_id
  HAVING 
    ARRAY_LENGTH(scores) >= 5
  ORDER BY 
    owner_user_id ASC
  LIMIT 3
),
active_users AS (
  -- Step 1: Extract exact percentile limits using the public UDF)
  SELECT 
    owner_user_id,
    percentiles_arr AS score_percentiles,
    -- Extract the 95th percentile score from the array's 4th element (OFFSET 3) directly
    percentiles_arr[OFFSET(3)] AS p95_score
  FROM (
    SELECT 
      owner_user_id,
      `bigquery-public-data.python_udfs.percentiles`(
        ARRAY(SELECT CAST(s AS FLOAT64) FROM UNNEST(scores) AS s), 
        [25.0, 50.0, 75.0, 95.0]
      ) AS percentiles_arr
    FROM 
      raw_user_scores
  )
),
target_questions AS (
  -- Isolate high-scoring questions from active users
  SELECT 
    q.id,
    q.owner_user_id,
    q.title,
    q.body AS raw_body,
    u.score_percentiles
  FROM 
    `bigquery-public-data.stackoverflow.posts_questions` q
  JOIN 
    active_users u ON q.owner_user_id = u.owner_user_id
  WHERE 
    -- Explicit cast for robust comparison
    q.score >= CAST(u.p95_score AS FLOAT64)
),
cleaned_data AS (
  -- Step 2: Clean HTML tags natively
  SELECT 
    id,
    owner_user_id,
    title,
    score_percentiles,
    `YOUR_PROJECT_ID.python_udfs.clean_html`(raw_body) AS cleaned_body
  FROM 
    target_questions
),
tokenized_data AS (
  -- Step 3: Perform local ML tokenization on the clean preview text
  SELECT 
    id,
    owner_user_id,
    title,
    score_percentiles,
    SUBSTR(cleaned_body, 1, 120) AS cleaned_body_preview,
    `YOUR_PROJECT_ID.python_udfs.tokenize`(SUBSTR(cleaned_body, 1, 120)) AS token_ids
  FROM 
    cleaned_data
)
SELECT 
  id,
  owner_user_id,
  title,
  score_percentiles,
  cleaned_body_preview AS cleaned_body,
  token_ids,
  ARRAY_LENGTH(token_ids) AS token_count
FROM 
  tokenized_data
ORDER BY 
  id ASC;

Aby sprawdzić uporządkowane dane wyjściowe, przełącz się na kartę JSON w BigQuery Studio.

[{
  "id": "9",
  "owner_user_id": "1",
  "title": "How do I calculate someone\u0027s age based on a DateTime type birthday?",
  "score_percentiles": ["22.5", "61.5", "346.75", "1762.0"],
  "cleaned_body": "Given a DateTime representing a person\u0027s birthday, how do I calculate their age in years?",
  "token_ids": ["9246", "3", "9", "7678", "13368", "9085", "3", "9", "568", "31", "7", "3591", "6", "149", "103", "27", "11837", "70", "1246", "16", "203", "58", "1"],
  "token_count": "23"
}, {
  "id": "17",
  "owner_user_id": "2",
  "title": "Binary Data in MySQL",
  "score_percentiles": ["3.5", "10.0", "90.0", "184.09999999999997"],
  "cleaned_body": "How do I store binary data in MySQL ?",
  "token_ids": ["571", "103", "27", "1078", "14865", "331", "16", "27563", "3", "58", "1"],
  "token_count": "11"
}, {
  "id": "33969",
  "owner_user_id": "3",
  "title": "Best way to implement request throttling in ASP.NET MVC?",
  "score_percentiles": ["3.25", "14.0", "24.75", "175.25"],
  "cleaned_body": "We\u0027re experimenting with various ways to throttle user actions in a given time period : Limit question/answer posts Limi",
  "token_ids": ["101", "31", "60", "3", "26718", "28", "796", "1155", "12", "28731", "1139", "2874", "16", "3", "9", "787", "97", "1059", "3", "10", "18185", "822", "87", "3247", "3321", "3489", "10908", "23", "1"],
  "token_count": "29"
}]

7. Dodatek: jak działa potok i jak sprawdzać koszty wykonania

W tej sekcji szczegółowo opisujemy mechanizmy kompleksowego zapytania o wstępne przetwarzanie i pokazujemy, jak monitorować dokładne zużycie przedziałów i koszty zarządzanego kontenera.

Architektura potoku

WITH raw_user_scores AS (
  SELECT 
    owner_user_id, 
    ARRAY_AGG(score ORDER BY id ASC) AS scores
  FROM 
    `bigquery-public-data.stackoverflow.posts_questions`
  WHERE 
    owner_user_id IS NOT NULL
  GROUP BY 
    owner_user_id
  HAVING 
    ARRAY_LENGTH(scores) >= 5
  ORDER BY 
    owner_user_id ASC
  LIMIT 3
)

Ten pierwszy segment zapytania zbiera surowe wyniki pytań aktywnych współtwórców Stack Overflow. Konsoliduje wyniki każdego użytkownika w jedną tablicę (ARRAY_AGG), jednocześnie wymuszając deterministyczny porządek sortowania (ORDER BY id). Zbiór danych jest filtrowany tak, aby zawierał tylko użytkowników, którzy zadali co najmniej 5 pytań, aby ustalić prawidłową statystyczną linię bazową.

active_users AS (
  SELECT 
    owner_user_id,
    percentiles_arr AS score_percentiles,
    -- Extract the 95th percentile score from the array's 4th element (OFFSET 3) directly
    percentiles_arr[OFFSET(3)] AS p95_score
  FROM (
    SELECT 
      owner_user_id,
      `bigquery-public-data.python_udfs.percentiles`(
        ARRAY(SELECT CAST(s AS FLOAT64) FROM UNNEST(scores) AS s), 
        [25.0, 50.0, 75.0, 95.0]
      ) AS percentiles_arr
    FROM 
      raw_user_scores
  )
)

Aby zidentyfikować najlepszych współtwórców, ten segment używa publicznej funkcji UDF w Pythonie percentiles do znajdowania dokładnych rozkładów wyników (25., 50., 75. i 95. percentyl). Aby uniknąć wielokrotnego wykonywania tej wymagającej obliczeń funkcji UDF, obliczenia są zawijane w zagnieżdżone podzapytanie. Wartość 95. percentyla jest następnie pobierana bezpośrednio z tablicy wynikowej na pozycji indeksu 3 (OFFSET(3)).

target_questions AS (
  -- Isolate high-scoring questions from active users
  SELECT 
    q.id,
    q.owner_user_id,
    q.title,
    q.body AS raw_body,
    u.score_percentiles
  FROM 
    `bigquery-public-data.stackoverflow.posts_questions` q
  JOIN 
    active_users u ON q.owner_user_id = u.owner_user_id
  WHERE 
    -- Explicit cast for robust comparison
    q.score >= CAST(u.p95_score AS FLOAT64)
)

Oryginalne pytania są łączone z listą aktywnych użytkowników, aby pobrać posty, które spełniają lub przekraczają próg 95. percentyla. Aby zapobiec błędom porównywania typów baz danych, wynik benchmarku jest przed oceną jawnie konwertowany za pomocą operacji CAST na typ FLOAT64.

cleaned_data AS (
  -- Clean HTML tags natively
  SELECT 
    id,
    owner_user_id,
    title,
    score_percentiles,
    `YOUR_PROJECT_ID.python_udfs.clean_html`(raw_body) AS cleaned_body
  FROM 
    target_questions
)

Surowe treści postów często zawierają nieuporządkowane znaczniki i kod HTML, które pogarszają dane wejściowe uczenia maszynowego. Zamiast używać złożonych wyrażeń regularnych, potok wywołuje naszą niestandardową funkcję UDF w Pythonie clean_html. Dynamicznie uruchamia środowisko wykonawcze Pythona w izolowanym kontenerze, używając biblioteki BeautifulSoup do czystego usuwania elementów i generowania zwykłego, czytelnego tekstu.

tokenized_data AS (
  -- Perform local ML tokenization on the clean preview text (called only once)
  SELECT 
    id,
    owner_user_id,
    title,
    score_percentiles,
    SUBSTR(cleaned_body, 1, 120) AS cleaned_body_preview,
    `YOUR_PROJECT_ID.python_udfs.tokenize`(SUBSTR(cleaned_body, 1, 120)) AS token_ids
  FROM 
    cleaned_data
)

Aby przygotować podgląd czystego tekstu do pozyskiwania przez model generatywny, potok wywołuje naszą niestandardową funkcję UDF w Pythonie tokenize na 120-znakowym wycinku. Funkcja UDF bezpiecznie łączy się z centrum Hugging Face, aby pobrać parametry tokenizatora Google T5. Ponieważ instancja tokenizatora jest wczytywana do zmiennej globalnej, pamięć podręczna kontenera przechowuje konfigurację, co pozwala na szybką tokenizację w pamięci bez opóźnień w sieci.

SELECT 
  id,
  owner_user_id,
  title,
  score_percentiles,
  cleaned_body_preview AS cleaned_body,
  token_ids,
  ARRAY_LENGTH(token_ids) AS token_count
FROM 
  tokenized_data
ORDER BY 
  id ASC;

Ostatni blok zapytania generuje przetworzony zbiór danych. Zamiast ponownie wykonywać funkcję UDF tokenizacji, aby zliczyć wygenerowane tokeny, natywna funkcja BigQuery ARRAY_LENGTH jest stosowana bezpośrednio do wstępnie obliczonej tablicy token_ids. Ta strategia zmniejsza liczbę zbędnych cykli procesora, operacji na kontenerach i ogólnych kosztów wykonania.

Sprawdzanie zużycia przedziałów i kosztów zarządzanych funkcji UDF

Chociaż BigQuery wdraża kompleksowe panele informacyjne dotyczące widoczności kosztów bezpośrednio w interfejsie konsoli Google Cloud, inżynierowie mogą programowo sprawdzać dokładne zużycie przedziałów i koszty wykonania zarządzanych kontenerów w przypadku dowolnego zapytania za pomocą identyfikatorów zadań BigQuery.

Aby sprawdzić wykonanie zapytania, znajdź identyfikator zadania.

  1. W BigQuery Studio możesz go znaleźć, otwierając kartę Historia zapytań u dołu konsoli.
  2. Kliknij wykonane zapytanie potoku.
  3. W panelu szczegółów Informacje o zadaniu znajdź pole Identyfikator zadania.

Gdy znajdziesz czysty identyfikator zadania, zastąp JOB_ID w zapytaniu poniżej i uruchom je w BigQuery Studio:

SELECT 
  job_id,
  total_slot_ms,
  external_service_costs
FROM 
  `YOUR_PROJECT_ID.region-us`.INFORMATION_SCHEMA.JOBS
WHERE 
  job_id = "JOB_ID";

Aby sprawdzić uporządkowane dane wyjściowe, przełącz się na kartę JSON w BigQuery Studio. Powinien się wyświetlić ładunek podobny do tego:

[{
  "job_id": "bquxjob_1234f5a_67ea8c9051a",
  "total_slot_ms": "815459",
  "external_service_costs": [{
    "external_service": "MANAGED_ROUTINE_EXECUTION",
    "bytes_processed": null,
    "bytes_billed": null,
    "slot_ms": "3000",
    "reserved_slot_count": null,
    "billing_method": "SERVICES_SKU"
  }]
}]

Interpretowanie danych wyjściowych:

8. Zwalnianie miejsca w zasobach w chmurze

Aby uniknąć ciągłych opłat lub wyczerpania limitów projektu, usuń zbiór danych BigQuery i połączenia w Cloud Shell:

# Cleanup BigQuery routines
bq rm -f --routine ${PROJECT_ID}:${BQ_DATASET}.clean_html
bq rm -f --routine ${PROJECT_ID}:${BQ_DATASET}.tokenize

# Cleanup connection
bq rm -f --connection --location=${REGION} ${PROJECT_ID}.${REGION}.${BQ_RESOURCE_CONN}

# Cleanup BigQuery Dataset
bq rm -r -f -d ${PROJECT_ID}:${BQ_DATASET}

9. Gratulacje!

Ukończono ćwiczenie dotyczące tworzenia i zabezpieczania funkcji UDF w Pythonie w środowisku wykonawczym bezserwerowym BigQuery.

W tym ćwiczeniu dowiesz się, jak:

  • Analizować dane za pomocą publicznych funkcji UDF: wywoływać wstępnie skompilowane publiczne funkcje UDF w Pythonie w zbiorach danych Stack Overflow, aby wykonywać operacje matematyczne na zagregowanych tablicach.
  • Integrować pakiety innych firm: wdrażać niestandardową trwałą funkcję UDF, która korzysta ze standardowego środowiska wykonawczego Pythona i biblioteki beautifulsoup4, aby natywnie usuwać surowe tagi HTML w zapytaniach SQL.
  • Konfigurowanie bezpiecznych połączeń zewnętrznych: Utwórz połączenie z zasobem Cloud BigQuery, aby bezpiecznie przyznawać izolowanym kontenerom funkcji UDF dostęp do sieci wychodzącej w celu pobierania zasobów zewnętrznych bez kodowania na stałe danych logowania.
  • Implementować lokalną tokenizację z użyciem pamięci podręcznej: importować bibliotekę transformers Hugging Face, aby wczytać tokenizator T5, używając zmiennych globalnych do przechowywania plików konfiguracyjnych w pamięci podręcznej i przetwarzania wierszy w kontenerach.
  • Sprawdzać wydajność i koszty wykonania: programowo wysyłać zapytania do regionalnych widoków INFORMATION_SCHEMA.JOBS za pomocą identyfikatorów zadań BigQuery, aby śledzić zużycie przedziałów (total_slot_ms) i koszty użycia kontenera (external_service_costs).

Co dalej?