Taming the ML Firehose: Scaling Feature Consistency

Bài tóm tắt Uber 2026 3. Quy mô lớn Độ khó: Khó

Uber · 2026 · 3. Quy mô lớn · Độ khó: Khó — bài gốc

Bối cảnh

  • Mục tiêu: feature dùng lúc inference chính là feature dùng để train model kế tiếp. Trước đó hai đường lệch nhau:
    • Online: feature truyền vào model lúc phục vụ có thể khác định dạng với lúc train (en thay vì en-US), nên model nhận giá trị chưa từng thấy; tín hiệu mạnh âm thầm yếu đi, phân phối feature trôi, không có lỗi rõ ràng.
    • Offline: dữ liệu train ghép từ app log, bảng feature Hive, click log qua nhiều ETL phụ thuộc nhau; pipeline offline bắt đầu sinh định dạng khác (jp_JA so với jp-JA online); lineage ETL mong manh (thiếu partition, upstream đổi); một số feature quan trọng trễ nhiều ngày từ production tới dữ liệu train; developer mất hàng tuần để lần ra lỗi.

Thay đổi chính

  • Feature logging: ghi lại chính các feature được dùng lúc inference rồi dựng bảng train từ đó.
  • Vấn đề quy mô: model gợi ý dựa trên transformer chạy tới 8 triệu QPS; log mọi dự đoán là hàng trăm tỉ bản ghi mỗi ngày, khoảng 5 nghìn tỉ dòng mỗi tháng, khoảng 1,7 PB mỗi ngày chưa nén, chi phí hạ tầng hàng triệu USD. Tên feature dài (namespace, nhiều tầng tổng hợp); với model gợi ý nhà hàng, gửi mọi feature là khoảng 9,3 GiB/giây, vượt xa năng lực các cụm Kafka hiện có. Client gửi nhiều feature hơn model dùng. Và phần lớn dự đoán không có ích cho train: chấm hàng nghìn ứng viên, khoảng 40% còn sau lọc và xếp hạng, chỉ khoảng 5% thành impression trên máy người dùng.
  • Ba cách giảm: allow list chỉ log feature model thật sự dùng (payload nhỏ đi 4–5 lần); ánh xạ tên feature thành enum số nguyên tất định lúc serialize ở endpoint inference; lọc theo impression: Flink join luồng dự đoán với event client trên Kafka bằng cửa sổ thời gian, giữ trong bộ nhớ vài phút đủ cho bách phân vị 90 của thời gian từ phiên tới impression.
  • Bài học vận hành Flink: tăng parallelism không tự giải nghẽn; profile từng operator và chỉnh riêng (512 cho operator trước join, 768 cho join, parallelism Flink lớn nhất từng chạy ở Uber). Checkpoint RocksDB mặc định phình hơn 12 TB/giờ tới mức ghi lên cloud bắt đầu hỏng; chuyển sang quản lý state riêng: chỉ giữ metadata cần thiết, xoá bản ghi ngay sau khi join, dedupe đầu vào. Thông lượng đến từ nhiều cải tiến nhỏ (dùng lại object, payload có kiểu, cache cấu hình, bớt serialize), consumer lag đỉnh giảm khoảng 70%. Rollback tự động, alert-as-code, query kiểm tất định, môi trường thử giống production.
  • Feature dạng chuỗi của transformer (mảng cửa hàng) vượt giới hạn payload Kafka và làm khó watermark theo từng cửa hàng; hệ đọc chiều của model (cache reflection) để nhận ra feature mảng và trải phẳng thành từng bản ghi key-value trước khi gửi; dữ liệu chia round-robin qua nhiều cụm Kafka.
  • Kiểm offline trước khi dùng: train model production trên bảng mới và, để so công bằng, train lại trên bảng ETL cũ cùng khoảng ngày; đạt AUC, MAP bằng hoặc hơn thì mới train model ứng viên cho thí nghiệm kế tiếp.
  • Kết quả, theo Uber: tỉ lệ lệch của các feature chính từ trên 10% xuống 0%; độ tươi của nhiều feature ưu tiên từ nhiều ngày xuống vài giờ.

Vì sao

  • Hai đường tính feature độc lập (online và offline) sẽ lệch nhau theo những cách nhỏ không ai thấy; ghi lại đúng thứ đã phục vụ là cách bỏ hẳn một đường.

Bài học cho SWE

  • Ghi lại input thật của quyết định thay vì tính lại ở offline: nguồn sự thật duy nhất cho train.
  • Ở quy mô lớn, tên trường dài và trường thừa là chi phí băng thông; ánh xạ sang ID và chỉ gửi phần cần.
  • Lọc dữ liệu theo giá trị dùng về sau (chỉ ứng viên đã thành impression) trước khi lưu.
  • State của stream join phải được thiết kế (xoá sớm, chỉ giữ metadata), không để mặc định phình.

Liên quan