Transformers documentation

Mô hình hóa ngôn ngữ có mặt nạ

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.2).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Mô hình hóa ngôn ngữ có mặt nạ

Masked language modeling dự đoán một token bị che trong một chuỗi, và mô hình có thể chú ý đến các token theo cả hai chiều. Điều này có nghĩa là mô hình có quyền truy cập đầy đủ vào các token ở bên trái và bên phải. Masked language modeling rất phù hợp cho các tác vụ đòi hỏi khả năng hiểu ngữ cảnh tốt của toàn bộ chuỗi. BERT là một ví dụ về mô hình ngôn ngữ bị che.

Hướng dẫn này sẽ cho bạn thấy cách:

  1. Finetune DistilRoBERTa trên tập con r/askscience của bộ dữ liệu ELI5.
  2. Sử dụng mô hình đã tinh chỉnh của bạn để suy luận.

Để xem tất cả các kiến trúc và checkpoint tương thích với tác vụ này, chúng tôi khuyến nghị bạn kiểm tra trang tác vụ

Trước khi bắt đầu, hãy đảm bảo bạn đã cài đặt tất cả các thư viện cần thiết:

pip install transformers datasets evaluate

Chúng tôi khuyến khích bạn đăng nhập vào tài khoản Hugging Face của mình để có thể tải lên và chia sẻ mô hình của bạn với cộng đồng. Khi được yêu cầu, hãy nhập token của bạn để đăng nhập:

>>> from huggingface_hub import notebook_login

>>> notebook_login()

Tải bộ dữ liệu ELI5

Bắt đầu bằng cách tải 5000 ví dụ đầu tiên từ bộ dữ liệu ELI5-Category bằng thư viện 🤗 Datasets. Điều này sẽ cho bạn cơ hội thử nghiệm và đảm bảo mọi thứ hoạt động trước khi dành thêm thời gian huấn luyện trên toàn bộ bộ dữ liệu.

>>> from datasets import load_dataset

>>> eli5 = load_dataset("dany0407/eli5_category", split="train[:5000]")

Chia split train của bộ dữ liệu thành tập train và tập test bằng phương thức train_test_split:

>>> eli5 = eli5.train_test_split(test_size=0.2)

Sau đó, hãy xem một ví dụ:

>>> eli5["train"][0]
{'q_id': '7h191n',
 'title': 'What does the tax bill that was passed today mean? How will it affect Americans in each tax bracket?',
 'selftext': '',
 'category': 'Economics',
 'subreddit': 'explainlikeimfive',
 'answers': {'a_id': ['dqnds8l', 'dqnd1jl', 'dqng3i1', 'dqnku5x'],
  'text': ["The tax bill is 500 pages long and there were a lot of changes still going on right to the end. It's not just an adjustment to the income tax brackets, it's a whole bunch of changes. As such there is no good answer to your question. The big take aways are: - Big reduction in corporate income tax rate will make large companies very happy. - Pass through rate change will make certain styles of business (law firms, hedge funds) extremely happy - Income tax changes are moderate, and are set to expire (though it's the kind of thing that might just always get re-applied without being made permanent) - People in high tax states (California, New York) lose out, and many of them will end up with their taxes raised.",
   'None yet. It has to be reconciled with a vastly different house bill and then passed again.',
   'Also: does this apply to 2017 taxes? Or does it start with 2018 taxes?',
   'This article explains both the House and senate bills, including the proposed changes to your income taxes based on your income level. URL_0'],
  'score': [21, 19, 5, 3],
  'text_urls': [[],
   [],
   [],
   ['https://www.investopedia.com/news/trumps-tax-reform-what-can-be-done/']]},
 'title_urls': ['url'],
 'selftext_urls': ['url']}

Mặc dù điều này có vẻ như là một khối lượng lớn, bạn thực sự chỉ quan tâm đến trường text. Điều thú vị về các tác vụ mô hình hóa ngôn ngữ là bạn không cần nhãn (còn được gọi là tác vụ không giám sát) vì từ tiếp theo chính là nhãn.

Tiền xử lý

Đối với mô hình hóa ngôn ngữ có che, bước tiếp theo là nạp một tokenizer DistilRoBERTa để xử lý trường con text:

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilroberta-base")

Bạn sẽ nhận thấy từ ví dụ ở trên, trường text thực sự nằm lồng bên trong answers. Điều này có nghĩa là bạn sẽ cần trích xuất trường con text khỏi cấu trúc lồng nhau bằng phương thức flatten:

