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

Điều chỉnh AI theo từ vựng tài liệu hướng dẫn và đo lường điều nó quên

Làm thế nào để dạy AI từ vựng kỹ thuật của nhà sản xuất? Huấn luyện nó bằng tài liệu hướng dẫn giả định, rồi kiểm tra xem việc đọc lại văn bản kiểm soát có giúp nó giữ kiến thức trước đó không.

Masquer → Adapter → Contrôler. Adapter BERT au domaine

Hãy hình dung cổng tài liệu của một nhà sản xuất. Bạn đã có back-office, các tài liệu hướng dẫn và quyền truy cập. Giờ bạn muốn khai thác những văn bản này bằng một mô hình ngôn ngữ, nhưng mã máy móc và từ vựng kỹ thuật không phải lúc nào cũng giống những gì mô hình đã gặp trong quá trình học ban đầu.

Một hướng đi là cho mô hình học các tài liệu hướng dẫn. Không phải bằng cách yêu cầu nó đọc chúng trong một prompt, mà bằng cách tiếp tục huấn luyện: chúng ta che một số phần của văn bản, rồi yêu cầu mô hình tìm lại chúng.

Cách này giống một bài tập điền vào chỗ trống. Khó khăn đáng chú ý xuất hiện sau đó: mô hình có tiến bộ trên các tài liệu hướng dẫn mà không thụt lùi trên các văn bản khác không? Và làm sao biết được nếu các chỗ trống thay đổi ở mỗi lần đánh giá?

Trong hướng dẫn này, chúng ta sẽ điều chỉnh BERT cho một lĩnh vực, so sánh một biến thể có ôn lại định kỳ một corpus đối chứng, rồi đo cả hai hiệu ứng bằng một bài kiểm tra giống hệt cho từng mô hình. Các tài liệu hướng dẫn đều là dữ liệu tổng hợp: chúng ta nghiên cứu một cơ chế học, không phải một trợ lý bảo trì sẵn sàng triển khai.

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

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

Dự án so sánh ba trạng thái của cùng một mô hình. Mốc tham chiếu, hay baseline, là mô hình trước khi được điều chỉnh. Nhánh thứ hai tiếp tục học trên các tài liệu của lĩnh vực. Nhánh thứ ba bổ sung replay: các khối của corpus đối chứng quay lại định kỳ trong các batch huấn luyện.

Một checkpoint là trạng thái đã lưu của mô hình; để tiếp tục huấn luyện, cũng cần các trạng thái khác cần thiết cho phép tính. Chúng ta sẽ trở lại với sự phân biệt này khi nói về việc tiếp tục chạy.

Trước khi so sánh các checkpoint này, chương trình chuẩn bị cùng các văn bản đánh giá và cùng các vị trí cần dự đoán. Sau đó nó đo hai điều, riêng cho lĩnh vực và đối chứng: xác suất dành cho các token mong đợi, được tóm tắt bằng NLL, và tần suất lựa chọn tốt nhất của mô hình là đúng, gọi là độ chính xác top-1.

Các tài liệu hướng dẫn của lĩnh vực và corpus đối chứng đi qua những mặt nạ cố định, rồi các checkpoint được so sánh từng token.

Cuối cùng, bạn sẽ biết theo dõi đường đi văn bản → token → đầu vào bị làm hỏng một phần → dự đoán → chỉ số. Bạn cũng sẽ biết phân biệt sự cải thiện trong bài tập này với năng lực trả lời đúng một câu hỏi nghiệp vụ. Chương trình không xây dựng cổng thông tin nêu ở phần mở đầu, cũng không xây dựng hệ thống ra quyết định công nghiệp.

Các khái niệm được giải thích

Token không nhất thiết là một từ

Mô hình không nhận trực tiếp một chuỗi Python. Một tokenizer chia văn bản thành các đơn vị từ vựng, gọi là token, rồi gán cho chúng các định danh số. Một đơn vị có thể là một từ, một phần từ hoặc một ký hiệu. Trong dự án này, chúng ta làm việc với ID của các từ con.

Hãy xem đây là chuyển đổi cách biểu diễn: ứng dụng của bạn xử lý văn bản; mô hình xử lý các mảng số nguyên. Một ID chỉ một mục trong từ vựng. Bản thân giá trị số của nó không nói lên nghĩa của token.

Biến thể được dùng là google-bert/bert-base-uncased. Trang chính thức cho biết nó được tiền huấn luyện bằng tiếng Anh và không phân biệt chữ hoa chữ thường. Phần giải thích tiếng Pháp của bài viết này không thay đổi mô hình hay dữ liệu tham chiếu đó.

Học bằng bài tập điền chỗ trống

Một masked language model, hay MLM, cố gắng tìm lại token từ ngữ cảnh của chúng. BERT có thể dùng văn bản trước và sau vị trí cần dự đoán. Vì vậy ở đây nó không làm cùng bài tập với mô hình chỉ viết tiếp phần sau của văn bản. Nguyên lý này được mô tả trong bài báo BERT và hướng dẫn MLM của Hugging Face.

Để minh họa ý tưởng, hãy tưởng tượng câu có chỗ trống: « Nhiệt độ được ghi nhận là [MASK] độ. » Mô hình phải đề xuất phần còn thiếu dựa vào ngữ cảnh. Đây là minh họa cơ chế, không phải đầu ra được đo hay tokenization BERT thực tế.

