Trao đổi về dự án
Trí Tuệ Nhân Tạo

Giữ lại hai câu trả lời đúng cho cùng một sự cố

Một sự cố có thể cần hai quy trình: khởi động lại xử lý và khôi phục tin nhắn đang chờ. Hãy học cách huấn luyện công cụ tìm kiếm để nhận ra nhiều câu trả lời đúng cho cùng một câu hỏi.

Juger → Encoder → Classer. Embeddings multi-positifs

Hãy hình dung trung tâm trợ giúp của ứng dụng web. Một người dùng báo rằng các tin nhắn vẫn bị kẹt trong hàng đợi sau khi một worker dừng. Công cụ tìm kiếm tìm được tài liệu hướng dẫn khởi động lại worker đó. Điều này hữu ích, nhưng một tài liệu thứ hai mô tả cách chạy lại các tin nhắn chưa được xác nhận xử lý. Tài liệu đó cũng trả lời vấn đề.

Làm thế nào để dạy công cụ tìm lại cả hai quy trình này, mà không nhầm chúng với các tài liệu khác dùng cùng từ ngữ nhưng mô tả một sự cố khác?

Chúng ta sẽ điều chỉnh một mô hình embedding: chương trình biến văn bản thành các vector để so sánh. Điểm khó không chỉ là đưa một câu hỏi gần với một câu trả lời đúng. Cũng cần tránh để câu trả lời đúng thứ hai bị coi nhầm là ví dụ phải đẩy xa trong lúc huấn luyện.

Tải xuống dự án hoàn chỉnh của hướng dẫn

Những gì chúng ta xây dựng

Dự án xây dựng một công cụ tìm kiếm nhỏ trên một kho dữ liệu hỗ trợ tổng hợp. Nó mã hóa truy vấn và quy trình bằng cùng một bộ mã hóa, chọn các tài liệu gần nhau một cách đánh lừa, rồi huấn luyện bộ mã hóa bằng hàm mất mát đối sánh nhiều tích cực. Cuối cùng, nó lưu thứ hạng và tính hai chỉ số: Recall@5 và MRR@10.

Nói cách khác, chúng ta muốn thay đổi điều mà mô hình coi là « gần ». Hai quy trình khác nhau có thể cùng phù hợp với một yêu cầu; khác biệt giữa chúng không được đủ để biến chúng thành các đối thủ không tương thích.

Quy trình tách ba trách nhiệm: chú thích xác định các quy trình phù hợp; huấn luyện dùng các chú thích đó để điều chỉnh mô hình; sau đó đánh giá kiểm tra vị trí các quy trình trong kết quả. Mô hình không tự xác định câu trả lời đúng theo sở thích của nó.

Chương trình này tìm và xếp hạng tài liệu. Nó không tạo một RAG hoàn chỉnh — tìm kiếm tài liệu rồi sinh câu trả lời — cũng không tạo quy tắc nghiệp vụ quyết định tự động cách giải quyết sự cố.

Một truy vấn, hai quy trình phù hợp và các ứng viên không tương thích đi vào bộ mã hóa, ma trận tương đồng và loss nhiều tích cực.

Giải thích các khái niệm trước khi viết mã

Embedding: các tọa độ để so sánh văn bản

Một embedding là danh sách các số được tính từ một văn bản. Bạn có thể hình dung nó như tọa độ của một điểm: hai văn bản có vector gần nhau được mô hình coi là giống nhau.

Chúng ta không tự chọn một trục « sự cố » và một trục « khởi động lại ». Các tham số của mạng, còn gọi là trọng số, quyết định cách văn bản trở thành tọa độ. Vì vậy điều chỉnh chúng sẽ thay đổi biểu diễn được tạo ra, chứ không thay đổi văn bản của các quy trình.

Mô hình dùng là sentence-transformers/all-MiniLM-L6-v2, được khóa ở bản sửa đổi 1110a243fdf4706b3f48f1d95db1a4f5529b4d41 trong locks/models.lock.json. Trang chính thức ở bản sửa đổi này mô tả các vector gồm 384 tọa độ. Mã định danh tài liệu giúp tìm một tài liệu; embedding của nó dùng để so sánh với truy vấn. Hai thứ này không thể thay thế nhau.

Từ văn bản đến một vector duy nhất

Phép tính gồm nhiều biến đổi. Tokenizer cắt văn bản thành các đơn vị, token, rồi thay chúng bằng các mã số. Một token có thể là một từ hoặc một phần từ: không nhất thiết tương ứng với một từ được ngăn cách bằng khoảng trắng.

Sau đó Transformer tính một vector cho mỗi vị trí có tính đến ngữ cảnh. Vì thế ta có nhiều vector cho một văn bản, trong khi tìm kiếm chỉ cần một. Pooling trung bình gộp chúng lại bằng cách tính trung bình từng tọa độ.

Tuy nhiên phải bỏ qua padding: các vị trí đệm được thêm vào để nhiều văn bản nằm trong một bảng có cùng chiều rộng. Mặt nạ chú ý chỉ ra các vị trí được dùng, 1 cho vị trí giữ lại và 0 cho phần đệm.

