Bạn đổi cách tính điểm tìm kiếm từ dot product sang cosine rồi thấy thứ tự tài liệu thay đổi. Trước khi sửa prompt hoặc thay model, hãy kiểm tra metric đang đo điều gì. Hai phép tính có liên quan nhưng không luôn cho cùng thứ tự khi độ dài vector khác nhau.
Bài này dùng các vector hai chiều tự tạo để tách riêng tác động của phép tính. Chúng không phải embedding từ một model thực tế và không chứng minh tài liệu nào liên quan hơn về ngữ nghĩa. Ví dụ được kiểm tra bằng Python 3.9.6; tài liệu được đối chiếu ngày 25/10/2026.
1. Phân biệt hướng và độ dài
Tài liệu Google về độ tương đồng embedding mô tả dot product bằng tổng các tích thành phần. Cosine chia dot product cho tích độ dài hai vector, nên loại ảnh hưởng của độ dài khỏi phép so sánh góc. Khi hai vector được chuẩn hóa về độ dài 1, hai phép tính này bằng nhau.
Hãy xem query [1, 0] và hai ứng viên A [1, 0], B [10, 10]. Dot product của query với A là 1, với B là 10. Cosine với A là 1, với B xấp xỉ 0.7071. Trong fixture này, chỉ thay metric đã đủ làm ứng viên đứng đầu đổi từ B sang A.
Điều đó không có nghĩa cosine luôn tốt hơn. Đây là một phản ví dụ cho giả định rằng mọi metric đều có thể thay thế nhau mà không ảnh hưởng cách xếp hạng. Lựa chọn thực tế còn cần hướng dẫn của model, cấu hình chỉ mục và bộ đánh giá của ứng dụng.
2. Một hàm nhỏ có kiểm tra đầu vào
Lưu đoạn sau thành similarity_demo.py. Hàm chỉ nhận danh sách số hữu hạn, cùng chiều và có norm khác 0. math.hypot tính độ dài vector theo tài liệu thư viện math của Python 3.9.
import math
def cosine(a, b):
if not a or len(a) != len(b):
raise ValueError("Vector phai cung chieu va khong rong")
if any(type(x) not in (int, float) or not math.isfinite(x)
for x in a + b):
raise ValueError("Chi nhan so huu han")
norm_a, norm_b = math.hypot(*a), math.hypot(*b)
if not norm_a or not norm_b:
raise ValueError("Khong tinh cosine cho vector 0")
if not math.isfinite(norm_a) or not math.isfinite(norm_b):
raise ValueError("Norm vuot pham vi so thuc cua vi du")
return sum((x / norm_a) * (y / norm_b) for x, y in zip(a, b))
query = [1, 0]
candidates = {"A": [1, 0], "B": [10, 10]}
for name, vector in candidates.items():
dot = sum(x * y for x, y in zip(query, vector))
print(name, "dot=", dot, "cosine=", round(cosine(query, vector), 4))
Chạy python3 similarity_demo.py. Kết quả đã kiểm tra cho A là dot 1, cosine 1.0; B là dot 10, cosine 0.7071. Hàm cố ý báo lỗi với vector 0 thay vì trả một điểm tùy ý, để lỗi dữ liệu không lẫn vào kết quả tìm kiếm bình thường.
Đây là code học phép tính, chưa phải thư viện xử lý hàng triệu vector. Nó không kiểm tra schema của một file embedding, không xây chỉ mục và không đo độ liên quan của model. Nếu dùng số có quy mô cực lớn, cần thêm chính sách xử lý giới hạn số học phù hợp.
3. Kiểm tra các tính chất trước khi tích hợp
Tự thêm bốn ca: vector cùng hướng, vuông góc, ngược hướng và vector 0. Với các cặp [1, 0] và [2, 0], [0, 1], [-1, 0], các giá trị tương ứng phải gần 1, 0 và -1. Với số thực, dùng sai số nhỏ khi so sánh thay vì đòi mọi kết quả đều khớp từng bit.
Sau đó thử query và candidate có số chiều khác nhau. Không dùng zip trực tiếp rồi âm thầm bỏ phần thừa. Kiểm tra số chiều là một phần của hợp đồng dữ liệu, không chỉ là chi tiết để tránh exception.
Trong ứng dụng, ghi lại model tạo embedding, cách chuẩn hóa và metric của index. Nếu tái tạo index bằng một cấu hình khác, hãy xem đó là thay đổi cần đánh giá lại kết quả. Không trộn vector từ hai nguồn chỉ vì chúng có cùng số chiều; số chiều giống nhau chưa chứng minh ý nghĩa từng tọa độ tương thích.
4. Đừng biến điểm tương đồng thành xác suất đúng
Cosine 0.8 không tự có nghĩa là tài liệu “đúng 80%” hay câu trả lời được chứng minh. Để dùng một ngưỡng trong sản phẩm, hãy tạo tập câu hỏi có tài liệu mong đợi, kiểm tra các trường hợp bỏ sót và kết quả nhiễu rồi ghi rõ chính sách chấp nhận.
Với một hệ thống hỏi đáp nội bộ, hãy xem cả tài liệu được lấy ra chứ không chỉ điểm số. Một chunk gần giống câu hỏi nhưng nói về phiên bản cũ có thể không đáp ứng nhu cầu. Đây là bước đánh giá nội dung sau truy hồi mà phép nhân vector trong ví dụ không thực hiện.
Lỗi thường gặp
Lỗi đầu tiên là đổi từ distance sang similarity nhưng vẫn sắp xếp tăng dần theo thói quen. Lỗi tiếp theo là chuẩn hóa hai lần ở một luồng nhưng bỏ qua ở luồng khác mà không ghi nhận cấu hình. Cuối cùng, không nên chữa vector 0 thành vector ngẫu nhiên để pipeline tiếp tục; cần tìm nguyên nhân từ bước tạo hoặc lưu dữ liệu.
Gợi ý tiếp theo
Giữ fixture toán học làm phép kiểm tra nhỏ, sau đó xây bộ đánh giá truy hồi riêng bằng câu hỏi thật đã được phép sử dụng. Tách hai câu hỏi: code tính metric có đúng không và kết quả có hữu ích không. Chỉ câu thứ hai mới giúp bạn chọn cấu hình tìm kiếm cho sản phẩm.