Nhưng đáp án đúng ở đâu khi các tài liệu không được gắn nhãn? Trong văn bản gốc, trước khi sửa đổi nó. Chương trình giữ ID token mong đợi, rồi thay đổi vị trí đó trong đầu vào. Nhờ vậy nó tự tạo ví dụ và đáp án: đây được gọi là học tự giám sát.

Vì vậy, « không có nhãn thủ công » không có nghĩa là « không có mục tiêu học ». Ở đây không ai cần chú thích từng câu bằng một hạng mục; các token gốc đóng vai trò label.

Huấn luyện thay đổi điều gì

Ở mỗi vị trí, mô hình tạo ra các logit: điểm số cho các token có thể. Softmax biến các điểm này thành một phân phối xác suất. Loss, hay hàm mất mát, phạt mô hình khi nó dành quá ít xác suất cho token tham chiếu.

Lan truyền ngược tính gradient, tức độ nhạy của loss đối với các tham số của mạng. Optimizer dùng các gradient này để thay đổi trọng số, những con số đã học quyết định hành vi của mô hình. Một batch gom nhiều ví dụ được xử lý trong một phép tính.

Chúng ta không bắt đầu lại từ trọng số ngẫu nhiên: BERT đã có quá trình học ban đầu. Chúng ta tiếp tục một mục tiêu ngôn ngữ trên một corpus chuyên biệt. Đây là điều chỉnh lĩnh vực bằng cách tiếp tục tiền huấn luyện, được nghiên cứu trong Don't Stop Pretraining.

Đây chưa phải huấn luyện có giám sát để trả lời câu hỏi kỹ thuật. Tìm lại một token trong tài liệu và chọn đúng quy trình trước một yêu cầu là hai nhiệm vụ khác nhau. Hướng dẫn chỉ đo nhiệm vụ thứ nhất.

Điều chỉnh mà không đặt cược mọi thứ vào lĩnh vực mới

Khi huấn luyện mô hình trên các tài liệu hướng dẫn, chúng ta thay đổi những trọng số cũng tham gia vào dự đoán của nó trên các văn bản khác. Vì vậy kết quả có thể tiến bộ ở đây và giảm sút ở nơi khác.

Quên thảm họa chỉ vấn đề mất hiệu năng trong quá trình học những điều mới. Trong thử nghiệm của chúng ta, ta quan sát một chỉ báo hẹp hơn: khả năng suy giảm trên corpus đối chứng đã chọn. Chúng ta không đo toàn bộ kiến thức trước đó của BERT.

Replay đóng vai trò các lần ôn xen kẽ: thay vì chỉ học chủ đề mới, quá trình huấn luyện cũng gặp các ví dụ đối chứng. Nó không khôi phục trọng số cũ và không gọi một mô hình thứ hai. Trong r1, biến thể thay đổi lịch batch chứ không thay đổi nguyên lý MLM.

Cuối cùng, việc so sánh phải dựa trên cùng một bài kiểm tra. Với nhà phát triển web, có thể ví nó như một bộ kiểm thử hồi quy: thay đổi test case cùng lúc với cách triển khai khiến không thể tách riêng điều đã tạo ra thay đổi điểm số.

Điều kiện tiên quyết Docker và GPU

Bạn cần có thể đọc các hàm Python, danh sách, từ điển và JSON, đồng thời sử dụng terminal với Docker. Các khái niệm học máy cần thiết được giải thích trong lộ trình.

Việc tái lập r1 dùng Linux x86_64, Docker Compose, NVIDIA Container Toolkit và một RTX 3090 24 Gio. Dự trù 16 Gio RAM và 16 Gio đĩa, không tính các layer Docker dùng chung. Đây là mức cấp phát của dự án, không phải mức tối thiểu đã đo; không có lộ trình CPU nào được xác thực ở đây.

locks/models.lock.json khóa google-bert/bert-base-uncased ở bản sửa đổi 86b5e0934494bd15c9632b12f734a8a67f723594. locks/environment.lock.json mô tả môi trường. Giống như tệp phụ thuộc được khóa trong một dự án web, những tệp này cho phép nhận diện công thức đã dùng: đừng thay một phiên bản để âm thầm làm biến mất lỗi.

Dịch vụ acquire tải các tài sản công khai. Dịch vụ gpu thực hiện các phép tính cục bộ không có mạng. Script check_environment.py khởi tạo CUDA, môi trường tính toán NVIDIA, từ trong container. Card nhìn thấy trên máy chủ là chưa đủ; Docker cũng phải dùng được nó. Hướng dẫn GPU của Docker Compose giải thích việc tích hợp này.

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

Chúng ta thực sự sẽ đếm gì?

Một corpus là tập hợp tài liệu. Một split là một phân vùng của tập đó: phần huấn luyện dùng để thay đổi mô hình, phần validation để theo dõi lựa chọn phát triển, và phần test để đánh giá cuối. Lĩnh vực và đối chứng đều có validation và test riêng.

Đây là số lượng được giữ lại trong đánh giá r1. Đây là các vị trí được giám sát, tức các token mà điểm số dựa vào, chứ không phải số tài liệu hay câu hỏi.

Phân vùng Token được đánh giá Vai trò
domain-validation 3 089 Theo dõi việc điều chỉnh trên lĩnh vực
domain-test 3 054 Đo kết quả cuối trên lĩnh vực
control-validation 1 318 Theo dõi hiệu ứng trên đối chứng
control-test 1 233 Đo kết quả cuối trên đối chứng