Hãy lấy ví dụ minh họa hai tọa độ, không liên quan đến các vector MiniLM thực sự tạo ra. Hai vị trí hữu ích là [1, 2] và [5, 6]. Vị trí thứ ba là phần đệm. Với mặt nạ [1, 1, 0], trung bình là [(1 + 5) / 2, (2 + 6) / 2], tức [3, 4]. Vị trí thứ ba không tham gia vào tổng lẫn mẫu số.

Chuẩn hóa: so sánh hướng thay vì độ lớn

Tiếp theo quy trình áp dụng F.normalize(..., dim=-1). Phép toán này chia mỗi vector cho chuẩn của nó, có bảo vệ với giá trị rất nhỏ. Chuẩn ở đây là độ dài hình học: với [3, 4], nó là sqrt(3² + 4²) = 5. Vector chuẩn hóa trở thành [0.6, 0.8], có chuẩn bằng 1.

Tích vô hướng nhân các tọa độ tương ứng rồi cộng kết quả. Với hai vector chuẩn 1, nó bằng độ tương đồng cosin, tức so sánh hướng của chúng. Ví dụ [0.6, 0.8] so với chính nó cho 0.6² + 0.8² = 1.

Không có chuẩn hóa này, một vector dài hơn có thể có tích vô hướng cao hơn chỉ do kích thước. Ở đây ta tìm sự gần nhau của biểu diễn, không phải cuộc thi độ dài. Tài liệu normalize cũng nêu cách xử lý các chuẩn rất nhỏ.

Điểm tương đồng không phải bằng chứng về mức phù hợp nghiệp vụ. Hai tài liệu có thể nói về cùng thành phần nhưng không khuyến nghị đúng hành động cho cùng sự cố.

Tích cực, tiêu cực và các ví dụ gây nhiễu

Một tích cực là tài liệu được chú thích phù hợp với một truy vấn. Một tiêu cực được dùng làm phản ví dụ cho truy vấn đó. Các vai trò này phụ thuộc vào yêu cầu: một quy trình có thể phù hợp với câu hỏi này nhưng không phù hợp với câu hỏi khác.

Một tiêu cực khó, hay hard negative, là phản ví dụ mà mô hình vẫn cho điểm cao. Nó giúp xử lý chính xác một sự nhầm lẫn. Tài liệu nói về cùng hàng đợi tin nhắn nhưng xử lý vấn đề khác có thể hữu ích hơn một tài liệu không liên quan đến hàng đợi.

Mining là việc chọn các phản ví dụ này. Nó dùng thứ hạng của mô hình để tìm ứng viên gần, nhưng dựa vào chú thích để loại các câu trả lời đúng. Tài liệu Sentence Transformers về tiêu cực khó trình bày nguyên tắc chung; hướng dẫn giữ mã chọn riêng của mình.

Nguy cơ là tiêu cực giả: một quy trình thực sự phù hợp nhưng bị huấn luyện coi là sai. Với hai tích cực, một quy ước chỉ công nhận một sẽ chính xác tạo ra lỗi này.

Mất mát đối sánh: học bằng so sánh

Một mất mát, hay loss, là con số mà huấn luyện cố giảm. Mất mát đối sánh so sánh các ứng viên: ở đây nó khuyến khích mô hình đặt trọng số cao hơn cho tài liệu phù hợp so với các tài liệu khác.

Lan truyền ngược tính gradient, cho biết một thay đổi tham số sẽ làm mất mát đổi ra sao. Bộ tối ưu dùng thông tin này để cập nhật trọng số. Các hướng dẫn PyTorch về gradient và tối ưu hóa trình bày chu trình này. Ngược lại, tính embedding bằng mô hình đã nạp mà không đổi trọng số là suy luận.

Sự điều chỉnh của chúng ta bắt đầu từ một mô hình có sẵn: ta không học cách đọc văn bản từ đầu. Ta cung cấp cho nó giám sát chính xác hơn về những sự gần nhau mong đợi trong kho dữ liệu này.

Nhiệt độ và softmax: điều chỉnh trọng số của chênh lệch

Softmax biến một hàng điểm thành các số dương có tổng bằng 1. Nó dùng exp, hàm mũ: hàm tăng cho giá trị dương, kể cả với điểm âm. Với ứng viên j, nó tính exp(score_j) / somme(exp(scores)). Các giá trị này diễn tả phân bố giữa các ứng viên được xét, không phải xác suất nghiệp vụ đã hiệu chuẩn. Định nghĩa softmax của PyTorch mô tả phép toán này.

Trong huấn luyện, các độ tương đồng được chia cho một nhiệt độ dương trước phép tính này. Nhiệt độ nhỏ làm chênh lệch rõ hơn; nhiệt độ cao hơn làm chúng dịu đi.

Ví dụ, các độ tương đồng giả định [0.4, 0.2, 0, -0.2] thành các điểm [2, 1, 0, -1] sau khi chia cho 0.2. Khi đó ứng viên đầu nhận khoảng 64,39 % khối lượng softmax, thay vì 32,92 % với nhiệt độ 1. Đây là giá trị minh họa, không phải cài đặt hay phép đo r1. Thứ hạng không đổi khi chia dương; mức độ coi trọng chênh lệch thay đổi.

