Obtén estadísticas a partir de datos estructurados y no estructurados con el paquete BigQuery DataFrames compatible con IA

1. Descripción general

En este lab, usarás BigQuery DataFrames desde un notebook de Python en BigQuery Studio para obtener estadísticas a partir de los datos con Python. Aprovecha la IA generativa de Google para analizar y visualizar datos de texto no estructurados.

Crearás un notebook de Python para categorizar y resumir una base de datos pública de reclamos de clientes. Se puede adaptar para que funcione con cualquier dato de texto no estructurado.

Objetivos

En este lab, aprenderás a realizar las siguientes tareas:

  • Activa y usa notebooks de Python en BigQuery Studio
  • Conéctate a BigQuery con el paquete de BigQuery DataFrames
  • Crea incorporaciones a partir de datos de texto no estructurados con BigQuery ML y una conexión a un extremo de incorporación de texto en Vertex AI
  • Agrupa clústeres de incorporaciones con BigQuery ML
  • Resume clústeres con un LLM a través de BigQuery ML

2. Requisitos

  • Un navegador, como Chrome o Firefox.
  • Un proyecto de Google Cloud con la facturación habilitada.

Antes de comenzar

Para seguir las instrucciones de este codelab, necesitarás un proyecto de Google Cloud con BigQuery Studio habilitado y una cuenta de facturación conectada.

  1. En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.
  2. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud. Obtén información para verificar si la facturación está habilitada en un proyecto.
  3. Sigue las instrucciones para habilitar BigQuery Studio para la administración de recursos.

Prepara BigQuery Studio

Crea un notebook vacío y conéctalo a un entorno de ejecución.

  1. Ve a BigQuery Studio en la consola de Google Cloud.
  2. Haz clic en junto al botón +.
  3. Selecciona Notebook de Python.
  4. Cierra el selector de plantillas.
  5. Selecciona + Código para crear una celda de código nueva.
  6. Instala la versión más reciente del paquete de BigQuery DataFrames desde la celda de código.Escribe el siguiente comando.
    %pip install --upgrade bigframes --quiet
    
    Haz clic en el botón 🞂 o presiona Mayúsculas + Intro para ejecutar la celda de código.

3. Cómo leer un conjunto de datos públicos

Inicializa el paquete de BigQuery DataFrames ejecutando lo siguiente en una celda de código nueva:

import bigframes.pandas as bpd

bpd.options.bigquery.ordering_mode = "partial"

Nota: En este instructivo, usamos el "modo de ordenamiento parcial" experimental, que permite realizar consultas más eficientes cuando se usa con filtros similares a los de pandas. Es posible que no funcionen algunas funciones de pandas que requieren un orden o un índice estrictos.

Base de datos de reclamos de consumidores

La Consumer Complaint Database se proporciona en BigQuery a través del programa de conjuntos de datos públicos de Google Cloud. Esta es una recopilación de reclamos sobre productos y servicios financieros para el consumidor, y los datos son recopilados por la Oficina de Protección Financiera del Consumidor de Estados Unidos.

En BigQuery, consulta la tabla bigquery-public-data.cfbp_complaints.complaint_database para analizar la base de datos de reclamos de consumidores. Usa el método bigframes.pandas.read_gbq() para crear un DataFrame a partir de una cadena de consulta o un ID de tabla.

Ejecuta lo siguiente en una celda de código nueva para crear un DataFrame llamado "feedback":

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

Descubre información básica sobre un DataFrame

Usa el método DataFrame.peek() para descargar una pequeña muestra de los datos.

Ejecuta esta celda:

feedback.peek()

Resultado esperado:

  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   

Nota: head() requiere ordenamiento y, por lo general, es menos eficiente que peek() si deseas visualizar una muestra de datos.

Al igual que con pandas, usa la propiedad DataFrame.dtypes para ver todas las columnas disponibles y sus tipos de datos correspondientes. Se exponen de una manera compatible con pandas.

Ejecuta esta celda:

feedback.dtypes

Resultado esperado:

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

El método DataFrame.describe() consulta algunas estadísticas básicas del DataFrame. Dado que este DataFrame no contiene columnas numéricas, muestra un resumen del recuento de valores no nulos y la cantidad de valores distintos.

Ejecuta esta celda:

# 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()

Resultado esperado:

         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. Cómo explorar los datos

Antes de analizar los reclamos reales, usa los métodos similares a los de Pandas en el DataFrame para visualizar los datos.

Visualiza el DataFrame

Existen varios métodos de visualización integrados, como DataFrame.plot.hist(). Dado que este DataFrame contiene principalmente datos de cadena y booleanos, primero podemos realizar alguna agregación para obtener más información sobre varias columnas.

Contamos cuántas reclamos se reciben de cada estado.

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

Convierte esto en un DataFrame de Pandas con el método DataFrame.to_pandas().

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