Ba checkpoint nhận cùng các vị trí và cùng đầu vào bị làm hỏng cho từng phân vùng. Cụm từ « token bị che » là cách nói tắt: chúng ta sẽ thấy rằng một số vị trí được đánh giá vẫn hiển thị trong đầu vào.

Một ví dụ của corpus r1 mang ID domain-006-0. Đó là tài liệu hướng dẫn Velon hư cấu về ghép nối bơm: nó yêu cầu cô lập bơm trước khi kiểm tra và nêu nhiệt độ vận hành được ghi nhận là 62 độ. Thông tin này thuộc dữ liệu bài tập. Đây tuyệt đối không phải chỉ dẫn cho thiết bị thực tế.

Cố định câu hỏi và đáp án

Đoạn liên tiếp sau, từ src/domain_mlm/eval_masks.py, xây dựng bộ so sánh rồi kiểm tra nó không thay đổi.

def freeze_evaluation_masks(config):
    tokenizer = AutoTokenizer.from_pretrained("models", local_files_only=True)
    generator = torch.Generator().manual_seed(config["seed"] + 1000)
    sets = {}
    for corpus in ("domain", "control"):
        for split in ("validation", "test"):
            key = corpus + "-" + split
            rows = read_json("outputs/" + key + ".json")
            ids = torch.tensor([r["input_ids"] for r in rows])
            masked, labels, _ = mask_tokens(
                ids, tokenizer, generator, config["mask_probability"]
            )
            sets[key] = [
                {
                    **row,
                    "original_ids": row["input_ids"],
                    "input_ids": masked[i].tolist(),
                    "labels": labels[i].tolist(),
                }
                for i, row in enumerate(rows)
            ]
    target = Path("outputs/eval-masks.json")
    if target.exists():
        assert read_json(target) == sets, "Evaluation masks changed"
    else:
        save_json(target, sets)

Hãy bắt đầu bằng điều hàm này không làm: nó không gọi BERT để dự đoán token và không khởi chạy huấn luyện nào. Nó chuẩn bị các ví dụ sẽ được gửi tới mô hình sau này.

AutoTokenizer.from_pretrained("models", local_files_only=True) tải tokenizer đã được lấy về trong thư mục cục bộ. Tên models ở đây là một đường dẫn, không phải yêu cầu tải tự động.

torch.Generator().manual_seed(config["seed"] + 1000) tạo một bộ sinh giả ngẫu nhiên chuyên dụng. Seed cố định điểm khởi đầu của chuỗi lần rút. Độ lệch + 1000 là lựa chọn của dự án; nó không có nghĩa là « một nghìn mặt nạ ».

Hai vòng lặp lần lượt đi qua domain-validation, domain-test, control-validation và control-test. torch.tensor(...) biến danh sách ID thành một mảng số, hay tensor. Cách xây dựng này giả định các dòng có độ dài tương thích; đoạn trích không tự làm chúng có cùng độ dài.

Sau mask_tokens, mỗi dòng giữ metadata nhờ **row, rồi có ba cách biểu diễn bổ sung: original_ids cho văn bản đã token hóa trước biến đổi, input_ids cho đầu vào bị làm hỏng và labels cho các mục tiêu cần tìm lại. tolist() làm tensor có thể lưu trong JSON.

Cuối cùng, outputs/eval-masks.json được tạo nếu chưa tồn tại. Nếu đã có, mã tái tạo dữ liệu mong đợi và so sánh toàn bộ với nội dung đã lưu. Một khác biệt gây ra Evaluation masks changed thay vì âm thầm ghi đè bài kiểm tra cũ. Lộ trình r1 cũng công bố một khóa và số mục tiêu theo split; phần ghi bổ sung của chúng không có trong đoạn trích này.

Cùng hạt giống và cùng mặt nạ: hai sự bảo đảm khác nhau

Bộ sinh số được dùng chung cho bốn tập dữ liệu và tiến lên sau mỗi lần rút. Vì vậy, thay đổi thứ tự các dòng, độ dài của chúng hoặc số lần rút có thể làm thay đổi chuỗi kết quả, ngay cả khi vẫn giữ cùng hạt giống ban đầu. Việc so sánh tệp bảo vệ dữ liệu thực sự đã được chuẩn bị, chứ không chỉ một giá trị cấu hình.

Cơ chế bảo vệ này có một giới hạn rõ ràng: nó dùng assert. Python có thể vô hiệu hóa các assertion ở chế độ tối ưu hóa, nhất là với tùy chọn -O. Hãy giữ chế độ thực thi của quy trình; trong một bản triển khai sản xuất, kiểm tra bắt buộc không nên phụ thuộc vào các assertion có thể bị tắt. Đây là hành vi được mô tả trong tài liệu Python, không phải là thay đổi đối với mã ở trên.

Cố định mặt nạ loại bỏ một nguồn biến thiên, chứ không phải tất cả các nguồn khác. Bạn vẫn phải giữ nguyên tokenizer, dữ liệu, tham số đánh giá và môi trường. PyTorch nhắc rằng cùng một hạt giống không bảo đảm kết quả giống hệt trên mọi nền tảng hoặc phiên bản.

Tạo đầu vào và mục tiêu MLM

Khóa đã mô tả bài kiểm tra. Đoạn liên tục từ src/domain_mlm/collator.py giờ mô tả cơ chế tạo đầu vào và mục tiêu, cũng được dùng khi huấn luyện.