Cuối cùng, log ở đây là logarit tự nhiên, hàm nghịch đảo của hàm mũ. logsumexp tính logarit của tổng các hàm mũ theo cách ổn định số học. Ta sẽ dùng nó để gộp đóng góp của nhiều tích cực mà không tính ngây thơ các hàm mũ rất lớn. Tài liệu torch.logsumexp mô tả phép toán này.

Điều kiện Docker và GPU

Bạn cần đọc được một hàm Python, thao tác danh sách và mã định danh, và chạy lệnh trong terminal. Quy trình r1 dùng Linux x86_64, Docker Compose, NVIDIA Container Toolkit và RTX 3090 24 Gio.

Cũng cần dự kiến 16 Gio RAM và 16 Gio đĩa, không tính các lớp Docker dùng chung. RAM là bộ nhớ máy chủ; bộ nhớ của card đồ họa là VRAM. Các mức này mô tả bối cảnh dự án, không phải mức tối thiểu đã được chứng minh trên mọi máy. Không có cấu hình CPU nào được trình bày như bản tái tạo đã xác thực của quy trình GPU.

locks/models.lock.json cố định tài sản mô hình và locks/environment.lock.json cố định môi trường dự kiến. Hãy giữ các khóa này để tái tạo thí nghiệm: thay một bản sửa đổi bằng nhánh hiện tại sẽ thay đổi các phần tử đang so sánh.

Dịch vụ acquire là dịch vụ duy nhất tải tài sản mô hình. Sau đó dịch vụ gpu thực hiện các tính toán cục bộ. Xây ảnh Docker và thu nhận trọng số là hai thao tác khác nhau: trọng số là tham số mô hình, không phải mã Python sử dụng chúng.

Kiểm tra môi trường khởi tạo CUDA và chạy một phép tính trong container. Chỉ nhìn thấy card từ Linux là chưa đủ. Tài liệu Docker Compose cho GPU và NVIDIA Container Toolkit giải thích tích hợp này.

Dữ liệu và các hàm trung tâm

Biết câu trả lời đúng trước khi huấn luyện

Kho tổng hợp gồm 240 tài liệu và 72 truy vấn kiểm tra. Mỗi truy vấn có đúng hai positive_ids. Các mã này là tham chiếu dùng để xác định quy trình nào phù hợp.

Các họ dữ liệu được tách thành train, validation và test trước khi huấn luyện. Train cung cấp ví dụ ảnh hưởng đến trọng số; validation dùng để quan sát lựa chọn huấn luyện; test dành riêng cho xếp hạng cuối. Tách các họ tránh việc một diễn đạt lại của cùng tập trường hợp xuất hiện ở cả hai phía của đánh giá.

Một truy vấn huấn luyện chỉ mine tài liệu huấn luyện. Mining xếp hạng chúng bằng mô hình hiện tại, loại rõ ràng hai ID tích cực, rồi giữ các tiêu cực. Vì vậy nó không đoán mức phù hợp của chúng. Ranh giới này ngăn một quy trình kiểm tra đã bị bộ tối ưu nhìn thấy như phản ví dụ.

Đừng nhầm 240 tài liệu công bố cho kho với tập được phép mining khi huấn luyện: tập sau bị giới hạn ở split train. Số lượng chi tiết và sự thuộc về các split nằm trong dữ liệu đã chuẩn bị.

Sợi chỉ xuyên suốt: hai quy trình, không phải hai bản sao

Ví dụ r1 atlas-queue-q0 có câu hỏi này:

The Atlas messages remain pending after the queue worker crashes. What should the operator do?

Để hiểu bằng tiếng Việt: « Các tin nhắn Atlas vẫn chờ sau khi worker của hàng đợi gặp sự cố. Người vận hành cần làm gì? » Bản dịch này hỗ trợ việc đọc; nó không thay thế văn bản của thí nghiệm.

Worker là tiến trình đảm nhận các công việc của một hàng đợi. Tin nhắn chưa được xác nhận chưa nhận được xác nhận xử lý mong đợi. Chạy lại tin nhắn như vậy đòi hỏi kiểm soát sự lặp lại: khóa idempotence giúp nhận diện một thao tác để việc chạy lại không lặp không đáng có các tác động.

Hai quy trình được chú thích trả lời những khía cạnh riêng của sự cố này:

Mã tích cực Vai trò của quy trình trong ví dụ này
atlas-queue-procedure-a Kiểm tra và khởi động lại worker.
atlas-queue-procedure-b Chạy lại các tin nhắn chưa được xác nhận bằng khóa idempotence.

Ta không tìm cách gộp các văn bản này, cũng không coi một văn bản luôn thay thế văn bản kia. Cả hai phải vẫn là tích cực cho truy vấn này. Tìm được các tài liệu đó tự nó chưa phải xác nhận vận hành việc thực thi chúng.

Xây dựng mặt nạ từ các mã định danh

Một batch, hay lô, gom nhiều ví dụ được xử lý cùng lúc. Ma trận scores có một hàng cho mỗi truy vấn và một cột cho mỗi tài liệu ứng viên. Do đó hình dạng là B × D, với B truy vấn và D tài liệu.

