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 des 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. Cette méthode peut être adaptée pour fonctionner sur n'importe quelles données textuelles non structurées.

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
  • Regrouper des embeddings à 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 avec BigQuery Studio activé et un compte de facturation associé.

  1. Dans la console Google Cloud, sur la page de sélection du 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 connectez-le à un environnement d'exécution.

  1. Accédez à BigQuery Studio dans la console Google Cloud.
  2. Cliquez sur à 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 la commande suivante 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 de tri partiel" expérimental, qui permet d'effectuer des requêtes plus efficaces lorsqu'il est utilisé avec un filtrage de type pandas. Il est possible que certaines fonctionnalités pandas nécessitant un ordre ou un index stricts ne fonctionnent pas.

Base de données des réclamations des consommateurs

La base de données des réclamations des consommateurs est disponible sur BigQuery via le programme d'ensembles de données publics de Google Cloud. Il s'agit d'une collection de réclamations concernant des 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.

Comme avec pandas, utilisez la propriété DataFrame.dtypes pour afficher toutes les colonnes disponibles et leurs types de données correspondants. Elles sont exposées 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 des statistiques de base à partir du DataFrame. Comme 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 d'examiner les réclamations proprement dites, utilisez les méthodes pandas-like 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 type chaîne et booléen, 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 que la Californie est l&#39;État qui enregistre le plus de réclamations

Effectuer une jointure avec d'autres ensembles de données

Auparavant, vous avez examiné les réclamations par État, mais cela perd un contexte important. Certains États ont une population plus importante que d'autres. Associez-le à un ensemble de données sur la population, comme l'American Community Survey 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 à la table "states" 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"]]
)

Joignez maintenant cette table à 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 graphique en nuage de points pour comparer la population des États au nombre de réclamations.

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

un graphique à nuage de points comparant la population et les réclamations.

Il semble que certains États soient des valeurs aberrantes lorsque l'on compare la population au nombre de réclamations. Le lecteur est invité à tracer les points avec des libellés pour les identifier. De même, élaborez des hypothèses pour expliquer ce phénomène (par exemple, des différences démographiques, un nombre différent d'entreprises de services financiers, etc.) et testez-les.

5. Calculer des embeddings

Souvent, des informations importantes sont cachées dans des données non structurées, comme 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 des sentiments, le "sac de mots" et word2vec, peuvent extraire des informations quantitatives à partir de données non structurées. Plus récemment, les modèles d'embedding vectoriel, 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 les autres opérations. Pour réduire les coûts et les problèmes de quota, sélectionnez un sous-ensemble de 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 réclamations ont été envoyées le 1er décembre 2022, contre près de 3,5 millions de lignes dans la base de données totale (vérifiez auprès de feedback.shape).

Sélectionnez uniquement les données du 01/12/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 ordre total des lignes, car elle tente de sélectionner la première ou la dernière ligne correspondante et de conserver l'index qui y est associé.

Au lieu de cela, agrégez les données en appelant la méthode groupby pour supprimer les lignes en double.

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. Cette opération est basée sur la méthode ML.GENERATE_EMBEDDING dans BigQuery ML, qui appelle les modèles d'embedding de texte 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 de texte.

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 comporter des erreurs et ne pas générer d'embedding. Les messages d'erreur sont indiqués dans la colonne 'ml_generate_embedding_status'. Une liste vide signifie qu'il n'y a aucune erreur.

Filtrez les embeddings 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. Regrouper des données à l'aide d'embeddings de texte

Regroupez maintenant les embeddings à l'aide de k-means. 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'intégration.

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

Jetez un coup d'œil à la distribution des commentaires par centroïde.

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

7. Résumer les clusters

Fournissez quelques commentaires associés à chaque centroïde 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()

Utiliser 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 des frais supplémentaires pour les tables ou autres ressources créées.

9. Félicitations !

Vous avez analysé des données structurées et non structurées à l'aide de BigQuery DataFrames. Vous avez exploré les ensembles de données publics de Google Cloud, les notebooks Python dans BigQuery Studio, BigQuery ML, Vertex AI et les fonctionnalités de langage naturel vers Python de BigQuery Studio. Bravo !

Étapes suivantes