Obtenir des insights à partir de données structurées et non structurées à l'aide du package DataFrames BigQuery compatible avec l'IA

1. Présentation

Dans cet atelier, vous allez utiliser BigQuery DataFrames à partir d'un notebook Python dans BigQuery Studio pour obtenir des insights à partir de données à l'aide de Python. Utilisez l'IA générative de Google pour analyser et visualiser les données textuelles non structurées.

Vous allez créer un notebook Python pour catégoriser et résumer une base de données publique de réclamations de clients. Vous pouvez l'adapter à n'importe quelle donnée textuelle non structurée.

Objectifs

Dans cet atelier, vous allez apprendre à effectuer les tâches suivantes :

  • Activer et utiliser des notebooks Python dans BigQuery Studio
  • Se connecter à BigQuery à l'aide du package BigQuery DataFrames
  • Créer des embeddings à partir de données textuelles non structurées à l'aide de BigQuery ML et d'une connexion à un point de terminaison d'embedding de texte dans Vertex AI
  • Ensembles d'engrammes de cluster à l'aide de BigQuery ML
  • Résumer des clusters avec un LLM via BigQuery ML

2. Conditions requises

  • Un navigateur tel que Chrome ou Firefox
  • Un projet Google Cloud avec facturation activée

Avant de commencer

Pour suivre les instructions de cet atelier de programmation, vous aurez besoin d'un projet Google Cloud dans lequel BigQuery Studio est activé et d'un compte de facturation associé.

  1. Dans la console Google Cloud, sur la page du sélecteur de projet, sélectionnez ou créez un projet Google Cloud.
  2. Assurez-vous que la facturation est activée pour votre projet Google Cloud. Découvrez comment vérifier si la facturation est activée sur un projet.
  3. Suivez les instructions pour activer BigQuery Studio pour la gestion des éléments.

Préparer BigQuery Studio

Créez un notebook vide et associez-le à un environnement d'exécution.

  1. Accédez à BigQuery Studio dans la console Google Cloud.
  2. Cliquez sur le bouton  à côté du bouton +.
  3. Sélectionnez Notebook Python.
  4. Fermez le sélecteur de modèle.
  5. Sélectionnez + Code pour créer une cellule de code.
  6. Installez la dernière version du package BigQuery DataFrames à partir de la cellule de code.Saisissez la commande suivante.
    %pip install --upgrade bigframes --quiet
    
    Cliquez sur le bouton 🞂 ou appuyez sur Maj+Entrée pour exécuter la cellule de code.

3. Lire un ensemble de données public

Initialisez le package BigQuery DataFrames en exécutant le code suivant dans une nouvelle cellule de code:

import bigframes.pandas as bpd

bpd.options.bigquery.ordering_mode = "partial"

Remarque: dans ce tutoriel, nous utilisons le mode d'ordonnancement partiel expérimental, qui permet d'effectuer des requêtes plus efficaces lorsqu'il est utilisé avec un filtrage semblable à celui de pandas. Certaines fonctionnalités pandas qui nécessitent un tri ou un indice stricts peuvent ne pas fonctionner.

Base de données des réclamations des consommateurs

La base de données des réclamations des consommateurs est disponible dans BigQuery via le programme d'ensembles de données publics de Google Cloud. Il s'agit d'un ensemble de plaintes concernant les produits et services financiers destinés aux consommateurs. Les données sont collectées par le Consumer Financial Protection Bureau des États-Unis.

Dans BigQuery, interrogez la table bigquery-public-data.cfbp_complaints.complaint_database pour analyser la base de données des réclamations des consommateurs. Utilisez la méthode bigframes.pandas.read_gbq() pour créer un DataFrame à partir d'une chaîne de requête ou d'un ID de table.

Exécutez le code suivant dans une nouvelle cellule de code pour créer un DataFrame nommé "feedback":

feedback = bpd.read_gbq(
    "bigquery-public-data.cfpb_complaints.complaint_database"
)

Découvrir des informations de base sur un DataFrame

Utilisez la méthode DataFrame.peek() pour télécharger un petit échantillon des données.

Exécutez cette cellule:

feedback.peek()

Résultat attendu :

  date_received                  product ... timely_response  consumer_disputed complaint_id  
