Elasticsearch Bài học 33: Boosting - Tùy chỉnh trọng số kết quả (Đẩy hàng tồn kho/Sản phẩm hot lên top)
Trong E-commerce, thuật toán tìm kiếm văn bản (BM25) phân tích tần suất từ khóa rất giỏi, nhưng nó lại hoàn toàn "mù tịt" về logic kinh doanh.
Giả sử khách tìm "áo thun". Thuật toán BM25 thấy một chiếc áo thun tồn kho từ năm 2022 có chữ "áo thun" xuất hiện nhiều lần nên chấm cho nó điểm cao và xếp Top 1. Trong khi đó, mẫu áo thun mới nhất của năm nay, bán chạy nhất, mang lại lợi nhuận cao nhất lại bị đẩy xuống Top 5. Đây là điều mà bộ phận Kinh doanh (Business) tuyệt đối không chấp nhận.
Để giải quyết bài toán "Search Relevance + Business Logic", chúng ta sử dụng kỹ thuật Boosting (Nhân trọng số).
Có hai cách tiếp cận: Index-time Boosting (Gắn trọng số lúc lưu dữ liệu) và Query-time Boosting (Gắn trọng số lúc tìm kiếm). Trong thực tế, Index-time Boosting đã bị Elasticsearch khai tử (deprecated) vì tính cứng nhắc của nó. Hôm nay, chúng ta sẽ tập trung toàn lực vào Query-time Boosting.
1. Positive Boosting: Thưởng điểm cho đặc tính tốt
Khi gọi API tìm kiếm, bạn có thể chủ động can thiệp vào cách Elasticsearch chấm điểm bằng tham số boost. Có hai kịch bản phổ biến nhất:
A. Boosting ở cấp độ Trường (Field-level) với multi_match
Như đã nhắc nhanh ở Bài 25, từ khóa xuất hiện ở Tên sản phẩm luôn quan trọng hơn xuất hiện ở phần Mô tả. Ta dùng ký hiệu ^ (mũ) để nhân điểm.
JSON
GET products/_search
{
"query": {
"multi_match": {
"query": "kem chống nắng",
"fields": ["name^3", "brand^1.5", "description"]
}
}
}
Logic: Nếu từ "kem chống nắng" match vào trường name, điểm BM25 của nó sẽ tự động nhân 3. Nếu match vào brand, nhân 1.5. Nếu match vào description, giữ nguyên (nhân 1).
B. Boosting ở cấp độ Mệnh đề (Clause-level) với bool -> should
Làm sao để ưu tiên những sản phẩm đang là "Best Seller" hoặc "Hàng mới về"? Hãy nhét chúng vào mảng should của bool query và cấp cho chúng một lượng điểm thưởng.
JSON
GET products/_search
{
"query": {
"bool": {
"must": [
{ "match": { "name": "áo thun" } }
],
"should": [
{
"term": {
"is_best_seller": {
"value": true,
"boost": 5.0
}
}
},
{
"range": {
"created_at": {
"gte": "now-30d/d",
"boost": 2.0 // Hàng mới ra mắt trong 30 ngày thưởng nhẹ 2 điểm
}
}
}
]
}
}
}
Logic: Mọi Document thỏa mãn chữ "áo thun" đều được trả về. Nhưng những Document nào is_best_seller = true sẽ nhận thêm 1 lượng điểm được nhân 5 lần, lập tức vọt lên Top đầu.
2. Negative Boosting: Phạt điểm (Dìm hàng thay vì Xóa sổ)
Sẽ có lúc bạn muốn dìm các sản phẩm Đã Hết Hàng (Out of stock).
-
Nếu bạn cho vào
must_not-> Sản phẩm sẽ biến mất hoàn toàn khỏi kết quả tìm kiếm. Trải nghiệm người dùng sẽ rất tệ ("Ơ, rõ ràng hôm qua shop có bán món này mà nay tìm không thấy?"). -
Tốt nhất là vẫn hiện ra, nhưng dìm nó xuống tận cùng của trang tìm kiếm để ưu tiên hàng còn tồn.
Chúng ta sử dụng một khối query chuyên dụng mang tên boosting query. Khối này chia làm 2 ngăn: positive (tính điểm bình thường) và negative (phạt điểm).
JSON
GET products/_search
{
"query": {
"boosting": {
"positive": {
"match": {
"name": "mặt nạ dưỡng da"
}
},
"negative": {
"term": {
"stock": 0
}
},
"negative_boost": 0.2
}
}
}
Cách hoạt động:
-
Elasticsearch tìm tất cả "mặt nạ dưỡng da" (ở ngăn
positive) và tính điểm BM25 bình thường. Giả sử Document A được10điểm, Document B được8điểm. -
Nó xét tiếp ngăn
negative: "Sản phẩm nào cóstock = 0?". Document A cóstock = 50(Không bị phạt). Document B cóstock = 0(Dính bẫy!). -
Nó áp dụng hình phạt
negative_boost = 0.2. Điểm của Document B bị bóp nghẹt:8 * 0.2 = 1.6. -
Kết quả: Document B từ 8 điểm rớt thảm hại xuống 1.6 điểm và ngậm ngùi nằm ở cuối trang. Nó không biến mất, chỉ bị dìm đi.
3. Nghệ thuật cân bằng (Cẩn thận với Boosting Overkill)
Boosting là một con dao hai lưỡi. Nếu bạn set hệ số boost cho tiêu chí "Best Seller" là 100.0, điểm BM25 của "Sữa rửa mặt" (khoảng 3.0 điểm) sẽ trở nên vô nghĩa. Khi đó, khách tìm "Sữa rửa mặt", hệ thống lại trả về... "Bông tẩy trang" ở Top 1 chỉ vì cục Bông tẩy trang này là Best Seller và được nhân 100 điểm.
-
Quy tắc ngón tay cái: Hãy bắt đầu với các hệ số Boost nhỏ (từ
1.2đến3.0). -
A/B Testing: Luôn chạy thử bằng
explain: true(Bài 28) để xem điểm BM25 gốc là bao nhiêu, sau đó mới quyết định nhân lên bao nhiêu lần để không phá vỡ tính logic của văn bản.
Tuy nhiên, boosting query và should clause vẫn chỉ là những phép cộng trừ nhân đơn giản. Nếu bạn muốn xếp hạng sản phẩm dựa trên một công thức phức tạp kiểu: Điểm cuối = Điểm BM25 + (Tồn kho * Lợi nhuận) / Số ngày ra mắt, thì bạn sẽ cần đến một vũ khí tối thượng có khả năng chạy code lập trình nhúng: Function Score & Script Score.
All rights reserved