def mask_tokens(input_ids, tokenizer, generator, probability=0.15):
    if not 0 <= probability <= 1:
        raise ValueError("Mask probability out of range")
    eligible = torch.ones_like(input_ids, dtype=torch.bool)
    for value in tokenizer.all_special_ids:
        eligible &= input_ids != value
    chosen = (torch.rand(input_ids.shape, generator=generator) < probability) & eligible
    labels = input_ids.clone()
    labels[~chosen] = -100
    corrupted = input_ids.clone()
    choice = torch.rand(input_ids.shape, generator=generator)
    corrupted[chosen & (choice < 0.8)] = tokenizer.mask_token_id
    admissible = torch.tensor(
        [i for i in range(len(tokenizer)) if i not in tokenizer.all_special_ids]
    )
    random_ids = admissible[
        torch.randint(len(admissible), input_ids.shape, generator=generator)
    ]
    random_positions = chosen & (choice >= 0.8) & (choice < 0.9)
    corrupted[random_positions] = random_ids[random_positions]
    return corrupted, labels, eligible

Bước đầu tiên là phân biệt những gì có thể được chọn với những gì thực sự đã được chọn.

eligible là một mảng Boolean có cùng hình dạng với các ID. Vòng lặp loại bỏ các token đặc biệt của tokenizer, ví dụ dấu bắt đầu, phân cách hoặc đệm. Padding, hay phần đệm, dùng để hoàn thiện các chuỗi ngắn thành các mảng có cùng chiều rộng; nó không đại diện cho văn bản cần khôi phục.

Sau đó, chosen thực hiện một lần rút cho mỗi vị trí và kết hợp nó với eligible. Tham số probability mặc định là 0.15 trong hàm này. Khi cố định đánh giá, nơi gọi truyền config["mask_probability"]: vì vậy bạn cần xem cấu hình đã khóa để biết giá trị thực tế của quy trình.

labels bắt đầu là một bản sao của các token gốc. Những vị trí không được chọn trở thành -100. Theo hợp đồng của cross-entropy trong PyTorch, giá trị này báo hiệu một mục tiêu cần bỏ qua. Nó không thay thế văn bản trong đầu vào của mô hình. Một token không được giám sát vẫn có thể giúp tìm token lân cận nhờ ngữ cảnh.

Sau đó mã tạo corrupted, một bản sao độc lập của đầu vào. Với các vị trí được chọn, lần rút thứ hai quyết định phép biến đổi: dưới 0.8, token trở thành dấu mặt nạ; từ 0.8 đến 0.9, nó được thay bằng một token ngẫu nhiên hợp lệ; từ 0.9 trở lên, nó không đổi.

Vì vậy, trong các vị trí được chọn ta có phân bố 80 % mặt nạ, 10 % thay thế ngẫu nhiên, 10 % không đổi. Đây là xác suất, không phải hạn ngạch chính xác áp dụng cho từng lô. Nếu tỷ lệ chọn là 15 %, các tỷ lệ kỳ vọng trên vị trí đủ điều kiện lần lượt là 15 % × 80 % = 12 %, 1,5 % và 1,5 %.

Một chi tiết quan trọng: admissible loại trừ các ID đặc biệt, nhưng không loại trừ ID gốc của mỗi vị trí. Vì vậy lần rút ngẫu nhiên có thể lại chọn chính token ban đầu. Quan sát một đầu vào không đổi là chưa đủ để biết nhánh nào đã được rút.

Theo dõi một dòng mà không tải BERT

Đây là ví dụ sư phạm được tạo thủ công với các ID giả định. Nó không dùng từ vựng BERT thực và không xuất phát từ kết quả r1. Chúng ta chọn ba vị trí để minh họa ba nhánh; không cố tái tạo tần suất của chúng trên một dòng quá ngắn.

Vị trí ID gốc Đủ điều kiện? Được chọn? ID đưa cho mô hình Nhãn Giải thích
0 1, bắt đầu Không Không 1 -100 Dấu đặc biệt
1 7 Có Có 3, mặt nạ 7 Mô hình phải tìm lại 7
2 8 Có Có 12 8 Việc thay thế không đổi đáp án
3 9 Có Có 9 9 Token vẫn hiển thị nhưng được đánh giá
4 10 Có Không 10 -100 Ngữ cảnh hiển thị, không có mục tiêu trực tiếp
5 2, phân cách Không Không 2 -100 Dấu đặc biệt
6 0, phần đệm Không Không 0 -100 Padding

Có ba mục tiêu, nhưng chỉ có một dấu mặt nạ. Vì vậy, chỉ đếm số lần xuất hiện của [MASK] sẽ cho mẫu số sai cho chỉ số. Các mục tiêu ở đây được nhận biết bằng labels != -100.

Các tên gần giống nhau có thể gây nhầm lẫn: eligible chỉ những vị trí có thể được che; chosen là những vị trí được giám sát; còn mặt nạ attention cho biết những vị trí nào mạng có thể dùng làm ngữ cảnh. Hàm được trình bày không trả về mặt nạ attention: nó trả về corrupted, labels và eligible.

Trường hợp cuối cần biết trước khi sửa chương trình: hàm chấp nhận xác suất bằng không và không bắt buộc mỗi lô phải có một mục tiêu. Với probability=0, mọi nhãn đều bằng -100. Việc tính mất mát trung bình phải xử lý rõ một lô không có mục tiêu, thay vì coi đó là học hoàn hảo. Đoạn trích này không cho thấy chính sách huấn luyện cho trường hợp đó; điều này không có nghĩa nó đã xảy ra trong r1.