0    2014-03-05  Bank account or service ...            True              False       743665   
1    2014-01-21  Bank account or service ...            True              False       678608   
2    2020-12-31          Debt collection ...            True               <NA>      4041190   
3    2014-02-12          Debt collection ...            True              False       714350   
4    2015-02-23          Debt collection ...            True              False      1251358   

Remarque: head() nécessite un tri et est généralement moins efficace que peek() si vous souhaitez visualiser un échantillon de données.

Tout comme avec pandas, utilisez la propriété DataFrame.dtypes pour afficher toutes les colonnes disponibles et les types de données correspondants. Ils sont exposés de manière compatible avec pandas.

Exécutez cette cellule:

feedback.dtypes

Résultat attendu :

date_received                   date32[day][pyarrow]
product                              string[pyarrow]
subproduct                           string[pyarrow]
issue                                string[pyarrow]
subissue                             string[pyarrow]
consumer_complaint_narrative         string[pyarrow]
company_public_response              string[pyarrow]
company_name                         string[pyarrow]
state                                string[pyarrow]
zip_code                             string[pyarrow]
tags                                 string[pyarrow]
consumer_consent_provided            string[pyarrow]
submitted_via                        string[pyarrow]
date_sent_to_company            date32[day][pyarrow]
company_response_to_consumer         string[pyarrow]
timely_response                              boolean
consumer_disputed                            boolean
complaint_id                         string[pyarrow]
dtype: object

La méthode DataFrame.describe() interroge certaines statistiques de base du DataFrame. Étant donné que ce DataFrame ne contient aucune colonne numérique, il affiche un récapitulatif du nombre de valeurs non nulles et du nombre de valeurs distinctes.

Exécutez cette cellule:

# Exclude some of the larger columns to make the query more efficient.
feedback.drop(columns=[
  "consumer_complaint_narrative",
  "company_public_response",
  "company_response_to_consumer",
]).describe()

Résultat attendu :

         product  subproduct    issue  subissue  company_name    state ... timely_response  consumer_disputed  complaint_id
count    3458906     3223615  3458906   2759004       3458906  3417792 ...         3458906             768399       3458906
nunique       18          76      165       221          6694       63 ...               2                  2       3458906

4. Explorer les données

Avant de vous pencher sur les réclamations, utilisez les méthodes semblables à celles de Pandas sur le DataFrame pour visualiser les données.

Visualiser le DataFrame

Il existe plusieurs méthodes de visualisation intégrées, comme DataFrame.plot.hist(). Étant donné que ce DataFrame contient principalement des données de chaîne et booléennes, nous pouvons d'abord effectuer une agrégation pour en savoir plus sur les différentes colonnes.

Comptez le nombre de réclamations reçues de chaque État.

complaints_by_state = (
  feedback.groupby(
    "state", as_index=False,
  ).size()
  .rename(columns={"size": "total_complaints"})
  .sort_values(by="total_complaints", ascending=False)
)

Convertissez-le en DataFrame pandas à l'aide de la méthode DataFrame.to_pandas().

complaints_pd = complaints_by_state.head(10).to_pandas()

Utilisez les méthodes de visualisation pandas sur ce DataFrame téléchargé.

complaints_pd.plot.bar(x="state", y="total_complaints")

Graphique à barres montrant la Californie comme État ayant enregistré le plus de réclamations

Associer des ensembles de données

Auparavant, vous examiniez les réclamations par État, mais vous perdiez ainsi un contexte important. Certains États ont une population plus importante que d'autres. Associez-les à un ensemble de données sur la population, tel que l'enquête sur la communauté américaine du Bureau du recensement des États-Unis et la table bigquery-public-data.geo_us_boundaries.states.

us_states = bpd.read_gbq("bigquery-public-data.geo_us_boundaries.states")
us_survey = bpd.read_gbq("bigquery-public-data.census_bureau_acs.state_2020_5yr")

# Ensure there are leading 0s on GEOIDs for consistency across tables.
us_states = us_states.assign(
    geo_id=us_states["geo_id"].str.pad(2, fillchar="0")
)

us_survey = us_survey.assign(
    geo_id=us_survey["geo_id"].str.pad(2, fillchar="0")
)

L'American Community Survey identifie les États par GEOID. Joignez la table des États pour obtenir la population par code d'État à deux lettres.

pops = us_states.set_index("geo_id")[["state"]].join(
  us_survey.set_index("geo_id")[["total_pop"]]
)