positive_mask có đúng cùng hình dạng. Nó chứa True khi tài liệu ở cột phù hợp với truy vấn ở hàng. Vì vậy một hàng có thể chứa nhiều True.

Hàm make_batch, trong src/contrastive_search/mining.py, xây mặt nạ này từ mã định danh, rồi kiểm tra có hai tích cực mỗi truy vấn. Đây tương đương một phép nối theo khóa trong ứng dụng: đổi thứ tự hàng không được làm đổi nghĩa của chúng.

Một đường chéo chỉ đánh dấu các vị trí có số hàng bằng số cột. Tự nó không thể biểu diễn hai tích cực trong một hàng. Vì vậy một quy ước vị trí trong lô không được thay thế các chú thích của chúng ta.

Đọc phần lõi của loss

Đây là đoạn liên tục trung tâm của src/contrastive_search/losses.py. Nó nhận các điểm đã tính, một mặt nạ phù hợp và, tùy chọn, mặt nạ ứng viên được đưa vào phép tính.

def multi_positive_nce(scores, positive_mask, allowed=None):
    if scores.ndim != 2 or positive_mask.shape != scores.shape:
        raise ValueError("B x D scores and positive mask required")
    allowed = (
        torch.ones_like(positive_mask, dtype=torch.bool) if allowed is None else allowed
    )
    if not torch.isfinite(scores).all():
        raise ValueError("Non-finite scores")
    if ((positive_mask & allowed).sum(1) == 0).any():
        raise ValueError("Each query needs an allowed positive")
    denominator = torch.logsumexp(scores.masked_fill(~allowed, -torch.inf), dim=1)
    numerator = torch.logsumexp(
        scores.masked_fill(~(positive_mask & allowed), -torch.inf), dim=1
    )
    return (denominator - numerator).mean()

Hãy bắt đầu với đầu vào. scores là bảng số PyTorch, còn gọi là tensor. positive_mask là bảng Boolean tương ứng. Khi không cung cấp allowed, mọi ứng viên đều được đưa vào.

Tên allowed ở đây nghĩa là « được tính trong công thức ». Mặt nạ này không phải xác thực cũng không phải quy tắc quyền tài liệu. Trên các bảng Boolean, ~ đảo True và False, còn & áp dụng phép « và » theo từng ô.

Mẫu số xét mọi ứng viên được đưa vào. masked_fill(~allowed, -torch.inf) thay các điểm bị loại bằng âm vô cùng. Vì hàm mũ của chúng bằng không, chúng không còn góp vào tổng. dim=1 yêu cầu tính trên các cột, riêng cho từng truy vấn.

Tử số xét mọi tích cực được đưa vào. Biểu thức positive_mask & allowed giữ phần giao của hai mặt nạ. Vì thế hai quy trình đúng cùng tham gia tử số, thay vì chỉ giữ một.

Với một truy vấn, ta tính:

L = log(tổng exp(scores) của các ứng viên được đưa vào) − log(tổng exp(scores) của các tích cực được đưa vào).

Trong mã, denominator và numerator là logarit của hai tổng này. Hiệu của chúng tương đương −log(tử số / mẫu số). .mean() cuối cùng lấy trung bình mất mát trên các truy vấn trong lô, không phải trên từng tài liệu tích cực riêng lẻ.

Tự tính lại bằng tay

Lấy lại hàng minh họa [2, 1, 0, -1], với hai tài liệu đầu là tích cực và mọi ứng viên được đưa vào. Bốn điểm này chỉ mang tính sư phạm; chúng không phải đầu ra được chụp từ mô hình.

Cột Điểm Tài liệu tích cực? Đóng góp exp(score), làm tròn
0 2 Có 7,389056
1 1 Có 2,718282
2 0 Không 1,000000
3 −1 Không 0,367879

Tổng mọi đóng góp xấp xỉ 11,475217. Tổng của hai tích cực xấp xỉ 10,107338. Do đó tỷ lệ xấp xỉ 0,880797, rồi mất mát −log(0,880797) xấp xỉ 0,126928.

Ở đây mô hình phân khoảng 88,08 % khối lượng softmax cho tập câu trả lời đúng. Mất mát giảm khi khối lượng này gần 1. Các tính toán số ở trên được thực hiện trước khi làm tròn giá trị hiển thị.

Với mặt nạ chỉ khai báo cột 0 là tích cực, tử số chỉ giữ 7,389056. Mất mát sẽ xấp xỉ 0,440190. Quan trọng hơn, cột 1 ở mẫu số nhưng không ở tử số: tăng điểm của nó sẽ tăng mất mát, dù quy trình của nó phù hợp. Ta sẽ kiểm tra dấu gradient của nó trong bài tập.

Công thức này bảo đảm gì và không bảo đảm gì

Hàm tránh coi tích cực thứ hai như một tiêu cực. Tuy vậy nó ưu tiên tập các tích cực, không đòi chúng nhận cùng điểm. Đây không phải trung bình của hai mất mát độc lập, mỗi mất mát yêu cầu tài liệu của mình tự thắng.

