Xây dựng ứng dụng đề xuất 360 độ về khách hàng bằng BigQuery Graph

1. Giới thiệu

Trong lớp học lập trình này, bạn sẽ tìm hiểu cách sử dụng BigQuery Graph để xây dựng chân dung khách hàng 360 độ và công cụ đề xuất cho Cymbal Pets, một công ty bán lẻ giả định. Bạn sẽ tận dụng sức mạnh của SQL để tạo, truy vấn và phân tích dữ liệu đồ thị ngay trong BigQuery, kết hợp với tính năng tìm kiếm vectơ để đưa ra các đề xuất sản phẩm nâng cao.

BigQuery Graph cho phép bạn mô hình hoá mối quan hệ giữa các thực thể dữ liệu (như khách hàng, sản phẩm và đơn đặt hàng) dưới dạng đồ thị, giúp bạn dễ dàng trả lời các câu hỏi phức tạp về hành vi của khách hàng và mối quan hệ giữa các sản phẩm.

Biểu đồ trường hợp sử dụng

Bạn sẽ thực hiện

  • Tạo tập dữ liệu và giản đồ BigQuery cho đồ thị Cymbal Pets
  • Tải dữ liệu mẫu (Khách hàng, Sản phẩm, Đơn đặt hàng, Cửa hàng) từ Cloud Storage
  • Tạo đồ thị thuộc tính trong BigQuery kết nối các thực thể này
  • Trực quan hoá nhật ký mua của khách hàng bằng cách sử dụng truy vấn đồ thị
  • Xây dựng hệ thống đề xuất sản phẩm bằng cách sử dụng tính năng tìm kiếm vectơ
  • Nâng cao đề xuất bằng cách sử dụng mối quan hệ đồ thị "Mua cùng nhau" và độ tương tự Jaccard

Bạn cần có

  • Trình duyệt web như Chrome
  • Dự án trên Google Cloud đã bật tính năng thanh toán

Lớp học lập trình này dành cho nhà phát triển ở mọi cấp độ, kể cả người mới bắt đầu.

2. Trước khi bắt đầu

Tạo một dự án trên Google Cloud

  1. Trong Bảng điều khiển Google Cloud, chọn hoặc tạo một dự án trên Google Cloud.
  2. Đảm bảo rằng bạn đã bật tính năng thanh toán cho dự án trên Cloud.

Khởi động Cloud Shell

  1. Nhấp vào Kích hoạt Cloud Shell ở đầu bảng điều khiển Cloud.
  2. Xác minh quy trình xác thực:
gcloud auth list
  1. Xác nhận dự án của bạn:
gcloud config get project
  1. Thiết lập nếu cần:
export PROJECT_ID=<YOUR_PROJECT_ID>
gcloud config set project $PROJECT_ID

Bật API

Chạy lệnh này để bật BigQuery API bắt buộc:

gcloud services enable bigquery.googleapis.com

3. Xác định giản đồ

Trước tiên, bạn cần tạo một tập dữ liệu để lưu trữ các bảng liên quan đến đồ thị và xác định giản đồ cho các nút và cạnh.

  1. Trong lớp học lập trình này, chúng ta sẽ thực thi các lệnh SQL. Bạn có thể chạy các lệnh này trong BigQuery Studio > Trình chỉnh sửa SQL hoặc sử dụng lệnh bq query trong Cloud Shell. Truy vấn SQL mớiChúng tôi giả định rằng bạn đang sử dụng Trình chỉnh sửa SQL BigQuery để có trải nghiệm tốt hơn với các câu lệnh tạo nhiều dòng.
  2. Tạo tập dữ liệu cymbal_pets_demo:
CREATE SCHEMA IF NOT EXISTS cymbal_pets_demo;
  1. Tạo các bảng cho order_items, products, orders, stores, customers, và co_related_products_for_angelica. Các bảng này sẽ đóng vai trò là dữ liệu nguồn cho đồ thị của chúng ta.
CREATE TABLE IF NOT EXISTS cymbal_pets_demo.order_items
(
  order_id INT64,
  product_id INT64,
  order_item_id INT64,
  quantity INT64,
  price FLOAT64,
  PRIMARY KEY (order_id, product_id, order_item_id) NOT ENFORCED
)
CLUSTER BY order_item_id;