Usa los métodos de visualización de Pandas en este DataFrame descargado.

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

Gráfico de barras que muestra a California como el estado con más reclamos

Unir con otros conjuntos de datos

Anteriormente, analizaste los reclamos por estado, pero esto pierde contexto importante. Algunos estados tienen más población que otros. Une los datos con un conjunto de datos de población, como la Encuesta sobre la comunidad estadounidense de la Oficina del Censo de EE.UU. y la tabla 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")
)

La Encuesta sobre la Comunidad Estadounidense identifica los estados por GEOID. Une la tabla de estados para obtener la población por código de estado de dos letras.

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

Ahora, une esta tabla a la base de datos de reclamos para comparar la población con la cantidad de reclamos.

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

Crea un diagrama de dispersión para comparar la población de los estados con la cantidad de reclamos.

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

Un diagrama de dispersión que compara la población con las reclamos

Al comparar la población con la cantidad de reclamos, parece que algunos estados son valores atípicos. Se deja como ejercicio para el lector que genere un gráfico con etiquetas de puntos para identificarlos. Del mismo modo, formula algunas hipótesis sobre por qué podría ser así (p.ej., diferentes datos demográficos, diferente cantidad de empresas de servicios financieros, etc.) y ponlas a prueba.

5. Cómo calcular embeddings

A menudo, la información importante se oculta en datos no estructurados, como texto, audio o imágenes. En este ejemplo, gran parte de la información útil de la base de datos de reclamos se encuentra en el contenido de texto del reclamo.

La IA y las técnicas tradicionales, como el análisis de opiniones, la "bolsa de palabras" y word2vec, pueden extraer información cuantitativa de los datos no estructurados. Más recientemente, los modelos de "incorporación de vectores", que están estrechamente relacionados con los LLM, pueden crear una secuencia de números de punto flotante que representan la información semántica del texto.

Selecciona un subconjunto de la base de datos

Ejecutar un modelo de incorporación de vectores usa más recursos que otras operaciones. Para reducir los costos y los problemas de cuota, selecciona un subconjunto de los datos para el resto de este instructivo.

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

El 2022-12-01, se enviaron alrededor de 1,000 reclamos, en comparación con los casi 3.5 millones de filas de la base de datos total (consulta con feedback.shape).

Selecciona solo los datos del 2022-12-01 y solo la columna 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

El método drop_duplicates de pandas requiere un orden total de las filas porque intenta seleccionar la primera o la última fila coincidente y conservar el índice asociado a ella.

En su lugar, agrega los datos con una llamada al método groupby para quitar las filas duplicadas.

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

feedback.shape

Genera embeddings

BigQuery DataFrames genera vectores de incorporación a través de la clase TextEmbeddingGenerator. Esto se basa en el método ML.GENERATE_EMBEDDING en BigQuery ML, que llama a los modelos de incorporación de texto proporcionados por Vertex AI.

from bigframes.ml.llm import TextEmbeddingGenerator

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

Observa cómo se ven los embeddings. Estos vectores representan el significado semántico del texto tal como lo entiende el modelo de embedding de texto.

feedback_embeddings.peek()

Resultado esperado:

                        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-...   

Estos vectores tienen muchas dimensiones. Observa un solo vector de embedding:

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

La generación de embeddings funciona según un contrato de "éxito parcial". Esto significa que algunas filas pueden tener errores y no generar una incorporación. La columna 'ml_generate_embedding_status' expone los mensajes de error. Si está vacío, significa que no hay errores.

Filtra las incorporaciones para incluir solo las filas en las que no se produjo ningún error.

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

6. Agrupa en clústeres con incorporaciones de texto

Ahora, agrupa los embeddings en clústeres con k-means. Para esta demostración, usa una cantidad arbitraria de grupos (también conocidos como centroides). Una solución de calidad para la producción debe ajustar la cantidad de centroides con una técnica como el método de la silueta.

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()

Quita los errores de incorporación.

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

Echa un vistazo y observa la distribución de los comentarios por centroide.

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

7. Resume los clústeres

Proporciona algunos comentarios asociados a cada centroide y pídele a Gemini que resuma los reclamos. La ingeniería de instrucciones es un área emergente, pero hay buenos ejemplos en Internet, como 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()

Usar Gemini para escribir un informe a partir de los resúmenes

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. Limpia

Si creaste un proyecto nuevo de Google Cloud para este instructivo, puedes borrarlo para evitar cargos adicionales por las tablas o los demás recursos creados.

9. ¡Felicitaciones!

Analizaste datos estructurados y no estructurados con BigQuery DataFrames. En el camino, exploraste los conjuntos de datos públicos de Google Cloud, los notebooks de Python en BigQuery Studio, BigQuery ML, Vertex AI y las funciones de lenguaje natural a Python de BigQuery Studio. ¡Gran trabajo!

Próximos pasos