Vì vậy một quy trình đúng có thể nhận phần lớn khối lượng softmax, quy trình kia ít hơn nhiều. Nếu mọi ứng viên được đưa vào đều tích cực, tử số và mẫu số thậm chí giống nhau: mất mát bằng không, bất kể thứ tự.

Điều này giải thích vì sao sau đó ta đo sự hiện diện của cả hai quy trình trong top 5. Mất mát thấp không đủ chứng minh từng quy trình được xếp hạng tốt.

Các hàng rào cũng có phạm vi chính xác. Hàm kiểm tra hình dạng scores và positive_mask, tính hữu hạn của điểm và sự tồn tại ít nhất một tích cực được đưa vào cho mỗi truy vấn. Nó không tự kiểm tra có đúng hai: việc đó thuộc make_batch. Các mặt nạ phải là Boolean tương thích do bên gọi cung cấp.

Kiểm tra giá trị hữu hạn diễn ra trước khi che. Do đó một NaN hoặc vô cùng đã có trong scores bị từ chối ngay cả khi cột của nó sẽ bị loại. Âm vô cùng được masked_fill chủ ý thêm vào thì dùng để loại đóng góp khỏi phép tính.

Cuối cùng, đoạn này không mã hóa văn bản, không chọn tiêu cực và không chia độ tương đồng cho nhiệt độ. Nó cũng không có backward() hay cập nhật trọng số. Nó tính một mất mát khả vi mà vòng lặp huấn luyện dùng sau đó.

Cài đặt, kiểm tra rồi chuẩn bị dữ liệu

Giải nén ZIP và vào thư mục gốc của nó. Mọi lệnh sau chạy từ thư mục gốc này, theo thứ tự đã nêu.

Trước hết ta chuẩn bị thư mục, kiểm tra cấu hình Compose, xây image, thu nhận tài sản đã khóa, kiểm tra CUDA và chạy kiểm thử. Sáu lệnh sau là sáu lệnh đầu tiên của quy trình r1.

sh scripts/setup.sh
docker compose config --quiet
docker compose build acquire gpu
docker compose run --rm acquire python scripts/acquire.py --config configs/minimum.yaml
docker compose run --rm gpu python scripts/check_environment.py --config configs/minimum.yaml
docker compose run --rm gpu python -m pytest -q

docker compose config --quiet kiểm tra cấu hình mà không in toàn bộ tệp đã phân giải. Build chuẩn bị image. Thu nhận xác thực các tệp được khóa xác định; kiểm tra môi trường xác minh phép tính CUDA; pytest chạy kiểm thử dự án. Tài liệu Docker Compose mô tả lệnh kiểm tra.

Không tiếp tục nếu một bước thất bại. Bộ kiểm thử thành công xác minh các hành vi chương trình; nó chưa cho biết bộ mã hóa đã điều chỉnh có xếp hạng các quy trình tốt hơn không.

Bây giờ hãy chuẩn bị kho, xem vector và trích các tiêu cực. Ta chưa huấn luyện mô hình.

docker compose run --rm gpu python -m contrastive_search.cli prepare --config configs/minimum.yaml
docker compose run --rm gpu python -m contrastive_search.cli inspect-embeddings --config configs/minimum.yaml
docker compose run --rm gpu python -m contrastive_search.cli mine --config configs/minimum.yaml
docker compose run --rm gpu python -m contrastive_search.cli check-loss --config configs/minimum.yaml

Sau prepare, hãy xem các split và mã định danh. Sau inspect-embeddings, hãy liên hệ các vector với văn bản sinh ra chúng: một bảng tọa độ tự nó không phải là thứ hạng hoàn chỉnh.

Sau mine, mở outputs/mining-audit.json. Xác minh các tiêu cực đến từ train và không positive_ids nào được chọn làm tiêu cực. Tỷ lệ tiêu cực giả kỳ vọng bằng không so với các chú thích đã biết. Kiểm tra này không thể phát hiện quy trình đúng thứ ba chưa được ai chú thích.

Cuối cùng, check-loss kiểm tra công thức trên các tensor đã biết. Nó giúp phát hiện lỗi cơ chế trước khi tìm lỗi trong hàng nghìn phép tính huấn luyện.

Huấn luyện và kiểm tra việc tiếp tục

Ta sẽ so sánh ba lần huấn luyện. Tham chiếu hard, chạy không có tùy chọn biến thể, dùng tiêu cực khó. random thay đổi cách chọn chúng; temperature thay đổi nhiệt độ. Mỗi biến thể thay đổi một quyết định thực nghiệm để khác biệt vẫn có thể diễn giải.

Chú ý từ vựng: baseline ở dưới là mô hình trước điều chỉnh này. Còn tham chiếu hard là một lần huấn luyện. Chúng không phải hai tên của cùng trạng thái.

Giá trị nhiệt độ chính xác và các cài đặt huấn luyện khác vẫn là giá trị trong cấu hình dự án. Các số trong ví dụ tính toán của ta không thay thế chúng.

Các lệnh cuối gây gián đoạn, tiếp tục checkpoint đã tạo và so sánh việc tiếp tục. Giữ thứ tự này: artifacts/checkpoints/interrupted/state.pt phải tồn tại trước lệnh nạp nó.