CREATE TABLE IF NOT EXISTS cymbal_pets_demo.products
(
  product_id INT64,
  product_name STRING,
  brand STRING,
  category STRING,
  subcategory INT64,
  animal_type INT64,
  search_keywords INT64,
  price FLOAT64,
  description STRING,
  inventory_level INT64,
  supplier_id INT64,
  average_rating FLOAT64,
  uri STRING,
  embedding ARRAY<FLOAT64>,
  PRIMARY KEY (product_id) NOT ENFORCED
)
CLUSTER BY product_id;

CREATE TABLE IF NOT EXISTS cymbal_pets_demo.orders
(
  customer_id INT64,
  order_id INT64,
  shipping_address_city STRING,
  store_id INT64,
  order_date STRING,
  order_type STRING,
  payment_method STRING,
  PRIMARY KEY (order_id) NOT ENFORCED
);

CREATE TABLE IF NOT EXISTS cymbal_pets_demo.stores
(
  store_id INT64,
  store_name STRING,
  address_state STRING,
  address_city STRING,
  latitude FLOAT64,
  longitude FLOAT64,
  opening_hours STRUCT<Monday STRING, Tuesday STRING, Wednesday STRING, Thursday STRING, Friday STRING, Saturday STRING, Sunday STRING>,
  manager_id INT64,
  PRIMARY KEY (store_id) NOT ENFORCED
)
CLUSTER BY store_id;

CREATE TABLE IF NOT EXISTS cymbal_pets_demo.customers
(
  customer_id INT64,
  first_name STRING,
  last_name STRING,
  email STRING,
  gender STRING,
  address_city STRING,
  address_state STRING,
  loyalty_member BOOL,
  PRIMARY KEY (customer_id) NOT ENFORCED
)
CLUSTER BY customer_id;

CREATE TABLE IF NOT EXISTS cymbal_pets_demo.co_related_products_for_angelica
(
  angelica_product_id INT64,
  other_product_id INT64,
  co_purchase_count INT64,
  jaccard_similarity FLOAT64
);

Bây giờ, bạn đã xác định cấu trúc cho dữ liệu đồ thị.

4. Tải dữ liệu

Bây giờ, hãy điền dữ liệu mẫu vào các bảng từ Cloud Storage.

Chạy các câu lệnh LOAD DATA sau trong trình chỉnh sửa SQL BigQuery:

LOAD DATA INTO `cymbal_pets_demo.customers`
FROM FILES (
    format = 'AVRO',
    uris = ['gs://sample-data-and-media/cymbal-pets/tables/customers/*.avro'],
    enable_logical_types = true
);

LOAD DATA INTO `cymbal_pets_demo.order_items`
FROM FILES (
    format = 'AVRO',
    uris = ['gs://sample-data-and-media/cymbal-pets/tables/order_items/*.avro'],
    enable_logical_types = true
);

LOAD DATA INTO `cymbal_pets_demo.orders`
FROM FILES (
    format = 'AVRO',
    uris = ['gs://sample-data-and-media/cymbal-pets/tables/orders/*.avro'],
    enable_logical_types = true
);

-- 1. Create a new partitioned and clustered table from your current data
CREATE TABLE cymbal_pets_demo.orders_temp
PARTITION BY order_date
CLUSTER BY order_id
AS
SELECT
  customer_id,
  order_id,
  shipping_address_city,
  store_id,
  -- Parse the string to a DATE type. Adjust format string ('%Y-%m-%d') if necessary.
  PARSE_DATE('%Y-%m-%d', order_date) AS order_date,
  order_type,
  payment_method
FROM
  cymbal_pets_demo.orders;

-- 2. Drop the original, non-partitioned table
DROP TABLE cymbal_pets_demo.orders;

-- 3. Rename the temporary table to the original table name
ALTER TABLE cymbal_pets_demo.orders_temp RENAME TO orders;

LOAD DATA INTO `cymbal_pets_demo.products`
FROM FILES (
    format = 'AVRO',
    uris = ['gs://sample-data-and-media/cymbal-pets/tables/products/*.avro'],
    enable_logical_types = true
);

