loader

Trong quá trình vận hành hệ thống dự báo cho dự án bán lẻ, Team Ayai chúng tôi đã gặp phải một hiện tượng kỳ lạ: Các biểu đồ trực quan cho thấy đường dự báo bám rất sát thực tế, nhưng chỉ số đo lường hiệu suất (KPI) – cụ thể là WAPE – lại báo cáo sai số lên tới 177%.

Sau khi “mổ xẻ” module , chúng tôi đã phát hiện ra một bài học đắt giá về Độ hạt dữ liệu (Data Granularity) và cách nó có thể bóp méo hoàn toàn kết quả của các mô hình Machine Learning.

1. Bản chất của sai số: Sự lệch pha về độ hạt

Trong hệ thống dữ liệu bán lẻ, dữ liệu thường tồn tại ở hai trạng thái với mục đích khác nhau:

  • Dữ liệu Giao dịch (Transactional Data – Actuals): Lưu trữ trong DB dưới dạng từng dòng đơn hàng. Một sản phẩm A tại kho B trong một ngày có thể phát sinh 10, 20 hoặc cả trăm giao dịch nhỏ lẻ.
  • Dữ liệu Dự báo (Forecasted Data): Để phục vụ lập kế hoạch tồn kho, mô hình AI thường đưa ra con số Tổng nhu cầu ngày (Daily Total). Ví dụ: “Ngày mai, chi nhánh này cần 40 bút bi”.

Lỗi logic xảy ra khi hàm đánh giá _append_records thực hiện phép tính sai số bằng cách lấy một giá trị dự báo tổng (Aggregate) đem so sánh lần lượt với từng dòng giao dịch riêng lẻ (Atomic).

Mô phỏng toán học của lỗi:

Giả sử Forecast cho ngày D là 40 units. Thực tế bán được 41 units thông qua 3 đơn hàng: a=10, b=11, c=20.

WAPE=|ActualiForecasti|ActualiWAPE = \frac{\sum \left| Actual_i – Forecast_i \right|}{\sum Actual_i}
  • Cách tính đúng (Sau khi Aggregate):
    • Error = | (10 + 11 + 20) - 40 | = | 41 - 40 | = 1 (Sai số cực nhỏ)
  • Cách tính sai (Trước khi fix – lặp qua từng giao dịch):
    • Errortotal = |10 – 40| + |11 – 40| + |20 – 40| = 30 + 29 + 20 = 79 (Sai số bị thổi phồng lên gấp 79 lần!)

2. Phân tích tác động: Từ 9,369 dòng xuống còn 84 dòng

Một trong những dấu hiệu rõ nhất của lỗi này chính là Sample Count (Số lượng mẫu đánh giá).

Trước khi fix, hệ thống báo cáo sample_count = 9,369. Đây thực tế là tổng số giao dịch (transactions) trong tệp dữ liệu test. Việc đánh giá dựa trên từng giao dịch không mang lại giá trị cho bài toán Supply Chain, vì chúng ta không dự báo cho từng khách hàng vãng lai, chúng ta dự báo cho tổng lượng hàng rời kho.

Sau khi thực hiện gộp dữ liệu (Aggregate) theo cặp (product_id, location_id)date, con số này giảm xuống còn khoảng 84.

  • Công thức: 84 = (Số lượng SKU-Location) x (Số ngày trong chu kỳ Eval ).
  • Đây mới là con số thực tế đại diện cho số lượng “quyết định dự báo” mà model thực hiện. Khi sample_count chuẩn hóa, WAPE tự động rơi từ mức “thảm họa” 177% về mức “khả thi”.

3. Giải pháp kỹ thuật và Tối ưu hóa Code

Chúng tôi đã tái cấu trúc lại luồng xử lý trong forecast_x1 . Thay vì map trực tiếp actual_df vào vòng lặp, chúng tôi thực hiện một bước “Pre-evaluation Processing”:

  1. Chuẩn hóa Actuals: Sử dụng hàm groupby trên Pandas để nén dữ liệu giao dịch thành dữ liệu ngày.
  2. Đồng bộ Key: Tạo ra một Unique Key kết hợp giữa ID sản phẩm + ID vị trí + Ngày để đảm bảo phép Join (kết nối) giữa Forecast và Actual là 1-1.
  3. Xử lý giá trị thiếu: Trong trường hợp một ngày có Forecast nhưng không có giao dịch thực tế (Actual = 0), hệ thống phải tự động fill 0 thay vì bỏ qua record đó để tránh làm đẹp chỉ số một cách giả tạo.
# Logic core sau khi điều chỉnh
actual_daily = actual_df.groupby(['product_id', 'location_id', 'date']).agg({
    'sales_quantity': 'sum'
}).reset_index()

# Đánh giá trên dữ liệu đã gộp
evaluation_results = calculate_wape(actual_daily, forecast_df)

4. Bài học cho các kỹ sư dữ liệu (Data Engineers)

Vấn đề này không chỉ là một bug code đơn thuần, nó là bài học về tư duy hệ thống:

  • Hiểu về Granularity: Luôn đặt câu hỏi “Dữ liệu này đang ở cấp độ nào?”. Dự báo cấp ngày không thể so sánh với giao dịch cấp giây.
  • Sự nguy hiểm của WAPE: WAPE là chỉ số rất nhạy cảm với trọng số. Khi tử số (lỗi tuyệt đối) bị lặp lại quá nhiều lần trong khi mẫu số (tổng thực tế) không đổi, chỉ số này sẽ mất đi ý nghĩa.
  • Trực quan hóa là chìa khóa: Nếu chúng tôi chỉ nhìn vào con số 177% mà không vẽ biểu đồ so sánh đường Forecast và Actual, có lẽ chúng tôi đã tốn hàng tuần để tinh chỉnh Model (Fine-tuning) trong khi thực tế Model đã chạy rất tốt.

Kết luận

Việc đưa WAPE từ 177% về giá trị thực không chỉ giúp báo cáo đẹp hơn, mà quan trọng nhất là phục hồi niềm tin của bộ phận vận hành vào hệ thống AI. Trong thế giới của Data Science, đôi khi một dòng lệnh groupby đúng chỗ còn giá trị hơn cả việc thay đổi toàn bộ kiến trúc mô hình Neural Network.

Thông tin dự án:

  • Project: Ayai – Retail Forecasting
  • Issue: Evaluation Granularity Mismatch.