docker compose run --rm gpu python -m contrastive_search.cli train --config configs/minimum.yaml
docker compose run --rm gpu python -m contrastive_search.cli train --config configs/minimum.yaml --variant random
docker compose run --rm gpu python -m contrastive_search.cli train --config configs/minimum.yaml --variant temperature
docker compose run --rm gpu python -m contrastive_search.cli train --config configs/minimum.yaml --stop-after 10 --run-name interrupted
docker compose run --rm gpu python -m contrastive_search.cli train --config configs/minimum.yaml --resume artifacts/checkpoints/interrupted/state.pt --run-name resumed
docker compose run --rm gpu python -m contrastive_search.cli compare-resume --config configs/minimum.yaml

Checkpoint là bản lưu trạng thái tính toán. Có lại trọng số để tìm kiếm và có lại trạng thái cần để tiếp tục học là hai nhu cầu khác nhau. Tùy vòng lặp, việc tiếp tục còn phụ thuộc bộ tối ưu, tiến trình trong dữ liệu và bộ sinh ngẫu nhiên. Tài liệu PyTorch về lưu để tiếp tục giải thích vì sao chỉ giữ trọng số không phải lúc nào cũng đủ. Nội dung chính xác của bản lưu dự án này có thể xem trong archive.

compare-resume dùng để kiểm tra trạng thái cần thiết đã được dựng lại, thay vì đánh giá độ giống nhau của hai đường cong. Hãy đọc các kiểm tra: nạp không lỗi tự nó chưa chứng minh việc tiếp tục tương đương.

Đo trước bài tập

Bây giờ hãy tính các thứ hạng tham chiếu, trước mọi sửa đổi do bài tập dẫn dắt.

docker compose run --rm gpu python -m contrastive_search.cli evaluate --config configs/minimum.yaml

Lệnh ghi chỉ số và thứ hạng vào outputs/. Hai loại đầu ra bổ sung nhau: chỉ số tóm tắt lô, còn thứ hạng giúp hiểu chuyện gì xảy ra với một truy vấn cụ thể.

Để đọc lần đầu, tìm ID của một câu hỏi, hai positive_ids của nó, rồi vị trí của chúng trong từng thứ hạng. Sau đó xem trung bình trên tập kiểm tra. Một tìm kiếm thành công không đủ để xác lập chất lượng toàn hệ thống.

Chỉ số trước khi đo

Recall@5: ta đã tìm được hai quy trình chưa?

Recall@5, hay recall ở năm kết quả, là phần tài liệu phù hợp xuất hiện trong năm vị trí đầu:

Recall@5 = số tích cực trong top 5 / tổng số tích cực kỳ vọng.

Với hai quy trình phù hợp, chỉ một trong top 5 cho 1 / 2 = 0,5. Cả hai cho 2 / 2 = 1. Trung bình cuối gộp đóng góp của các truy vấn.

Mẫu số ở đây là 2, không phải 5. Recall hỏi « tôi đã tìm được bao nhiêu phần các quy trình đúng? ». Nó không hỏi « bao nhiêu phần trong năm kết quả là phù hợp? », đó là chỉ số precision khác. Vì vậy hai quy trình đúng trong năm kết quả có thể cho 100 % recall mà không phải cả năm đều phù hợp.

MRR@10: quy trình đúng đầu tiên xuất hiện ở đâu?

Với một truy vấn, hạng nghịch đảo là nghịch đảo của hạng tích cực đầu tiên trong mười vị trí đầu. Tích cực ở hạng 1 cho 1; hạng 2 cho 1 / 2 = 0,5; hạng 4 cho 1 / 4 = 0,25. Nếu không có tích cực nào trong mười vị trí đầu, đóng góp là 0.

MRR là Mean Reciprocal Rank: sau đó ta lấy trung bình các đóng góp trên các truy vấn. Ví dụ ba đóng góp 1, 0,5 và 0 cho MRR (1 + 0,5 + 0) / 3 = 0,5. Phép tính nhỏ này minh họa chỉ số, không phải lô r1.

MRR bỏ qua vị trí tích cực thứ hai khi đã tìm được tích cực đầu. Vì vậy cần đọc nó cùng Recall@5, không thay thế Recall@5. Tài liệu đánh giá tìm kiếm của Sentence Transformers đặt các chỉ số này trong thiết bị dựa trên truy vấn, kho và chú thích.

Sáu thứ hạng để phân biệt hai câu hỏi

Đây là các ví dụ sư phạm với đúng hai tích cực. Các số cho biết hạng của chúng, bắt đầu từ 1; các tài liệu khác không phù hợp.

Hạng của hai tích cực Recall@5 của truy vấn Hạng nghịch đảo ở 10 Điều quan sát
1 và 2 1 1 Cả hai quy trình đứng đầu.
1 và 5 1 1 Cùng chỉ số, nhưng tích cực thứ hai thấp hơn.
2 và 4 1 0,5 Cả hai được tìm thấy; không cái nào đứng đầu.
1 và 8 0,5 1 Cái đầu đặt lý tưởng, cái thứ hai thiếu ở top 5.
6 và 9 0 Khoảng 0,1667 Các tích cực ở top 10, không ở top 5.
11 và 12 0 0 Không có tích cực trong phạm vi đo.