Associez-la maintenant à la base de données des réclamations pour comparer la population au nombre de réclamations.

complaints_and_pops = complaints_by_state.set_index("state").join(
    pops.set_index("state")
)

Créez un nuage de points pour comparer la population des États au nombre de plaintes.

(
  complaints_and_pops
  .to_pandas()
  .plot.scatter(x="total_pop", y="total_complaints")
)

un graphique à nuage de points comparant la population aux réclamations

Quelques États semblent être des valeurs aberrantes lorsque l'on compare la population au nombre de réclamations. Il est laissé au lecteur de les identifier en les étiquetant. De même, formulez des hypothèses pour expliquer ce résultat (par exemple, différentes données démographiques, différents nombres d'entreprises de services financiers, etc.) et testez-les.

5. Calculer des embeddings

Des informations importantes sont souvent cachées dans des données non structurées, telles que du texte, de l'audio ou des images. Dans cet exemple, la plupart des informations utiles de la base de données des réclamations se trouvent dans le contenu textuel de la réclamation.

L'IA et les techniques traditionnelles, telles que l'analyse du sentiment, le "bag of words" et word2vec, peuvent extraire certaines informations quantitatives à partir de données non structurées. Plus récemment, les modèles de "représentation vectorielle continue", qui sont étroitement liés aux LLM, peuvent créer une séquence de nombres à virgule flottante représentant les informations sémantiques du texte.

Sélectionner un sous-ensemble de la base de données

L'exécution d'un modèle d'embedding vectoriel utilise plus de ressources que d'autres opérations. Pour réduire les coûts et les problèmes de quota, sélectionnez un sous-ensemble des données pour le reste de ce tutoriel.

import bigframes.pandas as bpd

bpd.options.bigquery.ordering_mode = "partial"

feedback = bpd.read_gbq(
    "bigquery-public-data.cfpb_complaints.complaint_database"
)

# Note: if not using ordering_mode = "partial", you must specify these in read_gbq
# for these to affect query efficiency.
# feedback = bpd.read_gbq(
#    "bigquery-public-data.cfpb_complaints.complaint_database",
#     columns=["consumer_complaint_narrative"],
#     filters= [
#         ("consumer_complaint_narrative", "!=", ""),
#         ("date_received", "==", "2022-12-01")])

feedback.shape

Environ 1 000 plaintes ont été envoyées le 01/12/2022, contre près de 3,5 millions de lignes dans la base de données totale (vérifiez avec feedback.shape).

Sélectionnez uniquement les données du 1er décembre 2022 et la colonne consumer_complaint_narrative.

import datetime

feedback = feedback[
    # Filter rows by passing in a boolean Series.
    (feedback["date_received"] == datetime.date(2022, 12, 1))
    & ~(feedback["date_received"].isnull())
    & ~(feedback["consumer_complaint_narrative"].isnull())
    & (feedback["consumer_complaint_narrative"] != "")
    & (feedback["state"] == "CA")

    # Uncomment the following if using free credits for a workshop.
    # Billing accounts with free credits have limited Vertex AI quota.
    # & (feedback["product"] == "Mortgage")
][
    # Filter columns by passing in a list of strings.
    ["consumer_complaint_narrative"]
]

feedback.shape

La méthode drop_duplicates de pandas nécessite un tri total des lignes, car elle tente de sélectionner la première ou la dernière ligne correspondante et de conserver l'index qui lui est associé.

Agrégez plutôt les données à l'aide d'un appel à la méthode groupby pour dédupliquer les lignes.

feedback = (
  feedback.groupby("consumer_complaint_narrative", as_index=False)
  .size()
)[["consumer_complaint_narrative"]]

feedback.shape

Générer des embeddings

BigQuery DataFrames génère des vecteurs d'embedding via la classe TextEmbeddingGenerator. Il s'agit de la méthode ML.GENERATE_EMBEDDING, dans BigQuery ML, qui appelle les modèles d'embedding textuel fournis par Vertex AI.

from bigframes.ml.llm import TextEmbeddingGenerator

embedding_model = TextEmbeddingGenerator(
    model_name="text-embedding-004"
)
feedback_embeddings = embedding_model.predict(feedback)

Découvrez à quoi ressemblent les embeddings. Ces vecteurs représentent la signification sémantique du texte telle qu'elle est comprise par le modèle d'embedding textuel.

feedback_embeddings.peek()