>>> eli5 = eli5.flatten()
>>> eli5["train"][0]
{'q_id': '7h191n',
 'title': 'What does the tax bill that was passed today mean? How will it affect Americans in each tax bracket?',
 'selftext': '',
 'category': 'Economics',
 'subreddit': 'explainlikeimfive',
 'answers.a_id': ['dqnds8l', 'dqnd1jl', 'dqng3i1', 'dqnku5x'],
 'answers.text': ["The tax bill is 500 pages long and there were a lot of changes still going on right to the end. It's not just an adjustment to the income tax brackets, it's a whole bunch of changes. As such there is no good answer to your question. The big take aways are: - Big reduction in corporate income tax rate will make large companies very happy. - Pass through rate change will make certain styles of business (law firms, hedge funds) extremely happy - Income tax changes are moderate, and are set to expire (though it's the kind of thing that might just always get re-applied without being made permanent) - People in high tax states (California, New York) lose out, and many of them will end up with their taxes raised.",
  'None yet. It has to be reconciled with a vastly different house bill and then passed again.',
  'Also: does this apply to 2017 taxes? Or does it start with 2018 taxes?',
  'This article explains both the House and senate bills, including the proposed changes to your income taxes based on your income level. URL_0'],
 'answers.score': [21, 19, 5, 3],
 'answers.text_urls': [[],
  [],
  [],
  ['https://www.investopedia.com/news/trumps-tax-reform-what-can-be-done/']],
 'title_urls': ['url'],
 'selftext_urls': ['url']}

Mỗi trường con giờ là một cột riêng biệt như được chỉ ra bởi tiền tố answers, và trường text giờ là một danh sách. Thay vì tách token từng câu riêng lẻ, hãy chuyển danh sách thành một chuỗi để bạn có thể tách token chúng cùng nhau.

Dưới đây là một hàm tiền xử lý đầu tiên để ghép danh sách các chuỗi cho mỗi ví dụ và thực hiện token hóa kết quả:

>>> def preprocess_function(examples):
...     return tokenizer([" ".join(x) for x in examples["answers.text"]])

Để áp dụng hàm tiền xử lý này cho toàn bộ tập dữ liệu, hãy sử dụng phương thức map của 🤗 Datasets. Bạn có thể tăng tốc hàm map bằng cách đặt batched=True để xử lý nhiều phần tử của tập dữ liệu cùng lúc, và tăng số lượng tiến trình bằng num_proc. Xóa bất kỳ cột nào bạn không cần:

>>> tokenized_eli5 = eli5.map(
...     preprocess_function,
...     batched=True,
...     num_proc=4,
...     remove_columns=eli5["train"].column_names,
... )

Bộ dữ liệu này chứa các chuỗi token, nhưng một số chuỗi này dài hơn độ dài đầu vào tối đa của mô hình.

Bạn có thể sử dụng một hàm tiền xử lý thứ hai để

  • kết nối tất cả các chuỗi
  • chia các chuỗi đã nối thành các đoạn ngắn hơn được xác định bởi block_size, trong đó block_size phải ngắn hơn chiều dài đầu vào tối đa và đủ ngắn để phù hợp với bộ nhớ GPU của bạn.
>>> block_size = 128


>>> def group_texts(examples):
...     # Concatenate all texts.
...     concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()}
...     total_length = len(concatenated_examples[list(examples.keys())[0]])
...     # We drop the small remainder, we could add padding if the model supported it instead of this drop, you can
...     # customize this part to your needs.
...     if total_length >= block_size:
...         total_length = (total_length // block_size) * block_size
...     # Split by chunks of block_size.
...     result = {
...         k: [t[i : i + block_size] for i in range(0, total_length, block_size)]
...         for k, t in concatenated_examples.items()
...     }
...     return result

Áp dụng hàm group_texts trên toàn bộ tập dữ liệu:

>>> lm_dataset = tokenized_eli5.map(group_texts, batched=True, num_proc=4)

Bây giờ hãy tạo một batch các ví dụ bằng cách sử dụng DataCollatorForLanguageModeling. Việc đệm động các câu đến độ dài dài nhất trong một batch trong quá trình collation sẽ hiệu quả hơn so với việc đệm toàn bộ dataset đến độ dài tối đa.

Sử dụng token kết thúc chuỗi làm token đệm và chỉ định mlm_probability để che ngẫu nhiên các token mỗi khi bạn lặp lại trên dữ liệu:

>>> from transformers import DataCollatorForLanguageModeling

>>> tokenizer.pad_token = tokenizer.eos_token
>>> data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm_probability=0.15)

Huấn luyện

Nếu bạn chưa quen với việc tinh chỉnh mô hình bằng Trainer, hãy xem hướng dẫn cơ bản tại đây!

Bạn đã sẵn sàng để bắt đầu huấn luyện mô hình của mình! Nạp DistilRoBERTa bằng AutoModelForMaskedLM:

>>> from transformers import AutoModelForMaskedLM

>>> model = AutoModelForMaskedLM.from_pretrained("distilbert/distilroberta-base")

Tới bước này, chỉ còn ba bước nữa:

  1. Định nghĩa các siêu tham số huấn luyện của bạn trong TrainingArguments. Tham số bắt buộc duy nhất là output_dir, chỉ định nơi lưu trữ mô hình của bạn. Bạn sẽ đẩy mô hình này lên Hub bằng cách đặt push_to_hub=True (bạn cần đăng nhập vào Hugging Face để tải lên mô hình của mình).
  2. Truyền các đối số huấn luyện vào Trainer cùng với mô hình, các bộ dữ liệu và bộ tổng hợp dữ liệu.
  3. Gọi train() để tinh chỉnh mô hình của bạn.
>>> training_args = TrainingArguments(
...     output_dir="my_awesome_eli5_mlm_model",
...     eval_strategy="epoch",
...     learning_rate=2e-5,
...     num_train_epochs=3,
...     weight_decay=0.01,
...     push_to_hub=True,
... )

>>> trainer = Trainer(
...     model=model,
...     args=training_args,
...     train_dataset=lm_dataset["train"],
...     eval_dataset=lm_dataset["test"],
...     data_collator=data_collator,
...     processing_class=tokenizer,
... )

>>> trainer.train()

Sau khi quá trình huấn luyện hoàn tất, hãy sử dụng phương thức evaluate() để đánh giá mô hình của bạn và lấy độ perplexity của nó:

>>> import math

>>> eval_results = trainer.evaluate()
>>> print(f"Perplexity: {math.exp(eval_results['eval_loss']):.2f}")
Perplexity: 8.76

Sau đó, chia sẻ mô hình của bạn lên Hub bằng phương thức push_to_hub() để mọi người có thể sử dụng mô hình của bạn:

>>> trainer.push_to_hub()

Để xem một ví dụ chi tiết hơn về cách tinh chỉnh một mô hình cho mô hình hóa ngôn ngữ có mặt nạ, hãy xem notebook PyTorch tương ứng.

Suy luận

Tuyệt, giờ đây bạn đã tinh chỉnh một mô hình, bạn có thể sử dụng nó để suy luận!

Hãy nghĩ ra một số văn bản mà bạn muốn mô hình điền vào chỗ trống, và sử dụng token đặc biệt <mask> để chỉ ra chỗ trống:

>>> text = "The Milky Way is a <mask> galaxy."

Cách đơn giản nhất để thử nghiệm mô hình đã tinh chỉnh của bạn cho suy luận là sử dụng nó trong một pipeline(). Khởi tạo một pipeline cho fill-mask với mô hình của bạn, và truyền văn bản của bạn vào đó. Nếu muốn, bạn có thể sử dụng tham số top_k để chỉ định số lượng dự đoán cần trả về:

>>> from transformers import pipeline

>>> mask_filler = pipeline("fill-mask", "username/my_awesome_eli5_mlm_model")
>>> mask_filler(text, top_k=3)
[{'score': 0.5150994658470154,
  'token': 21300,
  'token_str': ' spiral',
  'sequence': 'The Milky Way is a spiral galaxy.'},
 {'score': 0.07087188959121704,
  'token': 2232,
  'token_str': ' massive',
  'sequence': 'The Milky Way is a massive galaxy.'},
 {'score': 0.06434620916843414,
  'token': 650,
  'token_str': ' small',
  'sequence': 'The Milky Way is a small galaxy.'}]

Thực hiện tokenize văn bản và trả về input_ids dưới dạng tensor PyTorch. Bạn cũng cần chỉ định vị trí của token <mask>:

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("username/my_awesome_eli5_mlm_model")
>>> inputs = tokenizer(text, return_tensors="pt")
>>> mask_token_index = torch.where(inputs["input_ids"] == tokenizer.mask_token_id)[1]

Truyền các đầu vào của bạn vào mô hình và trả về logits của token bị che:

>>> from transformers import AutoModelForMaskedLM

>>> model = AutoModelForMaskedLM.from_pretrained("username/my_awesome_eli5_mlm_model")
>>> logits = model(**inputs).logits
>>> mask_token_logits = logits[0, mask_token_index, :]

Sau đó, trả về ba token bị che có xác suất cao nhất và in chúng ra:

>>> top_3_tokens = torch.topk(mask_token_logits, 3, dim=1).indices[0].tolist()

>>> for token in top_3_tokens:
...     print(text.replace(tokenizer.mask_token, tokenizer.decode([token])))
The Milky Way is a spiral galaxy.
The Milky Way is a massive galaxy.
The Milky Way is a small galaxy.
Update on GitHub