Hai hệ quả hiện rõ. Recall@5 hoàn hảo có thể đồng thời với MRR vẫn còn có thể cải thiện. Và ngay cả khi cả hai chỉ số bằng 1, chúng không chứng minh hai quy trình chiếm hai hạng đầu.

Bốn biến thể đều giữ Recall@5 100 %; MRR@10 tăng từ 0,9792 lên 1,0000 trong bản tái tạo này.

Kết quả thực và điều đã học

Các giá trị sau là phép đo r1 báo cáo trên 72 truy vấn kiểm tra và 240 tài liệu. Chúng khác các ví dụ tính toán trước đó.

Trạng thái đánh giá Recall@5 MRR@10
Baseline, trước điều chỉnh 1,0 0,9791666666666666
Huấn luyện random 1,0 1,0
Huấn luyện hard 1,0 1,0
Huấn luyện temperature 1,0 1,0

Quan sát đầu tiên là recall không tăng: nó đã hoàn hảo trước huấn luyện. Baseline tìm được hai quy trình phù hợp trong năm kết quả đầu cho mọi truy vấn được đánh giá.

MRR tăng từ 0,9791666666666666 lên 1,0. Sau mỗi trong ba lần huấn luyện, quy trình phù hợp đầu tiên đến hạng một cho mọi truy vấn của lô. Điều đó không ngụ ý quy trình thứ hai luôn ở hạng 2: bảng sư phạm trước cho thấy lý do.

Ba biến thể huấn luyện có cùng chỉ số. Vì vậy ta không thể gán cho tiêu cực khó một ưu thế mà bảng này không cho thấy. Kho tổng hợp nhỏ và đã dễ; nó trình bày vận hành của giao thức này, không phải bảng xếp hạng phổ quát các chiến lược huấn luyện.

Tổng hợp cũng không cho phân bố chi tiết các hạng baseline. Muốn biết truy vấn nào thay đổi, hãy đọc lại các thứ hạng đã lưu thay vì dựng câu chuyện chỉ từ trung bình.

Bài học chính là cấu trúc: nhãn phù hợp phải còn nguyên qua việc tạo batch. Quy trình đúng thứ hai vẫn là quy trình đúng, bất kể cột nào trong ma trận. Và loss tính đúng không bao giờ miễn cho việc đo thứ hạng cuối.

Bài tập: dự đoán rồi chạy

Lấy lại hàng [2, 1, 0, -1]. Cột 0 và 1 tương ứng hai quy trình phù hợp. Bây giờ hãy tưởng tượng mặt nạ dựa trên đường chéo chỉ giữ một: chỉ cột 0 còn được đánh dấu tích cực.

Trước khi chạy lệnh, hãy ghi dự đoán: gradient của điểm cột 1 sẽ khuyến khích điểm này tăng hay giảm?

Để đọc dấu, hãy nhớ cập nhật cơ bản: new_score = score − step × gradient, với bước dương. Gradient âm đẩy điểm lên; gradient dương đẩy điểm xuống. Ở đây ta nói về đạo hàm theo điểm để cô lập cơ chế.

docker compose run --rm gpu python -m contrastive_search.cli exercise --config configs/exercise.yaml

Đầu ra bài tập lưu hai gradient và xác nhận lực đẩy không công bằng khi tích cực thứ hai không còn được đánh dấu. Hãy so với dự đoán của bạn.

Để hỗ trợ việc đọc, đây là các đạo hàm tính trên hàng sư phạm, với mọi ứng viên được đưa vào và chỉ một truy vấn. Chúng không được trình bày như ảnh chụp huấn luyện r1.

Cột Gradient với hai tích cực Gradient chỉ với tích cực đầu Tác động của quy ước thứ hai
0, tích cực −0,087144 −0,356086 Tích cực đầu vẫn được khuyến khích.
1, tích cực −0,032059 +0,236883 Tích cực thứ hai nay bị đẩy xa.
2, tiêu cực +0,087144 +0,087144 Tiêu cực vẫn bị đẩy xa.
3, tiêu cực +0,032059 +0,032059 Tiêu cực vẫn bị đẩy xa.

Đổi dấu ở cột 1 là lỗi cần nhớ. Mô hình không phát hiện quy trình sai: chúng ta đã cung cấp cho nó giám sát sai.

Trong mạng thật, các điểm chia sẻ cùng tham số. Các đạo hàm mô tả áp lực loss đặt lên mỗi điểm; chúng không bảo đảm từng điểm sẽ đổi riêng theo chiều này sau cập nhật mọi trọng số.

Sau đó hãy hoàn thiện artefact và dừng quy trình, theo thứ tự:

docker compose run --rm gpu python lesson/figures/plot.py
docker compose run --rm gpu python scripts/check_checkpoints.py
docker compose run --rm gpu python scripts/clean.py --scope generated --dry-run
docker compose down --remove-orphans