Résultat attendu :

                        ml_generate_embedding_result  \
0  [ 7.36380890e-02  2.11779331e-03  2.54309829e-...   
1  [-1.10935252e-02 -5.53950183e-02  2.01338865e-...   
2  [-7.85628427e-03 -5.39347418e-02  4.51385677e-...   
3  [ 0.02013054 -0.0224789  -0.00164843  0.011354...   
4  [-1.51684484e-03 -5.02693094e-03  1.72322839e-...   

Ces vecteurs comportent de nombreuses dimensions. Examinons un seul vecteur d'embedding:

feedback_embeddings["ml_generate_embedding_result"].peek().iloc[0]

La génération d'embeddings fonctionne selon un contrat de "succès partiel". Cela signifie que certaines lignes peuvent contenir des erreurs et ne pas générer d'encapsulation. Les messages d'erreur sont exposés dans la colonne 'ml_generate_embedding_status'. Si le champ est vide, cela signifie qu'aucune erreur ne s'affiche.

Filtrez les représentations vectorielles continues pour n'inclure que les lignes où aucune erreur ne s'est produite.

mask = feedback_embeddings["ml_generate_embedding_status"] == ""
valid_embeddings = feedback_embeddings[mask]
valid_embeddings.shape

6. Créer des clusters à l'aide de représentations vectorielles continues de texte

Maintenant, regroupez les représentations vectorielles continues à l'aide de k-moyennes. Pour cette démonstration, utilisez un nombre arbitraire de groupes (ou centroïdes). Une solution de qualité de production doit ajuster le nombre de centroïdes à l'aide d'une technique telle que la méthode Silhouette.

from bigframes.ml.cluster import KMeans

num_clusters = 5
cluster_model = KMeans(n_clusters=num_clusters)
cluster_model.fit(valid_embeddings["ml_generate_embedding_result"])
clusters = cluster_model.predict(valid_embeddings)
clusters.peek()

Supprimez les échecs d'embedding.

mask = clusters["ml_generate_embedding_status"] == ""
clusters = clusters[mask]

Consultez la distribution des commentaires par centre de gravité.

clusters.groupby("CENTROID_ID").size()

7. Résumer les clusters

Fournissez des commentaires associés à chaque centroid et demandez à Gemini de résumer les réclamations. L'ingénierie des requêtes est un domaine émergent, mais il existe de bons exemples sur Internet, comme https://www.promptingguide.ai/.

from bigframes.ml.llm import GeminiTextGenerator

preamble = "What is the main concern in this list of user complaints:"
suffix = "Write the main issue using a formal tone."

# Now let's sample the raw comments and get the LLM to summarize them.
prompts = []
for centroid_id in range(1, num_clusters + 1):
  cluster = clusters[clusters["CENTROID_ID"] == centroid_id]
  comments = "\n".join(["- {0}".format(x) for x in cluster.content.peek(40)])
  prompts.append("{}:\n{}\n{}".format(preamble, comments, suffix))

prompt_df = bpd.DataFrame(prompts)
gemini = GeminiTextGenerator(model_name="gemini-1.5-flash-001")
issues = gemini.predict(X=prompt_df, temperature=0.0)
issues.peek()

Utilisez Gemini pour rédiger un rapport à partir des résumés.

from IPython.display import display, Markdown

prompt = "Turn this list of issues into a short, concise report:"
for value in issues["ml_generate_text_llm_result"]:
  prompt += "- {}".format(value)
prompt += "Using a formal tone, write a markdown text format report."

summary_df = bpd.DataFrame(([prompt]))
summary = gemini.predict(X=summary_df, temperature=0.0)

report = (summary["ml_generate_text_llm_result"].values[0])
display(Markdown(report))

8. Effectuer un nettoyage

Si vous avez créé un projet Google Cloud pour ce tutoriel, vous pouvez le supprimer pour éviter que des frais supplémentaires ne soient facturés pour les tables ou d'autres ressources créées.

9. Félicitations !

Vous avez analysé des données structurées et non structurées à l'aide de BigQuery DataFrames. Au cours de ce tutoriel, vous avez découvert les ensembles de données publics de Google Cloud, les notebooks Python dans BigQuery Studio, BigQuery ML, Vertex AI et les fonctionnalités de BigQuery Studio permettant de passer du langage naturel au langage Python. Bravo !

Étapes suivantes