Cài đặt, kiểm tra và cố định bài kiểm tra

Chuẩn bị môi trường

Giải nén ZIP và đặt terminal của bạn tại thư mục gốc của nó. Mọi lệnh sau đều bắt đầu từ thư mục này. Chúng giữ nguyên thứ tự của quy trình r1 được mô tả trong README.md và lesson/tutorial.json.

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

setup.sh chuẩn bị dự án. docker compose config --quiet kiểm tra cấu hình Compose có thể sử dụng, sau đó build xây dựng ảnh của các dịch vụ. Bước thu thập tải các tài sản theo các khóa; kiểm tra môi trường xác minh CUDA trong container; pytest chạy các kiểm thử của chương trình.

Hãy chờ các bước này kết thúc không lỗi trước khi huấn luyện. Một bộ kiểm thử thành công kiểm tra các cơ chế mà nó bao phủ; nó chưa cho biết việc thích nghi có hữu ích hay không.

Chuẩn bị tài liệu và lưu giữ bài kiểm tra

docker compose run --rm gpu python -m domain_mlm.cli prepare --config configs/minimum.yaml
docker compose run --rm gpu python -m domain_mlm.cli inspect-mask --config configs/minimum.yaml
docker compose run --rm gpu python -m domain_mlm.cli freeze-eval --config configs/minimum.yaml

prepare tạo dữ liệu cho quy trình. inspect-mask cho phép quan sát sự biến đổi của chúng: hãy đối chiếu ID gốc, đầu vào bị làm nhiễu và các nhãn. Đặc biệt, hãy tìm một vị trí bị bỏ qua và một vị trí được chọn; chúng không có cùng vai trò.

Sau đó freeze-eval chuẩn bị bốn tập đánh giá cố định. Trong outputs/eval-masks.json, hãy kiểm tra sự hiện diện của tên chúng và các trường original_ids, input_ids và labels. Số lượng mục tiêu phải khớp với bảng trước.

Việc cố định diễn ra trước các lần huấn luyện mà chúng ta muốn so sánh. Nó ngăn việc âm thầm thay một bài kiểm tra khó bằng bài khác thuận lợi hơn. Khi đó, thay đổi checkpoint vẫn có thể so sánh trên cùng đầu vào, với điều kiện phần còn lại của giao thức cũng được giữ nguyên.

Thích nghi, tiếp tục và đo lường

Giờ chúng ta có thể chạy riêng việc thích nghi, rồi chạy phiên bản có replay. Các lệnh sau cũng kiểm tra việc ngắt và tiếp tục trước khi tạo các số đo cuối cùng.

docker compose run --rm gpu python -m domain_mlm.cli train --config configs/minimum.yaml
docker compose run --rm gpu python -m domain_mlm.cli train --config configs/minimum.yaml --variant replay
docker compose run --rm gpu python -m domain_mlm.cli train --config configs/minimum.yaml --stop-after 150 --run-name interrupted
docker compose run --rm gpu python -m domain_mlm.cli train --config configs/minimum.yaml --resume artifacts/checkpoints/interrupted/state.pt --run-name resumed
docker compose run --rm gpu python -m domain_mlm.cli compare-resume --config configs/minimum.yaml
docker compose run --rm gpu python -m domain_mlm.cli evaluate --config configs/minimum.yaml
docker compose run --rm gpu python -m domain_mlm.cli inspect-cases --config configs/minimum.yaml

Đọc các lệnh như một thí nghiệm

Lệnh train đầu tiên đi theo hướng thích nghi miền. Lệnh thứ hai chọn --variant replay. Theo mô tả r1, biến thể này chỉ thay đổi chương trình các lô: nó đưa các lần ôn lại của tập kiểm soát vào quá trình học. Đây không phải một loại mô hình thứ hai hay một chỉ số mới.

Sau đó, hai biến thể phải được đọc lại so với tham chiếu chưa thích nghi, trên miền và tập kiểm soát. Chỉ xem các tài liệu hướng dẫn sẽ giống như kiểm tra một chức năng mới mà quên các kiểm thử của chức năng hiện có.

Tiếp tục không chỉ là nạp lại trọng số

Lệnh với --stop-after 150 --run-name interrupted tạo điểm dừng đã định của quy trình. Lệnh tiếp theo tiếp tục từ artifacts/checkpoints/interrupted/state.pt dưới tên resumed. Hãy giữ đúng thứ tự: tệp phải tồn tại trước khi được đọc.

Tại sao không chỉ nạp lại mô hình rồi bắt đầu lại? Vì quỹ đạo huấn luyện còn phụ thuộc vào trạng thái của optimizer và các lần rút ngẫu nhiên. RNG, hay bộ sinh số giả ngẫu nhiên, phải tiếp tục ở vị trí đã đạt tới, không nhất thiết từ đầu chuỗi. Tài liệu về torch.Generator phân biệt rõ hạt giống ban đầu với trạng thái đã lưu.

Các bước interrupted, resumed và compare-resume dùng để kiểm tra sự tiếp tục đầy đủ này trong r1. Không nên hiểu chúng là một lần huấn luyện bổ sung nhằm cải thiện điểm số. Hãy xem kết quả compare-resume để xác minh thuộc tính này trong lần chạy của bạn.