Lệnh đầu tạo hình; lệnh thứ hai kiểm tra checkpoint, đặc biệt các checkpoint tạo trong huấn luyện. clean.py --dry-run chỉ xem trước việc dọn dẹp và không xóa tệp nào. Lệnh cuối dừng tài nguyên Compose của dự án. Đừng nhầm xem trước này với việc dừng container thực sự.

Điều chỉnh, sản xuất, khắc phục sự cố và dọn dẹp

Chuyển từ kho tổng hợp sang tài liệu của bạn

Để điều chỉnh cơ chế, hãy bắt đầu bằng các phán đoán phù hợp được lập phiên bản và rà soát. Một yêu cầu có thể có hai quy trình đúng, nhưng cũng có một, nhiều hơn hoặc không có. Hợp đồng « đúng hai tích cực » là của hướng dẫn này; nó không được thành giả định ngầm cho dữ liệu thực.

Giữ phân tách rõ giữa huấn luyện, xác thực và kiểm tra. Phân tích dương tính giả theo họ: đó là quy trình được xếp hữu ích nhưng không trả lời yêu cầu. Phân biệt chúng với tiêu cực giả của mining, là quy trình tốt bị dùng sai làm phản ví dụ huấn luyện.

Tiêu cực khó có thể là một mức phù hợp chưa chú thích. Việc ID của nó vắng trong danh sách tích cực không chứng minh nó vô ích. Vì vậy hãy giữ audit mining và cơ chế rà soát. Sửa chú thích là quyết định dữ liệu, không phải tác động tự động của mô hình.

Nếu điều chỉnh mô hình, hãy tính lại các biểu diễn tài liệu bằng bộ mã hóa tương ứng trước khi so sánh truy vấn mới: trộn hai trạng thái bộ mã hóa không còn tái tạo tìm kiếm cùng một mô hình được mô tả ở đây. Tính nhất quán này xuất phát từ vận hành quy trình, không phải chức năng di chuyển chỉ mục đã có.

Câu hỏi Atlas ở trên bằng tiếng Anh và trang mô hình ghi language: en. Một kho tiếng Pháp vì vậy là thí nghiệm cần đánh giá riêng; bản dịch sư phạm của bài viết không xác nhận chất lượng tiếng Pháp của nó.

Đừng biến trung bình cục bộ thành cam kết dịch vụ

Đo độ trễ dưới tải mục tiêu của bạn. Phép đo nóng mỗi truy vấn — khi mô hình đã nạp — không mô tả lưu lượng đồng thời hay việc chờ trong hàng truy vấn. Nó cũng không xác thực chỉ mục ANN, tức tìm kiếm láng giềng gần xấp xỉ để tránh so sánh toàn bộ vector.

Dự án không trở thành API sản xuất chỉ vì Recall@5 bằng 1. Nạp tài liệu, quyền truy cập, cập nhật chỉ mục, sao lưu, giới hạn đầu vào và quan sát cần thiết kế và kiểm thử riêng cho ứng dụng. Các mở rộng này khác với thí nghiệm đã đo.

Chẩn đoán mà không âm thầm thay đổi thí nghiệm

CUDA thất bại. Trước hết chạy lại check_environment.py và xem hướng dẫn NVIDIA Container Toolkit. Lỗi truy cập GPU không phải lý do để đổi công thức loss.

Thu nhận thất bại. Kiểm tra mạng của dịch vụ acquire, rồi chạy lại đúng lệnh đó. Đừng sửa tệp khóa để chấp nhận một tài sản khác.

Loss từ chối truy vấn không có tích cực được đưa vào. Kiểm tra ID, split và giao positive_mask & allowed. Bỏ kiểm tra chỉ làm mất triệu chứng, không làm mất giám sát bị thiếu.

Điểm gây bất ngờ. Xem hạng của cả hai tích cực, không chỉ kết quả đầu hay loss. MRR hoàn hảo có thể che tích cực thứ hai quá thấp; Recall@5 đã hoàn hảo thì không thể tăng thêm.

Cuối cùng, docker compose down --remove-orphans dừng dịch vụ dự án và giữ các tệp cục bộ mô tả trong quy trình. clean.py --dry-run chỉ hiển thị mục tiêu của nó. Giữ đầu ra tham chiếu trước mọi thí nghiệm riêng hoặc xóa thực sự.

Nguồn chính thức

Mô hình và học. Trang all-MiniLM-L6-v2 giới thiệu mô hình; phiên bản khóa của nó được liên kết ở trên. Tài liệu Sentence Transformers về loss, mining tiêu cực khó và đánh giá tìm kiếm đi sâu các chủ đề này. Chúng không thay thế công thức nhiều tích cực riêng trong mã archive này.

Tính toán số. Các tham chiếu PyTorch cho normalize, softmax và logsumexp nêu rõ các phép toán dùng trong giải thích. Trang phiên bản PyTorch là tham chiếu cài đặt; để tái tạo r1, giữ môi trường cố định trong các khóa dự án.

Môi trường. Xem Docker Compose và truy cập GPU cùng hướng dẫn NVIDIA Container Toolkit để hiểu tích hợp giữa máy chủ, container và card.

Chia sẻ bài viết