LOAD DATA INTO `cymbal_pets_demo.stores`
FROM FILES (
    format = 'AVRO',
    uris = ['gs://sample-data-and-media/cymbal-pets/tables/stores/*.avro'],
    enable_logical_types = true
);

Bạn sẽ thấy thông báo xác nhận rằng các hàng đã được tải vào từng bảng.

5. Tạo đồ thị thuộc tính

Sau khi tải dữ liệu, bạn có thể xác định đồ thị thuộc tính. Đồ thị này cho BigQuery biết bảng nào đại diện cho nút (các thực thể như Khách hàng, Sản phẩm) và bảng nào đại diện cho cạnh (các mối quan hệ như "Đã truy cập", "Đã đặt", "Có").

Giản đồ biểu đồ

Chạy câu lệnh DDL sau:

CREATE OR REPLACE PROPERTY GRAPH cymbal_pets_demo.PetsOrderGraph
NODE TABLES (
  cymbal_pets_demo.customers KEY(customer_id) LABEL Customer,
  cymbal_pets_demo.products KEY(product_id) LABEL Products,
  cymbal_pets_demo.stores KEY(store_id) LABEL Stores,
  cymbal_pets_demo.orders KEY(order_id) LABEL Orders
)
EDGE TABLES (
  cymbal_pets_demo.orders as customer_to_store_edge
    KEY (order_id)
    SOURCE KEY (customer_id) references customers(customer_id)
    DESTINATION KEY (store_id) references stores(store_id)
    LABEL Visited
    PROPERTIES ALL COLUMNS,

  cymbal_pets_demo.order_items
    KEY (order_item_id)
    SOURCE KEY (order_id) references orders(order_id)
    DESTINATION KEY (product_id) references products(product_id)
    LABEL Has
    PROPERTIES ALL COLUMNS,

  cymbal_pets_demo.orders as customer_to_orders_edge
    KEY (order_id)
    SOURCE KEY (customer_id) references customers(customer_id)
    DESTINATION KEY (order_id) references orders(order_id)
    LABEL Placed
    PROPERTIES ALL COLUMNS,

  cymbal_pets_demo.co_related_products_for_angelica
    KEY (angelica_product_id)
    SOURCE KEY (angelica_product_id) references products(product_id)
    DESTINATION KEY (other_product_id) references products(product_id)
    LABEL BoughtTogether
    PROPERTIES ALL COLUMNS
);

Thao tác này sẽ tạo đồ thị PetsOrderGraph cho phép chúng ta thực hiện việc duyệt đồ thị bằng cách sử dụng toán tử GRAPH_TABLE.

6. Trực quan hoá nhật ký mua của tất cả khách hàng

Đối với các phần trực quan hoá và đề xuất của lớp học lập trình này, chúng ta sẽ sử dụng tính năng trực quan hoá đồ thị gốc trong BigQuery Studio. Tính năng này cho phép chúng ta dễ dàng trực quan hoá kết quả đồ thị.

Ngoài ra, bạn có thể trực quan hoá trong Sổ tay BigQuery Graph bằng cách sử dụng IPython Magics. Bằng cách thêm lệnh magic %%bigquery với hàm TO_JSON, bạn có thể trực quan hoá kết quả như minh hoạ trong các phần sau.

Giả sử Cymbal Pets muốn xem trực quan 360 độ về tất cả khách hàng và các giao dịch mua mà họ đã thực hiện trong một khoảng thời gian cụ thể.

Chạy lệnh sau trong một thẻ BigQuery Studio mới:

GRAPH cymbal_pets_demo.PetsOrderGraph
  # finds the customer node and then finds all
  # the Orders nodes that are connected to that customer through the
  # Placed relationship
  MATCH (customer:Customer)-[placed:Placed]->(ordr:Orders)-[has:Has]->(product:Products)
  # filters the Orders nodes to only include those where the
  # order_date is within the last 3 months.
  WHERE ordr.order_date >= date('2024-11-27')
  # # This line finds all the Products nodes that are connected to the
  # # filtered Orders nodes through the Has relationship.
  MATCH p=(customer:Customer)-[placed:Placed]->(ordr:Orders)-[has:Has]->(product:Products)
  LIMIT 40
  RETURN 
    TO_JSON(p) as paths

