Con người không ăn bằng lịch sử, họ ăn bằng khoảnh khắc
Có một chuyện khá buồn cười.
Thứ Hai, bạn đặt cơm gà.
Thứ Ba, bạn tiếp tục đặt cơm gà.
Thứ Tư, vì không biết ăn gì, bạn lại đặt cơm gà.
Hệ thống nhìn vào lịch sử và đi đến một kết luận rất khoa học:
Người này yêu cơm gà.
Thứ Năm, bạn vừa mở ứng dụng lên, cơm gà xuất hiện ở hàng đầu tiên.
Thứ Sáu, vẫn là cơm gà.
Thứ Bảy, cơm gà tiếp tục đứng đó, nhìn bạn bằng ánh mắt đầy hy vọng.
Đến Chủ Nhật, hệ thống gần như tin rằng mục đích tồn tại của bạn trên Trái Đất là ăn cơm gà.
Nhưng sự thật có thể đơn giản hơn nhiều.
Ba ngày đó bạn tăng ca.
Quán cơm gà ở gần công ty.
Giao nhanh.
Giá rẻ.
Không phải suy nghĩ.
Hôm nay bạn đang ở nhà cùng gia đình.
Ngoài trời mưa.
Mọi người muốn ăn lẩu.
Đứa nhỏ muốn gà rán.
Người lớn muốn món Việt.
Còn bạn chỉ muốn một thứ gì đó đến trong vòng ba mươi phút để khỏi phải rửa chén.
Một hệ thống chỉ nhìn vào thống kê cũ sẽ tiếp tục đưa cơm gà lên đầu.
Một hệ thống hiểu được chuỗi hành vi, thời gian, vị trí và những tín hiệu vừa mới xảy ra có thể nhận ra rằng hôm nay là một câu chuyện hoàn toàn khác.
Đó cũng là điều khiến mình tâm đắc khi đọc bài viết “Next-Gen Restaurant Recommendation with Generative Modeling and Real-Time Features” của Uber Engineering.
Uber không chỉ thay một mô hình recommendation cũ bằng một mô hình lớn hơn.
Họ thay đổi cách nhìn về người dùng.
Từ một bảng thống kê bất động.
Thành một câu chuyện đang diễn ra.
Recommendation không phải đọc suy nghĩ
Khi mở Uber Eats, người dùng có thể nhìn thấy hàng trăm nhà hàng, cửa hàng thực phẩm, siêu thị và nhiều loại dịch vụ khác nhau.
Không ai muốn ngồi xem hết.
Nhiệm vụ của home feed là thu nhỏ một thành phố rộng lớn thành vài lựa chọn có khả năng phù hợp nhất với người đang cầm điện thoại.
Đối với người dùng, feed tốt giúp giảm bớt việc phải suy nghĩ.
Đối với nhà hàng, vị trí xuất hiện trên feed có thể quyết định họ được nhìn thấy hay bị chìm xuống dưới hàng trăm đối thủ khác.
Uber mô tả home feed là cửa ngõ khám phá chính của hàng triệu người dùng, được vận hành bởi hệ thống recommendation tổng hợp hàng tỷ tín hiệu, từ hành vi gần thời gian thực tới ngữ cảnh địa lý. Khi nền tảng mở rộng từ nhà hàng sang hàng tạp hóa, đồ uống và bán lẻ, bài toán này càng trở nên phức tạp. (Uber)
Nghe tới đây, recommendation có vẻ giống một nhà ngoại cảm.
Nhưng thực tế nó không đọc được suy nghĩ.
Nó chỉ nhìn vào dấu chân.
Bạn bấm vào đâu.
Bạn bỏ qua món gì.
Bạn đặt món nào.
Bạn đang ở khu vực nào.
Mấy giờ rồi.
Bạn vừa tìm “bún bò” hay vừa xem năm quán sushi.
Từ hàng tỷ dấu chân nhỏ đó, hệ thống cố đoán xem bước tiếp theo của bạn sẽ đi về đâu.
Vấn đề là dấu chân không đứng yên.
Một người có thể thích cà phê vào buổi sáng, cơm văn phòng vào buổi trưa, món ăn gia đình vào cuối tuần và một ly trà sữa rất vô lý vào lúc 11 giờ đêm.
Con người không phải một dòng cố định trong database.
Con người là một biến số biết đói.
Cách cũ: Đếm những gì người dùng từng làm
Trong nhiều năm, mô hình DeepCVR của Uber Eats chủ yếu dựa vào các đặc trưng thống kê tổng hợp và những feature được thiết kế thủ công để dự đoán mức độ phù hợp giữa người dùng với một nhà hàng.
Ví dụ, hệ thống có thể biết:
- Người này từng đặt món Việt bao nhiêu lần.
- Giá trị đơn hàng trung bình là bao nhiêu.
- Người này thường đặt trong khung giờ nào.
- Nhà hàng có tỷ lệ được bấm vào bao nhiêu.
- Khoảng cách từ người dùng tới nhà hàng.
- Điểm đánh giá của quán.
- Mức độ phổ biến của loại món ăn.
Đây là cách làm hợp lý.
Nếu một người đã đặt phở hai mươi lần, khả năng họ đặt phở lần thứ hai mươi mốt không phải bằng không.
Nếu một quán cách người dùng ba mươi kilomet, việc xếp nó xuống thấp cũng không phải ý tưởng tệ.
Nhưng số liệu tổng hợp có một nhược điểm lớn.
Nó biết bao nhiêu, nhưng thường không biết theo thứ tự nào.
Giả sử lịch sử của hai người đều có:
- 5 lần xem pizza
- 3 lần xem sushi
- 2 lần đặt cơm
Nhìn vào bảng thống kê, hai người có vẻ giống nhau.
Nhưng chuỗi hành vi của người thứ nhất có thể là:
1Pizza → Pizza → Pizza → Sushi → Sushi → Cơm
Người này có thể đã chán pizza và đang chuyển sang món khác.
Trong khi người thứ hai lại có chuỗi:
1Cơm → Sushi → Sushi → Pizza → Pizza → Pizza
Người này vừa bắt đầu quan tâm tới pizza.
Các con số tổng cộng giống nhau.
Ý định hiện tại lại khác nhau.
Giống như hai bộ phim có cùng số lượng diễn viên, xe hơi và tiếng súng.
Không có nghĩa nội dung của chúng giống nhau.
Thứ tự tạo ra câu chuyện.
Một bảng chấm công không kể được cuộc đời của nhân viên
Feature thống kê giống như bảng chấm công.
Nó cho biết một nhân viên đã đi làm bao nhiêu ngày.
Nhưng không cho biết tuần trước người đó vừa chuyển bộ phận.
Không cho biết hôm qua họ vừa nghỉ bệnh.
Không cho biết sáng nay họ bước vào công ty với một lá đơn xin nghỉ việc trong túi.
Recommendation cũng vậy.
Một bảng ghi:
1Người dùng thích món Việt: 72%
2Người dùng thích đồ ăn nhanh: 18%
3Người dùng thích món Nhật: 10%
trông rất chắc chắn.
Nhưng nó có thể đã được tạo ra từ hành vi của ba tháng trước.
Trong ba tháng đó, người dùng có thể đã chuyển nhà.
Đổi công ty.
Bắt đầu ăn chay.
Có con nhỏ.
Hoặc đơn giản là vừa nhận lương và hôm nay muốn ăn sang.
Thống kê lịch sử vẫn có giá trị.
Nhưng nó giống phần nền của bức tranh.
Nó không nên che mất thứ vừa xảy ra ở phía trước.
Bước chuyển đầu tiên: Biến hành vi thành một câu chuyện
Để hiểu rõ hơn ý định đang thay đổi của người dùng, Uber bổ sung một nhánh xử lý chuỗi hành vi bằng Transformer.
Kiến trúc mới ban đầu là một hệ thống lai gồm hai đường:
- Nhánh DCNv2 tiếp tục xử lý các feature số, feature phân loại, đặc trưng thưa và những thống kê truyền thống.
- Nhánh Transformer xử lý chuỗi hành động theo thời gian như click và order.
Sau đó, biểu diễn được học từ chuỗi hành vi sẽ được ghép trở lại với mô hình DeepCVR chính để đưa ra dự đoán cuối cùng. (Uber)
Có thể hình dung mô hình cũ giống một người bán hàng cầm cuốn sổ:
Anh Tùng đã mua món Việt 14 lần. Chắc hôm nay anh ấy tiếp tục mua món Việt.
Nhánh Transformer giống một người bán hàng chịu khó quan sát thêm:
Nhưng năm phút vừa rồi anh ấy xem ba quán pizza, bấm vào một cửa hàng tiện lợi và tìm “đồ ăn cho bé”. Có lẽ hôm nay đừng đưa bún bò lên trước.
Hai cách nhìn không loại trừ nhau.
Một bên hiểu thói quen dài hạn.
Một bên hiểu chuyển động gần đây.
Ghép chúng lại giúp hệ thống vừa nhớ người dùng là ai, vừa nhìn thấy người dùng đang làm gì.
Transformer trong recommendation không phải để viết thơ về bún bò
Khi nghe tới Transformer, nhiều người nghĩ ngay tới ChatGPT.
Một mô hình biết viết văn.
Trả lời câu hỏi.
Sinh code.
Thỉnh thoảng bịa ra một thư viện Python chưa từng tồn tại.
Nhưng Transformer không sinh ra chỉ để nói chuyện.
Điểm mạnh quan trọng của nó là cơ chế attention, cho phép mô hình xem xét mối quan hệ giữa nhiều phần tử trong một chuỗi.
Trong recommendation, mỗi hành động của người dùng có thể được xem như một “từ”.
1Click quán A
2→ xem menu
3→ quay lại
4→ tìm “pizza”
5→ click quán B
6→ thêm món vào giỏ
Cả chuỗi giống một câu.
Mô hình không chỉ đếm từng hành động riêng lẻ.
Nó tìm hiểu hành động nào có liên quan tới hành động nào.
Thứ gì vừa xảy ra.
Thứ gì nên được chú ý nhiều hơn.
Các mô hình như Behavior Sequence Transformer từng được đề xuất để khai thác tín hiệu tuần tự trong hành vi thương mại điện tử. DIN cũng nhấn mạnh rằng sở thích của người dùng nên được biểu diễn trong mối quan hệ với chính món hàng đang được đánh giá, thay vì nén toàn bộ lịch sử thành một vector bất động. (arXiv)
Nói theo cách lúa nhất có thể:
Transformer không chỉ hỏi:
Người này từng ăn gì?
Nó hỏi thêm:
Người này vừa làm gì, theo thứ tự nào, và những việc đó đang dẫn tới món nào?
Target-aware: Cùng một quá khứ, nhưng hỏi về từng quán khác nhau
Một chi tiết kỹ thuật mình thấy rất hay trong kiến trúc của Uber là target-aware sequence modeling.
Thay vì đọc lịch sử người dùng rồi tạo ra một biểu diễn chung cho mọi nhà hàng, hệ thống gắn thêm nhà hàng đang cần chấm điểm vào cuối chuỗi.
Ví dụ, lịch sử người dùng là:
1Click sushi
2→ đặt cơm Nhật
3→ xem ramen
Khi chấm điểm một quán ramen, mô hình sẽ nhìn chuỗi theo dạng:
1Click sushi
2→ đặt cơm Nhật
3→ xem ramen
4→ [Quán ramen cần chấm điểm]
Khi chấm điểm một quán bánh mì, chuỗi lại trở thành:
1Click sushi
2→ đặt cơm Nhật
3→ xem ramen
4→ [Quán bánh mì cần chấm điểm]
Cùng một lịch sử.
Nhưng câu hỏi khác nhau.
Vì vậy mức độ liên quan cũng khác nhau.
Uber cho biết kỹ thuật này được lấy cảm hứng từ những hướng như Deep Interest Network và Behavior Sequence Transformer. Nhà hàng mục tiêu được đưa vào chuỗi để Transformer trực tiếp tính quan hệ giữa hành vi quá khứ với ứng viên hiện tại. (Uber)
Có thể viết trực giác này bằng một công thức đơn giản:
$$ h_m = Transformer(a_1, a_2, \dots, a_n, m) $$
Trong đó:
- $a_1, a_2, \dots, a_n$ là các hành động trước đó.
- $m$ là nhà hàng đang được chấm điểm.
- $h_m$ là biểu diễn lịch sử người dùng trong mối quan hệ với chính nhà hàng đó.
Điểm quan trọng nằm ở ký hiệu $m$.
Lịch sử không được hiểu một cách cô lập.
Nó được hiểu trong ngữ cảnh của thứ chúng ta đang cân nhắc đưa ra trước mặt người dùng.
Ngoài đời cũng vậy.
Khi một người nói:
Tôi vừa chạy bộ một tiếng.
Thông tin đó có thể làm tăng khả năng họ muốn uống nước.
Nhưng không nhất thiết làm tăng khả năng họ muốn mua một cái tủ lạnh.
Cùng một sự kiện.
Ý nghĩa thay đổi theo câu hỏi đang được đặt ra.
Dữ liệu của hôm qua không hiểu được cơn đói hôm nay
Kiến trúc mô hình tốt vẫn chưa đủ nếu feature đến quá chậm.
Trước đây, nhiều feature của hệ thống recommendation Uber được tính bằng các batch job offline, dẫn tới độ trễ 24 giờ hoặc lâu hơn.
Điều đó có nghĩa mô hình có thể không biết người dùng vừa làm gì trong phiên hiện tại. (Uber)
Hai mươi bốn giờ trong một số hệ thống không phải thời gian dài.
Báo cáo doanh thu cuối ngày chậm một chút có thể vẫn dùng được.
Nhưng với đồ ăn, 24 giờ gần như là một kiếp khác.
Hôm qua bạn đặt cà phê không có nghĩa tối nay bạn muốn uống cà phê.
Sáng nay bạn tìm đồ ăn sáng không có nghĩa buổi trưa hệ thống tiếp tục đưa bánh mì lên đầu.
Năm phút trước bạn vừa bấm vào bốn quán đồ chay.
Đó có thể là tín hiệu có giá trị hơn lịch sử sáu tháng trước.
Recommendation cần sống gần với thời điểm con người đưa ra quyết định.
Chậm một ngày không chỉ là dữ liệu cũ.
Nó là hiểu sai bối cảnh.
Near real-time feature: Nghe tiếng bước chân khi nó vừa chạm đất
Để giảm khoảng cách này, Uber sử dụng nền tảng cá nhân hóa nội bộ Next Personalization Platform.
Thành phần trung tâm là UserContext, một lịch sử hành động của người dùng theo mô hình event-sourced, được cập nhật gần thời gian thực và dùng xuyên qua nhiều mảng sản phẩm.
Thay vì đợi batch job tạo sẵn hàng loạt con số tổng hợp, hệ thống có thể tính feature từ chuỗi hành động ngay lúc inference. Uber cho biết độ trễ dữ liệu đã được giảm từ nhiều ngày xuống còn vài giây, giúp mô hình nhìn thấy những tương tác gần nhất trong cùng phiên sử dụng. (Uber)
Hãy tưởng tượng một người đi trên bãi cát.
Batch feature giống như sáng hôm sau mới có người ra chụp lại dấu chân.
Có thể vẫn biết người đó từng đi qua.
Nhưng không biết họ đang ở đâu.
Near real-time feature giống như nghe tiếng cát vừa lún xuống dưới bàn chân.
Dấu vết còn mới.
Hướng đi còn rõ.
Bạn không chỉ biết người đó từng đi.
Bạn có cơ hội đoán họ đang đi về đâu.
Cold start không hẳn là người không có lịch sử
Uber đặc biệt nhắc tới lợi ích của feature mới đối với nhóm cold-start, tức những người dùng có rất ít hoặc gần như chưa có lịch sử trên nền tảng. (Uber)
Cold start thường được hình dung là một người vừa tạo tài khoản.
Hệ thống không biết họ thích gì.
Thế là đưa vài quán phổ biến lên trước và cầu nguyện.
Nhưng ngay cả người dùng mới cũng bắt đầu tạo tín hiệu ngay khi mở ứng dụng:
1Tìm “cơm”
2→ bỏ qua đồ ăn nhanh
3→ bấm món Việt
4→ xem quán giao dưới 25 phút
Chỉ vài hành động đã bắt đầu vẽ ra ý định.
Nếu phải đợi tới ngày hôm sau mới dùng được những tín hiệu này, phiên hiện tại gần như bị bỏ phí.
Khi dữ liệu được cập nhật trong vài giây, hệ thống có thể học ngay trong lần đầu người đó sử dụng.
Người dùng chưa có quá khứ dài.
Nhưng họ đã có một hiện tại.
Và đôi khi hiện tại là tất cả những gì recommendation cần.
Cái bẫy đáng sợ: Lúc học ăn một món, lúc thi lại được đưa món khác
Một trong những vấn đề khó chịu nhất của hệ thống machine learning production là training-serving skew.
Nói đơn giản, feature dùng khi train không giống feature được tính khi chạy thật.
Giống như lúc ôn thi, giáo viên cho học sinh dùng máy tính.
Đến ngày thi lại phát bàn tính.
Hoặc lúc tập lái dùng xe số tự động.
Đến ngày sát hạch được đưa một chiếc xe tải số sàn đang chết máy giữa dốc.
Mô hình không nhất thiết kém.
Môi trường đã thay đổi.
Uber giải quyết vấn đề này bằng cách sử dụng cùng các FeatureExtractor cho cả online serving và quá trình tạo dữ liệu training.
Ở online, các FeatureExtractor là những hàm Java thuần được gọi bởi Feature Store.
Khi tạo training data, Spark tái dựng UserContext tại thời điểm inference trong quá khứ rồi gọi lại chính các FeatureExtractor đó. Uber còn ghi log mẫu feature online và so sánh với kết quả được tính lại offline để theo dõi độ nhất quán. (Uber)
Đây là một chi tiết ít hào nhoáng hơn Transformer.
Nhưng theo mình, nó đáng học không kém.
Một mô hình ấn tượng trên notebook chưa chắc sống nổi trong production.
Bởi vì model chỉ học từ những gì chúng ta đưa cho nó.
Nếu bếp lúc tập dùng một công thức, lúc bán hàng lại nấu theo công thức khác, khách ăn trúng món lạ cũng không có gì bí ẩn.
Điều mình tâm đắc: Đừng cố làm model thông minh trên một dòng dữ liệu bị lệch
Nhiều đội machine learning gặp kết quả không tốt sẽ nghĩ ngay tới việc đổi model.
Thêm layer.
Tăng embedding.
Dùng Transformer lớn hơn.
Chuyển từ FP32 sang BF16.
Tăng GPU.
Thêm vài triệu parameter cho con số đẹp hơn.
Nhưng nếu feature online và offline không giống nhau, chúng ta đang gắn động cơ phản lực lên một chiếc xe có hai bánh quay ngược hướng.
Xe rung mạnh hơn.
Tốn xăng hơn.
Nhưng chưa chắc đi xa hơn.
Điều đáng học từ Uber không chỉ là mô hình recommendation mới.
Mà là cách họ xây một con đường để dữ liệu từ training tới serving đi qua cùng một logic.
Model mạnh là quan trọng.
Nhưng model được nuôi bằng cùng một loại thức ăn lúc học và lúc làm việc còn quan trọng hơn.
Từ pointwise: Gọi từng quán vào phỏng vấn
Sau kiến trúc hybrid, Uber tiếp tục hướng tới một hệ thống tập trung vào Transformer và Generative Recommendation.
Một hạn chế của mô hình cũ là cách chấm điểm pointwise.
Mỗi lần inference, hệ thống xét một cặp:
1Người dùng A + Nhà hàng X
Sau đó chạy tiếp:
1Người dùng A + Nhà hàng Y
Rồi:
1Người dùng A + Nhà hàng Z
Nếu có 100 nhà hàng ứng viên, có thể hình dung hệ thống đang gọi từng quán vào phòng để phỏng vấn riêng.
Quán X, anh phù hợp với khách này bao nhiêu điểm?
Xong.
Quán Y, tới lượt chị.
Xong.
Quán Z, mời vào.
Mỗi nhà hàng được đánh giá tương đối độc lập.
Cách này có thể hoạt động tốt, nhưng khá tốn kém khi số ứng viên tăng lên.
Quan trọng hơn, việc chấm từng quán riêng lẻ không tự nhiên nhìn thấy toàn bộ danh sách đang cạnh tranh với nhau.
Một quán có điểm 0,8 nghe rất cao.
Nhưng nếu 20 quán khác đều có điểm 0,95 thì nó không còn đặc biệt.
Recommendation cuối cùng không đưa ra một con số.
Nó đưa ra một danh sách.
Vì vậy, học cách nhìn cả danh sách là bước đi khá tự nhiên.
Listwise: Xếp tất cả lên cùng một bàn
Trong kiến trúc mới, Uber chuyển dần từ pointwise sang listwise.
Thay vì mỗi forward pass chỉ nhận một nhà hàng mục tiêu, mô hình nhận một mảng các nhà hàng ứng viên trong cùng phiên và tạo điểm cho cả danh sách trong một lần chạy.
Theo Uber, cách làm này giúp cải thiện hiệu quả training và serving, giảm độ phức tạp tính trên mỗi nhà hàng xuống xấp xỉ $1/T$ so với kiến trúc cũ, với $T$ là số nhà hàng mục tiêu được xử lý cùng lúc. (Uber)
Nếu pointwise giống gọi từng thí sinh vào phỏng vấn riêng, listwise giống xếp tất cả lên cùng sân khấu.
Lúc này, hệ thống có thể nhìn:
- Quán nào giống nhau quá.
- Quán nào nổi bật hơn trong nhóm.
- Danh sách có toàn đồ ăn nhanh hay không.
- Lựa chọn nào phù hợp nhất trong bối cảnh hiện tại.
- Vị trí của từng nhà hàng so với các ứng viên còn lại.
Ở mức đơn giản, ta có thể hình dung:
$$ [s_1, s_2, \dots, s_T]
F(\text{user history}, [m_1, m_2, \dots, m_T]) $$
Thay vì chạy riêng:
$$ s_i = f(\text{user}, m_i) $$
Mô hình không còn chỉ hỏi:
Quán này có tốt không?
Nó bắt đầu hỏi:
Trong số những quán đang có mặt ở đây, quán nào nên đứng trước quán nào?
Đó là một câu hỏi gần hơn với thứ feed thực sự cần trả lời.
Generative Recommendation không phải ChatGPT tự bịa ra một nhà hàng
Tên “Generative Recommendation” rất dễ gây hiểu nhầm.
Nghe như thể hệ thống sẽ ngồi sinh ra một quán ăn chưa từng tồn tại:
Hôm nay tôi xin đề xuất Nhà hàng Rồng Bay trên tầng mây, giao món bằng đại bàng trong 12 phút.
Không phải vậy.
Trong hướng Generative Recommender, lịch sử hành vi, item và ngữ cảnh được tổ chức giống các token trong một chuỗi.
Mô hình học cấu trúc của chuỗi đó và hướng tới việc dự đoán hoặc tạo ra các lựa chọn tiếp theo theo cách thống nhất hơn, thay vì ghép nhiều khối feature và mô hình chuyên biệt rời rạc.
Trong kiến trúc Uber mô tả, Transformer trở thành thân chính của mô hình.
Các feature không phải chuỗi không còn đi qua một nhánh DLRM riêng biệt, mà được biến đổi rồi ghép với feature của target trước khi vào Transformer. Đây là bước chuyển từ kiến trúc lai sang kiến trúc lấy Transformer làm trung tâm, tạo nền móng cho hướng GenRec hoàn chỉnh hơn. (Uber)
Có thể hình dung hệ thống cũ giống một công ty có nhiều phòng ban:
- Phòng thống kê tính số lần mua.
- Phòng xử lý hành vi đọc lịch sử.
- Phòng ranking ghép kết quả.
- Phòng score chấm từng nhà hàng.
Mỗi nơi giữ một phần câu chuyện.
Kiến trúc mới cố đưa nhiều phần hơn vào cùng một ngôn ngữ chung.
Giống như thay vì mười người chuyền mười tờ giấy, cả nhóm cùng nhìn vào một tấm bản đồ.
Từ DLRM sang Transformer-centered không có nghĩa vứt bỏ mọi feature cũ
Đây là chỗ cần tránh đơn giản hóa quá mức.
Thống kê truyền thống không vô dụng.
Khoảng cách giao hàng vẫn quan trọng.
Giá món vẫn quan trọng.
Điểm đánh giá vẫn quan trọng.
Khuyến mãi vẫn quan trọng.
Thời gian giao dự kiến vẫn quan trọng.
Transformer không làm vật lý biến mất.
Nó không thể dùng attention để giao một tô phở từ Hà Nội vào TP.HCM trong hai mươi phút.
Điểm thay đổi nằm ở cách các feature được xử lý.
Thay vì để feature truyền thống chạy trong một con đường riêng rồi mới ghép với sequence embedding, Uber đang chuyển chúng thành biểu diễn gắn với target và đưa vào Transformer làm phần dữ liệu chung của sequence module. (Uber)
Đây là một thay đổi kiến trúc.
Không phải nghi lễ đốt bỏ quá khứ.
Máy học tốt không phải lúc nào cũng chọn giữa “cũ” và “mới”.
Nhiều khi nó chỉ tìm cách đặt chúng vào đúng chỗ.
Model lớn hơn kéo theo một cái bếp lớn hơn
Nói tới đây, mọi thứ có vẻ dễ.
Thêm Transformer.
Thêm sequence.
Đổi sang listwise.
Xong thuật toán, quá dễ.
Nhưng production không quan tâm slide đẹp tới đâu.
Nó hỏi những câu rất đời:
- Train bao lâu?
- GPU đủ không?
- Startup model mất bao nhiêu giây?
- Request đầu tiên có bị chậm không?
- CPU đang bận làm gì?
- GPU có thực sự chạy hay đang ngồi chờ feature?
- Model có deploy ổn trên nhiều loại phần cứng không?
Một mô hình có metric tốt hơn 1% nhưng tốn gấp mười lần tài nguyên có thể không phải cải tiến.
Nó chỉ là một con bò ăn khỏe.
Vì vậy Uber phải thay đổi cả hệ sinh thái training và serving, không chỉ phần model.
Chuyển sang PyTorch 2, multi-hash embedding và BF16
Uber chuyển từ Keras/TensorFlow sang PyTorch 2 để có độ linh hoạt tốt hơn cho kiến trúc sequence nâng cao.
Họ cũng áp dụng multi-hash embedding và training mixed precision bằng BF16 nhằm tăng throughput, qua đó có thể train mô hình lớn hơn mà vẫn giữ thời gian training gần với baseline trước đó. (Uber)
Multi-hash embedding có thể hiểu nôm na như việc một item không chỉ được tra trong một cuốn sổ duy nhất.
Nó được ánh xạ qua nhiều cách băm để giảm một số vấn đề khi không gian ID rất lớn và tăng hiệu quả xử lý.
BF16 lại giống việc ghi số với ít chi tiết hơn ở một số phần, đổi lại máy tính xử lý nhanh hơn và dùng ít tài nguyên hơn, trong khi vẫn giữ được khoảng giá trị rộng phù hợp với nhiều bài toán deep learning.
Tất nhiên, đây không phải công thức thần kỳ.
Dùng precision thấp hơn cần kiểm tra độ ổn định số học.
Hashing có thể sinh va chạm.
Nhưng trong hệ thống quy mô lớn, những tối ưu tưởng như nhỏ có thể quyết định model chạy được ngoài đời hay chỉ đẹp trong notebook.
Đừng bắt GPU rửa rau
Một chi tiết khác mình rất thích là kiến trúc tách rời CPU và GPU.
Trong pipeline inference, không phải mọi công việc đều cần GPU.
Có những bước như:
- đọc dữ liệu,
- biến đổi feature,
- xử lý categorical value,
- chuẩn bị tensor,
- ghép sequence,
- tra cứu metadata.
Đó là những việc CPU có thể làm tốt.
Nếu để GPU vừa chạy model vừa chờ CPU-side preprocessing trong cùng một tiến trình chật chội, GPU đắt tiền có thể ngồi không.
Uber tách phần preprocessing nặng về CPU sang tài nguyên CPU riêng, để GPU tập trung vào inference tốc độ cao.
Theo bài viết, kiến trúc này mang lại mức cải thiện throughput hai chữ số trên mỗi node với độ trễ tăng thêm không đáng kể. (Uber)
Nói theo kiểu nhà bếp:
GPU là đầu bếp chính.
Nhiệm vụ của đầu bếp là đứng trước chảo.
Nếu bắt đầu bếp vừa chiên thức ăn, vừa rửa rau, bóc hành, chạy ra chợ mua thịt và lau sàn, căn bếp sẽ rất bận nhưng món ăn vẫn ra chậm.
Tách CPU và GPU không phải để hệ thống trông phức tạp hơn.
Mà để mỗi loại máy làm đúng việc nó giỏi.
Tối ưu model lúc deploy, đừng vừa mở cửa hàng vừa lắp bếp
Trước đây, Uber thực hiện một số bước chuyển đổi model ngay lúc khởi động.
Việc chuyển từ ONNX sang TensorRT tại runtime có thể gây chậm khởi động từ 10 tới 60 giây và tạo ra cạnh tranh tài nguyên GPU.
Họ chuyển sang pipeline tối ưu offline, thực hiện chuyển đổi trước và đóng gói model phù hợp với phần cứng.
Cách làm này đưa cold-start latency xuống gần bằng không và giúp quá trình triển khai nhất quán hơn trên đội GPU đa dạng. (Uber)
Hãy tưởng tượng một quán ăn chờ khách bước vào rồi mới bắt đầu:
- lắp bếp gas,
- nối đường điện,
- mài dao,
- kê bàn,
- thử nồi.
Khách đầu tiên sẽ phải ngồi chờ rất lâu.
Offline optimization giống việc chuẩn bị tất cả từ tối hôm trước.
Sáng mở cửa.
Bật bếp.
Nấu.
Production thích những thứ đã chuẩn bị xong.
Không thích một hệ thống vừa nhận request vừa suy nghĩ xem mình nên biến đổi model như thế nào.
Recommendation nhà hàng không sống trong thế giới ảo
Phần khiến mình thấy bài viết này thú vị hơn recommendation phim hay nhạc nằm ở những ràng buộc vật lý.
Một bộ phim có thể phát tới bất kỳ ai có Internet.
Một bài hát có thể được nghe ở đầu bên kia thế giới gần như ngay lập tức.
Nhưng một tô bún bò thì không.
Nó có nhiệt độ.
Khoảng cách.
Thời gian nấu.
Tài xế.
Mưa.
Kẹt xe.
Giờ mở cửa.
Tình trạng hết món.
Một quán rất hợp khẩu vị nhưng đã đóng cửa thì điểm recommendation cao tới đâu cũng vô nghĩa.
Một nhà hàng tuyệt vời cách người dùng hai mươi kilomet có thể không phải lựa chọn tốt hơn quán khá ổn cách hai kilomet.
Uber nhấn mạnh rằng hướng Generative Recommendation trong thế giới thật phải tôn trọng các ràng buộc như bán kính giao hàng và vị trí địa lý.
Hệ thống cũng phải cân bằng nhiều mục tiêu: người dùng thường muốn đặt lại món quen, nhưng nền tảng vẫn cần tạo cơ hội để họ khám phá quán mới. (Uber)
Đây là điều mình tâm đắc nhất trong toàn bộ bài.
Recommendation không chỉ là học sở thích. Nó là thương lượng giữa mong muốn của con người và giới hạn của vật chất.
Bạn có thể thích một quán.
Nhưng quán phải mở.
Món phải còn.
Tài xế phải đến được.
Thời gian phải chấp nhận được.
Giá phải hợp lý.
Toàn bộ thế giới vật lý chen vào giữa một vector embedding và bữa tối.
Bài toán đặt lại món cũ và khám phá món mới
Nếu hệ thống chỉ tối ưu khả năng order ngay lập tức, nó có xu hướng đưa những thứ người dùng từng mua lên trước.
Điều này an toàn.
Người từng mua phở có khả năng mua lại phở.
Nhưng nếu ngày nào feed cũng chỉ có những quán quen, trải nghiệm trở thành một con đường vòng tròn.
Người dùng không khám phá được món mới.
Nhà hàng mới khó tiếp cận khách hàng.
Nền tảng dần củng cố chính những dữ liệu mà nó đã tạo ra.
Đây là vòng lặp khá thú vị:
- Hệ thống đưa quán A lên đầu.
- Người dùng nhìn thấy quán A nhiều hơn.
- Quán A có nhiều click hơn.
- Dữ liệu nói rằng quán A phổ biến.
- Hệ thống tiếp tục đưa quán A lên đầu.
Không hẳn vì A luôn tốt nhất.
Mà vì A được nhìn thấy nhiều nhất.
Recommendation mạnh không chỉ đoán đúng thứ người dùng sẽ bấm.
Nó còn phải quyết định khi nào nên đưa một lựa chọn mới vào.
Giống người bạn rủ đi ăn.
Một người bạn quá an toàn sẽ nói:
Ăn quán cũ đi.
Một người bạn quá phiêu lưu lại dẫn bạn vào một quán không có bảng hiệu, không menu và có một con mèo đang ngủ trong nồi.
Hệ thống tốt phải đứng ở giữa.
Đủ quen để không gây khó chịu.
Đủ mới để thế giới không trở nên nhàm chán.
Sở thích nhất thời và sở thích ăn sâu
Trong lộ trình tiếp theo, Uber muốn kéo dài chuỗi hành vi từ phạm vi gần và trung hạn tới lịch sử trong suốt vòng đời tài khoản.
Mục tiêu là phân biệt được hai thứ:
- Ý định nhất thời.
- Sở thích đã hình thành trong nhiều tháng hoặc nhiều năm.
Ví dụ, hôm nay bạn xem năm quán cháo.
Có thể bạn thích cháo.
Cũng có thể bạn đang bệnh.
Nếu chỉ nhìn phiên hiện tại, hệ thống dễ kết luận rằng cháo là chân ái.
Nếu chỉ nhìn lịch sử dài hạn, hệ thống lại bỏ qua việc hôm nay bạn đang cần một món nhẹ.
Chuỗi dài giúp mô hình nhìn thấy nhiều tầng thời gian hơn:
- Vài phút gần đây.
- Vài ngày gần đây.
- Thói quen cuối tuần.
- Chu kỳ theo mùa.
- Sở thích lâu dài.
Uber gọi hướng này là account life sequence learning, nhằm giúp mô hình phân biệt cơn thèm nhất thời với khẩu vị ăn sâu. (Uber)
Con người vừa có ký ức, vừa có tâm trạng.
Recommendation tốt phải hiểu cả hai.
Feed không phải một hàng dọc, nó là cả một gian hàng
Một hướng khác Uber đề cập là 2-D whole page personalization.
Nhiều hệ thống recommendation xem bài toán như một danh sách một chiều:
1Quán A
2Quán B
3Quán C
4Quán D
Nhưng màn hình thực tế không chỉ có một cột nhà hàng.
Nó có nhiều carousel:
- Gần bạn.
- Giao nhanh.
- Món Việt.
- Ưu đãi hôm nay.
- Đặt lại món quen.
- Thử món mới.
- Đồ ăn cho nhóm.
- Cửa hàng tiện lợi.
Feed là một mặt phẳng hai chiều.
Không chỉ cần chọn quán nào đứng trước.
Còn phải quyết định:
- Carousel nào xuất hiện.
- Thứ tự các carousel.
- Quán nào nằm trong từng nhóm.
- Có lặp lại cùng một quán quá nhiều hay không.
- Toàn trang có đủ đa dạng không.
- Dòng đầu tiên và dòng cuối cùng phối hợp ra sao.
Uber cho biết hướng tương lai là tối ưu toàn bộ bố cục hai chiều và sự đa dạng của trang, thay vì chỉ tính độ liên quan giữa người dùng với từng nhà hàng riêng lẻ. (Uber)
Điều này giống sắp xếp một siêu thị.
Không phải cứ đặt 100 sản phẩm bán chạy nhất ngay cửa ra vào là tốt.
Nếu toàn bộ gian đầu đều là nước ngọt, khách muốn mua rau sẽ nghĩ siêu thị này có vấn đề.
Một trang feed tốt không phải đống item có điểm cao nhất.
Nó là một bố cục có chủ ý.
Một kiến trúc đơn giản để hình dung
Nếu lược bớt rất nhiều chi tiết production, ta có thể hình dung hệ thống theo dòng sau:
1Hành vi người dùng
2(click, order, search...)
3 │
4 ▼
5UserContext gần thời gian thực
6 │
7 ▼
8FeatureExtractor
9 │
10 ├──────────────┐
11 │ │
12 ▼ ▼
13Chuỗi hành vi Feature truyền thống
14 │ │
15 └──────┬───────┘
16 ▼
17 Transformer-centered model
18 │
19 ▼
20 Danh sách nhà hàng ứng viên
21 │
22 ▼
23 Click / Order probabilities
24 │
25 ▼
26 Ranking và Home Feed
Phía sau sơ đồ ngắn này là một hệ thống khá lớn:
- Event ingestion.
- Feature Store.
- Event-sourced user context.
- Spark training pipeline.
- Online-offline consistency.
- GPU serving.
- Model optimization.
- Monitoring.
- Candidate generation.
- Business constraints.
- Experimentation.
Sơ đồ kiến trúc luôn rất sạch.
Production thì giống căn bếp sau giờ cao điểm.
Dầu văng.
Nồi nóng.
Người chạy qua chạy lại.
Và một request chậm hơn 100 mili giây cũng có thể khiến ai đó bắt đầu mở dashboard.
Nếu áp dụng vào recommendation bán lẻ tại Việt Nam
Bài toán này không chỉ dành cho đồ ăn.
Một website bán lẻ cũng có thể gặp tình huống tương tự.
Giả sử một khách hàng thường xem:
- Tivi.
- Tủ lạnh.
- Máy lạnh.
Thống kê dài hạn nói họ quan tâm điện máy.
Nhưng trong phiên hiện tại, người đó vừa:
- Tìm “iPhone 17”.
- Xem ba điện thoại dưới 20 triệu.
- So sánh camera.
- Kiểm tra trả góp.
- Bấm vào chương trình thu cũ đổi mới.
Nếu feature cập nhật chậm một ngày, trang chủ vẫn tiếp tục gợi ý máy lạnh.
Về mặt lịch sử, không sai.
Về mặt khoảnh khắc, sai hoàn toàn.
Trong bán lẻ, sequence còn có thể giúp hiểu quá trình ra quyết định:
1Xem sản phẩm
2→ đọc đánh giá
3→ so sánh
4→ kiểm tra tồn kho
5→ xem trả góp
6→ quay lại sản phẩm đầu tiên
Chuỗi này nói lên ý định mạnh hơn nhiều so với con số “đã xem 6 trang”.
Target-aware modeling cũng hữu ích.
Cùng một lịch sử xem điện thoại, mức quan tâm tới một chiếc iPhone cao cấp khác với một tai nghe, một ốp lưng hay một gói bảo hành.
Near real-time feature giúp recommendation phản ứng ngay khi nhu cầu đổi hướng.
Listwise giúp xếp cả tập sản phẩm cùng lúc thay vì chấm điểm từng món trong chân không.
Và các ràng buộc vật lý vẫn còn đó:
- Cửa hàng gần nhất còn hàng không?
- Có giao trong ngày không?
- Khu vực đó có áp dụng khuyến mãi không?
- Hạn mức trả góp đủ không?
- Sản phẩm có phù hợp với ngân sách hiện tại không?
Recommendation bán lẻ cũng là cuộc thương lượng giữa ý định, tồn kho, giá, địa điểm và thời gian.
Model chỉ là một người trong cuộc họp.
Những điều không nên học một cách máy móc
Uber có quy mô dữ liệu, hạ tầng và đội ngũ rất lớn.
Không phải hệ thống recommendation nào cũng cần:
- Transformer khổng lồ.
- UserContext event-sourced toàn doanh nghiệp.
- GPU serving phân tách CPU/GPU.
- TensorRT.
- Sequence kéo dài cả vòng đời tài khoản.
- Whole-page 2-D ranking.
Nếu một website chỉ có 10.000 sản phẩm và vài nghìn người dùng mỗi ngày, một mô hình đơn giản hơn có thể đã đủ tốt.
Thậm chí recommendation theo luật:
- Sản phẩm cùng danh mục.
- Sản phẩm bán chạy.
- Sản phẩm đang giảm giá.
- Sản phẩm vừa xem.
có thể tạo ra phần lớn giá trị ban đầu.
Điều nên học không phải là sao chép toàn bộ stack.
Mà là thứ tự giải bài toán:
- Xác định feature hiện tại đang thiếu điều gì.
- Bổ sung chuỗi hành vi nếu thứ tự có ý nghĩa.
- Giảm độ trễ nếu ý định thay đổi nhanh.
- Đảm bảo feature online và offline giống nhau.
- Chỉ tăng độ phức tạp model khi hạ tầng đủ sức phục vụ.
- Đo giá trị bằng metric online, không chỉ metric offline.
- Luôn giữ các ràng buộc vật lý và kinh doanh trong vòng quyết định.
Đừng xây tàu vũ trụ để đi mua bánh mì đầu ngõ.
Nhưng cũng đừng dùng chiếc xe đạp cũ rồi than rằng nó không bay được.
Điều mình tâm đắc nhất: Recommendation là mô hình của thời gian
Ban đầu, mình nghĩ bài viết này chủ yếu nói về Transformer.
Đọc kỹ hơn, mình lại thấy nhân vật chính là thời gian.
Thống kê cũ nén thời gian thành một con số.
Chuỗi hành vi mở nó ra trở lại.
Near real-time feature kéo mô hình tới gần hiện tại.
Account life sequence kéo mô hình về quá khứ dài hạn.
Target-aware attention hỏi phần nào của quá khứ có ý nghĩa với ứng viên đang xét.
Listwise modeling nhìn nhiều lựa chọn trong cùng một khoảnh khắc.
Toàn bộ kiến trúc thực chất đang cố trả lời một câu hỏi:
Trong tất cả những gì người này từng làm, điều gì quan trọng nhất ngay lúc này?
Đó là câu hỏi rất khó.
Bởi vì con người không nhất quán.
Hôm qua thích một thứ.
Hôm nay muốn thứ khác.
Ngày mai lại quay về cái cũ.
Chúng ta không phải một vector cố định.
Chúng ta là một dòng thời gian có trí nhớ, thói quen, bốc đồng và cơn đói.
Hệ thống recommendation thế hệ mới không chỉ cần biết người dùng thích gì.
Nó phải hiểu:
Người dùng đang ở đoạn nào trong câu chuyện của chính họ?
Tổng kết
Điều đáng học từ hệ thống recommendation mới của Uber Eats không phải chỉ là việc họ đưa Transformer vào mô hình.
Nếu chỉ nhìn ở mức đó, bài viết sẽ trở thành:
Công ty lớn dùng model lớn.
Không có gì quá bất ngờ.
Điểm đáng giá nằm ở chuỗi thay đổi liên kết với nhau:
- Từ feature thống kê sang chuỗi hành vi.
- Từ dữ liệu chậm nhiều giờ hoặc nhiều ngày sang tín hiệu trong vài giây.
- Từ biểu diễn người dùng chung chung sang target-aware modeling.
- Từ chấm từng nhà hàng riêng lẻ sang xử lý cả danh sách.
- Từ kiến trúc lai sang mô hình lấy Transformer làm trung tâm.
- Từ tối ưu model đơn thuần sang tối ưu cả training, serving và phần cứng.
- Từ xếp hạng một chiều sang cá nhân hóa toàn bộ trang.
- Từ thế giới số thuần túy trở về với khoảng cách giao hàng, giờ mở cửa và những giới hạn vật lý rất thật.
Một cú chạm nhỏ trên màn hình có thể kéo theo một chuỗi hoạt động khổng lồ.
Sự kiện được ghi vào Kafka.
UserContext được cập nhật.
Feature được trích xuất.
CPU chuẩn bị dữ liệu.
GPU chạy inference.
Hàng trăm nhà hàng được chấm điểm.
Các ràng buộc địa lý loại bớt ứng viên.
Feed được sắp xếp.
Tất cả diễn ra trong thời gian ngắn tới mức người dùng chỉ nhìn thấy vài tấm ảnh món ăn trượt nhẹ trên màn hình.
Chúng ta gọi đó là “gợi ý”.
Nhưng phía sau nó là một cỗ máy đang cố hiểu một sinh vật rất khó hiểu: con người.
Một sinh vật có thể ăn cơm gà ba ngày liên tiếp, rồi sáng hôm sau thức dậy và quyết định rằng hôm nay nhất định phải ăn lẩu.
Hệ thống cũ sẽ nhìn ba ngày cơm gà.
Hệ thống tốt hơn sẽ nhìn cú chuyển mình vừa xảy ra.
Có lẽ đó mới là bản chất của recommendation hiện đại.
Không phải tìm món người dùng yêu thích nhất trong mọi thời đại.
Mà là tìm món hợp lý nhất với con người đang tồn tại trong khoảnh khắc này.
Tham khảo
- Uber Engineering – Next-Gen Restaurant Recommendation with Generative Modeling and Real-Time Features, Yicheng Chen, Peng Chen, Nikat Patel và cộng sự. (Uber)
- Ruoxi Wang và cộng sự – DCN V2: Improved Deep & Cross Network and Practical Lessons for Web-scale Learning to Rank Systems. (arXiv)
- Guorui Zhou và cộng sự – Deep Interest Network for Click-Through Rate Prediction. (arXiv)
- Qiwei Chen và cộng sự – Behavior Sequence Transformer for E-commerce Recommendation in Alibaba. (arXiv)
Bình luận