Cuối cùng, evaluate tạo các chỉ số, rồi inspect-cases hiển thị các ví dụ. Thứ tự này hữu ích: các trường hợp giải thích một thước đo tổng thể, không thay thế nó. Với một tài liệu hướng dẫn cụ thể, hãy so sánh token kỳ vọng, các dự đoán và xác suất của chúng; đừng chỉ chọn trường hợp thuyết phục nhất.

Các chỉ số trước khi đo lường

NLL: đáp án đúng nhận được bao nhiêu xác suất?

NLL là viết tắt của negative log-likelihood, hay log-likelihood âm. Với một vị trí được đánh giá, nếu mô hình gán xác suất p cho token gốc, đóng góp của nó là:

NLL của vị trí = -ln(p)

ln là logarit tự nhiên. Bạn không cần ghi nhớ các giá trị của nó: hãy nhớ rằng gán xác suất cao hơn cho token kỳ vọng làm giảm hình phạt. Một xác suất rất thấp tốn kém hơn nhiều so với sự do dự vừa phải. Logarit tự nhiên biểu diễn mất mát này bằng nat, không phải phần trăm.

Bảng sau là một ví dụ sư phạm với ba ứng viên, độc lập với BERT và các số đo r1. Ở đây « Khác » đại diện cho một ứng viên thứ ba duy nhất; mỗi dòng cộng lại bằng 1.

Tình huống giả định Xác suất token kỳ vọng Đối thủ chính Khác Top-1 đúng? NLL xấp xỉ
A 0,20 0,75 0,05 Không 1,6094
B 0,40 0,50 0,10 Không 0,9163
C 0,60 0,35 0,05 Có 0,5108
D 0,90 0,07 0,03 Có 0,1054

Giữa A và B, lựa chọn tốt nhất vẫn sai, nhưng xác suất của token kỳ vọng tăng lên: NLL cải thiện. Giữa C và D, lựa chọn tốt nhất vẫn đúng, và NLL vẫn phân biệt hai phân bố. Vì vậy độ chính xác top-1 không kể hết những gì xác suất cho biết.

Trên một corpus, chúng ta cộng các đóng góp rồi chia cho số vị trí được giám sát. Các vị trí khác, nhất là những vị trí có nhãn -100, không được làm loãng giá trị trung bình này.

Hãy lấy một phép tính giả định khác: một khối có một mục tiêu với NLL bằng 2, và khối khác có ba mục tiêu với NLL trung bình 0,5. Trung bình theo token là (2 + 3 × 0,5) / 4 = 0,875. Trung bình không trọng số của hai khối sẽ là (2 + 0,5) / 2 = 1,25, một đại lượng khác. Đơn vị được nêu ở đây là token được đánh giá, không phải tài liệu hay lô.

Độ chính xác top-1: lựa chọn đầu tiên có đúng không?

Với mỗi vị trí được giám sát, chúng ta giữ token có xác suất cao nhất và so sánh ID của nó với ID tham chiếu.

độ chính xác top-1 = số token được dự đoán đúng / số token được đánh giá

Ba dự đoán đúng trên bốn cho 3 / 4 = 0,75, tức 75 %. Điều đó không có nghĩa ba trong bốn tài liệu được hiểu hoàn toàn, cũng không có nghĩa ba trong bốn câu trả lời nghiệp vụ sẽ đúng.

Đáp án vẫn là token gốc. Một token khác nghe hợp lý không nhận điểm chỉ vì con người có thể thấy câu chấp nhận được. Tương tự, dự đoán đúng một mảnh của từ không bảo đảm cả từ hoặc cả câu đều đúng. Những quy ước này làm phép đo chính xác, nhưng cũng giới hạn cách diễn giải nó.

Chỉ báo quên: một chênh lệch có dấu

Trường control_test_forgetting_nll đo:

NLL của biến thể trên control-test - NLL của tham chiếu trên control-test

Giá trị dương nghĩa là NLL tăng: biến thể kém hơn ở bài kiểm soát này. Giá trị âm nghĩa là có cải thiện. Không nghĩa là hai NLL trung bình bằng nhau; điều đó không chứng minh từng dự đoán đều giống hệt.

Ví dụ giả định: nếu tham chiếu là 2,0 và biến thể là 2,6, chỉ báo bằng +0,6. Với biến thể ở 1,7, nó bằng -0,3. Những chênh lệch này không phải phần trăm cũng không phải số lượng kiến thức đã mất.

NLL có thể tăng đáng kể trong khi top-1 thay đổi ít: mô hình có thể giữ nhiều lựa chọn đầu tiên nhưng giảm mạnh xác suất của các mục tiêu ở một số vị trí. Hai chỉ số bổ sung cho nhau.

Thích nghi riêng cải thiện miền nhưng tăng NLL kiểm soát thêm 0,6229; replay duy trì cả hai corpus trong lần tái lập này.

Kết quả thực tế và những gì chúng ta đã học

Các số sau là của lần tái lập r1, trên corpus tổng hợp và các mặt nạ cố định đã mô tả ở trên. Chúng không phải các ví dụ tính toán ở phần trước.

Trạng thái mô hình Top-1 trên domain-test Top-1 trên control-test control_test_forgetting_nll
Tham chiếu chưa thích nghi 0,3850687622789784 0,46228710462287104 0, theo định nghĩa của chênh lệch
Chỉ thích nghi miền 0,9747871643745907 0,45093268450932683 +0,6229252393711704
Thích nghi với replay 0,9754420432220039 0,9772911597729116 -2,7341342250796132