Để trực quan hoá kết quả, trong ngăn Kết quả truy vấn, hãy nhấp vào Đồ thị.

Nhật ký mua hàng của tất cả khách hàng

7. Trực quan hoá nhật ký mua của Angelica

Giả sử Cymbal Pets muốn tìm hiểu sâu về một khách hàng tên là Angelica Russell. Họ muốn phân tích các sản phẩm mà Angelica đã mua trong vòng 3 tháng qua và các cửa hàng mà khách hàng đã ghé thăm.

GRAPH cymbal_pets_demo.PetsOrderGraph
  # finds the customer node with the name "Angelica Russell" and then finds all
  # the Orders nodes that are connected to that customer through the
  # Placed relationship and all the Products nodes that are connected to the
  # filtered Orders nodes through the Has relationship.
   MATCH p=(customer:Customer {first_name: 'Angelica', last_name: 'Russell'})-[placed:Placed]->(ordr:Orders)-[has:Has]->(product:Products)
  # filters the Orders nodes to only include those where the
  # order_date is within the last 3 months.
  WHERE ordr.order_date >= date('2024-11-27')
  # finds the Stores nodes where Angelica placed order from
  MATCH p2=(customer)-[visited:Visited]->(store:Stores)
  RETURN
    TO_JSON(p) as path, TO_JSON(p2) as path2

Nhật ký mua hàng của Angelica

8. Đề xuất sản phẩm bằng cách sử dụng tính năng tìm kiếm vectơ

Cymbal Pets muốn đề xuất sản phẩm cho Angelica dựa trên những gì cô ấy đã mua gần đây. Chúng ta có thể sử dụng tính năng tìm kiếm vectơ để tìm các sản phẩm có vectơ nhúng tương tự với các giao dịch mua trước đây của cô ấy.

Chạy tập lệnh SQL sau trong một ô Colab mới. Tập lệnh này:

  1. Xác định các sản phẩm mà Angelica đã mua gần đây.
  2. Sử dụng VECTOR_SEARCH để tìm 4 sản phẩm tương tự hàng đầu trong bảng products.

Lưu ý: Bước này giả định rằng bạn đã chạy AI.GENERATE_EMBEDDINGS để tạo cột vectơ nhúng trong bảng sản phẩm.

DECLARE products_bought_by_angelica ARRAY<INT64>;

-- 1. Get IDs of products bought by Angelica
SET products_bought_by_angelica = (
  SELECT ARRAY_AGG(product_id) FROM
   GRAPH_TABLE(
    cymbal_pets_demo.PetsOrderGraph
      MATCH (c:Customer {first_name: 'Angelica', last_name: 'Russell'})-[placed:Placed]->(o:Orders)
      WHERE o.order_date >= date('2024-11-27')
      MATCH (o)-[has_edge:Has]->(p:Products)
      RETURN DISTINCT p.product_id as product_id
  ));

-- 2. Find similar products using vector search
SELECT 
  query.product_name as AngelicaBought, 
  base.product_name as RecommendedProducts, 
  base.category
FROM
  VECTOR_SEARCH(
    TABLE cymbal_pets_demo.products,
    'embedding',
    (SELECT * FROM cymbal_pets_demo.products
     WHERE product_id IN UNNEST(products_bought_by_angelica)),
    'embedding',
    top_k => 4)
WHERE query.product_name <> base.product_name;

Bạn sẽ thấy danh sách các sản phẩm được đề xuất có ý nghĩa tương tự với những gì Angelica đã mua.

Kết quả tìm kiếm vectơ

9. Đề xuất bằng cách sử dụng "Mua cùng nhau" và độ tương tự Jaccard

Một kỹ thuật đề xuất mạnh mẽ khác là "Lọc cộng tác" – đề xuất các sản phẩm mà những người dùng khác thường mua cùng nhau.

Chúng ta có thể tìm thấy các sản phẩm này bằng cách duyệt đồ thị từ một khách hàng đến các sản phẩm mà họ đã mua, sau đó đến những khách hàng khác đã mua các sản phẩm đó và cuối cùng là đến các sản phẩm khác mà những khách hàng đó đã mua.

