Liên kết phá hủy mô hình của bạn: Sự bùng nổ số lượng bản ghi trong cơ sở dữ liệu sản xuất phá hủy các tính năng ML như thế nào
Không ai nói với tôi rằng một đợt chuẩn hóa cơ sở dữ liệu sẽ làm hỏng mô hình dự đoán doanh thu của chúng tôi.
Sự việc xảy ra sáu tuần sau khi chúng tôi triển khai. Nhóm backend đã thực hiện mọi thứ đúng quy trình. Họ đã tách bảng `customer_orders` nguyên khối thành ba bảng được chuẩn hóa: `orders`, `order_items` và `order_promotions`. Mỗi bảng đều sạch hơn, nhanh hơn và được lập chỉ mục tốt hơn. Quá trình di chuyển diễn ra đúng như sách giáo khoa.
Tuy nhiên, quy trình trích xuất tính năng của chúng tôi, vốn liên kết các bảng này để tính toán các tổng hợp ở cấp độ khách hàng, lại không diễn ra như sách giáo khoa. Nó đã bị
Lỗi gây hỏng mô hình: Sự bùng nổ số lượng bản ghi trong cơ sở dữ liệu sản xuất phá hủy các tính năng học máy
Không ai nói với tôi rằng một đợt chuẩn hóa cơ sở dữ liệu sẽ làm hỏng mô hình dự đoán doanh thu của chúng tôi.
Sự việc xảy ra sáu tuần sau khi chúng tôi triển khai. Nhóm backend đã thực hiện mọi thứ đúng quy trình. Họ đã chia bảng `customer_orders` đơn khối thành ba bảng được chuẩn hóa: `orders`, `order_items` và `order_promotions`. Mỗi bảng đều sạch hơn, nhanh hơn và được lập chỉ mục tốt hơn. Quá trình di chuyển diễn ra đúng như sách giáo khoa.
Tuy nhiên, quy trình tính năng của chúng tôi, vốn kết nối các bảng này để tính toán các tổng hợp cấp độ khách hàng, lại không theo sách giáo khoa. Nó được viết khi `customer_orders` là một bảng duy nhất với một hàng cho mỗi đơn hàng. Sau khi di chuyển, việc kết nối `orders` với `order_items` tạo ra nhiều hàng cho mỗi đơn hàng vì mỗi đơn hàng hiện có nhiều mặt hàng. Tính năng `SUM(order_value)` của chúng tôi, trước đây tổng hợp một hàng cho mỗi đơn hàng, giờ đây tổng hợp một hàng cho mỗi mặt hàng. Đối với một khách hàng có 10 đơn hàng, trung bình mỗi đơn hàng có 5 mặt hàng, tính năng này bị thổi phồng lên 5 lần.
Mô hình dự đoán doanh thu đã được huấn luyện trên các tính năng được tính toán từ lược đồ cũ. Trong môi trường sản xuất, mọi tính năng liên quan đến giá trị đơn hàng đều được nhân với số lượng mặt hàng trung bình của đơn hàng. Mô hình không hề hay biết. Nó chưa bao giờ thấy sự phân phối này. Nó bắt đầu dự đoán các con số doanh thu cao hơn thực tế từ 4 đến 6 lần.
Nhóm kinh doanh là những người đầu tiên phát hiện ra điều này. Không phải nhóm ML. Không phải nhóm dữ liệu. Mà là nhóm kinh doanh, bởi vì mô hình đang tạo ra các dự báo doanh thu không có ý nghĩa thương mại.
Đây là một sự bùng nổ số lượng bản ghi (cardinality explosion). Đây là một trong những chế độ lỗi phổ biến nhất và ít được thảo luận nhất trong các quy trình ML sản xuất. Nó hoàn toàn có thể ngăn chặn được bằng cách sử dụng các cơ sở dữ liệu tổng hợp được thiết kế rõ ràng để kiểm tra số lượng bản ghi kết nối trước khi bất kỳ thay đổi lược đồ nào ảnh hưởng đến quy trình tính năng.
**Ý nghĩa của sự bùng nổ số lượng bản ghi trong thực tế**
Số lượng bản ghi (cardinality) trong một phép nối cơ sở dữ liệu đề cập đến số lượng hàng được tạo ra so với số lượng hàng trong bảng nguồn.
Một phép nối một-một (one-to-one join) duy trì số lượng bản ghi. Một hàng khách hàng nối với một hàng hồ sơ. Đầu ra có cùng số lượng hàng với đầu vào.
Một phép nối một-nhiều (one-to-many join) nhân số lượng bản ghi. Một hàng khách hàng nối với nhiều hàng đơn hàng. Đầu ra có nhiều hàng hơn đầu vào, và bất kỳ tổng hợp nào được tính toán mà không có mệnh đề `GROUP BY` chính xác sẽ tạo ra kết quả sai.
Hầu hết các quy trình tính năng được viết với một giả định cụ thể về số lượng bản ghi. Khi lược đồ thay đổi và giả định đó bị phá vỡ, quy trình vẫn tiếp tục chạy mà không có lỗi nhưng tạo ra các tính năng bị thổi phồng hoặc giảm đi một cách âm thầm.
Bốn mẫu phổ biến nhất gây ra sự bùng nổ số lượng bản ghi trong các quy trình tính năng ML là:
* **Chuẩn hóa bảng.** Một bảng không chuẩn hóa được chia thành một bảng cha và một bảng con. Mọi phép nối trước đây liên quan đến bảng không chuẩn hóa giờ đây tạo ra nhiều hàng cho mỗi bản ghi gốc.
* **Bảng quan hệ mới.** Một bảng quan hệ nhiều-nhiều (many-to-many relationship table) được thêm vào giữa hai bảng hiện có. Bất kỳ truy vấn nào nối qua nó giờ đây sẽ mở rộng.
* **Dữ liệu lịch sử được điền lại (backfilled).** Một bảng trước đây có các bản ghi thưa thớt được điền lại bằng các sự kiện lịch sử. Số lượng bản ghi trung bình trên mỗi bản ghi cha tăng vọt chỉ sau một đêm.
* **Sự trôi dạt của bảng tổng hợp.** Một bảng tóm tắt đã được tổng hợp trước đó được thay thế bằng một bảng sự kiện thô để linh hoạt hơn. Các truy vấn trước đây đọc một hàng tóm tắt giờ đây đọc hàng trăm hàng sự kiện.
Không có điều nào trong số này gây ra lỗi quy trình. Chúng gây ra sự biến dạng quy trình.
**Tại sao cơ sở dữ liệu tổng hợp là công cụ phát hiện phù hợp**
Sự bùng nổ số lượng bản ghi có thể được phát hiện trước khi đưa vào sản xuất nếu bạn
một cơ sở dữ liệu tổng hợp:
Phản ánh lược đồ sản xuất, bao gồm cấu trúc chuẩn hóa mới.
Phản ánh phân phối số lượng bản ghi thực tế cho mọi mối quan hệ cha-con.
Được sử dụng để chạy quy trình tính năng và xác thực phân phối đầu ra trước khi triển khai.
Điểm mấu chốt là: không cần phải khắc phục sự bùng nổ số lượng bản ghi sau khi nó xảy ra. Cần làm cho nó hiển thị trước khi nó tiếp cận một mô hình.
Bước 1: Tạo Cơ sở dữ liệu tổng hợp với Kiểm soát số lượng bản ghi rõ ràng
python
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
from faker import Faker
fake = Faker('en_IN')
np.random.seed(42)
def generate_ecommerce_database(n_customers=1000, avg_orders_per_customer=8, avg_items_per_order=4, # ← Đây là tham số số lượng bản ghi
avg_promotions_per_order=1.2):
"""Tạo cơ sở dữ liệu thương mại điện tử tổng hợp với kiểm soát số lượng bản ghi rõ ràng.
avg_items_per_order kiểm soát số lượng bản ghi một-nhiều giữa
đơn hàng và các mặt hàng trong đơn hàng. Đây là tham số thay đổi khi
nhóm backend chuẩn hóa lược đồ.
Thay đổi tham số này từ 1 (giả định lược đồ cũ) thành 4 (lược đồ thực tế)
sẽ làm lộ ra các rủi ro bùng nổ số lượng bản ghi trong quy trình tính năng."""
# Tạo khách hàng
customer_ids = [f'CUST{str(i).zfill(6)}' for i in range(1, n_customers + 1)]
start = datetime(2022, 1, 1)
end = datetime(2025, 12, 31)
span = (end - start).days
customers_df = pd.DataFrame({
'customer_id': customer_ids,
'signup_date': [start + timedelta(days=int(np.random.randint(0, span))) for _ in range(n_customers)],
'segment': np.random.choice(['bronze', 'silver', 'gold'], size=n_customers, p=[0.6, 0.3, 0.1])
})
# Tạo đơn hàng
order_rows = []
order_counter = 1
for _, customer in customers_df.iterrows():
n_orders = max(1, np.random.poisson(avg_orders_per_customer))
signup = customer['signup_date']
days_active = (datetime(2026, 1, 1) - signup).days
for _ in range(n_orders):
order_date = signup + timedelta(days=int(np.random.randint(0, max(1, days_active))))
order_rows.append({
'order_id': f'ORD{str(order_counter).zfill(8)}',
'customer_id': customer['customer_id'],
'order_date': order_date,
'order_status': np.random.choice(['completed', 'cancelled', 'refunded'], p=[0.82, 0.12, 0.06])
})
order_counter += 1
orders_df = pd.DataFrame(order_rows)
# Tạo các mặt hàng trong đơn hàng (một-nhiều: đây là điểm bùng nổ)
item_rows = []
item_counter = 1
for _, order in orders_df.iterrows():
n_items = max(1, np.random.poisson(avg_items_per_order))
for _ in range(n_items):
unit_price = round(np.random.lognormal(4.5, 0.8), 2)
quantity =
Nguồn tin: Medium Towards AI — Tác giả: Jitendra Devabhakthuni. Bản dịch tiếng Việt do AI thực hiện, có thể có sai sót.