Bảng cho các chênh lệch NLL, không phải các giá trị tuyệt đối của chúng. Không có NLL của tham chiếu, các chênh lệch này không cho phép dựng lại ba mất mát. Hãy giữ sự phân biệt này khi đọc lại đầu ra của evaluate.

Thích nghi riêng tiến bộ trên tài liệu hướng dẫn, nhưng thụt lùi trên tập kiểm soát

Tính theo phần trăm, top-1 miền tăng từ khoảng 38,51 % lên 97,48 %. Trên tập kiểm soát, nó giảm từ khoảng 46,23 % xuống 45,09 %, tức giảm khoảng 1,14 điểm phần trăm. Chỉ báo NLL +0,6229252393711704 cũng mô tả sự suy giảm ở tập kiểm soát, theo một thang đo khác.

Ta có thể làm các tỷ lệ này cụ thể hơn. Nhân các tỷ lệ công bố với số lượng mục tiêu, ta có 1 176 rồi 2 977 token đúng trên 3 054 cho miền, và 570 rồi 556 trên 1 233 cho tập kiểm soát. Đây là các số lượng tính từ các phép đo, không phải suy luận mới.

Kết quả cho thấy đúng sự đánh đổi được tìm kiếm: cải thiện trên miền mới không bảo đảm duy trì hiệu năng ở nơi khác.

Replay cải thiện cả hai điểm số trong thí nghiệm này

Với replay, miền đạt khoảng 97,54 %, tức 2 979 token đúng trên 3 054; tập kiểm soát đạt khoảng 97,73 %, tức 1 205 trên 1 233. Chỉ báo NLL âm cũng cho thấy tập kiểm soát được cải thiện so với tham chiếu.

Trên miền, chênh lệch giữa thích nghi riêng và replay tương ứng với hai token đúng bổ sung, tức khoảng 0,0655 điểm phần trăm. Điều này đặt nhiều chữ số thập phân trong bảng vào đúng bối cảnh: chúng không phải bằng chứng về ưu thế tổng quát hay ý nghĩa thống kê.

Trên tập kiểm soát, replay ở đây làm nhiều hơn việc giữ mức ban đầu: nó cải thiện mạnh. Nhận định này thuộc về corpus tổng hợp này. Ta không thể suy ra rằng mô hình đã giữ mọi năng lực chung, hay rằng nó sẽ trả lời đúng một câu hỏi của người dùng.

Việc cố định mặt nạ không chứng minh điều gì

Các ví dụ giống hệt làm phép so sánh dễ đọc hơn. Tự chúng không chứng minh không có chồng lấp giữa dữ liệu huấn luyện và đánh giá, cũng không chứng minh sự đa dạng của cách diễn đạt.

Mô tả r1 nêu replay của corpus kiểm soát, nhưng hai đoạn trích hiển thị không nêu chi tiết các ID được đưa lại hay tần suất replay. Để kiểm tra ranh giới này, cần đọc cấu hình và chương trình các lô trong ZIP. Khi thích nghi với dữ liệu của bạn, hãy dành riêng các tập kiểm soát khác nhau cho replay và cho đánh giá: huấn luyện trên tập kiểm soát kiểm thử rồi chấm nó sẽ là một giao thức khác.

Điểm này không phải là nhận định về rò rỉ trong r1. Đây là sự phân biệt giữa điều mã tạo mặt nạ nhìn thấy ở đây kiểm tra và điều cần được kiểm tra ở nơi khác trong dự án.

Vì vậy bài học chính không phải là « replay giải quyết quên lãng ». Nó hữu ích hơn: đo đồng thời việc học miền và sự thụt lùi trên một tập kiểm soát, rồi giữ các bằng chứng giúp hiểu chênh lệch giữa chúng.

Bài tập: dự đoán, rồi thực thi

Hãy tưởng tượng hai checkpoint được đánh giá với các mặt nạ rút độc lập. Checkpoint đầu phải tìm các từ thông dụng, checkpoint thứ hai phải tìm các tham chiếu kỹ thuật hiếm. Chỉ một chênh lệch điểm có đủ để quy sự cải thiện hay suy giảm cho mô hình không?

Trước lệnh, hãy ghi câu trả lời của bạn cho câu hỏi này, rồi cho câu hỏi tiếp theo: điểm số có thể thay đổi ngay cả khi chúng ta dùng hai lần chính xác cùng một checkpoint không?

Một phản ví dụ nhỏ được tạo thủ công giúp hình thành giả thuyết. Giả sử một mô hình cố định tìm đúng vị trí A và B, nhưng không tìm đúng C và D. Bài kiểm tra trên A và B cho 100 %. Bài khác trên C và D cho 0 %. Trọng số không đổi: chỉ câu hỏi thay đổi. Đây không phải đầu ra của BERT, mà là minh họa cho bẫy so sánh.

Bây giờ hãy chạy bài tập được cung cấp:

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

Hãy so sánh đầu ra của nó với tập mặt nạ cố định. Lời giải thích mong đợi không phải mọi thay đổi mặt nạ nhất thiết làm đổi điểm: nó cũng có thể không thay đổi gì. Vấn đề là nó đưa vào một nguyên nhân bổ sung mà chỉ riêng điểm số không thể tách khỏi hiệu ứng của checkpoint.