Khắc phục sai lệch về mức độ phổ biến bằng độ tương tự Jaccard

Mặc dù số lượng đồng mua thô rất hữu ích, nhưng chúng có thể bị sai lệch đối với các sản phẩm phổ biến. Một sản phẩm rất phổ biến có thể được mua cùng với nhiều thứ chỉ là do ngẫu nhiên.

Độ tương tự Jaccard giúp đề xuất thêm một bước nữa bằng cách chuẩn hoá số lượng đồng mua. Độ tương tự Jaccard đo lường mức độ tương tự giữa hai tập hợp (trong trường hợp này là các tập hợp đơn đặt hàng chứa từng sản phẩm).

Công thức tính độ tương tự Jaccard là:

Trong đó:

  • A giao B là số lượng đơn đặt hàng chứa cả sản phẩm A và sản phẩm B (số lượng đồng mua).
  • A là tổng số đơn đặt hàng chứa sản phẩm A.
  • B là tổng số đơn đặt hàng chứa sản phẩm B.

Trong ví dụ sau, tập hợp A = {b,c,e,f,g}, tập hợp B = {a,d,b,g}, giao của chúng A⋂B = {b,g}, hợp của chúng A⋃B = {a,b,c,d,e,f,g}, do đó, độ tương tự Jaccard giữa A và B là 2 / 7 = 0,285714

Tạo ứng viên và sắp xếp lại

Trong các hệ thống đề xuất thực tế hoạt động trên các tập dữ liệu khổng lồ, việc tính toán các điểm tương tự phức tạp (như Jaccard) cho tất cả các cặp sản phẩm có thể có thường là không thực tế. Thay vào đó, một mẫu phổ biến là sử dụng phương pháp gồm 2 giai đoạn:

  1. Tạo ứng viên: Sử dụng một chỉ số đơn giản, nhanh chóng (như số lượng đồng mua thô) để lọc không gian tìm kiếm và tìm một số lượng ứng viên có thể quản lý được (ví dụ: 10 ứng viên hàng đầu).
  2. Sắp xếp lại: Áp dụng một chỉ số chính xác hơn nhưng tốn nhiều tài nguyên tính toán hơn (như độ tương tự Jaccard) để xếp hạng tập hợp nhỏ các ứng viên đó và chọn các đề xuất hàng đầu cuối cùng.

Trong lớp học lập trình này, chúng ta sẽ tuân theo mẫu này:

  • Giai đoạn 1: Chạy truy vấn để tìm 10 sản phẩm được đồng mua hàng đầu cho mỗi sản phẩm, dựa trên số lượng đồng mua thô và lưu trữ chúng trong một bảng.
  • Giai đoạn 2: Sử dụng truy vấn đồ thị để truy xuất các ứng viên này, xếp hạng chúng theo độ tương tự Jaccard và trả về 3 ứng viên hàng đầu.

[!WARNING] Nhược điểm: Bằng cách lọc theo số lượng thô ở Giai đoạn 1, chúng ta có thể mất "khả năng thu hồi" các giao dịch đồng mua có tần suất thấp nhưng rất cụ thể. Nếu một sản phẩm rất giống với một sản phẩm khác nhưng cả hai đều hiếm khi được mua, thì sản phẩm đó có thể không lọt vào top 10 ứng viên và sẽ bị bỏ lỡ.

Chạy truy vấn sau để tính toán cả số lượng đồng mua thô và độ tương tự Jaccard, đồng thời lưu trữ 10 ứng viên hàng đầu theo số lượng thô:

CREATE OR REPLACE TABLE cymbal_pets_demo.co_related_products_for_angelica AS
-- Calculate the total number of orders for each product
WITH ProductOrderCounts AS (
    SELECT product_id, COUNT(DISTINCT order_id) as total_count
    FROM cymbal_pets_demo.order_items
    GROUP BY product_id
),
-- Calculate the intersection of each product pairs
CoPurchases AS (
    SELECT
        angelicaProduct.product_id AS angelica_product_id,
        otherProduct.product_id AS other_product_id,
        count(DISTINCT otherOrder.order_id) AS co_purchase_count
    FROM
        GRAPH_TABLE (cymbal_pets_demo.PetsOrderGraph
          MATCH (angelica:Customer {first_name: 'Angelica', last_name: 'Russell'})-[:Placed]->(o:Orders)-[:Has]->(angelicaProduct:Products)
          WHERE o.order_date >= date('2024-11-27')
          WITH angelica, angelicaProduct
          MATCH (otherCustomer:Customer)-[:Placed]->(otherOrder:Orders)-[:Has]->(angelicaProduct)
          WHERE otherCustomer <> angelica
          WITH angelicaProduct, otherOrder
          MATCH (otherOrder)-[:HAS]->(otherProduct:Products)
          WHERE angelicaProduct <> otherProduct
          RETURN angelicaProduct, otherProduct, otherOrder
        )
    GROUP BY
        angelicaProduct.product_id, otherProduct.product_id
)
SELECT * FROM (
    SELECT
        cp.angelica_product_id,
        cp.other_product_id,
        cp.co_purchase_count,
        -- The Jaccard calculation, which is the intersection of A and B divided by (A + B - intersection)
        SAFE_DIVIDE(cp.co_purchase_count, (poc1.total_count + poc2.total_count - cp.co_purchase_count)) AS jaccard_similarity,
        ROW_NUMBER() OVER (PARTITION BY cp.angelica_product_id ORDER BY cp.co_purchase_count DESC) AS rn
    FROM CoPurchases cp
    JOIN ProductOrderCounts poc1 ON cp.angelica_product_id = poc1.product_id
    JOIN ProductOrderCounts poc2 ON cp.other_product_id = poc2.product_id
)
WHERE rn <= 10;

Logic đề xuất

Chạy truy vấn này để đề xuất 3 sản phẩm hàng đầu cho mỗi giao dịch mua của Angelica, được kết nối trực tiếp thông qua cạnh BoughtTogether, cho thấy cả số lượng đồng mua và độ tương tự Jaccard:

SELECT * FROM GRAPH_TABLE(
  cymbal_pets_demo.PetsOrderGraph
  MATCH (customer:Customer {first_name: 'Angelica', last_name: 'Russell'})-[placed:Placed]->(ordr:Orders)
  WHERE ordr.order_date >= date('2024-11-27')
  MATCH (ordr)-[has:Has]->(product:Products)
  MATCH (product)-[bought_together:BoughtTogether]->(recommended_product:Products)
  RETURN 
    product.product_name AS OriginalProduct,
    recommended_product.product_name AS Recommended,
    bought_together.co_purchase_count AS Strength,
    bought_together.jaccard_similarity AS JaccardSimilarity
)
-- Rank product recommendations by Jaccard Similarity
QUALIFY ROW_NUMBER() OVER (PARTITION BY OriginalProduct ORDER BY JaccardSimilarity DESC) <= 3
ORDER BY OriginalProduct;

Truy vấn này duyệt từ Khách hàng -> Đơn đặt hàng -> Sản phẩm -> (Mua cùng nhau) -> Sản phẩm được đề xuất, cho bạn thấy các đề xuất dựa trên hành vi mua tập thể và truy xuất điểm tương tự của chúng.

Mua cùng nhau

10. Dọn dẹp

Để tránh các khoản phí liên tục cho tài khoản Google Cloud, hãy xoá các tài nguyên được tạo trong lớp học lập trình này.

Xoá tập dữ liệu và tất cả các bảng:

DROP SCHEMA IF EXISTS cymbal_pets_demo CASCADE;

Nếu bạn đã tạo một dự án mới cho lớp học lập trình này, bạn cũng có thể xoá dự án đó:

gcloud projects delete $PROJECT_ID

11. Xin chúc mừng

Xin chúc mừng! Bạn đã xây dựng thành công chân dung khách hàng 360 độ và công cụ đề xuất bằng BigQuery Graph.

Kiến thức bạn học được

  • Cách tạo đồ thị thuộc tính trong BigQuery.
  • Cách tải dữ liệu vào các nút và cạnh của đồ thị.
  • Cách truy vấn các mẫu đồ thị bằng cách sử dụng GRAPH_TABLEMATCH.
  • Cách kết hợp truy vấn đồ thị với tính năng tìm kiếm vectơ để đưa ra các đề xuất kết hợp.

Các bước tiếp theo