Ngược lại, một bài kiểm tra cố định không được trở thành tập đáp án mà ta liên tục điều chỉnh tham số theo đó. Validation phục vụ các lựa chọn phát triển; test vẫn chỉ dành cho tổng kết cuối cùng. Để nghiên cứu biến thiên của việc tạo mặt nạ trong một thí nghiệm riêng, một khả năng là chuẩn bị trước nhiều bài kiểm tra và cho mỗi bài áp dụng cho mọi checkpoint. Phần mở rộng này không được thực hiện bởi quy trình trình bày.

Kết thúc bằng các hình, kiểm tra checkpoint, xem trước việc dọn dẹp và dừng dự án:

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

--dry-run hiển thị những gì việc dọn dẹp sẽ nhắm tới, nhưng không xóa bất kỳ kết quả nào. Dừng Compose không thay thế việc lưu giữ các JSON và checkpoint cần thiết để đọc lại thí nghiệm của bạn.

Thích nghi, sản xuất, khắc phục sự cố và dọn dẹp

Thay corpus tổng hợp mà không mất phép so sánh

Hãy bắt đầu bằng cách xác định những năng lực mô hình phải tiếp thu và những năng lực nó phải giữ lại. Một corpus kiểm soát hữu ích cho ứng dụng của bạn phải đại diện cho những năng lực sau; nó không tự động là mẫu của mọi điều việc tiền huấn luyện ban đầu đã dạy.

Hãy lập phiên bản cho tài liệu, tokenizer, các tập dữ liệu và mặt nạ. Trong một thí nghiệm mới, cũng cô lập các tài liệu gần nhau hoặc các phiên bản của cùng một tài liệu hướng dẫn để tránh bài kiểm tra chỉ còn là tìm lại một cách diễn đạt gần như đã thấy khi huấn luyện. Khuyến nghị này bổ sung cho giao thức; không có kiểm toán về mức gần nhau của tài liệu được nêu chi tiết trong hai đoạn trích đã trình bày.

Hãy chọn các thiết lập trên validation, đồng thời lưu dấu vết về khoảng thời gian và nguồn của replay. Hãy đặt ngưỡng chấp nhận trên miền và tập kiểm soát trước khi bàn giao. Đừng rút gọn việc so sánh chỉ còn một ví dụ thành công hoặc chỉ NLL miền.

Với văn bản tiếng Pháp hoặc đa ngôn ngữ, đừng cho rằng biến thể tiếng Anh đã khóa đáp ứng cùng một nhu cầu. Thay tokenizer hoặc mô hình tạo ra một thí nghiệm mới: số lượng và độ chi tiết của token có thể thay đổi, vì vậy NLL trên mỗi token không còn so sánh trực tiếp được với r1.

Đi từ dự đoán token đến một ứng dụng

Dự án thích nghi một mô hình ngôn ngữ bị che. Nó không xác thực một hệ thống hỏi đáp, bộ trích xuất hướng dẫn hay quyết định bảo trì. Các trường hợp sử dụng này cần dữ liệu tham chiếu, tiêu chí lỗi và đánh giá riêng bởi chuyên gia miền.

Trên các tài liệu nghiệp vụ, hãy quản lý quyền truy cập, thông tin nhạy cảm và việc lưu giữ hiện vật. Một tài liệu hướng dẫn được hoàn thành đúng trong bài tập không trở thành một quy trình đã được phê duyệt. Trường hợp Velon và 62 độ vẫn là dữ liệu hư cấu, không phải hướng dẫn vận hành.

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

CUDA thất bại. Hãy chạy lại check_environment.py, rồi kiểm tra container có truy cập GPU theo hướng dẫn NVIDIA Container Toolkit hay không. Đừng thay đổi mô hình để giải quyết một vấn đề môi trường.

Bước thu thập thất bại. Hãy khôi phục mạng của dịch vụ acquire, rồi chạy lại lệnh của nó. Giữ nguyên các khóa của mô hình và các phụ thuộc; một bản sửa đổi khác sẽ không còn là cùng một lần tái lập.

Evaluation masks changed xuất hiện. Hãy so sánh dữ liệu đã chuẩn bị, thứ tự của chúng, cấu hình và tokenizer với lần cố định ban đầu. Đừng xóa tệp cũ để kiểm tra được thông qua: bạn sẽ mất cơ sở so sánh. Một thay đổi có chủ ý cần một thí nghiệm riêng và lần cố định mới, không phải thay thế vô hình.

Thiếu checkpoint để tiếp tục. Hãy kiểm tra lệnh interrupted đã hoàn thành và tạo tệp mong đợi trước khi chạy resumed. Việc có một tệp trọng số bất kỳ không thay thế trạng thái đầy đủ mà lệnh này yêu cầu.

Cuối cùng, docker compose down --remove-orphans dừng các dịch vụ của dự án mà không xóa các đầu ra được mô tả ở đây. clean.py --dry-run chỉ thông báo các mục tiêu của nó. Hãy giữ kết quả của bạn trước khi tiến hành một kiểu dọn dẹp khác.

Nguồn chính thức

Các số đo, đường dẫn, lệnh và hai đoạn mã ở trên thuộc về quy trình r1 có thể tải xuống. Các tham chiếu sau dùng để tìm hiểu sâu hơn về cơ chế; chúng không phải nguồn của các điểm số và không thay thế các phiên bản đã khóa.

Chia sẻ bài viết