# Course

## Docs

- [Đố vui cuối chương](https://huggingface.co/learn/course/vi/chapter7/9.md)
- [Tóm tắt](https://huggingface.co/learn/course/vi/chapter7/5.md)
- [Giới thiệu](https://huggingface.co/learn/course/vi/chapter7/1.md)
- [Tinh chỉnh một mô hình ngôn ngữ bị ẩn đi](https://huggingface.co/learn/course/vi/chapter7/3.md)
- [THuấn luyện một mô hình ngôn ngữ nhân quả từ đầu](https://huggingface.co/learn/course/vi/chapter7/6.md)
- [Làm chủ NLP](https://huggingface.co/learn/course/vi/chapter7/8.md)
- [Dịch máy](https://huggingface.co/learn/course/vi/chapter7/4.md)
- [Hỏi đáp](https://huggingface.co/learn/course/vi/chapter7/7.md)
- [Phân loại token](https://huggingface.co/learn/course/vi/chapter7/2.md)
- [Hoàn thành phần 1!](https://huggingface.co/learn/course/vi/chapter4/5.md)
- [Hugging Face Hub](https://huggingface.co/learn/course/vi/chapter4/1.md)
- [Chia sẻ các mô hình huấn luyện trước](https://huggingface.co/learn/course/vi/chapter4/3.md)
- [Đố vui cuối chương](https://huggingface.co/learn/course/vi/chapter4/6.md)
- [Xây dựng các thẻ mô hình](https://huggingface.co/learn/course/vi/chapter4/4.md)
- [Sử dụng các mô hình huấn luyện trước](https://huggingface.co/learn/course/vi/chapter4/2.md)
- [Tỉnh chỉnh, thử xem!](https://huggingface.co/learn/course/vi/chapter3/5.md)
- [Giới thiệu](https://huggingface.co/learn/course/vi/chapter3/1.md)
- [Tinh chỉnh một mô hình với Trainer API](https://huggingface.co/learn/course/vi/chapter3/3.md)
- [Đố vui cuối chương](https://huggingface.co/learn/course/vi/chapter3/6.md)
- [Tinh chỉnh một mô hình với Keras](https://huggingface.co/learn/course/vi/chapter3/3_tf.md)
- [Bản huấn luyện hoàn chỉnh](https://huggingface.co/learn/course/vi/chapter3/4.md)
- [Xử lý dữ liệu](https://huggingface.co/learn/course/vi/chapter3/2.md)
- [Làm thế nào để viết một vấn đề hay](https://huggingface.co/learn/course/vi/chapter8/5.md)
- [Giới thiệu](https://huggingface.co/learn/course/vi/chapter8/1.md)
- [Yêu cầu trợ giúp trên diễn đàn](https://huggingface.co/learn/course/vi/chapter8/3.md)
- [Phần 2 đã hoàn thành!](https://huggingface.co/learn/course/vi/chapter8/6.md)
- [Gỡ lỗi quy trình huấn luyện](https://huggingface.co/learn/course/vi/chapter8/4.md)
- [Đố vui cuối chương](https://huggingface.co/learn/course/vi/chapter8/7.md)
- [Gỡ lỗi quy trình huấn luyện](https://huggingface.co/learn/course/vi/chapter8/4_tf.md)
- [Phải làm gì khi bạn gặp lỗi](https://huggingface.co/learn/course/vi/chapter8/2.md)
- [Đố vui cuối chương](https://huggingface.co/learn/course/vi/chapter9/9.md)
- [Tích hợp với Hugging Face Hub](https://huggingface.co/learn/course/vi/chapter9/5.md)
- [Giới thiệu](https://huggingface.co/learn/course/vi/chapter9/1.md)
- [Hiểu lớp Interface](https://huggingface.co/learn/course/vi/chapter9/3.md)
- [Các tính năng nâng cao của Interface](https://huggingface.co/learn/course/vi/chapter9/6.md)
- [Gradio, kiểm tra nào!](https://huggingface.co/learn/course/vi/chapter9/8.md)
- [Chia sẻ các bản demo với người khác](https://huggingface.co/learn/course/vi/chapter9/4.md)
- [Giới thiệu về Gradio Blocks](https://huggingface.co/learn/course/vi/chapter9/7.md)
- [Xây dựng bản demo đầu tiên của bạn](https://huggingface.co/learn/course/vi/chapter9/2.md)
- [Sự kiện Phát hành Phần 2](https://huggingface.co/learn/course/vi/events/2.md)
- [Tokenizers, kiểm tra nào!](https://huggingface.co/learn/course/vi/chapter6/9.md)
- [Byte-Pair Encoding tokenization](https://huggingface.co/learn/course/vi/chapter6/5.md)
- [Giới thiệu](https://huggingface.co/learn/course/vi/chapter6/1.md)
- [Sức mạnh đặc biệt của tokenizer nhanh](https://huggingface.co/learn/course/vi/chapter6/3.md)
- [WordPiece tokenization](https://huggingface.co/learn/course/vi/chapter6/6.md)
- [Đố vui cuối chương](https://huggingface.co/learn/course/vi/chapter6/10.md)
- [Xây dựng từng khối tokenizer](https://huggingface.co/learn/course/vi/chapter6/8.md)
- [Chuẩn hoá và tiền tokenize](https://huggingface.co/learn/course/vi/chapter6/4.md)
- [Unigram tokenization](https://huggingface.co/learn/course/vi/chapter6/7.md)
- [Fast tokenizers in the QA pipeline](https://huggingface.co/learn/course/vi/chapter6/3b.md)
- [Huấn luyện một tokenizer mới từ cái cũ](https://huggingface.co/learn/course/vi/chapter6/2.md)
- [Tạo tập dữ liệu của riêng bạn](https://huggingface.co/learn/course/vi/chapter5/5.md)
- [Giới thiệu](https://huggingface.co/learn/course/vi/chapter5/1.md)
- [Sắp xếp dữ liệu](https://huggingface.co/learn/course/vi/chapter5/3.md)
- [Tìm kiếm ngữ nghĩa với FAISS](https://huggingface.co/learn/course/vi/chapter5/6.md)
- [Đố vui cuối chương](https://huggingface.co/learn/course/vi/chapter5/8.md)
- [Dữ liệu lớn? 🤗 Bộ dữ liệu để giải cứu!](https://huggingface.co/learn/course/vi/chapter5/4.md)
- [🤗 Datasets, kiểm tra nào!](https://huggingface.co/learn/course/vi/chapter5/7.md)
- [Nếu như dữ liệu của ta không trên Hub thì sao?](https://huggingface.co/learn/course/vi/chapter5/2.md)
- [Xử lý đa chuỗi](https://huggingface.co/learn/course/vi/chapter2/5.md)
- [Giới thiệu](https://huggingface.co/learn/course/vi/chapter2/1.md)
- [Các mô hình](https://huggingface.co/learn/course/vi/chapter2/3.md)
- [Kết hợp lại](https://huggingface.co/learn/course/vi/chapter2/6.md)
- [Đố vui cuối chương](https://huggingface.co/learn/course/vi/chapter2/8.md)
- [Tokenizers](https://huggingface.co/learn/course/vi/chapter2/4.md)
- [Hoàn thành cách sử dụng cơ bản!](https://huggingface.co/learn/course/vi/chapter2/7.md)
- [Đằng sau pipeline](https://huggingface.co/learn/course/vi/chapter2/2.md)
- [Giới thiệu](https://huggingface.co/learn/course/vi/chapter0/1.md)
- [Tổng kết](https://huggingface.co/learn/course/vi/chapter1/9.md)
- [Các mô hình mã hóa](https://huggingface.co/learn/course/vi/chapter1/5.md)
- [Giới thiệu](https://huggingface.co/learn/course/vi/chapter1/1.md)
- [Transformers có thể làm những gì?](https://huggingface.co/learn/course/vi/chapter1/3.md)
- [CCác mô hình giải mã](https://huggingface.co/learn/course/vi/chapter1/6.md)
- [Đố vui cuối chương](https://huggingface.co/learn/course/vi/chapter1/10.md)
- [Thiên kiến và hạn chế](https://huggingface.co/learn/course/vi/chapter1/8.md)
- [Cơ chế hoạt động của Transformer?](https://huggingface.co/learn/course/vi/chapter1/4.md)
- [Các mô hình mã hoá-giải mã](https://huggingface.co/learn/course/vi/chapter1/7.md)
- [Xử lý Ngôn Ngữ Tự nhiên](https://huggingface.co/learn/course/vi/chapter1/2.md)

### Đố vui cuối chương
https://huggingface.co/learn/course/vi/chapter7/9.md

# Đố vui cuối chương

Cùng kiểm tra xem bạn đã học được những gì trong chương này!

### 1. Tác vụ nào sau đây có thể được coi là vấn đề phân loại token?

<Question
	choices={[
		{
			text: "Tìm các thành phần ngữ pháp trong một câu.",
			explain: "Đúng! Sau đó, chúng tôi có thể gắn nhãn mỗi từ là danh từ, động từ, v.v.",
			correct: true
		},
		{
			text: "Tìm xem một câu có đúng ngữ pháp hay không.",
			explain: "Không, đây là một bài toán phân loại chuỗi."
		},
		{
			text: "Tìm thực thể người được đề cập trong một câu.",
			explain: "Đúng! Chúng ta có thể gắn nhãn từng từ có phải là thực thể người không.",
            correct: true
		},
        {
			text: "Tìm đoạn từ trong một câu trả lời cho một câu hỏi.",
			explain: "Không, đó sẽ là một vấn đề hỏi đáp."
		}
	]}
/>

### 2. Phần tiền xử lý để phân loại token khác với các pipeline tiền xử lý khác ở điểm nào?

<Question
	choices={[
		{
			text: "Không cần phải làm gì cả; các văn bản đã được tokenize",
			explain: "Các văn bản thực sự được cung cấp dưới dạng các từ riêng biệt, nhưng chúng ta vẫn cần áp dụng mô hình tokenize từ phụ."
		},
		{
			text: "Các văn bản được cung cấp dưới dạng từ, vì vậy chúng ta chỉ cần áp dụng tokenize từ phụ.",
			explain: "Chính xác! Điều này khác với quy trình tiền xử lý thông thường, ở đó chúng ta cần áp dụng quy trình tokenize đầy đủ. Bạn có thể nghĩ xem còn sự khác biệt nào khác không?",
			correct: true
		},
		{
			text: "Ta sử dụng <code>-100</code> để đánh nhãn các token đặc biệt.",
			explain: "Điều đó không dành riêng cho việc phân loại token -- ta luôn sử dụng <code>-100</code> như nhãn của token ta muốn bỏ quả trong hàm mất mát."
		},
		{
			text: "Chúng ta cần đảm bảo cắt bớt hoặc đệm các nhãn có cùng kích thước với các đầu vào khi áp dụng phép cắt bớt/đệm.",
			explain: "Thật vậy! Tuy nhiên đó không phải là sự khác biệt duy nhất.",
			correct: true
		}
	]}
/>

### 3. Vấn đề gì phát sinh khi ta tokenize các từ trong bài toán phân loại token và muốn đánh nhãn token?

<Question
	choices={[
		{
			text: "Trình tokenizer thêm các kí tự token đặc biệt và ta không có nhãn cho chúng.",
			explain: "Ta đãnh nhãn <code>-100</code> cho chúng để chúng bị bỏ qua khi tính sự mất mát."
		},
		{
			text: "Mỗi từ có thể tạo ra nhiều token, nên đến cuối ta sẽ có nhiều token hơn số nhãn.",
			explain: "Đó là vấn đề chính và chúng ta cần phải căn chỉnh các nhãn gốc với các token.",
			correct: true
		},
		{
			text: "Các token được thêm không có nhãn, nên không có vấn đề gì.",
			explain: "Không chính xác; ta cần số nhãn tương ứng số token nếu không mô hình sẽ báo lỗi."
		}
	]}
/>

### 4. "Thích ứng chuyên môn" là gì?

<Question
	choices={[
		{
			text: "Đó là khi chúng ta chạy một mô hình trên tập dữ liệu và nhận các dự đoán cho từng mẫu trong tập dữ liệu đó.",
			explain: "Không, đây chỉ là chạy quá trình luận suy."
		},
		{
			text: "Đó là khi chúng ta huấn luyện một mô hình trên tập dữ liệu.",
			explain: "Không, đây là huấn luyện một mô hình; không có sự thích nghi ở đây."
		},
		{
			text: "Đó là khi chúng ta tinh chỉnh một mô hình được huấn luyện trước trên một tập dữ liệu mới và nó đưa ra các dự đoán phù hợp hơn với tập dữ liệu đó",
			explain: "Đúng! Mô hình đã điều chỉnh kiến ​​thức của nó để phù hợp với tập dữ liệu mới.",
            correct: true
		},
        {
			text: "Đó là khi chúng ta thêm các mẫu bị phân loại sai vào tập dữ liệu để làm cho mô hình mạnh mẽ hơn.",
			explain: "Đó chắc chắn là điều bạn nên làm nếu huấn luyện lại mô hình của mình thường xuyên, nhưng đó không phải là thích ứng chuyên môn."
		}
	]}
/>

### 5. Các nhãn trong bài toán mô hình ngôn ngữ bị ẩn đi là gì?

<Question
	choices={[
		{
			text: "Một số token đầu vào bị che ngẫu nhiên và nhãn là các token đầu vào ban đầu.",
			explain: "Chính nó!",
            correct: true
		},
		{
			text: "Một số token đầu vào bị che ngẫu nhiên và nhãn là các token đầu vào ban đầu, được dịch sang bên trái.",
			explain: "Không, dịch các nhãn sang bên trái tương ứng với dự đoán từ tiếp theo hay mô hình ngôn ngữ nhân quả."
		},
		{
			text: "Một số token đầu vào bị che ngẫu nhiên và nhãn là các câu phủ định hay khẳng định.",
			explain: "Đó là vấn đề phân loại chuỗi với tăng cường dữ liệu, không phải mô hình ngôn ngữ bị che đi."
		},
        {
			text: "Một số token trong hai câu đầu vào bị che đi ngẫu nhiên, và nhãn là hai câu đó có giống nhau hay không.",
			explain: "Đó là vấn đề phân loại chuỗi với tăng cường dữ liệu, không phải mô hình ngôn ngữ bị che đi."
		}
	]}
/>

### 6. Tác vụ nào sau đây có thể được coi là bài toán chuỗi sang chuỗi?

<Question
	choices={[
		{
			text: "Viết đánh giá ngắn về tài liệu dài",
			explain: "Đúng, đó là một bài toán tóm tắt. Hãy thử một câu trả lời khác!",
            correct: true
		},
		{
			text: "Trả lời câu hỏi về một tài liệu",
			explain: "Đây có thể được coi là một bài toán chuỗi sang chuỗi. Tuy nhiên, đây không phải là câu trả lời đúng duy nhất.",
            correct: true
		},
		{
			text: "Dịch một văn bản bằng tiếng Trung sang tiếng Anh",
			explain: "Đó chắc chắn là bài toán chuỗi sang chuỗi. Bạn có thể tìm thấy đáp án nào nữa không?",
            correct: true
		},
        {
			text: "Sửa các tin nhắn do cháu trai/ bạn tôi gửi để chúng được viết bằng tiếng Anh chuẩn",
			explain: "Đó là một dạng dịch máy, nên chắc chắn là bài toán chuỗi sang chuỗi. Tuy nhiên, đó không phải là đáp án đúng duy nhất!",
			correct: true
		}
	]}
/>

### 7. Đây là phuwong pháp phù hợp để tiền xử lý dữ liệu cho bài toán chuỗi sang chuỗi?

<Question
	choices={[
		{
			text: "Đầu vào và nhãn phải được gửi cùng nhau tới tokenizer qua <code>inputs=...</code> và <code>targets=...</code>.",
			explain: "Đây có thể là một API mà chúng tôi sẽ thêm vào trong tương lai, nhưng điều đó không khả thi ở thời điểm hiện tại."
		},
		{
			text: "Đầu vào và nhãn đều phải được tiền xử lý, trong hai lệch gọi riêng biệt tới tokenizer.",
			explain: "Điều đó đúng, nhưng chưa đủ. Bạn cần phải làm gì đó nữa để đảm bảo trình tokenizer xử lý đúng cả hai."
		},
		{
			text: "Như thường lệ, chúng ta chỉ phhải tokenize đầu vào.",
			explain: "Không phải với bài toán phân loại chuỗi; nhãn cũng là văn bản nên ta cần chuyển sang dạng số!"
		},
        {
			text: "Đầu vào phải đước gửi tới trình tokenizer, và nhãn cũng vậy, nhưng theo trình quản lý ngữ cảnh đặc biệt.",
			explain: "Đúng vậy, tokenizer cần xử lý nhãn dựa trên trình quản lý ngữ cảnh.",
			correct: true
		}
	]}
/>

{#if fw === 'pt'}

### 8. Vì sao lại có lớp con `Trainer` cho các bài toán chuỗi sang chuỗi?

<Question
	choices={[
		{
			text: "Vì các vấn đề chuỗi sang chuỗi sử dụng một hàm mất mát tuỳ chỉnh để bỏ qua tập hợp nhãn <code>-100</code>",
			explain: "Đây không phải là tuỳ chỉnh mất mát, mà là cách sự mất mát vẫn luôn được tính toán."
		},
		{
			text: "Vì các vấn đề chuỗi sang chuỗi cần một vòng đánh giá đặc biệt",
			explain: "Chính xác. Các dự đoán mô hình chuỗi sang chuỗi thường được chạy sử dụng phương thức <code>generate()</code>.",
			correct: true
		},
		{
			text: "Bởi vì nhãn là văn bản trong bài toán chuỗi sang chuỗi",
			explain: "<code>Trainer</code> không thực sự quan tâm vì chúng đã được tiền xử lý trước đó."
		},
        {
			text: "Vì ta sử dụng hai mô hình trong bài toán chuỗi sang chuỗi",
			explain: "Chúng ta sử dụng hai mô hình cùng một cách, một trình mã hoá và một trình giải mã, nhưng ta sẽ nhóm chúng lại trong một mô hình."
		}
	]}
/>

{:else}

### 9. Vì sao không cần thiết chỉ định hàm mất mát khi gọi `compile()` trong mô hình Transformer?

<Question
	choices={[
		{
			text: "Vì các mô hình Transformer models được huấn luyện phi giám sát",
			explain: "Không hẳn -- kể cả học phi giám sát cùng cần hàm mất mát!"
		},
		{
			text: "Vì mất mát bên trong của đầu ra được sử dụng theo mặc định",
			explain: "Chính xác!",
			correct: true
		},
		{
			text: "Vì thay vào đó chúng ta tính các thước đo sau khi huấn luyện",
			explain: "Ta thường làm vậy, nhưng nó không giải thích cho việc nhận giá trị mất mát khi ta tối ưu trong khi huấn luyện."
		},
        {
			text: "Vì sự mất mát được chỉ định cụ thể trong `model.fit()`",
			explain: "Không, hàm mất mát luôn cố định khi bạn chạy `model.compile()`, và không thể thay đổi trong `model.fit()`."
		}
	]}
/>

{/if}

### 10. Khi nào bạn nên huấn luyện trước một mô hình mới?

<Question
	choices={[
		{
			text: "Khi không có mô hình huấn luyện sẵn cho ngôn ngữ của bạn",
			explain: "Chính xác.",
			correct: true
		},
		{
			text: "Khi bạn có rất nhiều dữ liệu có sẵn, ngay cả khi đã có một mô hình huấn luyện trước có thể hoạt động trên đó",
			explain: "Trong trường hợp này, bạn nên sử dụng mô hình đã được huấn luyện sẵn và tinh chỉnh theo dữ liệu của bạn, tránh chi phí tính toán lớn."
		},
		{
			text: "Khi bạn lăn tăn về sự sai lệch của mô hình huấn luyện trước bạn sử dụng",
			explain: "Đúng, nhưng bạn phải đảm bảo rằng dữ liệu bạn sử dụng cho huấn luyện sẽ luôn tốt hơn.",
			correct: true
		},
        {
			text: "Khi các mô hình huấn luyện trước có sẵn không đủ tốt",
			explain: "Bạn có chắc là bạn đã gỡ lỗi đúng cách cho quá trình huấn luyện của mình không?"
		}
	]}
/>

### 11. Vì sao ta dễ huấn luyện trước một mô hình ngôn ngữ khi có khối lượng văn bản khổng lồ?

<Question
	choices={[
		{
			text: "Vì có rất nhiều dữ liệu văn bản có sẵn trên mạng",
			explain: "Dù đúng, nhưng nó không thực sự là đáp án cho câu hỏi. Hãy thử lại!"
		},
		{
			text: "Vì mục đích huấn luyện trước không cần con người gán nhãn dữ liệu the pretraining objective does not require humans to label the data",
			explain: "Chính xác, mô hình ngôn ngữ là bài toán tự giám sát.",
			correct: true
		},
		{
			text: "Vì thư viện 🤗 Transformers chỉ yêu cầu một vài dòng mã để bắt đầu huấn luyện",
			explain: "Dù đúng, nhưng nó không thực sự là đáp án cho câu hỏi. Hãy thử đáp án khác"
		}
	]}
/>

### 12. Đâu là những thách thức chính khi tiền xử lí dữ liệu cho tác vụ hỏi đáp?

<Question
	choices={[
		{
			text: "Bạn cần tokenize đầu vào.",
			explain: "Chính xác, nhưng đó có thực sự là một thách thức chính?"
		},
		{
			text: "Bạn cần xử lý chuỗi ngữ cảnh dài nhận nhiều đặc trưng huấn luyện có thể chứa đáp án bên trong hoặc không.",
			explain: "Đây chắc chắn là một trong những thách thức.",
			correct: true
		},
		{
			text: "Bạn cần tokenize câu trả lời của câu hỏi cũng như đầu vào.",
			explain: "Không, trừ khi bạn đang coi vấn đề hỏi đáp là một tác vụ chuỗi sang chuỗi."
		},
       {
			text: "Từ mảng câu trả lời trong văn bản, bạn phải tìm ra token bắt đầu và kết thúc trong đầu vào đã được tokenize.",
			explain: "Đây chính là một trong những phần khó nhất, chính xác!",
			correct: true
		}
	]}
/>

### 13. Làm thể nào để hậu xử lý trong bài toán hỏi đáp?

<Question
	choices={[
		{
			text: "Mô hình cung cấp cho bạn vị trí bắt đầu và kết thúc của câu trả lời và bạn chỉ cần giải mã khoảng token tương ứng..",
			explain: "Đó có thể là một cách để làm điều đó, nhưng nó hơi quá đơn giản."
		},
		{
			text: "Mô hình cung cấp cho bạn vị trí bắt đầu và kết thúc của câu trả lời cho mỗi đặc trưng được tạo bởi một mẫu và bạn chỉ cần giải mã khoảng token tương ứng của cái có điểm tốt nhất.",
			explain: "Nó gần giống quá trình hậu xử lý chúng ta đã học, nhưng nó không hoàn toàn đúng."
		},
		{
			text: "Mô hình cung cấp cho bạn vị trí bắt đầu và kết thúc của câu trả lời cho mỗi đặc trưng được tạo bởi một mẫu và bạn chỉ cần đối sánh chúng với khoảng trong ngữ cảnh của cái có điểm tốt nhất.",
			explain: "Chính nó!",
			correct: true
		},
        {
			text: "Mô hình tạo ra một câu trả lời và bạn chỉ cần giải mã nó.",
			explain: "Không, trừ khi bạn đang coi vấn đề hỏi đáp là một tác vụ chuỗi sang chuỗi."
		}
	]}
/>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter7/9.mdx" />

### Tóm tắt
https://huggingface.co/learn/course/vi/chapter7/5.md

# Tóm tắt

{#if fw === 'pt'}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section5_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section5_pt.ipynb"},
]} />

{:else}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section5_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section5_tf.ipynb"},
]} />

{/if}

Trong phần này, chúng ta sẽ xem xét cách các mô hình Transformer có thể được sử dụng để cô đọng các tài liệu dài thành các bản tóm tắt, một tác vụ được gọi là _text summarization_ hay _tóm tắt văn bản_. Đây là một trong những tác vụ NLP thách thức nhất vì nó đòi hỏi nhiều khả năng, chẳng hạn như hiểu các đoạn văn dài và tạo ra văn bản mạch lạc nắm bắt các chủ đề chính trong tài liệu. Tuy nhiên, khi được thực hiện tốt, tóm tắt văn bản là một công cụ mạnh mẽ có thể tăng tốc các quy trình kinh doanh khác nhau bằng cách giảm bớt gánh nặng cho các chuyên gia miền phải đọc chi tiết các tài liệu dài.

<Youtube id="yHnr5Dk2zCI"/>

Mặc dù đã tồn tại nhiều mô hình được tinh chỉnh khác nhau để tóm tắt trên [Hugging Face Hub](https://huggingface.co/models?pipeline_tag=summarization&sort=downloads), hầu hết tất cả các mô hình này chỉ phù hợp với các tài liệu tiếng Anh. Vì vậy, để tạo thêm một điểm nhấn trong phần này, chúng tôi sẽ huấn luyện một mô hình song ngữ cho tiếng Anh và tiếng Tây Ban Nha. Đến cuối phần này, bạn sẽ có một [mô hình](https://huggingface.co/huggingface-course/mt5-small-finetuned-amazon-en-es) có thể tóm tắt các đánh giá của khách hàng như được hiển thị ở đây:

<iframe src="https://course-demos-mt5-small-finetuned-amazon-en-es.hf.space" frameBorder="0" height="400" title="Gradio app" class="block dark:hidden container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Như chúng ta sẽ thấy, những bản tóm tắt này ngắn gọn vì chúng được học từ các tiêu đề mà khách hàng cung cấp trong các bài đánh giá sản phẩm của họ. Hãy bắt đầu bằng cách tập hợp một kho ngữ liệu song ngữ phù hợp cho tác vụ này.

## Chuẩn bị kho ngữ liệu đa ngôn ngữ

Chúng ta sẽ sử dụng [Multilingual Amazon Reviews Corpus](https://huggingface.co/datasets/amazon_reviews_multi) để tạo trình tóm tắt song ngữ. Tập tài liệu này bao gồm các bài đánh giá sản phẩm của Amazon bằng sáu ngôn ngữ và thường được sử dụng để đánh giá các bộ phân loại đa ngôn ngữ. Tuy nhiên, vì mỗi bài đánh giá đi kèm với một tiêu đề ngắn, chúng ta có thể sử dụng các tiêu đề này làm nhãn tóm tắt cho mô hình của chúng ta để học! Để bắt đầu, hãy tải xuống các tập hợp con tiếng Anh và tiếng Tây Ban Nha từ Hugging Face Hub:

```python
from datasets import load_dataset

spanish_dataset = load_dataset("amazon_reviews_multi", "es")
english_dataset = load_dataset("amazon_reviews_multi", "en")
english_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
        num_rows: 200000
    })
    validation: Dataset({
        features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
        num_rows: 5000
    })
    test: Dataset({
        features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
        num_rows: 5000
    })
})
```

Như bạn có thể thấy, đối với mỗi ngôn ngữ, có 200,000 đánh giá cho phần `huấn luyện` và 5,000 nhận xét cho mỗi phần `kiểm định` và `kiểm thử`. Thông tin đánh giá mà chúng ta quan tâm được chứa trong cột `review_body` và `review_title`. Hãy xem một vài ví dụ bằng cách tạo một hàm đơn giản lấy một mẫu ngẫu nhiên từ tập huấn luyện với các kỹ thuật chúng ta đã học trong [Chương 5](/course/chapter5):

```python
def show_samples(dataset, num_samples=3, seed=42):
    sample = dataset["train"].shuffle(seed=seed).select(range(num_samples))
    for example in sample:
        print(f"\n'>> Title: {example['review_title']}'")
        print(f"'>> Review: {example['review_body']}'")


show_samples(english_dataset)
```

```python out
'>> Title: Worked in front position, not rear'
'>> Review: 3 stars because these are not rear brakes as stated in the item description. At least the mount adapter only worked on the front fork of the bike that I got it for.'

'>> Title: meh'
'>> Review: Does it’s job and it’s gorgeous but mine is falling apart, I had to basically put it together again with hot glue'

'>> Title: Can\'t beat these for the money'
'>> Review: Bought this for handling miscellaneous aircraft parts and hanger "stuff" that I needed to organize; it really fit the bill. The unit arrived quickly, was well packaged and arrived intact (always a good sign). There are five wall mounts-- three on the top and two on the bottom. I wanted to mount it on the wall, so all I had to do was to remove the top two layers of plastic drawers, as well as the bottom corner drawers, place it when I wanted and mark it; I then used some of the new plastic screw in wall anchors (the 50 pound variety) and it easily mounted to the wall. Some have remarked that they wanted dividers for the drawers, and that they made those. Good idea. My application was that I needed something that I can see the contents at about eye level, so I wanted the fuller-sized drawers. I also like that these are the new plastic that doesn\'t get brittle and split like my older plastic drawers did. I like the all-plastic construction. It\'s heavy duty enough to hold metal parts, but being made of plastic it\'s not as heavy as a metal frame, so you can easily mount it to the wall and still load it up with heavy stuff, or light stuff. No problem there. For the money, you can\'t beat it. Best one of these I\'ve bought to date-- and I\'ve been using some version of these for over forty years.'
```

<Tip>

✏️ **Thử nghiệm thôi!** Thay đổi seed ngẫu nhiên trong lệnh `Dataset.shuffle()` để khám phá các bài đánh giá khác trong kho tài liệu. Nếu bạn là người nói tiếng Tây Ban Nha, hãy xem một số bài đánh giá trong `spanish_dataset` để xem liệu các tiêu đề có giống như những bản tóm tắt hợp lý hay không.

</Tip>

Mẫu này cho thấy sự đa dạng của các bài đánh giá mà người ta thường tìm thấy trên mạng, từ tích cực đến tiêu cực (và mọi thứ ở giữa!). Mặc dù ví dụ với tiêu đề "meh" không nhiều thông tin, nhưng các tiêu đề khác trông giống như những bản tóm tắt phù hợp về bản thân các đánh giá. Việc huấn luyện một mô hình tóm tắt cho tất cả 400,000 bài đánh giá sẽ mất quá nhiều thời gian trên một GPU, vì vậy thay vào đó, chúng ta sẽ tập trung vào việc tạo tóm tắt cho một miền sản phẩm. Để biết tên miền mà chúng ta có thể chọn, hãy chuyển đổi `english_dataset` thành `pandas.DataFrame` và tính toán số lượng đánh giá cho mỗi danh mục sản phẩm:

```python
english_dataset.set_format("pandas")
english_df = english_dataset["train"][:]
# Hiển thị số lượng cho 20 sản phẩm hàng đầu
english_df["product_category"].value_counts()[:20]
```

```python out
home                      17679
apparel                   15951
wireless                  15717
other                     13418
beauty                    12091
drugstore                 11730
kitchen                   10382
toy                        8745
sports                     8277
automotive                 7506
lawn_and_garden            7327
home_improvement           7136
pet_products               7082
digital_ebook_purchase     6749
pc                         6401
electronics                6186
office_product             5521
shoes                      5197
grocery                    4730
book                       3756
Name: product_category, dtype: int64
```

Các sản phẩm phổ biến nhất trong tập dữ liệu tiếng Anh là về đồ gia dụng, quần áo và thiết bị điện tử không dây. Tuy nhiên, để gắn bó với chủ đề Amazon, chúng ta hãy tập trung vào việc tóm tắt các bài đánh giá sách - xét cho cùng, đây là những gì công ty được thành lập! Chúng tôi có thể thấy hai danh mục sản phẩm phù hợp với hóa đơn (`book` và `digital_ebook_purchase`), vì vậy, hãy lọc tập dữ liệu bằng cả hai ngôn ngữ chỉ cho các sản phẩm này. Như chúng ta đã thấy trong [Chương 5](/course/chapter5), hàm `Dataset.filter()` cho phép chúng ta cắt một tập dữ liệu rất hiệu quả, vì vậy chúng ta có thể xác định một hàm đơn giản để thực hiện điều này:

```python
def filter_books(example):
    return (
        example["product_category"] == "book"
        or example["product_category"] == "digital_ebook_purchase"
    )
```

Bây giờ khi chúng ta áp dụng hàm này cho `english_dataset` và `spanish_dataset`, kết quả sẽ chỉ chứa những hàng liên quan đến danh mục sách. Trước khi áp dụng bộ lọc, hãy chuyển định dạng của `english_dataset` từ `"pandas"` trở lại `"arrow"`:

```python
english_dataset.reset_format()
```

Sau đó, chúng tôi có thể áp dụng chức năng bộ lọc và để kiểm tra một mẫu đánh giá để xem chúng có thực sự là về sách hay không:

```python
spanish_books = spanish_dataset.filter(filter_books)
english_books = english_dataset.filter(filter_books)
show_samples(english_books)
```

```python out
'>> Title: I\'m dissapointed.'
'>> Review: I guess I had higher expectations for this book from the reviews. I really thought I\'d at least like it. The plot idea was great. I loved Ash but, it just didnt go anywhere. Most of the book was about their radio show and talking to callers. I wanted the author to dig deeper so we could really get to know the characters. All we know about Grace is that she is attractive looking, Latino and is kind of a brat. I\'m dissapointed.'

'>> Title: Good art, good price, poor design'
'>> Review: I had gotten the DC Vintage calendar the past two years, but it was on backorder forever this year and I saw they had shrunk the dimensions for no good reason. This one has good art choices but the design has the fold going through the picture, so it\'s less aesthetically pleasing, especially if you want to keep a picture to hang. For the price, a good calendar'

'>> Title: Helpful'
'>> Review: Nearly all the tips useful and. I consider myself an intermediate to advanced user of OneNote. I would highly recommend.'
```

Được rồi, chúng ta có thể thấy rằng các bài đánh giá không hoàn toàn về sách và có thể đề cập đến những thứ như lịch và các ứng dụng điện tử như OneNote. Tuy nhiên, mảng này có vẻ thích hợp để huấn luyện một mô hình tóm tắt. Trước khi xem xét các mô hình khác nhau phù hợp cho tác vụ này, chúng ta còn một bước chuẩn bị dữ liệu cuối cùng cần làm: kết hợp các bài đánh giá bằng tiếng Anh và tiếng Tây Ban Nha dưới dạng một đối tượng `DatasetDict` duy nhất. 🤗 Datasets cung cấp một hàm `concatenate_datasets()` tiện dụng (như tên cho thấy) sẽ xếp chồng hai đối tượng `Dataset` lên trên nhau. Vì vậy, để tạo tập dữ liệu song ngữ của mình, chúng ta sẽ lặp lại từng phần dữ liệu, nối các tập dữ liệu cho phần đó và xáo trộn kết quả để đảm bảo mô hình không quá phù hợp với một ngôn ngữ duy nhất:

```python
from datasets import concatenate_datasets, DatasetDict

books_dataset = DatasetDict()

for split in english_books.keys():
    books_dataset[split] = concatenate_datasets(
        [english_books[split], spanish_books[split]]
    )
    books_dataset[split] = books_dataset[split].shuffle(seed=42)

# Chọn ra một vài mẫu
show_samples(books_dataset)
```

```python out
'>> Title: Easy to follow!!!!'
'>> Review: I loved The dash diet weight loss Solution. Never hungry. I would recommend this diet. Also the menus are well rounded. Try it. Has lots of the information need thanks.'

'>> Title: PARCIALMENTE DAÑADO'
'>> Review: Me llegó el día que tocaba, junto a otros libros que pedí, pero la caja llegó en mal estado lo cual dañó las esquinas de los libros porque venían sin protección (forro).'

'>> Title: no lo he podido descargar'
'>> Review: igual que el anterior'
```

Đây chắc chắn là sự kết hợp giữa các bài đánh giá bằng tiếng Anh và tiếng Tây Ban Nha! Bây giờ chúng ta đã có một kho tài liệu huấn luyện, một điều cuối cùng cần kiểm tra là sự phân bố các từ trong các bài đánh giá và tiêu đề của chúng. Điều này đặc biệt quan trọng đối với các tác vụ tóm tắt, trong đó các tóm tắt tham chiếu ngắn trong dữ liệu có thể làm sai lệch mô hình chỉ xuất ra một hoặc hai từ trong các tóm tắt đã tạo. Các biểu đồ bên dưới hiển thị các phân bố từ và chúng ta có thể thấy rằng các tiêu đề bị lệch nhiều về chỉ 1-2 từ:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/review-lengths.svg" alt="Word count distributions for the review titles and texts."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/review-lengths-dark.svg" alt="Word count distributions for the review titles and texts."/>
</div>

Để giải quyết vấn đề này, chúng ta sẽ lọc ra các ví dụ có tiêu đề rất ngắn để mô hình có thể tạo ra các bản tóm tắt thú vị hơn. Vì chúng ta đang xử lý các văn bản tiếng Anh và tiếng Tây Ban Nha, chúng ta có thể sử dụng phương pháp thô để phân chia các tiêu đề theo dấu cách và sau đó sử dụng phương pháp `Dataset.filter()` đáng tin cậy của mình như sau:

```python
books_dataset = books_dataset.filter(lambda x: len(x["review_title"].split()) > 2)
```

Bây giờ chúng ta đã chuẩn bị kho tài liệu của mình, hãy cùng xem một vài mẫu Transformer khả thi mà người ta có thể tinh chỉnh nó!

## Các mô hình cho tóm tắt văn bản

Nếu bạn nghĩ về nó, tóm tắt văn bản là một loại tác vụ tương tự như dịch máy: chúng ta có một phần nội dung văn bản giống như một bài đánh giá mà ta muốn "dịch" thành một phiên bản ngắn hơn để nắm bắt các tính năng nổi bật của đầu vào. Theo đó, hầu hết các mô hình Transformer để tóm tắt đều áp dụng kiến trúc bộ mã hóa-giải mã mà chúng ta đã gặp lần đầu tiên trong [Chương 1](/course/chapter1), mặc dù có một số ngoại lệ như họ mô hình GPT cũng có thể được sử dụng để tóm tắt trong cài đặt few-shot. Bảng sau đây liệt kê một số mô hình được huấn luyện trước phổ biến có thể được tinh chỉnh để tóm tắt.

| Mô hình Transformer | Mô tả                                                                                                                                                                                                    | Đa ngôn ngữ? |
| :---------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------: |
|    [GPT-2](https://huggingface.co/gpt2-xl)    | Mặc dù được huấn luyện như một mô hình ngôn ngữ tự hồi quy, bạn có thể dùng GPT-2 để tạo ra các bản tóm tắt bằng cách nối "TL;DR" cuối mỗi đoạn đầu vào. |      ❌       |
|   [PEGASUS](https://huggingface.co/google/pegasus-large)   | Sử dụng hàm mục tiêu huấn luyện trước để dự đoán các câu bị ẩn đi trong văn bản đa câu. Cơ chế này gần với tóm tắt hơn mô hình ngôn ngữ vanilla và đạt điểm cao hơn trên các chuẩn phổ biến. |      ❌       |
|     [T5](https://huggingface.co/t5-base)      | Một kiến trúc Transformer phổ quát tạo ra tất cả các tác vụ trong khung văn bản sang văn bản; ví dụ,định dạng đầu vào cho mô hình để tóm tắt tài liệu là `summarize: ARTICLE`. |      ❌       |
|     [mT5](https://huggingface.co/google/mt5-base)     | Một phiên bản đa ngôn ngữ của T5, được huấn luyện trước trên kho ngữ liệu Common Crawl corpus (mC4), bao gồm 101 ngôn ngữ. |      ✅       |
|    [BART](https://huggingface.co/facebook/bart-base)     | Một kiến trúc Transformer mới với cả bộ mã hóa và giải mã được huấn luyện để tái tạo lại đầu vào bị phá, kết hợp các cơ chế huấn luyện trước của BERT và GPT-2.                 |      ❌       |
|  [mBART-50](https://huggingface.co/facebook/mbart-large-50) | Phiên bản đa ngôn ngữ của BART, huấn luyện trước trên 50 ngôn ngữ.|      ✅       |

Như bạn có thể thấy từ bảng này, phần lớn các mô hình Transformer để tóm tắt (và thực sự là hầu hết các tác vụ NLP) là đơn ngữ. Điều này thật tuyệt nếu tác vụ của bạn sử dụng ngôn ngữ "nhiều tài nguyên" như tiếng Anh hoặc tiếng Đức, nhưng bớt dần đối với hàng nghìn ngôn ngữ khác đang được sử dụng trên khắp thế giới. May mắn thay, có một loại mô hình Transformer đa ngôn ngữ, như mT5 và mBART, ra đời để giải cứu ta. Những mô hình này được huấn luyện trước bằng cách sử dụng mô hình ngôn ngữ, nhưng có một điểm khác biệt: thay vì huấn luyện ngữ liệu của một ngôn ngữ, chúng được huấn luyện cùng lúc về các văn bản bằng hơn 50 ngôn ngữ cùng một lúc!

Chúng ta sẽ tập trung vào mT5, một kiến trúc thú vị dựa trên T5 đã được huấn luyện trước trong khung văn bản sang văn bản. Trong T5, mọi tác vụ NLP được xây dựng dưới dạng tiền tố nhắc như `summarize:` điều kiện nào để mô hình điều chỉnh văn bản được tạo thành lời nhắc. Như thể hiện trong hình bên dưới, điều này làm cho T5 trở nên cực kỳ linh hoạt, vì bạn có thể giải quyết nhiều tác vụ chỉ với một mô hình duy nhất!

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/t5.svg" alt="Different tasks performed by the T5 architecture."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/t5-dark.svg" alt="Different tasks performed by the T5 architecture."/>
</div>

mT5 không sử dụng tiền tố, nhưng chia sẻ phần lớn tính linh hoạt của T5 và có lợi thế là đa ngôn ngữ. Giờ ta đã chọn một mô hình, hãy xem xét việc chuẩn bị dữ liệu để huấn luyện.


<Tip>

✏️ **Thử nghiệm thôi!** Khi bạn đã làm qua phần này, hãy xem mT5 so với mBART tốt như thế nào bằng cách tinh chỉnh phần sau với các kỹ thuật tương tự. Để có điểm thưởng, bạn cũng có thể thử tinh chỉnh T5 chỉ trên các bài đánh giá tiếng Anh. Vì T5 có tiền tố nhắc đặc biệt, bạn sẽ cần thêm  `summarize:` vào trước các mẫu đầu vào trong các bước tiền xử lý bên dưới.

</Tip>

## Tiền xử lý dữ liệu

<Youtube id="1m7BerpSq8A"/>

Tác vụ tiếp theo của chúng ta là tokenize và mã hóa các bài đánh giá và tiêu đề của chúng. Như thường lệ, ta bắt đầu bằng cách tải tokenizer được liên kết với checkpoint mô hình được huấn luyện trước. Chúng ta sẽ sử dụng `mt5-small` làm checkpoint để có thể tinh chỉnh mô hình trong một khoảng thời gian hợp lý:

```python
from transformers import AutoTokenizer

model_checkpoint = "google/mt5-small"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
```

<Tip>

💡 Trong giai đoạn đầu của các dự án NLP của bạn, một phương pháp hay là huấn luyện một lớp các mô hình "nhỏ" trên một mẫu dữ liệu nhỏ. Điều này cho phép bạn gỡ lỗi và lặp lại nhanh hơn đối với quy trình làm việc đầu cuối. Một khi bạn tự tin vào kết quả, bạn luôn có thể mở rộng mô hình bằng cách thay đổi checkpoint của mô hình!
</Tip>

Hãy thử nghiệm mT5 tokenizer trên một ví dụ nhỏ:

```python
inputs = tokenizer("I loved reading the Hunger Games!")
inputs
```

```python out
{'input_ids': [336, 259, 28387, 11807, 287, 62893, 295, 12507, 1], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
```

Ở đây, chúng ta có thể thấy `input_ids` và `attention_mask` mà chúng ta đã gặp trong các thử nghiệm tinh chỉnh đầu tiên của chúng ta trong [Chương 3](/course/chapter3). Hãy giải mã các ID đầu vào này bằng hàm `convert_ids_to_tokens()` của tokenizer để xem chúng ta đang xử lý loại tokenizer nào:

```python
tokenizer.convert_ids_to_tokens(inputs.input_ids)
```

```python out
['▁I', '▁', 'loved', '▁reading', '▁the', '▁Hung', 'er', '▁Games', '</s>']
```

Ký tự Unicode đặc biệt `▁` và token cuối chuỗi `</s>` cho biết ta đang xử lý SentencePiece tokenizer, dựa trên thuật toán phân đoạn Unigram được thảo luận trong [Chương 6](/course/chapter6). Unigram đặc biệt hữu ích đối với kho ngữ liệu đa ngôn ngữ vì nó cho phép SentencePiece bất khả tri về dấu, dấu câu và thực tế là nhiều ngôn ngữ, như tiếng Nhật, không có ký tự khoảng trắng.

Để mã hóa kho tài liệu của mình, chúng ta phải xử lý một cách tính tế với tóm tắt: bởi vì các nhãn cũng là văn bản, có thể chúng vượt quá kích thước ngữ cảnh tối đa của mô hình. Điều này có nghĩa là chúng ta cần áp dụng việc cắt bớt cho cả các bài đánh giá và tiêu đề của chúng để đảm bảo không truyền các đầu vào quá dài cho mô hình của mình. Các tokenizer trong 🤗 Transformers cung cấp một hàm `as_target_tokenizer()` tiện lợi cho phép bạn mã hóa các nhãn song song với các đầu vào. Điều này thường được thực hiện bằng cách sử dụng trình quản lý ngữ cảnh bên trong một chức năng tiền xử lý, trước tiên mã hóa các đầu vào, sau đó mã hóa các nhãn dưới dạng một cột riêng biệt. Đây là một ví dụ về một hàm như vậy cho mT5:

```python
max_input_length = 512
max_target_length = 30


def preprocess_function(examples):
    model_inputs = tokenizer(
        examples["review_body"], max_length=max_input_length, truncation=True
    )
    # Thiết lập tokenizer cho nhãn
    with tokenizer.as_target_tokenizer():
        labels = tokenizer(
            examples["review_title"], max_length=max_target_length, truncation=True
        )

    model_inputs["labels"] = labels["input_ids"]
    return model_inputs
```

Hãy xem qua đoạn mã này để hiểu điều gì đang xảy ra. Điều đầu tiên chúng ta đã làm là xác định các giá trị cho `max_input_length` và `max_target_length`, đặt giới hạn trên cho thời lượng các bài đánh giá và tiêu đề của chúng. Vì nội dung đánh giá thường lớn hơn nhiều so với tiêu đề, chúng ta đã điều chỉnh các giá trị này cho phù hợp. Sau đó, trong chính `preprocess_function()`, chúng ta có thể thấy các bài đánh giá được tokenize đầu tiên, tiếp theo là các tiêu đề với `as_target_tokenizer()`.

Với `preprocess_function()`, việc tokenize toàn bộ kho dữ liệu bằng hàm `Dataset.map()` tiện dụng mà chúng ta đã sử dụng rộng rãi trong suốt khóa học này là một vấn đề đơn giản:

```python
tokenized_datasets = books_dataset.map(preprocess_function, batched=True)
```

Bây giờ kho dữ liệu đã được xử lý trước, chúng ta hãy xem xét một số chỉ số thường được sử dụng để tóm tắt. Như chúng ta sẽ thấy, không có giải pháp dễ dàng và nhanh chóng khi nói đến việc đo lường chất lượng của văn bản do máy tạo ra.

<Tip>

💡 Bạn có thể nhận thấy rằng chúng ta đã sử dụng `batched=True` trong hàm`Dataset.map()` ở trên. Điều này mã hóa các mẫu theo lô 1,000 (mặc định) và cho phép bạn sử dụng khả năng đa luồng của các bộ tokenizer nhanh trong 🤗 Transformers. Nếu có thể, hãy thử sử dụng `batched=True` để tận dụng tối đa quá trình tiền xử lý của bạn!

</Tip>

## Thước đo cho tóm tắt văn bản

<Youtube id="TMshhnrEXlg"/>

So với hầu hết các tác vụ khác mà chúng ta đã đề cập trong khóa học này, việc đo lường hiệu suất của các tác vụ tạo văn bản như tóm tắt hoặc dịch không đơn giản bằng. Ví dụ: được đưa ra một bài đánh giá như "I loved reading the Hunger Games", có nhiều bản tóm tắt hợp lệ, chẳng hạn như "I loved the Hunger Games" hoặc "Hunger Games is a great read". Rõ ràng, việc áp dụng một số loại đối sánh chính xác nào đó giữa bản tóm tắt được tạo và nhãn không phải là một giải pháp tốt - ngay cả con người cũng sẽ đánh giá thấp hơn theo một chỉ số như vậy, bởi vì tất cả chúng ta đều có phong cách viết riêng của mình.

Để tóm tắt, một trong những chỉ số được sử dụng phổ biến nhất là [điểm ROUGE](https://en.wikipedia.org/wiki/ROUGE_(metric)) (viết tắt của Recall-Oriented Understudy for Gisting Assessment). Ý tưởng cơ bản đằng sau thước đo này là so sánh một bản tóm tắt đã tạo với một tập hợp các bản tóm tắt tham chiếu thường do con người tạo ra. Để làm cho điều này chính xác hơn, giả sử chúng ta muốn so sánh hai bản tóm tắt sau:

```python
generated_summary = "I absolutely loved reading the Hunger Games"
reference_summary = "I loved reading the Hunger Games"
```

Một cách để có thể so sánh chúng là đếm số từ trùng lặp, trong trường hợp này sẽ là 6. Tuy nhiên, điều này hơi thô, vì vậy thay vào đó ROUGE dựa trên việc tính toán điểm số _precision_  và _recall_ cho sự trùng lặp.

<Tip>

🙋 Đừng lo lắng nếu đây là lần đầu tiên bạn nghe nói về precision và recall - chúng ta sẽ cùng nhau điểm qua một số ví dụ rõ ràng để làm rõ tất cả. Các chỉ số này thường gặp trong các tác vụ phân loại, vì vậy nếu bạn muốn hiểu cách xác định precision và recall trong ngữ cảnh đó, chúng tôi khuyên bạn nên xem [hướng dẫn `scikit-learn`](https://scikit-learn.org/stable/auto_examples/model_selection/plot_precision_recall.html).

</Tip>

Đối với ROUGE, recall đo lường mức độ tóm tắt tham chiếu thu được từ cái đã tạo. Nếu chúng ta chỉ so sánh các từ, recall có thể được tính theo công thức sau:

$$ \mathrm{Recall} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, reference\, summary}} $$

Đối với ví dụ đơn giản ở trên, công thức này cho phép nhớ hoàn hảo là 6/6 = 1; tức là tất cả các từ trong bản tóm tắt tham chiếu đã được tạo ra bởi mô hình. Điều này nghe có vẻ tuyệt vời, nhưng hãy tưởng tượng nếu bản tóm tắt được tạo của chúng ta là "I really really loved reading the Hunger Games all night". Điều này cũng sẽ có một recall hoàn hảo, nhưng được cho là một bản tóm tắt tồi tệ hơn vì nó dài dòng. Để đối phó với những tình huống này, chúng ta cũng tính toán độ precision, trong ngữ cảnh ROUGE đo lường mức độ liên quan của bản tóm tắt đã tạo:

$$ \mathrm{Precision} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, generated\, summary}} $$

Áp dụng điều này cho bản tóm tắt dài dòng của chúng ta sẽ cho precision là 6/10 = 0.6, thấp hơn đáng kể so với precision 6/7 = 0.86 thu được bằng phương pháp ngắn hơn của mình. Trong thực tế, cả precision và recall thường được tính toán, và sau đó điểm F1 (giá trị trung bình hài hòa của precision và recall) được báo cáo. Chúng ta có thể thực hiện việc này dễ dàng trong 🤗 Datasets bằng cách cài đặt gói `rouge_score` trước:

```py
!pip install rouge_score
```

và sau đó tải chỉ số ROUGE như sau:

```python
import evaluate

rouge_score = evaluate.load("rouge")
```

Sau đó ta có thể sử dụng hàm `rouge_score.compute()` để tính tất cả các chỉ số trong một lần:

```python
scores = rouge_score.compute(
    predictions=[generated_summary], references=[reference_summary]
)
scores
```

```python out
{'rouge1': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92)),
 'rouge2': AggregateScore(low=Score(precision=0.67, recall=0.8, fmeasure=0.73), mid=Score(precision=0.67, recall=0.8, fmeasure=0.73), high=Score(precision=0.67, recall=0.8, fmeasure=0.73)),
 'rougeL': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92)),
 'rougeLsum': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92))}
```

Chà, có rất nhiều thông tin trong đầu ra đó - tất cả có nghĩa là gì? Đầu tiên, 🤗 Datasets thực sự tính toán khoảng tin cậy cho precision, recall và F1 score; đây là các thuộc tính `low`, `mid` và `high` mà bạn có thể xem ở đây. Hơn nữa, 🤗 Datasets tính toán nhiều điểm ROUGE khác nhau dựa trên các loại văn bản chi tiết khác nhau khi so sánh các tóm tắt đã tạo và tham chiếu. Biến thể `rouge1` là sự chồng chéo của các khối đơn - đây chỉ là một cách nói hoa mỹ để nói về sự chồng chéo của các từ và chính xác là số liệu mà chúng ta đã thảo luận ở trên. Để xác minh điều này, chúng ta hãy lấy ra giá trị `mid` điểm số của mình:

```python
scores["rouge1"].mid
```

```python out
Score(precision=0.86, recall=1.0, fmeasure=0.92)
```

Tuyệt vời, precision và recall lại khớp với nhau! Còn những điểm ROUGE khác thì sao? `rouge2` đo lường sự trùng lặp giữa các bigram (hãy nghĩ rằng đó là sự chồng chéo của các cặp từ), trong khi `rougeL` và `rougeLsum` đo lường các chuỗi từ phù hợp dài nhất bằng cách tìm kiếm các chuỗi con chung dài nhất trong các bản tóm tắt được tạo và tham chiếu. "sum" trong `rougeLsum` đề cập đến thực tế là chỉ số này được tính trên toàn bộ bản tóm tắt, trong khi `rougeL` được tính là giá trị trung bình trên các câu riêng lẻ.

<Tip>

✏️ **Thử nghiệm thôi!** Tạo ví dụ của riêng bạn về bản tóm tắt được tạo và tham khảo, và xem liệu điểm kết quả ROUGE có giống với tính toán thủ công dựa trên các công thức về precision và recall hay không. Để có điểm thưởng, hãy chia văn bản thành bigrams và so sánh độ chính xác và thu hồi cho chỉ số `rouge2`.

</Tip>

Chúng tôi sẽ sử dụng các điểm ROUGE này để theo dõi hiệu suất của mô hình, nhưng trước khi làm điều đó, hãy làm điều mà mọi người thực hành NLP giỏi nên làm: tạo một đường cơ sở mạnh mẽ nhưng đơn giản!

### Tạo một đường cơ sở mạnh mẽ

Môt mô hình cơ sở cho tóm tắt văn bản, đó là chỉ cần lấy ba câu đầu tiên của một bài báo, thường được gọi là _lead-3_ hay _3 bài đầu_. Chúng ta có thể sử dụng các dấu chấm để theo dõi ranh giới câu, nhưng điều này sẽ không thành công đối với các từ viết tắt như "U.S." hoặc "U.N." - vì vậy thay vào đó, chúng ta sẽ sử dụng thư viện `nltk`, bao gồm một thuật toán tốt hơn để xử lý những trường hợp này. Bạn có thể cài đặt gói bằng cách sử dụng `pip` như sau:

```python
!pip install nltk
```

và sau đó tải các quy tắc dấu câu:

```python
import nltk

nltk.download("punkt")
```

Tiếp theo, chúng ta nhập tokenizer câu từ `nltk` và tạo một hàm đơn giản để trích xuất ba câu đầu tiên trong một bài đánh giá. Quy ước trong phần tóm tắt văn bản là tách từng phần tóm tắt bằng một dòng mới, vì vậy hãy bao gồm phần này và kiểm tra nó trên một ví dụ huấn luyện:

```python
from nltk.tokenize import sent_tokenize


def three_sentence_summary(text):
    return "\n".join(sent_tokenize(text)[:3])


print(three_sentence_summary(books_dataset["train"][1]["review_body"]))
```

```python out
'I grew up reading Koontz, and years ago, I stopped,convinced i had "outgrown" him.'
'Still,when a friend was looking for something suspenseful too read, I suggested Koontz.'
'She found Strangers.'
```

Điều này có vẻ hiệu quả, vì vậy bây giờ chúng ta hãy triển khai một hàm trích xuất các "tóm tắt" này từ tập dữ liệu và tính toán điểm ROUGE cho mô hình cơ sở:

```python
def evaluate_baseline(dataset, metric):
    summaries = [three_sentence_summary(text) for text in dataset["review_body"]]
    return metric.compute(predictions=summaries, references=dataset["review_title"])
```

Sau đó, chúng ta có thể sử dụng hàm này để tính toán điểm ROUGE trên tập kiểm định và kiểm tra chúng một chút bằng cách sử dụng Pandas:

```python
import pandas as pd

score = evaluate_baseline(books_dataset["validation"], rouge_score)
rouge_names = ["rouge1", "rouge2", "rougeL", "rougeLsum"]
rouge_dict = dict((rn, round(score[rn].mid.fmeasure * 100, 2)) for rn in rouge_names)
rouge_dict
```

```python out
{'rouge1': 16.74, 'rouge2': 8.83, 'rougeL': 15.6, 'rougeLsum': 15.96}
```

Chúng ta có thể thấy rằng điểm `rouge2` thấp hơn đáng kể so với phần còn lại; điều này có thể phản ánh thực tế là tiêu đề bài đánh giá thường ngắn gọn và do đó, mô hình cơ sở của 3 bài đầu quá dài dòng. Bây giờ chúng ta đã có một cơ sở tốt để làm việc, hãy chuyển sự chú ý của chúng ta sang việc tinh chỉnh mT5!

{#if fw === 'pt'}

## Tinh chỉnh mT5 với API `Trainer`

Việc tinh chỉnh một mô hình để tóm tắt rất giống với các tác vụ khác mà chúng ta đã đề cập trong chương này. Điều đầu tiên chúng ta cần làm là tải mô hình được huấn luyện trước từ checkpoint `mt5-small`. Vì tóm tắt là một tác vụ chuỗi sang chuỗi, chúng ta có thể tải mô hình bằng lớp `AutoModelForSeq2SeqLM`, lớp này sẽ tự động tải xuống và lưu vào bộ nhớ cache các trọng số:

```python
from transformers import AutoModelForSeq2SeqLM

model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

{:else}

## Tinh chỉnh mT5 với Keras

Việc tinh chỉnh một mô hình để tóm tắt rất giống với các tác vụ khác mà chúng ta đã đề cập trong chương này. Điều đầu tiên chúng ta cần làm là tải mô hình được huấn luyện trước từ checkpoint `mt5-small`. Vì tóm tắt là một tác vụ chuỗi sang chuỗi, chúng ta có thể tải mô hình bằng lớp `TFAutoModelForSeq2SeqLM`, lớp này sẽ tự động tải xuống và lưu vào bộ nhớ cache các trọng số:

```python
from transformers import TFAutoModelForSeq2SeqLM

model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

{/if}

<Tip>

💡 Nếu bạn đang tự hỏi tại sao bạn không thấy bất kỳ cảnh báo nào về việc tinh chỉnh mô hình trên một tác vụ phía sau, đó là bởi vì đối với các tác vụ chuỗi sang chuỗi, chúng ta giữ tất cả các trọng số của mạng. So sánh mô hình này với mô hình phân loại văn bản trong [Chương 3](/course/chapter3), trong đó phần đầu của mô hình định sẵn được thay thế bằng một mạng được khởi tạo ngẫu nhiên.

</Tip>

Điều tiếp theo chúng ta cần làm là đăng nhập vào Hugging Face Hub. Nếu bạn đang chạy đoạn mã này trong notebook, bạn có thể làm như vậy với hàm tiện ích sau:

```python
from huggingface_hub import notebook_login

notebook_login()
```

sẽ hiển thị một tiện ích mà bạn có thể nhập thông tin đăng nhập của mình. Ngoài ra, bạn có thể chạy lệnh này trong thiết bị đầu cuối của mình và đăng nhập vào đó:

```
huggingface-cli login
```

{#if fw === 'pt'}

Chúng ta sẽ cần tạo tóm tắt để tính điểm ROUGE trong quá trình huấn luyện. May mắn thay, 🤗 Transformers cung cấp các lớp `Seq2SeqTrainingArguments` và `Seq2SeqTrainer` chuyên dụng có thể tự động làm việc này cho chúng ta! Để xem cách này hoạt động như thế nào, trước tiên chúng ta hãy xác định các siêu tham số và các tham số khác cho các thử nghiệm của mình:

```python
from transformers import Seq2SeqTrainingArguments

batch_size = 8
num_train_epochs = 8
# Hiện thị mất mát huấn luyện mỗi epoch
logging_steps = len(tokenized_datasets["train"]) // batch_size
model_name = model_checkpoint.split("/")[-1]

args = Seq2SeqTrainingArguments(
    output_dir=f"{model_name}-finetuned-amazon-en-es",
    evaluation_strategy="epoch",
    learning_rate=5.6e-5,
    per_device_train_batch_size=batch_size,
    per_device_eval_batch_size=batch_size,
    weight_decay=0.01,
    save_total_limit=3,
    num_train_epochs=num_train_epochs,
    predict_with_generate=True,
    logging_steps=logging_steps,
    push_to_hub=True,
)
```

Ở đây, tham số `predict_with_generate` đã được đặt để chỉ ra rằng chúng ta nên tạo các bản tóm tắt trong quá trình đánh giá để có thể tính toán điểm ROUGE cho mỗi epoch. Như đã thảo luận trong [Chương 1](/course/chapter1), bộ giải mã thực hiện luận suy bằng cách dự đoán từng token và điều này được thực hiện bởi phương thức `generate()` của mô hình. Đặt `predict_with_generate=True` sẽ cho `Seq2SeqTrainer` sử dụng phương pháp đó để đánh giá. Chúng ta cũng đã điều chỉnh một số siêu tham số mặc định, như tốc độ học, số epoch và giảm trọng số và chúng ta đã đặt tùy chọn `save_total_limit` để chỉ lưu tối đa 3 checkpoint trong quá trình huấn luyện - điều này là do ngay cả phiên bản "nhỏ" của mT5 sử dụng khoảng một GB dung lượng ổ cứng và chúng ta có thể tiết kiệm một chút dung lượng bằng cách giới hạn số lượng bản sao ta lưu.

Tham số `push_to_hub=True` sẽ cho phép chúng ta đẩy mô hình vào Hub sau khi huấn luyện; bạn sẽ tìm thấy kho lưu trữ bên dưới hồ sơ người dùng của mình ở vị trí được xác định bởi `output_dir`. Lưu ý rằng bạn có thể chỉ định tên của kho lưu trữ mà bạn muốn đẩy đến bằng tham số `hub_model_id` (cụ thể là bạn sẽ phải sử dụng tham số này để đẩy đến một tổ chức). Ví dụ: khi chúng ta đẩy mô hình vào tổ chức [`huggingface-course`](https://huggingface.co/huggingface-course), chúng ta đã thêm `hub_model_id="huggingface-course/mt5-finetuned-amazon-en-es"` thành `Seq2SeqTrainingArguments`.

Điều tiếp theo chúng ta cần làm là cung cấp cho người huấn luyện một hàm `compute_metrics()` để chúng ta có thể đánh giá mô hình của mình trong quá trình huấn luyện. Để tóm tắt, điều này cần nhiều hơn là đơn giản gọi `rouge_score.compute()` trên các dự đoán của mô hình, vì chúng ta cần _giải mã_ các kết quả đầu ra và nhãn thành văn bản trước khi chúng ta có thể tính điểm ROUGE. Hàm sau thực hiện chính xác điều đó và cũng sử dụng hàm `sent_tokenize()` từ `nltk` để tách các câu tóm tắt bằng các dòng mới:

```python
import numpy as np


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    # Giải mã các tóm tắt được tạo ra thành văn bản
    decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
    # Thay -100 vào nhãn vì ta không thể giải mã chúng
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    # Giải mã các tóm tắt mẫu thành văn bản
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
    # ROUGE kì vọng dòng mới sau mỗi câu
    decoded_preds = ["\n".join(sent_tokenize(pred.strip())) for pred in decoded_preds]
    decoded_labels = ["\n".join(sent_tokenize(label.strip())) for label in decoded_labels]
    # Tính điểm ROUGE
    result = rouge_score.compute(
        predictions=decoded_preds, references=decoded_labels, use_stemmer=True
    )
    # Trích xuất điểm trung vị
    result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
    return {k: round(v, 4) for k, v in result.items()}
```

{/if}

Tiếp theo, chúng ta cần phải xác định một bộ đối chiếu dữ liệu cho tác vụ chuỗi sang chuỗi của chúng ta. Vì mT5 là mô hình Transformer bộ mã hóa-giải mã, một điều tinh tế khi chuẩn bị các lô là trong quá trình giải mã, chúng ta cần chuyển các nhãn sang phải từng nhãn. Điều này là bắt buộc để đảm bảo rằng bộ giải mã chỉ nhìn thấy các nhãn trước đó chứ không phải nhãn hiện tại hoặc tương lai, điều này sẽ giúp mô hình dễ dàng ghi nhớ. Điều này tương tự như cách áp dụng masked self-attention cho các đầu vào trong một tác vụ như [mô hình ngôn ngữ nhân quả](/course/chapter7/6).

May mắn thay, 🤗 Transformers cung cấp một bộ đối chiếu `DataCollatorForSeq2Seq` sẽ tự động đệm các đầu vào và nhãn cho chúng ta. Để khởi tạo bộ đối chiếu này, chúng ta chỉ cần cung cấp `tokenizer` và `model`:

{#if fw === 'pt'}

```python
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)
```

{:else}

```python
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf")
```

{/if}

Hãy xem những gì mà máy đối chiếu này tạo ra khi được cung cấp một loạt các ví dụ nhỏ. Đầu tiên, chúng ta cần xóa các cột có chuỗi vì trình đối chiếu sẽ không biết cách chèn các phần tử này:

```python
tokenized_datasets = tokenized_datasets.remove_columns(
    books_dataset["train"].column_names
)
```

Vì bộ đối chiếu mong đợi một danh sách các `dict`, trong đó mỗi `dict` đại diện cho một ví dụ duy nhất trong tập dữ liệu, chúng ta cũng cần cuộn dữ liệu thành định dạng mong đợi trước khi chuyển nó đến bộ đối chiếu dữ liệu:

```python
features = [tokenized_datasets["train"][i] for i in range(2)]
data_collator(features)
```

```python out
{'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]), 'input_ids': tensor([[  1494,    259,   8622,    390,    259,    262,   2316,   3435,    955,
            772,    281,    772,   1617,    263,    305,  14701,    260,   1385,
           3031,    259,  24146,    332,   1037,    259,  43906,    305,    336,
            260,      1,      0,      0,      0,      0,      0,      0],
        [   259,  27531,  13483,    259,   7505,    260, 112240,  15192,    305,
          53198,    276,    259,  74060,    263,    260,    459,  25640,    776,
           2119,    336,    259,   2220,    259,  18896,    288,   4906,    288,
           1037,   3931,    260,   7083, 101476,   1143,    260,      1]]), 'labels': tensor([[ 7483,   259,  2364, 15695,     1,  -100],
        [  259, 27531, 13483,   259,  7505,     1]]), 'decoder_input_ids': tensor([[    0,  7483,   259,  2364, 15695,     1],
        [    0,   259, 27531, 13483,   259,  7505]])}
```

Điều chính cần chú ý ở đây là mẫu đầu tiên dài hơn thứ hai, do đó, `input_ids` và `attention_mask` của mẫu thứ hai đã được đệm ở bên phải bằng `[PAD]` (có ID là `0`). Tương tự, chúng ta có thể thấy rằng `labels` đã được đệm bằng `-100`, để đảm bảo rằng các token đệm được bỏ qua bởi hàm mất mát. Và cuối cùng, chúng ta có thể thấy một `decoder_input_ids` mới đã chuyển các nhãn sang bên phải bằng cách chèn `[PAD]` vào mục nhập đầu tiên.

{#if fw === 'pt'}

Cuối cùng thì chúng ta cũng có tất cả những nguyên liệu cần thiết để luyện tập! Bây giờ chúng ta chỉ cần khởi tạo trình huấn luyện với các tham số tiêu chuẩn:

```python
from transformers import Seq2SeqTrainer

trainer = Seq2SeqTrainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics,
)
```

và khởi chạy chương trình huấn luyện của mình:

```python
trainer.train()
```

Trong quá trình huấn luyện, bạn sẽ thấy mất mát huấn luyện giảm và điểm ROUGE tăng lên theo từng epoch. Sau khi quá trình huấn luyện hoàn tất, bạn có thể xem điểm ROUGE cuối cùng bằng cách chạy `Trainer.evaluate()`:

```python
trainer.evaluate()
```

```python out
{'eval_loss': 3.028524398803711,
 'eval_rouge1': 16.9728,
 'eval_rouge2': 8.2969,
 'eval_rougeL': 16.8366,
 'eval_rougeLsum': 16.851,
 'eval_gen_len': 10.1597,
 'eval_runtime': 6.1054,
 'eval_samples_per_second': 38.982,
 'eval_steps_per_second': 4.914}
```

Từ điểm số, chúng ta có thể thấy rằng mô hình của mình đã vượt trội so với mô hình cơ sở với 3 bài đầu tiên - thật tuyệt! Điều cuối cùng cần làm là đẩy các trọng số mô hình vào Hub, như sau:

```
trainer.push_to_hub(commit_message="Training complete", tags="summarization")
```

```python out
'https://huggingface.co/huggingface-course/mt5-finetuned-amazon-en-es/commit/aa0536b829b28e73e1e4b94b8a5aacec420d40e0'
```

Thao tác này sẽ lưu các checkpoint và các tệp cấu hình vào `output_dir`, trước khi tải tất cả các tệp lên Hub. Bằng cách chỉ định tham số `tags`, chúng ta cũng đảm bảo rằng tiện ích con trên Hub sẽ là một tiện ích con dành cho quy trình tóm tắt thay vì tiện ích tạo văn bản mặc định được liên kết với kiến trúc mT5 (để biết thêm thông tin về thẻ mô hình, hãy xem tài liệu [🤗 Hub ](https://huggingface.co/docs/hub/main#how-is-a-models-type-of-inference-api-and-widget-detined)). Đầu ra từ `trainr.push_to_hub())` là một URL tới hàm băm cam kết Git, vì vậy bạn có thể dễ dàng xem các thay đổi đã được thực hiện đối với kho lưu trữ mô hình!

Để kết thúc phần này, hãy xem cách chúng ta cũng có thể tinh chỉnh mT5 bằng cách sử dụng các tính năng cấp thấp do 🤗 Accelerate cung cấp.

{:else}

Chúng tôi gần như đã sẵn sàng để huấn luyện! Chúng ta chỉ cần chuyển đổi tập dữ liệu của mình thành `tf.data.Dataset` bằng cách sử dụng trình đối chiếu dữ liệu đã xác định ở trên, sau đó mô hình `compile()` và `fit()`. Đầu tiên, bộ dữ liệu:

```python
tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
    columns=["input_ids", "attention_mask", "labels"],
    collate_fn=data_collator,
    shuffle=True,
    batch_size=8,
)
tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset(
    columns=["input_ids", "attention_mask", "labels"],
    collate_fn=data_collator,
    shuffle=False,
    batch_size=8,
)
```

Bây giờ, chúng ta xác định các siêu tham số huấn luyện của mình và biên dịch:

```python
from transformers import create_optimizer
import tensorflow as tf

# Số bước huấn luyện là số lượng mẫu trong tập dữ liệu, chia cho kích thước lô sau đó nhân
# với tổng số epoch. Lưu ý rằng tf_train_dataset ở đây là tf.data.Dataset theo lô,
# không phải là Hugging Face Dataset ban đầu, vì vậy len() của nó vốn là num_samples // batch_size.

num_train_epochs = 8
num_train_steps = len(tf_train_dataset) * num_train_epochs
model_name = model_checkpoint.split("/")[-1]

optimizer, schedule = create_optimizer(
    init_lr=5.6e-5,
    num_warmup_steps=0,
    num_train_steps=num_train_steps,
    weight_decay_rate=0.01,
)

model.compile(optimizer=optimizer)

# Huấn luyện trong mixed-precision float16
tf.keras.mixed_precision.set_global_policy("mixed_float16")
```

And finally, we fit the model. We use a `PushToHubCallback` to save the model to the Hub after each epoch, which will allow us to use it for inference later:

```python
from transformers.keras_callbacks import PushToHubCallback

callback = PushToHubCallback(
    output_dir=f"{model_name}-finetuned-amazon-en-es", tokenizer=tokenizer
)

model.fit(
    tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback], epochs=8
)
```

Chúng ta nhận được một số giá trị mất mát trong quá trình huấn luyện, nhưng thực sự chúng ta muốn xem các chỉ số ROUGE mà ta đã tính toán trước đó. Để có được các số liệu đó, chúng tôi sẽ cần tạo kết quả đầu ra từ mô hình và chuyển đổi chúng thành chuỗi. Hãy xây dựng một số danh sách nhãn và dự đoán cho chỉ số ROUGE để so sánh (lưu ý rằng nếu bạn gặp lỗi nhập cho phần này, bạn có thể cần phải `!pip install tqdm`):

```python
from tqdm import tqdm
import numpy as np

all_preds = []
all_labels = []
for batch in tqdm(tf_eval_dataset):
    predictions = model.generate(**batch)
    decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
    labels = batch["labels"].numpy()
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
    decoded_preds = ["\n".join(sent_tokenize(pred.strip())) for pred in decoded_preds]
    decoded_labels = ["\n".join(sent_tokenize(label.strip())) for label in decoded_labels]
    all_preds.extend(decoded_preds)
    all_labels.extend(decoded_labels)
```

Khi chúng ta có danh sách các chuỗi nhãn và chuỗi dự đoán, việc tính toán điểm ROUGE thật dễ dàng:

```python
result = rouge_score.compute(
    predictions=decoded_preds, references=decoded_labels, use_stemmer=True
)
result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
{k: round(v, 4) for k, v in result.items()}
```

```
{'rouge1': 31.4815, 'rouge2': 25.4386, 'rougeL': 31.4815, 'rougeLsum': 31.4815}
```


{/if}

{#if fw === 'pt'}

## Tinh chỉnh mT5 với 🤗 Accelerate

Tinh chỉnh mô hình của chúng ta với 🤗 Accelerate rất giống với ví dụ phân loại văn bản mà chúng ta đã gặp trong [Chương 3](/course/chapter3). Sự khác biệt chính sẽ là nhu cầu tạo tóm tắt của chúng ta một cách rõ ràng trong quá trình huấn luyện và xác định cách chúng ta tính điểm ROUGE (nhớ lại rằng `Seq2SeqTrainer` đã làm cho chúng ta). Hãy xem cách chúng ta có thể thực hiện hai yêu cầu này trong 🤗 Accelerate!

### Chuẩn bị mọi thứ cho huấn luyện

Điều đầu tiên chúng ta cần làm là tạo một `DataLoader` cho mỗi phần tách của chúng ta. Vì các bộ lưu trữ dữ liệu PyTorch mong đợi hàng loạt các tensor, chúng ta cần đặt định dạng thành `"torch"` trong bộ dữ liệu của mình:

```python
tokenized_datasets.set_format("torch")
```

Bây giờ chúng ta đã có tập dữ liệu chỉ bao gồm các tensor, việc tiếp theo cần làm là khởi tạo lại `DataCollatorForSeq2Seq`. Đối với điều này, chúng ta cần cung cấp một phiên bản mới của mô hình, vì vậy hãy tải lại từ bộ nhớ cache của mình:

```python
model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

Sau đó, chúng ta có thể khởi tạo trình đối chiếu dữ liệu và sử dụng công cụ này để xác định bộ lưu dữ liệu của mình:

```python
from torch.utils.data import DataLoader

batch_size = 8
train_dataloader = DataLoader(
    tokenized_datasets["train"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=batch_size,
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], collate_fn=data_collator, batch_size=batch_size
)
```

Điều tiếp theo cần làm là xác định trình tối ưu hóa mà chúng ta muốn sử dụng. Như trong các ví dụ khác, chúng ta sẽ sử dụng `AdamW`, trình hoạt động tốt cho hầu hết các vấn đề:

```python
from torch.optim import AdamW

optimizer = AdamW(model.parameters(), lr=2e-5)
```

Cuối cùng, chúng ta cung cấp mô hình, trình tối ưu hóa và bộ ghi dữ liệu của mình vào phương thức `accelerator.prepare()`:

```python
from accelerate import Accelerator

accelerator = Accelerator()
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

<Tip>

🚨 Nếu bạn đang huấn luyện trên TPU, bạn sẽ cần chuyển tất cả đoạn mã ở trên vào một hàm huấn luyện chuyên dụng. Xem [Chương 3(/course/chapter3) để biết thêm chi tiết.

</Tip>

Bây giờ chúng ta đã chuẩn bị các đối tượng của mình, còn ba việc cần làm:

* Xác định lịch trình tốc độ học.
* Thực hiện chức năng hậu xử lý các bản tóm tắt để đánh giá.
* Tạo một kho lưu trữ trên Hub mà ta có thể đẩy mô hình của mình lên đó.

Đối với lịch trình tốc độ học, chúng ta sẽ sử dụng lịch trình tuyến tính tiêu chuẩn từ các phần trước:

```python
from transformers import get_scheduler

num_train_epochs = 10
num_update_steps_per_epoch = len(train_dataloader)
num_training_steps = num_train_epochs * num_update_steps_per_epoch

lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)
```

Để hậu xử lý, chúng ta cần một hàm chia các tóm tắt đã tạo thành các câu được phân tách bằng các dòng mới. Đây là định dạng mà chỉ số ROUGE mong đợi và chúng ta có thể đạt được điều này bằng đoạn mã sau:

```python
def postprocess_text(preds, labels):
    preds = [pred.strip() for pred in preds]
    labels = [label.strip() for label in labels]

    # ROUGE kì vọng dòng mới sau mỗi câu
    preds = ["\n".join(nltk.sent_tokenize(pred)) for pred in preds]
    labels = ["\n".join(nltk.sent_tokenize(label)) for label in labels]

    return preds, labels
```

Điều này sẽ trông quen thuộc với bạn nếu bạn nhớ lại cách chúng ta đã định nghĩa hàm `compute_metrics()` của `Seq2SeqTrainer`.

Cuối cùng, chúng ta cần tạo một kho lưu trữ mô hình trên Hugging Face Hub. Đối với điều này, chúng ta có thể sử dụng thư viện 🤗 Hub có tiêu đề thích hợp. Chúng ta chỉ cần xác định tên cho kho lưu trữ của mình và thư viện có chức năng tiện ích để kết hợp ID kho lưu trữ với hồ sơ người dùng:

```python
from huggingface_hub import get_full_repo_name

model_name = "test-bert-finetuned-squad-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'lewtun/mt5-finetuned-amazon-en-es-accelerate'
```

Bây giờ chúng ta có thể sử dụng tên kho lưu trữ này để sao chép phiên bản cục bộ vào thư mục kết quả của chúng ta, nơi sẽ lưu trữ các tạo tác huấn luyện:

```python
from huggingface_hub import Repository

output_dir = "results-mt5-finetuned-squad-accelerate"
repo = Repository(output_dir, clone_from=repo_name)
```

Điều này sẽ cho phép chúng ta đẩy các tạo tác trở lại Hub bằng cách gọi phương thức `repo.push_to_hub()` trong quá trình huấn luyện! Bây giờ chúng ta hãy kết thúc phân tích bằng cách viết ra vòng lặp huấn luyện.

### Vòng lặp huấn luyện

Vòng lặp huấn luyện để tóm tắt khá giống với các ví dụ 🤗 Accelerate khác mà chúng ta đã gặp và gần như được chia thành bốn bước chính:

1. Huấn luyện mô hình bằng cách lặp lại tất cả các ví dụ trong `train_dataloader` cho mỗi epoch.
2. Tạo tóm tắt mô hình vào cuối mỗi epoch, bằng cách tạo token đầu tiên và sau đó giải mã chúng (và tóm tắt tham chiếu) thành văn bản.
3. Tính toán điểm ROUGE bằng các kỹ thuật tương tự mà chúng ta đã thấy trước đó.
4. Lưu các checkpoint và đẩy mọi thứ vào Hub. Ở đây, chúng ta dựa vào tham số `blocking=False` tiện lợi của đối tượng `Repository` để có thể đẩy các checkpoint ở mỗi epoch _bất đồng bộ_. Điều này cho phép ta tiếp tục huấn luyện mà không phải đợi tải lên hơi chậm với mô hình có kích thước GB!

Các bước này có thể được nhìn thấy trong khối mã sau:

```python
from tqdm.auto import tqdm
import torch
import numpy as np

progress_bar = tqdm(range(num_training_steps))

for epoch in range(num_train_epochs):
    # Huấn luyện
    model.train()
    for step, batch in enumerate(train_dataloader):
        outputs = model(**batch)
        loss = outputs.loss
        accelerator.backward(loss)

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)

    # Đánh giá
    model.eval()
    for step, batch in enumerate(eval_dataloader):
        with torch.no_grad():
            generated_tokens = accelerator.unwrap_model(model).generate(
                batch["input_ids"],
                attention_mask=batch["attention_mask"],
            )

            generated_tokens = accelerator.pad_across_processes(
                generated_tokens, dim=1, pad_index=tokenizer.pad_token_id
            )
            labels = batch["labels"]

            # Nếu ta không đệm đến độ giải tối đa, ta cần đệm cả nhãn nữa
            labels = accelerator.pad_across_processes(
                batch["labels"], dim=1, pad_index=tokenizer.pad_token_id
            )

            generated_tokens = accelerator.gather(generated_tokens).cpu().numpy()
            labels = accelerator.gather(labels).cpu().numpy()

            # Thay -100 ở nhãn vì ta không thể  giải mã chúng
            labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
            if isinstance(generated_tokens, tuple):
                generated_tokens = generated_tokens[0]
            decoded_preds = tokenizer.batch_decode(
                generated_tokens, skip_special_tokens=True
            )
            decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)

            decoded_preds, decoded_labels = postprocess_text(
                decoded_preds, decoded_labels
            )

            rouge_score.add_batch(predictions=decoded_preds, references=decoded_labels)

    # Tính toán các chỉ số
    result = rouge_score.compute()
    # Trích xuất điểm trung vị ROUGE
    result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
    result = {k: round(v, 4) for k, v in result.items()}
    print(f"Epoch {epoch}:", result)

    # Lưu và tải
    accelerator.wait_for_everyone()
    unwrapped_model = accelerator.unwrap_model(model)
    unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
    if accelerator.is_main_process:
        tokenizer.save_pretrained(output_dir)
        repo.push_to_hub(
            commit_message=f"Training in progress epoch {epoch}", blocking=False
        )
```

```python out
Epoch 0: {'rouge1': 5.6351, 'rouge2': 1.1625, 'rougeL': 5.4866, 'rougeLsum': 5.5005}
Epoch 1: {'rouge1': 9.8646, 'rouge2': 3.4106, 'rougeL': 9.9439, 'rougeLsum': 9.9306}
Epoch 2: {'rouge1': 11.0872, 'rouge2': 3.3273, 'rougeL': 11.0508, 'rougeLsum': 10.9468}
Epoch 3: {'rouge1': 11.8587, 'rouge2': 4.8167, 'rougeL': 11.7986, 'rougeLsum': 11.7518}
Epoch 4: {'rouge1': 12.9842, 'rouge2': 5.5887, 'rougeL': 12.7546, 'rougeLsum': 12.7029}
Epoch 5: {'rouge1': 13.4628, 'rouge2': 6.4598, 'rougeL': 13.312, 'rougeLsum': 13.2913}
Epoch 6: {'rouge1': 12.9131, 'rouge2': 5.8914, 'rougeL': 12.6896, 'rougeLsum': 12.5701}
Epoch 7: {'rouge1': 13.3079, 'rouge2': 6.2994, 'rougeL': 13.1536, 'rougeLsum': 13.1194}
Epoch 8: {'rouge1': 13.96, 'rouge2': 6.5998, 'rougeL': 13.9123, 'rougeLsum': 13.7744}
Epoch 9: {'rouge1': 14.1192, 'rouge2': 7.0059, 'rougeL': 14.1172, 'rougeLsum': 13.9509}
```

Và thế đó! Khi bạn chạy nó, bạn sẽ có một mô hình và kết quả khá giống với những mô hình mà chúng ta thu được với `Trainer`.

{/if}

## Sử dụng mô hình tinh chỉnh của bạn

Khi bạn đã đẩy mô hình vào Hub, bạn có thể chơi với nó thông qua tiện ích luận suy hoặc với đối tượng `pipeline`, như sau:

```python
from transformers import pipeline

hub_model_id = "huggingface-course/mt5-small-finetuned-amazon-en-es"
summarizer = pipeline("summarization", model=hub_model_id)
```

Chúng ta có thể cung cấp một số mẫu từ bộ kiểm thử (mà mô hình chưa thấy) vào pipeline để có cảm nhận về chất lượng của các bản tóm tắt. Trước tiên, hãy triển khai một chức năng đơn giản để hiển thị bài đánh giá, tiêu đề và bản tóm tắt đã tạo cùng nhau:

```python
def print_summary(idx):
    review = books_dataset["test"][idx]["review_body"]
    title = books_dataset["test"][idx]["review_title"]
    summary = summarizer(books_dataset["test"][idx]["review_body"])[0]["summary_text"]
    print(f"'>>> Review: {review}'")
    print(f"\n'>>> Title: {title}'")
    print(f"\n'>>> Summary: {summary}'")
```

Hãy xem một trong những mẫu tiếng Anh mà chúng ta nhận được:

```python
print_summary(100)
```

```python out
'>>> Review: Nothing special at all about this product... the book is too small and stiff and hard to write in. The huge sticker on the back doesn’t come off and looks super tacky. I would not purchase this again. I could have just bought a journal from the dollar store and it would be basically the same thing. It’s also really expensive for what it is.'

'>>> Title: Not impressed at all... buy something else'

'>>> Summary: Nothing special at all about this product'
```

Điều này không quá tệ! Chúng ta có thể thấy rằng mô hình của mình đã thực sự có thể thực hiện tóm tắt _trừu tượng_ bằng cách tăng cường các phần của bài đánh giá bằng các từ mới. Và có lẽ khía cạnh thú vị nhất của mô hình của chúng ta là nó được sử dụng song ngữ, vì vậy ta cũng có thể tạo tóm tắt các bài đánh giá bằng tiếng Tây Ban Nha:

```python
print_summary(0)
```

```python out
'>>> Review: Es una trilogia que se hace muy facil de leer. Me ha gustado, no me esperaba el final para nada'

'>>> Title: Buena literatura para adolescentes'

'>>> Summary: Muy facil de leer'
```

Bản tóm tắt được dịch thành "Very easy to read" trong tiếng Anh, mà chúng ta có thể thấy trong trường hợp này được trích trực tiếp từ đánh giá. Tuy nhiên, điều này cho thấy tính linh hoạt của mô hình mT5 và cho bạn biết cảm giác triển khai với kho ngữ liệu đa ngôn ngữ là như thế nào!

Tiếp theo, chúng ta sẽ chuyển sự chú ý sang một tác vụ phức tạp hơn một chút: huấn luyện một mô hình ngôn ngữ từ đầu.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter7/5.mdx" />

### Giới thiệu
https://huggingface.co/learn/course/vi/chapter7/1.md

# Giới thiệu

Trong [Chương 3](/course/chapter3), bạn đã thấy cách tinh chỉnh một mô hình để phân loại văn bản. Trong chương này, chúng ta sẽ giải quyết các tác vụ NLP phổ biến sau:

- Phần loại token
- Mô hình ngôn ngữ bị ẩn đi (như BERT)
- Tóm tắt
- Dịch máy
- Mô hình ngôn ngữ nhân quả huấn luyện trước (như GPT-2)
- Hỏi đáp

{#if fw === 'pt'}

Để làm được điều này, bạn sẽ cần tận dụng mọi thứ bạn đã học về API `Trainer` và thư viện 🤗 Accelerate trong [Chương 3](/course/chapter3), thư viện 🤗 Datasets trong [Chapter 5](/course/chapter5), và thư viện 🤗 Tokenizers trong [Chương 6](/course/chap6). Chúng ta cũng sẽ tải kết quả của mình lên Model Hub, giống như đã làm trong [Chương 4](/course/chap4), vì vậy đây thực sự là chương mà mọi thứ kết hợp với nhau!

Mỗi phần có thể được đọc độc lập và sẽ chỉ cho bạn cách huấn luyện một mô hình bằng API `Trainer` hoặc với vòng huấn luyện của riêng bạn, sử dụng 🤗 Accelerate. Vui lòng bỏ qua một trong hai phần và tập trung vào phần mà bạn quan tâm nhất: API `Trainer` rất tuyệt vời để tinh chỉnh hoặc huấn luyện mô hình của bạn mà không cần lo lắng về những gì đang diễn ra ở phía sau, trong khi vòng huấn luyện với `Accelerate` sẽ cho phép bạn tùy chỉnh bất kỳ phần nào bạn muốn dễ dàng hơn.

{:else}

Để làm được điều này, bạn sẽ cần tận dụng mọi thứ bạn đã học về các mô hình huấn luyện với API Keras [trong [Chương 3](/course/chapter3), thư viện 🤗 Datasets trong [Chapter 5](/course/chapter5), và thư viện 🤗 Tokenizers trong [Chương 6](/course/chap6). Chúng ta cũng sẽ tải kết quả của mình lên Model Hub, giống như đã làm trong [Chương 4](/course/chap4), vì vậy đây thực sự là chương mà mọi thứ kết hợp với nhau!

Mỗi phần có thể được đọc độc lập.

{/if}

<Tip>

Nếu bạn đọc các phần theo trình tự, bạn sẽ nhận thấy rằng chúng có khá nhiều điểm chung về đoạn mã và văn xuôi mô tả. Việc lặp lại là có chủ đích, để cho phép bạn nhúng tay vào (hoặc quay lại sau) bất kỳ tác vụ nào mà bạn quan tâm và tìm thấy một ví dụ hoạt động hoàn chỉnh.

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter7/1.mdx" />

### Tinh chỉnh một mô hình ngôn ngữ bị ẩn đi
https://huggingface.co/learn/course/vi/chapter7/3.md

# Tinh chỉnh một mô hình ngôn ngữ bị ẩn đi

{#if fw === 'pt'}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section3_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section3_pt.ipynb"},
]} />

{:else}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section3_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section3_tf.ipynb"},
]} />

{/if}

Đối với nhiều ứng dụng NLP liên quan đến các mô hình Transformer, bạn có thể chỉ cần lấy một mô hình được huấn luyện trước từ Hugging Face Hub và tinh chỉnh trực tiếp trên dữ liệu của bạn cho tác vụ hiện tại. Với điều kiện là ngữ liệu được sử dụng để huấn luyện trước không quá khác biệt với ngữ liệu được sử dụng để tinh chỉnh, việc học chuyển tiếp thường sẽ mang lại kết quả tốt.

Tuy nhiên, có một vài trường hợp mà trước tiên bạn sẽ muốn tinh chỉnh các mô hình ngôn ngữ trên dữ liệu của mình, trước khi huấn luyện đầu tác vụ cụ thể. Ví dụ: nếu tập dữ liệu của bạn chứa các hợp đồng pháp lý hoặc các bài báo khoa học, thì mô hình thuần Transformer như BERT thường sẽ coi các từ chuyên môn trong kho dữ liệu của bạn là token hiếm và hiệu suất kết quả có thể kém hơn. Bằng cách tinh chỉnh mô hình ngôn ngữ trên dữ liệu chuyên môn, bạn có thể tăng hiệu suất của nhiều tác vụ xuôi dòng, có nghĩa là bạn thường chỉ phải thực hiện bước này một lần!

Quá trình tinh chỉnh mô hình ngôn ngữ được huấn luyện trước trên dữ liệu trong mảng này thường được gọi là _domain adapt_ hay _thích ứng chuyên môn_. Nó được phổ biến vào năm 2018 bởi [ULMFiT](https://arxiv.org/abs/1801.06146), là một trong những kiến ​​trúc mạng thần kinh đầu tiên (dựa trên LSTM) để làm cho việc học chuyển tiếp thực sự hiệu quả cho NLP. Một ví dụ về thích ứng chuyên môn với ULMFiT được hiển thị trong hình dưới đây; trong phần này, chúng ta sẽ làm điều tương tự, nhưng với Transformer thay vì LSTM!

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/ulmfit.svg" alt="ULMFiT."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/ulmfit-dark.svg" alt="ULMFiT."/>
</div>

Đến cuối phần này, bạn sẽ có một [mô hình ngôn ngữ bị ẩn đi](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.) trên Hub có thể tự động hoàn thiện câu như dưới đây:

<iframe src="https://course-demos-distilbert-base-uncased-finetune-7400b54.hf.space" frameBorder="0" height="300" title="Gradio app" class="block dark:hidden container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Cùng đi sâu vào thôi!

<Youtube id="mqElG5QJWUg"/>

<Tip>

🙋 Nếu các thuật ngữ "mô hình ngôn ngữ bị ẩn đi" và "mô hình huấn luyện trước" nghe có vẻ xa lạ với bạn, hãy xem [Chương 1](/course/chapter1), nơi chúng tôi giải thích tất cả các khái niệm cốt lõi này, kèm theo video!

</Tip>

## Chọn một mô hình huấn luyện trước cho mô hình ngôn ngữ bị ẩn đi

Để bắt đầu, hãy chọn một mô hình được huấn luyện trước phù hợp để tạo mô hình ngôn ngữ bị ẩn đi. Như được hiển thị trong ảnh chụp màn hình dưới đây, bạn có thể tìm thấy danh sách các ứng cử viên bằng cách áp dụng bộ lọc "Fill-Mask" trên [Hugging Face Hub](https://huggingface.co/models?pipeline_tag=fill-mask&sort=downloads):

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/mlm-models.png" alt="Hub models." width="80%"/>
</div>

Mặc dù dòng mô hình BERT và RoBERTa được tải xuống nhiều nhất, chúng ta sẽ sử dụng mô hình có tên [DistilBERT](https://huggingface.co/distilbert-base-uncased)
có thể huấn luyện nhanh hơn nhiều mà ít hoặc không bị mất hiệu suất. Mô hình này được huấn luyện bằng cách sử dụng một kỹ thuật đặc biệt có tên là [_knowledge distillation_](https://en.wikipedia.org/wiki/Knowledge_distillation), trong đó một "mô hình giáo viên" lớn như BERT được sử dụng để hướng dẫn huấn luyện "mô hình sinh viên" có ít tham số hơn nhiều. Phần giải thích chi tiết về quá trình chắt lọc kiến ​​thức sẽ đưa chúng ta đi quá xa trong phần này, nhưng nếu bạn quan tâm, bạn có thể đọc tất cả về nó trong [_Natural Language Processing with Transformers_](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/) (thường được gọi là sách giáo khoa về Transformer).

{#if fw === 'pt'}

Hãy tiếp tục và tải xuống DistilBERT bằng cách sử dụng lớp `AutoModelForMaskedLM`:

```python
from transformers import AutoModelForMaskedLM

model_checkpoint = "distilbert-base-uncased"
model = AutoModelForMaskedLM.from_pretrained(model_checkpoint)
```

Chúng ta có thể xem mô hình này có bao nhiêu tham số bằng cách gọi phương thức `num_parameters()`:

```python
distilbert_num_parameters = model.num_parameters() / 1_000_000
print(f"'>>> DistilBERT number of parameters: {round(distilbert_num_parameters)}M'")
print(f"'>>> BERT number of parameters: 110M'")
```

```python out
'>>> DistilBERT number of parameters: 67M'
'>>> BERT number of parameters: 110M'
```

{:else}

Hãy tiếp tục và tải xuống DistilBERT bằng cách sử dụng lớp `AutoModelForMaskedLM`:

```python
from transformers import TFAutoModelForMaskedLM

model_checkpoint = "distilbert-base-uncased"
model = TFAutoModelForMaskedLM.from_pretrained(model_checkpoint)
```

Chúng ta có thể xem mô hình này có bao nhiêu tham số bằng cách gọi phương thức `summary()`:

```python
model(model.dummy_inputs)  # Xây dựng mô hình
model.summary()
```

```python out
Model: "tf_distil_bert_for_masked_lm"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
distilbert (TFDistilBertMain multiple                  66362880  
_________________________________________________________________
vocab_transform (Dense)      multiple                  590592    
_________________________________________________________________
vocab_layer_norm (LayerNorma multiple                  1536      
_________________________________________________________________
vocab_projector (TFDistilBer multiple                  23866170  
=================================================================
Total params: 66,985,530
Trainable params: 66,985,530
Non-trainable params: 0
_________________________________________________________________
```

{/if}

Với khoảng 67 triệu tham số, DistilBERT nhỏ hơn khoảng hai lần so với mô hình cơ sở BERT, gần như được hiểu là tăng tốc gấp hai lần khi huấn luyện - thật tuyệt! Bây giờ chúng ta hãy xem những loại token nào mô hình này dự đoán là có nhiều khả năng hoàn thành một mẫu văn bản nhỏ:

```python
text = "This is a great [MASK]."
```

Là con người, chúng ta có thể tưởng tượng ra nhiều khả năng đối với token `[MASK]`, ví dụ "day", "ride", hoặc "painting". Đối với các mô hình được huấn luyện trước, các dự đoán phụ thuộc vào kho ngữ liệu mô hình đó huấn luyện, vì nó học cách chọn các mẫu thống kê có trong dữ liệu. Giống BERT, DistilBERT được huấn luyện trước trên bộ dữ liệu [English Wikipedia](https://huggingface.co/datasets/wikipedia) và [BookCorpus](https://huggingface.co/datasets/bookcorpus), nên ta kì vọng các dự đoán cho `[MASK]` sẽ phản ánh các mảng này. Để dự đoán ta cần trình tokenizer của DistilBERT tạo ra các đầu vào cho mô hình, vì vậy hãy tải từ Hub:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
```

Với một tokenizer và một mô hình, ta có thể truyền các đoạn văn ví dụ tới mô hình, trích xuất logits, và xin ra 5 ứng cử viên: 

{#if fw === 'pt'}

```python
import torch

inputs = tokenizer(text, return_tensors="pt")
token_logits = model(**inputs).logits
# Tìm vị trí [MASK] và trích xuất logit
mask_token_index = torch.where(inputs["input_ids"] == tokenizer.mask_token_id)[1]
mask_token_logits = token_logits[0, mask_token_index, :]
# Chọn ứng viên cho [MASK] với logit cao nhất
top_5_tokens = torch.topk(mask_token_logits, 5, dim=1).indices[0].tolist()

for token in top_5_tokens:
    print(f"'>>> {text.replace(tokenizer.mask_token, tokenizer.decode([token]))}'")
```

{:else}

```python
import numpy as np
import tensorflow as tf

inputs = tokenizer(text, return_tensors="np")
token_logits = model(**inputs).logits
# Tìm vị trí [MASK] và trích xuất logit
mask_token_index = np.argwhere(inputs["input_ids"] == tokenizer.mask_token_id)[0, 1]
mask_token_logits = token_logits[0, mask_token_index, :]
# PChọn ứng viên cho [MASK] với logit cao nhất
# Chúng ta phủ định mảng trước argsort để lấy logits lớn nhất chứ không phải nhỏ nhất
top_5_tokens = np.argsort(-mask_token_logits)[:5].tolist()

for token in top_5_tokens:
    print(f">>> {text.replace(tokenizer.mask_token, tokenizer.decode([token]))}")
```

{/if}

```python out
'>>> This is a great deal.'
'>>> This is a great success.'
'>>> This is a great adventure.'
'>>> This is a great idea.'
'>>> This is a great feat.'
```

Chúng ta có thể thấy từ kết quả đầu ra rằng các dự đoán của mô hình đề cập đến các thuật ngữ hàng ngày, điều này có lẽ không có gì đáng ngạc nhiên khi dựa trên nền tảng của Wikipedia tiếng Anh. Hãy xem cách chúng ta có thể thay đổi mảng này thành một thứ gì đó thích hợp hơn một chút - các bài đánh giá phim phân cực cao!

## Bộ dữ liệu

Để giới thiệu việc thích ứng chuyên môn, chúng ta sẽ sử dụng bộ dữ liệu nổi tiếng [Large Movie Review Dataset](https://huggingface.co/datasets/imdb)(hay viết tắt là IMDb), là tập hợp các bài đánh giá phim thường được dùng để đánh giá các mô hình phân tích cảm xúc. Bằng cách tinh chỉnh DistilBERT trên kho ngữ liệu này, chúng ta hy vọng mô hình ngôn ngữ sẽ điều chỉnh vốn từ vựng của nó từ dữ liệu thực tế của Wikipedia mà nó đã được huấn luyện trước để phù hợp với các yếu tố chủ quan hơn của các bài đánh giá phim. Chúng ta có thể lấy dữ liệu từ Hugging Face Hub bằng hàm `load_dataset()` từ 🤗 Datasets:

```python
from datasets import load_dataset

imdb_dataset = load_dataset("imdb")
imdb_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['text', 'label'],
        num_rows: 25000
    })
    test: Dataset({
        features: ['text', 'label'],
        num_rows: 25000
    })
    unsupervised: Dataset({
        features: ['text', 'label'],
        num_rows: 50000
    })
})
```

Chúng ta có thể thấy rằng mỗi phần `huấn luyện` và `kiểm thử` bao gồm 25,000 đánh giá, trong khi phần không được gắn nhãn được gọi là `phi giám sát` chứa 50,000 đánh giá. Chúng ta hãy xem một vài mẫu để có ý tưởng về loại văn bản mà ta đang xử lý. Như chúng ta đã thực hiện trong các chương trước của khóa học, chúng ta sẽ xâu chuỗi các hàm `Dataset.shuffle()` và `Dataset.select()` để tạo một mẫu ngẫu nhiên:

```python
sample = imdb_dataset["train"].shuffle(seed=42).select(range(3))

for row in sample:
    print(f"\n'>>> Review: {row['text']}'")
    print(f"'>>> Label: {row['label']}'")
```

```python out

'>>> Review: This is your typical Priyadarshan movie--a bunch of loony characters out on some silly mission. His signature climax has the entire cast of the film coming together and fighting each other in some crazy moshpit over hidden money. Whether it is a winning lottery ticket in Malamaal Weekly, black money in Hera Pheri, "kodokoo" in Phir Hera Pheri, etc., etc., the director is becoming ridiculously predictable. Don\'t get me wrong; as clichéd and preposterous his movies may be, I usually end up enjoying the comedy. However, in most his previous movies there has actually been some good humor, (Hungama and Hera Pheri being noteworthy ones). Now, the hilarity of his films is fading as he is using the same formula over and over again.<br /><br />Songs are good. Tanushree Datta looks awesome. Rajpal Yadav is irritating, and Tusshar is not a whole lot better. Kunal Khemu is OK, and Sharman Joshi is the best.'
'>>> Label: 0'

'>>> Review: Okay, the story makes no sense, the characters lack any dimensionally, the best dialogue is ad-libs about the low quality of movie, the cinematography is dismal, and only editing saves a bit of the muddle, but Sam" Peckinpah directed the film. Somehow, his direction is not enough. For those who appreciate Peckinpah and his great work, this movie is a disappointment. Even a great cast cannot redeem the time the viewer wastes with this minimal effort.<br /><br />The proper response to the movie is the contempt that the director San Peckinpah, James Caan, Robert Duvall, Burt Young, Bo Hopkins, Arthur Hill, and even Gig Young bring to their work. Watch the great Peckinpah films. Skip this mess.'
'>>> Label: 0'

'>>> Review: I saw this movie at the theaters when I was about 6 or 7 years old. I loved it then, and have recently come to own a VHS version. <br /><br />My 4 and 6 year old children love this movie and have been asking again and again to watch it. <br /><br />I have enjoyed watching it again too. Though I have to admit it is not as good on a little TV.<br /><br />I do not have older children so I do not know what they would think of it. <br /><br />The songs are very cute. My daughter keeps singing them over and over.<br /><br />Hope this helps.'
'>>> Label: 1'
```

Đúng, đây chắc chắn là những bài đánh giá phim, và nếu bạn đủ lớn, bạn thậm chí có thể hiểu nhận xét trong bài đánh giá cuối cùng về việc sở hữu phiên bản VHS 😜! Mặc dù chúng ta sẽ không cần nhãn để cho mô hình ngôn ngữ, nhưng chúng ta có thể thấy rằng `0` biểu thị một đánh giá tiêu cực, trong khi `1` tương ứng với một đánh giá tích cực.

<Tip>

✏️ **Thử nghiệm thôi!** Tạo ra các mẫu ngẫu nhiền từ phần `phi giám sát` và kiểm định xem nhãn của chúng là `0` hay `1`. Khi đang ở đó, bạn cũng có thể kiểm tra xem các nhãn trong phần `huấn luyện` và `kiểm thử` có thực sử là `0` hoặc `1` không -- đây là một phần kiểm tra hữu ích mànhững nhà NLP nên thực hiện đầu dự án!. 

</Tip>

Bây giờ chúng ta đã có một cái nhìn nhanh về dữ liệu, hãy đi sâu vào việc chuẩn bị nó cho việc lập mô hình ngôn ngữ bị ẩn đi. Như chúng ta sẽ thấy, có một số bước bổ sung mà người ta cần thực hiện so với các tác vụ phân loại chuỗi mà chúng ta đã thấy trong [Chương 3](/course/chapter3). Đi thôi!

## Tiền xử lý dữ liệu

<Youtube id="8PmhEIXhBvI"/>

Đối với cả mô hình ngôn ngữ tự động hồi quy và bị ẩn đi, một bước tiền xử lý phổ biến là nối tất cả các mẫu và sau đó chia toàn bộ ngữ liệu thành các phần có kích thước bằng nhau. Điều này hoàn toàn khác với cách tiếp cận thông thường, khi chúng ta chỉ cần tokenize các mẫu riêng lẻ. Tại sao lại nối mọi thứ lại với nhau? Lý do là các mẫu riêng lẻ có thể bị cắt ngắn nếu chúng quá dài và điều đó sẽ dẫn đến việc mất thông tin có thể hữu ích cho tác vụ mô hình hóa ngôn ngữ!

Vì vậy, để bắt đầu, trước tiên chúng ta sẽ tokenize kho tài liệu của mình như bình thường, nhưng _không_ đặt tùy chọn `truncation=True` trong trình tokenize của chúng ta. Chúng ta cũng sẽ lấy các ID từ nếu chúng có sẵn ((chúng sẽ có sẵn nếu ta đang sử dụng công cụ tokenize nhanh, như được mô tả trong [Chương 6](/course/chap6/3)), vì ta sẽ cần chúng sau này để thực hiện che toàn bộ từ. Chúng ta sẽ gói nó trong một hàm đơn giản và trong khi thực hiện, ta sẽ xóa các cột `text` và `label` vì không cần chúng nữa:

```python
def tokenize_function(examples):
    result = tokenizer(examples["text"])
    if tokenizer.is_fast:
        result["word_ids"] = [result.word_ids(i) for i in range(len(result["input_ids"]))]
    return result


# Dùng batched=True để kích hoạt đa luồng nhanh!
tokenized_datasets = imdb_dataset.map(
    tokenize_function, batched=True, remove_columns=["text", "label"]
)
tokenized_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['attention_mask', 'input_ids', 'word_ids'],
        num_rows: 25000
    })
    test: Dataset({
        features: ['attention_mask', 'input_ids', 'word_ids'],
        num_rows: 25000
    })
    unsupervised: Dataset({
        features: ['attention_mask', 'input_ids', 'word_ids'],
        num_rows: 50000
    })
})
```

Vì DistilBERT là một mô hình giống như BERT, chúng ta có thể thấy rằng các văn bản được tokenize bao gồm `input_ids` và `attention_mask` ta đã thấy trong các chương khác, cũng như `word_ids` mà ta đã thêm vào.

Gờ chúng ta đã tokenize các bài đánh giá phim của mình, bước tiếp theo là nhóm tất cả chúng lại với nhau và chia kết quả thành nhiều phần. Nhưng những khối này phải lớn đến mức nào? Điều này cuối cùng sẽ được xác định bởi dung lượng bộ nhớ GPU mà bạn có sẵn, nhưng điểm khởi đầu tốt là xem kích thước ngữ cảnh tối đa của mô hình là bao nhiêu. Điều này có thể được suy ra bằng cách kiểm tra thuộc tính `model_max_length` của tokenizer:

```python
tokenizer.model_max_length
```

```python out
512
```

Giá trị này có nguồn gốc từ tệp *tokenizer_config.json* được liên kết với một checkpoint; trong trường hợp này, chúng ta có thể thấy rằng kích thước ngữ cảnh là 512 token, giống như với BERT.

<Tip>

✏️ **Thử nghiệm thôi!** Một số mô hình Transformer, như[BigBird](https://huggingface.co/google/bigbird-roberta-base) và [Longformer](hf.co/allenai/longformer-base-4096),có độ dài ngữ cảnh dài hơn nhiều so với BERT và các mô hình Transformer đời đầu khác. Khởi tạo tokenizer cho một trong những checkpoint và xác minh rằng `model_max_length` tương ứng với những gì được trích dẫn trên thẻ mô hình của nó.

</Tip>

Vì vậy, để chạy các thử nghiệm trên GPU như những GPU được tìm thấy trên Google Colab, chúng ta sẽ chọn thứ gì đó nhỏ hơn một chút có thể vừa với bộ nhớ:

```python
chunk_size = 128
```

<Tip warning={true}>

Lưu ý rằng việc sử dụng kích thước phân đoạn nhỏ có thể gây bất lợi trong các tình huống thực tế, vì vậy bạn nên sử dụng kích thước tương ứng với trường hợp sử dụng mà bạn sẽ áp dụng mô hình của mình.

</Tip>

Bây giờ đến phần thú vị. Để cho biết cách nối hoạt động, hãy lấy một vài bài đánh giá từ bộ huấn luyện được tokenize và in ra số lượng token cho mỗi bài đánh giá:

```python
# Tạo ra một danh sách các danh sách cho từng đặc trưng
tokenized_samples = tokenized_datasets["train"][:3]

for idx, sample in enumerate(tokenized_samples["input_ids"]):
    print(f"'>>> Review {idx} length: {len(sample)}'")
```

```python out
'>>> Review 0 length: 200'
'>>> Review 1 length: 559'
'>>> Review 2 length: 192'
```

We can then concatenate all these examples with a simple dictionary comprehension, as follows:

```python
concatenated_examples = {
    k: sum(tokenized_samples[k], []) for k in tokenized_samples.keys()
}
total_length = len(concatenated_examples["input_ids"])
print(f"'>>> Concatenated reviews length: {total_length}'")
```

```python out
'>>> Concatenated reviews length: 951'
```

Tuyệt vời, tổng độ dài đã được kiểm tra - vì vậy bây giờ hãy chia các bài đánh giá được nối thành các phần có kích thước được cung cấp bởi `block_size`. Để làm như vậy, chúng ta lặp qua các đặc trưng trong `concatenated_examples` và sử dụng khả năng hiểu danh sách để tạo các phần của từng đặc trưng. Kết quả là một từ điển các khối cho từng đặc trưng:

```python
chunks = {
    k: [t[i : i + chunk_size] for i in range(0, total_length, chunk_size)]
    for k, t in concatenated_examples.items()
}

for chunk in chunks["input_ids"]:
    print(f"'>>> Chunk length: {len(chunk)}'")
```

```python out
'>>> Chunk length: 128'
'>>> Chunk length: 128'
'>>> Chunk length: 128'
'>>> Chunk length: 128'
'>>> Chunk length: 128'
'>>> Chunk length: 128'
'>>> Chunk length: 128'
'>>> Chunk length: 55'
```

Như bạn có thể thấy trong ví dụ này, đoạn cuối thường sẽ nhỏ hơn kích thước đoạn tối đa. Có hai chiến lược chính để giải quyết vấn đề này:

* Bỏ đoạn cuối cùng nếu nó nhỏ hơn `chunk_size`.
* Đệm đoạn cuối cùng cho đến khi độ dài của nó bằng `chunk_size`.

Chúng tôi sẽ thực hiện cách tiếp cận đầu tiên ở đây, vì vậy hãy gói tất cả logic ở trên trong một hàm duy nhất mà chúng tôi có thể áp dụng cho tập dữ liệu được tokenize của mình:


```python
def group_texts(examples):
    # Nối tất cả các văn bản
    concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()}
    # Tính độ dài của các văn bản được nối
    total_length = len(concatenated_examples[list(examples.keys())[0]])
    # Chúng tôi bỏ đoạn cuối cùng nếu nó nhỏ hơn chunk_size
    total_length = (total_length // chunk_size) * chunk_size
    # Chia phần theo max_len
    result = {
        k: [t[i : i + chunk_size] for i in range(0, total_length, chunk_size)]
        for k, t in concatenated_examples.items()
    }
    # Tạo cột nhãn mới
    result["labels"] = result["input_ids"].copy()
    return result
```

Lưu ý rằng trong bước cuối cùng của `group_texts()`, chúng ta tạo một cột mới `labels` là bản sao của cột `input_ids`. Như chúng ta sẽ thấy ngay sau đây, đó là bởi vì trong mô hình ngôn ngữ bị ẩn đi, mục tiêu là dự đoán các token được che ngẫu nhiên trong lô đầu vào và bằng cách tạo cột `labels`, chúng ta cung cấp sự thật cơ bản cho mô hình ngôn ngữ để học hỏi.

Bây giờ, hãy áp dụng `group_texts()` cho các tập dữ liệu được tokenize của mình bằng cách sử dụng hàm `Dataset.map()` đáng tin cậy:

```python
lm_datasets = tokenized_datasets.map(group_texts, batched=True)
lm_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
        num_rows: 61289
    })
    test: Dataset({
        features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
        num_rows: 59905
    })
    unsupervised: Dataset({
        features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
        num_rows: 122963
    })
})
```

Bạn có thể thấy rằng việc nhóm và sau đó phân chia các đoạn văn bản đã tạo ra nhiều mẫu hơn so với 25,000 mẫu ban đầu của chúng ta cho phần tách `huấn luyện` và `kiểm thử`. Đó là bởi vì chúng ta hiện có các mẫu liên quan đến _token liên tục_ trải dài trên nhiều mẫu từ kho tài liệu gốc. Bạn có thể thấy điều này một cách rõ ràng bằng cách tìm kiếm các token đặc biệt `[SEP]` và `[CLS]` trong một trong các phần:

```python
tokenizer.decode(lm_datasets["train"][1]["input_ids"])
```

```python out
".... at.......... high. a classic line : inspector : i'm here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn't! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless"
```

Trong ví dụ này, bạn có thể thấy hai bài đánh giá phim trùng nhau, một bài về phim cấp ba và bài còn lại về tình trạng vô gia cư. Hãy cũng xem các nhãn trông như thế nào cho mô hình ngôn ngữ bị ẩn đi:

```python out
tokenizer.decode(lm_datasets["train"][1]["labels"])
```

```python out
".... at.......... high. a classic line : inspector : i'm here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn't! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless"
```

Như mong đợi từ hàm `group_texts()` của chúng ta ở trên, hàm này trông giống hệt với `input_ids` đã được giải mã - nhưng sau đó làm thế nào để mô hình của chúng ta có thể học được bất cứ điều gì? Chúng ta đang thiếu một bước quan trọng: chèn token `[MASK]` ở các vị trí ngẫu nhiên trong đầu vào! Hãy xem cách chúng ta có thể thực hiện điều này một cách nhanh chóng trong quá trình tinh chỉnh bằng công cụ đối chiếu dữ liệu đặc biệt.

## Tinh chỉnh DistilBERT với API `Trainer`

Tinh chỉnh mô hình ngôn ngữ bị ẩn đi gần giống như tinh chỉnh mô hình phân loại chuỗi, giống như chúng ta đã làm trong [Chương 3](/course/chapter3). Sự khác biệt duy nhất là chúng ta cần một trình đối chiếu dữ liệu đặc biệt có thể che giấu ngẫu nhiên một số token trong mỗi lô văn bản. May mắn thay, 🤗 Transformers được chuẩn bị với một `DataCollatorForLanguageModeling` dành riêng cho tác vụ này. Chúng ta chỉ cần chuyển nó vào tokenizer và tham số `mlm_probability` để chỉ định phần nào trong số các token cần che. Chúng tôi sẽ chọn 15%, là số được sử dụng cho BERT và một lựa chọn phổ biến trong các tài liệu:

```python
from transformers import DataCollatorForLanguageModeling

data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm_probability=0.15)
```

Để xem cách hoạt động của việc che ngẫu nhiên, hãy cung cấp một vài mẫu cho trình đối chiếu dữ liệu. Vì nó mong đợi một danh sách các `dict`, trong đó mỗi `dict` đại diện cho một đoạn văn bản liền kề, đầu tiên chúng ta lặp tập dữ liệu trước khi cung cấp lô cho bộ đối chiếu. Chúng ta xóa khóa `"word_ids"` cho trình đối chiếu dữ liệu này vì nó không cần chúng:

```python
samples = [lm_datasets["train"][i] for i in range(2)]
for sample in samples:
    _ = sample.pop("word_ids")

for chunk in data_collator(samples)["input_ids"]:
    print(f"\n'>>> {tokenizer.decode(chunk)}'")
```

```python output
'>>> [CLS] bromwell [MASK] is a cartoon comedy. it ran at the same [MASK] as some other [MASK] about school life, [MASK] as " teachers ". [MASK] [MASK] [MASK] in the teaching [MASK] lead [MASK] to believe that bromwell high\'[MASK] satire is much closer to reality than is " teachers ". the scramble [MASK] [MASK] financially, the [MASK]ful students whogn [MASK] right through [MASK] pathetic teachers\'pomp, the pettiness of the whole situation, distinction remind me of the schools i knew and their students. when i saw [MASK] episode in [MASK] a student repeatedly tried to burn down the school, [MASK] immediately recalled. [MASK]...'

'>>> .... at.. [MASK]... [MASK]... high. a classic line plucked inspector : i\'[MASK] here to [MASK] one of your [MASK]. student : welcome to bromwell [MASK]. i expect that many adults of my age think that [MASK]mwell [MASK] is [MASK] fetched. what a pity that it isn\'t! [SEP] [CLS] [MASK]ness ( or [MASK]lessness as george 宇in stated )公 been an issue for years but never [MASK] plan to help those on the street that were once considered human [MASK] did everything from going to school, [MASK], [MASK] vote for the matter. most people think [MASK] the homeless'
```

Tốt, nó đã hoạt động! Chúng ta có thể thấy rằng `[MASK]` đã được chèn ngẫu nhiên tại các vị trí khác nhau trong văn bản. Đây sẽ là những token mà mô hình sẽ phải dự đoán trong quá trình huấn luyện - và cái hay của công cụ đối chiếu dữ liệu là nó sẽ ngẫu nhiên chèn `[MASK]` với mọi lô!

<Tip>

✏️ **Thử nghiệm thôi!** Chạy đoạn mã trên vài lần để xem việc che ngẫu nhiên diễn ra ngay trước mắt bạn! Đồng thời  thử thay thế phương thức `tokenizer.decode()` bằng `tokenizer.convert_ids_to_tokens()` để thấy rằng đôi khi một token từ một từ nhất định bị che, chứ không phải những cái khác.

</Tip>

{#if fw === 'pt'}

Một tác dụng phụ của việc che ngẫu nhiên là các chỉ số đánh giá của chúng ta sẽ không xác định khi sử dụng `Trainer`, vì chúng ta sử dụng cùng một công cụ đối chiếu dữ liệu cho các tập huấn luyện và kiểm thủ. Chúng ta sẽ thấy ở phần sau, khi chúng ta xem xét việc tinh chỉnh với 🤗 Accelerate, chúng ta có thể sử dụng tính linh hoạt của vòng đánh giá tùy chỉnh như thế nào để đóng băng tính ngẫu nhiên.

{/if}

Khi huấn luyện các mô hình để tạo mô hình ngôn ngữ bị ẩn đi, một kỹ thuật có thể được sử dụng là ghép các từ lại với nhau, không chỉ các token riêng lẻ. Cách tiếp cận này được gọi là _whole word masking_ hay _che toàn bộ từ_. Nếu chúng ta muốn che toàn bộ từ, chúng ta sẽ cần phải tự xây dựng một bộ đối chiếu dữ liệu. Bộ đối chiếu dữ liệu chỉ là một chức năng lấy danh sách các mẫu và chuyển đổi chúng thành một lô, vì vậy hãy làm điều này ngay bây giờ! Chúng ta sẽ sử dụng các ID từ đã tính toán trước đó để tạo bản đồ giữa các chỉ số từ và các mã thông báo tương ứng, sau đó quyết định ngẫu nhiên những từ nào cần che và che các đầu vào. Lưu ý rằng tất cả các nhãn đều là `-100` ngoại trừ các nhãn tương ứng với các từ bị che.

{#if fw === 'pt'}

```py
import collections
import numpy as np

from transformers import default_data_collator

wwm_probability = 0.2


def whole_word_masking_data_collator(features):
    for feature in features:
        word_ids = feature.pop("word_ids")

        # Tạo ra ánh xạ giữa các từ và chỉ mục token tương ứng
        mapping = collections.defaultdict(list)
        current_word_index = -1
        current_word = None
        for idx, word_id in enumerate(word_ids):
            if word_id is not None:
                if word_id != current_word:
                    current_word = word_id
                    current_word_index += 1
                mapping[current_word_index].append(idx)

        # Che ngẫu nhiền từ
        mask = np.random.binomial(1, wwm_probability, (len(mapping),))
        input_ids = feature["input_ids"]
        labels = feature["labels"]
        new_labels = [-100] * len(labels)
        for word_id in np.where(mask)[0]:
            word_id = word_id.item()
            for idx in mapping[word_id]:
                new_labels[idx] = labels[idx]
                input_ids[idx] = tokenizer.mask_token_id
        feature["labels"] = new_labels

    return default_data_collator(features)
```

{:else}

```py
import collections
import numpy as np

from transformers.data.data_collator import tf_default_data_collator

wwm_probability = 0.2


def whole_word_masking_data_collator(features):
    for feature in features:
        word_ids = feature.pop("word_ids")

        # Tạo ra ánh xạ giữa các từ và chỉ mục token tương ứng
        mapping = collections.defaultdict(list)
        current_word_index = -1
        current_word = None
        for idx, word_id in enumerate(word_ids):
            if word_id is not None:
                if word_id != current_word:
                    current_word = word_id
                    current_word_index += 1
                mapping[current_word_index].append(idx)

        # Che ngẫu nhiền từ
        mask = np.random.binomial(1, wwm_probability, (len(mapping),))
        input_ids = feature["input_ids"]
        labels = feature["labels"]
        new_labels = [-100] * len(labels)
        for word_id in np.where(mask)[0]:
            word_id = word_id.item()
            for idx in mapping[word_id]:
                new_labels[idx] = labels[idx]
                input_ids[idx] = tokenizer.mask_token_id
        feature["labels"] = new_labels

    return tf_default_data_collator(features)
```

{/if}

Tiếp theo, ta cso thể thử trên một vài mẫu như trên:

```py
samples = [lm_datasets["train"][i] for i in range(2)]
batch = whole_word_masking_data_collator(samples)

for chunk in batch["input_ids"]:
    print(f"\n'>>> {tokenizer.decode(chunk)}'")
```

```python out
'>>> [CLS] bromwell high is a cartoon comedy [MASK] it ran at the same time as some other programs about school life, such as " teachers ". my 35 years in the teaching profession lead me to believe that bromwell high\'s satire is much closer to reality than is " teachers ". the scramble to survive financially, the insightful students who can see right through their pathetic teachers\'pomp, the pettiness of the whole situation, all remind me of the schools i knew and their students. when i saw the episode in which a student repeatedly tried to burn down the school, i immediately recalled.....'

'>>> .... [MASK] [MASK] [MASK] [MASK]....... high. a classic line : inspector : i\'m here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn\'t! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless'
```

<Tip>

✏️ **Thử nghiệm thôi!** Chạy đoạn mã trên vài lần để xem việc che ngẫu nhiên diễn ra ngay trước mắt bạn! Đồng thời  thử thay thế phương thức `tokenizer.decode()` bằng `tokenizer.convert_ids_to_tokens()` để thấy rằng đôi khi một token từ một từ nhất định bị che, chứ không phải những cái khác.

</Tip>

Giờ chúng ta có hai trình đối chiếu dữ liệu, phần còn lại của các bước tinh chỉnh là tiêu chuẩn. Quá trình huấn luyện có thể mất một khoảng thời gian trên Google Colab nếu bạn không đủ may mắn để đạt được GPU P100 thần thoại 😭, vì vậy, trước tiên chúng ta sẽ giảm kích thước của tập huấn luyện xuống còn vài nghìn mẫu. Đừng lo lắng, chúng ta sẽ vẫn nhận được một mô hình ngôn ngữ khá tốt! Một cách nhanh chóng để giảm mẫu một tập dữ liệu trong 🤗 Datasets là thông qua hàm `Dataset.train_test_split()` mà chúng ta đã thấy trong [Chapter 5](/course/chapter5):

```python
train_size = 10_000
test_size = int(0.1 * train_size)

downsampled_dataset = lm_datasets["train"].train_test_split(
    train_size=train_size, test_size=test_size, seed=42
)
downsampled_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
        num_rows: 10000
    })
    test: Dataset({
        features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
        num_rows: 1000
    })
})
```

Điều này đã tự động tạo các phần tách `huấn luyện` và `kiểm thử` mới, với kích thước tập huấn luyện được đặt thành 10,000 mẫu và xác thực được đặt thành 10% - vui lòng tăng điều này nếu bạn có GPU mạnh! Điều tiếp theo chúng ta cần làm là đăng nhập vào Hugging Face Hub. Nếu bạn đang chạy mã này trong notebook, bạn có thể làm như vậy với chức năng tiện ích sau:

```python
from huggingface_hub import notebook_login

notebook_login()
```

sẽ hiển thị một tiện ích mà bạn có thể nhập thông tin đăng nhập của mình. Ngoài ra, bạn có thể chạy:

```
huggingface-cli login
```

trong thiết bị đầu cuối yêu thích của bạn và đăng nhập ở đó.

{#if fw === 'tf'}

Khi đã đăng nhập, chúng ta có thể tạo tập dữ liệu `tf.data` của mình. Chúng tôi sẽ chỉ sử dụng trình đối chiếu dữ liệu tiêu chuẩn ở đây, nhưng bạn cũng có thể thử trình đối chiếu che toàn bộ từ và so sánh kết quả như một bài tập:

```python
tf_train_dataset = downsampled_dataset["train"].to_tf_dataset(
    columns=["input_ids", "attention_mask", "labels"],
    collate_fn=data_collator,
    shuffle=True,
    batch_size=32,
)

tf_eval_dataset = downsampled_dataset["test"].to_tf_dataset(
    columns=["input_ids", "attention_mask", "labels"],
    collate_fn=data_collator,
    shuffle=False,
    batch_size=32,
)
```

Tiếp theo, chúng ta thiết lập các siêu tham số huấn luyện và biên dịch mô hình. Chúng ta sử dụng hàm `create_optimizer()` từ thư viện 🤗 Transformers, cung cấp cho chúng ta trình tối ưu hóa `AdamW` với phân rã tốc độ học tuyến tính. Chúng ta cũng sử dụng hàm mất mát có sẵn của mô hình, là mặc định khi không có tổn thất nào được chỉ định làm tham số cho `compile()` và đặt độ chính xác huấn luyện thành `"mixed_float16"`. Lưu ý rằng nếu bạn đang sử dụng GPU Colab hoặc GPU khác không có hỗ trợ float16 tăng tốc, bạn có thể nên đổi dòng đó thành chú thích.

Ngoài ra, chúng ta thiết lập một `PushToHubCallback` sẽ lưu mô hình vào Hub sau mỗi epoch. Bạn có thể chỉ định tên của kho lưu trữ mà bạn muốn đẩy đến bằng tham số `hub_model_id` (cụ thể là bạn sẽ phải sử dụng tham số này để đẩy đến một tổ chức). Ví dụ: để đẩy mô hình vào tổ chức [`huggingface-course`](https://huggingface.co/huggingface-course), chúng tôi đã thêm `hub_model_id="huggingface-course/distilbert-finetuned-imdb"`. Theo mặc định, kho lưu trữ được sử dụng sẽ nằm trong không gian tên của bạn và được đặt tên theo thư mục đầu ra mà bạn đã đặt, vì vậy trong trường hợp của chúng tôi, nó sẽ là `"lewtun/distilbert-finetuned-imdb"`.

```python
from transformers import create_optimizer
from transformers.keras_callbacks import PushToHubCallback
import tensorflow as tf

num_train_steps = len(tf_train_dataset)
optimizer, schedule = create_optimizer(
    init_lr=2e-5,
    num_warmup_steps=1_000,
    num_train_steps=num_train_steps,
    weight_decay_rate=0.01,
)
model.compile(optimizer=optimizer)

# Huấn luyện trong mixed-precision float16
tf.keras.mixed_precision.set_global_policy("mixed_float16")

callback = PushToHubCallback(
    output_dir=f"{model_name}-finetuned-imdb", tokenizer=tokenizer
)
```

Bây giờ chúng ta đã sẵn sàng chạy `model.fit()` - nhưng trước khi làm như vậy, hãy xem xét ngắn gọn _perplexity_, là một chỉ số phổ biến để đánh giá hiệu suất của các mô hình ngôn ngữ.

{:else}

Khi đã đăng nhập, chúng ta có thể chỉ định các tham số cho `Trainer`:

```python
from transformers import TrainingArguments

batch_size = 64
# In ra sự mất mát khi huấn luyện ở mỗi epoch
logging_steps = len(downsampled_dataset["train"]) // batch_size
model_name = model_checkpoint.split("/")[-1]

training_args = TrainingArguments(
    output_dir=f"{model_name}-finetuned-imdb",
    overwrite_output_dir=True,
    evaluation_strategy="epoch",
    learning_rate=2e-5,
    weight_decay=0.01,
    per_device_train_batch_size=batch_size,
    per_device_eval_batch_size=batch_size,
    push_to_hub=True,
    fp16=True,
    logging_steps=logging_steps,
)
```

Ở đây, chúng ta đã điều chỉnh một số tùy chọn mặc định, bao gồm `log_steps` để đảm bảo theo dõi sự mất mát trong quá trình huấn luyện theo từng epoch. Chúng ta cũng đã sử dụng `fp16=True` để cho phép huấn luyện chính xác hỗn hợp, giúp tăng tốc độ. Theo mặc định, `Trainer` sẽ loại bỏ bất kỳ cột nào không phải là một phần của phương thức `forward()` của mô hình. Điều này có nghĩa là nếu bạn đang sử dụng công cụ che toàn bộ từ, bạn cũng cần đặt `remove_unused_columns=False` để đảm bảo chúng ta không mất cột `word_ids` trong quá trình huấn luyện.

Lưu ý rằng bạn có thể chỉ định tên của kho lưu trữ mà bạn muốn đẩy đến bằng tham số `hub_model_id` (cụ thể là bạn sẽ phải sử dụng tham số này để đẩy đến một tổ chức). Ví dụ: khi chúng ta đẩy mô hình vào tổ chức [`huggingface-course`](https://huggingface.co/huggingface-course), chúng ta đã thêm `hub_model_id="huggingface-course/distilbert-finetuned-imdb"` vào `TrainingArguments`. Theo mặc định, kho lưu trữ được sử dụng sẽ nằm trong không gian tên của bạn và được đặt tên theo thư mục đầu ra mà bạn đã đặt, vì vậy trong trường hợp của chúng ta, nó sẽ là`"lewtun/distilbert-finetuned-imdb"`.

Bây giờ chúng ta có tất cả các thành phần để tạo ra `Trainer`. Ở đây chúng ta chỉ sử dụng `data_collator` tiêu chuẩn, nhưng bạn có thể thử toàn bộ công cụ che toàn bộ từ và so sánh kết quả như một bài tập:

```python
from transformers import Trainer

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=downsampled_dataset["train"],
    eval_dataset=downsampled_dataset["test"],
    data_collator=data_collator,
    tokenizer=tokenizer,
)
```

Giờ chúng ta đã sẵn sàng chạy `trainer.train()` - nhưng trước khi làm như vậy, chúng ta hãy xem xét ngắn gọn _perplexity_, là một chỉ số phổ biến để đánh giá hiệu suất của các mô hình ngôn ngữ.

{/if}

### Perplexity cho mô hình ngôn ngữ

<Youtube id="NURcDHhYe98"/>

Không giống như các tác vụ khác như phân loại văn bản hoặc hỏi đáp mà chúng ta được cung cấp một kho ngữ liệu được gắn nhãn để huấn luyện, với mô hình ngôn ngữ, ta không có bất kỳ nhãn rõ ràng nào. Vậy làm cách nào để xác định điều gì tạo nên một mô hình ngôn ngữ tốt? Giống như tính năng tự động sửa lỗi trong điện thoại của bạn, một mô hình ngôn ngữ tốt là một mô hình ngôn ngữ chỉ định xác suất cao cho các câu đúng ngữ pháp và xác suất thấp cho các câu vô nghĩa. Để giúp bạn biết rõ hơn về hình thức này, bạn có thể tìm thấy toàn bộ tập hợp "tự động sửa lỗi" trực tuyến, trong đó mô hình trong điện thoại đã tạo ra một số hoàn thành khá hài hước (và thường không phù hợp)!

{#if fw === 'pt'}

Giả sử bộ kiểm thử của chúng ta bao gồm hầu hết các câu đúng ngữ pháp, thì một cách để đo lường chất lượng của mô hình ngôn ngữ là tính toán xác suất nó gán cho từ tiếp theo trong tất cả các câu của bộ kiểm thử. Khả năng xảy ra cao chỉ ra rằng mô hình không bị "ngạc nhiên" hoặc "bối rối" bởi các mẫu không nhìn thấy và cho thấy nó đã học được các mẫu ngữ pháp cơ bản trong ngôn ngữ. Có nhiều định nghĩa toán học khác nhau về perplexity, nhưng chúng ta sẽ sử dụng định nghĩa là hàm mũ của mất mát entropy chéo. Do đó, chúng ta có thể tính toán perplexity của mô hình được huấn luyện trước của mình bằng cách sử dụng hàm `Trainer.evaluate()` để tính toán mất mát entropy chéo trên tập kiểm thử và sau đó lấy theo cấp số nhân của kết quả:


```python
import math

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

{:else}


Giả sử bộ kiểm thử của chúng ta bao gồm hầu hết các câu đúng ngữ pháp, thì một cách để đo lường chất lượng của mô hình ngôn ngữ là tính toán xác suất nó gán cho từ tiếp theo trong tất cả các câu của bộ kiểm thử. Khả năng xảy ra cao chỉ ra rằng mô hình không bị "ngạc nhiên" hoặc "bối rối" bởi các mẫu không nhìn thấy và cho thấy nó đã học được các mẫu ngữ pháp cơ bản trong ngôn ngữ. Có nhiều định nghĩa toán học khác nhau về perplexity, nhưng chúng ta sẽ sử dụng định nghĩa là hàm mũ của mất mát entropy chéo. Do đó, chúng ta có thể tính toán perplexity của mô hình được huấn luyện trước của mình bằng cách sử dụng hàm `Trainer.evaluate()` để tính toán mất mát entropy chéo trên tập kiểm thử và sau đó lấy theo cấp số nhân của kết quả:

```python
import math

eval_loss = model.evaluate(tf_eval_dataset)
print(f"Perplexity: {math.exp(eval_loss):.2f}")
```

{/if}

```python out
>>> Perplexity: 21.75
```

Perplexity thấp hơn có nghĩa là một mô hình ngôn ngữ tốt hơn và chúng ta có thể thấy ở đây rằng mô hình bắt đầu của chúng ta có một giá trị hơi lớn. Hãy xem liệu chúng ta có thể hạ thấp nó bằng cách tinh chỉnh không! Để làm điều đó, trước tiên chúng ta chạy vòng lặp huấn luyện:

{#if fw === 'pt'}

```python
trainer.train()
```

{:else}

```python
model.fit(tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback])
```

{/if}

và sau đó tính kết quả perplexity trên tập kiểm thử:

{#if fw === 'pt'}

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

{:else}

```python
eval_loss = model.evaluate(tf_eval_dataset)
print(f"Perplexity: {math.exp(eval_loss):.2f}")
```

{/if}

```python out
>>> Perplexity: 11.32
```

Tốt -- nó giảm perplexity, cho thấy mô hình đã học được điều gì đó về mảng đánh giá phim!

{#if fw === 'pt'}

Sau khi quá trình huấn luyện kết thúc, chúng ta có thể đẩy thẻ mô hình có thông tin huấn luyện vào Hub (các checkpoint được lưu trong quá trình tự huấn luyện):

Once training is finished, we can push the model card with the training information to the Hub (the checkpoints are saved during training itself):

```python
trainer.push_to_hub()
```

{/if}

<Tip>

✏️ **Đến lượt bạn!** Chạy bướchuấn luyện trên sau khi thay đổi trình thu thập dữ liệu thành che toàn bộ từ. Bạn có nhận được kết quả tốt hơn không?

</Tip>

{#if fw === 'pt'}

Trong trường hợp của mình, chúng ta không cần làm gì đặc biệt với vòng huấn luyện, nhưng một số trường hợp bạn sẽ cần phải triển khai một số logic tuỳ chỉnh. Với những ứng dụng này, bạn có thể sử dụng 🤗 Accelerate -- hãy cũng xem xem!

## Tinh chỉnh DistilBERT với 🤗 Accelerate

Như chúng ta đã thấy với `Trainer`, việc tinh chỉnh mô hình ngôn ngữ bị ản đi rất giống với ví dụ phân loại văn bản từ [Chapter 3](/course/chapter3). Trên thực tế, sự tinh tế duy nhất là việc sử dụng một công cụ đối chiếu dữ liệu đặc biệt và chúng ta đã đề cập đến điều đó trước đó trong phần này!

Tuy nhiên, chúng ta thấy rằng `DataCollatorForLanguageModeling` cũng áp dụng tính năng che ngẫu nhiên với mỗi lần đánh giá, vì vậy chúng ta sẽ thấy một số biến động về perplexity với mỗi lần chạy huấn luyện. Một cách để loại bỏ tính ngẫu nhiên này là áp dụng  che _chỉ một lần_ trên toàn bộ tập kiểm thử, sau đó sử dụng trình đối chiếu dữ liệu mặc định trong 🤗 Transformers để thu thập các lô trong quá trình đánh giá. Để xem cách này hoạt động như thế nào, hãy triển khai một chức năng đơn giản áp dụng che trên một lô, tương tự như lần đầu của chúng ta với `DataCollatorForLanguageModeling`:

```python
def insert_random_mask(batch):
    features = [dict(zip(batch, t)) for t in zip(*batch.values())]
    masked_inputs = data_collator(features)
    # Tạo ra một cột "masked" mới cho mỗi cột trong bộ dữ liệu
    return {"masked_" + k: v.numpy() for k, v in masked_inputs.items()}
```

Tiếp theo, chúng ta sẽ áp dụng chức năng này cho tập kiểm thử của mình và bỏ các cột không che để có thể thay thế chúng bằng những cột bị che. Bạn có thể sử dụng che toàn bộ từ bằng cách thay thế `data_collator` ở trên bằng cái thích hợp, trong trường hợp đó, bạn nên xóa dòng đầu tiên tại đây:

```py
downsampled_dataset = downsampled_dataset.remove_columns(["word_ids"])
eval_dataset = downsampled_dataset["test"].map(
    insert_random_mask,
    batched=True,
    remove_columns=downsampled_dataset["test"].column_names,
)
eval_dataset = eval_dataset.rename_columns(
    {
        "masked_input_ids": "input_ids",
        "masked_attention_mask": "attention_mask",
        "masked_labels": "labels",
    }
)
```

Sau đó, chúng ta có thể thiết lập bộ lưu dữ liệu như bình thường, nhưng ta sẽ sử dụng `default_data_collator` từ 🤗 Transformers cho tập kiểm định:

```python
from torch.utils.data import DataLoader
from transformers import default_data_collator

batch_size = 64
train_dataloader = DataLoader(
    downsampled_dataset["train"],
    shuffle=True,
    batch_size=batch_size,
    collate_fn=data_collator,
)
eval_dataloader = DataLoader(
    eval_dataset, batch_size=batch_size, collate_fn=default_data_collator
)
```

Từ đây, chúng ta làm theo các bước tiêu chuẩn với 🤗 Accelerate. Yêu cầu đầu tiên của công việc là tải một phiên bản mới của mô hình được huấn luyện trước:

```
model = AutoModelForMaskedLM.from_pretrained(model_checkpoint)
```

Sau đó, chúng ta cần chỉ định trình tối ưu hóa; chúng ta sẽ sử dụng tiêu chuẩn `AdamW`:

```python
from torch.optim import AdamW

optimizer = AdamW(model.parameters(), lr=5e-5)
```

Với những đối tượng này, bây giờ chúng ta có thể chuẩn bị mọi thứ cho quá trình huấn luyện với đối tượng `Accelerator`:

```python
from accelerate import Accelerator

accelerator = Accelerator()
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

Bây giờ mô hình, trình tối ưu hóa và bộ ghi dữ liệu của chúng ta đã được định cấu hình, chúng ta có thể chỉ định bộ lập lịch tốc độ học như sau:

```python
from transformers import get_scheduler

num_train_epochs = 3
num_update_steps_per_epoch = len(train_dataloader)
num_training_steps = num_train_epochs * num_update_steps_per_epoch

lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)
```

Chỉ có một điều cuối cùng cần làm trước khi huấn luyện: tạo một kho lưu trữ mô hình trên Hugging Face Hub! Trước tiên, chúng ta có thể sử dụng thư viện 🤗 Hub để tạo tên đầy đủ cho repo của mình:

```python
from huggingface_hub import get_full_repo_name

model_name = "distilbert-base-uncased-finetuned-imdb-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'lewtun/distilbert-base-uncased-finetuned-imdb-accelerate'
```

sau đó tạo và sao chép kho lưu trữ bằng cách sử dụng lớp `Repository` từ 🤗 Hub:

```python
from huggingface_hub import Repository

output_dir = model_name
repo = Repository(output_dir, clone_from=repo_name)
```

Sau khi thực hiện xong, việc viết ra toàn bộ vòng lặp huấn luyện và đánh giá chỉ là một vấn đề đơn giản:

```python
from tqdm.auto import tqdm
import torch
import math

progress_bar = tqdm(range(num_training_steps))

for epoch in range(num_train_epochs):
    # Huấn luyện
    model.train()
    for batch in train_dataloader:
        outputs = model(**batch)
        loss = outputs.loss
        accelerator.backward(loss)

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)

    # Đánh giá
    model.eval()
    losses = []
    for step, batch in enumerate(eval_dataloader):
        with torch.no_grad():
            outputs = model(**batch)

        loss = outputs.loss
        losses.append(accelerator.gather(loss.repeat(batch_size)))

    losses = torch.cat(losses)
    losses = losses[: len(eval_dataset)]
    try:
        perplexity = math.exp(torch.mean(losses))
    except OverflowError:
        perplexity = float("inf")

    print(f">>> Epoch {epoch}: Perplexity: {perplexity}")

    # Lưu và tải
    accelerator.wait_for_everyone()
    unwrapped_model = accelerator.unwrap_model(model)
    unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
    if accelerator.is_main_process:
        tokenizer.save_pretrained(output_dir)
        repo.push_to_hub(
            commit_message=f"Training in progress epoch {epoch}", blocking=False
        )
```

```python out
>>> Epoch 0: Perplexity: 11.397545307900472
>>> Epoch 1: Perplexity: 10.904909330983092
>>> Epoch 2: Perplexity: 10.729503505340409
```

Tuyệt vời, chúng tôi đã có thể đánh giá mức độ phức tạp theo từng epoch và đảm bảo rằng nhiều lần chạy huấn luyện có thể tái tạo!

{/if}

## Sử dụng mô hình tinh chỉnh của mình

ạn có thể tương tác với mô hình đã được tinh chỉnh của mình bằng cách sử dụng tiện ích của nó trên Hub hoặc cục bộ với `pipeline` từ 🤗 Transformers. Hãy sử dụng cái sau để tải xuống mô hình của chúng tôi bằng cách sử dụng pipeline `fill-mask`:

```python
from transformers import pipeline

mask_filler = pipeline(
    "fill-mask", model="huggingface-course/distilbert-base-uncased-finetuned-imdb"
)
```

Sau đó, chúng ta có thể cung cấp văn bản mẫu "This is a great [MASK]" và xem 5 dự đoán đầu là gì:

```python
preds = mask_filler(text)

for pred in preds:
    print(f">>> {pred['sequence']}")
```

```python out
'>>> this is a great movie.'
'>>> this is a great film.'
'>>> this is a great story.'
'>>> this is a great movies.'
'>>> this is a great character.'
```

Gọn gàng - mô hình của chúng ta rõ ràng đã điều chỉnh trọng số của nó để dự đoán các từ liên quan nhiều hơn đến phim!

<Youtube id="0Oxphw4Q9fo"/>

Điều này kết thúc thử nghiệm đầu tiên của chúng ta với việc huấn luyện một mô hình ngôn ngữ. Trong [phần 6](/course/chapter7/section6), bạn sẽ học cách huấn luyện một mô hình tự động hồi quy như GPT-2 từ đầu; hãy đến đó nếu bạn muốn xem cách bạn có thể huấn luyện trước mô hình Transformer của riêng mình!

<Tip>

✏️ **Thử nghiệm thôi!** Để định lượng lợi ích của việc thích ứng chuyên môn, hãy tinh chỉnh bộ phân loại trên các nhãn IMDb cho cả các checkpoint DistilBERT được huấn luyện trước và tinh chỉnh. Nếu bạn cần bồi dưỡng về phân loại văn bản, hãy xem [Chương 3](/course/chapter3).

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter7/3.mdx" />

### THuấn luyện một mô hình ngôn ngữ nhân quả từ đầu
https://huggingface.co/learn/course/vi/chapter7/6.md

# THuấn luyện một mô hình ngôn ngữ nhân quả từ đầu

{#if fw === 'pt'}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {
      label: "Google Colab",
      value:
        "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section6_pt.ipynb",
    },
    {
      label: "Aws Studio",
      value:
        "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section6_pt.ipynb",
    },
  ]}
/>

{:else}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {
      label: "Google Colab",
      value:
        "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section6_tf.ipynb",
    },
    {
      label: "Aws Studio",
      value:
        "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section6_tf.ipynb",
    },
  ]}
/>

{/if}

Cho đến thời điểm hiện tại, chúng ta chủ yếu sử dụng các mô hình được huấn luyện trước và tinh chỉnh chúng cho các trường hợp sử dụng mới bằng cách sử dụng lại các trọng số từ huấn luyện trước. Như chúng ta đã thấy trong [Chương 1](/course/chapter1), điều này thường được gọi là _transfer learning_ hay _học chuyển giao_, và đó là một chiến lược rất thành công để áp dụng các mô hình Transformer cho hầu hết các trường hợp sử dụng trong thế giới thực nơi dữ liệu được gắn nhãn là thưa thớt. Trong chương này, chúng ta sẽ thực hiện một cách tiếp cận khác và huấn luyện một mô hình hoàn toàn mới từ đầu. Đây là một cách tiếp cận tốt để thực hiện nếu bạn có nhiều dữ liệu và nó rất khác với dữ liệu huấn luyện trước được sử dụng cho các mô hình có sẵn. Tuy nhiên, nó cũng đòi hỏi nhiều tài nguyên máy tính hơn đáng kể để huấn luyện trước một mô hình ngôn ngữ hơn là chỉ để tinh chỉnh mô hình hiện có. Các ví dụ có thể có ý nghĩa khi huấn luyện một mô hình mới bao gồm các tập dữ liệu bao gồm các nốt nhạc, trình tự phân tử như DNA hoặc ngôn ngữ lập trình. Công cụ thứ hai gần đây đã đạt được sức hút nhờ các công cụ như TabNine và GitHub's Copilot, được hỗ trợ bởi mô hình Codex của OpenAI, có thể tạo ra các chuỗi mã dài. Tác vụ tạo văn bản này được giải quyết tốt nhất với các mô hình ngôn ngữ tự động hồi quy hoặc nhân quả như GPT-2.

Trong phần này, chúng ta sẽ xây dựng một phiên bản thu nhỏ của mô hình tạo mã: chúng ta sẽ tập trung vào các hoàn thành một dòng thay vì các hàm hoặc lớp đầy đủ, sử dụng một tập hợp con mã Python. Khi làm việc với dữ liệu bằng Python, bạn thường xuyên tiếp xúc với bộ khoa học dữ liệu Python, bao gồm các thư viện `matplotlib`, `seaborn`, `pandas` và `scikit-learn`. Khi sử dụng chúng, thông thường cần phải tra cứu các lệnh cụ thể, vì vậy sẽ rất tuyệt nếu chúng ta có thể sử dụng một mô hình để hoàn thành các lệnh gọi này cho chúng ta.

<Youtube id="Vpjb1lu0MDk" />

Trong [Chương 6](/course/chapter6), chúng ta đã tạo một trình tokenize hiệu quả để xử lý mã nguồn Python, nhưng những gì chúng ta vẫn cần là một tập dữ liệu quy mô lớn để huấn luyện trước một mô hình. Ở đây, chúng ta sẽ áp dụng tokenizer cho một kho lưu trữ mã Python có nguồn gốc từ kho lưu trữ GitHub. Sau đó, chúng ta sẽ sử dụng API `Trainer` và 🤗 Accelerate để huấn luyện mô hình. Chúng ta hãy đi đến đó!

<iframe
  src="https://course-demos-codeparrot-ds.hf.space"
  frameBorder="0"
  height="300"
  title="Gradio app"
  class="block dark:hidden container p-0 flex-grow space-iframe"
  allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking"
  sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"
></iframe>

Đây thực sự cách mô hình đã được huấn luyện và tải lên Hub bằng cách sử dụng mã được hiển thị trong phần này. Bạn có thể tìm thấy nó [tại đây](https://huggingface.co/huggingface-course/codeparrot-ds?text=plt.imshow%28). Lưu ý rằng vì có một số ngẫu nhiên xảy ra trong quá trình tạo văn bản, bạn có thể sẽ nhận được một kết quả hơi khác.

## Thu thập dữ liệu

Mã Python có sẵn rất nhiều từ các kho mã như GitHub, mà chúng ta có thể sử dụng để tạo tập dữ liệu bằng cách đào mọi kho lưu trữ Python. Đây là phương pháp được thực hiện trong [sách giáo khoa về Transformers](https://learning.oreilly.com/library/view/natural-language-processing/9781098103231/) để huấn luyện trước một mô hình GPT-2 lớn. Sử dụng kết xuất GitHub khoảng 180 GB chứa khoảng 20 triệu tệp Python có tên là `codeparrot`, các tác giả đã xây dựng một tập dữ liệu mà sau đó họ chia sẻ trên [Hugging Face Hub](https://huggingface.co/datasets/transformersbook/codeparrot) .

Tuy nhiên, việc huấn luyện trên toàn bộ ngữ liệu này tốn nhiều thời gian và tính toán, và chúng ta chỉ cần tập con của tập dữ liệu liên quan đến ngăn xếp khoa học dữ liệu Python. Vì vậy, hãy bắt đầu bằng cách lọc tập dữ liệu `codeparrot` cho tất cả các tệp bao gồm bất kỳ thư viện nào trong ngăn xếp này. Do kích thước của tập dữ liệu, chúng ta muốn tránh tải nó xuống; thay vào đó, ta sẽ sử dụng tính năng phát trực tuyến để lọc nó một cách nhanh chóng. Để giúp chúng ta lọc các mẫu mã bằng cách sử dụng các thư viện đã đề cập trước đó, ta sẽ sử dụng hàm sau:

```py
def any_keyword_in_string(string, keywords):
    for keyword in keywords:
        if keyword in string:
            return True
    return False
```

Hãy kiểm tra nó trên hai ví dụ:

```py
filters = ["pandas", "sklearn", "matplotlib", "seaborn"]
example_1 = "import numpy as np"
example_2 = "import pandas as pd"

print(
    any_keyword_in_string(example_1, filters), any_keyword_in_string(example_2, filters)
)
```

```python out
False True
```

Chúng ta có thể sử dụng điều này để tạo một hàm sẽ truyền trực tuyến tập dữ liệu và lọc các phần tử ta muốn:

```py
from collections import defaultdict
from tqdm import tqdm
from datasets import Dataset


def filter_streaming_dataset(dataset, filters):
    filtered_dict = defaultdict(list)
    total = 0
    for sample in tqdm(iter(dataset)):
        total += 1
        if any_keyword_in_string(sample["content"], filters):
            for k, v in sample.items():
                filtered_dict[k].append(v)
    print(f"{len(filtered_dict['content'])/total:.2%} of data after filtering.")
    return Dataset.from_dict(filtered_dict)
```

Sau đó, chúng ta có thể chỉ cần áp dụng chức năng này cho tập dữ liệu phát trực tuyến:

```py
# Ô này sẽ mất rất nhiều thời gian để thực thi, vì vậy bạn nên bỏ qua và chuyển đến
# cái tiếp theo!
from datasets import load_dataset

split = "train"  # "valid"
filters = ["pandas", "sklearn", "matplotlib", "seaborn"]

data = load_dataset(f"transformersbook/codeparrot-{split}", split=split, streaming=True)
filtered_data = filter_streaming_dataset(data, filters)
```

```python out
3.26% of data after filtering.
```

Điều này để lại cho chúng ta khoảng 3% tập dữ liệu ban đầu, vẫn còn khá lớn - tập dữ liệu kết quả là 6GB và bao gồm 600,000 tập lệnh Python!

Việc lọc toàn bộ tập dữ liệu có thể mất 2-3 giờ tùy thuộc vào máy và băng thông của bạn. Nếu bạn không muốn tự mình trải qua quá trình kéo dài này, chúng ta cung cấp tập dữ liệu đã lọc trên Hub để bạn tải xuống:

```py
from datasets import load_dataset, DatasetDict

ds_train = load_dataset("huggingface-course/codeparrot-ds-train", split="train")
ds_valid = load_dataset("huggingface-course/codeparrot-ds-valid", split="validation")

raw_datasets = DatasetDict(
    {
        "train": ds_train,  # .shuffle().select(range(50000)),
        "valid": ds_valid,  # .shuffle().select(range(500))
    }
)

raw_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['repo_name', 'path', 'copies', 'size', 'content', 'license'],
        num_rows: 606720
    })
    valid: Dataset({
        features: ['repo_name', 'path', 'copies', 'size', 'content', 'license'],
        num_rows: 3322
    })
})
```

<Tip>

Việc huấn luyện trước mô hình ngôn ngữ sẽ mất một lúc. Chúng tôi khuyên bạn trước tiên nên chạy vòng lặp huấn luyện trên một mẫu dữ liệu bằng cách bỏ chú thích hai dòng một phần ở trên và đảm bảo rằng quá trình huấn luyện hoàn tất thành công và các mô hình được lưu trữ. Không có gì khó chịu hơn là một lần chạy huấn luyện không thành công ở bước cuối cùng vì bạn quên tạo một thư mục hoặc vì có lỗi đánh máy ở cuối vòng lặp huấn luyện!

</Tip>

Hãy xem một ví dụ từ tập dữ liệu. Chúng ta sẽ chỉ hiển thị 200 ký tự đầu tiên của mỗi trường:

```py
for key in raw_datasets["train"][0]:
    print(f"{key.upper()}: {raw_datasets['train'][0][key][:200]}")
```

```python out
'REPO_NAME: kmike/scikit-learn'
'PATH: sklearn/utils/__init__.py'
'COPIES: 3'
'SIZE: 10094'
'''CONTENT: """
The :mod:`sklearn.utils` module includes various utilites.
"""

from collections import Sequence

import numpy as np
from scipy.sparse import issparse
import warnings

from .murmurhash import murm
LICENSE: bsd-3-clause'''
```

Chúng ta có thể thấy rằng trường `content` chứa mã mà chúng ta muốn mô hình của mình huấn luyện. Bây giờ chúng ta đã có một tập dữ liệu, chúng ta cần chuẩn bị các văn bản để chúng có định dạng phù hợp để huấn luyện trước.

## Chuẩn bị tập dữ liệu

<Youtube id="ma1TrR7gE7I" />

Bước đầu tiên sẽ là tokenize dữ liệu để chúng ta có thể sử dụng nó để huấn luyện. Vì mục tiêu của chúng ta chủ yếu là tự động hoàn thành các lệnh gọi hàm ngắn, chúng ta có thể giữ kích thước ngữ cảnh tương đối nhỏ. Điều này có lợi ích là chúng ta có thể huấn luyện mô hình nhanh hơn nhiều và nó cần ít bộ nhớ hơn đáng kể. Nếu điều quan trọng là ứng dụng của bạn phải có nhiều ngữ cảnh hơn (ví dụ: nếu bạn muốn mô hình viết các bài kiểm tra đơn vị dựa trên tệp có định nghĩa hàm), hãy đảm bảo bạn tăng con số đó, nhưng cũng lưu ý rằng điều này đi kèm với bộ nhớ GPU lớn hơn. Hiện tại, hãy sửa kích thước ngữ cảnh ở 128 token, trái ngược với 1,024 hoặc 2,048 được sử dụng trong GPT-2 hoặc GPT-3, tương ứng.

Hầu hết các tài liệu chứa nhiều hơn 128 token, vì vậy chỉ cần cắt bớt đầu vào đến độ dài tối đa sẽ loại bỏ một phần lớn tập dữ liệu của mình. Thay vào đó, chúng ta sẽ sử dụng tùy chọn `return_overflowing_tokens` để token toàn bộ đầu vào và chia nó thành nhiều phần, như chúng ta đã làm trong [Chương 6](/course/chapter6/4). Chúng ta cũng sẽ sử dụng tùy chọn `return_length` để tự động trả về độ dài của mỗi đoạn được tạo. Thường thì phần cuối cùng sẽ nhỏ hơn kích thước ngữ cảnh và chúng ta sẽ loại bỏ những phần này để tránh các vấn đề về phần đệm; chúng ta không thực sự cần chúng vì dù sao chúng ta cũng có nhiều dữ liệu.

<div class="flex justify-center">
  <img
    class="block dark:hidden"
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/chunking_texts.svg"
    alt="Chunking a large texts in several pieces."
  />
  <img
    class="hidden dark:block"
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/chunking_texts-dark.svg"
    alt="Chunking a large texts in several pieces."
  />
</div>

Hãy xem chính xác cách thức hoạt động của điều này bằng cách xem hai ví dụ đầu tiên:

```py
from transformers import AutoTokenizer

context_length = 128
tokenizer = AutoTokenizer.from_pretrained("huggingface-course/code-search-net-tokenizer")

outputs = tokenizer(
    raw_datasets["train"][:2]["content"],
    truncation=True,
    max_length=context_length,
    return_overflowing_tokens=True,
    return_length=True,
)

print(f"Input IDs length: {len(outputs['input_ids'])}")
print(f"Input chunk lengths: {(outputs['length'])}")
print(f"Chunk mapping: {outputs['overflow_to_sample_mapping']}")
```

```python out
Input IDs length: 34
Input chunk lengths: [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 117, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 41]
Chunk mapping: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
```

Chúng ta có thể thấy rằng chúng ta nhận được tổng cộng 34 phân đoạn từ hai ví dụ đó. Nhìn vào độ dài phân đoạn, chúng ta có thể thấy rằng các đoạn ở cuối cả hai tài liệu có ít hơn 128 token (tương ứng là 117 và 41). Chúng chỉ đại diện cho một phần nhỏ trong tổng số các khối mà chúng ta có, vì vậy chúng ta có thể vứt chúng đi một cách an toàn. Với trường `overflow_to_sample_mapping`, chúng ta cũng có thể tạo lại các phần thuộc về mẫu đầu vào nào.

Với thao tác này, chúng ta đang sử dụng một tính năng tiện dụng của hàm `Dataset.map()` trong 🤗 Datasets, đó là nó không yêu cầu ánh xạ 1-1; như chúng ta đã thấy trong [phần 3](/course/chapter7/3), chúng ta có thể tạo các lô có nhiều phần tử hơn hoặc ít hơn lô đầu vào. Điều này rất hữu ích khi thực hiện các hoạt động như tăng dữ liệu hoặc lọc dữ liệu làm thay đổi số lượng phần tử. Trong trường hợp của chúng ta, khi tokenize mỗi phần tử thành các phần có kích thước ngữ cảnh được chỉ định, chúng ta tạo nhiều mẫu từ mỗi tài liệu. Chúng ta chỉ cần đảm bảo xóa các cột hiện có, vì chúng có kích thước xung đột. Nếu chúng ta muốn giữ chúng, chúng ta có thể lặp lại chúng một cách thích hợp và trả lại chúng trong lệnh gọi `Dataset.map()`:

```py
def tokenize(element):
    outputs = tokenizer(
        element["content"],
        truncation=True,
        max_length=context_length,
        return_overflowing_tokens=True,
        return_length=True,
    )
    input_batch = []
    for length, input_ids in zip(outputs["length"], outputs["input_ids"]):
        if length == context_length:
            input_batch.append(input_ids)
    return {"input_ids": input_batch}


tokenized_datasets = raw_datasets.map(
    tokenize, batched=True, remove_columns=raw_datasets["train"].column_names
)
tokenized_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['input_ids'],
        num_rows: 16702061
    })
    valid: Dataset({
        features: ['input_ids'],
        num_rows: 93164
    })
})
```

Hiện chúng ta có 16,7 triệu ví dụ với 128 token mỗi ví dụ, tương ứng với tổng cộng khoảng 2,1 tỷ token. Để tham khảo, các mô hình GPT-3 và Codex của OpenAI được huấn luyện trên 300 và 100 tỷ token tương ứng, trong đó các mô hình Codex được khởi tạo từ các checkpoint GPT-3. Mục tiêu của chúng ta trong phần này không phải là cạnh tranh với các mô hình này, có thể tạo ra các văn bản dài, mạch lạc, mà là tạo ra một phiên bản thu nhỏ cung cấp chức năng tự động hoàn thành nhanh chóng cho các nhà khoa học dữ liệu.

Bây giờ chúng ta đã có tập dữ liệu sẵn sàng, hãy thiết lập mô hình!

<Tip>

✏️ **Thử nghiệm thôi!** Loại bỏ tất cả các phần nhỏ hơn kích thước ngữ cảnh không phải là vấn đề lớn ở đây vì chúng ta đang sử dụng các cửa sổ ngữ cảnh nhỏ. Khi bạn tăng kích thước ngữ cảnh (hoặc nếu bạn có một kho tài liệu ngắn), phần nhỏ các phần bị vứt bỏ cũng sẽ tăng lên. Một cách hiệu quả hơn để chuẩn bị dữ liệu là kết hợp tất cả các mẫu được tokenize trong một lô với token `eos_token_id` ở giữa, và sau đó thực hiện phân đoạn trên các chuỗi được nối. Như một bài tập, hãy sửa đổi hàm `tokenize()` để sử dụng cách tiếp cận đó. Lưu ý rằng bạn sẽ muốn đặt `truncation=False` và xóa các tham số khác khỏi tokenizer để nhận được chuỗi đầy đủ của token ID.

</Tip>

## Khởi tạo mô hình mới

Bước đầu tiên của chúng ta là khởi chạy mới mô hình GPT-2. Chúng ta sẽ sử dụng cùng một cấu hình cho mô hình của mình như cho mô hình GPT-2 nhỏ, vì vậy chúng ta tải cấu hình định sẵn, đảm bảo rằng kích thước tokenizer khớp với kích thước từ vựng của mô hình và chuyển `bos` và `eos` (bắt đầu và cuối chuỗi) token ID:

{#if fw === 'pt'}

```py
from transformers import AutoTokenizer, GPT2LMHeadModel, AutoConfig

config = AutoConfig.from_pretrained(
    "gpt2",
    vocab_size=len(tokenizer),
    n_ctx=context_length,
    bos_token_id=tokenizer.bos_token_id,
    eos_token_id=tokenizer.eos_token_id,
)
```

Với cấu hình đó, chúng ta có thể tải một mô hình mới. Lưu ý rằng đây là lần đầu tiên chúng ta không sử dụng hàm `from_pretrained()`, vì chúng ta thực sự đang khởi tạo một mô hình của chính chúng ta:

```py
model = GPT2LMHeadModel(config)
model_size = sum(t.numel() for t in model.parameters())
print(f"GPT-2 size: {model_size/1000**2:.1f}M parameters")
```

```python out
GPT-2 size: 124.2M parameters
```

{:else}

```py
from transformers import AutoTokenizer, TFGPT2LMHeadModel, AutoConfig

config = AutoConfig.from_pretrained(
    "gpt2",
    vocab_size=len(tokenizer),
    n_ctx=context_length,
    bos_token_id=tokenizer.bos_token_id,
    eos_token_id=tokenizer.eos_token_id,
)
```

Với cấu hình đó, chúng ta có thể tải một mô hình mới. Lưu ý rằng đây là lần đầu tiên chúng ta không sử dụng hàm `from_pretrained()`, vì chúng ta thực sự đang khởi tạo một mô hình của chính chúng ta:

```py
model = TFGPT2LMHeadModel(config)
model(model.dummy_inputs)  # Xây mô hình
model.summary()
```

```python out
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
transformer (TFGPT2MainLayer multiple                  124242432
=================================================================
Total params: 124,242,432
Trainable params: 124,242,432
Non-trainable params: 0
_________________________________________________________________
```

{/if}

Mô hình của chúng ta có 124 triệu thông số mà ta sẽ phải điều chỉnh. Trước khi có thể bắt đầu huấn luyện, chúng ta cần thiết lập một bộ đối chiếu dữ liệu sẽ đảm nhận việc tạo các lô. Chúng ta có thể sử dụng trình cắt ghép `DataCollatorForLanguageModeling`, được thiết kế đặc biệt cho mô hình ngôn ngữ (như tên gọi gợi ý một cách tinh tế). Bên cạnh việc xếp chồng và đệm các lô, nó cũng đảm nhận việc tạo các nhãn của mô hình ngôn ngữ - trong mô hình ngôn ngữ nhân quả, các đầu vào cũng đóng vai trò là nhãn (chỉ được dịch chuyển bởi một phần tử) và trình đối chiếu dữ liệu này tạo chúng nhanh chóng trong quá trình huấn luyện, vì vậy chúng tôi ta không cần sao chép `input_ids`.

Lưu ý rằng `DataCollatorForLanguageModeling` hỗ trợ cả mô hình hóa ngôn ngữ bị ẩn đi (MLM) và mô hình ngôn ngữ nhân quả (CLM). Theo mặc định, nó chuẩn bị dữ liệu cho MLM, nhưng chúng ta có thể chuyển sang CLM bằng cách đặt đối số `mlm=False`:

{#if fw === 'pt'}

```py
from transformers import DataCollatorForLanguageModeling

tokenizer.pad_token = tokenizer.eos_token
data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False)
```

{:else}

```py
from transformers import DataCollatorForLanguageModeling

tokenizer.pad_token = tokenizer.eos_token
data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False, return_tensors="tf")
```

{/if}

Hãy xem một ví dụ:

```py
out = data_collator([tokenized_datasets["train"][i] for i in range(5)])
for key in out:
    print(f"{key} shape: {out[key].shape}")
```

{#if fw === 'pt'}

```python out
input_ids shape: torch.Size([5, 128])
attention_mask shape: torch.Size([5, 128])
labels shape: torch.Size([5, 128])
```

{:else}

```python out
input_ids shape: (5, 128)
attention_mask shape: (5, 128)
labels shape: (5, 128)
```

{/if}

Chúng ta có thể thấy rằng các ví dụ đã được xếp chồng lên nhau và tất cả các tensor có cùng hình dạng.

{#if fw === 'tf'}

Bây giờ chúng ta có thể sử dụng phương thức `to_tf_dataset()` để chuyển đổi tập dữ liệu của mình thành tập dữ liệu TensorFlow bằng công cụ đối chiếu dữ liệu đã tạo ở trên:

```python
tf_train_dataset = tokenized_dataset["train"].to_tf_dataset(
    columns=["input_ids", "attention_mask", "labels"],
    collate_fn=data_collator,
    shuffle=True,
    batch_size=32,
)
tf_eval_dataset = tokenized_dataset["valid"].to_tf_dataset(
    columns=["input_ids", "attention_mask", "labels"],
    collate_fn=data_collator,
    shuffle=False,
    batch_size=32,
)
```

{/if}

<Tip warning={true}>

⚠️ Việc dịch chuyển các đầu vào và nhãn để căn chỉnh chúng xảy ra bên trong mô hình, do đó, bộ đối chiếu dữ liệu chỉ cần sao chép các đầu vào để tạo nhãn.

</Tip>

Bây giờ chúng ta có mọi thứ để thực sự huấn luyện mô hình của mình - đó không phải là quá nhiều công việc! Trước khi bắt đầu luyện tập, chúng ta nên đăng nhập vào Hugging Face. Nếu bạn đang làm việc trong notebook, bạn có thể làm như vậy với hàm tiện ích sau:

```python
from huggingface_hub import notebook_login

notebook_login()
```

Thao tác này sẽ hiển thị một tiện ích mà bạn có thể nhập thông tin đăng nhập Hugging Face của mình.

Nếu bạn không làm việc trong notebook, chỉ cần nhập dòng sau vào thiết bị đầu cuối của bạn:

```bash
huggingface-cli login
```

{#if fw === 'pt'}

Tất cả những gì còn lại cần làm là cấu hình các tham số huấn luyện và kích hoạt `Trainer`. Chúng ta sẽ sử dụng lịch trình tốc độ học cosine với một số lần khởi động và kích thước lô hiệu quả là 256 (`per_device_train_batch_size` \* `gradient_accumulation_steps`). Tích lũy gradient được sử dụng khi một loạt lô duy nhất không vừa với bộ nhớ và dần dần tích lũy gradient thông qua một số lần truyền xuôi/ngược. Chúng ta sẽ thấy điều này hoạt động khi chúng ta tạo vòng huấn luyện với 🤗 Accelerate.

```py
from transformers import Trainer, TrainingArguments

args = TrainingArguments(
    output_dir="codeparrot-ds",
    per_device_train_batch_size=32,
    per_device_eval_batch_size=32,
    evaluation_strategy="steps",
    eval_steps=5_000,
    logging_steps=5_000,
    gradient_accumulation_steps=8,
    num_train_epochs=1,
    weight_decay=0.1,
    warmup_steps=1_000,
    lr_scheduler_type="cosine",
    learning_rate=5e-4,
    save_steps=5_000,
    fp16=True,
    push_to_hub=True,
)

trainer = Trainer(
    model=model,
    tokenizer=tokenizer,
    args=args,
    data_collator=data_collator,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["valid"],
)
```

Bây giờ chúng ta có thể khởi động `Trainer` và đợi quá trình huấn luyện kết thúc. Tùy thuộc vào việc bạn chạy nó trên toàn bộ hay một tập hợp con của bộ huấn luyện, tương ứng sẽ mất 20 hoặc 2 giờ, vì vậy hãy lấy một ít cà phê và một cuốn sách hay để đọc!

```py
trainer.train()
```

Sau khi quá trình huấn luyện hoàn tất, chúng ta có thể đẩy mô hình và trình tokenizer vào Hub:

```py
trainer.push_to_hub()
```

{:else}

Tất cả những gì còn lại cần làm là định cấu hình các siêu tham số huấn luyện và gọi `compile()`và `fit()`. Chúng ta sẽ sử dụng lịch trình tốc độ học với một số khởi động để cải thiện tính ổn định của quá trình huấn luyện:

```py
from transformers import create_optimizer
import tensorflow as tf

num_train_steps = len(tf_train_dataset)
optimizer, schedule = create_optimizer(
    init_lr=5e-5,
    num_warmup_steps=1_000,
    num_train_steps=num_train_steps,
    weight_decay_rate=0.01,
)
model.compile(optimizer=optimizer)

# Huấn luyện trong mixed-precision float16
tf.keras.mixed_precision.set_global_policy("mixed_float16")
```

Bây giờ chúng ta có thể gọi `model.fit()` và đợi quá trình huấn luyện kết thúc. Tùy thuộc vào việc bạn chạy nó trên toàn bộ hay một tập hợp con của bộ huấn luyện, tương ứng sẽ mất 20 hoặc 2 giờ, vì vậy hãy lấy một ít cà phê và một cuốn sách hay để đọc! Sau khi huấn luyện xong, chúng ta có thể đẩy mô hình và trình tokenize vào Hub:

```py
from transformers.keras_callbacks import PushToHubCallback

callback = PushToHubCallback(output_dir="codeparrot-ds", tokenizer=tokenizer)

model.fit(tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback])
```

{/if}

<Tip>

✏️ **Thử nghiệm thôi!** Chỉ mất khoảng 30 dòng mã ngoài `TrainingArguments` để từ văn bản thô đến huấn luyện GPT-2. Hãy dùng thử với tập dữ liệu của riêng bạn và xem liệu bạn có thể đạt được kết quả tốt hay không!

</Tip>

<Tip>

{#if fw === 'pt'}

💡 Nếu bạn có quyền truy cập vào một máy có nhiều GPU, hãy thử chạy mã ở đó. `Trainer` tự động quản lý nhiều máy và điều này có thể tăng tốc quá trình huấn luyện lên rất nhiều.

{:else}

💡 Nếu bạn có quyền truy cập vào một máy có nhiều GPU, bạn có thể thử sử dụng ngữ cảnh `MirroredStrategy` để tăng tốc đáng kể cho quá trình huấn luyện. Bạn sẽ cần tạo một đối tượng `tf.distribute.MirroredStrategy` và đảm bảo rằng các lệnh `to_tf_dataset` cũng như tạo mô hình và lệnh gọi đến `fit()` đều được chạy trong ngữ cảnh `scope()` của nó. Bạn có thể xem tài liệu về điều này [tại đây](https://www.tensorflow.org/guide/distributed_training#use_tfdistributestrategy_with_keras_modelfit).

{/if}

</Tip>

## Tạo mã với một pipeline

Bây giờ là thời điểm của sự thật: chúng ta hãy xem mô hình được huấn luyện thực sự hoạt động tốt như thế nào! Chúng ta có thể thấy trong nhật ký rằng mất mát đã giảm đều đặn, nhưng để đưa mô hình vào thử nghiệm, chúng ta hãy xem nó hoạt động tốt như thế nào trên một số lời nhắc. Để làm điều đó, chúng ta sẽ bao bọc mô hình trong một `pipeline` tạo văn bản và chúng ta sẽ đưa nó vào GPU cho các thế hệ nhanh nếu có sẵn:

{#if fw === 'pt'}

```py
import torch
from transformers import pipeline

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
pipe = pipeline(
    "text-generation", model="huggingface-course/codeparrot-ds", device=device
)
```

{:else}

```py
from transformers import pipeline

course_model = TFGPT2LMHeadModel.from_pretrained("huggingface-course/codeparrot-ds")
course_tokenizer = AutoTokenizer.from_pretrained("huggingface-course/codeparrot-ds")
pipe = pipeline(
    "text-generation", model=course_model, tokenizer=course_tokenizer, device=0
)
```

{/if}

Hãy bắt đầu với tác vụ đơn giản là tạo một biểu đồ phân tán:

```py
txt = """\
# create some data
x = np.random.randn(100)
y = np.random.randn(100)

# create scatter plot with x, y
"""
print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
```

```python out
# create some data
x = np.random.randn(100)
y = np.random.randn(100)

# create scatter plot with x, y
plt.scatter(x, y)

# create scatter
```

Kết quả có vẻ chính xác. Nó cũng hoạt động với `pandas`? Hãy xem liệu chúng ta có thể tạo một `DataFrame` từ hai mảng không:

```py
txt = """\
# create some data
x = np.random.randn(100)
y = np.random.randn(100)

# create dataframe from x and y
"""
print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
```

```python out
# create some data
x = np.random.randn(100)
y = np.random.randn(100)

# create dataframe from x and y
df = pd.DataFrame({'x': x, 'y': y})
df.insert(0,'x', x)
for
```

Thật tuyệt, đó là câu trả lời chính xác - mặc dù sau đó nó lại chèn thêm cột `x`. Vì số lượng token được tạo có giới hạn, vòng lặp `for` sau đây sẽ bị cắt. Hãy xem liệu chúng ta có thể làm điều gì đó phức tạp hơn một chút và để mô hình giúp chúng ta sử dụng hoạt động `groupby`:

```py
txt = """\
# dataframe with profession, income and name
df = pd.DataFrame({'profession': x, 'income':y, 'name': z})

# calculate the mean income per profession
"""
print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
```

```python out
# dataframe with profession, income and name
df = pd.DataFrame({'profession': x, 'income':y, 'name': z})

# calculate the mean income per profession
profession = df.groupby(['profession']).mean()

# compute the
```

Không tệ; đó là cách làm đúng. Cuối cùng, hãy xem liệu chúng ta có thể sử dụng nó cho `scikit-learn` và thiết lập mô hình Random Forest hay không:

```py
txt = """
# import random forest regressor from scikit-learn
from sklearn.ensemble import RandomForestRegressor

# fit random forest model with 300 estimators on X, y:
"""
print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
```

```python out
# import random forest regressor from scikit-learn
from sklearn.ensemble import RandomForestRegressor

# fit random forest model with 300 estimators on X, y:
rf = RandomForestRegressor(n_estimators=300, random_state=random_state, max_depth=3)
rf.fit(X, y)
rf
```

{#if fw === 'tf'}

Nhìn vào một vài ví dụ này, có vẻ như mô hình đã học được một số cú pháp của bộ khoa học dữ liệu Python. Tất nhiên, chúng ta sẽ cần phải đánh giá mô hình kỹ lưỡng hơn trước khi triển khai nó trong thế giới thực, nhưng đây vẫn là một nguyên mẫu ấn tượng.

{:else}

Nhìn vào một vài ví dụ này, có vẻ như mô hình đã học được một số cú pháp của bộ khoa học dữ liệu Python (tất nhiên, chúng tôi sẽ cần đánh giá kỹ lưỡng hơn trước khi triển khai mô hình trong thế giới thực). Tuy nhiên, đôi khi nó đòi hỏi phải tùy chỉnh nhiều hơn việc huấn luyện mô hình để đạt được hiệu suất cần thiết cho một trường hợp sử dụng nhất định. Ví dụ: điều gì sẽ xảy ra nếu chúng ta muốn cập nhật động kích thước lô hoặc có một vòng lặp huấn luyện có điều kiện để bỏ qua các ví dụ xấu một cách nhanh chóng? Một tùy chọn sẽ là phân lớp `Trainer` và thêm các thay đổi cần thiết, nhưng đôi khi việc viết vòng lặp huấn luyện từ đầu sẽ đơn giản hơn. Đó là lúc 🤗 Accelerate xuất hiện.

{/if}

{#if fw === 'pt'}

## Huấn luyện với 🤗 Accelerate

Chúng ta đã thấy cách huấn luyện một mô hình với `Trainer`, có thể cho phép một số tùy chỉnh. Tuy nhiên, đôi khi chúng ta muốn toàn quyền kiểm soát vòng lặp huấn luyện hoặc chúng ta muốn thực hiện một số thay đổi kỳ lạ. Trong trường hợp này 🤗 Accelerate là một lựa chọn tuyệt vời và trong phần này, chúng ta sẽ xem xét các bước sử dụng nó để huấn luyện mô hình của mình. Để làm cho mọi thứ thú vị hơn, chúng ta cũng sẽ thêm một số điều chỉnh vào vòng lặp huấn luyện.

<Youtube id="Hm8_PgVTFuc" />

Vì chúng ta chủ yếu quan tâm đến tính năng tự động hoàn thành hợp lý cho các thư viện khoa học dữ liệu, nên việc đưa ra nhiều trọng số hơn cho các mẫu huấn luyện sử dụng nhiều hơn các thư viện này. Chúng ta có thể dễ dàng xác định những ví dụ này thông qua việc sử dụng các từ khóa như `plt`, `pd`, `sk`, `fit` và `predict`, là những tên nhập thường gặp nhất cho `matplotlib.pyplot`, `pandas`, và `sklearn` cũng như các hành vi sau đó. Nếu chúng được biểu diễn dưới dạng một token duy nhất, chúng ta có thể dễ dàng kiểm tra xem chúng có xuất hiện trong chuỗi đầu vào hay không. Các token có thể có tiền tố khoảng trắng, vì vậy chúng ta cũng sẽ kiểm tra các phiên bản đó trong từ vựng bộ tokenizer. Để xác minh rằng nó hoạt động, chúng ta sẽ thêm một token kiểm thử sẽ được chia thành nhiều token:

```py
keytoken_ids = []
for keyword in [
    "plt",
    "pd",
    "sk",
    "fit",
    "predict",
    " plt",
    " pd",
    " sk",
    " fit",
    " predict",
    "testtest",
]:
    ids = tokenizer([keyword]).input_ids[0]
    if len(ids) == 1:
        keytoken_ids.append(ids[0])
    else:
        print(f"Keyword has not single token: {keyword}")
```

```python out
'Keyword has not single token: testtest'
```

Tuyệt vời, điều đó có vẻ hoạt động tốt! Bây giờ chúng ta có thể viết một hàm mất mát tùy chỉnh lấy chuỗi đầu vào, logits và token khóa mà chúng ta vừa chọn làm đầu vào. Trước tiên, chúng ta cần căn chỉnh logits và đầu vào: chuỗi đầu vào được dịch chuyển một đơn vị sang bên phải tạo thành các nhãn, vì token tiếp theo là nhãn cho token hiện tại. Chúng ta có thể đạt được điều này bằng cách bắt đầu các nhãn từ token thứ hai của chuỗi đầu vào, vì dù sao thì mô hình cũng không đưa ra dự đoán cho token đầu tiên. Sau đó, chúng ta cắt logit cuối cùng, vì chúng ta không có nhãn cho token theo trình tự đầu vào đầy đủ. Nhờ đó, chúng ta có thể tính toán sự mất mát trên mỗi mẫu và đếm số lần xuất hiện của tất cả các từ khóa trong mỗi mẫu. Cuối cùng, chúng ta tính giá trị trung bình có trọng số trên tất cả các mẫu bằng cách sử dụng các lần xuất hiện dưới dạng trọng số. Vì chúng ta không muốn loại bỏ tất cả các mẫu không có từ khóa, chúng ta thêm 1 vào các trọng số:

```py
from torch.nn import CrossEntropyLoss
import torch


def keytoken_weighted_loss(inputs, logits, keytoken_ids, alpha=1.0):
    # Dịch chuyển để token < n dự đoán n
    shift_labels = inputs[..., 1:].contiguous()
    shift_logits = logits[..., :-1, :].contiguous()
    # Tính độ mất mát từng token
    loss_fct = CrossEntropyLoss(reduce=False)
    loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
    # Thay đổi kích thước và mất mát trung bình trên mỗi mẫu
    loss_per_sample = loss.view(shift_logits.size(0), shift_logits.size(1)).mean(axis=1)
    # Tính toán và chia tỷ trọng
    weights = torch.stack([(inputs == kt).float() for kt in keytoken_ids]).sum(
        axis=[0, 2]
    )
    weights = alpha * (1.0 + weights)
    # Tính giá trị trung bình có trọng số
    weighted_loss = (loss_per_sample * weights).mean()
    return weighted_loss
```

Trước khi có thể bắt đầu huấn luyện với hàm mất mát mới tuyệt vời này, chúng ta cần chuẩn bị một số thứ:

- Chúng ta cần bộ ghi dữ liệu để tải dữ liệu theo lô.
- Chúng ta cần thiết lập các thông số phân rã trọng số.
- Theo thời gian, chúng ta muốn đánh giá, vì vậy sẽ hợp lý khi bao mã đánh giá trong một hàm.

Hãy bắt đầu với bộ dữ liệu. Chúng ta chỉ cần đặt định dạng của tập dữ liệu thành `"torch"`, và sau đó có thể chuyển nó đến PyTorch `DataLoader` với kích thước lô thích hợp:

```py
from torch.utils.data.dataloader import DataLoader

tokenized_dataset.set_format("torch")
train_dataloader = DataLoader(tokenized_dataset["train"], batch_size=32, shuffle=True)
eval_dataloader = DataLoader(tokenized_dataset["valid"], batch_size=32)
```

Tiếp theo, chúng ta nhóm các tham số để trình tối ưu hóa biết những thông số nào sẽ bị giảm trọng số bổ sung. Thông thường, tất cả các điều khoản thiên vị và trọng số LayerNorm đều được miễn trừ; đây là cách chúng ta có thể làm điều này:

```py
weight_decay = 0.1


def get_grouped_params(model, no_decay=["bias", "LayerNorm.weight"]):
    params_with_wd, params_without_wd = [], []
    for n, p in model.named_parameters():
        if any(nd in n for nd in no_decay):
            params_without_wd.append(p)
        else:
            params_with_wd.append(p)
    return [
        {"params": params_with_wd, "weight_decay": weight_decay},
        {"params": params_without_wd, "weight_decay": 0.0},
    ]
```

Vì chúng ta muốn đánh giá mô hình thường xuyên trên bộ xác nhận trong quá trình huấn luyện, chúng ta hãy viết một hàm cho điều đó. Nó chỉ chạy qua bộ dữ liệu đánh giá và tập hợp tất cả các mất mát qua các quy trình:

```py
def evaluate():
    model.eval()
    losses = []
    for step, batch in enumerate(eval_dataloader):
        with torch.no_grad():
            outputs = model(batch["input_ids"], labels=batch["input_ids"])

        losses.append(accelerator.gather(outputs.loss))
    loss = torch.mean(torch.cat(losses))
    try:
        perplexity = torch.exp(loss)
    except OverflowError:
        perplexity = float("inf")
    return loss.item(), perplexity.item()
```

Với hàm `evaluate()`, chúng ta có thể báo cáo mất mát và [perplexity](/course/chapter7/3) theo khoảng thời gian đều đặn. Tiếp theo, chúng ta xác định lại mô hình của mình để đảm bảo chúng ta huấn luyện lại từ đầu:

```py
model = GPT2LMHeadModel(config)
```

Sau đó, chúng ta có thể xác định trình tối ưu hóa của mình, sử dụng hàm từ trước để phân chia các tham số cho phân rã trọng số:

```py
from torch.optim import AdamW

optimizer = AdamW(get_grouped_params(model), lr=5e-4)
```

Bây giờ, hãy chuẩn bị mô hình, trình tối ưu hóa và bộ ghi dữ liệu để chúng ta có thể bắt đầu huấn luyện:

```py
from accelerate import Accelerator

accelerator = Accelerator(fp16=True)

model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

<Tip>

🚨 Nếu bạn đang huấn luyện trên TPU, bạn sẽ cần chuyển tất cả mã bắt đầu từ ô ở trên vào một hàm huấn luyện chuyên dụng. Xem [Chapter 3](/course/chapter3) để biết thêm chi tiết.

</Tip>

Bây giờ, chúng ta đã gửi `train_dataloader` của mình tới `accelerator.prepare()`, chúng ta có thể sử dụng độ dài của nó để tính số bước huấn luyện. Hãy nhớ rằng chúng ta phải luôn làm điều này sau khi chuẩn bị dataloader, vì phương thức đó sẽ thay đổi độ dài của nó. Chúng ta sử dụng một lịch trình tuyến tính cổ điển từ tốc độ học đến 0:

```py
from transformers import get_scheduler

num_train_epochs = 1
num_update_steps_per_epoch = len(train_dataloader)
num_training_steps = num_train_epochs * num_update_steps_per_epoch

lr_scheduler = get_scheduler(
    name="linear",
    optimizer=optimizer,
    num_warmup_steps=1_000,
    num_training_steps=num_training_steps,
)
```

Cuối cùng, để đẩy mô hình lên Hub, chúng ta sẽ cần tạo một đối tượng `Repository` trong một thư mục đang làm việc. Trước tiên, hãy đăng nhập vào Hugging Face Hub, nếu bạn chưa đăng nhập. Chúng ta sẽ xác định tên kho lưu trữ từ ID mô hình mà ta muốn cung cấp cho mô hình của mình (vui lòng thay thế `repo_name` bằng sự lựa chọn của riêng bạn; nó chỉ cần chứa tên người dùng của bạn, đó là những gì hàm `get_full_repo_name()` thực hiện ):

```py
from huggingface_hub import Repository, get_full_repo_name

model_name = "codeparrot-ds-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'sgugger/codeparrot-ds-accelerate'
```

Sau đó, chúng ta có thể sao chép kho lưu trữ đó trong một thư mục cục bộ. Nếu nó đã tồn tại, thư mục cục bộ này phải là bản sao hiện có của kho lưu trữ mà chúng ta đang làm việc:

```py
output_dir = "codeparrot-ds-accelerate"
repo = Repository(output_dir, clone_from=repo_name)
```

Bây giờ chúng ta có thể tải lên bất cứ thứ gì chúng ta lưu trong `output_dir` bằng cách gọi phương thức `repo.push_to_hub()`. Điều này sẽ giúp chúng ta tải lên các mô hình trung gian ở cuối mỗi epoch.

Trước khi huấn luyện, hãy chạy thử nhanh để xem chức năng đánh giá có hoạt động bình thường không:

```py
evaluate()
```

```python out
(10.934126853942871, 56057.14453125)
```

Đó là những giá trị rất cao về mức mất mát và perplexity, nhưng điều đó không đáng ngạc nhiên vì chúng ta chưa huấn luyện mô hình. Cùng với đó, chúng ta đã chuẩn bị mọi thứ để viết phần cốt lõi của kịch bản huấn luyện: vòng lặp huấn luyện. Trong vòng lặp huấn luyện, chúng ta lặp qua dataloader và truyền các lô vào mô hình. Với nhật ký, sau đó chúng ta có thể đánh giá hàm mất mát tùy chỉnh của mình. Chúng ta chia tỷ lệ mất mát theo số bước tích lũy gradient để không tạo ra mất mát lớn hơn khi tổng hợp nhiều bước hơn. Trước khi tối ưu hóa, chúng ta cũng cắt các gradient để hội tụ tốt hơn. Cuối cùng, cứ sau vài bước, chúng ta đánh giá mô hình trên tập hợp đánh giá với hàm `eval()` mới của mình:

```py
from tqdm.notebook import tqdm

gradient_accumulation_steps = 8
eval_steps = 5_000

model.train()
completed_steps = 0
for epoch in range(num_train_epochs):
    for step, batch in tqdm(
        enumerate(train_dataloader, start=1), total=num_training_steps
    ):
        logits = model(batch["input_ids"]).logits
        loss = keytoken_weighted_loss(batch["input_ids"], logits, keytoken_ids)
        if step % 100 == 0:
            accelerator.print(
                {
                    "lr": get_lr(),
                    "samples": step * samples_per_step,
                    "steps": completed_steps,
                    "loss/train": loss.item() * gradient_accumulation_steps,
                }
            )
        loss = loss / gradient_accumulation_steps
        accelerator.backward(loss)
        if step % gradient_accumulation_steps == 0:
            accelerator.clip_grad_norm_(model.parameters(), 1.0)
            optimizer.step()
            lr_scheduler.step()
            optimizer.zero_grad()
            completed_steps += 1
        if (step % (eval_steps * gradient_accumulation_steps)) == 0:
            eval_loss, perplexity = evaluate()
            accelerator.print({"loss/eval": eval_loss, "perplexity": perplexity})
            model.train()
            accelerator.wait_for_everyone()
            unwrapped_model = accelerator.unwrap_model(model)
            unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
            if accelerator.is_main_process:
                tokenizer.save_pretrained(output_dir)
                repo.push_to_hub(
                    commit_message=f"Training in progress step {step}", blocking=False
                )
```

Vậy là xong - bây giờ bạn có vòng huấn luyện tùy chỉnh của riêng mình cho các mô hình ngôn ngữ nhân quả chẳng hạn như GPT-2 mà bạn có thể tùy chỉnh thêm theo nhu cầu của mình.

<Tip>

✏️ **Thử nghiệm thôi!** Hoặc tạo hàm mất tùy chỉnh của riêng bạn phù hợp với trường hợp sử dụng của bạn hoặc thêm một bước tùy chỉnh khác vào vòng lặp huấn luyện.

</Tip>

<Tip>

✏️ **Thử nghiệm thôi!** Khi chạy các thử nghiệm huấn luyện dài, bạn nên ghi lại các chỉ số quan trọng bằng cách sử dụng các công cụ như TensorBoard hoặc Weights & Biases. Thêm ghi nhật ký thích hợp vào vòng lặp huấn luyện để bạn luôn có thể kiểm tra quá trình huấn luyện diễn ra như thế nào.

</Tip>

{/if}


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter7/6.mdx" />

### Làm chủ NLP
https://huggingface.co/learn/course/vi/chapter7/8.md

# Làm chủ NLP

Nếu bạn đã tiến xa đến mức này trong khóa học, xin chúc mừng - bạn hiện có tất cả kiến ​​thức và công cụ cần thiết để giải quyết (gần như) bất kỳ tác vụ NLP nào với 🤗 Transformers và hệ sinh thái Hugging Face!

Chúng ta đã thấy rất nhiều trình thu thập dữ liệu khác nhau, vì vậy chúng tôi đã tạo video nhỏ này để giúp bạn tìm thấy công cụ nào sẽ sử dụng cho từng tác vụ:

<Youtube id="-RPeakdlHYo"/>

Sau khi hoàn thành chuyến khám phá chớp nhoáng đến các tác vụ NLP cốt lõi, bạn nên:

* Nắm được kiến ​​trúc nào (bộ mã hóa, bộ giải mã hoặc bộ mã hóa-giải mã) phù hợp nhất cho từng tác vụ
* Hiểu sự khác biệt giữa huấn luyện trước trước và tinh chỉnh mô hình ngôn ngữ
* Biết cách huấn luyện các mô hình Transformer bằng cách sử dụng API `Trainer` và các tính năng huấn luyện phân tán của 🤗 Accelerate hoặc TensorFlow và Keras, tùy thuộc vào việc bạn đang theo dõi hướng nào
* Hiểu ý nghĩa và giới hạn của các chỉ số như ROUGE và BLEU đối với các tác vụ tạo văn bản
* Biết cách tương tác với các mô hình được tinh chỉnh của bạn, cả trên Hub và sử dụng `pipeline` từ 🤗 Transformers

Bất chấp tất cả những kiến ​​thức này, sẽ có lúc bạn gặp phải một lỗi khó trong đoạn mã của mình hoặc có câu hỏi về cách giải quyết một vấn đề NLP cụ thể. May mắn thay, cộng đồng Hugging Face ở đây để giúp bạn! Trong chương cuối cùng của phần này của khóa học, chúng ta sẽ khám phá cách bạn có thể gỡ lỗi các mô hình Transformer của mình và yêu cầu trợ giúp một cách hiệu quả.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter7/8.mdx" />

### Dịch máy
https://huggingface.co/learn/course/vi/chapter7/4.md

# Dịch máy

{#if fw === 'pt'}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section4_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section4_pt.ipynb"},
]} />

{:else}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section4_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section4_tf.ipynb"},
]} />

{/if}

Bây giờ chúng ta hãy đi sâu vào dịch máy. Đây là một [tác vụ chuỗi sang chuỗi](/course/chapter1/7), có nghĩa là đây là một vấn đề có thể được hình thành như đi từ một chuỗi này sang chuỗi khác. Theo nghĩa đó, vấn đề khá giống với [tóm tắt](/course/chapter7/6) và bạn có thể điều chỉnh những gì chúng ta sẽ thấy ở đây thành các vấn đề chuỗi sang chuỗi khác như:

- **Chuyển văn phong**: Tạo mô hình *dịch* văn bản được viết theo một phong cách nhất định sang một phong cách khác (ví dụ: từ trang trọng sang thông thường hoặc tiếng Anh Shakespearean sang tiếng Anh hiện đại)
- **Hỏi đáp chung**: Tạo một mô hình tạo câu trả lời cho các câu hỏi, dựa trên ngữ cảnh

<Youtube id="1JvfrvZgi6c"/>

Nếu bạn có một kho văn bản đủ lớn với hai (hoặc nhiều) ngôn ngữ, bạn có thể huấn luyện một mô hình dịch mới từ đầu giống như chúng ta sẽ làm trong phần [lập mô hình ngôn ngữ nhân quả](/course/chapter7/6). Tuy nhiên, sẽ nhanh hơn nếu tinh chỉnh mô hình dịch hiện có, có thể là mô hình đa ngôn ngữ như mT5 hoặc mBART mà bạn muốn tinh chỉnh cho phù hợp với một cặp ngôn ngữ cụ thể hoặc thậm chí là một mô hình chuyên dụng để dịch từ ngôn ngữ này sang ngôn ngữ khác mà bạn muốn tinh chỉnh để phù hợp với kho dữ liệu cụ thể của mình.

Trong phần này, chúng ta sẽ tinh chỉnh mô hình Marian được huấn luyện trước để dịch từ tiếng Anh sang tiếng Pháp (vì rất nhiều nhân viên của Hugging Face nói cả hai ngôn ngữ đó) trên [tập dữ liệu KDE4](https://huggingface.co/datasets/kde4 ), là tập dữ liệu các tệp được bản địa hóa cho [ứng dụng KDE](https://apps.kde.org/). Mô hình chúng ta sẽ sử dụng đã được huấn luyện trước trên một kho dữ liệu lớn gồm các văn bản tiếng Pháp và tiếng Anh được lấy từ [Tập dữ liệu Opus](https://opus.nlpl.eu/), thực sự chứa tập dữ liệu KDE4. Nhưng ngay cả khi mô hình huấn luyện trước mà chúng ta sử dụng đã nhìn thấy dữ liệu đó trong quá trình huấn luyện trước của nó, chúng ta sẽ thấy rằng ta có thể nhận được phiên bản tốt hơn của nó sau khi tinh chỉnh.

Sau khi hoàn thành, chúng ta sẽ có một mô hình có thể đưa ra các dự đoán như sau:

<iframe src="https://course-demos-marian-finetuned-kde4-en-to-fr.hf.space" frameBorder="0" height="350" title="Gradio app" class="block dark:hidden container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

<a class="flex justify-center" href="/huggingface-course/marian-finetuned-kde4-en-to-fr">
<img class="block dark:hidden lg:w-3/5" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/modeleval-marian-finetuned-kde4-en-to-fr.png" alt="One-hot encoded labels for question answering."/>
<img class="hidden dark:block lg:w-3/5" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/modeleval-marian-finetuned-kde4-en-to-fr-dark.png" alt="One-hot encoded labels for question answering."/>
</a>

Như trong các phần trước, bạn có thể tìm thấy mô hình thực tế mà chúng ta sẽ huấn luyện và tải lên Hub bằng cách sử dụng đoạn mã bên dưới và kiểm tra kỹ các dự đoán của nó [tại đây](https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr?text=This+plugin+allows+you+to+automatically+translate+web+pages+between+several+languages.).

## Chuẩn bị dữ liệu

Để tinh chỉnh hoặc huấn luyện một mô hình dịch từ đầu, chúng ta sẽ cần một tập dữ liệu phù hợp với tác vụ. Như đã đề cập trước đây, chúng ta sẽ sử dụng [tập dữ liệu KDE4](https://huggingface.co/datasets/kde4) trong phần này, nhưng bạn có thể điều chỉnh đoạn mã để sử dụng dữ liệu của riêng mình khá dễ dàng, miễn là bạn có các cặp của các câu bằng hai ngôn ngữ mà bạn muốn dịch từ và tới. Tham khảo lại [Chương 5](/course/chapter5) nếu bạn cần lời nhắc về cách tải dữ liệu tùy chỉnh của mình trong `Dataset`.

### Bộ dữ liệu KDE4

Như thường lệ, chúng ta tải xuống tập dữ liệu của mình bằng cách sử dụng hàm `load_dataset()`:

```py
from datasets import load_dataset

raw_datasets = load_dataset("kde4", lang1="en", lang2="fr")
```

Nếu bạn muốn làm việc với một cặp ngôn ngữ khác, bạn có thể chỉ định chúng bằng đoạn mã của chúng. Có tổng số 92 ngôn ngữ có sẵn cho bộ dữ liệu này; bạn có thể thấy tất cả chúng bằng cách mở rộng các thẻ ngôn ngữ trên [thẻ dữ liệu](https://huggingface.co/datasets/kde4) của nó.

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/language_tags.png" alt="Language available for the KDE4 dataset." width="100%">

Hãy xem tập dữ liệu:

```py
raw_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['id', 'translation'],
        num_rows: 210173
    })
})
```

Chúng ta có 210,173 cặp câu, nhưng chỉ trong một lần tách, vì vậy chúng ta sẽ cần tạo bộ kiểm định của riêng mình. Như chúng ta đã thấy trong [Chương 5](/course/chapter5), `Dataset` có phương thức `train_test_split()` có thể giúp chúng ta. Chúng ta sẽ cung cấp một hạt giống (seed) cho khả năng tái tạo:

```py
split_datasets = raw_datasets["train"].train_test_split(train_size=0.9, seed=20)
split_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['id', 'translation'],
        num_rows: 189155
    })
    test: Dataset({
        features: ['id', 'translation'],
        num_rows: 21018
    })
})
```

Bạn có thể đổi `"test"` thành `"validation"` như sau:

```py
split_datasets["validation"] = split_datasets.pop("test")
```

Bây giờ chúng ta hãy xem xét một phần tử của tập dữ liệu:

```py
split_datasets["train"][1]["translation"]
```

```python out
{'en': 'Default to expanded threads',
 'fr': 'Par défaut, développer les fils de discussion'}
```

Chúng ta nhận được một từ điển có hai câu bằng cặp ngôn ngữ mà ta yêu cầu. Một điểm đặc biệt của bộ dữ liệu đầy đủ các thuật ngữ khoa học máy tính kỹ thuật này là chúng đều được dịch hoàn toàn bằng tiếng Pháp. Tuy nhiên, các kỹ sư Pháp thường lười biếng và để lại hầu hết các từ chuyên ngành khoa học máy tính bằng tiếng Anh khi họ nói chuyện. Ví dụ, ở đây, từ "threads" có thể xuất hiện trong một câu tiếng Pháp, đặc biệt là trong một cuộc trò chuyện kỹ thuật; nhưng trong tập dữ liệu này, nó đã được dịch thành đúng hơn là "fils de discussion". Mô hình huấn luyện trước mà chúng ta sử dụng, đã được huấn luyện trước trên một kho ngữ liệu lớn hơn của các câu tiếng Pháp và tiếng Anh, có tùy chọn dễ dàng hơn là để nguyên từ như sau:

```py
from transformers import pipeline

model_checkpoint = "Helsinki-NLP/opus-mt-en-fr"
translator = pipeline("translation", model=model_checkpoint)
translator("Default to expanded threads")
```

```python out
[{'translation_text': 'Par défaut pour les threads élargis'}]
```

Một ví dụ khác về hành vi này có thể được nhìn thấy với từ "plugin", đây không phải là một từ chính thức trong tiếng Pháp nhưng hầu hết người bản ngữ sẽ hiểu và không bận tâm đến việc dịch.
Trong tập dữ liệu KDE4, từ này đã được dịch bằng tiếng Pháp một cách chính thống hơn thành "module d'extension":

```py
split_datasets["train"][172]["translation"]
```

```python out
{'en': 'Unable to import %1 using the OFX importer plugin. This file is not the correct format.',
 'fr': "Impossible d'importer %1 en utilisant le module d'extension d'importation OFX. Ce fichier n'a pas un format correct."}
```

Tuy nhiên, mô hình được huấn luyện trước của chúng ta gắn với từ tiếng Anh nhỏ gọn và quen thuộc:

```py
translator(
    "Unable to import %1 using the OFX importer plugin. This file is not the correct format."
)
```

```python out
[{'translation_text': "Impossible d'importer %1 en utilisant le plugin d'importateur OFX. Ce fichier n'est pas le bon format."}]
```

Sẽ rất thú vị khi xem liệu mô hình tinh chỉnh của mình có tiếp thu những đặc điểm đó của tập dữ liệu hay không (cảnh báo spoiler: nó sẽ xảy ra).

<Youtube id="0Oxphw4Q9fo"/>

<Tip>

✏️ **Đến lượt bạn!** Một từ tiếng Anh khác thường được sử dụng trong tiếng Pháp là "email". Tìm mẫu đầu tiên trong tập dữ liệu huấn luyện sử dụng từ này. Nó được dịch như thế nào? Làm thế nào để mô hình huấn luyện trước dịch cùng một câu tiếng Anh?

</Tip>

### Chuẩn bị dữ liệu

<Youtube id="XAR8jnZZuUs"/>

Bây giờ bạn nên biết điều này: tất cả các văn bản cần được chuyển đổi thành tập hợp các token ID để mô hình có thể hiểu được chúng. Đối với tác vụ này, chúng ta sẽ cần tokenize cả đầu vào và nhãn. Tác vụ đầu tiên của chúng ta là tạo đối tượng `tokenizer`. Như đã lưu ý trước đó, chúng ta sẽ sử dụng mô hình huấn luyện trước từ tiếng Anh sang tiếng Pháp của Marian. Nếu bạn đang thử đoạn mã này với một cặp ngôn ngữ khác, hãy đảm bảo điều chỉnh checkpoint của mô hình. Tổ chức [Helsinki-NLP](https://huggingface.co/Helsinki-NLP) cung cấp hơn một nghìn mô hình bằng nhiều ngôn ngữ.

```python
from transformers import AutoTokenizer

model_checkpoint = "Helsinki-NLP/opus-mt-en-fr"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, return_tensors="tf")
```

You can also replace the `model_checkpoint` with any other model you prefer from the [Hub](https://huggingface.co/models), or a local folder where you've saved a pretrained model and a tokenizer.

<Tip>

💡 Nếu bạn đang sử dụng trình tokenize đa ngôn ngữ như mBART, mBART-50 hoặc M2M100, bạn sẽ cần đặt mã ngôn ngữ của đầu vào và nhãn của mình trong trình tokenize bằng cách đặt `tokenizer.src_lang` và `tokenizer.tgt_lang` ở bên phải các giá trị.

</Tip>

Việc chuẩn bị dữ liệu của chúng ta khá đơn giản. Chỉ có một điều cần nhớ: bạn xử lý các đầu vào như bình thường, nhưng đối với các nhãn, bạn cần phải bọc tokenizer bên trong trình quản lý ngữ cảnh `as_target_tokenizer()`.

Trình quản lý ngữ cảnh trong Python được giới thiệu với câu lệnh `with` và rất hữu ích khi bạn có hai hoạt động liên quan để thực thi như một cặp. Ví dụ phổ biến nhất về điều này là khi bạn viết hoặc đọc một tệp, thường được thực hiện bên trong một lệnh như:

```
with open(file_path) as f:
    content = f.read()
```

Ở đây, hai hoạt động liên quan được thực hiện như một cặp là các hành động mở và đóng tệp. Đối tượng tương ứng với tệp đã mở `f` chỉ tồn tại bên trong khối được thụt lề dưới dấu `with`; sự mở đầu xảy ra trước khối đó và đóng ở cuối khối.

Trong trường hợp này, trình quản lý ngữ cảnh `as_target_tokenizer()` sẽ đặt tokenizer ở ngôn ngữ đầu ra (ở đây, tiếng Pháp) trước khi khối được thụt lề được thực thi, sau đó đặt nó trở lại bằng ngôn ngữ đầu vào (ở đây, tiếng Anh).

Vì vậy, việc xử lý trước một mẫu trông như thế này:

```python
en_sentence = split_datasets["train"][1]["translation"]["en"]
fr_sentence = split_datasets["train"][1]["translation"]["fr"]

inputs = tokenizer(en_sentence)
with tokenizer.as_target_tokenizer():
    targets = tokenizer(fr_sentence)
```

Nếu chúng ta quên tokenize các nhãn bên trong trình quản lý ngữ cảnh, chúng sẽ được tokenize bởi trình tokenize đầu vào, trong trường hợp mô hình Marian sẽ không hoạt động tốt chút nào:

```python
wrong_targets = tokenizer(fr_sentence)
print(tokenizer.convert_ids_to_tokens(wrong_targets["input_ids"]))
print(tokenizer.convert_ids_to_tokens(targets["input_ids"]))
```

```python out
['▁Par', '▁dé', 'f', 'aut', ',', '▁dé', 've', 'lop', 'per', '▁les', '▁fil', 's', '▁de', '▁discussion', '</s>']
['▁Par', '▁défaut', ',', '▁développer', '▁les', '▁fils', '▁de', '▁discussion', '</s>']
```

Như chúng ta có thể thấy, việc sử dụng trình tokenize tiếng Anh để xử lý trước một câu tiếng Pháp dẫn đến nhiều token hơn, vì trình tokenize không biết bất kỳ từ tiếng Pháp nào (ngoại trừ những từ cũng xuất hiện trong tiếng Anh, như "discussion").

Cả `inputs` và `targets` đều là từ điển với các khóa thông thường của chúng ta (ID đầu vào, attention mask, v.v.), vì vậy bước cuối cùng là đặt `"labels"` bên trong các đầu vào. Chúng ta thực hiện điều này trong chức năng tiền xử lý mà ta sẽ áp dụng trên các tập dữ liệu:

```python
max_input_length = 128
max_target_length = 128


def preprocess_function(examples):
    inputs = [ex["en"] for ex in examples["translation"]]
    targets = [ex["fr"] for ex in examples["translation"]]
    model_inputs = tokenizer(inputs, max_length=max_input_length, truncation=True)

    # Thiết lập tokenizer cho nhãn
    with tokenizer.as_target_tokenizer():
        labels = tokenizer(targets, max_length=max_target_length, truncation=True)

    model_inputs["labels"] = labels["input_ids"]
    return model_inputs
```

Note that we set similar maximum lengths for our inputs and outputs. Since the texts we're dealing with seem pretty short, we use 128.

<Tip>

💡 Nếu bạn đang sử dụng mô hình T5 (cụ thể hơn là một trong các checkpoint `t5-xxx`), mô hình sẽ mong đợi các đầu vào văn bản có tiền tố cho biết tác vụ đang thực hiện, chẳng hạn như `translate: English to French:`.

</Tip>

<Tip warning={true}>

⚠️ Chúng ta không chú ý đến attention mask của các nhãn, vì mô hình sẽ không mong đợi điều đó. Thay vào đó, các nhãn tương ứng với token đệm phải được đặt thành `-100` để chúng bị bỏ qua trong tính toán mất mát. Điều này sẽ được thực hiện bởi trình đối chiếu dữ liệu của chúng ta sau này vì chúng ta đang áp dụng đệm động, nhưng nếu bạn sử dụng đệm ở đây, bạn nên điều chỉnh chức năng tiền xử lý để đặt tất cả các nhãn tương ứng với token đệm thành `-100`.

</Tip>

Bây giờ chúng ta có thể áp dụng tiền xử lý đó trong một lần trên tất cả các phần của tập dữ liệu của mình:

```py
tokenized_datasets = split_datasets.map(
    preprocess_function,
    batched=True,
    remove_columns=split_datasets["train"].column_names,
)
```

Bây giờ dữ liệu đã được tiền xử lý, chúng ta đã sẵn sàng để tinh chỉnh mô hình tiền xử lý của mình!

{#if fw === 'pt'}

## Tinh chỉnh mô hình với API `Trainer`

Đoạn mã thực sử dụng `Trainer` sẽ giống như trước đây, chỉ với một thay đổi nhỏ: chúng ta sử dụng [`Seq2SeqTrainer`](https://huggingface.co/transformers/main_classes/trainer.html#seq2seqtrainer) tại đây, là một lớp con của `Trainer` sẽ cho phép chúng ta xử lý tốt việc đánh giá, sử dụng phương thức `generate()` để dự đoán kết quả đầu ra từ các đầu vào. Chúng ta sẽ đi sâu vào vấn đề đó chi tiết hơn khi ta nói về tính toán số liệu.

Điều đầu tiên, chúng ta cần một mô hình thực tế để tinh chỉnh. Chúng ta sẽ sử dụng API `AutoModel`:

```py
from transformers import AutoModelForSeq2SeqLM

model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

{:else}

## Tinh chỉnh mô hình với Keras

Điều đầu tiên, chúng ta cần một mô hình thực tế để tinh chỉnh. Chúng ta sẽ sử dụng API `AutoModel`:

```py
from transformers import TFAutoModelForSeq2SeqLM

model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint, from_pt=True)
```

<Tip warning={false}>

💡 Checkpoint `Helsinki-NLP/opus-mt-en-fr` chỉ có trọng số PyTorch, nên bạn sẽ nhận được lỗi nếu bạn cố tải mô hình mà không sử dụng tham số `from_pt=True`, thư viện sẽ tự động tải và chuyển các trọng số Pytorch cho bạn. Như bạn có thể thấy, rất đơn giản để chuyển giữa các khung trong 🤗 Transformers!

</Tip>

{/if}

Lưu ý rằng lần này chúng ta đang sử dụng một mô hình đã được huấn luyện về tác vụ dịch và thực sự có thể được sử dụng, vì vậy không có cảnh báo nào về việc thiếu các trọng số hoặc những trọng số mới được khởi tạo.

### Đối chiếu dữ liệu

Chúng ta sẽ cần một công cụ đối chiếu dữ liệu để xử lý phần đệm cho phân phối động. Chúng ta không thể chỉ sử dụng một `DataCollatorWithPadding` như [Chương 3](/course/ chapter3) trong trường hợp này, bởi vì điều đó chỉ đệm các đầu vào (ID đầu vào, attention mask, và loại token ID). Các nhãn của chúng ta cũng phải được đệm theo chiều dài tối đa có trong nhãn. Và, như đã đề cập trước đây, giá trị đệm được sử dụng để đệm các nhãn phải là `-100` chứ không phải token đệm của trình tokenize, để đảm bảo các giá trị đệm đó bị bỏ qua trong tính toán mất mát.

Tất cả điều này được thực hiện bởi [`DataCollatorForSeq2Seq`](https://huggingface.co/transformers/main_classes/data_collator.html#datacollatorforseq2seq). Giống như `DataCollatorWithPadding`, nó sử dụng `tokenizer` được sử dụng để xử lý trước các đầu vào, nhưng nó cũng lấy `model`. Điều này là do trình đối chiếu dữ liệu này cũng sẽ chịu trách nhiệm chuẩn bị các ID đầu vào của bộ giải mã, là các phiên bản được dịch chuyển của các nhãn với một token đặc biệt ở đầu. Vì sự thay đổi này được thực hiện hơi khác đối với các kiến ​​trúc khác nhau, nên `DataCollatorForSeq2Seq` cần biết đối tượng `model`:

{#if fw === 'pt'}

```py
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)
```

{:else}

```py
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf")
```

{/if}

Để kiểm tra điều này trên một số mẫu, chúng ta chỉ cần gọi nó trong danh sách các ví dụ từ bộ huấn luyện được tokenize của mình:

```py
batch = data_collator([tokenized_datasets["train"][i] for i in range(1, 3)])
batch.keys()
```

```python out
dict_keys(['attention_mask', 'input_ids', 'labels', 'decoder_input_ids'])
```

Chúng tôi có thể kiểm tra nhãn đã được đệm đến độ dài tối đa của lô hay chưa, bằng cách sử dụng `-100`:

```py
batch["labels"]
```

```python out
tensor([[  577,  5891,     2,  3184,    16,  2542,     5,  1710,     0,  -100,
          -100,  -100,  -100,  -100,  -100,  -100],
        [ 1211,     3,    49,  9409,  1211,     3, 29140,   817,  3124,   817,
           550,  7032,  5821,  7907, 12649,     0]])
```

Và chúng tôi cũng có thể xem xét các ID đầu vào của bộ giải mã, để biết rằng chúng là các phiên bản được thay đổi của nhãn:

```py
batch["decoder_input_ids"]
```

```python out
tensor([[59513,   577,  5891,     2,  3184,    16,  2542,     5,  1710,     0,
         59513, 59513, 59513, 59513, 59513, 59513],
        [59513,  1211,     3,    49,  9409,  1211,     3, 29140,   817,  3124,
           817,   550,  7032,  5821,  7907, 12649]])
```

Dưới đây là các nhãn cho các phần tử đầu tiên và thứ hai trong tập dữ liệu của mình:

```py
for i in range(1, 3):
    print(tokenized_datasets["train"][i]["labels"])
```

```python out
[577, 5891, 2, 3184, 16, 2542, 5, 1710, 0]
[1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124, 817, 550, 7032, 5821, 7907, 12649, 0]
```

{#if fw === 'pt'}

Chúng ta sẽ truyền `data_collator` vào `Seq2SeqTrainer`. Tiếp theo, chúng ta hãy xem xét chỉ số.

{:else}

Ta có thể sử dụng `data_collator` để chuyển mỗi phần dữ liệu thành `tf.data.Dataset`, sẵn sàng để huấn luyện:

```python
tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
    columns=["input_ids", "attention_mask", "labels"],
    collate_fn=data_collator,
    shuffle=True,
    batch_size=32,
)
tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset(
    columns=["input_ids", "attention_mask", "labels"],
    collate_fn=data_collator,
    shuffle=False,
    batch_size=16,
)
```

{/if}

### Thước đo

<Youtube id="M05L1DhFqcw"/>

{#if fw === 'pt'}

Tính năng mà `Seq2SeqTrainer` thêm vào lớp cha `Trainer` của nó là khả năng sử dụng phương thức `generate()` trong quá trình đánh giá hoặc dự đoán. Trong quá trình huấn luyện, mô hình sẽ sử dụng `decoder_input_ids` với attention mask đảm bảo nó không sử dụng các token sau token mà nó đang cố gắng dự đoán, để tăng tốc độ huấn luyện. Trong quá trình luận suy, chúng ta sẽ không thể sử dụng những thứ đó vì chúng ta sẽ không có nhãn, vì vậy, bạn nên đánh giá mô hình của mình với cùng một thiết lập.

Như chúng ta đã thấy trong [Chương 1](/course/chapter1/6), bộ giải mã thực hiện luận suy bằng cách dự đoán từng token - một thứ được triển khai phía sau trong 🤗 Transformers bằng phương thức `generate()`. `Seq2SeqTrainer` sẽ cho phép chúng ta sử dụng phương pháp đó để đánh giá nếu chúng ta đặt `predict_with_generate=True`.

{/if}

Chỉ số truyền thống được sử dụng cho bài toán dịch là [điểm BLEU](https://en.wikipedia.org/wiki/BLEU), được giới thiệu trong [một bài báo năm 2002](https://aclanthology.org/P02-1040.pdf) bởi Kishore Papineni và cộng sự. Điểm BLEU đánh giá mức độ gần gũi của bản dịch với nhãn của chúng. Nó không đo lường mức độ dễ hiểu hoặc tính đúng ngữ pháp của các đầu ra được tạo ra của mô hình, nhưng sử dụng các quy tắc thống kê để đảm bảo rằng tất cả các từ trong các đầu ra được tạo cũng xuất hiện trong các nhãn. Ngoài ra, có các quy tắc phạt việc lặp lại các từ giống nhau nếu chúng không được lặp lại trong các nhãn (để tránh mô hình xuất ra các câu như `"the the the"`) và xuất ra các câu ngắn hơn các câu trong nhãn (để tránh mô hình xuất ra các câu như `"the"`).

Một điểm yếu của BLEU là nó mong đợi văn bản đã được tokenize, điều này gây khó khăn cho việc so sánh điểm giữa các mô hình sử dụng các bộ tokenize khác nhau. Vì vậy, thay vào đó, chỉ số được sử dụng phổ biến nhất cho các mô hình dịch điểm chuẩn ngày nay là [SacreBLEU](https://github.com/mjpost/sacrebleu), giải quyết điểm yếu này (và các chỉ số khác) bằng cách chuẩn hóa bước tokenize. Để sử dụng chỉ số này, trước tiên chúng ta cần cài đặt thư viện SacreBLEU:

```py
!pip install sacrebleu
```

Chúng ta có thể tải nó với `evaluate.load()` như chúng ta đã làm trong [Chương 3](/course/chapter3):

```py
import evaluate

metric = evaluate.load("sacrebleu")
```

Chỉ số này sẽ lấy văn bản làm đầu vào và nhãn. Nó được thiết kế để chấp nhận một số nhãn có thể chấp nhận được, vì thường có nhiều bản dịch có thể chấp nhận được của cùng một câu - tập dữ liệu ta đang sử dụng chỉ cung cấp một nhãn, nhưng không hiếm trong NLP để tìm tập dữ liệu cung cấp một số câu dưới dạng nhãn. Vì vậy, các dự đoán phải là một danh sách các câu, nhưng các tham chiếu phải là một danh sách các danh sách các câu.

Hãy thử một mẫu:

```py
predictions = [
    "This plugin lets you translate web pages between several languages automatically."
]
references = [
    [
        "This plugin allows you to automatically translate web pages between several languages."
    ]
]
metric.compute(predictions=predictions, references=references)
```

```python out
{'score': 46.750469682990165,
 'counts': [11, 6, 4, 3],
 'totals': [12, 11, 10, 9],
 'precisions': [91.67, 54.54, 40.0, 33.33],
 'bp': 0.9200444146293233,
 'sys_len': 12,
 'ref_len': 13}
```

Ta nhận được điểm BLEU là 46.75, khá tốt - để tham khảo, mô hình Transformer ban đầu trong bài báo ["Attention Is All You Need"](https://arxiv.org/pdf/1706.03762.pdf) đạt được điểm BLEU là 41.8 cho một tác vụ dịch tương tự giữa tiếng Anh và tiếng Pháp! (Để biết thêm thông tin về các chỉ số riêng lẻ, như `counts` và `bp`, hãy xem [kho lưu trữ SacreBLEU](https://github.com/mjpost/sacrebleu/blob/078c440168c6adc89ba75fe6d63f0d922d42bcfe/sacrebleu/metrics/bleu.py#L74 ).) Mặt khác, nếu chúng ta thử với hai loại dự đoán không tốt (nhiều lần lặp lại hoặc quá ngắn) thường xuất hiện trong các mô hình dịch, chúng ta sẽ nhận được điểm BLEU khá tệ:

```py
predictions = ["This This This This"]
references = [
    [
        "This plugin allows you to automatically translate web pages between several languages."
    ]
]
metric.compute(predictions=predictions, references=references)
```

```python out
{'score': 1.683602693167689,
 'counts': [1, 0, 0, 0],
 'totals': [4, 3, 2, 1],
 'precisions': [25.0, 16.67, 12.5, 12.5],
 'bp': 0.10539922456186433,
 'sys_len': 4,
 'ref_len': 13}
```

```py
predictions = ["This plugin"]
references = [
    [
        "This plugin allows you to automatically translate web pages between several languages."
    ]
]
metric.compute(predictions=predictions, references=references)
```

```python out
{'score': 0.0,
 'counts': [2, 1, 0, 0],
 'totals': [2, 1, 0, 0],
 'precisions': [100.0, 100.0, 0.0, 0.0],
 'bp': 0.004086771438464067,
 'sys_len': 2,
 'ref_len': 13}
```

Điểm số có thể tăng từ 0 đến 100 và càng cao thì càng tốt.

{#if fw === 'tf'}

Để chuyển từ kết quả đầu ra của mô hình thành văn bản mà chỉ số có thể sử dụng, chúng ta sẽ sử dụng phương thức `tokenizer.batch_decode()`. Chúng ta chỉ cần xóa tất cả các `-100` trong các nhãn; tokenizer sẽ tự động làm điều tương tự đối với token đệm. Hãy xác định một hàm sử dụng mô hình và tập dữ liệu của chúng ta và tính toán các số liệu trên đó. Vì việc tạo chuỗi dài có thể chậm, chúng ta lấy mẫu thay thế bộ kiểm định để đảm bảo điều này không chạy mãi mãi:

```py
import numpy as np


def compute_metrics():
    all_preds = []
    all_labels = []
    sampled_dataset = tokenized_datasets["validation"].shuffle().select(range(200))
    tf_generate_dataset = sampled_dataset.to_tf_dataset(
        columns=["input_ids", "attention_mask", "labels"],
        collate_fn=data_collator,
        shuffle=False,
        batch_size=4,
    )
    for batch in tf_generate_dataset:
        predictions = model.generate(
            input_ids=batch["input_ids"], attention_mask=batch["attention_mask"]
        )
        decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
        labels = batch["labels"].numpy()
        labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
        decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
        decoded_preds = [pred.strip() for pred in decoded_preds]
        decoded_labels = [[label.strip()] for label in decoded_labels]
        all_preds.extend(decoded_preds)
        all_labels.extend(decoded_labels)

    result = metric.compute(predictions=all_preds, references=all_labels)
    return {"bleu": result["score"]}
```

{:else}

To get from the model outputs to texts the metric can use, we will use the `tokenizer.batch_decode()` method. We just have to clean up all the `-100`s in the labels (the tokenizer will automatically do the same for the padding token):

```py
import numpy as np


def compute_metrics(eval_preds):
    preds, labels = eval_preds
    # Trong trường hợp mô hình trả về nhiều hơn logit dự đoán
    if isinstance(preds, tuple):
        preds = preds[0]

    decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)

    # Thay các gía trị -100 trong nhãn vì ta không giải mã chúng
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)

    # Thực một một xố hậu xủ lý đơn giản
    decoded_preds = [pred.strip() for pred in decoded_preds]
    decoded_labels = [[label.strip()] for label in decoded_labels]

    result = metric.compute(predictions=decoded_preds, references=decoded_labels)
    return {"bleu": result["score"]}
```

{/if}

Bây giờ điều này đã hoàn tất, chúng ta đã sẵn sàng tinh chỉnh mô hình của mình!

### Tinh chỉnh mô hình

Bước đầu tiên là đăng nhập vào Hugging Face để bạn có thể tải kết quả của mình lên Model Hub. Có một chức năng tiện lợi để giúp bạn làm điều này trong notebook:

```python
from huggingface_hub import notebook_login

notebook_login()
```

Thao tác này sẽ hiển thị một tiện ích mà bạn có thể nhập thông tin đăng nhập Hugging Face của mình.

Nếu bạn không làm việc trong notebook, chỉ cần nhập dòng sau vào terminal của bạn:

```bash
huggingface-cli login
```

{#if fw === 'tf'}

Trước khi bắt đầu, hãy xem loại kết quả nào chúng tôi nhận được từ mô hình của mình mà không cần huấn luyện:

```py
print(compute_metrics())
```

```
{'bleu': 33.26983701454733}
```

Khi điều này được thực hiện, chúng ta có thể chuẩn bị mọi thứ cần để biên dịch và huấn luyện mô hình của mình. Lưu ý việc sử dụng `tf.keras.mixed_precision.set_global_policy("mixed_float16")` - điều này sẽ yêu cầu Keras huấn luyện bằng cách sử dụng float16, điều có thể giúp tăng tốc đáng kể trên các GPU hỗ trợ nó (Nvidia 20xx/V100 hoặc mới hơn).

```python
from transformers import create_optimizer
from transformers.keras_callbacks import PushToHubCallback
import tensorflow as tf

# Số bước huấn luyện là số lượng mẫu trong tập dữ liệu, chia cho kích thước lô sau đó nhân
# với tổng số epoch. Lưu ý rằng tf_train_dataset ở đây là tf.data.Dataset theo lô,
# không phải là Hugging Face Dataset ban đầu, vì vậy len() của nó vốn là num_samples // batch_size.

num_epochs = 3
num_train_steps = len(tf_train_dataset) * num_epochs

optimizer, schedule = create_optimizer(
    init_lr=5e-5,
    num_warmup_steps=0,
    num_train_steps=num_train_steps,
    weight_decay_rate=0.01,
)
model.compile(optimizer=optimizer)

# Huấn luyện trong mixed-precision float16
tf.keras.mixed_precision.set_global_policy("mixed_float16")
```

Tiếp theo, chúng ta xác định một `PushToHubCallback` để tải mô hình lên Hub trong quá trình huấn luyện, như chúng ta đã thấy trong [phần 2]((/course/chapter7/2)), và sau đó chúng ta chỉ cần điều chỉnh mô hình với lệnh gọi lại đó:

```python
from transformers.keras_callbacks import PushToHubCallback

callback = PushToHubCallback(
    output_dir="marian-finetuned-kde4-en-to-fr", tokenizer=tokenizer
)

model.fit(
    tf_train_dataset,
    validation_data=tf_eval_dataset,
    callbacks=[callback],
    epochs=num_epochs,
)
```

Lưu ý rằng bạn có thể chỉ định tên của kho lưu trữ mà bạn muốn đẩy lên bằng tham số `hub_model_id` (cụ thể là bạn sẽ phải sử dụng tham số này để đẩy lên một tổ chức). Ví dụ: khi chúng tôi đẩy mô hình vào tổ chức [`huggingface-course`](https://huggingface.co/huggingface-course), chúng ta đã thêm `hub_model_id="huggingface-course/marian-finetuned-kde4-en-to-fr"` thành `Seq2SeqTrainingArguments`. Theo mặc định, kho lưu trữ được sử dụng sẽ nằm trong không gian tên của bạn và được đặt tên theo thư mục đầu ra mà bạn đã đặt, vì vậy ở đây nó sẽ là `"sgugger/marian-finetuned-kde4-en-to-fr"` (là mô hình mà chúng tôi đã liên kết với ở đầu phần này).

<Tip>

💡 Nếu thư mục đầu ra bạn đang sử dụng đã tồn tại, nó cần phải là bản sao cục bộ của kho lưu trữ mà bạn muốn đẩy đến. Nếu không, bạn sẽ gặp lỗi khi gọi `model.fit()` và sẽ cần đặt tên mới.

</Tip>

Cuối cùng, hãy xem các chỉ số của chúng ta trông như thế nào khi quá trình huấn luyện đã kết thúc:

```py
print(compute_metrics())
```

```
{'bleu': 57.334066271545865}
```

Ở giai đoạn này, bạn có thể sử dụng tiện ích luận suy trên Model Hub để kiểm tra mô hình của mình và chia sẻ với bạn bè. Bạn đã tinh chỉnh thành công một mô hình trong tác vụ dịch - xin chúc mừng!

{:else}

Khi điều này được thực hiện, chúng ta có thể xác định `Seq2SeqTrainingArguments`. Giống như đối với `Trainer`, chúng ta sử dụng một lớp con của `TrainingArguments` chứa thêm một số trường:

```python
from transformers import Seq2SeqTrainingArguments

args = Seq2SeqTrainingArguments(
    f"marian-finetuned-kde4-en-to-fr",
    evaluation_strategy="no",
    save_strategy="epoch",
    learning_rate=2e-5,
    per_device_train_batch_size=32,
    per_device_eval_batch_size=64,
    weight_decay=0.01,
    save_total_limit=3,
    num_train_epochs=3,
    predict_with_generate=True,
    fp16=True,
    push_to_hub=True,
)
```

Ngoài các siêu tham số thông thường (như tốc độ học, số epoch, kích thước lô và một số phân rã trọng số), đây là một số thay đổi so với những gì chúng ta đã thấy trong các phần trước:

- Chúng ta không đặt bất kỳ đánh giá thường xuyên nào, vì quá trình đánh giá sẽ mất một khoảng thời gian; chúng ta sẽ chỉ đánh giá mô hình của mình một lần trước khi huấn luyện và sau đó.
- Chúng ta đặt `fp16=True`, giúp tăng tốc quá trình huấn luyện trên các GPU hiện đại.
- Chúng ta đặt `predict_with_generate=True`, như đã thảo luận ở trên.
- Chúng ta sử dụng `push_to_hub=True` để tải mô hình lên Hub vào cuối mỗi epoch.

Lưu ý rằng bạn có thể chỉ định tên đầy đủ của kho lưu trữ mà bạn muốn đẩy đến bằng tham số `hub_model_id` (đặc biệt, bạn sẽ phải sử dụng tham số này để đẩy đến một tổ chức). Ví dụ: khi chúng ta đẩy mô hình vào tổ chức [`huggingface-course`](https://huggingface.co/huggingface-course), chúng ta đã thêm `hub_model_id="huggingface-course/marian-finetuned-kde4-en-to-fr"` thành `Seq2SeqTrainingArguments`. Theo mặc định, kho lưu trữ được sử dụng sẽ nằm trong không gian tên của bạn và được đặt tên theo thư mục đầu ra mà bạn đã đặt, vì vậy trong trường hợp của chúng tôi, nó sẽ là `"sgugger/marian-finetuned-kde4-en-to-fr"` (là mô hình chúng tôi liên kết đến ở đầu phần này).

<Tip>

💡 Nếu thư mục đầu ra bạn đang sử dụng đã tồn tại, nó cần phải là bản sao cục bộ của kho lưu trữ mà bạn muốn đẩy đến. Nếu không, bạn sẽ gặp lỗi khi xác định `Seq2SeqTrainer` của mình và sẽ cần đặt tên mới.

</Tip>

Cuối cùng, ta chỉ cần truyền mọi thứ cho `Seq2SeqTrainer`:

```python
from transformers import Seq2SeqTrainer

trainer = Seq2SeqTrainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics,
)
```

Trước khi huấn luyện, trước tiên chúng ta sẽ xem xét điểm mà mô hình của chúng ta nhận được, để kiểm tra kỹ xem chúng ta có đang không làm mọi thứ tồi tệ hơn với việc tinh chỉnh của chúng ta hay không. Lệnh này sẽ mất một chút thời gian, vì vậy bạn có thể uống một ly cà phê trong khi nó thực thi:

```python
trainer.evaluate(max_length=max_target_length)
```

```python out
{'eval_loss': 1.6964408159255981,
 'eval_bleu': 39.26865061007616,
 'eval_runtime': 965.8884,
 'eval_samples_per_second': 21.76,
 'eval_steps_per_second': 0.341}
```

Điểm BLEU là 39 không quá tệ, điều này phản ánh thực tế là mô hình của chúng ta đã rất giỏi trong việc dịch các câu tiếng Anh sang tiếng Pháp.

Tiếp theo là huấn luyện, cũng sẽ mất một chút thời gian:

```python
trainer.train()
```

Lưu ý rằng trong khi quá trình huấn luyện diễn ra, mỗi khi mô hình được lưu (ở đây, mỗi epoch), nó sẽ được tải lên Hub ở chế độ nền. Bằng cách này, bạn sẽ có thể tiếp tục huấn luyện của mình trên một máy khác nếu cần.

Sau khi huấn luyện xong, chúng ta đánh giá lại mô hình của mình - hy vọng chúng ta sẽ thấy một số cải thiện trong điểm BLEU!

```py
trainer.evaluate(max_length=max_target_length)
```

```python out
{'eval_loss': 0.8558505773544312,
 'eval_bleu': 52.94161337775576,
 'eval_runtime': 714.2576,
 'eval_samples_per_second': 29.426,
 'eval_steps_per_second': 0.461,
 'epoch': 3.0}
```

Đó là một cải tiến hơn gần 14 điểm, thật tuyệt vời.

Cuối cùng, chúng ta sử dụng phương thức `push_to_hub()` để đảm bảo tải lên phiên bản mới nhất của mô hình. `Trainer` cũng soạn thảo một thẻ mô hình với tất cả các kết quả đánh giá và tải nó lên. Thẻ mô hình này chứa siêu dữ liệu giúp Model Hub chọn tiện ích con cho bản trình diễn luận suy. Thông thường, không cần phải nói bất cứ điều gì vì nó có thể suy ra tiện ích con phù hợp từ lớp mô hình, nhưng trong trường hợp này, cùng một lớp mô hình có thể được sử dụng cho tất cả các loại vấn đề chuỗi sang chuỗi, vì vậy chúng ta chỉ định đó là một mô hình dịch:

```py
trainer.push_to_hub(tags="translation", commit_message="Training complete")
```
Lệnh này trả về URL của cam kết mà nó vừa thực hiện, nếu bạn muốn kiểm tra nó:

```python out
'https://huggingface.co/sgugger/marian-finetuned-kde4-en-to-fr/commit/3601d621e3baae2bc63d3311452535f8f58f6ef3'
```

Ở giai đoạn này, bạn có thể sử dụng tiện ích luận suy trên Model Hub để kiểm tra mô hình của mình và chia sẻ với bạn bè. Bạn đã tinh chỉnh thành công một mô hình trong tác vụ dịch - xin chúc mừng!

Nếu bạn muốn tìm hiểu sâu hơn một chút về vòng lặp huấn luyện, bây giờ chúng tôi sẽ hướng dẫn bạn cách thực hiện điều tương tự bằng cách sử dụng 🤗 Accelerate.

{/if}

{#if fw === 'pt'}

## Một vòng huấn luyện tùy chỉnh

Bây giờ chúng ta hãy xem toàn bộ vòng lặp huấn luyện, vì vậy bạn có thể dễ dàng tùy chỉnh các phần bạn cần. Nó sẽ trông rất giống những gì chúng ta đã làm trong [phần 2](/course/chapter7/2) và [Chương 3](/course/chapter3/4).

### Chuẩn bị mọi thứ cho qua trình huấn luyện

Bạn đã thấy tất cả điều này một vài lần rồi, vì vậy chúng ta sẽ xem qua đoạn mã khá nhanh. Đầu tiên, chúng ta sẽ xây dựng các `DataLoader` từ các tập dữ liệu của mình, sau khi đặt các tập dữ liệu thành định dạng` "torch" `để chúng ta nhận được các tensor PyTorch:

```py
from torch.utils.data import DataLoader

tokenized_datasets.set_format("torch")
train_dataloader = DataLoader(
    tokenized_datasets["train"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=8,
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], collate_fn=data_collator, batch_size=8
)
```

Tiếp theo, chúng ta khôi phục mô hình của mình, để đảm bảo rằng chúng ta không tiếp tục tinh chỉnh từ trước mà bắt đầu lại từ mô hình đã được huấn luyện trước:

```py
model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

Then we will need an optimizer:

```py
from torch.optim import AdamW

optimizer = AdamW(model.parameters(), lr=2e-5)
```

Khi chúng ta có tất cả các đối tượng đó, chúng ta có thể gửi chúng đến phương thức `accelerator.prepare()`. Hãy nhớ rằng nếu bạn muốn huấn luyện về TPU trong notebook Colab, bạn sẽ cần chuyển tất cả mã này vào một hàm huấn luyện và điều đó sẽ không thực thi bất kỳ ô nào khởi tạo một `Accelerator`.

```py
from accelerate import Accelerator

accelerator = Accelerator()
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

Bây giờ, chúng ta đã gửi `train_dataloader` của mình tới `accelerator.prepare()`, chúng ta có thể sử dụng độ dài của nó để tính số bước huấn luyện. Hãy nhớ rằng chúng ta phải luôn làm điều này sau khi chuẩn bị dataloader, vì phương thức đó sẽ thay đổi độ dài của `DataLoader`. Chúng ta sử dụng một lịch trình tuyến tính cổ điển từ tốc độ học đến 0:

```py
from transformers import get_scheduler

num_train_epochs = 3
num_update_steps_per_epoch = len(train_dataloader)
num_training_steps = num_train_epochs * num_update_steps_per_epoch

lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)
```

Cuối cùng, để đẩy mô hình của mình lên Hub, chúng ta sẽ cần tạo một đối tượng `Repository` trong một thư mục đang làm việc. Đầu tiên hãy đăng nhập vào Hugging Face Hub, nếu bạn chưa đăng nhập. Chúng ta sẽ xác định tên kho lưu trữ từ ID mô hình mà chúng ta muốn cung cấp cho mô hình của mình (vui lòng thay thế `repo_name` bằng sự lựa chọn của riêng bạn; nó chỉ cần chứa tên người dùng của bạn, đó là những gì hàm `get_full_repo_name()` thực hiện):

```py
from huggingface_hub import Repository, get_full_repo_name

model_name = "marian-finetuned-kde4-en-to-fr-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'sgugger/marian-finetuned-kde4-en-to-fr-accelerate'
```

Sau đó, chúng ta có thể sao chép kho lưu trữ đó trong một thư mục cục bộ. Nếu nó đã tồn tại, thư mục cục bộ này phải là bản sao của kho lưu trữ mà chúng ta đang làm việc:

```py
output_dir = "marian-finetuned-kde4-en-to-fr-accelerate"
repo = Repository(output_dir, clone_from=repo_name)
```

Bây giờ chúng ta có thể tải lên bất cứ thứ gì chúng ta lưu trong `output_dir` bằng cách gọi phương thức `repo.push_to_hub()`. Điều này sẽ giúp chúng ta tải lên các mô hình trung gian ở cuối mỗi epoch.

### Vòng lặp huấn luyện

Bây giờ chúng ta đã sẵn sàng để viết vòng lặp huấn luyện đầy đủ. Để đơn giản hóa phần đánh giá của nó, chúng ta định nghĩa hàm `postprocess()` này lấy các dự đoán và nhãn và chuyển đổi chúng thành danh sách các chuỗi mà đối tượng `metric` kì vọng:

```py
def postprocess(predictions, labels):
    predictions = predictions.cpu().numpy()
    labels = labels.cpu().numpy()

    decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)

    # Thay -100 trong nhãn vì ta không thế giải mã chúng.
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)

    # Thực hiện một số hậu xử lý đơn giản
    decoded_preds = [pred.strip() for pred in decoded_preds]
    decoded_labels = [[label.strip()] for label in decoded_labels]
    return decoded_preds, decoded_labels
```

Vòng lặp huấn luyện trông rất giống với các vòng lặp trong [phần 2](/course/chapter7/2) và [Chương 3](/course/chapter3), với một vài điểm khác biệt trong phần đánh giá - vì vậy hãy tập trung vào điều đó!

Điều đầu tiên cần lưu ý là chúng ta sử dụng phương thức `generate()` để tính toán các dự đoán, nhưng đây là một phương thức trên mô hình cơ sở, không phải mô hình được bao bọc 🤗 Accelerate được tạo trong phương thức `prepare()` . Đó là lý do tại sao chúng ta mở mô hình trước, sau đó gọi phương thức này.

Điều thứ hai là, giống như với [phân loại token](/course/chapter7/2), hai quy trình có thể đã đêm các đầu vào và nhãn thành các hình dạng khác nhau, vì vậy chúng tôi sử dụng `accelerator.pad_across_processes()` để đưa ra các dự đoán và nhãn cùng một hình dạng trước khi gọi phương thức `gather()` . Nếu chúng ta không làm điều này, đánh giá sẽ bị lỗi hoặc bị treo vĩnh viễn.

```py
from tqdm.auto import tqdm
import torch

progress_bar = tqdm(range(num_training_steps))

for epoch in range(num_train_epochs):
    # Huấn luyện
    model.train()
    for batch in train_dataloader:
        outputs = model(**batch)
        loss = outputs.loss
        accelerator.backward(loss)

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)

    # Đánh giá
    model.eval()
    for batch in tqdm(eval_dataloader):
        with torch.no_grad():
            generated_tokens = accelerator.unwrap_model(model).generate(
                batch["input_ids"],
                attention_mask=batch["attention_mask"],
                max_length=128,
            )
        labels = batch["labels"]

        # Cần đệm dự đoán và nhãn để dễ gom lại
        generated_tokens = accelerator.pad_across_processes(
            generated_tokens, dim=1, pad_index=tokenizer.pad_token_id
        )
        labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100)

        predictions_gathered = accelerator.gather(generated_tokens)
        labels_gathered = accelerator.gather(labels)

        decoded_preds, decoded_labels = postprocess(predictions_gathered, labels_gathered)
        metric.add_batch(predictions=decoded_preds, references=decoded_labels)

    results = metric.compute()
    print(f"epoch {epoch}, BLEU score: {results['score']:.2f}")

    # Lưu và tải
    accelerator.wait_for_everyone()
    unwrapped_model = accelerator.unwrap_model(model)
    unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
    if accelerator.is_main_process:
        tokenizer.save_pretrained(output_dir)
        repo.push_to_hub(
            commit_message=f"Training in progress epoch {epoch}", blocking=False
        )
```

```python out
epoch 0, BLEU score: 53.47
epoch 1, BLEU score: 54.24
epoch 2, BLEU score: 54.44
```

Khi điều này được thực hiện, bạn sẽ có một mô hình có kết quả khá giống với mô hình được huấn luyện với `Seq2SeqTrainer`. Bạn có thể kiểm tra đoạn mã mà chúng ta đã huấn luyện bằng cách sử dụng mã này tại [*huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate*](https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate). Và nếu bạn muốn kiểm tra bất kỳ tinh chỉnh nào đối với vòng lặp huấn luyện, bạn có thể trực tiếp thực hiện chúng bằng cách chỉnh sửa đoạn mã được hiển thị ở trên!

{/if}

## Sử dụng mô hình tinh chỉnh

Chúng tôi đã chỉ cho bạn cách bạn có thể sử dụng mô hình mà ta đã tinh chỉnh trên Model Hub bằng tiện ích luận suy. Để sử dụng nó cục bộ trong một `pipeline`, chúng ta chỉ cần chỉ định mã định danh mô hình thích hợp:

```py
from transformers import pipeline

# Thay nó với checkpoint của bạn
model_checkpoint = "huggingface-course/marian-finetuned-kde4-en-to-fr"
translator = pipeline("translation", model=model_checkpoint)
translator("Default to expanded threads")
```

```python out
[{'translation_text': 'Par défaut, développer les fils de discussion'}]
```

Đúng như mong đợi, mô hình được huấn luyện trước của chúng ta đã điều chỉnh kiến thức của nó cho phù hợp với kho ngữ liệu mà chúng ta đã tinh chỉnh và thay vì để nguyên từ "thread" trong tiếng Anh, giờ đây nó đã dịch nó sang phiên bản chính thức tiếng Pháp. Đối với "plugin" cũng vậy:

```py
translator(
    "Unable to import %1 using the OFX importer plugin. This file is not the correct format."
)
```

```python out
[{'translation_text': "Impossible d'importer %1 en utilisant le module externe d'importation OFX. Ce fichier n'est pas le bon format."}]
```

Một ví dụ tuyệt vời khác về thích ứng chuyện môn!

<Tip>

✏️ **Đến lượt bạn!** Mô hình trả về cái gì với từ "email" bạn xác định trước đó?

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter7/4.mdx" />

### Hỏi đáp
https://huggingface.co/learn/course/vi/chapter7/7.md

# Hỏi đáp

{#if fw === 'pt'}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section7_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section7_pt.ipynb"},
]} />

{:else}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section7_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section7_tf.ipynb"},
]} />

{/if}

Đã đến lúc xem phần hỏi đáp! Tác vụ này có nhiều loại, nhưng tác vụ mà chúng ta sẽ tập trung vào trong phần này được gọi là trả lời câu hỏi *khai thác*. Điều này liên quan đến việc đặt ra các câu hỏi về một tài liệu và xác định các câu trả lời dưới dạng _các khoảng của văn bản_ trong chính tài liệu đó.

<Youtube id="ajPx5LwJD-I"/>

Chúng ta sẽ tinh chỉnh mô hình BERT trên [bộ dữ liệu SQuAD](https://rajpurkar.github.io/SQuAD-explorer/), bao gồm các câu hỏi do cộng đồng đặt ra trên một tập các bài viết trên Wikipedia. Điều này sẽ cung cấp cho chúng ta một mô hình có thể tính toán các dự đoán như thế này:

<iframe src="https://course-demos-bert-finetuned-squad.hf.space" frameBorder="0" height="450" title="Gradio app" class="block dark:hidden container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Đây thực sự cách mô hình đã được huấn luyện và tải lên Hub bằng cách sử dụng mã được hiển thị trong phần này. Bạn có thể tìm thấy nó và kiểm tra các dự đoạn [tại đây](https://huggingface.co/huggingface-course/bert-finetuned-squad?context=%F0%9F%A4%97+Transformers+is+backed+by+the+three+most+popular+deep+learning+libraries+%E2%80%94+Jax%2C+PyTorch+and+TensorFlow+%E2%80%94+with+a+seamless+integration+between+them.+It%27s+straightforward+to+train+your+models+with+one+before+loading+them+for+inference+with+the+other.&question=Which+deep+learning+libraries+back+%F0%9F%A4%97+Transformers%3F).

<Tip>

💡 Các mô hình mã hóa như BERT có xu hướng tuyệt vời trong việc trích xuất câu trả lời cho các câu hỏi dạng thực tế như "Ai đã phát minh ra kiến trúc Transformer?" nhưng khá kém khi trả lời những câu hỏi mở như "Tại sao bầu trời lại có màu xanh?" Trong những trường hợp khó khăn hơn này, các mô hình mã hóa-giải mã như T5 và BART thường được sử dụng để tổng hợp thông tin theo cách khá giống với [tóm tắt văn bản](/course/chapter7/5). Nếu bạn quan tâm đến kiểu trả lời câu hỏi *chung chung* này, chúng tôi khuyên bạn nên xem [demo](https://yjernite.github.io/lfqa.html) của chúng tôi dựa trên [bộ dữ liệu ELI5](https://huggingface.co/datasets/eli5).

</Tip>

## Chuẩn bị dữ liệu

Tập dữ liệu được sử dụng nhiều nhất làm tiêu chuẩn học thuật để trả lời câu hỏi khai thác là [SQuAD](https://rajpurkar.github.io/SQuAD-explorer/), vì vậy đó là tập chúng ta sẽ sử dụng ở đây. Ngoài ra còn có một điểm chuẩn khó hơn [SQuAD v2](https://huggingface.co/datasets/squad_v2), bao gồm các câu hỏi không có câu trả lời. Miễn là tập dữ liệu của riêng bạn chứa một cột cho ngữ cảnh, một cột cho câu hỏi và một cột cho câu trả lời, bạn sẽ có thể điều chỉnh các bước bên dưới.

### Bộ dữ liệu SQuAD

Như thường lệ, chúng ta có thể tải xuống và lưu bộ dữ liệu vào bộ nhớ cache chỉ trong một bước nhờ vào `load_dataset()`:

```py
from datasets import load_dataset

raw_datasets = load_dataset("squad")
```

Sau đó, chúng ta có thể xem xét đối tượng này để tìm hiểu thêm về tập dữ liệu SQuAD:

```py
raw_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['id', 'title', 'context', 'question', 'answers'],
        num_rows: 87599
    })
    validation: Dataset({
        features: ['id', 'title', 'context', 'question', 'answers'],
        num_rows: 10570
    })
})
```

Có vẻ như chúng ta có mọi thứ ta cần với các trường `context`, `question`, và `answers`, vì vậy hãy in chúng cho phần tử đầu tiên của tập huấn luyện của mình:

```py
print("Context: ", raw_datasets["train"][0]["context"])
print("Question: ", raw_datasets["train"][0]["question"])
print("Answer: ", raw_datasets["train"][0]["answers"])
```

```python out
Context: 'Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend "Venite Ad Me Omnes". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary.'
Question: 'To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France?'
Answer: {'text': ['Saint Bernadette Soubirous'], 'answer_start': [515]}
```

Các trường `context` và `question` rất dễ sử dụng. Trường `answers` phức tạp hơn một chút vì nó so sánh một từ điển với hai trường đều là danh sách. Đây là định dạng sẽ được mong đợi bởi chỉ số `squad` trong quá trình đánh giá; nếu bạn đang sử dụng dữ liệu của riêng mình, bạn không nhất thiết phải lo lắng về việc đặt các câu trả lời ở cùng một định dạng. Trường `text` khá rõ ràng và trường `answer_start` chứa chỉ mục ký tự bắt đầu của mỗi câu trả lời trong ngữ cảnh.

Trong quá trình huấn luyện, chỉ có một câu trả lời khả dĩ. Chúng ta có thể kiểm tra kỹ điều này bằng cách sử dụng phương thức `Dataset.filter()`:

```py
raw_datasets["train"].filter(lambda x: len(x["answers"]["text"]) != 1)
```

```python out
Dataset({
    features: ['id', 'title', 'context', 'question', 'answers'],
    num_rows: 0
})
```

Tuy nhiên, để đánh giá, có một số câu trả lời có thể có cho mỗi mẫu, có thể giống hoặc khác nhau:

```py
print(raw_datasets["validation"][0]["answers"])
print(raw_datasets["validation"][2]["answers"])
```

```python out
{'text': ['Denver Broncos', 'Denver Broncos', 'Denver Broncos'], 'answer_start': [177, 177, 177]}
{'text': ['Santa Clara, California', "Levi's Stadium", "Levi's Stadium in the San Francisco Bay Area at Santa Clara, California."], 'answer_start': [403, 355, 355]}
```

Chúng ta sẽ không đi sâu vào tập lệnh đánh giá vì tất cả sẽ được bao bọc bởi chỉ số 🤗 Datasets, nhưng phiên bản ngắn là một số câu hỏi có một số câu trả lời có thể có và tập lệnh này sẽ so sánh một câu trả lời được dự đoán cho tất cả câu trả lời có thể chấp nhận được và dành điểm cao nhất. Ví dụ: nếu chúng ta xem xét mẫu ở chỉ mục 2:

```py
print(raw_datasets["validation"][2]["context"])
print(raw_datasets["validation"][2]["question"])
```

```python out
'Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi\'s Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the "golden anniversary" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as "Super Bowl L"), so that the logo could prominently feature the Arabic numerals 50.'
'Where did Super Bowl 50 take place?'
```

ta có thể thấy câu trả lời có thể thực ra là một trong số ba khả năng ta thấy trước đó.

### Xử lý dữ liệu huấn luyện

<Youtube id="qgaM0weJHpA"/>

Hãy bắt đầu với việc xử lý trước dữ liệu huấn luyện. Phần khó sẽ là tạo nhãn cho câu trả lời của câu hỏi, đó sẽ là vị trí bắt đầu và kết thúc của các thẻ tương ứng với câu trả lời bên trong ngữ cảnh.

Nhưng chúng ta đừng vượt lên chính mình. Đầu tiên, chúng ta cần chuyển đổi văn bản trong đầu vào thành các ID mà mô hình có thể hiểu được, sử dụng tokenizer:

```py
from transformers import AutoTokenizer

model_checkpoint = "bert-base-cased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
```

Như đã đề cập trước đó, chúng ta sẽ tinh chỉnh mô hình BERT, nhưng bạn có thể sử dụng bất kỳ loại mô hình nào khác miễn là nó có triển khai trình tokenize nhanh. Bạn có thể xem tất cả các kiến trúc đi kèm với phiên bản nhanh trong [bảng lớn này](https://huggingface.co/transformers/#supported-frameworks) và để kiểm tra xem đối tượng `tokenizer` mà bạn đang sử dụng có thực sự là được hỗ trợ bởi 🤗 Tokenizers, bạn có thể xem thuộc tính `is_fast` của nó:
```py
tokenizer.is_fast
```

```python out
True
```

Chúng ta có thể truyền câu hỏi và ngữ cảnh cho trình tokenizer của mình và nó sẽ chèn đúng các token đặc biệt để tạo thành một câu như sau:

```
[CLS] question [SEP] context [SEP]
```

Hãy cùng kiểm tra nó:

```py
context = raw_datasets["train"][0]["context"]
question = raw_datasets["train"][0]["question"]

inputs = tokenizer(question, context)
tokenizer.decode(inputs["input_ids"])
```

```python out
'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Architecturally, '
'the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin '
'Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms '
'upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred '
'Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a '
'replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette '
'Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 statues '
'and the Gold Dome ), is a simple, modern stone statue of Mary. [SEP]'
```

Các nhãn sau đó sẽ là chỉ mục của các token bắt đầu và kết thúc câu trả lời và mô hình sẽ có nhiệm vụ dự đoán một logit bắt đầu và kết thúc cho mỗi token trong đầu vào, với các nhãn lý thuyết như sau:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/qa_labels.svg" alt="One-hot encoded labels for question answering."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/qa_labels-dark.svg" alt="One-hot encoded labels for question answering."/>
</div>

Trong trường hợp này, ngữ cảnh không quá dài, nhưng một số mẫu trong tập dữ liệu có ngữ cảnh rất dài sẽ vượt quá độ dài tối đa mà chúng tôi đặt (trong trường hợp này là 384). Như chúng ta đã thấy trong [Chương 6](/course/chapter6/4) khi chúng ta khám phá phần bên trong của pipeline `question-answering`, chúng ta sẽ đối phó với các ngữ cảnh dài bằng cách tạo một số đặc trưng huấn luyện từ một mẫu tập dữ liệu của mình, với cửa sổ trượt giữa chúng.

Để xem cách này hoạt động như thế nào bằng cách sử dụng ví dụ hiện tại, chúng ta có thể giới hạn độ dài ở 100 và sử dụng cửa sổ trượt gồm 50 token. Xin nhắc lại, chúng ta sử dụng:

- `max_length` để đặt độ dài tối đa (ở đây là 100)
- `truncation="only_second"` để cắt ngắn ngữ cảnh (ở vị trí thứ hai) khi câu hỏi có ngữ cảnh quá dài
- `stride` để đặt số lượng token chồng chéo giữa hai phần liên tiếp (ở đây là 50)
- `return_overflowing_tokens=True` để cho trình tokenizer biết chúng ta muốn các token tràn

```py
inputs = tokenizer(
    question,
    context,
    max_length=100,
    truncation="only_second",
    stride=50,
    return_overflowing_tokens=True,
)

for ids in inputs["input_ids"]:
    print(tokenizer.decode(ids))
```

```python out
'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basi [SEP]'
'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin [SEP]'
'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 [SEP]'
'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP]. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 statues and the Gold Dome ), is a simple, modern stone statue of Mary. [SEP]'
```

Như chúng ta có thể thấy, ví dụ của chúng ta đã được chia thành bốn đầu vào, mỗi đầu vào chứa câu hỏi và một số phần của ngữ cảnh. Lưu ý rằng câu trả lời cho câu hỏi ("Bernadette Soubirous") chỉ xuất hiện trong đầu vào thứ ba và cuối cùng, vì vậy bằng cách xử lý các ngữ cảnh dài theo cách này, chúng ta sẽ tạo một số mẫu huấn luyện trong đó câu trả lời không được đưa vào ngữ cảnh. Đối với những ví dụ đó, nhãn sẽ là `start_position = end_position = 0` (vì vậy chúng tôi dự đoán token `[CLS]`). Chúng ta cũng sẽ đặt các nhãn đó trong trường hợp không may khi câu trả lời đã bị cắt bớt để chúng ta chỉ có phần đầu (hoặc phần cuối) của câu trả lời. Đối với các ví dụ trong đó câu trả lời nằm đầy đủ trong ngữ cảnh, các nhãn sẽ là chỉ mục của token nơi câu trả lời bắt đầu và chỉ mục của token nơi câu trả lời kết thúc.

Tập dữ liệu cung cấp cho chúng ta ký tự bắt đầu của câu trả lời trong ngữ cảnh và bằng cách thêm độ dài của câu trả lời, chúng ta có thể tìm thấy ký tự kết thúc trong ngữ cảnh. Để ánh xạ chúng với các chỉ số token, chúng ta sẽ cần sử dụng ánh xạ offset mà chúng ta đã nghiên cứu trong [Chương 6](/course/chapter6/4). Chúng ta có thể yêu cầu tokenizer trả lại những thứ này bằng cách truyền theo `return_offsets_mapping=True`:

```py
inputs = tokenizer(
    question,
    context,
    max_length=100,
    truncation="only_second",
    stride=50,
    return_overflowing_tokens=True,
    return_offsets_mapping=True,
)
inputs.keys()
```

```python out
dict_keys(['input_ids', 'token_type_ids', 'attention_mask', 'offset_mapping', 'overflow_to_sample_mapping'])
```

Như chúng ta có thể thấy, chúng ta lấy lại các ID đầu vào thông thường, token ID và attention mask, cũng như ánh xạ offset mà chúng ta yêu cầu và một khóa bổ sung, `overflow_to_sample_mapping`. Giá trị tương ứng sẽ được sử dụng cho chúng ta khi tokenize nhiều văn bản cùng một lúc (chúng ta nên làm để hưởng lợi từ thực tế là trình tokenizer được hỗ trợ bởi Rust). Vì một mẫu có thể cung cấp một số đối tượng địa lý, nên nó ánh xạ từng đối tượng địa lý với ví dụ mà nó có nguồn gốc. Bởi vì ở đây chúng ta chỉ tokenize một ví dụ, chúng ta nhận được danh sách các `0`:

```py
inputs["overflow_to_sample_mapping"]
```

```python out
[0, 0, 0, 0]
```

Nhưng nếu chúng ta mã hóa nhiều mẫu hơn, điều này sẽ trở nên hữu ích hơn:

```py
inputs = tokenizer(
    raw_datasets["train"][2:6]["question"],
    raw_datasets["train"][2:6]["context"],
    max_length=100,
    truncation="only_second",
    stride=50,
    return_overflowing_tokens=True,
    return_offsets_mapping=True,
)

print(f"The 4 examples gave {len(inputs['input_ids'])} features.")
print(f"Here is where each comes from: {inputs['overflow_to_sample_mapping']}.")
```

```python out
'The 4 examples gave 19 features.'
'Here is where each comes from: [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3].'
```

Như chúng ta có thể thấy, ba mẫu đầu tiên (tại chỉ số 2, 3 và 4 trong tập huấn luyện) mỗi mẫu đưa ra bốn đặc trưng và mẫu cuối cùng (tại chỉ mục 5 trong tập huấn luyện) đưa ra 7 đặc trưng.

Thông tin này sẽ hữu ích để ánh xạ từng đối tượng mà chúng ta nhận được với nhãn tương ứng của nó. Như đã đề cập trước đó, các nhãn đó là:

- `(0, 0)` nếu câu trả lời không nằm trong khoảng tương ứng của ngữ cảnh
- `(start_position, end_position)` nếu câu trả lời nằm trong khoảng tương ứng của ngữ cảnh, với `start_position` là chỉ mục của token (trong các ID đầu vào) ở đầu câu trả lời và `end_position` là chỉ mục của token (trong các ID đầu vào) nơi câu trả lời kết thúc.

Để xác định đây là trường hợp nào và nếu có liên quan, vị trí của các token, trước tiên chúng ta tìm các chỉ số bắt đầu và kết thúc ngữ cảnh trong các ID đầu vào. Chúng ta có thể sử dụng các token ID để thực hiện việc này, nhưng vì chúng không nhất thiết phải tồn tại cho tất cả các mô hình (ví dụ: DistilBERT không yêu cầu chúng), thay vào đó, chúng ta sẽ sử dụng phương thức `sequence_ids()` của `BatchEncoding` mà tokenizer của ta trả về.

Khi ta có các chỉ mục token đó, chúng ta xem xét các offset, là các bộ giá trị của hai số nguyên đại diện cho khoảng ký tự bên trong ngữ cảnh ban đầu. Do đó, chúng ta có thể phát hiện xem đoạn ngữ cảnh trong đặc trưng này bắt đầu sau câu trả lời hay kết thúc trước khi câu trả lời bắt đầu (trong trường hợp đó nhãn là `(0, 0)`). Nếu không phải như vậy, chúng ta lặp lại để tìm mã token đầu tiên và cuối cùng của câu trả lời:

```py
answers = raw_datasets["train"][2:6]["answers"]
start_positions = []
end_positions = []

for i, offset in enumerate(inputs["offset_mapping"]):
    sample_idx = inputs["overflow_to_sample_mapping"][i]
    answer = answers[sample_idx]
    start_char = answer["answer_start"][0]
    end_char = answer["answer_start"][0] + len(answer["text"][0])
    sequence_ids = inputs.sequence_ids(i)

    # Tìm điểm bắt đầu và kết thúc của ngữ cảnh
    while sequence_ids[idx] != 1:
        idx += 1
    context_start = idx
    while sequence_ids[idx] == 1:
        idx += 1
    context_end = idx - 1

    # Nếu câu trả lời không hoàn toàn nằm trong ngữ cảnh, nhãn là (0, 0)
    if offset[context_start][0] > start_char or offset[context_end][1] < end_char:
        start_positions.append(0)
        end_positions.append(0)
    else:
        # Nếu không nó sẽ là vị trí bắt đầu và kết thúc
        idx = context_start
        while idx <= context_end and offset[idx][0] <= start_char:
            idx += 1
        start_positions.append(idx - 1)

        idx = context_end
        while idx >= context_start and offset[idx][1] >= end_char:
            idx -= 1
        end_positions.append(idx + 1)

start_positions, end_positions
```

```python out
([83, 51, 19, 0, 0, 64, 27, 0, 34, 0, 0, 0, 67, 34, 0, 0, 0, 0, 0],
 [85, 53, 21, 0, 0, 70, 33, 0, 40, 0, 0, 0, 68, 35, 0, 0, 0, 0, 0])
```

Hãy cùng xem một vài kết quả để xác minh rằng cách tiếp cận của chúng ta là đúng. Đối với đặc trưng đầu tiên chúng ta tìm thấy `(83, 85)` dưới dạng nhãn, hãy so sánh câu trả lời lý thuyết với khoảng token được giải mã từ 83 đến 85 (bao gồm):

```py
idx = 0
sample_idx = inputs["overflow_to_sample_mapping"][idx]
answer = answers[sample_idx]["text"][0]

start = start_positions[idx]
end = end_positions[idx]
labeled_answer = tokenizer.decode(inputs["input_ids"][idx][start : end + 1])

print(f"Theoretical answer: {answer}, labels give: {labeled_answer}")
```

```python out
'Theoretical answer: the Main Building, labels give: the Main Building'
```

Kết quả khá là khớp nhau! Bây giờ chúng ta hãy kiểm tra chỉ mục 4, nơi chúng ta đặt nhãn thành `(0, 0)`, có nghĩa là câu trả lời không nằm trong phần ngữ cảnh của đặc trưng đó:

```py
idx = 4
sample_idx = inputs["overflow_to_sample_mapping"][idx]
answer = answers[sample_idx]["text"][0]

decoded_example = tokenizer.decode(inputs["input_ids"][idx])
print(f"Theoretical answer: {answer}, decoded example: {decoded_example}")
```

```python out
'Theoretical answer: a Marian place of prayer and reflection, decoded example: [CLS] What is the Grotto at Notre Dame? [SEP] Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grot [SEP]'
```

Bây giờ chúng ta đã thấy từng bước cách tiền xử lý dữ liệu huấn luyện của mình, chúng ta có thể nhóm nó trong một hàm mà ta sẽ áp dụng trên toàn bộ tập dữ liệu huấn luyện. Chúng ta sẽ đệm mọi đặc trưng đến độ dài tối đa mà ta đã đặt, vì hầu hết các ngữ cảnh sẽ dài (và các mẫu tương ứng sẽ được chia thành nhiều đặc trưng), vì vậy không có lợi ích thực sự nào khi áp dụng đệm động ở đây:

Thật vậy, chúng ta không thấy câu trả lời bên trong ngữ cảnh.

<Tip>

✏️ **Đến lượt bạn!** Khi sử dụng kiến trúc XLNet, phần đệm được áp dụng ở bên trái và câu hỏi và ngữ cảnh được chuyển đổi. Điều chỉnh tất cả mã chúng ta vừa thấy với kiến trúc XLNet (và thêm `padding=True`). Lưu ý rằng token `[CLS]` có thể không ở vị trí 0 khi áp dụng phần đệm.

</Tip>

Bây giờ chúng ta đã thấy từng bước cách tiền xử lý dữ liệu huấn luyện của mình, chúng ta có thể nhóm nó trong một hàm mà chúng ta sẽ áp dụng trên toàn bộ tập dữ liệu huấn luyện. Chúng ta sẽ đệm mọi đặc trưng đến độ dài tối đa mà chúng ta đã đặt, vì hầu hết các ngữ cảnh sẽ dài (và các mẫu tương ứng sẽ được chia thành nhiều đặc trưng), vì vậy không có lợi ích thực sự nào khi áp dụng đệm động ở đây:

```py
max_length = 384
stride = 128


def preprocess_training_examples(examples):
    questions = [q.strip() for q in examples["question"]]
    inputs = tokenizer(
        questions,
        examples["context"],
        max_length=max_length,
        truncation="only_second",
        stride=stride,
        return_overflowing_tokens=True,
        return_offsets_mapping=True,
        padding="max_length",
    )

    offset_mapping = inputs.pop("offset_mapping")
    sample_map = inputs.pop("overflow_to_sample_mapping")
    answers = examples["answers"]
    start_positions = []
    end_positions = []

    for i, offset in enumerate(offset_mapping):
        sample_idx = sample_map[i]
        answer = answers[sample_idx]
        start_char = answer["answer_start"][0]
        end_char = answer["answer_start"][0] + len(answer["text"][0])
        sequence_ids = inputs.sequence_ids(i)

        # Tìm điểm bắt đầu và kết thúc của ngữ cảnh
        idx = 0
        while sequence_ids[idx] != 1:
            idx += 1
        context_start = idx
        while sequence_ids[idx] == 1:
            idx += 1
        context_end = idx - 1

        # Nếu câu trả lời không hoàn toàn nằm trong ngữ cảnh, nhãn là (0, 0)
        if offset[context_start][0] > start_char or offset[context_end][1] < end_char:
            start_positions.append(0)
            end_positions.append(0)
        else:
            # Nếu không nó sẽ là vị trí token bắt đầu và kết thúc
            idx = context_start
            while idx <= context_end and offset[idx][0] <= start_char:
                idx += 1
            start_positions.append(idx - 1)

            idx = context_end
            while idx >= context_start and offset[idx][1] >= end_char:
                idx -= 1
            end_positions.append(idx + 1)

    inputs["start_positions"] = start_positions
    inputs["end_positions"] = end_positions
    return inputs
```

Lưu ý rằng chúng ta đã xác định hai hằng số để xác định độ dài tối đa được sử dụng cũng như độ dài của cửa sổ trượt và ta đã thêm một chút dọn dẹp trước khi tokenize: một số câu hỏi trong tập dữ liệu SQuAD có thêm khoảng trắng ở đầu và kết thúc mà không thêm bất kỳ thứ gì (và chiếm dung lượng khi được tokenize nếu bạn sử dụng mô hình như RoBERTa), vì vậy ta đã xóa những khoảng trắng thừa đó.

Để áp dụng hàm này cho toàn bộ tập huấn luyện, chúng ta sử dụng phương thức `Dataset.map()` với `batched=True`. Điều này cần thiết ở đây vì ta đang thay đổi độ dài của tập dữ liệu (vì một mẫu có thể cung cấp một số đặc trưng huấn luyện):

```py
train_dataset = raw_datasets["train"].map(
    preprocess_training_examples,
    batched=True,
    remove_columns=raw_datasets["train"].column_names,
)
len(raw_datasets["train"]), len(train_dataset)
```

```python out
(87599, 88729)
```

Như ta có thể thấy, quá trình tiền xử lý đã thêm khoảng 1,000 đặc trưng. Bộ huấn luyện hiện đã sẵn sàng để sử dụng - hãy cùng tìm hiểu về quá trình tiền xử lý của bộ kiểm định!

### Xử lý dữ liệu kiểm định

Việc xử lý trước dữ liệu kiểm định sẽ dễ dàng hơn một chút vì chúng ta không cần tạo nhãn (trừ khi chúng ta muốn tính toán mất mát kiểm định, nhưng con số đó sẽ không thực sự giúp chúng ta hiểu mô hình tốt như thế nào). Niềm vui thực sự sẽ là diễn giải các dự đoán của mô hình thành các khoảng của bối cảnh ban đầu. Đối với điều này, chúng ta sẽ chỉ cần lưu trữ cả ánh xạ offset và một số cách để khớp từng đối tượng đã tạo với ví dụ ban đầu mà nó xuất phát. Vì có một cột ID trong tập dữ liệu gốc, chúng ta sẽ sử dụng ID đó.

Điều duy nhất chúng ta sẽ thêm ở đây là một chút dọn dẹp các ánh xạ offset. Chúng sẽ chứa các phần bù cho câu hỏi và ngữ cảnh, nhưng khi chúng ta đang ở giai đoạn hậu xử lý, chúng ta sẽ không có cách nào để biết phần nào của ID đầu vào tương ứng với ngữ cảnh và phần nào là câu hỏi (phương thức `sequence_ids()` ta đã sử dụng chỉ có sẵn cho đầu ra của tokenizer). Vì vậy, chúng ta sẽ đặt các offset tương ứng với câu hỏi thành `None`:

```py
def preprocess_validation_examples(examples):
    questions = [q.strip() for q in examples["question"]]
    inputs = tokenizer(
        questions,
        examples["context"],
        max_length=max_length,
        truncation="only_second",
        stride=stride,
        return_overflowing_tokens=True,
        return_offsets_mapping=True,
        padding="max_length",
    )

    sample_map = inputs.pop("overflow_to_sample_mapping")
    example_ids = []

    for i in range(len(inputs["input_ids"])):
        sample_idx = sample_map[i]
        example_ids.append(examples["id"][sample_idx])

        sequence_ids = inputs.sequence_ids(i)
        offset = inputs["offset_mapping"][i]
        inputs["offset_mapping"][i] = [
            o if sequence_ids[k] == 1 else None for k, o in enumerate(offset)
        ]

    inputs["example_id"] = example_ids
    return inputs
```

Chúng ta có thể áp dụng hàm này trên toàn bộ tập dữ liệu kiểm định như trước đây:

```py
validation_dataset = raw_datasets["validation"].map(
    preprocess_validation_examples,
    batched=True,
    remove_columns=raw_datasets["validation"].column_names,
)
len(raw_datasets["validation"]), len(validation_dataset)
```

```python out
(10570, 10822)
```

Trong trường hợp này, chúng ta chỉ thêm một vài trăm mẫu, vì vậy có vẻ như các ngữ cảnh trong tập dữ liệu kiểm định ngắn hơn một chút.

Bây giờ chúng ta đã tiền xử lý tất cả dữ liệu, chúng ta có thể tham gia khóa huấn luyện.

{#if fw === 'pt'}

## Tinh chỉnh mô hìn với API `Trainer`

Đoạn mã huấn luyện cho mẫu này sẽ trông rất giống trong các phần trước - điều khó nhất sẽ là viết hàm `compute_metrics()`. Vì chúng ta đã đệm tất cả các mẫu đến độ dài tối đa mà ta đặt, không có công cụ đối chiếu dữ liệu để xác định, vì vậy việc tính toán số liệu này thực sự là điều duy nhất chúng ta phải lo lắng. Phần khó khăn sẽ là hậu xử lý các dự đoán của mô hình thành các khoảng văn bản trong các ví dụ ban đầu; khi ta đã làm điều đó, chỉ số từ thư viện 🤗 Datasets sẽ thực hiện hầu hết công việc cho mình.

{:else}

## Tinh chỉnh mô hìn với Keras

Đoạn mã huấn luyện cho mẫu này sẽ trông rất giống trong các phần trước, nhưng việc tính toán các số liệu sẽ là một thử thách độc đáo. Vì chúng ta đã đệm tất cả các mẫu đến độ dài tối đa mà chúng ta đặt, không có công cụ đối chiếu dữ liệu để xác định, vì vậy việc tính toán số liệu này thực sự là điều duy nhất ta phải lo lắng. Phần khó sẽ là hậu xử lý các dự đoán của mô hình thành các khoảng văn bản trong các ví dụ ban đầu; khi chúng ta đã làm điều đó, chỉ số từ thư viện 🤗 Datasets sẽ thực hiện hầu hết công việc cho ta.

{/if}

### Hậu xử lý

{#if fw === 'pt'}

<Youtube id="BNy08iIWVJM"/>

{:else}

<Youtube id="VN67ZpN33Ss"/>

{/if}

Mô hình sẽ trả về các logit đầu ra cho các vị trí bắt đầu và kết thúc của câu trả lời trong ID đầu vào, như chúng ta đã thấy trong quá trình khám phá pipeline [`question-answering`](/course/chapter6/3b). Bước tiền xử lý sẽ tương tự như những gì chúng ta đã làm ở đó, vì vậy đây là lời nhắc nhanh về các bước chúng ta đã thực hiện:

- Chúng ta đã che các logit bắt đầu và kết thúc tương ứng với các token bên ngoài ngữ cảnh.
- Sau đó, chúng ta chuyển đổi các logit bắt đầu và kết thúc thành xác suất bằng cách sử dụng softmax.
- Chúng ta quy điểm cho từng cặp `(start_token, end_token)` cách lấy tích của hai xác suất tương ứng.
- Chúng ta đã tìm kiếm cặp có điểm tối đa mang lại câu trả lời hợp lệ (ví dụ: `start_token` thấp hơn `end_token`).

Ở đây, chúng ta sẽ thay đổi quy trình này một chút vì chúng ta không cần tính điểm thực tế (chỉ là câu trả lời dự đoán). Điều này có nghĩa là chúng ta có thể bỏ qua bước softmax. Để đi nhanh hơn, chúng ta cũng sẽ không tính điểm tất cả các cặp `(start_token, end_token)` có thể, mà chỉ những cặp tương ứng với logit `n_best` cao nhất (với `n_best = 20`). Vì chúng ta sẽ bỏ qua softmax, những điểm đó sẽ là điểm logit và sẽ có được bằng cách lấy tổng của logit bắt đầu và kết thúc (thay vì nhân, vì quy tắc \(\log(ab) = \log(a) + \log(b)\\)).

Để chứng minh tất cả những điều này, chúng ta sẽ cần một số loại dự đoán. Vì chúng ta chưa huấn luyện mô hình của mình, chúng ta sẽ sử dụng mô hình mặc định cho pipeline QA để tạo ra một số dự đoán trên một phần nhỏ của tập hợp kiểm. Chúng ta có thể sử dụng chức năng xử lý tương tự như trước đây; bởi vì nó dựa vào hằng số toàn cục `tokenizer`, chúng ta chỉ cần thay đổi đối tượng đó thành tokenizer của mô hình mà chúng ta muốn sử dụng tạm thời:

```python
small_eval_set = raw_datasets["validation"].select(range(100))
trained_checkpoint = "distilbert-base-cased-distilled-squad"

tokenizer = AutoTokenizer.from_pretrained(trained_checkpoint)
eval_set = small_eval_set.map(
    preprocess_validation_examples,
    batched=True,
    remove_columns=raw_datasets["validation"].column_names,
)
```

Bây giờ, quá trình tiền xử lý đã hoàn tất, chúng ta thay đổi tokenizer trở lại cái mà chúng ta đã chọn ban đầu:

```python
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
```

Sau đó, chúng ta loại bỏ các cột của `eval_set` mà mô hình không mong đợi, xây dựng một lô với tất cả bộ kiểm định nhỏ đó và chuyển nó qua mô hình. Nếu có sẵn GPU, chúng ta sử dụng nó để chạy nhanh hơn:

{#if fw === 'pt'}

```python
import torch
from transformers import AutoModelForQuestionAnswering

eval_set_for_model = eval_set.remove_columns(["example_id", "offset_mapping"])
eval_set_for_model.set_format("torch")

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
batch = {k: eval_set_for_model[k].to(device) for k in eval_set_for_model.column_names}
trained_model = AutoModelForQuestionAnswering.from_pretrained(trained_checkpoint).to(
    device
)

with torch.no_grad():
    outputs = trained_model(**batch)
```

Vì `Trainer` sẽ trả cho ta các dự đoán dưới dạng mảng NumPy, ta sẽ lấy các logit bắt đầu và kết thúc và chuyển nó thành dạng:

```python
start_logits = outputs.start_logits.cpu().numpy()
end_logits = outputs.end_logits.cpu().numpy()
```

{:else}

```python
import tensorflow as tf
from transformers import TFAutoModelForQuestionAnswering

eval_set_for_model = eval_set.remove_columns(["example_id", "offset_mapping"])
eval_set_for_model.set_format("numpy")

batch = {k: eval_set_for_model[k] for k in eval_set_for_model.column_names}
trained_model = TFAutoModelForQuestionAnswering.from_pretrained(trained_checkpoint)

outputs = trained_model(**batch)
```

Để dễ dàng thử nghiệm, hãy chuyển đổi các kết quả đầu ra này thành mảng NumPy:

```python
start_logits = outputs.start_logits.numpy()
end_logits = outputs.end_logits.numpy()
```

{/if}

Bây giờ, chúng ta cần tìm câu trả lời dự đoán cho từng ví dụ trong `small_eval_set` của chúng ta. Một ví dụ có thể đã được chia thành nhiều đặc trưng trong `eval_set`, vì vậy bước đầu tiên là ánh xạ từng mẫu trong `small_eval_set` với các đặc trưng tương ứng trong `eval_set`:

```python
import collections

example_to_features = collections.defaultdict(list)
for idx, feature in enumerate(eval_set):
    example_to_features[feature["example_id"]].append(idx)
```

Với điều này trong tay, chúng ta thực sự có thể bắt đầu làm việc bằng cách lặp lại tất cả các mẫu và, đối với mỗi mẫu, thông qua tất cả các đặc trưng liên quan. Như chúng ta đã nói trước đây, chúng ta sẽ xem xét điểm logit cho các logit bắt đầu và kết thúc của `n_best`, ngoại trừ các vị trí cung cấp:

- Một câu trả lời sẽ không nằm trong ngữ cảnh
- Một câu trả lời có độ dài âm
- Một câu trả lời quá dài (chúng ta giới hạn khả năng ở mức `max_answer_length=30`)

Khi chúng ta có tất cả các câu trả lời có thể được ghi cho một mẫu, ta chỉ cần chọn một câu có điểm logit tốt nhất:

```python
import numpy as np

n_best = 20
max_answer_length = 30
predicted_answers = []

for example in small_eval_set:
    example_id = example["id"]
    context = example["context"]
    answers = []

    for feature_index in example_to_features[example_id]:
        start_logit = start_logits[feature_index]
        end_logit = end_logits[feature_index]
        offsets = eval_set["offset_mapping"][feature_index]

        start_indexes = np.argsort(start_logit)[-1 : -n_best - 1 : -1].tolist()
        end_indexes = np.argsort(end_logit)[-1 : -n_best - 1 : -1].tolist()
        for start_index in start_indexes:
            for end_index in end_indexes:
                # Bỏ qua các câu trả lời không đầu đủ trong ngữ cảnh
                if offsets[start_index] is None or offsets[end_index] is None:
                    continue
                # Bỏ qua những câu trả lời có độ dài < 0 hoặc > max_answer_length.
                if (
                    end_index < start_index
                    or end_index - start_index + 1 > max_answer_length
                ):
                    continue

                answers.append(
                    {
                        "text": context[offsets[start_index][0] : offsets[end_index][1]],
                        "logit_score": start_logit[start_index] + end_logit[end_index],
                    }
                )

    best_answer = max(answers, key=lambda x: x["logit_score"])
    predicted_answers.append({"id": example_id, "prediction_text": best_answer["text"]})
```

Định dạng cuối cùng của các câu trả lời được dự đoán là định dạng sẽ được dự đoán theo chỉ số mà chúng ta sẽ sử dụng. Như thường lệ, chúng ta có thể tải nó với sự trợ giúp của thư viện 🤗 Evaluate:

```python
import evaluate

metric = evaluate.load("squad")
```

Thước đo này mong đợi các câu trả lời được dự đoán ở định dạng mà chúng ta đã thấy ở trên (danh sách các từ điển có một khóa cho ID của mẫu và một khóa cho văn bản được dự đoán) và các câu trả lời lý thuyết ở định dạng bên dưới (danh sách các từ điển có một khóa cho ID của mẫu và một khóa cho các câu trả lời có thể có):


```python
theoretical_answers = [
    {"id": ex["id"], "answers": ex["answers"]} for ex in small_eval_set
]
```

Bây giờ chúng ta có thể kiểm tra xem ta có nhận được kết quả hợp lý hay không bằng cách xem xét yếu tố đầu tiên của cả hai danh sách:

```python
print(predicted_answers[0])
print(theoretical_answers[0])
```

```python out
{'id': '56be4db0acb8001400a502ec', 'prediction_text': 'Denver Broncos'}
{'id': '56be4db0acb8001400a502ec', 'answers': {'text': ['Denver Broncos', 'Denver Broncos', 'Denver Broncos'], 'answer_start': [177, 177, 177]}}
```

Không tệ lắm! Bây giờ chúng ta hãy xem xét điểm số mà số liệu mang lại cho chúng ta:

```python
metric.compute(predictions=predicted_answers, references=theoretical_answers)
```

```python out
{'exact_match': 83.0, 'f1': 88.25}
```

Một lần nữa, điều đó khá tốt theo [bài báo của nó](https://arxiv.org/abs/1910.01108v2), DistilBERT được tinh chỉnh trên SQuAD thu được 79.1 và 86.9 trên toàn bộ tập dữ liệu.

{#if fw === 'pt'}

Bây giờ chúng ta hãy đặt mọi thứ ta vừa làm trong một hàm `compute_metrics()` mà ta sẽ sử dụng trong `Trainer`. Thông thường, hàm `compute_metrics()` đó chỉ nhận được một tuple `eval_preds` với các logit và nhãn. Ở đây chúng ta sẽ cần nhiều hơn một chút, vì chúng ta phải tìm trong tập dữ liệu các đặc trưng cho phần bù và trong tập dữ liệu các ví dụ cho các ngữ cảnh ban đầu, vì vậy chúng ta sẽ không thể sử dụng chức năng này để nhận kết quả đánh giá thường xuyên trong quá trình huấn luyện. Chúng ta sẽ chỉ sử dụng nó khi kết thúc khóa huấnl luyện để kiểm tra kết quả.

Hàm `compute_metrics()` nhóm các bước giống như trước; chúng ta chỉ thêm một kiểm tra nhỏ trong trường hợp ta không đưa ra bất kỳ câu trả lời hợp lệ nào (trong trường hợp đó ta dự đoán một chuỗi trống).

{:else}

Bây giờ, hãy đặt mọi thứ ta vừa làm vào một hàm `compute_metrics()` mà ta sẽ sử dụng sau khi huấn luyện mô hình của mình. Chúng ta sẽ cần truyền nhiều hơn là nhật ký đầu ra, vì ta phải tìm trong tập dữ liệu các đặc trưng cho phần offset và trong tập dữ liệu các mẫu cho các ngữ cảnh ban đầu:

{/if}

```python
from tqdm.auto import tqdm


def compute_metrics(start_logits, end_logits, features, examples):
    example_to_features = collections.defaultdict(list)
    for idx, feature in enumerate(features):
        example_to_features[feature["example_id"]].append(idx)

    predicted_answers = []
    for example in tqdm(examples):
        example_id = example["id"]
        context = example["context"]
        answers = []

        # Lặp qua tất cả các đặc trưng liên quan tới mẫu đó
        for feature_index in example_to_features[example_id]:
            start_logit = start_logits[feature_index]
            end_logit = end_logits[feature_index]
            offsets = features[feature_index]["offset_mapping"]

            start_indexes = np.argsort(start_logit)[-1 : -n_best - 1 : -1].tolist()
            end_indexes = np.argsort(end_logit)[-1 : -n_best - 1 : -1].tolist()
            for start_index in start_indexes:
                for end_index in end_indexes:
                    # Bỏ qua câu trả lời không xuất hiện hoàn toàn trong ngữ cảnh
                    if offsets[start_index] is None or offsets[end_index] is None:
                        continue
                    # Bỏ qua những câu trả lời với độ dài < 0 hoặc > max_answer_length
                    if (
                        end_index < start_index
                        or end_index - start_index + 1 > max_answer_length
                    ):
                        continue

                    answer = {
                        "text": context[offsets[start_index][0] : offsets[end_index][1]],
                        "logit_score": start_logit[start_index] + end_logit[end_index],
                    }
                    answers.append(answer)

        # Chọn câu trả lời có điểm cao nhất
        if len(answers) > 0:
            best_answer = max(answers, key=lambda x: x["logit_score"])
            predicted_answers.append(
                {"id": example_id, "prediction_text": best_answer["text"]}
            )
        else:
            predicted_answers.append({"id": example_id, "prediction_text": ""})

    theoretical_answers = [{"id": ex["id"], "answers": ex["answers"]} for ex in examples]
    return metric.compute(predictions=predicted_answers, references=theoretical_answers)
```

Chúng ta có thể kiểm tra nó hoạt động dựa trên dự đoán của mình:

```python
compute_metrics(start_logits, end_logits, eval_set, small_eval_set)
```

```python out
{'exact_match': 83.0, 'f1': 88.25}
```

Trông khá ổn! Bây giờ chúng ta hãy sử dụng điều này để tinh chỉnh mô hình của mình.

### Tinh chỉnh mô hình

{#if fw === 'pt'}

Giờ ta đã sẵn sàng để huấn luyện mô hình của mình. Hãy cũng tạo ra nó sử dụng lớp `AutoModelForQuestionAnswering` như trước đó:

```python
model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
```

{:else}

Giờ ta đã sẵn sàng để huấn luyện mô hình của mình. Hãy cũng tạo ra nó sử dụng lớp `TFAutoModelForQuestionAnswering` như trước đó:

```python
model = TFAutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
```

{/if}

Như thường lệ, chúng ta nhận được cảnh báo rằng một số trọng số không được sử dụng (các trọng số từ phần đầu huấn luyện trước) và một số trọng số khác được khởi tạo ngẫu nhiên (các trọng số cho đầu trả lời câu hỏi). Bây giờ bạn nên quen với điều này, nhưng điều đó có nghĩa là mô hình này chưa sẵn sàng để sử dụng và cần được tinh chỉnh - điều tốt là chúng ta sắp làm được điều đó!

Để có thể đẩy mô hình của mình lên Hub, chúng ta cần đăng nhập vào Hugging Face. Nếu bạn đang chạy đoạn mã này trong notebook, bạn có thể làm như vậy với hàm tiện ích sau, hàm này sẽ hiển thị một tiện ích mà bạn có thể nhập thông tin đăng nhập của mình:

```python
from huggingface_hub import notebook_login

notebook_login()
```

Nếu bạn không làm việc trong notebook, chỉ cần nhập dòng sau vào thiết bị đầu cuối của bạn:

```bash
huggingface-cli login
```

{#if fw === 'pt'}

Khi điều này được thực hiện, chúng ta có thể xác định `TrainingArguments` của mình. Như ta đã nói khi xác định chức năng của mình để tính toán các chỉ số, chúng ta sẽ không thể có vòng lặp đánh giá thường xuyên vì đặc trưng của hàm `compute_metrics()`. Chúng ta có thể viết lớp con của riêng mình về `Trainer` để làm điều này (một cách tiếp cận bạn có thể tìm thấy trong [bộ lệnh mẫu cho hỏi đáp](https://github.com/huggingface/transformers/blob/master/examples/pytorch/question-answering/trainer_qa.py)), nhưng hơi dài cho phần này. Thay vào đó, chúng ta sẽ chỉ đánh giá mô hình khi kết thúc huấn luyện tại đây và chỉ cho bạn cách thực hiện đánh giá thường xuyên trong "Vòng huấn luyện tùy chỉnh" bên dưới.

Đây thực sự là nơi API `Trainer` thể hiện các giới hạn của nó và là lúc thư viện 🤗 Accelerate tỏa sáng: việc tùy chỉnh lớp cho một trường hợp sử dụng cụ thể có thể gây khó khăn, nhưng việc điều chỉnh một vòng huấn luyện được tiếp xúc hoàn toàn rất dễ dàng.

Chúng ta hãy xem xét các `TrainingArguments` của mình:

```python
from transformers import TrainingArguments

args = TrainingArguments(
    "bert-finetuned-squad",
    evaluation_strategy="no",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
    fp16=True,
    push_to_hub=True,
)
```

Chúng ta đã thấy hầu hết những điều này trước đây: chúng ta đặt một số siêu tham số (như tốc độ học, số epoch ta dùng để huấn luyện và một số phân rã trọng số) và cho biết rằng chúng ta muốn lưu mô hình vào cuối mỗi epoch, bỏ qua đánh giá và tải kết quả của mình lên Model Hub. Chúng ta cũng cho phép huấn luyện chính xác hỗn hợp với `fp16 = True`, vì nó có thể tăng tốc huấn luyện một cách độc đáo trên GPU gần đây.

{:else}

Bây giờ đã xong, chúng ta có thể tạo TF Datasets của mình. Chúng ta có thể sử dụng công cụ đối chiếu dữ liệu mặc định đơn giản lần này:

```python
from transformers import DefaultDataCollator

data_collator = DefaultDataCollator(return_tensors="tf")
```

Và giờ chúng ta tạo bộ dữ liệu như bình thường.

```python
tf_train_dataset = train_dataset.to_tf_dataset(
    columns=[
        "input_ids",
        "start_positions",
        "end_positions",
        "attention_mask",
        "token_type_ids",
    ],
    collate_fn=data_collator,
    shuffle=True,
    batch_size=16,
)
tf_eval_dataset = validation_dataset.to_tf_dataset(
    columns=["input_ids", "attention_mask", "token_type_ids"],
    collate_fn=data_collator,
    shuffle=False,
    batch_size=16,
)
```

Tiếp theo, chúng ta thiết lập các siêu tham số huấn luyện và biên dịch mô hình của mình:

```python
from transformers import create_optimizer
from transformers.keras_callbacks import PushToHubCallback
import tensorflow as tf

# Số bước huấn luyện là số lượng mẫu trong tập dữ liệu, chia cho kích thước lô sau đó nhân
# với tổng số epoch. Lưu ý rằng tf_train_dataset ở đây là tf.data.Dataset theo lô,
# không phải là Hugging Face Dataset ban đầu, vì vậy len() của nó vốn là num_samples // batch_size.

num_train_epochs = 3
num_train_steps = len(tf_train_dataset) * num_train_epochs
optimizer, schedule = create_optimizer(
    init_lr=2e-5,
    num_warmup_steps=0,
    num_train_steps=num_train_steps,
    weight_decay_rate=0.01,
)
model.compile(optimizer=optimizer)

# Huấn luyện trong mixed-precision float16
tf.keras.mixed_precision.set_global_policy("mixed_float16")
```

Cuối cùng, ta đã sẵn sàng để huấn luyện với `model.fit()`. Ta sử dụng `PushToHubCallback` để tải mô hình lên Hub sau mỗi epoch.

{/if}

Mặc định, kho lưu trữ được sử dụng sẽ nằm trong không gian tên của bạn và được đặt tên theo thư mục đầu ra mà bạn đã đặt, vì vậy trong trường hợp của mình, nó sẽ nằm trong `"sgugger/bert-finetuned-squad"`. Chúng ta có thể ghi đè điều này bằng cách chuyển một `hub_model_id`; ví dụ: để đẩy mô hình vào tổ chức `huggingface_course`, chúng ta đã sử dụng `hub_model_id="huggingface_course/bert-finetuned-squad"` (là mô hình mà ta đã liên kết ở đầu phần này).

{#if fw === 'pt'}

<Tip>

💡 Nếu thư mục đầu ra bạn đang sử dụng tồn tại, nó cần phải là bản sao cục bộ của kho lưu trữ mà bạn muốn đẩy đến (vì vậy hãy đặt tên mới nếu bạn gặp lỗi khi xác định `Trainer` của mình).

</Tip>

Cuối cùng, ta chỉ cần truyền mọi thứ vào lớp `Trainer` và khởi động việc huấn luyện:

```python
from transformers import Trainer

trainer = Trainer(
    model=model,
    args=args,
    train_dataset=train_dataset,
    eval_dataset=validation_dataset,
    tokenizer=tokenizer,
)
trainer.train()
```

{:else}

```python
from transformers.keras_callbacks import PushToHubCallback

callback = PushToHubCallback(output_dir="bert-finetuned-squad", tokenizer=tokenizer)

# Chúng ta sẽ thực hiện kiểm định sau đó, vì vậy không có quá trình huấnlluyện giữa quá trình kiểm định
model.fit(tf_train_dataset, callbacks=[callback], epochs=num_train_epochs)
```

{/if}

Lưu ý rằng trong khi quá trình huấn luyện diễn ra, mỗi khi mô hình được lưu (ở đây, mỗi epoch), nó sẽ được tải lên Hub ở chế độ nền. Bằng cách này, bạn sẽ có thể tiếp tục huấn luyện của mình trên một máy khác nếu cần. Toàn bộ quá trình huấn luyện mất một khoảng thời gian (hơn một giờ trên Titan RTX), vì vậy bạn có thể uống một ly cà phê hoặc đọc lại một số phần của khóa học mà bạn thấy khó khăn hơn trong khi tiếp tục. Cũng lưu ý rằng ngay sau khi epoch đầu tiên kết thúc, bạn sẽ thấy một số trọng số được tải lên Hub và bạn có thể bắt đầu chơi với mô hình của mình trên trang của nó.

{#if fw === 'pt'}

Sau khi quá trình huấn luyện hoàn tất, cuối cùng ta cũng có thể đánh giá mô hình của mình (và cầu nguyện rằng ta đã không dành tất cả thời gian tính toán vào việc gì). Phương thức `predict()` của `Trainer` sẽ trả về một bộ giá trị trong đó các phần tử đầu tiên sẽ là các dự đoán của mô hình (ở đây là một cặp với các logit bắt đầu và kết thúc). Chúng ta gửi chúng đến hàm `compute_metrics())` của mình:

```python
predictions, _, _ = trainer.predict(validation_dataset)
start_logits, end_logits = predictions
compute_metrics(start_logits, end_logits, validation_dataset, raw_datasets["validation"])
```

{:else}

Sau khi quá trình huấn luyện hoàn tất, cuối cùng ta cũng có thể đánh giá mô hình của mình (và cầu nguyện rằng ta đã không dành tất cả thời gian tính toán vào việc gì). Phương thức `predict()` của `model` sẽ đảm nhận việc nhận các dự đoán và vì ta đã thực hiện tất cả các công việc khó khăn trong việc xác định một hàm `compute_metrics()` trước đó, chúng ta có thể nhận được kết quả của mình trong một dòng duy nhất:

```python
predictions = model.predict(tf_eval_dataset)
compute_metrics(
    predictions["start_logits"],
    predictions["end_logits"],
    validation_dataset,
    raw_datasets["validation"],
)
```

{/if}

```python out
{'exact_match': 81.18259224219489, 'f1': 88.67381321905516}
```

Tuyệt quá! Để so sánh, điểm cơ bản được báo cáo trong bài báo BERT cho mô hình này là 80.8 và 88.5, vì vậy chúng ta đang ở đúng vị trí của mình.

{#if fw === 'pt'}

Cuối cùng, ta sử dụng phương thức `push_to_hub()` để đảm bảo ta sẽ tải phiên bản mới nhất của mô hình:

```py
trainer.push_to_hub(commit_message="Training complete")
```

Điều này trả về URL của cam kết mà nó vừa thực hiện, nếu bạn muốn kiểm tra nó:

```python out
'https://huggingface.co/sgugger/bert-finetuned-squad/commit/9dcee1fbc25946a6ed4bb32efb1bd71d5fa90b68'
```

`Trainer` cũng soạn thảo một thẻ mô hình với tất cả các kết quả đánh giá và tải nó lên.

{/if}

Ở giai đoạn này, bạn có thể sử dụng tiện ích luận suy trên Model Hub để kiểm tra mô hình và chia sẻ mô hình đó với bạn bè, gia đình và vật nuôi yêu thích của bạn. Bạn đã tinh chỉnh thành công một mô hình trong tác vụ hỏi đáp - xin chúc mừng!

<Tip>

✏️ **Đến lượt bạn!** Hãy thử một kiến trúc mô hình khác để xem liệu nó có hoạt động tốt hơn trong tác vụ này không!

</Tip>

{#if fw === 'pt'}

Nếu bạn muốn tìm hiểu sâu hơn một chút về vòng huấn luyện, bây giờ chúng tôi sẽ hướng dẫn bạn cách thực hiện điều tương tự bằng cách sử dụng 🤗 Accelerate.

## Một vòng lặp huấn luyện tuỳ chỉnh

Bây giờ chúng ta hãy xem toàn bộ vòng lặp huấn luyện, vì vậy bạn có thể dễ dàng tùy chỉnh các phần bạn cần. Nó sẽ trông rất giống với vòng lặp huấn luyện trong [Chương 3](/course/chapter3/4), ngoại trừ vòng lặp đánh giá. Chúng ta sẽ có thể đánh giá mô hình thường xuyên vì ta không bị hạn chế bởi lớp `Trainer` nữa.

### Chuấn bị mọi thứ cho huấn luyện

Đầu tiên, chúng ta cần xây dựng các `DataLoader` từ các tập dữ liệu của mình. Chúng ta đặt định dạng của các tập dữ liệu đó thành `"torch"` và xóa các cột trong tập xác thực không được mô hình sử dụng. Sau đó, chúng ta có thể sử dụng `default_data_collator` được cung cấp bởi Transformers dưới dạng `collate_fn` và xáo trộn bộ huấn luyện, nhưng không phải bộ kiểm định:

```py
from torch.utils.data import DataLoader
from transformers import default_data_collator

train_dataset.set_format("torch")
validation_set = validation_dataset.remove_columns(["example_id", "offset_mapping"])
validation_set.set_format("torch")

train_dataloader = DataLoader(
    train_dataset,
    shuffle=True,
    collate_fn=default_data_collator,
    batch_size=8,
)
eval_dataloader = DataLoader(
    validation_set, collate_fn=default_data_collator, batch_size=8
)
```

Tiếp theo, chúng ta khôi phục mô hình của mình, để đảm bảo rằng ta không tiếp tục tinh chỉnh từ trước mà bắt đầu lại từ mô hình được huấn luyện trước BERT:

```py
model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
```

Sau đó, chúng ta sẽ cần một trình tối ưu hóa. Như thường lệ, ta sử dụng `AdamW` cổ điển, giống như Adam, nhưng với một bản sửa lỗi trong cách phân rã trọng số được áp dụng:

```py
from torch.optim import AdamW

optimizer = AdamW(model.parameters(), lr=2e-5)
```

Khi chúng ta có tất cả các đối tượng đó, chúng ta có thể gửi chúng đến phương thức `accelerator.prepare()`. Hãy nhớ rằng nếu bạn muốn huấn luyện về TPU trong notebook Colab, bạn sẽ cần chuyển tất cả mã này vào một hàm huấn luyện và điều đó sẽ không thực thi bất kỳ ô khởi tạo một `Accelerator` nào. Chúng ta có thể buộc huấn luyện độ chính xác hỗn hợp bằng cách chuyển `fp16=True` vào `Accelerator` (hoặc, nếu bạn đang thực thi mã dưới dạng tập lệnh, chỉ cần đảm bảo điền vào 🤗 Accelerate `config` một cách thích hợp).

```py
from accelerate import Accelerator

accelerator = Accelerator(fp16=True)
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

Như bạn đã biết từ các phần trước, chúng ta chỉ có thể sử dụng độ dài `train_dataloader` để tính số bước huấn luyện sau khi nó đã trải qua phương thức `accelerator.prepare()`. Chúng ta sử dụng cùng một lịch trình tuyến tính như trong các phần trước:

```py
from transformers import get_scheduler

num_train_epochs = 3
num_update_steps_per_epoch = len(train_dataloader)
num_training_steps = num_train_epochs * num_update_steps_per_epoch

lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)
```

Để đẩy mô hình của mình lên Hub, chúng ta sẽ cần tạo một đối tượng `Repository` trong một thư mục đang làm việc. Đầu tiên hãy đăng nhập vào Hugging Face Hub, nếu bạn chưa đăng nhập. Chúng ta sẽ xác định tên kho lưu trữ từ ID mô hình mà ta muốn cung cấp cho mô hình của mình (vui lòng thay thế `repo_name` bằng sự lựa chọn của riêng bạn; nó chỉ cần chứa tên người dùng của bạn, đó là những gì hàm `get_full_repo_name()` thực hiện ):

```py
from huggingface_hub import Repository, get_full_repo_name

model_name = "bert-finetuned-squad-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'sgugger/bert-finetuned-squad-accelerate'
```

Sau đó, chúng ta có thể sao chép kho lưu trữ đó trong một thư mục cục bộ. Nếu nó đã tồn tại, thư mục cục bộ này phải là bản sao của kho lưu trữ mà ta đang làm việc:

```py
output_dir = "bert-finetuned-squad-accelerate"
repo = Repository(output_dir, clone_from=repo_name)
```

Giờ ta có thể tải mọi thử ta lưu trong `output_dir` bằng cách gọi phương thức `repo.push_to_hub()`. Nó sẽ giúp ta tải các mô hình tức thì ở cuối mỗi epoch.

## Vòng lặp huấn luyện

Bây giờ chúng ta đã sẵn sàng để viết vòng lặp huấn luyện đầy đủ. Sau khi xác định thanh tiến trình để theo dõi quá trình huấn luyện diễn ra như thế nào, vòng lặp có ba phần:

- Bản thân quá trình huấn luyện, là sự lặp lại cổ điển trên `train_dataloader`, truyền thẳng qua mô hình, sau đó truyền ngược và tối ưu hóa.
- Bước đánh giá, trong đó ta thu thập tất cả các giá trị cho `start_logits` và `end_logits` trước khi chuyển đổi chúng thành mảng NumPy. Khi vòng lặp đánh giá kết thúc, chúng ta nối tất cả các kết quả. Lưu ý rằng chúng ta cần cắt bớt vì `Accelerator` có thể đã thêm một vài mẫu vào cuối để đảm bảo chúng ta có cùng số lượng mẫu trong mỗi quy trình.
- Lưu và tải lên, nơi trước tiên chúng ta lưu mô hình và trình mã hóa, sau đó gọi `repo.push_to_hub()`. Như chúng ta đã làm trước đây, chúng ta sử dụng đối số `blocking=False` để yêu cầu thư viện 🤗 Hub đẩy vào một quá trình không đồng bộ. Bằng cách này, quá trình huấn luyện tiếp tục diễn ra bình thường và lệnh (dài) này được thực thi ở chế độ nền.

Đây là mã hoàn chỉnh cho vòng lặp huấn luyện:

```py
from tqdm.auto import tqdm
import torch

progress_bar = tqdm(range(num_training_steps))

for epoch in range(num_train_epochs):
    # Huấn luyện
    model.train()
    for step, batch in enumerate(train_dataloader):
        outputs = model(**batch)
        loss = outputs.loss
        accelerator.backward(loss)

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)

    # Đánh giá
    model.eval()
    start_logits = []
    end_logits = []
    accelerator.print("Evaluation!")
    for batch in tqdm(eval_dataloader):
        with torch.no_grad():
            outputs = model(**batch)

        start_logits.append(accelerator.gather(outputs.start_logits).cpu().numpy())
        end_logits.append(accelerator.gather(outputs.end_logits).cpu().numpy())

    start_logits = np.concatenate(start_logits)
    end_logits = np.concatenate(end_logits)
    start_logits = start_logits[: len(validation_dataset)]
    end_logits = end_logits[: len(validation_dataset)]

    metrics = compute_metrics(
        start_logits, end_logits, validation_dataset, raw_datasets["validation"]
    )
    print(f"epoch {epoch}:", metrics)

    # Lưu và tải
    accelerator.wait_for_everyone()
    unwrapped_model = accelerator.unwrap_model(model)
    unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
    if accelerator.is_main_process:
        tokenizer.save_pretrained(output_dir)
        repo.push_to_hub(
            commit_message=f"Training in progress epoch {epoch}", blocking=False
        )
```

Trong trường hợp đây là lần đầu tiên bạn thấy một mô hình được lưu bằng 🤗 Accelerate, hãy dành một chút thời gian để kiểm tra ba dòng mã đi kèm với nó:

```py
accelerator.wait_for_everyone()
unwrapped_model = accelerator.unwrap_model(model)
unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
```

Dòng đầu tiên đã tự giải thích: nó cho tất cả các quá trình chờ cho đến khi mọi người ở giai đoạn đó trước khi tiếp tục. Điều này là để đảm bảo rằng chúng ta có cùng một mô hình trong mọi quy trình trước khi lưu. Sau đó, ta lấy `unwrapped_model`, là mô hình cơ sở mà ta đã xác định. Phương thức `accelerator.prepare()` thay đổi mô hình để hoạt động trong huấn luyện phân tán, vì vậy nó sẽ không có phương thức `save_pretrained()` nữa; phương thức `accelerator.unwrap_model()` hoàn tác bước đó. Cuối cùng, chúng ta gọi `save_pretrained()` nhưng yêu cầu phương thức đó sử dụng `accelerator.save()` thay vì `torch.save()`.

Khi điều này được thực hiện, bạn sẽ có một mô hình tạo ra kết quả khá giống với mô hình được huấn luyện với `Trainer`. Bạn có thể kiểm tra mô hình mà ta đã huấn luyện bằng cách sử dụng mã này tại [*huggingface-course/bert-finetuned-squad-accelerate*](https://huggingface.co/huggingface-course/bert-finetuned-squad-accelerate). Và nếu bạn muốn kiểm tra bất kỳ tinh chỉnh nào đối với vòng lặp huấn luyện, bạn có thể trực tiếp thực hiện chúng bằng cách chỉnh sửa đoạn mã được hiển thị ở trên!

{/if}

## Sử dụng mô hình tinh chỉnh

Chúng tôi đã chỉ cho bạn cách bạn có thể sử dụng mô hình mà chúng ta đã tinh chỉnh trên Model Hub bằng tiện ích luận suy. Để sử dụng nó cục bộ trong một `pipeline`, bạn chỉ cần chỉ định mã định danh mô hình:

```py
from transformers import pipeline

# Thay thế nó với checkpoint của bạn
model_checkpoint = "huggingface-course/bert-finetuned-squad"
question_answerer = pipeline("question-answering", model=model_checkpoint)

context = """
🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch and TensorFlow — with a seamless integration
between them. It's straightforward to train your models with one before loading them for inference with the other.
"""
question = "Which deep learning libraries back 🤗 Transformers?"
question_answerer(question=question, context=context)
```

```python out
{'score': 0.9979003071784973,
 'start': 78,
 'end': 105,
 'answer': 'Jax, PyTorch and TensorFlow'}
```

Tuyệt quá! Mô hình của chúng ta đang hoạt động tốt như mô hình mặc định cho pipeline này!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter7/7.mdx" />

### Phân loại token
https://huggingface.co/learn/course/vi/chapter7/2.md

# Phân loại token

{#if fw === 'pt'}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {
      label: "Google Colab",
      value:
        "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section2_pt.ipynb",
    },
    {
      label: "Aws Studio",
      value:
        "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section2_pt.ipynb",
    },
  ]}
/>

{:else}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {
      label: "Google Colab",
      value:
        "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section2_tf.ipynb",
    },
    {
      label: "Aws Studio",
      value:
        "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section2_tf.ipynb",
    },
  ]}
/>

{/if}

Ứng dụng đầu tiên chúng ta sẽ cùng khám phá là phân loại token. Tác vụ chung này bao gồm bất kỳ vấn đề nào có thể được xây dựng dưới dạng "gán nhãn cho mỗi token trong một câu", chẳng hạn như:

- **Nhận dạng thực thể được đặt tên (NER)**: Tìm các thực thể (chẳng hạn như người, địa điểm hoặc tổ chức) trong một câu. Điều này có thể được xây dựng như là gán nhãn cho mỗi token bằng cách có một nhãn cho mỗi thực thể và một nhãn cho "không có thực thể".
- **Gán nhãn từ loại (POS)**: Đánh dấu mỗi từ trong câu tương ứng với một từ loại cụ thể của văn bản (chẳng hạn như danh từ, động từ, tính từ, v.v.).
- **Phân khúc**: Tìm các token thuộc cùng một thực thể. Tác vụ này (có thể được kết hợp với POS hoặc NER) có thể được xây dựng dưới dạng gán một nhãn (thường là `B-`) cho bất kỳ token nào ở đầu một đoạn, một nhãn khác (thường là `I-`) cho các token đó nằm bên trong một đoạn và một nhãn thứ ba (thường là `O`) token không thuộc bất kỳ đoạn nào.

<Youtube id="wVHdVlPScxA" />

Tất nhiên, có nhiều loại vấn đề phân loại token khác; đó chỉ là một vài ví dụ tiêu biểu. Trong phần này, chúng ta sẽ tinh chỉnh một mô hình (BERT) trên một tác vụ NER, sau đó sẽ có thể tính toán các dự đoán như sau:

<iframe
  src="https://course-demos-bert-finetuned-ner.hf.space"
  frameBorder="0"
  height="350"
  title="Gradio app"
  class="block dark:hidden container p-0 flex-grow space-iframe"
  allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking"
  sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"
></iframe>

<a class="flex justify-center" href="/huggingface-course/bert-finetuned-ner">
  <img
    class="block dark:hidden lg:w-3/5"
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/model-eval-bert-finetuned-ner.png"
    alt="One-hot encoded labels for question answering."
  />
  <img
    class="hidden dark:block lg:w-3/5"
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/model-eval-bert-finetuned-ner-dark.png"
    alt="One-hot encoded labels for question answering."
  />
</a>

Bạn có thể tìm mô hình ta sẽ huấn luyện và tải lên Hub và kiểm tra lại các dự đoán [tại đây](https://huggingface.co/huggingface-course/bert-finetuned-ner?text=My+name+is+Sylvain+and+I+work+at+Hugging+Face+in+Brooklyn).

## Chuẩn bị dữ liệu

Đầu tiên, ta cần bộ dữ liệu chuẩn bị cho phân loại token. Trong chương này, chúng ta sẽ sử dụng bộ dữ liệu [CoNLL-2003](https://huggingface.co/datasets/conll2003), bao gồm các câu chuyện tin tức từ Reuters.

<Tip>

💡 Miễn là tập dữ liệu của bạn bao gồm các văn bản được chia thành các từ với nhãn tương ứng của chúng, bạn sẽ có thể điều chỉnh các quy trình xử lý dữ liệu được mô tả ở đây với tập dữ liệu của riêng bạn. Tham khảo lại [Chapter 5](/course/chapter5) nếu bạn cần cập nhật về cách tải dữ liệu tùy chỉnh của riêng bạn trong `Dataset`.

</Tip>

### Tập dữ liệu CoNLL-2003

Để tải bộ dữ liệu CoNLL-2003, ta cần sử dụng phương thức `load_dataset()` từ thư viện 🤗 Datasets:

```py
from datasets import load_dataset

raw_datasets = load_dataset("conll2003")
```

Ta sẽ tải và lưu bộ dữ liệu vào cache, như ta đã thấy trong [Chương 3](/course/chapter3) cho bộ dữ liệu GLUE MRPC. Việc kiểm tra đối tượng này cho chúng ta thấy các cột hiện có và sự phân chia giữa các tập huấn luyện, kiểm định và kiểm thử:

```py
raw_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'],
        num_rows: 14041
    })
    validation: Dataset({
        features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'],
        num_rows: 3250
    })
    test: Dataset({
        features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'],
        num_rows: 3453
    })
})
```

Đặc biệt, chúng ta có thể thấy tập dữ liệu chứa các nhãn cho ba tác vụ mà chúng ta đã đề cập trước đó: NER, POS và chunking. Một sự khác biệt lớn so với các bộ dữ liệu khác là các văn bản đầu vào không được trình bày dưới dạng câu hoặc tài liệu, mà là danh sách các từ (cột cuối cùng được gọi là `tokens`, nhưng nó chứa các từ theo nghĩa đây là các đầu vào được tokenize trước vẫn cần để đi qua trình tokenize để tokenize từ phụ).

Hãy xem phần tử đầu tiên của tập huấn luyện:

```py
raw_datasets["train"][0]["tokens"]
```

```python out
['EU', 'rejects', 'German', 'call', 'to', 'boycott', 'British', 'lamb', '.']
```

Vì ta muốn thực hiện nhận dạng thực thể được đặt tên, chúng ta sẽ nhìn vào các thẻ NER:

```py
raw_datasets["train"][0]["ner_tags"]
```

```python out
[3, 0, 7, 0, 0, 0, 7, 0, 0]
```

Đó là những nhãn dưới dạng số nguyên sẵn sàng để huấn luyện, nhưng chúng không nhất thiết hữu ích khi chúng ta muốn kiểm tra dữ liệu. Giống như phân loại văn bản, chúng ta có thể truy cập sự tương ứng giữa các số nguyên đó và tên nhãn bằng cách xem thuộc tính `features` của tập dữ liệu:

```py
ner_feature = raw_datasets["train"].features["ner_tags"]
ner_feature
```

```python out
Sequence(feature=ClassLabel(num_classes=9, names=['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC', 'B-MISC', 'I-MISC'], names_file=None, id=None), length=-1, id=None)
```

Vì vậy, cột này chứa các phần tử là chuỗi của `ClassLabel`. Loại phần tử của chuỗi nằm trong thuộc tính `feature` của `ner_feature` này, và chúng ta có thể truy cập danh sách tên bằng cách xem thuộc tính `names` của `feature` đó:

```py
label_names = ner_feature.feature.names
label_names
```

```python out
['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC', 'B-MISC', 'I-MISC']
```

Chúng ta đã thấy các nhãn khi đào sâu vào pipeline `token-classification` trong [Chương 6](/course/chapter6/3), nhưng để cập nhật nhanh:

- `O` nghĩa là từ không thuộc bất kì thực thể nào.
- `B-PER`/`I-PER` nghĩa là từ tương ứng phần bắt đầu/ nằm bên trong của thực thể _person_ hay _con người_.
- `B-ORG`/`I-ORG` nghĩa là từ tương ứng phần bắt đầu/ nằm bên trong của thực thể _organization_ hay _tổ chức_.
- `B-LOC`/`I-LOC` nghĩa là từ tương ứng phần bắt đầu/ nằm bên trong của thực thể _location_ hay _địa điểm_.
- `B-MISC`/`I-MISC` nghĩa là từ tương ứng phần bắt đầu/ nằm bên trong của thực thể _miscellaneous_ hay _lộn xộn_.

Giờ khi giải mã các nhãn, ta thấy chúng cho ta:

```python
words = raw_datasets["train"][0]["tokens"]
labels = raw_datasets["train"][0]["ner_tags"]
line1 = ""
line2 = ""
for word, label in zip(words, labels):
    full_label = label_names[label]
    max_length = max(len(word), len(full_label))
    line1 += word + " " * (max_length - len(word) + 1)
    line2 += full_label + " " * (max_length - len(full_label) + 1)

print(line1)
print(line2)
```

```python out
'EU    rejects German call to boycott British lamb .'
'B-ORG O       B-MISC O    O  O       B-MISC  O    O'
```

Và đối với một ví dụ trộn nhãn `B-` và `I-`, đây là những gì mà cùng một đoạn mã cung cấp cho chúng ta về phần tử của tập huấn luyện ở chỉ mục 4:

```python out
'Germany \'s representative to the European Union \'s veterinary committee Werner Zwingmann said on Wednesday consumers should buy sheepmeat from countries other than Britain until the scientific advice was clearer .'
'B-LOC   O  O              O  O   B-ORG    I-ORG O  O          O         B-PER  I-PER     O    O  O         O         O      O   O         O    O         O     O    B-LOC   O     O   O          O      O   O       O'
```

Như chúng ta có thể thấy, các thực thể bao gồm hai từ, như "European Union" và "Werner Zwingmann", được gán nhãn `B-` cho từ đầu tiên và nhãn `I-` cho từ thứ hai.

<Tip>

✏️ **Đến lượt bạn!** In hai câu giống nhau bằng nhãn POS hoặc phân khúc của chúng.

</Tip>

### Xử lý dữ liệu

<Youtube id="iY2AZYdZAr0" />

Như thường lệ, các văn bản của chúng ta cần được chuyển đổi sang token ID trước khi mô hình có thể hiểu được chúng. Như chúng ta đã thấy trong [Chương 6](/course/chapter6/), một sự khác biệt lớn trong trường hợp tác vụ phân loại token là chúng ta có các đầu vào được tokenize trước. May mắn thay, API tokenizer có thể giải quyết vấn đề đó khá dễ dàng; chúng ta chỉ cần báo `tokenizer` bằng một lá cờ đặc biệt.

Để bắt đầu, hãy tạo đối tượng `tokenizer` của chúng ta. Như chúng tôi đã nói trước đây, chúng ta sẽ sử dụng mô hình huấn luyện trước BERT, vì vậy chúng ta sẽ bắt đầu bằng cách tải xuống và lưu vào bộ nhớ đệm của tokenizer liên quan:

```python
from transformers import AutoTokenizer

model_checkpoint = "bert-base-cased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
```

Bạn có thể thay thế `model_checkpoint` bằng bất kỳ mô hình nào khác mà bạn thích từ [Hub](https://huggingface.co/models) hoặc bằng một thư mục cục bộ trong đó bạn đã lưu một mô hình được huấn luyện trước và một trình tokenize. Hạn chế duy nhất là tokenizer cần được hỗ trợ bởi thư viện 🤗 Tokenizers, vì vậy sẽ có phiên bản "nhanh". Bạn có thể xem tất cả các kiến trúc đi kèm với phiên bản nhanh trong [bảng lớn này](https://huggingface.co/transformers/#supported-frameworks) và để kiểm tra xem đối tượng `tokenizer` mà bạn đang sử dụng có thực sự là được hỗ trợ bởi 🤗 Tokenizers, bạn có thể xem thuộc tính `is_fast` của nó:

```py
tokenizer.is_fast
```

```python out
True
```

Để tokenize dữ liệu đầu vào đã tiền tokenize, ta có thể sử dụng `tokenizer` như thường lệ và chỉ thêm `is_split_into_words=True`:

```py
inputs = tokenizer(raw_datasets["train"][0]["tokens"], is_split_into_words=True)
inputs.tokens()
```

```python out
['[CLS]', 'EU', 'rejects', 'German', 'call', 'to', 'boycott', 'British', 'la', '##mb', '.', '[SEP]']
```

Như chúng ta có thể thấy, trình tokenizer đã thêm các token đặc biệt được sử dụng bởi mô hình (`[CLS]` ở đầu và `[SEP]` ở cuối) và để nguyên hầu hết các từ. Tuy nhiên, từ `lamb` đã được tokenize thành hai từ phụ, `la` và `##mb`. Điều này dẫn đến sự không khớp giữa đầu vào và các nhãn: danh sách nhãn chỉ có 9 phần tử, trong khi đầu vào của chúng ta hiện có 12 token. Việc tính toán các token đặc biệt rất dễ dàng (chúng ta biết chúng nằm ở đầu và cuối), nhưng chúng ta cũng cần đảm bảo rằng chúng ta sắp xếp tất cả các nhãn với các từ thích hợp.

May mắn thay, bởi vì chúng ta đang sử dụng một tokenizer nhanh, chúng ta có quyền truy cập vào sức mạnh siêu cường 🤗 Tokenizers, có nghĩa là chúng ta có thể dễ dàng ánh xạ từng token với từ tương ứng của nó (như đã thấy trong [Chương 6](/course/chapter6/3)):

```py
inputs.word_ids()
```

```python out
[None, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, None]
```

Với một chút công việc, sau đó chúng ta có thể mở rộng danh sách nhãn của mình để phù hợp với các token. Quy tắc đầu tiên chúng ta sẽ áp dụng là các token đặc biệt có nhãn là `-100`. Điều này là do theo mặc định `-100` là chỉ số bị bỏ qua trong hàm mất mát mà chúng ta sẽ sử dụng (entropy chéo). Sau đó, mỗi token có cùng nhãn với token bắt đầu từ bên trong nó, vì chúng là một phần của cùng một thực thể. Đối với các token bên trong một từ nhưng không ở đầu, chúng ta thay thế `B-` bằng `I-` (vì token không bắt đầu thực thể):

```python
def align_labels_with_tokens(labels, word_ids):
    new_labels = []
    current_word = None
    for word_id in word_ids:
        if word_id != current_word:
            # Bắt đầu một từ mới!
            current_word = word_id
            label = -100 if word_id is None else labels[word_id]
            new_labels.append(label)
        elif word_id is None:
            # Token đặc biệt
            new_labels.append(-100)
        else:
            # Từ giống với token trước đó
            label = labels[word_id]
            # Nếu nhãn là B-XXX, ta đổi sang I-XXX
            if label % 2 == 1:
                label += 1
            new_labels.append(label)

    return new_labels
```

Hãy cùng thử với câu đầu tiên:

```py
labels = raw_datasets["train"][0]["ner_tags"]
word_ids = inputs.word_ids()
print(labels)
print(align_labels_with_tokens(labels, word_ids))
```

```python out
[3, 0, 7, 0, 0, 0, 7, 0, 0]
[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100]
```

Như chúng ta có thể thấy, hàm đã thêm `-100` cho hai token đặc biệt ở đầu và cuối, và dấu `0` mới cho từ của chúng ta đã được chia thành hai token.

<Tip>

✏️ **Đến lượt bạn!** Một số nhà nghiên cứu chỉ thích gán một nhãn cho mỗi từ và gán `-100` cho các token con khác trong một từ nhất định. Điều này là để tránh các từ dài được chia thành nhiều token phụ góp phần lớn vào hàm mất mát. Thay đổi chức năng trước đó để căn chỉnh nhãn với ID đầu vào bằng cách tuân theo quy tắc này.

</Tip>

Để xử lý trước toàn bộ tập dữ liệu của mình, chúng ta cần tokenize tất cả các đầu vào và áp dụng `align_labels_with_tokens()` trên tất cả các nhãn. Để tận dụng tốc độ của trình tokenize nhanh của mình, tốt nhất bạn nên tokenize nhiều văn bản cùng một lúc, vì vậy chúng ta sẽ viết một hàm xử lý danh sách các ví dụ và sử dụng phương thức `Dataset.map()` với tùy chọn `batched=True`. Điều duy nhất khác với ví dụ trước là hàm `word_ids()` cần lấy chỉ mục của mẫu mà chúng ta muốn các ID từ khi các đầu vào cho tokenizer là danh sách văn bản (hoặc trong trường hợp của chúng ta là danh sách danh sách các từ), vì vậy chúng ta cũng thêm vào đó:

```py
def tokenize_and_align_labels(examples):
    tokenized_inputs = tokenizer(
        examples["tokens"], truncation=True, is_split_into_words=True
    )
    all_labels = examples["ner_tags"]
    new_labels = []
    for i, labels in enumerate(all_labels):
        word_ids = tokenized_inputs.word_ids(i)
        new_labels.append(align_labels_with_tokens(labels, word_ids))

    tokenized_inputs["labels"] = new_labels
    return tokenized_inputs
```

Lưu ý rằng chúng ta chưa đệm vào của mình; chúng ta sẽ làm điều đó sau, khi tạo các lô bằng trình đối chiếu dữ liệu.

Bây giờ chúng ta có thể áp dụng tất cả tiền xử lý đó trong một lần vào các phần khác của tập dữ liệu của mình:

```py
tokenized_datasets = raw_datasets.map(
    tokenize_and_align_labels,
    batched=True,
    remove_columns=raw_datasets["train"].column_names,
)
```

Chúng ta đã hoàn thành phần khó nhất! Bây giờ, dữ liệu đã được tiền xử lý, quá trình huấn luyện thực tế sẽ giống như những gì chúng ta đã làm trong [Chương 3](/course/chapter3).

{#if fw === 'pt'}

## Tinh chỉnh mô hình trong API `Trainer`

Mã thực sử dụng `Trainer` sẽ giống như trước đây; những thay đổi duy nhất là cách dữ liệu được đối chiếu thành một lô và chức năng tính toán số liệu.

{:else}

## Tinh chỉnh mô hình với Keras

Mã thực sử dụng Keras sẽ giống như trước đây; những thay đổi duy nhất là cách dữ liệu được đối chiếu thành một lô và chức năng tính toán số liệu.

{/if}

### Đối chiếu dữ liệu

Chúng ta không thể chỉ sử dụng một `DataCollatorWithPadding` như trong [Chương 3](/course/chapter3) vì nó chỉ đệm các đầu vào (ID đầu vào, attention mask và loại token ID). Ở đây, các nhãn của chúng ta nên được đệm theo cùng một cách giống như các đầu vào để chúng giữ nguyên kích thước, sử dụng `-100` làm giá trị để các dự đoán tương ứng bị bỏ qua trong tính toán tổn thất.

Tất cả điều này được thực hiện bởi [`DataCollatorForTokenClassification`](https://huggingface.co/transformers/main_classes/data_collator.html#datacollatorfortokenclassification). Giống như `DataCollatorWithPadding`, nó sử dụng `tokenizer` để xử lý trước các đầu vào:

{#if fw === 'pt'}

```py
from transformers import DataCollatorForTokenClassification

data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)
```

{:else}

```py
from transformers import DataCollatorForTokenClassification

data_collator = DataCollatorForTokenClassification(
    tokenizer=tokenizer, return_tensors="tf"
)
```

{/if}

Để kiểm tra nó trên vài mẫu, ta có thể gọi nó trên danh sách các mẫu từ tập huấn luyện đã được tokenize:

```py
batch = data_collator([tokenized_datasets["train"][i] for i in range(2)])
batch["labels"]
```

```python out
tensor([[-100,    3,    0,    7,    0,    0,    0,    7,    0,    0,    0, -100],
        [-100,    1,    2, -100, -100, -100, -100, -100, -100, -100, -100, -100]])
```

Hãy so sánh điều này với các nhãn cho phần tử đầu tiên và thứ hai trong tập dữ liệu của mình:

```py
for i in range(2):
    print(tokenized_datasets["train"][i]["labels"])
```

```python out
[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100]
[-100, 1, 2, -100]
```

{#if fw === 'pt'}

Như chúng ta có thể thấy, tập hợp nhãn thứ hai đã được đệm bằng độ dài của tập đầu tiên bằng cách sử dụng `-100`.

{:else}

Trình đối chiếu dữ liệu của chúng ta đã sẵn sàng hoạt động! Bây giờ hãy sử dụng nó để tạo một `tf.data.Dataset` với phương thức `to_tf_dataset()`.

```py
tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "labels", "token_type_ids"],
    collate_fn=data_collator,
    shuffle=True,
    batch_size=16,
)

tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "labels", "token_type_ids"],
    collate_fn=data_collator,
    shuffle=False,
    batch_size=16,
)
```

Điểm dừng tiếp theo: chính là mô hình.

{/if}

{#if fw === 'tf'}

### Định nghĩa mô hình

Vì chúng tôi đang giải quyết vấn đề phân loại token, chúng ta sẽ sử dụng lớp `TFAutoModelForTokenClassification`. Điều chính cần nhớ khi xác định mô hình này là truyền một số thông tin về số lượng nhãn mà chúng ta có. Cách dễ nhất để làm điều này là truyền vào tham số `num_labels`, nhưng nếu chúng ta muốn một tiện ích luận suy đẹp hoạt động giống như tiện ích chúng ta đã thấy ở đầu phần này, tốt hơn nên đặt các nhãn tương ứng chính xác thay thế.

Chúng phải được đặt bởi hai từ điển, `id2label` và `label2id`, chứa ánh xạ từ ID đến nhãn và ngược lại:

```py
id2label = {i: label for i, label in enumerate(label_names)}
label2id = {v: k for k, v in id2label.items()}
```

Bây giờ chúng ta có thể chuyển chúng đến phương thức `TFAutoModelForTokenClassification.from_pretrained()` và chúng sẽ được đặt trong cấu hình của mô hình, sau đó được lưu và tải lên Hub một cách chính xác:

```py
from transformers import TFAutoModelForTokenClassification

model = TFAutoModelForTokenClassification.from_pretrained(
    model_checkpoint,
    id2label=id2label,
    label2id=label2id,
)
```

Giống như khi chúng tôi định nghĩa `TFAutoModelForSequenceClassification` của mình trong [Chương 3](/course/chapter3), việc tạo mô hình đưa ra cảnh báo rằng một số trọng số không được sử dụng (những trọng số từ đầu huấn luyện trước) và một số trọng số khác được khởi tạo ngẫu nhiên (những trọng số từ đầu phân loại token mới) và mô hình này nên được huấn luyện. Chúng ta sẽ làm điều đó sau một phút, nhưng trước tiên hãy kiểm tra kỹ xem mô hình của chúng ta có đúng số lượng nhãn hay không:

```python
model.config.num_labels
```

```python out
9
```

<Tip warning={true}>

⚠️ Nếu bạn có một mô hình có số nhãn sai, bạn sẽ gặp lỗi khó hiểu khi gọi `model.fit()` sau này. Điều này có thể gây khó chịu khi gỡ lỗi, vì vậy hãy đảm bảo bạn thực hiện kiểm tra này để xác nhận rằng bạn có số lượng nhãn dự kiến.

</Tip>

### Tinh chỉnh một mô hình

Bây giờ chúng tôi đã sẵn sàng để huấn luyện mô hình của mình! Tuy nhiên, chúng ta chỉ cần làm thêm một chút công việc trước tiên: chúng ta nên đăng nhập vào Hugging Face và xác định các siêu tham số huấn luyện của mình. Nếu bạn đang làm việc trên notebook, có một chức năng tiện lợi để giúp bạn làm điều này:

```python
from huggingface_hub import notebook_login

notebook_login()
```

Thao tác này sẽ hiển thị một tiện ích mà bạn có thể nhập thông tin đăng nhập  Hugging Face của mình.

Nếu bạn không làm việc trong notebook, chỉ cần nhập dòng sau vào thiết bị đầu cuối của bạn:

```bash
huggingface-cli login
```

Sau khi đăng nhập, chúng ta có thể chuẩn bị mọi thứ cần thiết để biên dịch mô hình của mình. 🤗 Transformers cung cấp một hàm `create_optimizer()` thuận tiện sẽ cung cấp cho bạn trình tối ưu hóa `AdamW` với các cài đặt thích hợp cho giảm trọng lượng và giảm tốc độ học tập, cả hai đều sẽ cải thiện hiệu suất mô hình của bạn so với trình tối ưu hóa `Adam` tích hợp sẵn:

```python
from transformers import create_optimizer
import tensorflow as tf

# Huấn luyện trong mixed-precision float16
# Bình luận dòng này nếu bạn đang sử dụng GPU nên không được hưởng lợi từ điều này
tf.keras.mixed_precision.set_global_policy("mixed_float16")

# Số bước huấn luyện là số lượng mẫu trong tập dữ liệu, chia cho kích thước lô sau đó nhân
# với số epoch. Lưu ý rằng tf_train_dataset ở đây là lô tf.data.Dataset,
# không phải Hugging Face Dataset gốc, nên len() của nó vốn là num_samples // batch_size.
num_epochs = 3
num_train_steps = len(tf_train_dataset) * num_epochs

optimizer, schedule = create_optimizer(
    init_lr=2e-5,
    num_warmup_steps=0,
    num_train_steps=num_train_steps,
    weight_decay_rate=0.01,
)
model.compile(optimizer=optimizer)
```

Cũng lưu ý rằng chúng ta không cung cấp tham số `loss` cho `compile()`. Điều này là do các mô hình thực sự có thể tính toán mất mát bên trong - nếu bạn biên dịch mà không mất mát và cung cấp các nhãn của bạn trong từ điển đầu vào (như chúng ta làm trong bộ dữ liệu của mình), thì mô hình sẽ huấn luyện bằng cách sử dụng mất mát nội bộ đó, điều này sẽ thích hợp cho tác vụ và loại mô hình bạn đã chọn.

Tiếp theo, chúng ta xác định một `PushToHubCallback` để tải mô hình lên Hub trong quá trình huấn luyện và phù hợp với mô hình với lệnh gọi lại đó:

```python
from transformers.keras_callbacks import PushToHubCallback

callback = PushToHubCallback(output_dir="bert-finetuned-ner", tokenizer=tokenizer)

model.fit(
    tf_train_dataset,
    validation_data=tf_eval_dataset,
    callbacks=[callback],
    epochs=num_epochs,
)
```

Bạn có thể chỉ định tên đầy đủ của kho lưu trữ mà bạn muốn đẩy đến bằng tham số `hub_model_id` (đặc biệt, bạn sẽ phải sử dụng tham số này để đẩy đến một tổ chức). Ví dụ: khi chúng ta đẩy mô hình vào tổ chức [`huggingface-course`](https://huggingface.co/huggingface-course), chúng ta đã thêm `hub_model_id="huggingface-course/bert-finetuned-ner"`. Theo mặc định, kho lưu trữ được sử dụng sẽ nằm trong không gian tên của bạn và được đặt tên theo thư mục đầu ra mà bạn đã đặt, ví dụ: `"cool_huggingface_user/bert-finetuned-ner"`.

<Tip>

💡 Nếu thư mục đầu ra bạn đang sử dụng đã tồn tại, nó cần phải là bản sao cục bộ của kho lưu trữ mà bạn muốn đẩy đến. Nếu không, bạn sẽ gặp lỗi khi gọi `model.fit()` và sẽ cần đặt tên mới.

</Tip>

Lưu ý rằng trong khi quá trình huấn luyện diễn ra, mỗi khi mô hình được lưu (ở đây, mỗi epoch), nó sẽ được tải lên Hub ở chế độ nền. Bằng cách này, bạn sẽ có thể tiếp tục quá trình huấn luyện của mình trên một máy khác nếu cần.

Ở giai đoạn này, bạn có thể sử dụng tiện ích luận suy trên Model Hub để kiểm tra mô hình của mình và chia sẻ với bạn bè. Bạn đã tinh chỉnh thành công một mô hình trong tác vụ phân loại token - xin chúc mừng! Nhưng mô hình của chúng ta thực sự tốt đến mức nào? Chúng ta nên có một số chỉ số hay thước đo để đánh giá.

{/if}

### Thước đo

{#if fw === 'pt'}

Để `Trainer` tính toán một thước đo cho mỗi epoch, ta sẽ cần định nghĩa hàm `compute_metrics()` nhận một array các dự đoán và nhãn, và trả về một từ điển với tên các thước đo và giá trị tương ứng.

Khung truyền thống được sử dụng để đánh giá phân loại token là [_seqeval_](https://github.com/chakki-works/seqeval). Để sử dụng thước đo này, ta sẽ cần cài đặt thư viện _seqeval_:

```py
!pip install seqeval
```

Chúng ta có thể tải nó qua hàm `evaluate.load()` như đã làm ở [Chương 3 3](/course/chapter3):

{:else}

Khung truyền thống được sử dụng để đánh giá phân loại token là [_seqeval_](https://github.com/chakki-works/seqeval). Để sử dụng thước đo này, ta sẽ cần cài đặt thư viện _seqeval_:

```py
!pip install seqeval
```

Chúng ta có thể tải nó qua hàm `evaluate.load()` như đã làm ở [Chương 3 3](/course/chapter3):

{/if}

```py
import evaluate

metric = evaluate.load("seqeval")
```

Thước đo này không giống như các thước đo độ chính xác thông thương: nó sẽ nhận một danh sách các nhãn như là chuỗi văn bản, không phải số nguyên, nên ta sẽ cần giải mã toàn bộ những dự đoán và nhãn trước khi truyền chúng vào thước đo. Hãy cùng xem nó hoạt động ra sao. Đầu tiên, ta sẽ lấy những nhãn từ mẫu huấn luyện đầu tiên:

```py
labels = raw_datasets["train"][0]["ner_tags"]
labels = [label_names[i] for i in labels]
labels
```

```python out
['B-ORG', 'O', 'B-MISC', 'O', 'O', 'O', 'B-MISC', 'O', 'O']
```

Ta có thể tạo ra những dự đoán giả cho chúng bằng cách thay đổi giá trị ở chỉ mục 2:

```py
predictions = labels.copy()
predictions[2] = "O"
metric.compute(predictions=[predictions], references=[labels])
```

Lưu ý rằng thước đo nhận danh sách các dự đoán (không chỉ một) và danh sách các nhãn. Đây là đầu ra:

```python out
{'MISC': {'precision': 1.0, 'recall': 0.5, 'f1': 0.67, 'number': 2},
 'ORG': {'precision': 1.0, 'recall': 1.0, 'f1': 1.0, 'number': 1},
 'overall_precision': 1.0,
 'overall_recall': 0.67,
 'overall_f1': 0.8,
 'overall_accuracy': 0.89}
```

{#if fw === 'pt'}

Điều này đang gửi lại rất nhiều thông tin! Chúng ta nhận được precision, recall, và điểm F1 cho từng thực thể riêng biệt, cũng như tổng thể. Đối với tính toán số liệu của mình, chúng ta sẽ chỉ giữ lại điểm tổng thể, nhưng hãy tinh chỉnh chức năng `compute_metrics()` để trả về tất cả các số liệu bạn muốn báo cáo.

Hàm `compute_metrics()` này trước tiên lấy argmax của logits để chuyển chúng thành các dự đoán (như thường lệ, logits và xác suất theo cùng một thứ tự, vì vậy chúng ta không cần áp dụng softmax). Sau đó, chúng ta phải chuyển đổi cả nhãn và dự đoán từ số nguyên sang chuỗi. Chúng ta xóa tất cả các giá trị có nhãn là `-100`, sau đó chuyển kết quả đến phương thức `metric.compute()`:

```py
import numpy as np


def compute_metrics(eval_preds):
    logits, labels = eval_preds
    predictions = np.argmax(logits, axis=-1)

    # Xoá những chỉ mục bị ngó lơ (token đặc biệt) và chuyển chúng thành nhãn
    true_labels = [[label_names[l] for l in label if l != -100] for label in labels]
    true_predictions = [
        [label_names[p] for (p, l) in zip(prediction, label) if l != -100]
        for prediction, label in zip(predictions, labels)
    ]
    all_metrics = metric.compute(predictions=true_predictions, references=true_labels)
    return {
        "precision": all_metrics["overall_precision"],
        "recall": all_metrics["overall_recall"],
        "f1": all_metrics["overall_f1"],
        "accuracy": all_metrics["overall_accuracy"],
    }
```

Bây giờ điều này đã được thực hiện, chúng ta gần như đã sẵn sàng để xác định `Trainer` của mình. Chúng ta chỉ cần một `model`  để tinh chỉnh!

{:else}

Điều này đang gửi lại rất nhiều thông tin! Chúng ta nhận được precision, recall, và điểm F1 cho từng thực thể riêng biệt, cũng như tổng thể. Hãy cũng xem chuyện gì xảy ra nêu sta thử sử dụng giá trị dự đoán thực của mô hình để tính ra điểm số thực.

TensorFlow không giống như việc nối các dự đoán của chúng ta lại với nhau, bởi vì chúng có độ dài chuỗi thay đổi. Điều này có nghĩa là chúng ta không thể chỉ sử dụng `model.predict()` --  hưng điều đó sẽ không ngăn cản chúng ta. Chúng ta sẽ nhận được một số dự đoán tại một thời điểm và nối chúng thành một danh sách dài lớn khi tiếp tục, bỏ các token `-100` tương ứng bị ẩn đi hoặc đệm, sau đó tính toán các số liệu trên danh sách ở cuối:

```py
import numpy as np

all_predictions = []
all_labels = []
for batch in tf_eval_dataset:
    logits = model.predict(batch)["logits"]
    labels = batch["labels"]
    predictions = np.argmax(logits, axis=-1)
    for prediction, label in zip(predictions, labels):
        for predicted_idx, label_idx in zip(prediction, label):
            if label_idx == -100:
                continue
            all_predictions.append(label_names[predicted_idx])
            all_labels.append(label_names[label_idx])
metric.compute(predictions=[all_predictions], references=[all_labels])
```

```python out
{'LOC': {'precision': 0.91, 'recall': 0.92, 'f1': 0.91, 'number': 1668},
 'MISC': {'precision': 0.70, 'recall': 0.79, 'f1': 0.74, 'number': 702},
 'ORG': {'precision': 0.85, 'recall': 0.90, 'f1': 0.88, 'number': 1661},
 'PER': {'precision': 0.95, 'recall': 0.95, 'f1': 0.95, 'number': 1617},
 'overall_precision': 0.87,
 'overall_recall': 0.91,
 'overall_f1': 0.89,
 'overall_accuracy': 0.97}
```

Mô hình của bạn đã làm như thế nào, so với mô hình của chúng tôi? Nếu bạn có những con số tương tự, khóa đào tạo của bạn đã thành công!

{/if}

{#if fw === 'pt'}

### Định nghĩa mô hình

Vì chúng ta đang giải quyết vấn đề phân loại token, chúng ta sẽ sử dụng lớp `AutoModelForTokenClassification`. Điều chính cần nhớ khi xác định mô hình này là truyền một số thông tin về số lượng nhãn mà chúng ta có. Cách dễ nhất để làm điều này là truyền vào tham số `num_labels`, nhưng nếu chúng ta muốn một tiện ích luận suy hoạt động giống như tiện ích chúng ta đã thấy ở đầu phần này, tốt hơn nên đặt các nhãn tương ứng chính xác thay thế.

Chúng phải được đặt bởi hai từ điển, `id2label` và `label2id`, chứa các ánh xạ từ ID đến nhãn và ngược lại:

```py
id2label = {i: label for i, label in enumerate(label_names)}
label2id = {v: k for k, v in id2label.items()}
```

Giờ ta có thể truyền chúng vào phương thức `AutoModelForTokenClassification.from_pretrained()`, và chúng sẽ được thiết lập trong cấu hình mô hình và sau đó được lưu vả tải lên Hub:

```py
from transformers import AutoModelForTokenClassification

model = AutoModelForTokenClassification.from_pretrained(
    model_checkpoint,
    id2label=id2label,
    label2id=label2id,
)
```

Giống như khi chúng tôi định nghĩa `AutoModelForSequenceClassification` của mình trong [Chương 3](/course/chapter3), việc tạo mô hình đưa ra cảnh báo rằng một số trọng số không được sử dụng (những trọng số từ đầu huấn luyện trước) và một số trọng số khác được khởi tạo ngẫu nhiên (những trọng số từ đầu phân loại token mới) và mô hình này nên được huấn luyện. Chúng ta sẽ làm điều đó sau một phút, nhưng trước tiên hãy kiểm tra kỹ xem mô hình của chúng ta có đúng số lượng nhãn hay không:

```python
model.config.num_labels
```

```python out
9
```

<Tip warning={true}>

⚠️ Nếu bạn có mô hình với số lượng nhãn sai, bạn sẽ nhận một lỗi khó hiểu khi gọi hàm `Trainer.train()` sau đó (giống như "CUDA error: device-side assert triggered"). Đây là nguyên nhân số một gây ra lỗi do người dùng báo cáo về những lỗi như vậy, vì vậy hãy đảm bảo bạn thực hiện kiểm tra này để xác nhận rằng bạn có số lượng nhãn dự kiến.

</Tip>

### Tinh chỉnh mô hình

Giờ ta đã sẵn sàng để huấn luyện mô hình của mình! Chúng ta chỉ cần làm hai điều trước khi định nghĩa `Trainer`:  đăng nhập vào Hugging Face và định nghĩa các tham số huấn luyện. Nếu bạn đang làm việc với notebook, có một hàm thuận tiện có thể giúp bạn: 

```python
from huggingface_hub import notebook_login

notebook_login()
```

Thao tác này sẽ hiển thị một tiện ích mà bạn có thể nhập thông tin đăng nhập Hugging Facecủa mình.

Nếu bạn không làm việc trong sổ ghi chép, chỉ cần nhập dòng sau vào thiết bị đầu cuối của bạn:

```bash
huggingface-cli login
```

Once this is done, we can define our `TrainingArguments`:

```python
from transformers import TrainingArguments

args = TrainingArguments(
    "bert-finetuned-ner",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
    push_to_hub=True,
)
```

Bạn đã từng thấy hầu hết những điều đó trước đây: chúng ta đặt một số siêu tham số (như tốc độ học, số epoch cần luyện tập và giảm trọng lượng) và chúng ta chỉ định `push_to_hub=True` để chỉ ra rằng chúng ta muốn lưu mô hình và đánh giá nó vào cuối mỗi epoch và rằng chúng ta muốn tải kết quả của mình lên Model Hub. Lưu ý rằng bạn có thể chỉ định tên của kho lưu trữ mà bạn muốn đẩy đến bằng tham số `hub_model_id` (cụ thể là bạn sẽ phải sử dụng tham số này để đẩy đến một tổ chức). Ví dụ: khi đẩy mô hình vào tổ chức [`huggingface-course`](https://huggingface.co/huggingface-course) chúng ta đã thêm `hub_model_id="huggingface-course/bert-finetuned-ner"` vào `TrainingArguments`. Theo mặc định, kho lưu trữ được sử dụng sẽ nằm trong không gian tên của bạn và được đặt tên theo thư mục đầu ra mà bạn đã đặt, vì vậy trong trường hợp của chúng tôi, nó sẽ là `"sgugger/bert-finetuned-ner"`.

<Tip>

💡 Nếu thư mục đầu ra bạn đang sử dụng đã tồn tại, nó cần phải là bản sao cục bộ của kho lưu trữ mà bạn muốn đẩy đến. Nếu không, bạn sẽ gặp lỗi khi xác định `Trainer` của mình và sẽ cần đặt một tên mới.

</Tip>

Cuối cùng, chúng ta chỉ cần truyền mọi thứ cho  `Trainer` và bắt đầu huấn luyện:

```python
from transformers import Trainer

trainer = Trainer(
    model=model,
    args=args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    compute_metrics=compute_metrics,
    tokenizer=tokenizer,
)
trainer.train()
```

Lưu ý rằng trong khi quá trình huấn luyện diễn ra, mỗi khi mô hình được lưu (ở đây, mỗi epoch), nó sẽ được tải lên Hub ở chế độ nền. Bằng cách này, bạn sẽ có thể tiếp tục huấn luyện của mình trên một máy khác nếu cần.

Sau khi quá trình huấn luyện hoàn tất, chúng ta sử dụng phương thức `push_to_hub()` để đảm bảo chúng ta tải lên phiên bản mới nhất của mô hình:

```py
trainer.push_to_hub(commit_message="Training complete")
```

Câu lệnh này trả về URL của cam khết nó vừa làm, nếu bạn muốn kiểm tra nó:

```python out
'https://huggingface.co/sgugger/bert-finetuned-ner/commit/26ab21e5b1568f9afeccdaed2d8715f571d786ed'
```

`Trainer` cũng soạn thảo một thẻ mô hình với tất cả các kết quả đánh giá và tải nó lên. Ở giai đoạn này, bạn có thể sử dụng tiện ích luận suy trên Model Hub để kiểm tra mô hình của mình và chia sẻ với bạn bè. Bạn đã tinh chỉnh thành công một mô hình trong tác vụ phân loại token - xin chúc mừng!

Nếu bạn muốn tìm hiểu sâu hơn một chút về vòng huấn luyện, bây giờ chúng tôi sẽ hướng dẫn bạn cách thực hiện điều tương tự bằng cách sử dụng 🤗 Accelerate.

## Một vòng huấn luyện tuỳ chỉnh

Bây giờ chúng ta hãy xem toàn bộ vòng lặp huấn luyện, vì vậy bạn có thể dễ dàng tùy chỉnh các phần bạn cần. Nó sẽ trông rất giống những gì chúng ta đã làm trong [Chương 3](/course/chapter3/4), với một vài thay đổi cho phần đánh giá.

### Chuẩn bị mọi thứ để huấn luyện

Đầu tiên, chúng ta cần xây dựng các `DataLoader` từ các tập dữ liệu của mình. Chúng ta sẽ sử dụng lại `data_collator` của mình dưới dạng `collate_fn` và xáo trộn tập huấn luyện, nhưng không phải tập kiểm định:

```py
from torch.utils.data import DataLoader

train_dataloader = DataLoader(
    tokenized_datasets["train"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=8,
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], collate_fn=data_collator, batch_size=8
)
```

Tiếp theo, chúng ta khôi phục mô hình của mình, để đảm bảo rằng chúng ta không tiếp tục tinh chỉnh từ trước mà bắt đầu lại từ mô hình được huấn luyện trước BERT:

```py
model = AutoModelForTokenClassification.from_pretrained(
    model_checkpoint,
    id2label=id2label,
    label2id=label2id,
)
```
Sau đó, chúng tôi sẽ cần một trình tối ưu hóa. Chúng ta sẽ sử dụng `AdamW` cổ điển, giống như `Adam`, nhưng với một bản sửa lỗi trong cách áp dụng weight decay (phân rã trọng số):

```py
from torch.optim import AdamW

optimizer = AdamW(model.parameters(), lr=2e-5)
```

Once we have all those objects, we can send them to the `accelerator.prepare()` method:

```py
from accelerate import Accelerator

accelerator = Accelerator()
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

<Tip>

🚨 Nếu bạn huấn luyện trên TPU, bạn sẽ cần chuyển tất cả các đoạn mã ở trên thành một hàm huấn luyện. Xem [Chương 3](/course/chapter3) để biết thêm chi tiết.

</Tip>

Bây giờ, chúng ta đã gửi `train_dataloader` của mình tới `speedrator.prepare()`, chúng ta có thể sử dụng độ dài của nó để tính số bước huấn luyện. Hãy nhớ rằng chúng ta phải luôn làm điều này sau khi chuẩn bị dataloader, vì phương thức đó sẽ thay đổi độ dài của nó. Chúng ta sử dụng một lịch trình tuyến tính cổ điển từ tốc độ học đến 0:

```py
from transformers import get_scheduler

num_train_epochs = 3
num_update_steps_per_epoch = len(train_dataloader)
num_training_steps = num_train_epochs * num_update_steps_per_epoch

lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)
```

Cuối cùng, để đẩy mô hình của chúng ta lên Hub, chúng ta sẽ cần tạo một đối tượng `Repository` trong một thư mục đang làm việc. Đầu tiên hãy đăng nhập vào Hugging Face, nếu bạn chưa đăng nhập. Chúng ta sẽ xác định tên kho lưu trữ từ ID mô hình mà ta muốn cung cấp cho mô hình của mình (vui lòng thay thế `repo_name` bằng sự lựa chọn của riêng bạn; nó chỉ cần chứa tên người dùng của bạn, đó là những gì hàm `get_full_repo_name ()` thực hiện):

```py
from huggingface_hub import Repository, get_full_repo_name

model_name = "bert-finetuned-ner-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'sgugger/bert-finetuned-ner-accelerate'
```

Sau đó, ta có thể sao chép kho lưu trữ đó trong một thư mục cục bộ. Nếu nó đã tồn tại, thư mục cục bộ này phải là bản sao hiện có của kho lưu trữ mà chúng ta đang làm việc:

```py
output_dir = "bert-finetuned-ner-accelerate"
repo = Repository(output_dir, clone_from=repo_name)
```

Giờ ta có thể tải mọi thứ ta lưu trong `output_dir` bằng cách gọi phương thức `repo.push_to_hub()`. Điều này sẽ giúp ta tải ngay lập tức mô hình ở cuối mỗi epoch.

### Vòng lặp huấn luyện

Bây giờ chúng ta đã sẵn sàng để viết vòng lặp  huấn luyện đầy đủ. Để đơn giản hóa phần đánh giá của nó, chúng ta định nghĩa hàm `postprocess()` lấy các dự đoán và nhãn và chuyển đổi chúng thành danh sách các chuỗi, giống như đối tượng `metric` mong đợi:

```py
def postprocess(predictions, labels):
    predictions = predictions.detach().cpu().clone().numpy()
    labels = labels.detach().cpu().clone().numpy()

    # Loại bỏ các chỉ mục bị ngó lơ (các token đặc biệt) và chuyển chúng thành nhãn
    true_labels = [[label_names[l] for l in label if l != -100] for label in labels]
    true_predictions = [
        [label_names[p] for (p, l) in zip(prediction, label) if l != -100]
        for prediction, label in zip(predictions, labels)
    ]
    return true_labels, true_predictions
```

Sau đó, chúng ta có thể viết vòng lặp huấn luyện. Sau khi xác định một thanh tiến trình để theo dõi quá trình huấn luyện diễn ra như thế nào, vòng lặp có ba phần:

- Bản thân quá trình huấn luyện, là vòng lặp cổ điển trên `train_dataloader`, truyền thẳng qua mô hình, sau đó truyền ngược và tối ưu hóa.
- Đánh giá, trong đó có một điểm mới sau khi nhận được kết quả đầu ra của mô hình trên một lô: vì hai quy trình có thể đã độn các đầu vào và nhãn thành các hình dạng khác nhau, chúng ta cần sử dụng `accelerator.pad_across_processes()`để đưa ra dự đoán và dán nhãn cho cùng một hình dạng trước khi gọi phương thức `collect()`. Nếu không làm điều này, đánh giá sẽ bị lỗi hoặc bị treo vĩnh viễn. Sau đó, chúng ta gửi kết quả đến `metric.add_batch()` và gọi `metric.compute()` khi vòng lặp đánh giá kết thúc.
- Lưu và tải lên, nơi đầu tiên chúng ta lưu mô hình và trình tokenize, sau đó gọi `repo.push_to_hub()`. Lưu ý rằng chúng ta sử dụng đối số `blocks=False` để yêu cầu thư viện 🤗 Hub đẩy vào một quá trình không đồng bộ. Bằng cách này, quá trình huấn luyện tiếp tục diễn ra bình thường và lệnh (dài) này được thực thi ở chế độ nền.

Đây là mã hoàn chỉnh cho vòng lặp huấn luyện:

```py
from tqdm.auto import tqdm
import torch

progress_bar = tqdm(range(num_training_steps))

for epoch in range(num_train_epochs):
    # Huấn luyện
    model.train()
    for batch in train_dataloader:
        outputs = model(**batch)
        loss = outputs.loss
        accelerator.backward(loss)

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)

    # Đánh giá
    model.eval()
    for batch in eval_dataloader:
        with torch.no_grad():
            outputs = model(**batch)

        predictions = outputs.logits.argmax(dim=-1)
        labels = batch["labels"]

        # Cần đệm các dự đoán và nhãn để tập hợp
        predictions = accelerator.pad_across_processes(predictions, dim=1, pad_index=-100)
        labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100)

        predictions_gathered = accelerator.gather(predictions)
        labels_gathered = accelerator.gather(labels)

        true_predictions, true_labels = postprocess(predictions_gathered, labels_gathered)
        metric.add_batch(predictions=true_predictions, references=true_labels)

    results = metric.compute()
    print(
        f"epoch {epoch}:",
        {
            key: results[f"overall_{key}"]
            for key in ["precision", "recall", "f1", "accuracy"]
        },
    )

    # Lưu và tải
    accelerator.wait_for_everyone()
    unwrapped_model = accelerator.unwrap_model(model)
    unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
    if accelerator.is_main_process:
        tokenizer.save_pretrained(output_dir)
        repo.push_to_hub(
            commit_message=f"Training in progress epoch {epoch}", blocking=False
        )
```

Trong trường hợp đây là lần đầu tiên bạn thấy một mô hình được lưu bằng 🤗 Accelerate, hãy dành một chút thời gian để kiểm tra ba dòng mã đi kèm với nó:

```py
accelerator.wait_for_everyone()
unwrapped_model = accelerator.unwrap_model(model)
unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
```

Dòng đầu tiên đã tự giải thích: nó cho tất cả các quá trình chờ cho đến khi mọi người ở giai đoạn đó trước khi tiếp tục. Điều này là để đảm bảo rằng ta có cùng một mô hình trong mọi quy trình trước khi lưu. Sau đó, chúng ta lấy `unwrapped_model`, là mô hình cơ sở mà ta đã xác định. Phương thức `accelerator.prepare()` thay đổi mô hình để hoạt động trong huấn luyện phân tán, vì vậy nó sẽ không có phương thức `save_pretrained()` nữa; phương thức `accelerator.unwrap_model()` hoàn tác bước đó. Cuối cùng, chúng ta gọi là `save_pretrained()` nhưng yêu cầu phương thức đó sử dụng `accelerator.save()` thay vì `torch.save()`.

Khi điều này được thực hiện, bạn sẽ có một mô hình tạo ra kết quả khá giống với mô hình được huấn luyện với `Trainer`. Bạn có thể kiểm tra mô hình mà chúng ta đã huấn luyện bằng cách sử dụng đoạn mã này tại [_huggingface-course/bert-finetuned-ner-accelerate_](https://huggingface.co/huggingface-course/bert-finetuned-ner-accelerate). Và nếu bạn muốn kiểm tra bất kỳ tinh chỉnh nào đối với vòng lặp huấn luyện, bạn có thể trực tiếp thực hiện chúng bằng cách chỉnh sửa đoạn mã được hiển thị ở trên!

{/if}

## Sử dụng mô hình đã được tinh chỉnh

Chúng tôi đã chỉ cho bạn cách bạn có thể sử dụng mô hình mà chúng ta đã tinh chỉnh trên Model Hub bằng tiện ích luận suy. Để sử dụng nó cục bộ trong một `pipeline`, bạn chỉ cần chỉ định mã định danh mô hình thích hợp:

```py
from transformers import pipeline

# Thay thế nó với checkpoint của ta
model_checkpoint = "huggingface-course/bert-finetuned-ner"
token_classifier = pipeline(
    "token-classification", model=model_checkpoint, aggregation_strategy="simple"
)
token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
```

```python out
[{'entity_group': 'PER', 'score': 0.9988506, 'word': 'Sylvain', 'start': 11, 'end': 18},
 {'entity_group': 'ORG', 'score': 0.9647625, 'word': 'Hugging Face', 'start': 33, 'end': 45},
 {'entity_group': 'LOC', 'score': 0.9986118, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
```

Tuyệt quá! Mô hình của chúng ta đang hoạt động tốt như mô hình mặc định cho pipeline này!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter7/2.mdx" />

### Hoàn thành phần 1!
https://huggingface.co/learn/course/vi/chapter4/5.md

# Hoàn thành phần 1!

<CourseFloatingBanner
    chapter={4}
    classNames="absolute z-10 right-0 top-0"
/>

Đây là mục cuối của phần đầu tiên trong khóa học! Phần 2 sẽ ra mắt vào ngày 15/11 tới đây với một sự kiện cộng đồng lớn, xem thêm thông tin [tại đây](https://huggingface.co/blog/course-launch-event).

Giờ đây, bạn có thể tinh chỉnh mô hình được huấn luyện trước về vấn đề phân loại văn bản (đơn hoặc cặp câu) và tải kết quả lên Model Hub. Để đảm bảo bạn thành thạo phần đầu tiên này, bạn nên làm chính xác phần đó đối với một vấn đề mà bạn quan tâm (và không nhất thiết phải bằng tiếng Anh nếu bạn nói một ngôn ngữ khác)! Bạn có thể tìm trợ giúp trong [diễn đàn Hugging Face](https://discuss.huggingface.co/) và chia sẻ dự án của mình trong [chủ đề này](https://discuss.huggingface.co/t/share-your-projects/6803) sau khi bạn hoàn thành.

Chúng tôi háo hức chờ đợi để xem bạn sẽ xây dựng những gì với điều này!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter4/5.mdx" />

### Hugging Face Hub
https://huggingface.co/learn/course/vi/chapter4/1.md

# Hugging Face Hub

<CourseFloatingBanner
    chapter={4}
    classNames="absolute z-10 right-0 top-0"
/>

[Hugging Face Hub](https://huggingface.co/) –- trang web chính của chúng tôi –- là một nền tảng tập trung cho phép mọi người khám phá, sử dụng và đóng góp các mô hình và bộ dữ liệu hiện đại nhất. Nó lưu trữ nhiều mô hình khác nhau, với hơn 10,000 mô hình được công bố rộng rãi. Chúng ta sẽ tập trung vào các mô hình trong chương này và xem xét các tập dữ liệu trong Chương 5.

Các mô hình trong Hub không giới hạn ở 🤗 Transformer hoặc thậm chí là NLP. Có các mô hình từ [Flair](https://github.com/flairNLP/flair) và [AllenNLP](https://github.com/allenai/allennlp) cho NLP, [Asteroid](https://github.com/asteroid-team/asteroid) và  [pyannote](https://github.com/pyannote/pyannote-audio) cho âm thanh và [timm](https://github.com/rwightman/pytorch-image-models) cho hình ảnh.

Mỗi mô hình được lưu trữ ở kho lưu trữ Git, cho phép tạo phiên bản và khả năng tái tạo. Chia sẻ mô hình trên Hub có nghĩa là mở rộng mô hình đó với cộng đồng và giúp bất kỳ ai muốn sử dụng mô hình đó đều có thể dễ dàng truy cập, do đó loại bỏ nhu cầu tự huấn luyện mô hình của họ và đơn giản hóa việc chia sẻ và sử dụng.

Ngoài ra, việc chia sẻ một mô hình trên Hub sẽ tự động triển khai một API luận suy được lưu trữ cho mô hình đó. Bất kỳ ai trong cộng đồng đều có thể tự do kiểm tra nó trực tiếp trên trang của mô hình, với các đầu vào tùy chỉnh và các vật dụng thích hợp.

Phần tốt nhất là việc chia sẻ và sử dụng bất kỳ mô hình công khai nào trên Hub là hoàn toàn miễn phí! [Gói trả phí](https://huggingface.co/pricing) cũng tồn tại nếu bạn muốn chia sẻ mô hình một cách riêng tư.

Video dưới đây hướng dẫn cách dùng Hub.

<Youtube id="XvSGPZFEjDY" />

Bạn cần có tài khoản huggingface.co để làm theo phần này, vì chúng ta sẽ tạo và quản lý kho lưu trữ trên Hugging Face Hub: [tạo tài khoản](https://huggingface.co/join)


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter4/1.mdx" />

### Chia sẻ các mô hình huấn luyện trước
https://huggingface.co/learn/course/vi/chapter4/3.md

# Chia sẻ các mô hình huấn luyện trước

{#if fw === 'pt'}

<CourseFloatingBanner chapter={4}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {
      label: "Google Colab",
      value:
        "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter4/section3_pt.ipynb",
    },
    {
      label: "Aws Studio",
      value:
        "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter4/section3_pt.ipynb",
    },
  ]}
/>

{:else}

<CourseFloatingBanner chapter={4}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {
      label: "Google Colab",
      value:
        "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter4/section3_tf.ipynb",
    },
    {
      label: "Aws Studio",
      value:
        "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter4/section3_tf.ipynb",
    },
  ]}
/>

{/if}

Trong các bước bên dưới, chúng ta sẽ xem xét các cách dễ nhất để chia sẻ các mô hình được huấn luyện trước với 🤗 Hub. Ta có sẵn các công cụ và tiện ích giúp việc chia sẻ và cập nhật mô hình trực tiếp trên Hub trở nên đơn giản, và chúng ta sẽ cùng nhau khám phá bên dưới.

<Youtube id="9yY3RB_GSPM" />

Chúng tôi khuyến khích tất cả người dùng huấn luyện mô hình đóng góp bằng cách chia sẻ chúng với cộng đồng - chia sẻ mô hình, ngay cả khi được huấn luyện trên các bộ dữ liệu rất cụ thể, sẽ giúp ích cho những người khác, giúp họ tiết kiệm thời gian và tính toán tài nguyên và cung cấp quyền truy cập vào các hiện vật hữu ích được huấn luyện. Đổi lại, bạn có thể hưởng lợi từ công việc mà những người khác đã làm!

Có ba cách để tạo kho lưu trữ mô hình mới:

- Sử dụng API `push_to_hub`
- Sử dụng thư viện Python `huggingface_hub`
- Sử dụng giao diện web

Khi bạn đã tạo một kho lưu trữ, bạn có thể tải tệp lên đó qua git và git-lfs. Chúng tôi sẽ hướng dẫn bạn cách tạo kho lưu trữ mô hình và tải tệp lên chúng trong các phần sau.

## Sử dụng API `push_to_hub`

{#if fw === 'pt'}

<Youtube id="Zh0FfmVrKX0" />

{:else}

<Youtube id="pUh5cGmNV8Y" />

{/if}

Cách đơn giản nhất để tải tệp lên Hub là tận dụng API `push_to_hub`.

Trước khi đi xa hơn, bạn sẽ cần tạo token xác thực để API `huggingface_hub` biết bạn là ai và bạn có quyền ghi vào không gian tên nào. Đảm bảo rằng bạn đang ở trong môi trường mà bạn đã cài đặt `transformers` (xem [Thiết lập](/course/chapter0)). Nếu bạn đang ở trong notebook, bạn có thể sử dụng chức năng sau để đăng nhập:

```python
from huggingface_hub import notebook_login

notebook_login()
```

Trên terminal, bạn có thể:

```bash
huggingface-cli login
```

Trong cả hai trường hợp, bạn sẽ được nhắc nhập tên người dùng và mật khẩu của mình, đó là những mật khẩu mà bạn sử dụng để đăng nhập vào Hub. Nếu bạn chưa có hồ sơ Hub, bạn nên tạo một hồ sơ [tại đây](https://huggingface.co/join).

Tuyệt vời! Bây giờ bạn có token xác thực được lưu trữ trong thư mục bộ nhớ cache của mình. Hãy tạo một số kho lưu trữ thôi!

{#if fw === 'pt'}

Nếu bạn đã thử với API `Trainer` để huấn luyện một mô hình, thì cách dễ nhất để tải nó lên Hub là đặt `push_to_hub=True` khi bạn định nghĩa  `TrainingArguments`:

```py
from transformers import TrainingArguments

training_args = TrainingArguments(
    "bert-finetuned-mrpc", save_strategy="epoch", push_to_hub=True
)
```

Khi bạn gọi `trainer.train()`, `Trainer` sau đó sẽ tải mô hình của bạn lên Hub mỗi khi nó được lưu (ở đây là mỗi epoch) trong một kho lưu trữ trong không gian tên của bạn. Kho lưu trữ đó sẽ được đặt tên giống như thư mục đầu ra bạn đã chọn (ở đây là `bert-finetuned-mrpc`) nhưng bạn có thể chọn một tên khác với `hub_model_id = "a_different_name"`.

Để tải mô hình của bạn lên tổ chức mà bạn là thành viên, chỉ cần truyền nó vào qua `hub_model_id = "my_organization/my_repo_name"`.

Sau khi quá trình huấn luyện của bạn kết thúc, bạn nên thực hiện một `trainer.push_to_hub()` cuối cùng để tải lên phiên bản cuối cùng của mô hình của bạn. Nó cũng sẽ tạo ra một thẻ mô hình với tất cả các siêu dữ liệu liên quan, báo cáo các siêu tham số được sử dụng và kết quả đánh giá! Dưới đây là một ví dụ về nội dung bạn có thể tìm thấy trong một thẻ mô hình như vậy:

<div class="flex justify-center">
  <img
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/model_card.png"
    alt="An example of an auto-generated model card."
    width="100%"
  />
</div>

{:else}

Nếu bạn sử dụng Keras để huấn luyện mô hình, cách dễ nhất để tải nó lên Hub là sử dụng `PushToHubCallback` khi bạn gọi `model.fit()`:

```py
from transformers import PushToHubCallback

callback = PushToHubCallback(
    "bert-finetuned-mrpc", save_strategy="epoch", tokenizer=tokenizer
)
```

Sau đó, bạn nên thêm `callbacks=[callback]` trong lệnh gọi của mình tới `model.fit()`. Sau đó, lệnh này sẽ tải mô hình của bạn lên Hub mỗi khi nó được lưu (ở đây là mỗi epoch) trong một kho lưu trữ trên không gian tên của bạn. Kho lưu trữ đó sẽ được đặt tên giống như thư mục đầu ra bạn đã chọn (ở đây là `bert-finetuned-mrpc`) nhưng bạn có thể chọn một tên khác với `hub_model_id = "a_different_name"`.

Để tải mô hình của bạn lên tổ chức mà bạn là thành viên, chỉ cần truyền nó vào qua `hub_model_id = "my_organization/my_repo_name"`.

{/if}

Ở cấp độ thấp hơn, việc truy cập Model Hub có thể được thực hiện trực tiếp trên các mô hình, tokenizer và các đối tượng cấu hình thông qua phương thức `push_to_hub()`. Phương pháp này xử lý cả việc tạo kho lưu trữ và đẩy các tệp mô hình và tệp tokenizer trực tiếp đến kho lưu trữ. Không cần xử lý thủ công, không giống như với API mà chúng ta sẽ thấy bên dưới.

Để có ý tưởng về cách nó hoạt động, trước tiên chúng ta hãy khởi tạo một mô hình và một tokenizer:

{#if fw === 'pt'}

```py
from transformers import AutoModelForMaskedLM, AutoTokenizer

checkpoint = "camembert-base"

model = AutoModelForMaskedLM.from_pretrained(checkpoint)
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
```

{:else}

```py
from transformers import TFAutoModelForMaskedLM, AutoTokenizer

checkpoint = "camembert-base"

model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
```

{/if}

Bạn có thể tự do làm bất cứ điều gì bạn muốn với những thứ này - thêm token vào trình tokenize, huấn luyện mô hình, tinh chỉnh nó. Khi bạn hài lòng với kết quả mô hình, trọng số, và tokenizer, bạn có thể tận dụng phương thức `push_to_hub()` có sẵn trực tiếp trên đối tượng `model`:

```py
model.push_to_hub("dummy-model")
```

Điều này sẽ tạo kho lưu trữ mới `dummy-model` trong hồ sơ của bạn và điền nó vào các tệp mô hình của bạn.
Làm tương tự với tokenizer để tất cả các tệp có sẵn trong kho lưu trữ này:

```py
tokenizer.push_to_hub("dummy-model")
```

Nếu bạn thuộc một tổ chức, chỉ cần chỉ định tham số `organization` để tải lên không gian tên của tổ chức đó:

```py
tokenizer.push_to_hub("dummy-model", organization="huggingface")
```

Nếu bạn muốn sử dụng một token Hugging Face cụ thể, bạn cũng có thể chỉ định nó thông qua `push_to_hub()`:

```py
tokenizer.push_to_hub("dummy-model", organization="huggingface", use_auth_token="<TOKEN>")
```

Giờ hãy đi tới Model Hub để tìm mô hình mới được tải lên của bạn: *https://huggingface.co/user-or-organization/dummy-model*.

Nhấp vào tab "Files and versions" ("Tệp và phiên bản") và bạn sẽ thấy các tệp hiển thị trong ảnh chụp màn hình sau:

{#if fw === 'pt'}

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/push_to_hub_dummy_model.png" alt="Dummy model containing both the tokenizer and model files." width="80%"/>
</div>
{:else}
<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/push_to_hub_dummy_model_tf.png" alt="Dummy model containing both the tokenizer and model files." width="80%"/>
</div>
{/if}

<Tip>

✏️ **Thử nghiệm thôi!** Lấy mô hình và trình tokenize được liên kết với checkpoint `bert-base-cased` và tải chúng lên kho lưu trữ trong không gian tên của bạn bằng phương thức `push_to_hub()`. Kiểm tra kỹ xem repo có xuất hiện chính xác trên trang của bạn hay không trước khi xóa nó.

</Tip>

Như bạn đã thấy, phương thức `push_to_hub()` nhận một vài tham số, giúp bạn có thể tải lên không gian tên tổ chức hoặc kho lưu trữ cụ thể hoặc sử dụng token API khác. Chúng tôi khuyên bạn nên xem thông số kỹ thuật phương pháp có sẵn trực tiếp trong [🤗 tài liệu về Transformers](https://huggingface.co/transformers/model_sharing.html) để biết những gì ta có thể làm.

Phương thức `push_to_hub()` được hỗ trợ bởi gói Python [`huggingface_hub`](https://github.com/huggingface/huggingface_hub), cung cấp một API trực tiếp đến Hugging Face Hub. Nó được tích hợp trong 🤗 Transformers và một số thư viện học máy khác, như [`allenlp`](https://github.com/allenai/allennlp). Mặc dù chúng tôi tập trung vào tích hợp 🤗 Transformers trong chương này, việc tích hợp nó vào mã hoặc thư viện của riêng bạn rất đơn giản.

Chuyển đến phần cuối cùng để xem cách tải tệp lên kho lưu trữ mới tạo của bạn!

## Sử dụng thư viện Python `huggingface_hub`

Thư viện Python `huggingface_hub` là một gói cung cấp một bộ công cụ cho các hub mô hình và tập dữ liệu. Nó cung cấp các phương thức và lớp đơn giản cho các tác vụ phổ biến như tiếp nhận thông tin về kho lưu trữ trên hub và quản lý chúng. Nó cung cấp các API đơn giản hoạt động trên git để quản lý nội dung của các kho đó và tích hợp Hub trong các dự án và thư viện của bạn.

Tương tự như việc sử dụng API `push_to_hub`, điều này sẽ yêu cầu bạn lưu token API vào bộ nhớ cache của mình. Để thực hiện việc này, bạn sẽ cần sử dụng lệnh `login` từ CLI, như đã đề cập trong phần trước (một lần nữa, hãy đảm bảo thêm các lệnh này với ký tự  `!` nếu chạy trên Google Colab):

```bash
huggingface-cli login
```

Gói `huggingface_hub` cung cấp một số phương thức và lớp hữu ích cho mục đích của chúng ta. Thứ nhất, có một số phương pháp để quản lý việc tạo, xóa kho lưu trữ và các phương pháp khác:

```python no-format
from huggingface_hub import (
    # Quản lý người dùng
    login,
    logout,
    whoami,

    # Tạo và quản lý kho dữ liệu
    create_repo,
    delete_repo,
    update_repo_visibility,

    # Và một số phương thức truy xuất/thay đổi thông tin về mặt nội dung 
    list_models,
    list_datasets,
    list_metrics,
    list_repo_files,
    upload_file,
    delete_file,
)
```

Ngoài ra, nó cung cấp lớp `Repository` rất mạnh mẽ để quản lý một kho lưu trữ cục bộ. Chúng ta sẽ khám phá các phương thức này và lớp đó trong phần tiếp theo để hiểu cách tận dụng chúng.

Phương thức `create_repo` có thể được sử dụng để tạo một kho lưu trữ mới trên hub:

```py
from huggingface_hub import create_repo

create_repo("dummy-model")
```

Thao tác này sẽ tạo kho lưu trữ `dummy-model` trong không gian tên của bạn. Nếu muốn, bạn có thể chỉ định tổ chức nào mà kho lưu trữ sẽ thuộc về bằng cách sử dụng tham số `organization`:

```py
from huggingface_hub import create_repo

create_repo("dummy-model", organization="huggingface")
```

Thao tác này sẽ tạo kho lưu trữ `dummy-model` trong không gian tên `huggingface`, giả sử bạn thuộc tổ chức đó.
Các tham số có thể hữu ích khác là:

- `private`, để chỉ định xem liệu kho lưu trữ có nên hiển thị với những người khác hay không.
- `token`, nếu bạn muốn ghi đè token được lưu trữ trong bộ nhớ cache của mình bằng một token nhất định.
- `repo_type`, nếu bạn muốn tạo `dataset`  hoặc `space` thay vì một mô hình. Các giá trị được chấp nhận là `"dataset"` và `"space"`.

Khi kho lưu trữ được tạo, chúng ta nên thêm tệp vào đó! Chuyển sang phần tiếp theo để xem ba cách có thể xử lý vấn đề này.

## Sử dụng giao diện web

Giao diện web cung cấp các công cụ để quản lý kho lưu trữ trực tiếp trong Hub. Sử dụng giao diện này, bạn có thể dễ dàng tạo kho lưu trữ, thêm tệp (thậm chí cả tệp lớn!), Khám phá các mô hình, trực quan hóa các điểm khác biệt và hơn thế nữa.

Để tạo một kho lưu trữ mới, hãy truy cập [huggingface.co/new](https://huggingface.co/new):

<div class="flex justify-center">
  <img
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/new_model.png"
    alt="Page showcasing the model used for the creation of a new model repository."
    width="80%"
  />
</div>

Đầu tiên, chỉ định chủ sở hữu của kho lưu trữ: đây có thể là bạn hoặc bất kỳ tổ chức nào mà bạn liên kết. Nếu bạn chọn một tổ chức, mô hình sẽ được giới thiệu trên trang của tổ chức và mọi thành viên của tổ chức sẽ có khả năng đóng góp vào kho lưu trữ.

Tiếp theo, nhập tên mô hình của bạn. Đây cũng sẽ là tên của kho lưu trữ. Cuối cùng, bạn có thể chỉ định xem bạn muốn mô hình của mình là công khai hay riêng tư. Các mô hình tư nhân được ẩn khỏi chế độ xem công khai.

Sau khi tạo kho mô hình, bạn sẽ thấy một trang như sau:

<div class="flex justify-center">
  <img
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/empty_model.png"
    alt="An empty model page after creating a new repository."
    width="80%"
  />
</div>

Đây là nơi mô hình của bạn sẽ được lưu trữ. Để bắt đầu điền nó, bạn có thể thêm tệp README trực tiếp từ giao diện web.

<div class="flex justify-center">
  <img
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/dummy_model.png"
    alt="The README file showing the Markdown capabilities."
    width="80%"
  />
</div>

Tệp README nằm trong Markdown - hãy thoải mái sử dụng nó! Phần thứ ba của chương này dành riêng cho việc xây dựng một thẻ mô hình. Đây là những điều quan trọng hàng đầu trong việc mang lại giá trị cho mô hình của bạn, vì chúng là nơi bạn nói cho người khác biết nó có thể làm gì.

Nếu bạn nhìn vào tab "Files and versions" hay "Tệp và phiên bản", bạn sẽ thấy rằng chưa có nhiều tệp ở đó - chỉ có _README.md_  bạn vừa tạo và tệp _.gitattributes_ theo dõi các tệp lớn.

<div class="flex justify-center">
  <img
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/files.png"
    alt="The 'Files and versions' tab only shows the .gitattributes and README.md files."
    width="80%"
  />
</div>

Tiếp theo, chúng ta sẽ xem xét cách thêm một số tệp mới.

## Tải các tệp mô hình

Hệ thống quản lý tệp trên Hugging Face Hub dựa trên git cho các tệp thông thường và git-lfs (viết tắt của [Git Large File Storage](https://git-lfs.github.com/)) cho các tệp lớn hơn .

Trong phần tiếp theo, chúng ta sẽ xem xét ba cách khác nhau để tải tệp lên Hub: thông qua `huggingface_hub` và thông qua lệnh git.

### Phương pháp `upload_file`

Sử dụng `upload_file` không yêu cầu cài đặt git và git-lfs trên hệ thống của bạn. Nó đẩy các tệp trực tiếp đến 🤗 Hub bằng cách sử dụng các yêu cầu HTTP POST. Một hạn chế của phương pháp này là nó không xử lý các tệp có kích thước lớn hơn 5GB.
Nếu tệp của bạn lớn hơn 5GB, vui lòng làm theo hai phương pháp khác được nêu chi tiết bên dưới.

API có thể được sử dụng như sau:

```py
from huggingface_hub import upload_file

upload_file(
    "<path_to_file>/config.json",
    path_in_repo="config.json",
    repo_id="<namespace>/dummy-model",
)
```

Thao tác này sẽ tải tệp `config.json` có sẵn tại `<path_to_file>` vào thư mục gốc của kho lưu trữ là  `config.json`, vào kho lưu trữ `dummy-model`.
Các tham số có thể hữu ích khác là:

- `token`, nếu bạn muốn ghi đè token được lưu trữ trong bộ nhớ cache của mình bằng một token nhất định.
- `repo_type`, nếu bạn muốn tải lên `dataset` hoặc `space` thay vì một mô hình. Các giá trị được chấp nhận là `"dataset"` và `"space"`.

### Lớp `Repository`

Lớp `Repository` quản lý một kho lưu trữ cục bộ theo cách giống như git. Nó tóm tắt hầu hết các điểm khó khăn mà người ta có thể có với git để cung cấp tất cả các tính năng mà chúng tôi yêu cầu.

Sử dụng lớp này yêu cầu phải cài đặt git và git-lfs, vì vậy hãy đảm bảo rằng bạn đã cài đặt git-lfs (xem [tại đây](https://git-lfs.github.com/) để biết hướng dẫn cài đặt) và thiết lập trước khi bắt đầu.

Để bắt đầu chơi với kho lưu trữ chúng ta vừa tạo, chúng ta có thể bắt đầu bằng cách khởi tạo nó vào một thư mục cục bộ bằng cách sao chép kho lưu trữ từ xa:

```py
from huggingface_hub import Repository

repo = Repository("<path_to_dummy_folder>", clone_from="<namespace>/dummy-model")
```

Thao tác này đã tạo thư mục `<path_to_dummy_folder>` trong thư mục làm việc của chúng ta. Thư mục này chỉ chứa tệp `.gitattributes` vì đó là tệp duy nhất được tạo khi khởi tạo kho lưu trữ thông qua `create_repo`.

Từ thời điểm này, chúng ta có thể tận dụng một số phương pháp git truyền thống:

```py
repo.git_pull()
repo.git_add()
repo.git_commit()
repo.git_push()
repo.git_tag()
```

Và những cái khác! Chúng tôi khuyên bạn nên xem tài liệu về `Repository` hay `Kho lưu trữ` có sẵn [tại đây](https://github.com/huggingface/huggingface_hub/tree/main/src/huggingface_hub#advanced-programmatic-repository-management) để biết tổng quan về tất cả các phương pháp.

Hiện tại, chúng ta có một mô hình và một tokenizer mà ta muốn đưa vào Hub. Chúng ta đã nhân bản thành công kho lưu trữ, do đó chúng tôi có thể lưu các tệp trong kho lưu trữ đó.

Trước tiên, chúng tôi đảm bảo rằng bản sao cục bộ được cập nhật bằng cách kéo về những thay đổi mới nhất:

```py
repo.git_pull()
```

Sau đó, ta lưu mô hình và tệp tokenizer:

```py
model.save_pretrained("<path_to_dummy_folder>")
tokenizer.save_pretrained("<path_to_dummy_folder>")
```

`<path_to_dummy_folder>` bây giờ chứa tất cả các tệp mô hình và tokenizer. Chúng ta thực hiện theo quy trình làm việc git thông thường bằng cách thêm tệp vào khu vực lưu trữ thay đổi, cam kết chúng và đẩy chúng vào hub:

```py
repo.git_add()
repo.git_commit("Thêm mô hình và tệp tokenizer")
repo.git_push()
```

Xin chúc mừng! Bạn vừa đẩy các tệp đầu tiên của mình lên Hub.

### Phương pháp dựa trên git

Đây là cách tiếp cận rất đơn giản để tải tệp lên: chúng ta sẽ làm trực tiếp với git và git-lfs. Hầu hết khó khăn đã được loại bỏ bởi các cách tiếp cận trước đây, nhưng có một số lưu ý với phương pháp tiếp theo, vì vậy chúng ta sẽ theo một trường hợp sử dụng phức tạp hơn.

Sử dụng lớp này yêu cầu phải cài đặt git và git-lfs, vì vậy hãy đảm bảo bạn đã cài đặt [git-lfs](https://git-lfs.github.com/) (xem hướng dẫn cài đặt tại đây) và cài đặt trước khi bắt đầu .

Trước tiên, hãy bắt đầu bằng cách khởi tạo git-lfs:

```bash
git lfs install
```

```bash
Updated git hooks.
Git LFS initialized.
```

Sau khi hoàn tất, bước đầu tiên là sao chép kho lưu trữ mô hình của bạn:

```bash
git clone https://huggingface.co/<namespace>/<your-model-id>
```

Tên người dùng của tôi là `lysandre` và ta đã sử dụng tên mô hình là `dummy`, vì vậy lệnh kết thúc như sau:

```
git clone https://huggingface.co/lysandre/dummy
```

Bây giờ ta có một thư mục tên _dummy_ trong thư mục làm việc của mình. Ta có thể `cd` vào thư mục và xem nội dung:

```bash
cd dummy && ls
```

```bash
README.md
```

Nếu bạn vừa tạo kho lưu trữ của mình bằng phương pháp `create_repo` của Hugging Face Hub, thì thư mục này chỉ nên chứa tệp `.gitattributes` ẩn. Nếu bạn đã làm theo hướng dẫn trong phần trước để tạo kho lưu trữ bằng giao diện web, thì thư mục phải chứa một tệp _README.md_ duy nhất cùng với tệp `.gitattributes` ẩn, như được hiển thị ở đây.

Việc thêm một tệp có kích thước thông thường, chẳng hạn như tệp cấu hình, tệp từ vựng hoặc về cơ bản là bất kỳ tệp nào dưới vài megabyte, được thực hiện chính xác như cách người ta làm trong bất kỳ hệ thống dựa trên git nào. Tuy nhiên, các tệp lớn hơn phải được đăng ký thông qua git-lfs để đẩy chúng lên _huggingface.co_.

Hãy quay lại Python một chút để tạo một mô hình và trình tokenize mà chúng ta muốn cam kết với kho lưu trữ dummy của chúng ta:

{#if fw === 'pt'}

```py
from transformers import AutoModelForMaskedLM, AutoTokenizer

checkpoint = "camembert-base"

model = AutoModelForMaskedLM.from_pretrained(checkpoint)
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

# Làm bất cứ điều gì với mô hình, huấn luyện nó, tinh chỉnh nó ...

model.save_pretrained("<path_to_dummy_folder>")
tokenizer.save_pretrained("<path_to_dummy_folder>")
```

{:else}

```py
from transformers import TFAutoModelForMaskedLM, AutoTokenizer

checkpoint = "camembert-base"

model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

# Làm bất cứ điều gì với mô hình, huấn luyện nó, tinh chỉnh nó ...

model.save_pretrained("<path_to_dummy_folder>")
tokenizer.save_pretrained("<path_to_dummy_folder>")
```

{/if}

Bây giờ chúng ta đã lưu một số tạo tác mô hình và tokenizer, hãy xem xét lại thư mục _dummy_:

```bash
ls
```

{#if fw === 'pt'}

```bash
config.json  pytorch_model.bin  README.md  sentencepiece.bpe.model  special_tokens_map.json tokenizer_config.json  tokenizer.json
```

Nếu bạn nhìn vào kích thước tệp (ví dụ: với `ls -lh`), bạn sẽ thấy rằng tệp dict trạng thái mô hình (_pytorch_model.bin_) là ngoại lệ duy nhất, với hơn 400 MB.

{:else}

```bash
config.json  README.md  sentencepiece.bpe.model  special_tokens_map.json  tf_model.h5  tokenizer_config.json  tokenizer.json
```

Nếu bạn nhìn vào kích thước tệp (ví dụ: với `ls -lh`), bạn sẽ thấy rằng tệp dict trạng thái mô hình (_t5_model.h5_) là ngoại lệ duy nhất, với hơn 400 MB.

{/if}

<Tip>
  ✏️  Khi tạo kho lưu trữ từ giao diện web, tệp *.gitattributes* được tự động thiết lập để xem xét các tệp có phần mở rộng nhất định, chẳng hạn như *.bin* và *.h5*, là tệp lớn và git-lfs sẽ theo dõi chúng mà không có thiết lập cần thiết về phía bạn.
</Tip>{" "}

Bây giờ chúng ta có thể tiếp tục và tiến hành như chúng ta thường làm với các kho lưu trữ Git truyền thống. Chúng ta có thể thêm tất cả các tệp vào môi trường dàn dựng của Git bằng lệnh `git add`:

```bash
git add .
```

Sau đó, chúng ta có thể xem xét các tệp hiện đang được sắp xếp:

```bash
git status
```

{#if fw === 'pt'}

```bash
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
  modified:   .gitattributes
	new file:   config.json
	new file:   pytorch_model.bin
	new file:   sentencepiece.bpe.model
	new file:   special_tokens_map.json
	new file:   tokenizer.json
	new file:   tokenizer_config.json
```

{:else}

```bash
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
  modified:   .gitattributes
  	new file:   config.json
	new file:   sentencepiece.bpe.model
	new file:   special_tokens_map.json
	new file:   tf_model.h5
	new file:   tokenizer.json
	new file:   tokenizer_config.json
```

{/if}

Tương tự, chúng ta có thể đảm bảo rằng git-lfs đang theo dõi các tệp chính xác bằng cách sử dụng lệnh `status`:

```bash
git lfs status
```

{#if fw === 'pt'}

```bash
On branch main
Objects to be pushed to origin/main:


Objects to be committed:

	config.json (Git: bc20ff2)
	pytorch_model.bin (LFS: 35686c2)
	sentencepiece.bpe.model (LFS: 988bc5a)
	special_tokens_map.json (Git: cb23931)
	tokenizer.json (Git: 851ff3e)
	tokenizer_config.json (Git: f0f7783)

Objects not staged for commit:


```

Chúng ta có thể thấy rằng tất cả các tệp đều có `Git` làm trình xử lý, ngoại trừ _pytorch_model.bin_ và _sentencepiece.bpe.model_, có` LFS`. Tuyệt vời!

{:else}

```bash
On branch main
Objects to be pushed to origin/main:


Objects to be committed:

	config.json (Git: bc20ff2)
	sentencepiece.bpe.model (LFS: 988bc5a)
	special_tokens_map.json (Git: cb23931)
	tf_model.h5 (LFS: 86fce29)
	tokenizer.json (Git: 851ff3e)
	tokenizer_config.json (Git: f0f7783)

Objects not staged for commit:


```

Chúng ta có thể thấy rằng tất cả các tệp đều có `Git` làm trình xử lý, ngoại trừ _t5_model.h5_, có `LFS`. Tuyệt vời!

{/if}

Hãy tiến hành các bước cuối cùng, cam kết và đẩy đến kho lưu trữ từ xa _huggingface.co_:

```bash
git commit -m "First model version"
```

{#if fw === 'pt'}

```bash
[main b08aab1] First model version
 7 files changed, 29027 insertions(+)
  6 files changed, 36 insertions(+)
 create mode 100644 config.json
 create mode 100644 pytorch_model.bin
 create mode 100644 sentencepiece.bpe.model
 create mode 100644 special_tokens_map.json
 create mode 100644 tokenizer.json
 create mode 100644 tokenizer_config.json
```

{:else}

```bash
[main b08aab1] First model version
 6 files changed, 36 insertions(+)
 create mode 100644 config.json
 create mode 100644 sentencepiece.bpe.model
 create mode 100644 special_tokens_map.json
 create mode 100644 tf_model.h5
 create mode 100644 tokenizer.json
 create mode 100644 tokenizer_config.json
```

{/if}

Việc đẩy có thể mất một chút thời gian, tùy thuộc vào tốc độ kết nối internet và kích thước tệp của bạn:

```bash
git push
```

```bash
Uploading LFS objects: 100% (1/1), 433 MB | 1.3 MB/s, done.
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 12 threads
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 288.27 KiB | 6.27 MiB/s, done.
Total 9 (delta 1), reused 0 (delta 0), pack-reused 0
To https://huggingface.co/lysandre/dummy
   891b41d..b08aab1  main -> main
```

{#if fw === 'pt'}
Nếu chúng ta xem qua kho lưu trữ mô hình khi quá trình này kết thúc, chúng ta có thể thấy tất cả các tệp được thêm gần đây:

<div class="flex justify-center">
  <img
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/full_model.png"
    alt="The 'Files and versions' tab now contains all the recently uploaded files."
    width="80%"
  />
</div>

Giao diện người dùng cho phép bạn khám phá các tệp mô hình và các cam kết cũng như xem sự khác biệt được giới thiệu bởi mỗi cam kết:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/diffs.gif" alt="The diff introduced by the recent commit." width="80%"/>
</div>
{:else}
Nếu chúng ta xem qua kho lưu trữ mô hình khi quá trình này kết thúc, chúng ta có thể thấy tất cả các tệp được thêm gần đây:

<div class="flex justify-center">
  <img
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/full_model_tf.png"
    alt="The 'Files and versions' tab now contains all the recently uploaded files."
    width="80%"
  />
</div>

Giao diện người dùng cho phép bạn khám phá các tệp mô hình và các cam kết cũng như xem sự khác biệt được giới thiệu bởi mỗi cam kết:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/diffstf.gif" alt="The diff introduced by the recent commit." width="80%"/>
</div>
{/if}


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter4/3.mdx" />

### Đố vui cuối chương
https://huggingface.co/learn/course/vi/chapter4/6.md

# Đố vui cuối chương

<CourseFloatingBanner
    chapter={4}
    classNames="absolute z-10 right-0 top-0"
/>

Hãy kiểm tra những gì bạn đã học được trong chương này!

### 1. Các mô hình tải lên trên Hub có giới hạn gì?

<Question
  choices={[
    {
      text: "Mô hình từ thư viện 🤗 Transformers.",
      explain:
        "Mặc dù các mô hình từ thư viện 🤗 Transformers được hỗ trợ trên Hugging Face Hub, nhưng chúng không phải là những mô hình duy nhất!",
    },
    {
      text: "Tất cả các mô hình có giao diện tương tự như 🤗 Transformers.",
      explain:
        "Không có yêu cầu giao diện nào được đặt ra khi tải các mô hình lên Hugging Face Hub.",
    },
    {
      text: "Không có giới hạn.",
      explain: "Đúng! Không có giới hạn khi tải mô hình lên Hub.",
      correct: true,
    },
    {
      text: "Các mô hình theo một cách nào đó có liên quan đến NLP.",
      explain: "Không có yêu cầu nào được đặt ra đối với lĩnh vực ứng dụng!",
    },
  ]}
/>

### 2. Bạn có thể quản lý các mô hình trên Hub bằng cách nào?

<Question
  choices={[
    {
      text: "Thông qua một tài khoản GCP.",
      explain: "Sai!",
    },
    {
      text: "Thông qua phân phối ngang hàng.",
      explain: "Sai!",
    },
    {
      text: "Thông qua git và git-lfs.",
      explain:
        "Đúng! Các mô hình trên Hub là kho lưu trữ Git đơn giản, tận dụng <code>git-lfs</code> cho các tệp lớn.",
      correct: true,
    },
  ]}
/>

### 3. Bạn có thể làm những gì khi sử dụng giao diện web Hugging Face Hub?

<Question
  choices={[
    {
      text: "Fork một kho lưu trữ hiện có.",
      explain: "Không thể trên Hugging Face Hub.",
    },
    {
      text: "Tạo kho lưu trữ mô hình mới.",
      explain: "Đúng! Nhưng đó không phải là tất cả những gì bạn có thể làm.",
      correct: true,
    },
    {
      text: "Quản lý và chỉnh sửa tệp.",
      explain: "Đúng! Nhưng đó không phải là câu trả lời đúng duy nhất.",
      correct: true,
    },
    {
      text: "Tải lên tệp.",
      explain: "Đúng! Nhưng đó không phải là tất cả.",
      correct: true,
    },
    {
      text: "Xem sự khác biệt giữa các phiên bản.",
      explain: "Đúng! Nhưng đó không phải là tất cả những gì bạn có thể làm",
      correct: true,
    },
  ]}
/>

### 4. Thẻ mô hình là gì?

<Question
  choices={[
    {
      text: "Mô tả sơ bộ về mô hình, do đó ít quan trọng hơn mô hình và tệp tokenizer.",
      explain:
        "Nó thực sự là một mô tả về mô hình, nhưng đó là một phần quan trọng: nếu nó không đầy đủ hoặc bị thiếu mất, tiện ích của mô hình sẽ giảm đáng kể.",
    },
    {
      text: "Một cách để đảm bảo khả năng tái tạo, khả năng tái sử dụng và tính công bằng.",
      explain:
        "Đúng! Chia sẻ thông tin phù hợp trong thẻ mô hình sẽ giúp người dùng tận dụng mô hình của bạn và nhận thức được các giới hạn và sai lệch ​​của nó.",
      correct: true,
    },
    {
      text: "Một tệp Python có thể được chạy để truy xuất thông tin về mô hình.",
      explain: "Thẻ mô hình là các tệp Markdown đơn giản thôi.",
    },
  ]}
/>

### 5. Đối tượng nào sau đây của thư viện 🤗 Transformers có thể được chia sẻ trực tiếp trên Hub với `push_to_hub()`?

{#if fw === 'pt'}

<Question
	choices={[
		{
			text: "Một tokenizer",
			explain: "Đúng! Tất cả các trình tokenize đều có phương thức <code>push_to_hub</code> giúp đẩy tất cả các tệp tokenizer (từ vựng, kiến ​​trúc của tokenizer, v.v.) đến một repo nhất định. Tuy nhiên, đó không phải là câu trả lời đúng duy nhất!",
            correct: true
		},
		{
			text: "Một tệp cấu hình mô hình",
			explain: "Đúng vậy! Tất cả các tệp cấu hình mô hình đều có phương thức <code>push_to_hub</code> giúp đẩy chúng đến một repo. Bạn có thể chia sẻ điều gì khác nữa không?",
            correct: true
		},
		{
			text: "Một mô hình",
			explain: "Chính xác! Tất cả các mô hình đều có phương thức <code>push_to_hub</code> giúp đẩy mô hình và các tệp cấu hình đến một repo nhất định.Tuy nhiên, đó không phải là câu trả lời đúng duy nhất!",
            correct: true
		},
        {
			text: "Một Trainer",
			explain: "Đúng vậy— <code>Trainer</code> cũng triển khai phương thức <code>push_to_hub</code> giúp tải mô hình, cấu hình, tokenizer và thẻ mô hình của chúng đến một repo nhất định. Thử thêm đáp án khác nữa xem!",
            correct: true
		}
	]}
/>
{:else}
<Question
	choices={[
		{
			text: "Một tokenizer",
			explain: "Đúng! Tất cả các trình tokenize đều có phương thức <code>push_to_hub</code> giúp đẩy tất cả các tệp tokenizer (từ vựng, kiến ​​trúc của tokenizer, v.v.) đến một repo nhất định. Tuy nhiên, đó không phải là câu trả lời đúng duy nhất!",
            correct: true
		},
		{
			text: "Một tệp cấu hình mô hình",
			explain: "Đúng vậy! Tất cả các tệp cấu hình mô hình đều có phương thức <code>push_to_hub</code> giúp đẩy chúng đến một repo. Bạn có thể chia sẻ điều gì khác nữa không?",
            correct: true
		},
		{
			text: "Một mô hình",
			explain: "Chính xác! Tất cả các mô hình đều có phương thức <code>push_to_hub</code> giúp đẩy mô hình và các tệp cấu hình đến một repo nhất định.Tuy nhiên, đó không phải là câu trả lời đúng duy nhất!",
            correct: true
		},
		{
			text: "Tất cả những điều trên với một callback đặc thù",
			explain: "Đúng vậy - <code>PushToHubCallback</code> sẽ thường xuyên gửi tất cả các đối tượng đó đến một repo trong quá trình huấn luyện.",
            correct: true
		}
	]}
/>
{/if}

### 6. Bước đầu tiên khi sử dụng phương thức `push_to_hub()` hoặc các công cụ CLI là gì?

<Question
  choices={[
    {
      text: "Đăng nhập trên trang web.",
      explain: "Điều này sẽ không giúp ích trên máy cá nhân của bạn.",
    },
    {
      text: "Chạy 'huggingface-cli login' trên terminal.",
      explain: "Đúng - điều này sẽ tải xuống và lưu vào bộ nhớ cache token cá nhân của bạn.",
      correct: true,
    },
    {
      text: "Chạy 'notebook_login()' trong notebook.",
      explain: "Đúng - điều này sẽ hiển thị một tiện ích con để bạn xác thực.",
      correct: true,
    },
  ]}
/>

### 7. Bạn đang sử dụng một mô hình và một tokenizer - làm cách nào bạn có thể tải chúng lên Hub?

<Question
  choices={[
    {
      text: "Bằng cách gọi phương thức push_to_hub trực tiếp trên mô hình và tokenizer.",
      explain: "Chính xác!",
      correct: true,
    },
    {
      text: "Trong thời gian chạy Python, bằng cách gói chúng trong tiện ích <code>huggingface_hub</code>.",
      explain:
        "Các mô hình và tokenizer đã hưởng lợi sẵn từ tiện ích <code>huggingface_hub</code>: không cần gói thêm!",
    },
    {
      text: "Bằng cách lưu chúng vào ổ đĩa và gọi lệnh <code>transformers-cli upload-model</code>",
      explain: "Lệnh <code>upload-model</code> không tồn tại.",
    },
  ]}
/>

### 8. Bạn có thể thực hiện các thao tác git nào với `Repository`?

<Question
  choices={[
    {
      text: "Commit (Cam kết)",
      explain:
        "Chính xác, phương thức <code>git_commit()</code> có sẵn cho điều đó.",
      correct: true,
    },
    {
      text: "Pull (Kéo lại)",
      explain: "Đó là mục đích của phương thức <code>git_pull()</code>.",
      correct: true,
    },
    {
      text: "Push (Đẩy lên)",
      explain: "Phương thức <code>git_push()</code> thực hiện điều này.",
      correct: true,
    },
    {
      text: "Merge (Gộp)",
      explain: "Không, thao tác đó sẽ không bao giờ có thể thực hiện được với API này.",
    },
  ]}
/>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter4/6.mdx" />

### Xây dựng các thẻ mô hình
https://huggingface.co/learn/course/vi/chapter4/4.md

# Xây dựng các thẻ mô hình

<CourseFloatingBanner
    chapter={4}
    classNames="absolute z-10 right-0 top-0"
/>

Thẻ mô hình là một tệp được cho là quan trọng như mô hình và tệp tokenizer trong kho lưu trữ mô hình. Đây là định nghĩa chủ đạo của mô hình, đảm bảo khả năng tái sử dụng của các thành viên trong cộng đồng và khả năng tái tạo kết quả, đồng thời cung cấp một nền tảng mà các thành viên khác có thể xây dựng các tác phẩm của họ.

Việc ghi lại quá trình huấn luyện và đánh giá giúp những người khác hiểu những gì mong đợi ở một mô hình - và cung cấp đầy đủ thông tin liên quan đến dữ liệu đã được sử dụng và quá trình tiền xử lý và hậu xử lý đã được thực hiện đảm bảo rằng các hạn chế, thành kiến ​​và bối cảnh trong đó mô hình đang và không hữu ích có thể được xác định và hiểu.

Vì vậy, tạo một thẻ mô hình xác định rõ ràng mô hình của bạn là một bước rất quan trọng. Ở đây, chúng tôi cung cấp một số mẹo sẽ giúp bạn điều này. Việc tạo thẻ mô hình được thực hiện thông qua tệp _README.md_ mà bạn đã thấy trước đó, đây là một tệp Markdown.

Khái niệm "thẻ mô hình" bắt nguồn từ một hướng nghiên cứu của Google, lần đầu tiên được chia sẻ trong bài báo ["Model Cards for Model Reporting"](https://arxiv.org/abs/1810.03993) của Margaret Mitchell và cộng sự. Nhiều thông tin ở đây dựa trên bài báo đó và chúng tôi khuyên bạn nên xem qua để hiểu tại sao thẻ mô hình lại quan trọng như vậy trong một thế giới coi trọng khả năng tái tạo, khả năng tái sử dụng và tính công bằng.

Thẻ mô hình thường bắt đầu với tổng quan rất ngắn gọn, cấp cao về mục đích của mô hình, tiếp theo là các chi tiết bổ sung trong các phần sau:

- Mô tả về mô hình
- Mục đích sử dụng & giới hạn
- Cách sử dụng
- Hạn chế và sai lệch
- Dữ liệu huấn luyện
- Quy trình huấn luyện
- Những kết quả đánh giá

Chúng ta hãy xem mỗi phần này nên chứa những gì.

### Mô tả về mô hình

Mô tả về mô hình cung cấp các chi tiết cơ bản về mô hình. Điều này bao gồm kiến ​​trúc, phiên bản, nếu nó được giới thiệu trong một bài báo, nếu có sẵn bản triển khai gốc, tác giả và thông tin chung về mô hình. Bất kỳ bản quyền nào cũng nên được ghi nhận ở đây. Thông tin chung về quy trình huấn luyện, các thông số và tuyên bố từ chối trách nhiệm quan trọng cũng có thể được đề cập trong phần này.

### Mục đích sử dụng & giới hạn

Ở đây, bạn mô tả các trường hợp sử dụng mà mô hình, bao gồm các ngôn ngữ, trường và mảng chuyên môn mà nó có thể được áp dụng. Phần này của thẻ mô hình cũng có thể ghi lại các khu vực được biết là nằm ngoài phạm vi của mô hình hoặc nơi nó có khả năng hoạt động dưới mức tối ưu.

### Cách sử dụng

Phần này nên bao gồm một số ví dụ về cách sử dụng mô hình. Điều này có thể giới thiệu cách sử dụng hàm  `pipeline()`, cách sử dụng mô hình và tokenizer, và bất kỳ đoạn mã nào khác mà bạn nghĩ có thể hữu ích.

### Dữ liệu huấn luyện

Phần này phải chỉ ra (các) tập dữ liệu nào mà mô hình đã được huấn luyện. Một mô tả ngắn gọn về (các) tập dữ liệu cũng được hoan nghênh.

### Quy trình huấn luyện

Trong phần này, bạn nên mô tả tất cả các khía cạnh liên quan của việc huấn luyện mà hữu ích từ góc độ khả năng tái tạo. Điều này bao gồm bất kỳ quá trình tiền xử lý và hậu xử lý nào đã được thực hiện trên dữ liệu, cũng như các chi tiết như số epoch mà mô hình được huấn luyện, kích thước lô, tốc độ học, v.v.

### Biến và chỉ số thước đo

Ở đây, bạn nên mô tả các số liệu bạn sử dụng để đánh giá, và các yếu tố khác nhau mà bạn đang đo lường. Đề cập đến (các) chỉ số nào đã được sử dụng, tập dữ liệu nào được sử dụng và tập dữ liệu được phân chia như thế nào, giúp dễ dàng so sánh hiệu suất của mô hình của bạn so với hiệu suất của các mô hình khác. Những điều này phải được thông báo bởi các phần trước, chẳng hạn như đối tượng người dùng và các trường hợp sử dụng.

### Những kết quả đánh giá

Cuối cùng, cung cấp chỉ báo về mức độ hoạt động của mô hình trên tập dữ liệu đánh giá. Nếu mô hình sử dụng ngưỡng quyết định, hãy cung cấp ngưỡng quyết định được sử dụng trong đánh giá hoặc cung cấp thông tin chi tiết về đánh giá ở các ngưỡng khác nhau phục vụ cho các mục đích sử dụng.

## Ví dụ

Hãy xem phần sau để biết một vài ví dụ về thẻ mô hình được chế tạo tốt:

- [`bert-base-cased`](https://huggingface.co/bert-base-cased)
- [`gpt2`](https://huggingface.co/gpt2)
- [`distilbert`](https://huggingface.co/distilbert-base-uncased)

Tham khảo thêm các ví dụ từ các tổ chức và công ty khác nhau [tại đây](https://github.com/huggingface/model_card/blob/master/examples.md).

## Lưu ý

Thẻ mô hình không phải là ràng buộc khi xuất bản mô hình và bạn không cần phải bao gồm tất cả các phần được mô tả ở trên khi tạo thẻ mô hình. Tuy nhiên, tài liệu rõ ràng về mô hình có thể mang lại lợi ích cho người dùng trong tương lai, vì vậy chúng tôi khuyên bạn nên điền vào nhiều phần nhất có thể theo khả năng và kiến ​​thức của mình.

## Siêu dữ liệu thẻ mô hình

Nếu bạn đã khám phá một chút về Hugging Face Hub, bạn sẽ thấy rằng một số kiểu mô hình thuộc một số nhóm nhất định: bạn có thể lọc chúng theo tác vụ, ngôn ngữ, thư viện, v.v. Các nhóm mà một mô hình thuộc về được xác định theo siêu dữ liệu bạn thêm vào tiêu đề thẻ mô hình.

Ví dụ: nếu bạn xem [thẻ mô hình camembert-base`](https://huggingface.co/camembert-base/blob/main/README.md), bạn sẽ thấy các dòng sau trong tiêu đề thẻ mô hình:

```
---
language: fr
license: mit
datasets:
- oscar
---
```

Siêu dữ liệu này được phân tích bởi Hugging Face Hub, sau đó xác định mô hình này là cho tiếng Pháp, có giấy phép MIT, được huấn luyện trên tập dữ liệu Oscar.

[Thông số kỹ thuật thẻ mô hình bản đầy đủ](https://github.com/huggingface/hub-docs/blame/main/modelcard.md) cho phép chỉ định ngôn ngữ, giấy phép, thẻ, bộ dữ liệu, số liệu cũng như kết quả đánh giá mô hình thu được khi huấn luyện.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter4/4.mdx" />

### Sử dụng các mô hình huấn luyện trước
https://huggingface.co/learn/course/vi/chapter4/2.md

# Sử dụng các mô hình huấn luyện trước

{#if fw === 'pt'}

<CourseFloatingBanner chapter={4}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {
      label: "Google Colab",
      value:
        "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter4/section2_pt.ipynb",
    },
    {
      label: "Aws Studio",
      value:
        "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter4/section2_pt.ipynb",
    },
  ]}
/>

{:else}

<CourseFloatingBanner chapter={4}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {
      label: "Google Colab",
      value:
        "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter4/section2_tf.ipynb",
    },
    {
      label: "Aws Studio",
      value:
        "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter4/section2_tf.ipynb",
    },
  ]}
/>

{/if}

Model Hub làm cho việc chọn mô hình thích hợp trở nên đơn giản, vì vậy việc sử dụng mô hình đó trong bất kỳ thư viện nào dưới đây có thể được thực hiện trong một vài dòng mã. Hãy cùng xem cách thực sự sử dụng một trong những mô hình này và cách đóng góp lại cho cộng đồng.

Giả sử chúng tôi đang tìm kiếm một mô hình cho tiếng Pháp có thể thực hiện tác vụ diền vào phần bị che đi.

<div class="flex justify-center">
  <img
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/camembert.gif"
    alt="Selecting the Camembert model."
    width="80%"
  />
</div>

Chúng tôi chọn checkpoint `camembert-base` để dùng thử. Từ `camembert-base` là tất cả những gì chúng ta cần để bắt đầu sử dụng nó! Như bạn đã thấy trong các chương trước, chúng ta có thể khởi tạo nó bằng cách sử dụng hàm `pipeline()`:

```py
from transformers import pipeline

camembert_fill_mask = pipeline("fill-mask", model="camembert-base")
results = camembert_fill_mask("Le camembert est <mask> :)")
```

```python out
[
  {'sequence': 'Le camembert est délicieux :)', 'score': 0.49091005325317383, 'token': 7200, 'token_str': 'délicieux'},
  {'sequence': 'Le camembert est excellent :)', 'score': 0.1055697426199913, 'token': 2183, 'token_str': 'excellent'},
  {'sequence': 'Le camembert est succulent :)', 'score': 0.03453313186764717, 'token': 26202, 'token_str': 'succulent'},
  {'sequence': 'Le camembert est meilleur :)', 'score': 0.0330314114689827, 'token': 528, 'token_str': 'meilleur'},
  {'sequence': 'Le camembert est parfait :)', 'score': 0.03007650189101696, 'token': 1654, 'token_str': 'parfait'}
]
```

Như bạn có thể thấy, việc tải một mô hình trong một pipeline cực kỳ đơn giản. Điều duy nhất bạn cần chú ý là checkpoint đã chọn có phù hợp với tác vụ mà nó sẽ được sử dụng hay không. Ví dụ: ở đây chúng tôi đang tải checkpoint `camembert-base` trong pipeline `fill-mask`, điều này hoàn toàn ổn. Nhưng nếu chúng tôi tải checkpoint này trong pipeline phân loại văn bản, kết quả sẽ không có ý nghĩa gì vì phần đầu của `camembert-base` không phù hợp với tác vụ này! Chúng tôi khuyên bạn nên sử dụng công cụ chọn tác vụ trong giao diện Hugging Face Hub để chọn các checkpoint thích hợp:

<div class="flex justify-center">
  <img
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/tasks.png"
    alt="The task selector on the web interface."
    width="80%"
  />
</div>

Bạn cũng có thể khởi tạo checkpoint bằng cách sử dụng kiến trúc mô hình trực tiếp:

{#if fw === 'pt'}

```py
from transformers import CamembertTokenizer, CamembertForMaskedLM

tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
model = CamembertForMaskedLM.from_pretrained("camembert-base")
```

Tuy nhiên, chúng tôi khuyên bạn nên sử dụng [`Auto*` classes](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes) vì đây là của kiến trúc thiết kế-bất khả tri. Trong khi đoạn mã trước đó giới hạn người dùng ở các checkpoint chỉ có thể tải được trong kiến trúc CamemBERT, việc sử dụng các lớp `Auto*` giúp việc chuyển đổi các checkpoint trở nên đơn giản:


```py
from transformers import AutoTokenizer, AutoModelForMaskedLM

tokenizer = AutoTokenizer.from_pretrained("camembert-base")
model = AutoModelForMaskedLM.from_pretrained("camembert-base")
```

{:else}

```py
from transformers import CamembertTokenizer, TFCamembertForMaskedLM

tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
model = TFCamembertForMaskedLM.from_pretrained("camembert-base")
```

Tuy nhiên, chúng tôi khuyên bạn nên sử dụng [`TFAuto*` classes](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes) vì đây là của kiến trúc thiết kế-bất khả tri. Trong khi đoạn mã trước đó giới hạn người dùng ở các checkpoint chỉ có thể tải được trong kiến trúc CamemBERT, việc sử dụng các lớp `TFAuto*` giúp việc chuyển đổi các checkpoint trở nên đơn giản:

```py
from transformers import AutoTokenizer, TFAutoModelForMaskedLM

tokenizer = AutoTokenizer.from_pretrained("camembert-base")
model = TFAutoModelForMaskedLM.from_pretrained("camembert-base")
```

{/if}

<Tip>

Khi sử dụng một mô hình được huấn luyện trước, hãy đảm bảo kiểm tra xem nó được huấn luyện như thế nào, dựa trên tập dữ liệu nào, các giới hạn và độ sai lệch của nó. Tất cả thông tin này phải được ghi trên thẻ mô hình của nó.

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter4/2.mdx" />

### Tỉnh chỉnh, thử xem!
https://huggingface.co/learn/course/vi/chapter3/5.md

# Tỉnh chỉnh, thử xem!

<CourseFloatingBanner
    chapter={3}
    classNames="absolute z-10 right-0 top-0"
/>

Trong hai chương đầu tiên, bạn đã học về các mô hình và tokenizer, và bây giờ bạn biết cách tinh chỉnh chúng cho dữ liệu của riêng bạn. Tóm lại, trong chương này bạn:

{#if fw === 'pt'}
* Đã tìm hiểu về tập dữ liệu trong [Hub](https://huggingface.co/datasets)
* Đã học cách tải và tiền xử lý bộ dữ liệu, bao gồm cả việc sử dụng đệm động và trình đối chiếu
* Thực hiện tinh chỉnh và đánh giá mô hình của riêng bạn
* Đã thực hiện một vòng huấn luyện cấp thấp hơn
* Được sử dụng 🤗 Accelerate để dễ dàng điều chỉnh vòng lặp huấn luyện của bạn để nó hoạt động với nhiều GPU hoặc TPU

{:else}
* Đã tìm hiểu về các bộ dữ liệu trong [Hub](https://huggingface.co/datasets)
* Đã học cách tải và tiền xử lý bộ dữ liệu
* Đã học cách tinh chỉnh và đánh giá một mô hình với Keras
* Đã triển khai các thước đo tùy chỉnh

{/if}


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter3/5.mdx" />

### Giới thiệu
https://huggingface.co/learn/course/vi/chapter3/1.md

# Giới thiệu

<CourseFloatingBanner
    chapter={3}
    classNames="absolute z-10 right-0 top-0"
/>

Trong [Chương 2](/course/chapter2), chúng ta đã khám phá cách sử dụng tokenizer và các mô hình huấn luyện trước để đưa ra dự đoán. Nhưng nếu bạn muốn tinh chỉnh một mô hình được huấn luyện trước cho tập dữ liệu của riêng mình thì sao? Đó là chủ đề của chương này! Bạn sẽ học:

{#if fw === 'pt'}
* Cách chuẩn bị một tập dữ liệu lớn từ Hub
* Cách sử dụng API `Trainer` cấp cao để tinh chỉnh mô hình
* Cách sử dụng vòng lặp huấn luyện tùy chỉnh
* Cách tận dụng thư viện 🤗 Accelerate để dễ dàng chạy vòng huấn luyện tùy chỉnh đó trên bất kỳ thiết lập phân tán nào

{:else}
* Cách chuẩn bị một tập dữ liệu lớn từ Hub
* Cách sử dụng Keras để tinh chỉnh mô hình
* Cách sử dụng Keras để đưa ra dự đoán
* Cách sử dụng thước đo tùy chỉnh

{/if}

Để tải các checkpoint được huấn luyện của bạn lên Hugging Face Hub, bạn sẽ cần có tài khoản huggingface.co: [tạo tài khoản](https://huggingface.co/join)


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter3/1.mdx" />

### Tinh chỉnh một mô hình với Trainer API
https://huggingface.co/learn/course/vi/chapter3/3.md

# Tinh chỉnh một mô hình với Trainer API

<CourseFloatingBanner chapter={3}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter3/section3.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter3/section3.ipynb"},
]} />

<Youtube id="nvBXf7s7vTI"/>

🤗 Transformers cung cấp lớp `Trainer` để giúp bạn tinh chỉnh bất kỳ mô hình huấn luyện trước nào mà nó cung cấp trên tập dữ liệu của bạn. Khi bạn đã hoàn thành tất cả công việc tiền xử lý dữ liệu trong phần cuối cùng, bạn chỉ còn một vài bước để định nghĩa `Trainer`. Phần khó nhất có thể là chuẩn bị môi trường để chạy `Trainer.train()`, vì nó sẽ chạy rất chậm trên CPU. Nếu bạn chưa thiết lập GPU, bạn có thể có quyền truy cập vào GPU hoặc TPU miễn phí trên [Google Colab](https://colab.research.google.com/).

Các ví dụ mã bên dưới giả sử bạn đã thực hiện các ví dụ trong phần trước. Dưới đây là một bản tóm tắt ngắn tóm tắt lại những gì bạn cần:

```py
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding

raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)


def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)


tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
```

### Huấn luyện

Bước đầu tiên trước khi chúng ta có thể định nghĩa `Trainer` của mình là định nghĩa một lớp `TrainingArguments` sẽ chứa tất cả các siêu tham số mà `Trainer` sẽ sử dụng để huấn luyện và đánh giá. Tham số duy nhất bạn phải cung cấp là một thư mục nơi mô hình được huấn luyện sẽ được lưu, cũng như các checkpoint đi kèm. Đối với tất cả phần còn lại, bạn có thể để mặc định, nó sẽ hoạt động khá tốt với tinh chỉnh cơ bản.

```py
from transformers import TrainingArguments

training_args = TrainingArguments("test-trainer")
```

<Tip>

💡 Nếu bạn muốn tự động tải mô hình của mình lên Hub trong quá trình huấn luyện, hãy chuyển sang phần `push_to_hub=True` trong phần `TrainingArguments`. Chúng ta sẽ tìm hiểu thêm về điều này trong [Chương 4](/course/chapter4/3)

</Tip>

Bước thứ hai là xác định mô hình của chúng ta. Như trong [chương trước](/course/chapter2), chúng ta sẽ sử dụng lớp `AutoModelForSequenceClassification`, với hai nhãn:

```py
from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
```

Bạn sẽ nhận thấy rằng không như trong [Chương 2](/course/chapter2), bạn nhận được một cảnh báo sau khi khởi tạo mô hình được huấn luyện trước này. Đây là do BERT chưa được huấn luyện trước về phân loại các cặp câu, vì vậy phần đầu của mô hình được huấn luyện trước đã bị loại bỏ và phần đầu mới phù hợp để phân loại chuỗi đã được chèn vào thay thế. Các cảnh báo chỉ ra rằng một số trọng số đã không được sử dụng (những trọng số tương ứng với đầu huấn luyện trước bị rụng) và một số trọng số khác khác được khởi tạo ngẫu nhiên (những trọng số dành cho đầu mới). Nó kết thúc bằng cách khuyến khích bạn huấn luyện mô hình, đó chính xác là những gì chúng ta sẽ làm bây giờ.

Khi chúng ta có mô hình của mình, chúng ta có thể xác định một `Trainer` bằng cách truyền vào tất cả các đối tượng được xây dựng từ trước đến nay - `model`, `training_args`, tập huấn luyện và kiểm định,`data_collator` và `tokenizer`:

```py
from transformers import Trainer

trainer = Trainer(
    model,
    training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
)
```

Lưu ý rằng khi bạn truyền `tokenizer` như chúng ta đã làm ở đây, mặc định `data_collator` được sử dụng bởi `Trainer` sẽ là `DataCollatorWithPadding` như đã định nghĩa trước đó, vì vậy bạn có thể bỏ qua dòng `data_collator = data_collator` trong lệnh gọi này. Điều quan trọng là phải cho bạn thấy phần này của quá trình trong phần 2!

Để tinh chỉnh mô hình trên tập dữ liệu, chúng ta chỉ cần gọi phương thức `train()` của `Trainer`:

```py
trainer.train()
```

Thao tác này sẽ bắt đầu quá trình tinh chỉnh (sẽ mất vài phút trên GPU) và báo cáo lỗi đào tạo sau mỗi 500 bước. Tuy nhiên, nó sẽ không cho bạn biết mô hình của bạn đang hoạt động tốt (hoặc tồi tệ như thế nào). Điều này là do:

1. Chúng ta đã không yêu cầu `Trainer` đánh giá trong quá trình huấn luyện bằng cách cài đặt `eval_strategy` thành `"steps"` (đánh giá mọi `eval_steps`) hoặc `"epoch"` (đánh giá vào cuối mỗi epoch).
2. Chúng ta đã không cung cấp cho `Trainer` một hàm `compute_metrics()` để tính toán chỉ số trong quá trình đánh giá nói trên (nếu không, đánh giá sẽ chỉ in ra lỗ, đây không phải là một chỉ số trực quan cho lắm).

### Đánh giá

Hãy xem cách chúng ta có thể xây dựng một hàm `compute_metrics()` hữu ích và sử dụng nó trong lần huấn luyện tiếp theo. Hàm phải nhận một đối tượng `EvalPrediction` (là một tuple được đặt tên với trường `predictions` và trường `label_ids`) và sẽ trả về một chuỗi ánh xạ từ thành số thực (các chuỗi là tên của các chỉ số được trả về và các giá trị của chúng ép về kiểu số thực). Để nhận được dự đoán từ mô hình, chúng ta có thể sử dụng lệnh `Trainer.predict()`:

```py
predictions = trainer.predict(tokenized_datasets["validation"])
print(predictions.predictions.shape, predictions.label_ids.shape)
```

```python out
(408, 2) (408,)
```
Đầu ra của phương thức `predict()` là một tuple có tên khác với ba trường: `predictions`, `label_ids`, và `metrics`. Trường `metrics` sẽ chỉ chứa sự mất mát trên tập dữ liệu đã truyền vào, cũng như một số chỉ số thời gian (tổng cộng và trung bình mất bao lâu để dự đoán). Sau khi chúng ta hoàn thành hàm `compute_metrics()` và truyền nó vào `Trainer`, trường đó cũng sẽ chứa các chỉ số được trả về bởi` compute_metrics()`.

Như bạn có thể thấy, `predictions` là một mảng hai chiều có hình dạng 408 x 2 (408 là số phần tử trong tập dữ liệu ta đã sử dụng). Đó là các logit cho từng phần tử của tập dữ liệu mà chúng ta đã truyền vào cho`predict()` ( như bạn đã thấy trong [chương trước](/course/chapter2), tất cả các mô hình Transformer đều trả về logit). Để chuyển đổi chúng thành các dự đoán mà chúng ta có thể so sánh với các nhãn của mình, chúng ta cần lấy chỉ số có giá trị lớn nhất trên trục thứ hai:

```py
import numpy as np

preds = np.argmax(predictions.predictions, axis=-1)
```

Giờ chúng ta có thể so sánh các `preds` đó với các nhãn. Để xây dựng hàm `compute_metric()`, chúng ta sẽ dựa vào các chỉ số từ thư viện 🤗 [Đánh giá](https://github.com/huggingface/evaluate/). Chúng ta có thể tải các chỉ số được liên kết với tập dữ liệu MRPC dễ dàng như khi chúng ta tải tập dữ liệu, lần này là với hàm `evaluate.load()`. Đối tượng được trả về có phương thức `compute()` mà chúng ta có thể sử dụng để thực hiện tính toán số liệu:

```py
import evaluate

metric = evaluate.load("glue", "mrpc")
metric.compute(predictions=preds, references=predictions.label_ids)
```

```python out
{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
```

Kết quả chính xác bạn nhận được có thể khác nhau, vì việc khởi tạo ngẫu nhiên phần đầu mô hình có thể thay đổi các chỉ số mà nó đạt được. Ở đây, chúng ta có thể thấy mô hình có độ chính xác 85.78% trên tập kiểm định và điểm F1 là 89.97. Đó là hai chỉ số được sử dụng để đánh giá kết quả trên tập dữ liệu MRPC theo điểm chuẩn GLUE. Bảng trong [bài báo BERT](https://arxiv.org/pdf/1810.04805.pdf) báo cáo điểm F1 là 88.9 cho mô hình cơ sở. Đó là mô hình `không phân biệt` viết hoa viết thường trong khi chúng ta hiện đang sử dụng mô hình `có phân biệt`, điều này giải thích kết quả tốt hơn.

Kết hợp mọi thứ lại với nhau, chúng ta nhận được hàm `compute_metrics()`:

```py
def compute_metrics(eval_preds):
    metric = evaluate.load("glue", "mrpc")
    logits, labels = eval_preds
    predictions = np.argmax(logits, axis=-1)
    return metric.compute(predictions=predictions, references=labels)
```

Và để xem nó được sử dụng trong thực tiễn để báo cáo các chỉ số ở cuối mỗi epoch như thế nào, đây là cách chúng tôi định nghĩa một `Trainer` mới với hàm `compute_metrics()` này:


```py
training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch")
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

trainer = Trainer(
    model,
    training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics,
)
```

Lưu ý rằng chúng ta tạo một `TrainingArguments` mới với `eval_strategy` của nó được đặt thành `"epoch"` và một mô hình mới - nếu không, chúng ta sẽ tiếp tục huấn luyện mô hình ta đã huấn luyện. Để khởi chạy một đợt huấn luyện mới, chúng ta thực hiện:

```
trainer.train()
```

Lần này, nó sẽ báo cáo thông số mất mát kiểm định và chỉ số ở cuối mỗi epoch ên cạnh thông số mất mát trên tập huấn luyện. Một lần nữa, độ chính xác tuyệt đối/điểm F1 mà bạn đạt được có thể hơi khác so với những gì chúng tôi tìm thấy, do việc khởi tạo đầu ngẫu nhiên của mô hình, nhưng nó phải ở trong cùng một khoảng.

`Trainer` sẽ hoạt động hiệu quả trên nhiều GPU hoặc TPU và cung cấp nhiều tùy chọn, chẳng hạn như huấn luyện về độ chính xác hỗn hợp (sử dụng `fp16=True` trong tham số huấn luyện của bạn). Chúng ta sẽ xem xét mọi thứ mà nó hỗ trợ trong Chương 10.

Phần này kết thúc phần giới thiệu về cách tinh chỉnh bằng API `Trainer`. Một ví dụ về việc thực hiện điều này đối với hầu hết các tác vụ NLP phổ biến sẽ được đưa ra trong [Chương 7](/course/chapter7), nhưng ở thời điểm này chúng ta hãy xem cách thực hiện điều tương tự trong PyTorch thuần túy.

<Tip>

✏️ **Thử nghiệm thôi!** Tinh chỉnh mô hình trên tập dữ liệu GLUE SST-2, sử dụng quá trình xử lý dữ liệu bạn đã thực hiện trong phần 2.

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter3/3.mdx" />

### Đố vui cuối chương
https://huggingface.co/learn/course/vi/chapter3/6.md

# Đố vui cuối chương

<CourseFloatingBanner
    chapter={3}
    classNames="absolute z-10 right-0 top-0"
/>

Kiểm tra những gì bạn đã học trong chương này!

### 1. Tập dữ liệu `emotion` chứa các tin nhắn Twitter được gắn nhãn cảm xúc. Tìm kiếm nó trong [Hub](https://huggingface.co/datasets) và đọc thẻ tập dữ liệu. Cảm xúc nào trong số này không phải là một trong những cảm xúc cơ bản của nó?

<Question
	choices={[
		{
			text: "Joy (Vui vẻ)",
			explain: "Hãy thử lại - cảm xúc này có trong tập dữ liệu đó!"
		},
		{
			text: "Love (Yêu thích)",
			explain: "Hãy thử lại - cảm xúc này có trong tập dữ liệu đó!"
		},
		{
			text: "Confusion (Bối rối)",
			explain: "Chính xác! Bối rối không phải là một trong sáu cảm xúc cơ bản.",
            correct: true
		},
        {
			text: "Surprise (Ngạc nhiên)",
			explain: "Ngạc nhiên chưa! Thử cái khác xem!"
		}
	]}
/>

### 2. Tìm kiếm tập dữ liệu `ar_sarcasm` trong [Hub](https://huggingface.co/datasets). Nó hỗ trợ tác vụ nào?

<Question
	choices={[
		{
			text: "Phân loại cảm xúc",
			explain: "Đúng rồi! Bạn có thể nhận ra nhờ tên thẻ.",
            correct: true
		},
		{
			text: "Dịch máy",
			explain: "Không phải rồi - thử xem lại tại  <a href='https://huggingface.co/datasets/ar_sarcasm'>thẻ dữ liệu</a> nha!"
		},
		{
			text: "Nhận dạng thực thể",
			explain: "Không phải rồi - thử xem lại tại  <a href='https://huggingface.co/datasets/ar_sarcasm'>thẻ dữ liệu</a> nha!"
		},
        {
			text: "Hỏi đáp",
			explain: "Tiếc ghê, không chính xác rồi. Thử lại nha!"
		}
	]}
/>

### 3. Mô hình BERT mong đợi một cặp câu được xử lý như thế nào?

<Question
	choices={[
		{
			text: "Tokens_of_sentence_1 [SEP] Tokens_of_sentence_2",
			explain: "Cần token đặc biệt <code>[SEP]</code> để phân cách hai câu, nhưng đây không phải thứ duy nhất!"
		},
		{
			text: "[CLS] Tokens_of_sentence_1 Tokens_of_sentence_2",
			explain: "Cần token đặc biệt <code>[CLS]</code> ở đầu, nhưng đây không phải thứ duy nhất!"
		},
		{
			text: "[CLS] Tokens_of_sentence_1 [SEP] Tokens_of_sentence_2 [SEP]",
			explain: "Chính xác!",
            correct: true
		},
        {
			text: "[CLS] Tokens_of_sentence_1 [SEP] Tokens_of_sentence_2",
			explain: "Cần token đặc biệt <code>[CLS]</code> ở đầu cũng như <code>[SEP]</code> để phân cách hai câu, nhưng đây không phải thứ duy nhất!"
		}
	]}
/>

{#if fw === 'pt'}
### 4. Lợi ích của phương thức `Dataset.map()` là gì?

<Question
	choices={[
		{
			text: "Kết quả của hàm được lưu vào bộ nhớ đệm, vì vậy sẽ không mất thời gian nếu chúng tôi thực thi lại mã.",
			explain: "Đó thực sự là một trong những lợi ích của phương pháp này! Tuy nhiên, nó không phải là lợi ích duy nhất...",
            correct: true
		},
		{
			text: "Nó có thể áp dụng xử lý đa luồng để thực thi nhanh hơn so với việc áp dụng hàm trên từng phần tử của tập dữ liệu.",
			explain: "Đây là một tính năng gọn gàng của phương pháp này, nhưng nó không phải là lợi ích duy nhất!",
            correct: true
		},
		{
			text: "Nó không tải toàn bộ tập dữ liệu vào bộ nhớ, lưu kết quả ngay sau khi một phần tử được xử lý.",
			explain: "Đó là một lợi thế của phương pháp này. Tuy nhiên, vẫn có những lợi ích khác!",
            correct: true
		},
	]}
/>

### 5. Đệm động nghĩa là sao?

<Question
	choices={[
		{
			text: "Đó là khi bạn đệm các đầu vào cho mỗi lô đến độ dài tối đa trong toàn bộ tập dữ liệu.",
			explain: "Nó ngụ ý đệm khi tạo lô, nhưng không có độ dài tối đa trong toàn bộ tập dữ liệu."
		},
		{
			text: "Đó là khi bạn đệm đầu vào của mình khi tạo lô, đến độ dài tối đa của các câu bên trong lô đó.",
			explain: "Đúng vậy! Phần \"động\" xuất phát từ thực tế là kích thước của mỗi lô được xác định tại thời điểm tạo ra và kết quả là tất cả các lô của bạn có thể có các hình dạng khác nhau.",
            correct: true
		},
		{
			text: "Đó là khi bạn đệm đầu vào của mình để mỗi câu có cùng số lượng token như câu trước trong tập dữ liệu.",
			explain: "Điều đó không chính xác, cộng với việc xem xét thứ tự trong tập dữ liệu không có ý nghĩa gì vì chúng tôi xáo trộn nó trong quá trình huấn luyện."
		},
	]}
/>

### 6. Mục đích của hàm đối chiếu là gì?

<Question
	choices={[
		{
			text: "Nó đảm bảo tất cả các chuỗi trong tập dữ liệu có cùng độ dài.",
			explain: "Một hàm đối chiếu có liên quan đến việc xử lý các lô riêng lẻ, không phải toàn bộ tập dữ liệu. Ngoài ra, chúng ta đang nói về các hàm đối chiếu chung, không cụ thể là <code>DataCollatorWithPadding</code>."
		},
		{
			text: "Nó tập hợp tất cả các mẫu lại trong một lô.",
			explain: "Đúng! Bạn có thể truyền hàm đối chiếu như một tham số của <code>DataLoader</code>. Chúng tôi đã sử dụng hàm <code>DataCollatorWithPadding</code>, một hàm đệm tất cả các mục trong một lô để chúng giống nhau về chiều dài.",
            correct: true
		},
		{
			text: "Nó tiền xử lý toàn bộ tập dữ liệu.",
			explain: "Đó là một hàm tiền xử lý, không phải là một hàm đối chiếu."
		},
        {
			text: "Nó cắt bớt các chuỗi trong tập dữ liệu.",
			explain: "Một hàm đối chiếu liên quan đến việc xử lý các lô riêng lẻ, không phải toàn bộ tập dữ liệu. Nếu bạn muốn cắt ngắn, bạn có thể sử dụng tham số <code>truncate</code> của <code>tokenizer</code>."
		}
	]}
/>

### 7. Điều gì xảy ra khi bạn khởi tạo một trong các lớp `AutoModelForXxx` với một mô hình ngôn ngữ huấn luyện trước( ví dụ như `bert-base-uncased`) mà liên quan tới một tác vụ khác hơn là tác vụ mà nó được huấn luyện sẵn?

<Question
	choices={[
		{
			text: "Nothing, but you get a warning.",
			explain: "You do get a warning, but that's not all!"
		},
		{
			text: "Phần đầu của mô hình được huấn luyện trước bị loại bỏ và phần đầu mới phù hợp với tác vụ sẽ được chèn vào thay thế.",
			explain: "Chính xác. Ví dụ, Khi ta sử dụng <code>AutoModelForSequenceClassification</code> với <code>bert-base-uncased</code>, ta nhận được cảnh báo khi khởi tạo mô hình. Phần đầu được huấn luyện trước không được sử dụng cho chuỗi tác vụ phân loại, vì vậy nó bị loại bỏ và một phần đầu mới được khởi tạo với các trọng số ngẫu nhiên.",
            correct: true
		},
		{
			text: "Phần đầu của mô hình được huấn luyện trước bị loại bỏ.",
			explain: "Một điều gì đó khác cần phải xảy ra. Hãy thử lại!"
		},
        {
			text: "Không có gì, vì mô hình vẫn có thể được tinh chỉnh cho các tác vụ khác.",
			explain: "Phần đầu của mô hình được huấn luyện trước không được huấn luyện để giải quyết tác vụ này, vì vậy chúng ta nên loại bỏ phần đầu!"
		}
	]}
/>

### 8. Mục đích của `TrainingArguments` là gì?

<Question
	choices={[
		{
			text: "Nó chứa tất cả các siêu tham số được sử dụng để huấn luyện và đánh giá với <code>Trainer</code>.",
			explain: "Chính xác!",
            correct: true
		},
		{
			text: "Nó chỉ định kích thước của mô hình.",
			explain: "Kích thước mô hình được xác định bởi cấu hình mô hình, không phải lớp <code>TrainingArguments</code>."
		},
		{
			text: "Nó chỉ chứa các siêu tham số được sử dụng để đánh giá.",
			explain: "Trong ví dụ này, chúng tôi đã chỉ định nơi mô hình và các checkpoint của nó sẽ được lưu. Hãy thử lại!"
		},
        {
			text: "Nó chỉ chứa các siêu tham số được sử dụng để huấn luyện.",
			explain: "Trong ví dụ này, chúng tôi cũng đã sử dụng <code>evaluation_strategy</code>, vì vậy điều này ảnh hưởng đến việc đánh giá mô hình. Hãy thử lại!"
		}
	]}
/>

### 9. Vì sao bạn nên sử dụng thư viện 🤗 Accelerate?

<Question
	choices={[
		{
			text: "Nó cung cấp quyền truy cập vào các mô hình nhanh hơn.",
			explain: "Không, thư viện 🤗 Accelerate không cung cấp bất kỳ mô hình nào."
		},
		{
			text: "INó cung cấp một API cấp cao nên tôi không phải triển khai vòng lặp huấn luyệncủa riêng mình.",
			explain: "Đây là những gì ta đã làm với <code>Trainer</code>, không phải với thư viện 🤗 Accelerate. Hãy thử lại!"
		},
		{
			text: "Nó làm cho các vòng huấn luyện hoạt động dựa trên các chiến lược phân tán",
			explain: "Đúng! Với 🤗 Accelerate, các vòng huấn luyện của bạn sẽ hoạt động cho nhiều GPU và TPU.",
            correct: true
		},
        {
			text: "Nó cung cấp nhiều hàm tối ưu hơn.",
			explain: "Không, thư viện 🤗 Accelerate không cung cấp bất kỳ hàm tối ưu nào."
		}
	]}
/>

{:else}
### 4. Điều gì xảy ra khi bạn khởi tạo một trong các lớp `TFAutoModelForXxx` với một mô hình ngôn ngữ huấn luyện trước( ví dụ như `bert-base-uncased`) mà liên quan tới một tác vụ khác hơn là tác vụ mà nó được huấn luyện sẵn?

<Question
	choices={[
		{
			text: "Không có gì, nhưng bạn nhận được một cảnh báo.",
			explain: "Bạn nhận được một cảnh báo, nhưng đó không phải là tất cả!"
		},
		{
			text: "Phần đầu của mô hình được huấn luyện trước bị loại bỏ và phần đầu mới phù hợp với tác vụ sẽ được chèn vào thay thế.",
			explain: "Chính xác. Ví dụ, Khi ta sử dụng <code>TFAutoModelForSequenceClassification</code> với <code>bert-base-uncased</code>, ta nhận được cảnh báo khi khởi tạo mô hình. Phần đầu được huấn luyện trước không được sử dụng cho chuỗi tác vụ phân loại, vì vậy nó bị loại bỏ và một phần đầu mới được khởi tạo với các trọng số ngẫu nhiên.",
            correct: true
		},
		{
			text: "Phần đầu của mô hình được huấn luyện trước bị loại bỏ.",
			explain: "Một điều gì đó khác cần phải xảy ra. Hãy thử lại!"
		},
        {
			text: "Không có gì, vì mô hình vẫn có thể được tinh chỉnh cho các tác vụ khác.",
			explain: "Phần đầu của mô hình được huấn luyện trước không được huấn luyện để giải quyết tác vụ này, vì vậy chúng ta nên loại bỏ phần đầu!"
		}
	]}
/>

### 5. Các mô hình TensorFlow từ `transformers` vốn đã là các mô hình Keras. Lợi ích của việc này là gì?

<Question
	choices={[
		{
			text: "Các mô hình hoạt động trên TPU khác với thường lệ.",
			explain: "Gần như vậy! Cần có một số thay đổi bổ sung nhỏ. Ví dụ: bạn cần chạy mọi thứ trong phạm vi <<code>TPUStrategy</code> , bao gồm cả việc khởi tạo mô hình."
		},
		{
			text: "Bạn có thể tận dụng các phương thức hiện có như <code>compile()</code>, <code>fit()</code>, và <code>predict()</code>.",
			explain: "Đúng! Sau khi bạn có dữ liệu, việc đào tạo về dữ liệu đó cần rất ít công việc.",
            correct: true
		},
		{
			text: "Bạn có thể học Keras cũng như transformers.",
			explain: "Đúng, nhưng chúng tôi đang tìm kiếm thứ khác :)",
			correct: true
		},
        {
			text: "Bạn có thể dễ dàng tính toán các chỉ số liên quan đến tập dữ liệu.",
			explain: "Keras giúp chúng ta huấn luyện và đánh giá mô hình, không phải tính toán các số liệu liên quan đến tập dữ liệu."
		}
	]}
/>

### 6. Làm thế nào bạn có thể định nghĩa thước đo tuỳ chỉnh của riêng bạn?

<Question
	choices={[
		{
			text: "Thông qua lớp con <code>tf.keras.metrics.Metric</code>.",
			explain: "Tuyệt vời!",
			correct: true
		},
		{
			text: "Sử dụng API chức năng của Keras.",
			explain: "Thử lại!"
		},
		{
			text: "Thông qua sử dụng <code>metric_fn(y_true, y_pred)</code>.",
			explain: "Chính xác!",
			correct: true
		},
        {
			text: "Sử dụng Googling.",
			explain: "Đó không phải là câu trả lời mà chúng tôi đang tìm kiếm, nhưng nó sẽ giúp bạn tìm thấy nó.",
			correct: true
		}
	]}
/>

{/if}


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter3/6.mdx" />

### Tinh chỉnh một mô hình với Keras
https://huggingface.co/learn/course/vi/chapter3/3_tf.md

# Tinh chỉnh một mô hình với Keras

<CourseFloatingBanner chapter={3}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter3/section3_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter3/section3_tf.ipynb"},
]} />

Khi bạn đã hoàn thành tất cả công việc tiền xử lý dữ liệu trong phần trước, bạn chỉ còn một vài bước nữa để huấn luyện mô hình. Tuy nhiên, lưu ý rằng lệnh `model.fit()` sẽ chạy rất chậm trên CPU. Nếu bạn chưa thiết lập GPU, bạn có thể có quyền truy cập vào GPU hoặc TPU miễn phí trên [Google Colab](https://colab.research.google.com/).

Các đoạn mã ví dụ bên dưới giả sử bạn đã thực thi các ví dụ trong phần trước. Dưới đây là một bản tóm tắt ngắn gọn tóm tắt lại những gì bạn cần:

```py
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding
import numpy as np

raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)


def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)


tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)

data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")

tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "token_type_ids"],
    label_cols=["labels"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=8,
)

tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "token_type_ids"],
    label_cols=["labels"],
    shuffle=False,
    collate_fn=data_collator,
    batch_size=8,
)
```

### Huấn luyện

Các mô hình TensorFlow nhập từ 🤗 Transformers vốn là các mô hình Keras. Đây là phần giới thiệu ngắn về Keras.

<Youtube id="rnTGBy2ax1c"/>

Điều đó có nghĩa là một khi chúng tôi có dữ liệu riêng mình, chúng ta chỉ cần thao tác ít bước nữa thôi để bắt đầu huấn luyện.

<Youtube id="AUozVp78dhk"/>

Như trong [chương trước](/course/chapter2), chúng ta sẽ sử dụng lớp `TFAutoModelForSequenceClassification`, với hai nhãn:

```py
from transformers import TFAutoModelForSequenceClassification

model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
```

Bạn sẽ nhận thấy rằng không như trong [Chương 2](/course/chapter2), bạn nhận được một cảnh báo sau khi khởi tạo mô hình được huấn luyện trước này. Đây là do BERT chưa được huấn luyện trước về phân loại các cặp câu, vì vậy phần đầu của mô hình được huấn luyện trước đã bị loại bỏ và phần đầu mới phù hợp để phân loại chuỗi đã được chèn vào thay thế. Các cảnh báo chỉ ra rằng một số trọng số đã không được sử dụng (những trọng số tương ứng với đầu huấn luyện trước bị rụng) và một số trọng số khác khác được khởi tạo ngẫu nhiên (những trọng số dành cho đầu mới). Nó kết thúc bằng cách khuyến khích bạn huấn luyện mô hình, đó chính xác là những gì chúng ta sẽ làm bây giờ.

Để tinh chỉnh mô hình trên tập dữ liệu của mình, chúng ta chỉ cần `compile()` mô hình và sau đó chuyển dữ liệu của ta đến phương thức `fit()`. Thao tác này sẽ bắt đầu quá trình tinh chỉnh (sẽ mất vài phút trên GPU) và báo cáo sự mất mát ở tập huấn luyện khi nó diễn ra, cộng với mất mát ở tập kiểm định ở cuối mỗi epoch.

<Tip>

Lưu ý rằng 🤗 các mô hình Transformers có một khả năng đặc biệt mà hầu hết các mô hình Keras không có - chúng có thể tự động sử dụng một lượng mất mát thích hợp mà chúng tính toán bên trong. Chúng sẽ sử dụng sự mất mát này theo mặc định nếu bạn không đặt tham số mất mát bên trong `compile()`. Lưu ý rằng để sử dụng hàm mất mát trong nội bộ, bạn sẽ cần truyền các nhãn của mình như một phần của đầu vào, không phải dưới dạng nhãn riêng biệt, đây là cách thông thường để sử dụng nhãn với các mô hình Keras. Bạn sẽ thấy các ví dụ về điều này trong Phần 2 của khóa học, trong đó việc xác định hàm mất mát chính xác có thể khó khăn. Tuy nhiên, đối với phân loại chuỗi, một hàm mất mát Keras tiêu chuẩn hoạt động khá tốt, vì vậy đó là những gì chúng ta sẽ sử dụng ở đây.

</Tip>

```py
from tensorflow.keras.losses import SparseCategoricalCrossentropy

model.compile(
    optimizer="adam",
    loss=SparseCategoricalCrossentropy(from_logits=True),
    metrics=["accuracy"],
)
model.fit(
    tf_train_dataset,
    validation_data=tf_validation_dataset,
)
```

<Tip warning={true}>

Lưu ý một lỗi rất phổ biến ở đây - bạn *có thể* chỉ cần truyền tên của hàm mất mát dưới dạng chuỗi cho Keras, nhưng theo mặc định, Keras sẽ cho rằng bạn đã áp dụng softmax cho đầu ra của mình. Tuy nhiên, nhiều mô hình xuất ra các giá trị ngay trước khi áp dụng softmax, còn được gọi là *logit*. Chúng ta cần nói với hàm mất mát rằng đó là những gì mô hình của chúng ta làm và cách duy nhất để làm điều đó là gọi nó trực tiếp, thay vì đặt tên bằng một chuỗi.

</Tip>

### Cải thiện hiệu suất huấn luyện

<Youtube id="cpzq6ESSM5c"/>

Nếu bạn thử đoạn mã trên, nó chắc chắn chạy, nhưng bạn sẽ thấy rằng hàm mất mát chỉ giảm từ từ hoặc không thường xuyên. Nguyên nhân chính là do *learning rate* hay *tốc độ học*. Với hàm mất mát, khi ta truyền cho Keras tên của trình tối ưu hóa dưới dạng một chuỗi, Keras sẽ khởi tạo trình tối ưu hóa đó với các giá trị mặc định cho tất cả các tham số, bao gồm cả tốc độ học. Tuy nhiên, từ kinh nghiệm lâu năm, chúng tôi biết
rằng các mô hình Transformer được hưởng lợi từ tốc độ học thấp hơn nhiều so với tỷ lệ mặc định cho Adam, là 1e-3, cũng được viết bằng 10 lũy thừa của -3, hoặc 0,001. 5e-5 (0,00005), thấp hơn khoảng hai mươi lần, là một điểm khởi đầu tốt hơn nhiều.

Ngoài việc giảm tốc độ học, chúng tôi có một mẹo thứ hai: Ta có thể từ từ giảm tốc độ học trong quá trình huấn luyện. Trong tài liệu, đôi khi bạn sẽ thấy điều này được gọi là *phân rã* hoặc *ủ* tốc độ học. Ở Keras, cách tốt nhất để làm điều này là sử dụng *learning rate scheduler* hay *công cụ lập lịch trình tốc độ học*. Một cái hay để sử dụng là `PolynomialDecay` - với cài đặt mặc định, nó chỉ đơn giản là giảm độ tuyến tính tốc độ học từ giá trị ban đầu đến giá trị cuối cùng trong quá trình huấn luyện, đó chính xác là những gì ta muốn. Tuy nhiên, để sử dụng bộ lập lịch một cách chính xác, chúng ta cần cho nó biết thời gian huấn luyện sẽ kéo dài. Chúng ta tính giá trị đó dưới dạng `num_train_steps` như sau.

```py
from tensorflow.keras.optimizers.schedules import PolynomialDecay

batch_size = 8
num_epochs = 3
# Số bước huấn luyện là số lượng mẫu trong tập dữ liệu, chia cho kích thước lô sau đó nhân
# với tổng số epoch. Lưu ý rằng tf_train_dataset ở đây là tf.data.Dataset theo lô,
# không phải là Hugging Face Dataset, vì vậy len() của nó đã là num_samples // batch_size.
num_train_steps = len(tf_train_dataset) * num_epochs
lr_scheduler = PolynomialDecay(
    initial_learning_rate=5e-5, end_learning_rate=0.0, decay_steps=num_train_steps
)
from tensorflow.keras.optimizers import Adam

opt = Adam(learning_rate=lr_scheduler)
```

<Tip>

Thư viện 🤗 Transformers cũng có một hàm `create_optimizer()` sẽ tạo ra một trình tối ưu hóa `AdamW` với sự giảm tốc độ học. Đây là một phím tắt thuận tiện mà bạn sẽ thấy chi tiết trong các phần sau của khóa học.

</Tip>

Bây giờ chúng ta đã có trình tối ưu hóa hoàn toàn mới và ta có thể thử huấn luyện với nó. Đầu tiên, hãy tải lại mô hình, để đặt lại các thay đổi đối với trọng số từ lần chạy huấn luyện mà chúng ta vừa thực hiện và sau đó ta có thể biên dịch nó bằng trình tối ưu hóa mới:

```py
import tensorflow as tf

model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
model.compile(optimizer=opt, loss=loss, metrics=["accuracy"])
```

Giờ ta sẽ fit lại 1 lần nữa:

```py
model.fit(tf_train_dataset, validation_data=tf_validation_dataset, epochs=3)
```

<Tip>

💡 Nếu bạn muốn tự động tải mô hình của mình lên Hub trong quá trình huấn luyện, bạn có thể truyền `PushToHubCallback` vào trong phương thức `model.fit()`. Chúng ta sẽ tìm hiểu thêm về điều này trong [Chương 4](/course/chapter4/3)

</Tip>

### Các dự đoán của mô hình

<Youtube id="nx10eh4CoOs"/>

Việc huấn luyện và theo dõi sự mất mát giảm xuống đều rất tốt, nhưng nếu chúng ta muốn thực sự có được kết quả đầu ra từ mô hình được huấn luyện, để tính toán một số chỉ số hoặc sử dụng mô hình đó trong sản xuất thì sao? Để làm điều đó, chúng ta chỉ có thể sử dụng phương thức `predict()`. Điều này sẽ trả về *logit* từ đầu ra của mô hình, một cho mỗi lớp.

```py
preds = model.predict(tf_validation_dataset)["logits"]
```

Chúng ta có thể chuyển đổi các logit này thành các dự đoán lớp của mô hình bằng cách sử dụng `argmax` để tìm logit cao nhất, tương ứng với lớp có nhiều khả năng nhất:

```py
class_preds = np.argmax(preds, axis=1)
print(preds.shape, class_preds.shape)
```

```python out
(408, 2) (408,)
```

Bây giờ, hãy sử dụng các `preds` đó để tính toán một số chỉ số! Chúng ta có thể tải các chỉ số được liên kết với tập dữ liệu MRPC dễ dàng như khi ta tải tập dữ liệu, lần này là với hàm `eval.load())`. Đối tượng được trả về có phương thức `compute()` mà chúng ta có thể sử dụng để thực hiện phép tính số liệu:

```py
import evaluate

metric = evaluate.load("glue", "mrpc")
metric.compute(predictions=class_preds, references=raw_datasets["validation"]["label"])
```

```python out
{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
```

Kết quả chính xác bạn nhận được có thể khác nhau, vì việc khởi tạo ngẫu nhiên phần đầu mô hình có thể thay đổi các chỉ số mà nó đạt được. Ở đây, chúng ta có thể thấy mô hình có độ chính xác 85.78% trên tập kiểm định và điểm F1 là 89.97. Đó là hai chỉ số được sử dụng để đánh giá kết quả trên tập dữ liệu MRPC theo điểm chuẩn GLUE. Bảng trong [bài báo BERT](https://arxiv.org/pdf/1810.04805.pdf) báo cáo điểm F1 là 88.9 cho mô hình cơ sở. Đó là mô hình `không phân biệt` viết hoa viết thường trong khi chúng ta hiện đang sử dụng mô hình `có phân biệt`, điều này giải thích kết quả tốt hơn.

Phần này kết thúc phần giới thiệu về cách tinh chỉnh bằng Keras API. Một ví dụ về cách làm này đối với hầu hết các tác vụ NLP phổ biến sẽ được đưa ra trong [Chương 7](/course/chapter7). Nếu bạn muốn trau dồi kỹ năng của mình trên API Keras, hãy cố gắng tinh chỉnh một mô hình trên tập dữ liệu GLUE SST-2, bằng cách sử dụng xử lý dữ liệu bạn đã thực hiện trong phần 2.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter3/3_tf.mdx" />

### Bản huấn luyện hoàn chỉnh
https://huggingface.co/learn/course/vi/chapter3/4.md

# Bản huấn luyện hoàn chỉnh

<CourseFloatingBanner chapter={3}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter3/section4.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter3/section4.ipynb"},
]} />

<Youtube id="Dh9CL8fyG80"/>

Bây giờ chúng ta sẽ xem cách đạt được kết quả tương tự như chúng ta đã làm trong phần trước mà không cần sử dụng lớp `Trainer`. Một lần nữa, chúng tôi giả sử bạn đã thực hiện bước xử lý dữ liệu trong phần 2. Dưới đây là một bản tóm tắt ngắn bao gồm mọi thứ bạn cần:

```py
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding

raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)


def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)


tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
```

### Chuẩn bị cho huấn luyện

Trước khi thực sự viết vòng lặp huấn luyện của mình, chúng ta sẽ cần xác định một vài đối tượng. Đầu tiên là bộ dữ liệu dataloader mà chúng tôi sẽ sử dụng để lặp qua các lô. Nhưng trước khi chúng ta có thể xác định các bộ dữ liệu đó, chúng ta cần áp dụng một chút hậu xử lý cho `tokenized_datasets` của mình, để xử lý một số thứ mà `Trainer` đã làm cho chúng ta một cách tự động. Cụ thể, chúng ta cần:

- Loại bỏ các cột tương ứng với các giá trị mà mô hình không mong đợi (như cột `sentence1` và `sentence2`).
- Đổi tên cột `label` thành `labels` (vì mô hình mong đợi đối số được đặt tên là `labels`).
- Đặt định dạng của bộ dữ liệu để chúng trả về các tensor PyTorch thay vì danh sách.

`Tokenized_datasets` của chúng ta có phương thức cho mỗi bước đó:

```py
tokenized_datasets = tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"])
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
tokenized_datasets.set_format("torch")
tokenized_datasets["train"].column_names
```

Sau đó, chúng ta có thể kiểm tra xem kết quả có chỉ có các cột mà mô hình của chúng ta sẽ chấp nhận không:

```python
["attention_mask", "input_ids", "labels", "token_type_ids"]
```

Xong rồi, chúng ta có thể dễ dàng định nghĩa các bộ dữ liệu của mình:

```py
from torch.utils.data import DataLoader

train_dataloader = DataLoader(
    tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
)
```

Để nhanh chóng kiểm tra không có sai sót trong quá trình xử lý dữ liệu, chúng ta có thể kiểm tra một lô như sau:

```py
for batch in train_dataloader:
    break
{k: v.shape for k, v in batch.items()}
```

```python out
{'attention_mask': torch.Size([8, 65]),
 'input_ids': torch.Size([8, 65]),
 'labels': torch.Size([8]),
 'token_type_ids': torch.Size([8, 65])}
```

Lưu ý rằng các hình dạng thực tế có thể sẽ hơi khác đối với bạn vì chúng tôi đặt `shuffle = True` cho dataloader huấn luyện và chúng tôi đang đệm đến độ dài tối đa bên trong lô.

Bây giờ chúng ta đã hoàn thành việc xử lý trước dữ liệu (một mục tiêu thỏa mãn nhưng khó nắm bắt đối với bất kỳ người thực hành ML nào), hãy chuyển sang mô hình thôi. Chúng ta khởi tạo nó chính xác như đã làm trong phần trước:

```py
from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
```

To make sure that everything will go smoothly during training, we pass our batch to this model:

```py
outputs = model(**batch)
print(outputs.loss, outputs.logits.shape)
```

```python out
tensor(0.5441, grad_fn=<NllLossBackward>) torch.Size([8, 2])
```

Tất cả các mô hình 🤗 Transformers sẽ trả về lượng mất mát khi `labels` được cung cấp và chúng ta cũng nhận được logit (hai cho mỗi đầu vào trong lô, do đó, một tensor có kích thước 8 x 2).

Chúng ta gần như đã sẵn sàng để viết vòng lặp huấn luyện của mình! Chúng ta chỉ thiếu hai thứ: một trình tối ưu hóa và một công cụ lập lịch tốc độ học tập. Vì chúng ta đang cố gắng tái tạo những gì mà `Trainer` đã làm bằng tay, nên ta sẽ sử dụng các giá trị mặc định tương tự. Trình tối ưu hóa được sử dụng bởi `Trainer` là `AdamW`, tương tự như Adam, nhưng có một bước ngoặt để điều chỉnh phân rã trọng số (xem ["Decoupled Weight Decay Regularization"](https://arxiv.org/abs/1711.05101) của Ilya Loshchilov và Frank Hutter):

```py
from torch.optim import AdamW

optimizer = AdamW(model.parameters(), lr=5e-5)
```

Cuối cùng, bộ lập lịch tốc độ học được sử dụng theo mặc định chỉ là một phân rã tuyến tính từ giá trị lớn nhất (5e-5) xuống 0. Để xác định đúng, chúng ta cần biết số bước huấn luyện sẽ thực hiện, đó là số epoch muốn chạy nhân với số lô huấn luyện (là độ dài của bộ dữ liệu huấn luyện). `Trainer` sử dụng ba epoch theo mặc định, vì vậy chúng tôi sẽ tuân theo điều đó:

```py
from transformers import get_scheduler

num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)
print(num_training_steps)
```

```python out
1377
```

### Vòng lặp huấn luyện

Một điều cuối cùng: chúng ta sẽ muốn sử dụng GPU nếu có quyền truy cập vào một GPU (trên CPU, quá trình huấn luyện có thể mất vài giờ thay vì vài phút). Để làm điều này, chúng ta xác định một `device`, ta sẽ đặt mô hình và các lô của ta trên đó:

```py
import torch

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
model.to(device)
device
```

```python out
device(type='cuda')
```

Giờ thì ta đã sẵn sàng để huấn luyện rồi! Để biết khi nào quá trình huấn luyện sẽ kết thúc, ta thêm thanh tiến trình qua số bước huấn luyện, sử dụng thư viện `tqdm`:

```py
from tqdm.auto import tqdm

progress_bar = tqdm(range(num_training_steps))

model.train()
for epoch in range(num_epochs):
    for batch in train_dataloader:
        batch = {k: v.to(device) for k, v in batch.items()}
        outputs = model(**batch)
        loss = outputs.loss
        loss.backward()

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)
```

Bạn có thể thấy rằng cốt lõi của vòng lặp huấn luyện trông rất giống như trong phần giới thiệu. Chúng ta đã không yêu cầu bất kỳ báo cáo nào, vì vậy vòng huấn luyện này sẽ không cho ta biết bất kỳ điều gì về cái giá của mô hình. Chúng ta cần thêm một vòng lặp đánh giá cho điều đó.

### Vòng lặp đánh giá

Như đã làm trước đó, chúng ta sẽ sử dụng một chỉ số được cung cấp bởi thư viện 🤗 Evaluate. Chúng ta đã thấy phương thức `metric.compute()`, các chỉ số thực sự có thể tích lũy các lô cho ta khi xem qua vòng dự đoán với phương thức `add_batch()`. Khi ta đã tích lũy tất cả các lô, chúng ta có thể nhận được kết quả cuối cùng với `metric.compute()`. Dưới đây là cách thực hiện tất cả những điều này trong một vòng lặp đánh giá:

```py
import evaluate

metric = evaluate.load("glue", "mrpc")
model.eval()
for batch in eval_dataloader:
    batch = {k: v.to(device) for k, v in batch.items()}
    with torch.no_grad():
        outputs = model(**batch)

    logits = outputs.logits
    predictions = torch.argmax(logits, dim=-1)
    metric.add_batch(predictions=predictions, references=batch["labels"])

metric.compute()
```

```python out
{'accuracy': 0.8431372549019608, 'f1': 0.8907849829351535}
```

Một lần nữa, kết quả của bạn sẽ hơi khác một chút vì sự ngẫu nhiên trong quá trình khởi tạo đầu mô hình và xáo trộn dữ liệu, nhưng chúng phải ở trong cùng một khoảng.

<Tip>

✏️ **Thử nghiệm thôi!** Sửa đổi vòng lặp huấn luyện trước đó để tinh chỉnh mô hình của bạn trên tập dữ liệu SST-2.

</Tip>

### Tăng cường trí thông minh của vòng huấn luyện với 🤗 Accelerate

<Youtube id="s7dy8QRgjJ0" />

Vòng lặp huấn luyện mà ta đã định nghĩa trước đó hoạt động tốt trên một CPU hoặc GPU. Nhưng bằng cách sử dụng thư viện [🤗 Accelerate](https://github.com/huggingface/accelerate), chỉ với một vài điều chỉnh, chúng ta có thể huấn luyện phân tán trên nhiều GPU hoặc TPU. Bắt đầu từ việc tạo bộ dữ liệu huấn luyện và kiểm định, đây là vòng lặp huấn luyện thủ công thực thi:

```py
from torch.optim import AdamW
from transformers import AutoModelForSequenceClassification, get_scheduler

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
optimizer = AdamW(model.parameters(), lr=3e-5)

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
model.to(device)

num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)

progress_bar = tqdm(range(num_training_steps))

model.train()
for epoch in range(num_epochs):
    for batch in train_dataloader:
        batch = {k: v.to(device) for k, v in batch.items()}
        outputs = model(**batch)
        loss = outputs.loss
        loss.backward()

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)
```

Và đây là một số thay đổi:

```diff
+ from accelerate import Accelerator
  from torch.optim import AdamW
  from transformers import AutoModelForSequenceClassification, get_scheduler

+ accelerator = Accelerator()

  model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
  optimizer = AdamW(model.parameters(), lr=3e-5)

- device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
- model.to(device)

+ train_dataloader, eval_dataloader, model, optimizer = accelerator.prepare(
+     train_dataloader, eval_dataloader, model, optimizer
+ )

  num_epochs = 3
  num_training_steps = num_epochs * len(train_dataloader)
  lr_scheduler = get_scheduler(
      "linear",
      optimizer=optimizer,
      num_warmup_steps=0,
      num_training_steps=num_training_steps
  )

  progress_bar = tqdm(range(num_training_steps))

  model.train()
  for epoch in range(num_epochs):
      for batch in train_dataloader:
-         batch = {k: v.to(device) for k, v in batch.items()}
          outputs = model(**batch)
          loss = outputs.loss
-         loss.backward()
+         accelerator.backward(loss)

          optimizer.step()
          lr_scheduler.step()
          optimizer.zero_grad()
          progress_bar.update(1)
```

Dòng đầu tiên cần thêm là dòng nhập. Dòng thứ hai khởi tạo một đối tượng `Accelerator` sẽ xem xét môi trường và khởi tạo thiết lập phân tán thích hợp. 🤗 Accelerate xử lý vị trí đặt thiết bị cho bạn, vì vậy bạn có thể xóa các dòng đặt mô hình trên thiết bị (hoặc, nếu bạn thích, hãy thay đổi chúng để sử dụng `accelerator.device` thay vì `device`).

Sau đó, phần lớn công việc chính được thực hiện trong dòng gửi bộ lưu dữ liệu, mô hình và trình tối ưu hóa đến `accelerator.prepare()`. Thao tác này sẽ bọc các đối tượng đó trong hộp chứa thích hợp để đảm bảo việc huấn luyện được phân phối hoạt động như dự định. Các thay đổi còn lại cần thực hiện là loại bỏ dòng đặt lô trên `device` (một lần nữa, nếu bạn muốn giữ lại điều này, bạn chỉ cần thay đổi nó thành sử dụng `accelerator.device`) và thay thế `loss.backward()` bằng  `accelerator.backward(loss)`.

<Tip>
⚠️ Để hưởng lợi từ việc tăng tốc độ do Cloud TPUs cung cấp, chúng tôi khuyên bạn nên đệm các mẫu của mình theo độ dài cố định bằng các tham số `padding="max_length"` và `max_length` của tokenizer.
</Tip>

Nếu bạn muốn sao chép và dán nó để mày mò, đây là giao diện của vòng huấn luyện hoàn chỉnh với 🤗 Accelerate:

```py
from accelerate import Accelerator
from torch.optim import AdamW
from transformers import AutoModelForSequenceClassification, get_scheduler

accelerator = Accelerator()

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
optimizer = AdamW(model.parameters(), lr=3e-5)

train_dl, eval_dl, model, optimizer = accelerator.prepare(
    train_dataloader, eval_dataloader, model, optimizer
)

num_epochs = 3
num_training_steps = num_epochs * len(train_dl)
lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)

progress_bar = tqdm(range(num_training_steps))

model.train()
for epoch in range(num_epochs):
    for batch in train_dl:
        outputs = model(**batch)
        loss = outputs.loss
        accelerator.backward(loss)

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)
```

Đặt điều này trong `train.py` sẽ làm cho tập lệnh đó có thể chạy được trên bất kỳ loại thiết lập phân tán nào. Để dùng thử trong thiết lập phân tán của bạn, hãy chạy lệnh:

```bash
accelerate config
```

điều này sẽ nhắc bạn trả lời một số câu hỏi và trích xuất câu trả lời của bạn vào tệp cấu hình bởi lệnh sau:

```
accelerate launch train.py
```

và nó sẽ khởi chạy chương trình huấn luyện phân tán.

Nếu bạn muốn thử điều này trong Notebook (ví dụ: để kiểm tra nó với TPU trên Colab), chỉ cần dán đoạn mã vào `training_function()` và chạy ô cuối cùng với:

```python
from accelerate import notebook_launcher

notebook_launcher(training_function)
```

Bạn có thể tìm thêm các ví dụ tại [🤗 Accelerate repo](https://github.com/huggingface/accelerate/tree/main/examples).


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter3/4.mdx" />

### Xử lý dữ liệu
https://huggingface.co/learn/course/vi/chapter3/2.md

# Xử lý dữ liệu

{#if fw === 'pt'}

<CourseFloatingBanner chapter={3}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter3/section2_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter3/section2_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={3}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter3/section2_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter3/section2_tf.ipynb"},
]} />

{/if}

{#if fw === 'pt'}
Tiếp tục với ví dụ từ [chương trước](/course/chapter2), đây là cách chúng ta sẽ huấn luyện một bộ phân loại chuỗi trên một lô trong PyTorch:

```python
import torch
from torch.optim import AdamW
from transformers import AutoTokenizer, AutoModelForSequenceClassification

# Tương tự như ví dụ trước
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
    "I've been waiting for a HuggingFace course my whole life.",
    "This course is amazing!",
]
batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")

# Đây là phần mới
batch["labels"] = torch.tensor([1, 1])

optimizer = AdamW(model.parameters())
loss = model(**batch).loss
loss.backward()
optimizer.step()
```
{:else}
Tiếp tục với ví dụ từ [chương trước](/course/chapter2), đây là cách chúng ta sẽ huấn luyện một bộ phân loại chuỗi trên một lô trong TensorFlow:

```python
import tensorflow as tf
import numpy as np
from transformers import AutoTokenizer, TFAutoModelForSequenceClassification

# Tương tự như ví dụ trước
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
    "I've been waiting for a HuggingFace course my whole life.",
    "This course is amazing!",
]
batch = dict(tokenizer(sequences, padding=True, truncation=True, return_tensors="tf"))

# Đây là phần mới
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy")
labels = tf.convert_to_tensor([1, 1])
model.train_on_batch(batch, labels)
```
{/if}

Tất nhiên, chỉ huấn luyện mô hình trên hai câu sẽ không mang lại kết quả tốt. Để có được kết quả tốt hơn, bạn sẽ cần chuẩn bị một bộ dữ liệu lớn hơn.

Trong phần này, chúng tôi sẽ sử dụng tập dữ liệu MRPC (Microsoft Research Paraphrase Corpus) làm ví dụ, được giới thiệu trong [bài báo](https://www.aclweb.org/anthology/I05-5002.pdf) của William B. Dolan và Chris Brockett. Tập dữ liệu bao gồm 5,801 cặp câu, với nhãn cho biết chúng có phải là câu diễn giải hay không (tức là nếu cả hai câu đều có nghĩa giống nhau). Chúng tôi đã chọn nó cho chương này vì nó là một tập dữ liệu nhỏ, vì vậy thật dễ dàng để thử nghiệm với việc huấn luyện về nó.

### Tải bộ dữ liệu từ Hub

{#if fw === 'pt'}
<Youtube id="_BZearw7f0w"/>
{:else}
<Youtube id="W_gMJF0xomE"/>
{/if}

Hub không chỉ chứa các mô hình; nó cũng có nhiều bộ dữ liệu nhiều ngôn ngữ khác nhau. Bạn có thể xem qua tập dữ liệu [tại đây](https://huggingface.co/datasets) và chúng tôi khuyên bạn nên thử tải và xử lý bộ dữ liệu mới khi bạn đã xem qua phần này (xem tài liệu chung [tại đây](https://huggingface.co/docs/datasets/loading)). Nhưng hiện tại, hãy tập trung vào bộ dữ liệu MRPC! Đây là một trong 10 bộ dữ liệu tạo nên [bộ chuẩn GLUE](https://gluebenchmark.com/), là một điểm chuẩn học thuật được sử dụng để đo hiệu suất của các mô hình ML trên 10 tác vụ phân loại văn bản khác nhau.

Thư viện 🤗 Datasets cung cấp một lệnh rất đơn giản để tải xuống và lưu vào bộ nhớ cache một tập dữ liệu trên Hub. Chúng ta có thể tải xuống bộ dữ liệu MRPC như sau:

```py
from datasets import load_dataset

raw_datasets = load_dataset("glue", "mrpc")
raw_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 3668
    })
    validation: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 408
    })
    test: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 1725
    })
})
```

Như bạn có thể thấy, chúng ta nhận được một đối tượng `DatasetDict` chứa tập huấn luyện, tập kiểm định và tập kiểm thử. Mỗi tập chứa một số cột (`sentence1`, `sentence2`, `label`, và `idx`) và một số hàng thay đổi, là số phần tử trong mỗi tập (vì vậy, có 3,668 cặp câu trong tập huấn luyện, 408 trong tập kiểm chứng và 1,725 trong tập kiểm định).

Lệnh này tải xuống và lưu vào bộ nhớ cache các tập dữ liệu, mặc định lưu trong *~/.cache/huggingface/datasets*. Nhớ lại từ Chương 2 rằng bạn có thể tùy chỉnh thư mục bộ nhớ cache của mình bằng cách đặt biến môi trường `HF_HOME`.

Chúng ta có thể truy cập từng cặp câu trong đối tượng `raw_datasets` của mình bằng cách lập chỉ mục, giống như với từ điển:

```py
raw_train_dataset = raw_datasets["train"]
raw_train_dataset[0]
```

```python out
{'idx': 0,
 'label': 1,
 'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'}
```

Chúng ta có thể thấy các nhãn vốn là số nguyên, vì vậy chúng ta không phải thực hiện bất kỳ bước xử lý trước nào ở đó. Để biết số nguyên nào tương ứng với nhãn nào, chúng ta có thể kiểm tra `features` của `raw_train_dataset`. Điều này sẽ cho chúng tôi biết loại của mỗi cột:

```py
raw_train_dataset.features
```

```python out
{'sentence1': Value(dtype='string', id=None),
 'sentence2': Value(dtype='string', id=None),
 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
 'idx': Value(dtype='int32', id=None)}
```

Phía sau, `label` thuộc loại `ClassLabel` và ánh xạ các số nguyên thành tên nhãn được lưu trữ trong thư mục *names*. `0` tương ứng với `không tương đương`, và `1` tương ứng với `tương đương`.

<Tip>

✏️ **Thử nghiệm thôi!** Nhìn vào phần tử thứ 15 của tập huấn luyện và phần tử 87 của tập kiểm định. Nhãn của chúng là gì?

</Tip>

### Tiền xử lý một bộ dữ liệu

{#if fw === 'pt'}
<Youtube id="0u3ioSwev3s"/>
{:else}
<Youtube id="P-rZWqcB6CE"/>
{/if}

Để tiền xử lý bộ dữ liệu, chúng ta cần chuyển văn bản thành các số mà mô hình có thể hiểu được. Như bạn đã thấy trong [chương trước](/course/chapter2), điều này được thực hiện với một tokenizer. Chúng ta có thể cung cấp cho tokenizer một câu hoặc một danh sách các câu, vì vậy chúng ta có thể tokenizer trực tiếp tất cả các câu đầu tiên và tất cả các câu thứ hai của mỗi cặp như sau:

```py
from transformers import AutoTokenizer

checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
tokenized_sentences_1 = tokenizer(raw_datasets["train"]["sentence1"])
tokenized_sentences_2 = tokenizer(raw_datasets["train"]["sentence2"])
```

Tuy nhiên, chúng ta không thể chỉ chuyển hai chuỗi vào mô hình và nhận được dự đoán liệu hai câu có phải là diễn giải hay không. Chúng ta cần xử lý hai chuỗi như một cặp và áp dụng tiền xử lý thích hợp. May mắn thay, tokenizer cũng có thể nhận một cặp chuỗi và chuẩn bị nó theo cách mà mô hình BERT của ta mong đợi:

```py
inputs = tokenizer("This is the first sentence.", "This is the second one.")
inputs
```

```python out
{ 
  'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102],
  'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
  'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
}
```

Chúng ta đã thảo luận về `input_ids` và `attention_mask` trong [Chương 2](/course/chapter2), nhưng chúng ta tạm dừng để nói về `token_type_ids`. Trong ví dụ này, đây là phần cho mô hình biết phần nào của đầu vào là câu đầu tiên và phần nào là câu thứ hai.

<Tip>

✏️ **Thử nghiệm thôi!** Lấy phần tử 15 của tập huấn luyện và tokenize hai câu riêng biệt và như một cặp. Sự khác biệt giữa hai kết quả là gì?

</Tip>

Nếu chúng ta giải mã các ID bên trong `input_ids` trở lại các từ:

```py
tokenizer.convert_ids_to_tokens(inputs["input_ids"])
```

ta sẽ nhận được:

```python out
['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
```

Có thể thấy mô hình kì vọng các đầu vào có dạng `[CLS] câu1 [SEP] câu2 [SEP]` khi có hai câu. Căn chỉnh điều này với `token_type_ids` cho ta kết quả:

```python out
['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
[      0,      0,    0,     0,       0,          0,   0,       0,      1,    1,     1,        1,     1,   1,       1]
```

Như bạn có thể thấy, các phần của đầu vào tương ứng với `[CLS] câu1 [SEP]` đều có loại token ID là `0`, trong khi các phần khác, tương ứng với `câu2 [SEP]`, tất cả đều có loại token ID là `1`.

Lưu ý rằng nếu bạn chọn một checkpoint khác, bạn sẽ không nhất thiết phải có `token_type_ids` trong đầu vào được tokenize của mình (ví dụ: chúng sẽ không được trả lại nếu bạn sử dụng mô hình DistilBERT). Chúng chỉ được trả lại khi mô hình biết phải làm gì với chúng, bởi vì nó đã nhìn thấy chúng trong quá trình huấn luyện trước.

Ở đây, BERT được huấn luyện trước với các token ID và trên đầu mục tiêu mô hình ngôn ngữ được che mà chúng ta đã đề cập trong [Chương 1](/course/chapter1), nó có một mục tiêu bổ sung được gọi là _dự đoán câu tiếp theo_. Mục tiêu của tác vụ này là mô hình hóa mối quan hệ giữa các cặp câu.

Với dự đoán câu tiếp theo, mô hình được cung cấp các cặp câu (với các token được che ngẫu nhiên) và được yêu cầu dự đoán liệu câu thứ hai có theo sau câu đầu tiên hay không. Để làm cho tác vụ trở nên không tầm thường, một nửa là các câu tiếp nối nhau trong tài liệu gốc mà chúng được trích xuất, và nửa còn lại là hai câu đến từ hai tài liệu khác nhau.

Nói chung, bạn không cần phải lo lắng về việc có hay không có `token_type_ids` trong đầu vào được tokenize của mình: miễn là bạn sử dụng cùng một checkpoint cho trình tokenize và mô hình, mọi thứ sẽ ổn vì trình tokenize nhận biết cần cung cấp những gì với mô hình của nó.

Bây giờ chúng ta đã thấy cách trình tokenize của chúng ta có thể xử lý một cặp câu, chúng ta có thể sử dụng nó để mã hóa toàn bộ tập dữ liệu của mình: giống như trong [chương trước](/course/chapter2), chúng ta có thể cung cấp cho trình tokenize danh sách các cặp bằng cách đưa cho nó danh sách các câu đầu tiên, sau đó là danh sách các câu thứ hai. Điều này cũng tương thích với các tùy chọn đệm và cắt bớt mà chúng ta đã thấy trong [Chương 2](/course/chapter2). Vì vậy, một cách để tiền xử lý trước tập dữ liệu huấn luyện là:

```py
tokenized_dataset = tokenizer(
    raw_datasets["train"]["sentence1"],
    raw_datasets["train"]["sentence2"],
    padding=True,
    truncation=True,
)
```

Điều này hoạt động tốt, nhưng nó có nhược điểm là trả về từ điển (với các khóa của chúng tôi, `input_ids`, `attention_mask` và `token_type_ids`, và các giá trị là danh sách các danh sách). Nó cũng sẽ chỉ hoạt động nếu bạn có đủ RAM để lưu trữ toàn bộ tập dữ liệu của mình trong quá trình tokenize (trong khi các tập dữ liệu từ thư viện 🤗 Datasets là các tệp [Apache Arrow](https://arrow.apache.org/) được lưu trữ trên đĩa, vì vậy bạn chỉ giữ các mẫu bạn yêu cầu đã tải trong bộ nhớ).

Để giữ dữ liệu dưới dạng tập dữ liệu, chúng ta sẽ sử dụng phương thức [`Dataset.map()`](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map). Điều này cũng cho phép chúng ta linh hoạt hơn, nếu chúng ta cần thực hiện nhiều tiền xử lý hơn là chỉ tokenize. Phương thức `map()` hoạt động bằng cách áp dụng một hàm trên mỗi phần tử của tập dữ liệu, vì vậy hãy xác định một hàm tokenize các đầu vào của chúng ta:

```py
def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
```

Hàm này lấy một từ điển (giống như các mục trong tập dữ liệu của chúng ta) và trả về một từ điển mới với các khóa `input_ids`, `attention_mask` và `token_type_ids`. Lưu ý rằng nó cũng hoạt động nếu từ điển `example` chứa một số mẫu (mỗi khóa là một danh sách các câu) vì `tokenizer` hoạt động trên danh sách các cặp câu, như đã thấy trước đây. Điều này sẽ cho phép chúng ta sử dụng tùy chọn `batch = True` trong lệnh gọi `map()`, từ đó sẽ tăng tốc đáng kể quá trình tokenize. `Tokenizer` được hỗ trợ bởi một tokenizer được viết bằng Rust từ thư viện [🤗 Tokenizer](https://github.com/huggingface/tokenizers). Tokenizer này có thể rất nhanh, nhưng chỉ khi chúng ta cung cấp nhiều đầu vào cùng một lúc.

Lưu ý rằng chúng ta đã để tạm bỏ qua tham số `padding` trong hàm tokenize của ta. Điều này là do việc đệm tất cả các mẫu đến chiều dài tối đa không hiệu quả: tốt hơn nên đệm các mẫu khi chúng ta đang tạo một lô, vì khi đó chúng ta chỉ cần đệm đến chiều dài tối đa trong lô đó chứ không phải chiều dài tối đa trong toàn bộ tập dữ liệu. Điều này có thể tiết kiệm rất nhiều thời gian và công suất xử lý khi các đầu vào có độ dài rất thay đổi!

Đây là cách chúng ta áp dụng chức năng mã hóa trên tất cả các tập dữ liệu của ta cùng một lúc. Chúng ta đang sử dụng `batch = True` trong lệnh gọi tới `map`, vì vậy, hàm được áp dụng cho nhiều phần tử của tập dữ liệu cùng một lúc, chứ không phải trên từng phần tử riêng biệt. Điều này cho phép việc tiền xử lý nhanh hơn.

```py
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
tokenized_datasets
```

Cách thư viện 🤗 Datasets áp dụng bước xử lý này là thêm các trường mới vào bộ dữ liệu, mỗi khóa trong từ điển được trả về bởi hàm tiền xử lý một trường:

```python out
DatasetDict({
    train: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 3668
    })
    validation: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 408
    })
    test: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 1725
    })
})
```

Bạn thậm chí có thể sử dụng đa xử lý khi áp dụng chức năng tiền xử lý của mình với `map()` bằng cách truyền tham số `num_proc`. Chúng ta không làm điều này ở đây vì thư viện 🤗 Tokenizers đã sử dụng nhiều chuỗi để tokenize ác mẫu của nhanh hơn, nhưng nếu bạn không sử dụng trình tokenize nhanh được thư viện này hỗ trợ, bước trên có thể tăng tốc quá trình xử lý trước của bạn.

`Tokenize_function` của chúng ta trả về một từ điển với các khóa `input_ids`, `attention_mask` và `token_type_ids`, vì vậy ba trường đó được thêm vào tất cả các phần bộ dữ liệu của chúng ta. Lưu ý rằng ta cũng có thể đã thay đổi các trường hiện có nếu hàm tiền xử lý trả về một giá trị mới cho một khóa hiện có trong tập dữ liệu mà ta đã áp dụng `map()`.

Điều cuối cùng chúng ta sẽ cần làm là đệm tất cả các ví dụ để có độ dài của phần tử dài nhất khi chúng tôi gộp các phần tử lại với nhau - một kỹ thuật mà chúng tôi gọi là *đệm động*.

### Phần đệm động

<Youtube id="7q5NyFT8REg"/>

{#if fw === 'pt'}
Hàm chịu trách nhiệm tập hợp các mẫu lại với nhau trong một lô được gọi là *collate function* hay *hàm đối chiếu*. Đó là một tham số bạn có thể đưa vào khi xây dựng một `DataLoader`, mặc định đây là một hàm sẽ chỉ chuyển đổi các mẫu của bạn thành các tensors PyTorch và nối chúng (đệ quy nếu các phần tử của bạn là list, tuple hoặc dict). Điều này sẽ không thể xảy ra trong trường hợp của chúng ta vì tất cả các đầu vào ta có sẽ không có cùng kích thước. Chúng ta đã cố tình hoãn việc bổ sung đệm, để chỉ áp dụng nó khi cần thiết trên mỗi lô và tránh để các đầu vào quá dài với nhiều đệm. Điều này sẽ đẩy nhanh quá trình huấn luyện lên một chút, nhưng lưu ý rằng nếu bạn đang huấn luyện trên TPU thì nó có thể gây ra vấn đề - TPU thích các hình dạng cố định, ngay cả khi điều đó yêu cầu thêm đệm.

{:else}

Hàm chịu trách nhiệm tập hợp các mẫu lại với nhau trong một lô được gọi là *collate function* hay *hàm đối chiếu*. Đó là một tham số bạn có thể đưa vào khi xây dựng một `DataLoader`, mặc định đây là một hàm sẽ chỉ chuyển đổi các mẫu của bạn thành các tensors PyTorch và nối chúng (đệ quy nếu các phần tử của bạn là list, tuple hoặc dict). Điều này sẽ không thể xảy ra trong trường hợp của chúng ta vì tất cả các đầu vào ta có sẽ không có cùng kích thước. Chúng ta đã cố tình hoãn việc bổ sung đệm, để chỉ áp dụng nó khi cần thiết trên mỗi lô và tránh để các đầu vào quá dài với nhiều đệm. Điều này sẽ đẩy nhanh quá trình huấn luyện lên một chút, nhưng lưu ý rằng nếu bạn đang huấn luyện trên TPU thì nó có thể gây ra vấn đề - TPU thích các hình dạng cố định, ngay cả khi điều đó yêu cầu thêm đệm.

{/if}

Để thực hiện điều này trong thực tế, chúng ta phải định nghĩa một hàm đối chiếu sẽ áp dụng đúng số lượng đệm cho các mục của tập dữ liệu mà chúng ta muốn gộp hàng loạt lại với nhau. May mắn thay, thư viện 🤗 Transformers cung cấp cho chúng ta một chức năng như vậy thông qua `DataCollatorWithPadding`. Cần có trình tokenize khi bạn khởi tạo nó (để biết cần sử dụng token đệm nào và liệu mô hình mong đợi đệm ở bên trái hay bên phải của các đầu vào) và sẽ thực hiện mọi thứ bạn cần:

{#if fw === 'pt'}
```py
from transformers import DataCollatorWithPadding

data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
```
{:else}
```py
from transformers import DataCollatorWithPadding

data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")
```
{/if}

Để kiểm tra món mới này, chúng ta hãy lấy một vài mẫu từ tập huấn luyện mà chúng ta muốn ghép lại với nhau. Ở đây, chúng ta xóa các cột `idx`, `sentence1`, và `sentence2` vì chúng không cần thiết và chứa các chuỗi (và chúng ta không thể tạo tensor bằng chuỗi) và xem độ dài của mỗi mục trong lô:

```py
samples = tokenized_datasets["train"][:8]
samples = {k: v for k, v in samples.items() if k not in ["idx", "sentence1", "sentence2"]}
[len(x) for x in samples["input_ids"]]
```

```python out
[50, 59, 47, 67, 59, 50, 62, 32]
```

Không có gì ngạc nhiên, ta nhận được các mẫu có độ dài khác nhau, từ 32 đến 67. Đệm động có nghĩa là tất cả các mẫu trong lô này phải được đệm đến chiều dài 67, chiều dài tối đa bên trong lô. Nếu không có đệm động, tất cả các mẫu sẽ phải được đệm đến độ dài tối đa trong toàn bộ tập dữ liệu hoặc độ dài tối đa mà mô hình có thể chấp nhận. Hãy kiểm tra kỹ xem `data_collator` của chúng ta có tự động đệm lô đúng cách hay không:

```py
batch = data_collator(samples)
{k: v.shape for k, v in batch.items()}
```

{#if fw === 'tf'}

```python out
{'attention_mask': TensorShape([8, 67]),
 'input_ids': TensorShape([8, 67]),
 'token_type_ids': TensorShape([8, 67]),
 'labels': TensorShape([8])}
```

{:else}

```python out
{'attention_mask': torch.Size([8, 67]),
 'input_ids': torch.Size([8, 67]),
 'token_type_ids': torch.Size([8, 67]),
 'labels': torch.Size([8])}
```

Trông khá ổn! Giờ ta đã chuyển từ văn bản thô sang các lô mà mô hình có thể xử lý, và ta đã sẵn sàng tinh chỉnh nó!

{/if}

<Tip>

✏️ **Thử nghiệm thôi!** Sao chép tiền xử lý trên tập dữ liệu GLUE SST-2. Nó hơi khác một chút vì nó bao gồm các câu đơn thay vì các cặp, nhưng phần còn lại của những gì ta đã làm sẽ tương tự nhau. Với một thử thách khó hơn, hãy cố gắng viết một hàm tiền xử lý hoạt động trên bất kỳ tác vụ GLUE nào.

</Tip>

{#if fw === 'tf'}

Bây giờ chúng ta đã có bộ dữ liệu và bộ đối chiếu dữ liệu, ta cần phải kết hợp chúng lại với nhau. Chúng ta có thể tải các lô và đối chiếu theo cách thủ công, nhưng cách này rất tốn công sức và có lẽ cũng không hiệu quả lắm. Thay vào đó, có một phương pháp đơn giản cung cấp giải pháp hiệu quả cho vấn đề này: `to_tf_dataset()`. Nso được bao một `tf.data.Dataset` xung quanh tập dữ liệu của bạn, với một chức năng đối chiếu tùy chọn. `tf.data.Dataset` là một định dạng TensorFlow gốc mà Keras có thể sử dụng cho `model.fit()`, vì vậy phương pháp này ngay lập tức chuyển đổi một 🤗 Dataset sang một định dạng sẵn sàng để huấn luyện. Hãy xem nó hoạt động với tập dữ liệu của chúng tôi!

```py
tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "token_type_ids"],
    label_cols=["labels"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=8,
)

tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "token_type_ids"],
    label_cols=["labels"],
    shuffle=False,
    collate_fn=data_collator,
    batch_size=8,
)
```

Và nó đó! Chúng ta có thể chuyển những bộ dữ liệu đó sang bài giảng tiếp theo, nơi việc huấn luyện sẽ trở nên đơn giản một cách dễ chịu sau tất cả những công việc khó khăn của việc xử lý trước dữ liệu.

{/if}


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter3/2.mdx" />

### Làm thế nào để viết một vấn đề hay
https://huggingface.co/learn/course/vi/chapter8/5.md

#  Làm thế nào để viết một vấn đề hay

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter8/section5.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter8/section5.ipynb"},
]} />

Khi bạn gặp điều gì đó có vẻ không ổn với một trong các thư viện Hugging Face, bạn chắc chắn nên cho chúng tôi biết để chúng tôi có thể sửa chữa nó (điều này cũng xảy ra với bất kỳ thư viện mã nguồn mở nào, đối với vấn đề đó). Nếu bạn không hoàn toàn chắc chắn liệu lỗi nằm trong mã của riêng bạn hay một trong các thư viện của chúng tôi, nơi đầu tiên cần kiểm tra là [diễn đàn](https://discuss.huggingface.co/). Cộng đồng sẽ giúp bạn tìm ra điều này và nhóm Hugging Face cũng theo dõi chặt chẽ các cuộc thảo luận tại đó.

<Youtube id="_PAli-V4wj0"/>

Khi bạn chắc chắn rằng bạn có một lỗi trong tay, bước đầu tiên là xây dựng một ví dụ có thể tái tạo tối thiểu.

## Tạo một ví dụ có thể tái tạo tối thiểu

Điều rất quan trọng là phải cô lập đoạn mã tạo ra lỗi, vì không có ai trong nhóm Hugging Face là ảo thuật gia và họ không thể sửa những gì họ không thể nhìn thấy. Một ví dụ có thể tái tạo tối thiểu, như tên đã chỉ ra, phải có thể tái tạo được. Điều này có nghĩa là nó không nên dựa vào bất kỳ tệp hoặc dữ liệu bên ngoài nào mà bạn có thể có. Cố gắng thay thế dữ liệu bạn đang sử dụng bằng một số giá trị giả trông giống như dữ liệu thật của bạn mà vẫn tạo ra lỗi tương tự.

<Tip>

🚨 Nhiều vấn đề trong kho lưu trữ 🤗 Transformers chưa được giải quyết vì không thể truy cập được dữ liệu được sử dụng để tái tạo chúng.

</Tip>

Một khi bạn có một cái gì đó độc lập, bạn có thể cố gắng giảm nó thành những dòng mã ít hơn, xây dựng cái mà chúng ta gọi là _ví dụ tối giản có thể tái tạo được_. Mặc dù điều này đòi hỏi bạn phải làm việc nhiều hơn một chút, nhưng bạn gần như sẽ được đảm bảo nhận được trợ giúp và bản sửa lỗi nếu bạn cung cấp một trình tạo lỗi ngắn gọn, đẹp mắt.

Nếu bạn cảm thấy đủ thoải mái, hãy kiểm tra mã nguồn nơi lỗi của bạn xảy ra. Bạn có thể tìm thấy giải pháp cho vấn đề của mình (trong trường hợp đó, bạn thậm chí có thể đề xuất một pull request để khắc phục nó), nhưng nhìn chung, điều này có thể giúp người bảo trì hiểu rõ hơn về nguồn khi họ đọc báo cáo của bạn.

## Điền vào mẫu vấn đề

Khi bạn gửi vấn đề của mình, bạn sẽ thấy có một mẫu để điền vào. Chúng tôi sẽ theo dõi thông tin về [🤗 Sự cố về Transformers](https://github.com/huggingface/transformers/issues/new/choose) tại đây, nhưng loại thông tin tương tự sẽ được yêu cầu nếu bạn báo cáo sự cố trong kho lưu trữ khác. Đừng để trống mẫu: dành thời gian điền vào mẫu sẽ tối đa hóa cơ hội nhận được câu trả lời và giải quyết vấn đề của bạn.

Nói chung, khi trình bày một vấn đề, hãy luôn giữ thái độ lịch sự. Đây là một dự án mã nguồn mở, vì vậy bạn đang sử dụng phần mềm miễn phí và không ai có nghĩa vụ phải giúp bạn. Bạn có thể bao gồm những gì bạn cảm thấy là những lời chỉ trích chính đáng trong vấn đề của bạn, nhưng sau đó những người bảo trì rất có thể coi thường nó và không vội vàng giúp bạn. Đảm bảo rằng bạn đã đọc [quy tắc ứng xử](https://github.com/huggingface/transformers/blob/master/CODE_OF_CONDUCT.md) của dự án.

###Bao gồm thông tin môi trường của bạn

🤗 Transformers cung cấp một tiện ích để lấy tất cả thông tin chúng tôi cần về môi trường của bạn. Chỉ cần nhập thông tin sau vào thiết bị đầu cuối như terminal của bạn:

```
transformers-cli env
```

và bạn sẽ nhận được một cái gì đó như thế này:

```out
Copy-and-paste the text below in your GitHub issue and FILL OUT the two last points.

- `transformers` version: 4.12.0.dev0
- Platform: Linux-5.10.61-1-MANJARO-x86_64-with-arch-Manjaro-Linux
- Python version: 3.7.9
- PyTorch version (GPU?): 1.8.1+cu111 (True)
- Tensorflow version (GPU?): 2.5.0 (True)
- Flax version (CPU?/GPU?/TPU?): 0.3.4 (cpu)
- Jax version: 0.2.13
- JaxLib version: 0.1.65
- Using GPU in script?: <fill in>
- Using distributed or parallel set-up in script?: <fill in>
```

Bạn cũng có thể thêm dấu `!` vào đầu lệnh `transformers-cli env` để thực thi nó từ một ô notebook, sau đó sao chép và dán kết quả vào đầu vấn đề của bạn.

### Gắn thẻ mọi người

Việc gắn thẻ mọi người bằng cách nhập `@` , theo sau là GitHub sẽ gửi cho họ thông báo để họ thấy vấn đề của bạn và có thể trả lời nhanh hơn. Sử dụng điều này một cách có kiểm duyệt, vì những người bạn gắn thẻ có thể không đánh giá cao việc được thông báo nếu đó là thứ mà họ không có liên kết trực tiếp. Nếu bạn đã xem các tệp nguồn liên quan đến lỗi của mình, bạn nên gắn thẻ người cuối cùng đã thực hiện thay đổi vào dòng mà bạn nghĩ là người chịu trách nhiệm cho vấn đề của bạn (bạn có thể tìm thông tin này bằng cách xem dòng đã nói trên GitHub, chọn nó, sau đó nhấp vào "View git blame" hay "Xem git đổ lỗi").

Nếu không, mẫu cung cấp đề xuất về những người cần gắn thẻ. Nói chung, đừng bao giờ gắn thẻ nhiều hơn ba người!

### Bao gồm một ví dụ có thể tái tạo

Nếu bạn đã cố gắng tạo một ví dụ độc lập tạo ra lỗi, bây giờ là lúc để đưa nó vào! Nhập một dòng có ba dấu gạch ngược theo sau là `python`, như thế này:

```python
```

sau đó dán vào ví dụ có thể tái tạo tối thiểu của bạn và nhập một dòng mới với ba dấu gạch ngược. Điều này sẽ đảm bảo mã của bạn được định dạng đúng.

Nếu bạn không quản lý để tạo một ví dụ có thể tái tạo, hãy giải thích theo các bước rõ ràng về cách bạn giải quyết vấn đề của mình. Bao gồm một liên kết đến một notebook Google Colab mà bạn gặp lỗi nếu có thể. Bạn càng chia sẻ nhiều thông tin, người bảo trì càng có thể trả lời bạn tốt hơn.

Trong mọi trường hợp, bạn nên sao chép và dán toàn bộ thông báo lỗi mà bạn đang nhận được. Nếu bạn đang làm việc trong Colab, hãy nhớ rằng một số khung có thể tự động được thu gọn trong dấu vết ngăn xếp, vì vậy hãy đảm bảo bạn mở rộng chúng trước khi sao chép. Giống như với mẫu mã, hãy đặt thông báo lỗi đó giữa hai dòng với ba dấu gạch ngược để nó được định dạng đúng.

### Mô tả hành vi kì vọng

Giải thích bằng một vài dòng những gì bạn mong đợi sẽ nhận được để những người bảo trì nắm bắt được đầy đủ vấn đề. Phần này nhìn chung khá rõ ràng, vì vậy nó nên nằm gọn trong một câu, nhưng trong một số trường hợp, bạn có thể có nhiều điều để nói.

## Và rồi chuyện gì xảy ra?

Sau khi vấn đề của bạn được gửi, hãy đảm bảo nhanh chóng kiểm tra mọi thứ có ổn không. Bạn có thể chỉnh sửa vấn đề nếu bạn mắc lỗi hoặc thậm chí thay đổi tiêu đề của vấn đề nếu bạn nhận ra vấn đề khác với những gì bạn nghĩ ban đầu.

Sẽ không có ích gì khi bạn không nhận được câu trả lời. Nếu không ai giúp bạn trong một vài ngày, có khả năng không ai có thể hiểu được vấn đề của bạn. Đừng ngần ngại quay lại ví dụ có thể tái tạo. Bạn có thể làm cho nó ngắn hơn và nhiều hơn vào điểm không? Nếu bạn không nhận được câu trả lời trong một tuần, bạn có thể nhẹ nhàng để lại tin nhắn yêu cầu trợ giúp, đặc biệt nếu bạn đã chỉnh sửa vấn đề của mình để bao gồm thêm thông tin về vấn đề.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter8/5.mdx" />

### Giới thiệu
https://huggingface.co/learn/course/vi/chapter8/1.md

# Giới thiệu

Bây giờ bạn đã biết cách giải quyết các tác vụ NLP phổ biến nhất với 🤗 Transformers, bạn sẽ có thể bắt đầu các dự án của riêng mình! Trong chương này, chúng ta sẽ khám phá những việc cần làm khi bạn gặp sự cố. Bạn sẽ học cách gỡ lỗi thành công mã hoặc quá trình huấn luyện của mình và cách yêu cầu cộng đồng trợ giúp nếu bạn không thể tự mình giải quyết vấn đề. Và nếu bạn cho rằng mình đã tìm thấy lỗi trong một trong các thư viện Hugging Faces, chúng tôi sẽ chỉ cho bạn cách tốt nhất để báo cáo lỗi đó để sự cố được giải quyết nhanh nhất có thể.

Chính xác hơn, trong chương này, bạn sẽ học:

- Điều đầu tiên cần làm khi bạn gặp lỗi
- Cách yêu cầu trợ giúp trên [diễn đàn](https://discuss.huggingface.co/)
- Cách gỡ lỗi đường dẫn huấn luyện của bạn
- Làm thế nào để viết một vấn đề tốt

Tất nhiên, không điều gì trong số này liên quan cụ thể đến 🤗 Transformers hoặc hệ sinh thái Hugging Face; các bài học từ chương này có thể áp dụng cho hầu hết các dự án nguồn mở!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter8/1.mdx" />

### Yêu cầu trợ giúp trên diễn đàn
https://huggingface.co/learn/course/vi/chapter8/3.md

# Yêu cầu trợ giúp trên diễn đàn

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter8/section3.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter8/section3.ipynb"},
]} />

<Youtube id="S2EEG3JIt2A"/>

[Diễn đàn Hugging Face](https://discuss.huggingface.co) là nơi tuyệt vời để nhận được sự giúp đỡ từ các nhóm nguồn mở và cộng đồng Hugging Face. Trang chủ luôn trông như sau:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forums.png" alt="The Hugging Face forums." width="100%"/>
</div>

Ở bên tay trái, bạn có thể thấy tất cả các danh mục mà các chủ đề khác nhau được nhóm lại, trong khi bên tay phải hiển thị các chủ đề gần đây nhất. Chủ đề là một bài đăng có chứa tiêu đề, danh mục và mô tả; nó khá giống với định dạng vấn đề GitHub mà chúng ta đã thấy khi tạo tập dữ liệu của riêng mình trong [Chương 5](/course/chapter5). Như tên cho thấy, danh mục [Beginners](https://discuss.huggingface.co/c/beginners/5) chủ yếu dành cho những người mới bắt đầu với hệ sinh thái và thư viện Hugging Face. Mọi câu hỏi trên bất kỳ thư viện nào đều được hoan nghênh ở đó, có thể là để gỡ lỗi một số mã hoặc để yêu cầu trợ giúp về cách thực hiện điều gì đó. (Điều đó nói rằng, nếu câu hỏi của bạn liên quan đến một thư viện cụ thể, bạn có thể nên chuyển đến danh mục thư viện tương ứng trên diễn đàn.)

Tương tự, danh mục [Intermediate](https://discuss.huggingface.co/c/intermediate/6)và [Research](https://discuss.huggingface.co/c/research/7) dành cho các câu hỏi nâng cao hơn , ví dụ về thư viện hoặc một số nghiên cứu NLP mới thú vị mà bạn muốn thảo luận.

Và đương nhiên, chúng ta cũng nên đề cập đến danh mục [Course](https://discuss.huggingface.co/c/course/20), nơi bạn có thể đặt bất kỳ câu hỏi nào liên quan đến khóa học Hugging Face!

Khi bạn đã chọn một danh mục, bạn sẽ sẵn sàng viết chủ đề đầu tiên của mình. Bạn có thể tìm thấy một số [hướng dẫn](https://discuss.huggingface.co/t/how-to-request-support/3128) trong diễn đàn về cách thực hiện việc này và trong phần này chúng ta sẽ xem xét một số tính năng tạo nên một chủ đề hay.

## Viết một bài đăng tốt trên diễn đàn

Như một ví dụ, giả sử rằng chúng ta đang cố gắng tạo các biểu diễn từ các bài viết trên Wikipedia để tạo một công cụ tìm kiếm tùy chỉnh. Như thường lệ, chúng ta tải tokenizer và mô hình như sau:

```python
from transformers import AutoTokenizer, AutoModel

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
model = AutoModel.from_pretrained(model_checkpoint)
```

Giờ giả sử ta đang cố nhúng toàn bộ phần này của [Wikipedia](https://en.wikipedia.org/wiki/Transformers) lên Transformers:

```python
text = """
Generation One is a retroactive term for the Transformers characters that
appeared between 1984 and 1993. The Transformers began with the 1980s Japanese
toy lines Micro Change and Diaclone. They presented robots able to transform
into everyday vehicles, electronic items or weapons. Hasbro bought the Micro
Change and Diaclone toys, and partnered with Takara. Marvel Comics was hired by
Hasbro to create the backstory; editor-in-chief Jim Shooter wrote an overall
story, and gave the task of creating the characthers to writer Dennis O'Neil.
Unhappy with O'Neil's work (although O'Neil created the name "Optimus Prime"),
Shooter chose Bob Budiansky to create the characters.

The Transformers mecha were largely designed by Shōji Kawamori, the creator of
the Japanese mecha anime franchise Macross (which was adapted into the Robotech
franchise in North America). Kawamori came up with the idea of transforming
mechs while working on the Diaclone and Macross franchises in the early 1980s
(such as the VF-1 Valkyrie in Macross and Robotech), with his Diaclone mechs
later providing the basis for Transformers.

The primary concept of Generation One is that the heroic Optimus Prime, the
villainous Megatron, and their finest soldiers crash land on pre-historic Earth
in the Ark and the Nemesis before awakening in 1985, Cybertron hurtling through
the Neutral zone as an effect of the war. The Marvel comic was originally part
of the main Marvel Universe, with appearances from Spider-Man and Nick Fury,
plus some cameos, as well as a visit to the Savage Land.

The Transformers TV series began around the same time. Produced by Sunbow
Productions and Marvel Productions, later Hasbro Productions, from the start it
contradicted Budiansky's backstories. The TV series shows the Autobots looking
for new energy sources, and crash landing as the Decepticons attack. Marvel
interpreted the Autobots as destroying a rogue asteroid approaching Cybertron.
Shockwave is loyal to Megatron in the TV series, keeping Cybertron in a
stalemate during his absence, but in the comic book he attempts to take command
of the Decepticons. The TV series would also differ wildly from the origins
Budiansky had created for the Dinobots, the Decepticon turned Autobot Jetfire
(known as Skyfire on TV), the Constructicons (who combine to form
Devastator),[19][20] and Omega Supreme. The Marvel comic establishes early on
that Prime wields the Creation Matrix, which gives life to machines. In the
second season, the two-part episode The Key to Vector Sigma introduced the
ancient Vector Sigma computer, which served the same original purpose as the
Creation Matrix (giving life to Transformers), and its guardian Alpha Trion.
"""

inputs = tokenizer(text, return_tensors="pt")
logits = model(**inputs).logits
```

```python output
IndexError: index out of range in self
```

Rất tiếc, chúng ta đã gặp sự cố - và thông báo lỗi khó hiểu hơn nhiều so với những thông báo chúng ta thấy trong [phần 2](/course/chapter8/section2)! Chúng ta không thể thực hiện được toàn bộ quá trình truy vết, vì vậy chúng ta quyết định chuyển sang diễn đàn Hugging Face để được trợ giúp. Làm thế nào chúng ta có thể tạo ra chủ đề?

Để bắt đầu, chúng ta cần nhấp vào nút "New Topic" hay "Chủ đề mới" ở góc trên bên phải (lưu ý rằng để tạo chủ đề, chúng ta cần đăng nhập):

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forums-new-topic.png" alt="Creating a new forum topic." width="100%"/>
</div>

Thao tác này sẽ hiển thị một giao diện viết, nơi chúng ta có thể nhập tiêu đề của chủ đề của mình, chọn một danh mục và soạn thảo nội dung:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic01.png" alt="The interface for creating a forum topic." width="100%"/>
</div>

Vì lỗi dường như chỉ xảy ra với 🤗 Transformers, nên chúng ta sẽ chọn lỗi này cho danh mục. Nỗ lực đầu tiên của chúng ta trong việc giải thích vấn đề có thể trông giống như sau:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic02.png" alt="Drafting the content for a new forum topic." width="100%"/>
</div>

Mặc dù chủ đề này chứa thông báo lỗi mà chúng tôi cần trợ giúp, nhưng có một số vấn đề với cách viết:

1. Tiêu đề không mang tính mô tả cao, vì vậy bất kỳ ai duyệt diễn đàn sẽ không thể biết chủ đề là gì nếu không đọc phần nội dung.
2. Phần thân không cung cấp đủ thông tin về _nơi_ bắt nguồn lỗi và _cách_ để tạo lại lỗi đó.
3. Chủ đề gắn thẻ trực tiếp một vài người với giọng điệu hơi khắt khe.

Các chủ đề như thế này không có khả năng nhận được câu trả lời nhanh (nếu họ nhận được một câu trả lời nào đó), vì vậy hãy xem cách chúng ta có thể cải thiện nó. Chúng ta sẽ bắt đầu với vấn đề đầu tiên là chọn một tiêu đề hay.

### Choosing a descriptive title

Nếu bạn đang cố gắng nhận trợ giúp về một lỗi trong mã của mình, một nguyên tắc chung là đưa đủ thông tin vào tiêu đề để người khác có thể nhanh chóng xác định xem họ có nghĩ rằng họ có thể trả lời câu hỏi của bạn hay không. Trong ví dụ đang chạy của mình, chúng ta biết tên của ngoại lệ đang được nêu ra và có một số gợi ý rằng nó được kích hoạt trong phần truyền thẳng của mô hình, nơi chúng tôi gọi là `model(**inputs)`. Để thông báo điều này, một tiêu đề có thể có là:

> Source of IndexError in the AutoModel forward pass?

hay 

> Nguồn của IndexError trong thẻ chuyển tiếp AutoModel?

Tiêu đề này cho người đọc biết bạn nghĩ rằng lỗi đến từ _đâu_ và nếu họ đã gặp phải `IndexError` trước đó, thì rất có thể họ sẽ biết cách gỡ lỗi nó. Tất nhiên, tiêu đề có thể là bất kỳ thứ gì bạn muốn và các biến thể khác như:

> Why does my model produce an IndexError?

hay 

> Tại sao mô hình của tôi tạo ra một IndexError?

cũng có thể ổn. Bây giờ chúng ta đã có một tiêu đề mô tả, hãy xem cách cải thiện nội dụng phần thân bài.

### Định dạng các đoạn mã của bạn

Đọc mã nguồn đã đủ khó trong IDE, nhưng còn khó hơn khi mã được sao chép và dán dưới dạng văn bản thuần túy! May mắn thay, các diễn đàn về Hugging Face hỗ trợ việc sử dụng Markdown, vì vậy bạn nên luôn đặt các khối mã của mình bằng ba dấu gạch ngược (```) để dễ đọc hơn. Hãy làm điều này để sửa chữa thông báo lỗi - và trong khi chúng ta xử lý nó, hãy làm cho phần nội dung lịch sự hơn một chút so với phiên bản gốc của mình:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic03.png" alt="Our revised forum topic, with proper code formatting." width="100%"/>
</div>

Như bạn có thể thấy trong ảnh chụp màn hình, việc bao bọc các khối mã trong dấu gạch ngược sẽ chuyển văn bản thô thành mã được định dạng, hoàn chỉnh với kiểu màu! Cũng lưu ý rằng các dấu gạch ngược đơn lẻ có thể được sử dụng để định dạng các biến nội tuyến, giống như chúng tôi đã làm cho `distilbert-base-unsased`. Chủ đề này có vẻ tốt hơn nhiều và với một chút may mắn, chúng ta có thể tìm thấy ai đó trong cộng đồng có thể đoán được lỗi là gì. Tuy nhiên, thay vì dựa vào may mắn, chúng ta hãy làm cho cuộc sống dễ dàng hơn bằng cách đưa vào chi tiết đầy đủ các truy vết của nó!

### Bao gồm toàn bộ truy vết

Vì dòng cuối cùng của bản truy vết thường đủ để gỡ lỗi đoạn mã của riêng bạn, nên bạn có thể chỉ cung cấp dòng đó trong chủ đề của mình để "tiết kiệm dung lượng". Mặc dù có chủ ý tốt, điều này thực sự khiến người khác có thể _khó_ gỡ lỗi vấn đề _hơn_ vì thông tin cao hơn trong bản truy xuất có thể thực sự hữu ích. Vì vậy, một phương pháp hay là sao chép và dán _toàn bộ_ dấu vết, đồng thời đảm bảo rằng nó được định dạng độc đáo. Vì những lần truy xuất này có thể mất nhiều thời gian, một số người thích hiển thị chúng sau khi họ đã giải thích mã nguồn. Làm thôi nào. Bây giờ, chủ đề diễn đàn của chúng ta trông giống như sau:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic04.png" alt="Our example forum topic, with the complete traceback." width="100%"/>
</div>

Điều này có nhiều thông tin hơn và một người đọc cẩn thận có thể chỉ ra rằng vấn đề dường như là do chuyển một đầu vào dài vì dòng này trong bản truy xuất:

> Token indices sequence length is longer than the specified maximum sequence length for this model (583 > 512).

Tuy nhiên, chúng ta có thể khiến mọi thứ trở nên dễ dàng hơn với họ bằng cách cung cấp mã thực đã gây ra lỗi. Hãy làm điều đó ngay bây giờ.

### Cung cấp một ví dụ có thể tái tạo

Nếu bạn đã từng cố gắng gỡ lỗi đoạn mã của người khác, trước tiên có thể bạn đã cố gắng tạo lại sự cố mà họ đã báo cáo để bạn có thể bắt đầu làm việc theo cách của mình thông qua truy xuất để xác định lỗi. Nó không khác gì khi nói đến (hoặc cung cấp) hỗ trợ trên các diễn đàn, vì vậy sẽ thực sự hữu ích nếu bạn có thể cung cấp một ví dụ nhỏ mô tả lại lỗi. Một nửa thời gian, chỉ cần đi qua bài tập này sẽ giúp bạn nhận ra điều gì đang xảy ra. Trong mọi trường hợp, phần còn thiếu trong ví dụ của chúng ta là hiển thị _các đầu vào_ mà ta đã cung cấp cho mô hình. Làm điều đó cho chúng ta một cái gì đó giống như ví dụ đã hoàn thành sau:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic05.png" alt="The final version of our forum topic." width="100%"/>
</div>

Chủ đề này hiện chứa khá nhiều thông tin và nó được viết theo cách có nhiều khả năng thu hút sự chú ý của cộng đồng và nhận được câu trả lời hữu ích. Với những hướng dẫn cơ bản này, giờ đây bạn có thể tạo các chủ đề tuyệt vời để tìm câu trả lời cho các câu hỏi về 🤗 Transformers của mình!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter8/3.mdx" />

### Phần 2 đã hoàn thành!
https://huggingface.co/learn/course/vi/chapter8/6.md

# Phần 2 đã hoàn thành!

Xin chúc mừng, bạn đã vượt qua phần thứ hai của khóa học! Chúng tôi đang tích cực làm việc trên bản thứ ba, vì vậy hãy đăng ký [bản tin](https://huggingface.curated.co/) của chúng tôi để đảm bảo bạn không bỏ lỡ bản phát hành của nó.

Bây giờ bạn có thể giải quyết một loạt các tác vụ NLP và tinh chỉnh hoặc huấn luyện trước một mô hình trên chúng. Đừng quên chia sẻ kết quả của bạn với cộng đồng trên [Model Hub](https://huggingface.co/models).

Chúng tôi rất nóng lòng được xem bạn sẽ xây dựng được những gì với kiến thức mà bạn đã thu được!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter8/6.mdx" />

### Gỡ lỗi quy trình huấn luyện
https://huggingface.co/learn/course/vi/chapter8/4.md

# Gỡ lỗi quy trình huấn luyện

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter8/section4.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter8/section4.ipynb"},
]} />

Bạn đã viết một kịch bản tuyệt đẹp để huấn luyện hoặc tinh chỉnh một mô hình trong một tác vụ nhất định, tuân thủ một cách nghiêm túc lời khuyên từ [Chương 7](/course/chapter7). Nhưng khi bạn khởi chạy lệnh `trainr.train()`, một điều kinh khủng xảy ra: bạn gặp lỗi 😱! Hoặc tệ hơn, mọi thứ dường như ổn và quá trình huấn luyện chạy mà không có lỗi, nhưng mô hình kết quả là tồi tệ. Trong phần này, chúng tôi sẽ chỉ cho bạn những gì bạn có thể làm để gỡ lỗi các loại vấn đề này.

## Gỡ lỗi quy trình huấn luyện

<Youtube id="L-WSwUWde1U"/>

Vấn đề khi bạn gặp lỗi trong `trainr.train()` có thể đến từ nhiều nguồn, vì `Trainer` thường tập hợp rất nhiều thứ lại với nhau. Nó chuyển đổi bộ dữ liệu thành các dataloader, do đó, vấn đề có thể là một cái gì đó sai trong bộ dữ liệu của bạn hoặc một số vấn đề khi cố gắng kết hợp hàng loạt các phần tử của bộ dữ liệu với nhau. Sau đó, nó lấy một loạt dữ liệu và đưa nó vào mô hình, vì vậy vấn đề có thể nằm ở mã mô hình. Sau đó, nó tính toán các độ dốc và thực hiện bước tối ưu hóa, vì vậy vấn đề cũng có thể nằm trong trình tối ưu hóa của bạn. Và ngay cả khi mọi thứ diễn ra tốt đẹp cho quá trình huấn luyện, vẫn có thể xảy ra sự cố trong quá trình đánh giá nếu có vấn đề với chỉ số của bạn.

Cách tốt nhất để gỡ lỗi phát sinh trong `trainr.train()` là đi qua toàn pipeline này theo cách thủ công để xem mọi thứ diễn ra như thế nào. Sau đó, lỗi thường rất dễ giải quyết.

Để chứng minh điều này, chúng ta sẽ sử dụng tập lệnh (cố gắng) tinh chỉnh mô hình DistilBERT trên [tập dữ liệu MNLI](https://huggingface.co/datasets/glue):

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    return metric.compute(predictions=predictions, references=labels)


trainer = Trainer(
    model,
    args,
    train_dataset=raw_datasets["train"],
    eval_dataset=raw_datasets["validation_matched"],
    compute_metrics=compute_metrics,
)
trainer.train()
```

Nếu bạn cố gắng thực thi nó, bạn sẽ gặp phải một lỗi khá khó hiểu:

```python out
'ValueError: You have to specify either input_ids or inputs_embeds'
```

### Kiểm tra dữ liệu của bạn

Điều này không cần phải nói, nhưng nếu dữ liệu của bạn bị hỏng, `Trainer` sẽ không thể tạo ra các lô chứ đừng nói đến việc huấn luyện mô hình của bạn. Vì vậy, điều đầu tiên, bạn cần phải xem xét những gì bên trong bộ huấn luyện của bạn.

Để tránh mất vô số giờ để cố gắng sửa một cái gì đó không phải là nguồn gốc của lỗi, chúng tôi khuyên bạn nên sử dụng `trainr.train_dataset` để kiểm tra. Vì vậy, hãy làm điều đó ở đây:

```py
trainer.train_dataset[0]
```

```python out
{'hypothesis': 'Product and geography are what make cream skimming work. ',
 'idx': 0,
 'label': 1,
 'premise': 'Conceptually cream skimming has two basic dimensions - product and geography.'}
```

Bạn có nhận thấy điều gì đó sai không? Điều này, cùng với thông báo lỗi về việc thiếu `input_ids`, sẽ khiến bạn nhận ra đó là các văn bản chứ không phải số mà mô hình có thể hiểu được. Ở đây, lỗi ban đầu rất dễ gây hiểu nhầm bởi vì `Trainer` tự động loại bỏ các cột không khớp với đặc trưng của mô hình (nghĩa là các tham số mà mô hình mong đợi). Điều đó có nghĩa là ở đây, mọi thứ ngoại trừ nhãn đều bị loại bỏ. Do đó, không có vấn đề gì với việc tạo các lô và sau đó gửi chúng đến mô hình, điều này do đó phàn nàn rằng nó không nhận được đầu vào thích hợp.

Tại sao dữ liệu không được xử lý? Chúng ta đã sử dụng phương thức `Dataset.map()` trên các tập dữ liệu để áp dụng tokenizer trên mỗi mẫu. Nhưng nếu bạn xem kỹ mã, bạn sẽ thấy rằng chúng ta đã mắc sai lầm khi chuyển các bộ huấn luyện và kiểm định cho `Trainer`. Thay vì sử dụng `tokenized_datasets` ở đây, chúng ta đã sử dụng `raw_datasets` 🤦. Vì vậy, hãy cùng sửa chữa điều này!

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    return metric.compute(predictions=predictions, references=labels)


trainer = Trainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation_matched"],
    compute_metrics=compute_metrics,
)
trainer.train()
```

Mã mới này bây giờ sẽ đưa ra một lỗi khác (có tiến triển!):

```python out
'ValueError: expected sequence of length 43 at dim 1 (got 37)'
```

Nhìn vào dấu truy vết, chúng ta có thể thấy lỗi xảy ra trong bước đối chiếu dữ liệu:

```python out
~/git/transformers/src/transformers/data/data_collator.py in torch_default_data_collator(features)
    105                 batch[k] = torch.stack([f[k] for f in features])
    106             else:
--> 107                 batch[k] = torch.tensor([f[k] for f in features])
    108 
    109     return batch
```

Vì vậy, chúng ta nên chuyển sang điều đó. Tuy nhiên, trước khi thực hiện, chúng ta hãy hoàn thành việc kiểm tra dữ liệu của mình, để chắc chắn rằng nó chính xác 100%.

Một điều bạn luôn nên làm khi gỡ lỗi một phiên huấn luyện là xem xét các đầu vào được giải mã của mô hình của bạn. Chúng ta không thể hiểu được những con số mà chúng ta cung cấp trực tiếp cho nó, vì vậy chúng ta nên xem những con số đó đại diện cho điều gì. Ví dụ: trong thị giác máy tính, điều đó có nghĩa là nhìn vào hình ảnh được giải mã của các pixel bạn chuyển qua, trong lời nói, điều đó có nghĩa là nghe các mẫu âm thanh được giải mã và đối với ví dụ NLP của chúng ta ở đây, điều đó có nghĩa là sử dụng trình tokenizer để giải mã đầu vào:

```py
tokenizer.decode(trainer.train_dataset[0]["input_ids"])
```

```python out
'[CLS] conceptually cream skimming has two basic dimensions - product and geography. [SEP] product and geography are what make cream skimming work. [SEP]'
```

Vì vậy, điều đó có vẻ chính xác. Bạn nên làm điều này cho tất cả các phím trong đầu vào:

```py
trainer.train_dataset[0].keys()
```

```python out
dict_keys(['attention_mask', 'hypothesis', 'idx', 'input_ids', 'label', 'premise'])
```

Lưu ý rằng các khóa không tương ứng với đầu vào được mô hình chấp nhận sẽ tự động bị loại bỏ, vì vậy ở đây chúng tôi sẽ chỉ giữ lại `input_ids`, `attention_mask`, và `label` (sẽ được đổi tên thành `labels`). Để kiểm tra kỹ mô hình, bạn có thể in loại mô hình của mình, sau đó kiểm tra tài liệu của nó:

```py
type(trainer.model)
```

```python out
transformers.models.distilbert.modeling_distilbert.DistilBertForSequenceClassification
```

Vì vậy, trong trường hợp của mình, chúng ta có thể kiểm tra các tham số được chấp nhận trên [trang này](https://huggingface.co/transformers/model_doc/distilbert.html#distilbertforsequenceclassification). `Trainer` cũng sẽ ghi lại các cột mà nó đang loại bỏ.

Chúng ta đã kiểm tra xem các ID đầu vào có chính xác hay không bằng cách giải mã chúng. Tiếp theo là `attention_mask`:

```py
trainer.train_dataset[0]["attention_mask"]
```

```python out
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
```

Vì chúng ta không áp dụng đệm trong quá trình tiền xử lý của mình, điều này có vẻ hoàn toàn tự nhiên. Để đảm bảo không có vấn đề gì với attention mask đó, hãy kiểm tra xem nó có cùng độ dài với ID đầu vào của chúng ta không:

```py
len(trainer.train_dataset[0]["attention_mask"]) == len(
    trainer.train_dataset[0]["input_ids"]
)
```

```python out
True
```

Tốt đấy! Cuối cùng, hãy kiểm tra nhãn của mình:

```py
trainer.train_dataset[0]["label"]
```

```python out
1
```

Giống như các ID đầu vào, đây là một con số không thực sự có ý nghĩa. Như chúng ta đã thấy trước đây, ánh xạ giữa các số nguyên và tên nhãn được lưu trữ bên trong thuộc tính `names` của *đặc trưng* tương ứng của tập dữ liệu:

```py
trainer.train_dataset.features["label"].names
```

```python out
['entailment', 'neutral', 'contradiction']
```

Vì vậy, `1` có nghĩa là `neutral`, có nghĩa là hai câu chúng ta đã thấy ở trên không mâu thuẫn với nhau và câu đầu tiên không bao hàm câu thứ hai. Điều đó có vẻ đúng!

Chúng ta không có token ID ở đây, vì DistilBERT không mong đợi chúng; nếu bạn có một số trong mô hình của mình, bạn cũng nên đảm bảo rằng chúng khớp đúng với vị trí của câu đầu tiên và câu thứ hai trong đầu vào.

<Tip>

✏️ **Đến lượt bạn!** Kiểm tra xem mọi thứ có chính xác không với phần tử thứ hai của tập dữ liệu huấn luyện.

</Tip>

Chúng ta chỉ thực hiện kiểm tra tập huấn luyện ở đây, nhưng tất nhiên bạn nên kiểm tra kỹ các tập kiểm định và kiểm tra theo cùng một cách.

Bây giờ chúng ta biết bộ dữ liệu của mình trông ổn, đã đến lúc kiểm tra bước tiếp theo của quy trình huấn luyện.

### Từ bộ dữ liệu thành dataloader

Điều tiếp theo có thể xảy ra sai sót trong quy trình huấn luyện là khi `Trainer` cố gắng tạo các lô từ tập huấn luyện hoặc kiểm định. Khi bạn chắc chắn rằng tập dữ liệu của `Trainer` là chính xác, bạn có thể thử tạo một loạt theo cách thủ công bằng cách thực hiện như sau (thay thế `train` bằng `eval` cho dataloader kiểm định):

```py
for batch in trainer.get_train_dataloader():
    break
```

Mã này tạo ra dataloader huấn luyện, sau đó lặp qua nó, dừng lại ở lần lặp đầu tiên. Nếu mã thực thi mà không có lỗi, bạn có lô huấn luyện đầu tiên mà bạn có thể kiểm tra và nếu mã lỗi xảy ra, bạn biết chắc chắn vấn đề nằm trong dataloader, như trường hợp ở đây:

```python out
~/git/transformers/src/transformers/data/data_collator.py in torch_default_data_collator(features)
    105                 batch[k] = torch.stack([f[k] for f in features])
    106             else:
--> 107                 batch[k] = torch.tensor([f[k] for f in features])
    108 
    109     return batch

ValueError: expected sequence of length 45 at dim 1 (got 76)
```

Việc kiểm tra khung cuối cùng của quá trình truy xuất sẽ đủ để cung cấp cho bạn manh mối, nhưng hãy tìm hiểu kỹ hơn một chút. Hầu hết các vấn đề trong quá trình tạo lô đều phát sinh do việc đối chiếu các ví dụ thành một lô duy nhất, vì vậy, điều đầu tiên cần kiểm tra khi nghi ngờ là `collate_fn` mà ` DataLoader` của bạn đang sử dụng:

```py
data_collator = trainer.get_train_dataloader().collate_fn
data_collator
```

```python out
<function transformers.data.data_collator.default_data_collator(features: List[InputDataClass], return_tensors='pt') -> Dict[str, Any]>
```

Vì vậy, đây là `default_data_collator`, nhưng đó không phải là những gì chúng ta muốn trong trường hợp này. Chúng ta muốn đưa các ví dụ của mình vào câu dài nhất trong lô, được thực hiện bởi trình đối chiếu `DataCollatorWithPadding`. Và trình đối chiếu dữ liệu này được cho là được sử dụng theo mặc định bởi `Trainer`, vậy tại sao nó không được sử dụng ở đây?

Câu trả lời là vì chúng ta đã không chuyển `tokenizer` cho `Trainer`, vì vậy nó không thể tạo `DataCollatorWithPadding` mà chúng ta muốn. Trong thực tế, bạn đừng bao giờ ngần ngại chuyển một cách rõ ràng bộ đối chiếu dữ liệu mà bạn muốn sử dụng, để đảm bảo rằng bạn tránh được những loại lỗi này. Hãy điều chỉnh mã của chúng ta để thực hiện chính xác điều đó:

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    DataCollatorWithPadding,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    return metric.compute(predictions=predictions, references=labels)


data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

trainer = Trainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation_matched"],
    compute_metrics=compute_metrics,
    data_collator=data_collator,
    tokenizer=tokenizer,
)
trainer.train()
```

Tin tốt? Chúng ta không gặp lỗi như trước nữa, đó chắc chắn là sự tiến bộ. Các tin xấu? Thay vào đó, chúng ta nhận được một lỗi CUDA khét tiếng:

```python out
RuntimeError: CUDA error: CUBLAS_STATUS_ALLOC_FAILED when calling `cublasCreate(handle)`
```

Điều này thật tệ vì lỗi CUDA nói chung rất khó gỡ lỗi. Chúng ta sẽ xem trong một phút nữa cách giải quyết vấn đề này, nhưng trước tiên hãy kết thúc phân tích của chúng ta về tạo lô.

Nếu bạn chắc chắn trình đối chiếu dữ liệu của mình là đúng, bạn nên thử áp dụng nó trên một vài mẫu của tập dữ liệu của mình:

```py
data_collator = trainer.get_train_dataloader().collate_fn
batch = data_collator([trainer.train_dataset[i] for i in range(4)])
```

Mã này sẽ không thành công vì `train_dataset` chứa các cột chuỗi mà `Trainer` thường loại bỏ. Bạn có thể xóa chúng theo cách thủ công hoặc nếu bạn muốn sao chép chính xác những gì mà `Trainer` đang làm ở hậu trường, bạn có thể gọi phương thức riêng  `Trainer._remove_unused_columns()` để thực hiện điều đó:

```py
data_collator = trainer.get_train_dataloader().collate_fn
actual_train_set = trainer._remove_unused_columns(trainer.train_dataset)
batch = data_collator([actual_train_set[i] for i in range(4)])
```

Sau đó, bạn sẽ có thể gỡ lỗi theo cách thủ công những gì xảy ra bên trong bộ đối chiếu dữ liệu nếu lỗi vẫn tiếp diễn.

Bây giờ chúng ta đã gỡ lỗi quy trình tạo lô, đã đến lúc chuyển qua mô hình!

### Xem qua mô hình

Bạn sẽ có thể nhận được một lô bằng cách thực hiện lệnh sau:

```py
for batch in trainer.get_train_dataloader():
    break
```

Nếu bạn đang chạy mã này trong notebook, bạn có thể gặp lỗi CUDA tương tự như lỗi đã thấy trước đó, trong trường hợp đó, bạn cần khởi động lại notebook của mình và thực hiện lại đoạn mã cuối cùng mà không có dòng `trainer.train()`. Đó là điều khó chịu thứ hai về lỗi CUDA: chúng phá vỡ kernel của bạn một cách không thể khắc phục được. Điều khó chịu nhất về chúng là thực tế là chúng rất khó để gỡ lỗi.

Tại sao vậy? Nó liên quan đến cách hoạt động của GPU. Chúng cực kỳ hiệu quả trong việc thực hiện song song nhiều thao tác, nhưng hạn chế là khi một trong các lệnh đó dẫn đến lỗi, bạn sẽ không biết ngay lập tức. Chỉ khi chương trình gọi đồng bộ hóa nhiều quy trình trên GPU thì nó mới nhận ra có gì đó không ổn, vì vậy lỗi thực sự được phát sinh ở một nơi không liên quan gì đến những gì đã tạo ra nó. Ví dụ, nếu chúng ta xem lại lần truy xuất trước của mình, lỗi đã được phát sinh trong quá trình truyền ngược, nhưng chúng ta sẽ thấy trong một phút rằng nó thực sự bắt nguồn từ một cái gì đó trong truyền thẳng.

Vậy làm cách nào để gỡ những lỗi đó? Câu trả lời rất dễ dàng: chúng tôi không. Trừ khi lỗi CUDA của bạn là lỗi hết bộ nhớ (có nghĩa là không có đủ bộ nhớ trong GPU của bạn), bạn nên quay lại CPU để gỡ lỗi.

Để thực hiện điều này trong trường hợp của mình, chúng ta chỉ cần đặt mô hình trở lại CPU và gọi nó vào lô của mình - lô được trả về bởi `DataLoader` vẫn chưa được chuyển đến GPU:

```python
outputs = trainer.model.cpu()(**batch)
```

```python out
~/.pyenv/versions/3.7.9/envs/base/lib/python3.7/site-packages/torch/nn/functional.py in nll_loss(input, target, weight, size_average, ignore_index, reduce, reduction)
   2386         )
   2387     if dim == 2:
-> 2388         ret = torch._C._nn.nll_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index)
   2389     elif dim == 4:
   2390         ret = torch._C._nn.nll_loss2d(input, target, weight, _Reduction.get_enum(reduction), ignore_index)

IndexError: Target 2 is out of bounds.
```

Vì vậy, bức tranh ngày càng rõ ràng. Thay vì gặp lỗi CUDA, bây giờ chúng ta có `IndexError` trong tính toán mất mát (vì vậy không liên quan gì đến lan truyền ngược, như ta đã nói trước đó). Chính xác hơn, chúng ta có thể thấy rằng nhãn 2 tạo ra lỗi, vì vậy đây là thời điểm rất tốt để kiểm tra số lượng nhãn của mô hình của ta:

```python
trainer.model.config.num_labels
```

```python out
2
```

Với hai nhãn, chỉ có 0 và 1 được phép làm nhãn, nhưng theo thông báo lỗi, chúng tôi nhận được 2. Nhận được 2 thực ra là bình thường: nếu chúng ta nhớ tên nhãn mà chúng ta đã trích xuất trước đó, có ba, vì vậy chúng ta có chỉ số 0 , 1 và 2 trong tập dữ liệu của mình. Vấn đề là chúng ta đã không nói điều đó với mô hình của mình, mô hình này lẽ ra phải được tạo với ba nhãn. Vì vậy, chúng ta hãy khắc phục điều đó!

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    DataCollatorWithPadding,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=3)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    return metric.compute(predictions=predictions, references=labels)


data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

trainer = Trainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation_matched"],
    compute_metrics=compute_metrics,
    data_collator=data_collator,
    tokenizer=tokenizer,
)
```

Chúng ta chưa bao gồm dòng `trainer.train()`, để dành thời gian kiểm tra xem mọi thứ có ổn không. Nếu chúng ta yêu cầu một lô và chuyển nó vào mô hình của mình, nó hiện hoạt động mà không có lỗi!

```py
for batch in trainer.get_train_dataloader():
    break

outputs = trainer.model.cpu()(**batch)
```

Bước tiếp theo là quay lại GPU và kiểm tra xem mọi thứ vẫn hoạt động không:

```py
import torch

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
batch = {k: v.to(device) for k, v in batch.items()}

outputs = trainer.model.to(device)(**batch)
```

Nếu bạn vẫn gặp lỗi, hãy đảm bảo rằng bạn khởi động lại notebook của mình và chỉ thực thi phiên bản cuối cùng của tập lệnh.

### Thực hiện một bước tối ưu hóa

Bây giờ ta biết rằng chúng ta có thể xây dựng các lô thực sự đi qua mô hình, chúng ta đã sẵn sàng cho bước tiếp theo của quy trình huấn luyện: tính toán độ dốc và thực hiện bước tối ưu hóa.

Phần đầu tiên chỉ là vấn đề gọi phương thức `backward()` khi tính mất mát:

```py
loss = outputs.loss
loss.backward()
```

Rất hiếm khi gặp lỗi ở giai đoạn này, nhưng nếu bạn gặp lỗi, hãy đảm bảo quay lại CPU để nhận được thông báo lỗi hữu ích.

To perform the optimization step, we just need to create the `optimizer` and call its `step()` method:

```py
trainer.create_optimizer()
trainer.optimizer.step()
```

Một lần nữa, nếu bạn đang sử dụng trình tối ưu hóa mặc định trong `Trainer`, bạn sẽ không gặp lỗi ở giai đoạn này, nhưng nếu bạn có trình tối ưu hóa tùy chỉnh, có thể có một số vấn đề cần gỡ lỗi ở đây. Đừng quên quay lại CPU nếu bạn gặp lỗi CUDA lạ ở giai đoạn này. Nói về lỗi CUDA, trước đó chúng ta đã đề cập đến một trường hợp đặc biệt. Bây giờ chúng ta hãy xem xét điều đó.

### Xử lý lỗi hết bộ nhớ CUDA

Bất cứ khi nào bạn nhận được thông báo lỗi bắt đầu bằng `RuntimeError: CUDA out of memory`, điều này cho biết bạn đã hết bộ nhớ GPU. Điều này không được liên kết trực tiếp với mã của bạn và nó có thể xảy ra với một tập lệnh chạy hoàn toàn tốt. Lỗi này có nghĩa là bạn đã cố gắng đưa quá nhiều thứ vào bộ nhớ trong của GPU và dẫn đến lỗi. Giống như với các lỗi CUDA khác, bạn sẽ cần khởi động lại kernel của mình để ở vị trí mà bạn có thể chạy lại quá trình huấn luyện của mình.

Để giải quyết vấn đề này, bạn chỉ cần sử dụng ít dung lượng GPU hơn - điều mà nói thì dễ hơn làm. Trước tiên, hãy đảm bảo rằng bạn không có hai mô hình GPU trên cùng một lúc (tất nhiên là trừ khi đó là yêu cầu cho vấn đề của bạn). Sau đó, bạn có thể nên giảm kích thước lô của mình, vì nó ảnh hưởng trực tiếp đến kích thước của tất cả các đầu ra trung gian của mô hình và độ dốc của chúng. Nếu sự cố vẫn tiếp diễn, hãy xem xét sử dụng phiên bản mô hình nhỏ hơn của bạn.

<Tip>

Trong phần tiếp theo của khóa học, chúng ta sẽ xem xét các kỹ thuật nâng cao hơn có thể giúp bạn giảm dung lượng bộ nhớ và cho phép bạn tinh chỉnh các mô hình lớn nhất.

</Tip>

### Đánh giá mô hình

Bây giờ chúng tôi đã giải quyết tất cả các vấn đề với mã của mình, mọi thứ đều hoàn hảo và quá trình huấn luyện sẽ diễn ra suôn sẻ, phải không? Không quá nhanh! Nếu bạn chạy lệnh `trainer.train()`, lúc đầu mọi thứ sẽ ổn, nhưng sau một thời gian, bạn sẽ nhận được những điều sau:

```py
# Quá trình này sẽ mất nhiều thời gian và xảy ra lỗi, vì vậy bạn không nên chạy ô này
trainer.train()
```

```python out
TypeError: only size-1 arrays can be converted to Python scalars
```

Bạn sẽ nhận ra lỗi này xuất hiện trong giai đoạn kiểm định, vì vậy đây là điều cuối cùng chúng tôi sẽ cần gỡ lỗi.

Bạn có thể chạy vòng lặp kiểm định của `Trainer` một cách độc lập để hình thành khóa huấn luyện như sau:

```py
trainer.evaluate()
```

```python out
TypeError: only size-1 arrays can be converted to Python scalars
```

<Tip>

💡 Bạn phải luôn đảm bảo rằng mình có thể chạy `trainr.evaluate()` trước khi khởi chạy `trainer.train()`, để tránh lãng phí nhiều tài nguyên máy tính trước khi gặp lỗi.

</Tip>

Trước khi cố gắng gỡ lỗi một vấn đề trong vòng kiểm định, trước tiên bạn nên đảm bảo rằng bạn đã xem xét dữ liệu, có thể tạo một lô đúng cách và có thể chạy mô hình của bạn trên đó. Chúng ta đã hoàn thành tất cả các bước đó, vì vậy mã sau có thể được thực thi mà không có lỗi:

```py
for batch in trainer.get_eval_dataloader():
    break

batch = {k: v.to(device) for k, v in batch.items()}

with torch.no_grad():
    outputs = trainer.model(**batch)
```

Lỗi xuất hiện sau đó, vào cuối giai đoạn đánh giá và nếu chúng ta xem lại bản ghi lại, chúng ta thấy điều này:

```python trace
~/git/datasets/src/datasets/metric.py in add_batch(self, predictions, references)
    431         """
    432         batch = {"predictions": predictions, "references": references}
--> 433         batch = self.info.features.encode_batch(batch)
    434         if self.writer is None:
    435             self._init_writer()
```

Điều này cho chúng tôi biết rằng lỗi bắt nguồn từ mô-đun `datasets/metric.py` - vì vậy đây là sự cố với hàm `compute_metrics()` của mình. Nó cần một bộ dữ liệu với các logits và các nhãn dưới dạng mảng NumPy, vì vậy chúng ta hãy thử cung cấp cho nó rằng:

```py
predictions = outputs.logits.cpu().numpy()
labels = batch["labels"].cpu().numpy()

compute_metrics((predictions, labels))
```

```python out
TypeError: only size-1 arrays can be converted to Python scalars
```

Chúng ta nhận được cùng một lỗi, vì vậy vấn đề chắc chắn nằm ở hàm đó. Nếu chúng ta nhìn lại mã của nó, chúng ta thấy nó chỉ chuyển tiếp các  `predictions` và `labels`  đến `metric.compute()`. Vậy có vấn đề gì với phương pháp đó không? Không hẳn vậy. Chúng ta hãy xem nhanh các hình dạng:

```py
predictions.shape, labels.shape
```

```python out
((8, 3), (8,))
```

Các dự đoán của chúng tôi vẫn là logit, không phải dự đoán thực tế, đó là lý do tại sao số liệu trả về lỗi (hơi tối nghĩa) này. Việc sửa chữa khá dễ dàng; chúng ta chỉ cần thêm một argmax trong hàm `compute_metrics()`:

```py
import numpy as np


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    predictions = np.argmax(predictions, axis=1)
    return metric.compute(predictions=predictions, references=labels)


compute_metrics((predictions, labels))
```

```python out
{'accuracy': 0.625}
```

Bây giờ lỗi của chúng ta đã được sửa chữa! Đây là lần cuối cùng, vì vậy kịch bản của chúng ta bây giờ sẽ đào tạo một mô hình đúng cách.

Để tham khảo, đây là tập lệnh hoàn toàn cố định:

```py
import numpy as np
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    DataCollatorWithPadding,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=3)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    predictions = np.argmax(predictions, axis=1)
    return metric.compute(predictions=predictions, references=labels)


data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

trainer = Trainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation_matched"],
    compute_metrics=compute_metrics,
    data_collator=data_collator,
    tokenizer=tokenizer,
)
trainer.train()
```

Trong trường hợp này, không còn vấn đề gì nữa và tập lệnh của chúng ta sẽ tinh chỉnh một mô hình sẽ cho kết quả hợp lý. Nhưng chúng ta có thể làm gì khi quá trình huấn luyện diễn ra mà không có bất kỳ lỗi nào, và mô hình được huấn luyện không hoạt động tốt chút nào? Đó là phần khó nhất của học máy và chúng ta sẽ chỉ cho bạn một vài kỹ thuật có thể hữu ích.

<Tip>

💡 Nếu bạn đang sử dụng vòng lặp huấn luyện thủ công, các bước tương tự sẽ áp dụng để gỡ lỗi quy trình huấn luyện của bạn, nhưng việc tách chúng ra sẽ dễ dàng hơn. Tuy nhiên, hãy đảm bảo rằng bạn không quên `model.eval()` hoặc `model.train()` ở đúng nơi, hoặc `zero_grad()` ở mỗi bước!

</Tip>

## Debugging silent errors during training

What can we do to debug a training that completes without error but doesn't get good results? We'll give you some pointers here, but be aware that this kind of debugging is the hardest part of machine learning, and there is no magical answer.

### Kiểm tra lại dữ liệu của bạn (một lần nữa!)

Mô hình của bạn sẽ chỉ học được điều gì đó nếu nó thực sự có thể học được bất cứ điều gì từ dữ liệu của bạn. Nếu có lỗi làm hỏng dữ liệu hoặc các nhãn được gán ngẫu nhiên, rất có thể bạn sẽ không huấn luyện được mô hình nào về tập dữ liệu của mình. Vì vậy, hãy luôn bắt đầu bằng cách kiểm tra kỹ các đầu vào và nhãn đã được giải mã của bạn và tự hỏi bản thân những câu hỏi sau:

- Dữ liệu được giải mã có dễ hiểu không?
- Bạn có đồng ý với các nhãn?
- Có một nhãn nào phổ biến hơn những nhãn khác không?
- Mất mát/Chỉ số sẽ là bao nhiêu nếu mô hình dự đoán một câu trả lời ngẫu nhiên/luôn là một câu trả lời giống nhau?

<Tip warning={true}>

⚠️ Nếu bạn đang thực hiện huấn luyện phân tán, hãy in các mẫu tập dữ liệu của bạn trong mỗi quy trình và kiểm tra ba lần để đảm bảo bạn nhận được điều tương tự. Một lỗi phổ biến là có một số nguồn ngẫu nhiên trong quá trình tạo dữ liệu khiến mỗi quy trình có một phiên bản khác nhau của tập dữ liệu.

</Tip>

Sau khi xem xét dữ liệu của bạn, hãy xem qua một số dự đoán của mô hình và giải mã chúng. Nếu mô hình luôn dự đoán cùng một điều, có thể là do tập dữ liệu của bạn thiên về một loại (đối với các vấn đề phân loại); các kỹ thuật như lấy mẫu quá mức các lớp hiếm có thể hữu ích.

Nếu mất mát/chỉ số đánh giá bạn nhận được trên mô hình ban đầu của mình rất khác với cái bạn mong đợi cho các dự đoán ngẫu nhiên, hãy kiểm tra kỹ cách tính toán tổn thất hoặc số liệu của bạn, vì có thể có một lỗi ở đó. Nếu bạn đang sử dụng một số mất mát mà bạn thêm vào cuối, hãy đảm bảo rằng chúng có cùng quy mô.

Khi bạn chắc chắn dữ liệu của mình là hoàn hảo, bạn có thể xem liệu mô hình có khả năng huấn luyện về nó hay không bằng một bài kiểm tra đơn giản.

### Học kĩ mô hình của bạn trong một lô

Việc học quá nhiều thường là điều chúng ta cố gắng tránh khi huấn luyện, vì nó có nghĩa là mô hình không học cách nhận ra các đặc điểm chung ta muốn mà thay vào đó chỉ là ghi nhớ các mẫu huấn luyện. Tuy nhiên, cố gắng huấn luyện mô hình của bạn lặp đi lặp lại là một bài kiểm tra tốt để kiểm tra xem vấn đề như bạn đã định hình có thể được giải quyết bằng mô hình mà bạn đang cố gắng huấn luyện hay không. Nó cũng sẽ giúp bạn xem liệu tốc độ học ban đầu của bạn có quá cao hay không.

Thực hiện điều này khi bạn đã xác định được `Trainer` của mình thực sự dễ dàng; chỉ cần lấy một loạt dữ liệu huấn luyện, sau đó chạy một vòng huấn luyện thủ công nhỏ chỉ sử dụng lô đó cho một cái gì đó giống như 20 bước:

```py
for batch in trainer.get_train_dataloader():
    break

batch = {k: v.to(device) for k, v in batch.items()}
trainer.create_optimizer()

for _ in range(20):
    outputs = trainer.model(**batch)
    loss = outputs.loss
    loss.backward()
    trainer.optimizer.step()
    trainer.optimizer.zero_grad()
```

<Tip>

💡 Nếu dữ liệu huấn luyện của bạn không cân bằng, hãy đảm bảo tạo một loạt dữ liệu huấn luyện có chứa tất cả các nhãn.

</Tip>

Mô hình phải có kết quả trả về gần như hoàn hảo trên cùng một `lô`. Hãy tính toán các chỉ số trên các dự đoán kết quả:

```py
with torch.no_grad():
    outputs = trainer.model(**batch)
preds = outputs.logits
labels = batch["labels"]

compute_metrics((preds.cpu().numpy(), labels.cpu().numpy()))
```

```python out
{'accuracy': 1.0}
```

Chính xác 100%, đây là một ví dụ điển hình về việc overfitt(có nghĩa là nếu bạn thử mô hình của mình trên bất kỳ câu nào khác, rất có thể nó sẽ đưa ra câu trả lời sai)!

Nếu bạn không quản lý để mô hình của mình có được kết quả hoàn hảo như thế này, điều đó có nghĩa là có điều gì đó không ổn trong cách bạn định khung vấn đề hoặc dữ liệu của mình, vì vậy bạn nên khắc phục điều đó. Chỉ khi bạn vượt qua được bài kiểm tra overfit, bạn mới có thể chắc chắn rằng mô hình của mình thực sự có thể học được điều gì đó.

<Tip warning={true}>

⚠️ Bạn sẽ phải tạo lại mô hình và `Trainer`của mình sau bài kiểm tra overfitt này, vì mô hình thu được có thể sẽ không thể khôi phục và học được điều gì đó hữu ích trên tập dữ liệu đầy đủ của bạn.

</Tip>

### Không điều chỉnh bất cứ thứ gì cho đến khi bạn có mô hình cơ sở đầu tiên

Điều chỉnh siêu tham số luôn được nhấn mạnh là phần khó nhất của học máy, nhưng nó chỉ là bước cuối cùng giúp bạn hiểu được một chút về chỉ số này. Hầu hết thời gian, các siêu tham số mặc định của `Trainer` sẽ hoạt động tốt để cung cấp cho bạn kết quả tốt, vì vậy đừng khởi chạy tìm kiếm siêu tham số tốn thời gian và tốn kém cho đến khi bạn có thứ gì đó vượt qua mô hình cơ sở mà bạn có trên tập dữ liệu của mình.

Khi bạn đã có một mô hình đủ tốt, bạn có thể bắt đầu điều chỉnh một chút. Đừng thử khởi chạy một nghìn lần chạy với các siêu tham số khác nhau, nhưng hãy so sánh một vài lần chạy với các giá trị khác nhau cho một siêu thông số để có được ý tưởng về giá trị nào có tác động lớn nhất.

Nếu bạn đang điều chỉnh chính mô hình, hãy giữ nó đơn giản và đừng thử bất cứ điều gì mà bạn không thể biện minh một cách hợp lý. Luôn đảm bảo rằng bạn quay lại kiểm tra overfit để xác minh rằng thay đổi của bạn không gây ra bất kỳ hậu quả ngoài ý muốn nào.

### Yêu cầu giúp đỡ

Hy vọng rằng bạn sẽ tìm thấy một số lời khuyên trong phần này để giúp bạn giải quyết vấn đề của mình, nhưng nếu không phải vậy, hãy nhớ rằng bạn luôn có thể hỏi cộng đồng trên [diễn đàn](https://discuss.huggingface.co/).

Dưới đây là một số tài liệu bổ sung có thể hữu ích:

- ["Reproducibility as a vehicle for engineering best practices"](https://docs.google.com/presentation/d/1yHLPvPhUs2KGI5ZWo0sU-PKU3GimAk3iTsI38Z-B5Gw/edit#slide=id.p) bởi Joel Grus
- ["Checklist for debugging neural networks"](https://towardsdatascience.com/checklist-for-debugging-neural-networks-d8b2a9434f21) bởi Cecelia Shao
- ["How to unit test machine learning code"](https://medium.com/@keeper6928/how-to-unit-test-machine-learning-code-57cf6fd81765) bởi Chase Roberts
- ["A Recipe for Training Neural Networks"](http://karpathy.github.io/2019/04/25/recipe/) bởi Andrej Karpathy

Tất nhiên, không phải mọi vấn đề bạn gặp phải khi huấn luyện mạng thần kinh đều là lỗi của chính bạn! Nếu bạn gặp điều gì đó trong thư viện 🤗 Transformers hoặc 🤗 Datasets có vẻ không ổn, có thể bạn đã gặp lỗi. Bạn chắc chắn nên cho chúng tôi biết tất cả về điều đó và trong phần tiếp theo, chúng tôi sẽ giải thích chính xác cách thực hiện điều đó.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter8/4.mdx" />

### Đố vui cuối chương
https://huggingface.co/learn/course/vi/chapter8/7.md

# Đố vui cuối chương

Cũng kiểm tra xem bạn đã học được gì từ chương này!

### 1. Bạn nên đọc truy vết của Python theo thứ tự nào?

<Question
  choices={[
    {
      text: "Từ trên xuống dưới",
      explain:
        "Hãy thử lại - mặc dù hầu hết các ngôn ngữ lập trình khác in ngoại lệ ở trên cùng, Python đặc biệt về mặt này.",
    },
    {
      text: "Từ dưới lên trên",
      explain:
        "Đúng! Một lợi thế của các truy vết của Python hiển thị ngoại lệ ở phía dưới là việc gỡ lỗi dễ dàng hơn khi bạn đang làm việc trong thiết bị đầu cuối như terminal và đây là dòng cuối cùng bạn nhìn thấy.",
      correct: true,
    },
  ]}
/>

### 2. Ví dụ có thể tái tạo tối thiểu là gì?

<Question
  choices={[
    {
      text: "Cách triển khai đơn giản của kiến ​​trúc Transformer từ một bài báo nghiên cứu",
      explain:
        "Mặc dù việc triển khai các mô hình Transformer của riêng bạn từ đầu rất mang tính giáo dục, nhưng đây không phải là điều chúng ta đang nói ở đây.",
    },
    {
      text: "Một khối mã nhỏ gọn và khép kín có thể chạy mà không có bất kỳ sự phụ thuộc bên ngoài nào vào các tệp hoặc dữ liệu riêng tư",
      explain:
        "Đúng! Các ví dụ có thể tái tạo tối thiểu giúp người bảo trì của thư viện tái tạo vấn đề bạn đang gặp phải, vì vậy họ có thể tìm ra giải pháp nhanh hơn.",
      correct: true,
    },
    {
      text: "Ảnh chụp màn hình của truy vết Python",
      explain:
        "Hãy thử lại - mặc dù rất hấp dẫn để bao gồm ảnh chụp màn hình về lỗi bạn đang gặp phải khi gửi vấn đề, điều này khiến những người khác rất khó tái tạo lỗi.",
    },
    {
      text: "Một notebook chứa toàn bộ phân tích của bạn, bao gồm các phần không liên quan đến lỗi",
      explain:
        "Không hẳn - mặc dù có thể hữu ích khi chia sẻ notebook Google Colab hiển thị lỗi, hãy đảm bảo rằng notebook đó ngắn và chỉ chứa mã có liên quan.",
    },
  ]}
/>

### 3. Giả sử bạn cố gắng chạy đoạn mã sau, mà mã này xảy ra lỗi như dưới đây:

```py
from transformers import GPT3ForSequenceClassification

# ImportError: cannot import name 'GPT3ForSequenceClassification' from 'transformers' (/Users/lewtun/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/__init__.py)
# ---------------------------------------------------------------------------
# ImportError                               Traceback (most recent call last)
# /var/folders/28/k4cy5q7s2hs92xq7_h89_vgm0000gn/T/ipykernel_30848/333858878.py in <module>
# ----> 1 from transformers import GPT3ForSequenceClassification

# ImportError: cannot import name 'GPT3ForSequenceClassification' from 'transformers' (/Users/lewtun/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/__init__.py)
```

Điều nào dưới đây có thể là một lựa chọn tốt cho tiêu đề của một chủ đề diễn đàn để yêu cầu trợ giúp?

<Question
  choices={[
    {
      text: "<code>ImportError: cannot import name 'GPT3ForSequenceClassification' from 'transformers' (/Users/lewtun/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/__init__.py)</code>",
      explain:
        "Việc bao gồm dòng cuối cùng của truy vết có thể mang tính mô tả, nhưng điều này tốt hơn nên dành cho phần chính của chủ đề. Hãy thử lại!",
    },
    {
      text: "Vấn đề với <code>from transformers import GPT3ForSequenceClassification</code>",
      explain:
        "Hãy thử lại - mặc dù điều này cung cấp thông tin hữu ích, nhưng nó có lẽ tốt nhất nên được dành cho phần chính của văn bả.",
    },
    {
      text: "Tại sao tôi không thể nhập <code>GPT3ForSequenceClassification</code>?",
      explain:
        "Lựa chọn tốt! Tiêu đề này ngắn gọn và cung cấp cho người đọc manh mối về những gì có thể sai (tức là GPT-3 không được hỗ trợ trong 🤗 Transformers).",
      correct: true,
    },
    {
      text: "Liệu GPT-3 có được hỗ trợ trong 🤗 Transformers?",
      explain:
        "Một câu hỏi hay! Sử dụng câu hỏi làm tiêu đề chủ đề là một cách tuyệt vời để truyền đạt vấn đề với cộng đồng.",
      correct: true,
    },
  ]}
/>

### 4.Giả sử bạn đang cố gắng chạy `trainer.train()` và gặp phải lỗi khó hiểu không cho bạn biết chính xác lỗi đến từ đâu. Đâu sẽ là nơi đầu tiên bạn tìm lỗi trong pipeline huấn luyện của mình?

<Question
  choices={[
    {
      text: "Bước tối ưu hóa nơi chúng ta tính toán độ dốc và thực hiện lan truyền ngược",
      explain:
        "Mặc dù có thể có lỗi trong trình tối ưu hóa của bạn, nhưng đây thường là một vài bước trong quá trình huấn luyện, vì vậy, có những thứ khác cần kiểm tra trước. Hãy thử lại!",
    },
    {
      text: "Bước đánh giá nơi chúng tôi tính toán các chỉ số",
      explain:
        "Đánh giá thường là những gì bạn làm sau khi huấn luyện cho một epoch đầy đủ, vì vậy trước tiên bạn nên kiểm tra một chỗ nào đó trước trong quá trình huấn luyện.",
    },
    {
      text: "Bộ dữ liệu",
      explain:
        "Đúng! Xem xét dữ liệu của bạn hầu như luôn là điều đầu tiên bạn nên làm, để đảm bảo văn bản được tokenize phù hợp, có các đặc trưng mong đợi, v.v.",
      correct: true,
    },
    {
      text: "The dataloaders",
      explain:
        "Hãy thử lại - điều này rất gần với điều đầu tiên bạn nên kiểm tra. Bạn có nhớ đối tượng mà chúng ta truyền cho bộ dữ liệu là gì không?",
    },
  ]}
/>

### 5. Đâu là cách tốt nhất để gỡ lỗi CUDA?

<Question
  choices={[
    {
      text: "Đăng thông báo lỗi trên diễn đàn hoặc GitHub.",
      explain:
        "Điều đó sẽ không giúp ích cho bất kỳ ai vì các thông báo lỗi CUDA thường rất khó hiểu.",
    },
    {
      text: "Thực thi cùng một mã trên CPU.",
      explain: "Chính xác, điều đó sẽ cung cấp cho bạn thông báo lỗi tốt hơn!",
      correct: true,
    },
    {
      text: "Đọc phần theo dõi để tìm hiểu nguyên nhân gây ra lỗi.",
      explain:
        "Đó là những gì bạn sẽ làm đối với bất kỳ lỗi nào khác, nhưng lỗi CUDA thường không được nêu ra ở nơi chúng xảy ra vì hầu hết các hoạt động CUDA là không đồng bộ.",
    },
    {
      text: "Giảm kích thước lô.",
      explain:
        "Giảm kích thước lô thường là một chiến lược tốt để xử lý lỗi CUDA hết bộ nhớ, nhưng không phải cho vấn đề cụ thể này. Hãy thử lại!",
    },
    {
      text: "Khởi động lại Jupyter kernel.",
      explain:
        "Hãy thử lại - khởi động lại kernel sẽ không làm cho lỗi biến mất một cách kỳ diệu!",
    },
  ]}
/>

### 6. Đâu là cách tốt nhất để khắc phục lỗi trên Github?

<Question
  choices={[
    {
      text: "ăng một ví dụ có thể tái tạo đầy đủ về lỗi.",
      explain:
        "Vâng, đó là cách tốt nhất để giúp những người bảo trì tìm ra lỗi của bạn. Bạn nên làm gì khác?",
      correct: true,
    },
    {
      text: "Yêu cầu cập nhật mỗi ngày.",
      explain:
        "Điều đó không thể giúp bạn bất kỳ sự giúp đỡ nào; mọi người có thể sẽ bỏ qua bạn nhiều hơn.",
    },
    {
      text: "Kiểm tra mã nguồn xung quanh lỗi và cố gắng tìm lý do tại sao nó xảy ra. Đăng kết quả về vấn đề.",
      explain:
        "Điều đó chắc chắn sẽ giúp ích cho những người bảo trì! Và nếu bạn tìm ra nguồn gốc của lỗi và cách khắc phục, bạn thậm chí có thể mở một pull request. Bạn nên làm gì khác?",
      correct: true,
    },
  ]}
/>

### 7. Tại sao học quá kĩ (overfit) vào một lô thường là cách gỡ lỗi tốt nhất?

<Question
  choices={[
    {
      text: "Không đâu; nó luôn là điều xấu ta nên tránh.",
      explain:
        "Khi huấn luyện trên toàn bộ tập dữ liệu, việc học kĩ quá mức thực sự có thể là một dấu hiệu cho thấy mô hình của bạn sẽ không khái quát tốt cho các ví dụ mới. Tuy nhiên, để gỡ lỗi, chúng tôi thường không huấn luyện trên toàn bộ tập dữ liệu. Hãy thử lại!",
    },
    {
      text: "Nó cho phép chúng ta xác minh rằng mô hình có thể giảm mất mát xuống bằng không.",
      explain:
        "Đúng! Với một lô nhỏ có ít nhất hai ví dụ, chúng ta có thể nhanh chóng xác minh xem mô hình có khả năng học hỏi hay không.",
      correct: true,
    },
    {
      text: "Nó cho phép chúng tôi xác minh rằng hình dạng tensor của đầu vào và nhãn của chúng tôi là chính xác.",
      explain:
        "Hãy thử lại - nếu hình dạng tensor của bạn bị lệch, thì bạn chắc chắn sẽ không thể huấn luyện, ngay cả trên một lô duy nhất.",
    },
  ]}
/>

### 8. Tại sao bao gồm chi tiết về môi trường tính toán với `transformers-cli env` khi tạo ra một issue (vấn đề) trên kho 🤗 Transformers là một ý hay?

<Question
  choices={[
    {
      text: "Nó cho phép người bảo trì hiểu phiên bản thư viện bạn đang sử dụng.",
      explain:
        "Đúng! Vì mỗi phiên bản chính của thư viện có thể có những thay đổi trong API, nên việc biết bạn đang sử dụng phiên bản cụ thể nào có thể giúp thu hẹp vấn đề. Các lợi ích khác là gì?",
      correct: true,
    },
    {
      text: "Nó cho phép người bảo trì biết liệu bạn đang chạy mã trên Windows, macOS hay Linux.",
      explain:
        "Đúng! Các lỗi đôi khi có thể do hệ điều hành cụ thể mà bạn đang sử dụng gây ra và việc biết điều này sẽ giúp người bảo trì tái tạo chúng cục bộ. Tuy nhiên, đó không phải là lý do duy nhất.",
      correct: true,
    },
    {
      text: "Nó cho phép người bảo trì biết liệu bạn đang chạy mã trên GPU hay CPU.",
      explain:
        "Đúng! Như chúng ta đã thấy trong chương này, mã chạy trên GPU hoặc CPU có thể tạo ra các kết quả hoặc lỗi khác nhau và việc biết bạn đang sử dụng phần cứng nào có thể giúp tập trung sự chú ý của người bảo trì. Nhưng đây không phải là điều duy nhất lợi ích...",
      correct: true,
    },
  ]}
/>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter8/7.mdx" />

### Gỡ lỗi quy trình huấn luyện
https://huggingface.co/learn/course/vi/chapter8/4_tf.md

# Gỡ lỗi quy trình huấn luyện

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter8/section4_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter8/section4_tf.ipynb"},
]} />

Bạn đã viết một kịch bản tuyệt đẹp để huấn luyện hoặc tinh chỉnh một mô hình trong một tác vụ nhất định, tuân thủ một cách nghiêm túc lời khuyên từ [Chương 7](/course/chapter7). Nhưng khi bạn khởi chạy lệnh `model.fit()`, một điều kinh khủng xảy ra: bạn gặp lỗi 😱! Hoặc tệ hơn, mọi thứ dường như ổn và quá trình huấn luyện chạy mà không có lỗi, nhưng mô hình kết quả là tồi tệ. Trong phần này, chúng tôi sẽ chỉ cho bạn những gì bạn có thể làm để gỡ lỗi các loại vấn đề này.

<Youtube id="N9kO52itd0Q"/>

Vấn đề khi bạn gặp lỗi trong  `model.fit()`  có thể đến từ nhiều nguồn, vì việc huấn luyện thường tập hợp rất nhiều thứ lại với nhau. Vấn đề có thể là một cái gì đó sai trong bộ dữ liệu của bạn hoặc một số vấn đề khi cố gắng kết hợp hàng loạt các phần tử của bộ dữ liệu với nhau. Sau đó, nó lấy một loạt dữ liệu và đưa nó vào mô hình, vì vậy vấn đề có thể nằm ở mã mô hình. Sau đó, nó tính toán các độ dốc và thực hiện bước tối ưu hóa, vì vậy vấn đề cũng có thể nằm trong trình tối ưu hóa của bạn. Và ngay cả khi mọi thứ diễn ra tốt đẹp cho quá trình huấn luyện, vẫn có thể xảy ra sự cố trong quá trình đánh giá nếu có vấn đề với chỉ số của bạn.

Cách tốt nhất để gỡ lỗi phát sinh trong `model.fit()` là đi qua toàn pipeline này theo cách thủ công để xem mọi thứ diễn ra như thế nào. Sau đó, lỗi thường rất dễ giải quyết.

Để chứng minh điều này, chúng ta sẽ sử dụng tập lệnh (cố gắng) tinh chỉnh mô hình DistilBERT trên [tập dữ liệu MNLI](https://huggingface.co/datasets/glue):

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    TFAutoModelForSequenceClassification,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)

train_dataset = tokenized_datasets["train"].to_tf_dataset(
    columns=["input_ids", "labels"], batch_size=16, shuffle=True
)

validation_dataset = tokenized_datasets["validation_matched"].to_tf_dataset(
    columns=["input_ids", "labels"], batch_size=16, shuffle=True
)

model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint)

model.compile(loss="sparse_categorical_crossentropy", optimizer="adam")

model.fit(train_dataset)
```

Nếu bạn cố gắng thực thi nó, bạn có thể nhận được một số `VisibleDeprecationWarning` khi thực hiện chuyển đổi tập dữ liệu - đây là một vấn đề UX đã biết mà chúng ta gặp phải, vì vậy vui lòng bỏ qua nó. Nếu bạn đang đọc khóa học sau đó, chẳng hạn như tháng 11 năm 2021 và nó vẫn đang diễn ra, thì hãy gửi những dòng tweet giận dữ tại @carrigmat cho đến khi anh ấy sửa nó.

Tuy nhiên, một vấn đề nghiêm trọng hơn là chúng ta nhận được một lỗi trọn vẹn. Và nó thực sự rất dài:

```python out
ValueError: No gradients provided for any variable: ['tf_distil_bert_for_sequence_classification/distilbert/embeddings/word_embeddings/weight:0', '...']
```

Điều đó nghĩa là gì? Chúng ta đã cố gắng huấn luyện trên dữ liệu của mình, nhưng chúng ta không có gradient? Điều này khá bối rối; làm thế nào để chúng ta thậm chí bắt đầu gỡ lỗi một cái gì đó như vậy? Khi lỗi bạn gặp phải không gợi ý ngay vấn đề nằm ở đâu, giải pháp tốt nhất thường là thực hiện mọi thứ theo trình tự, đảm bảo ở mỗi giai đoạn mọi thứ đều ổn. Và tất nhiên, nơi bắt đầu luôn là...

### Kiểm tra dữ liệu của bạn

Điều này không cần phải nói, nhưng nếu dữ liệu của bạn bị hỏng, Keras sẽ không thể sửa nó cho bạn. Vì vậy, điều đầu tiên, bạn cần phải xem xét những gì bên trong bộ huấn luyện của bạn.

Mặc dù rất hấp dẫn khi nhìn vào bên trong `raw_datasets` và `tokenized_datasets`, chúng tôi thực sự khuyên bạn nên truy cập dữ liệu ngay tại điểm mà nó được đưa vào mô hình. Điều đó có nghĩa là đọc kết quả đầu ra từ `tf.data.Dataset` mà bạn đã tạo bằng hàm `to_tf_dataset()`! Vì vậy, làm thế nào để chúng ta làm điều đó? Các đối tượng `tf.data.Dataset` cung cấp cho chúng ta toàn bộ các lô cùng một lúc và không hỗ trợ lập chỉ mục, vì vậy chúng ta không thể chỉ yêu cầu `train_dataset[0]`. Tuy nhiên, chúng ta có thể yêu cầu nó một cách lịch sự cho một lô:

```py
for batch in train_dataset:
    break
```

`break` kết thúc vòng lặp sau một lần lặp, vì vậy, điều này lấy lô đầu tiên ra khỏi `train_dataset` và lưu nó dưới dạng `batch`. Bây giờ, chúng ta hãy xem những gì bên trong:

```python out
{'attention_mask': <tf.Tensor: shape=(16, 76), dtype=int64, numpy=
 array([[1, 1, 1, ..., 0, 0, 0],
        [1, 1, 1, ..., 0, 0, 0],
        [1, 1, 1, ..., 0, 0, 0],
        ...,
        [1, 1, 1, ..., 1, 1, 1],
        [1, 1, 1, ..., 0, 0, 0],
        [1, 1, 1, ..., 0, 0, 0]])>,
 'label': <tf.Tensor: shape=(16,), dtype=int64, numpy=array([0, 2, 1, 2, 1, 1, 2, 0, 0, 0, 1, 0, 1, 2, 2, 1])>,
 'input_ids': <tf.Tensor: shape=(16, 76), dtype=int64, numpy=
 array([[ 101, 2174, 1010, ...,    0,    0,    0],
        [ 101, 3174, 2420, ...,    0,    0,    0],
        [ 101, 2044, 2048, ...,    0,    0,    0],
        ...,
        [ 101, 3398, 3398, ..., 2051, 2894,  102],
        [ 101, 1996, 4124, ...,    0,    0,    0],
        [ 101, 1999, 2070, ...,    0,    0,    0]])>}
```

Điều này có vẻ đúng, phải không? Chúng ta đang chuyển các nhãn `labels`, `attention_mask`, và `input_ids` cho mô hình, đây sẽ là mọi thứ nó cần để tính toán kết quả đầu ra và tính toán mất mát. Vậy tại sao chúng ta không có một gradient? Nhìn kỹ hơn: chúng ta đang chuyển một từ điển duy nhất làm đầu vào, nhưng một lô huấn luyện thường là một tensor đầu vào hoặc từ điển, cộng với một tensor nhãn. Nhãn của chúng ta là một chìa khóa trong từ điển đầu vào của mình.

Đây có phải là vấn đê? Không phải lúc nào cũng vậy! Nhưng đó là một trong những vấn đề phổ biến nhất mà bạn sẽ gặp phải khi huấn luyện các mô hình Transformer với TensorFlow. Tất cả các mô hình của chúng ta đều có thể tính toán mất mát trong nội bộ, nhưng để làm được điều đó, các nhãn cần được chuyển vào từ điển đầu vào. Đây là mất mát được sử dụng khi chúng ta không chỉ định giá trị tổn thất cho `compile()`. Mặt khác, Keras thường mong đợi các nhãn được chuyển riêng khỏi từ điển đầu vào và các tính toán tổn thất thường sẽ thất bại nếu bạn không làm điều đó.

Vấn đề giờ đã trở nên rõ ràng hơn: chúng ta đã thông qua tham số `loss`, có nghĩa là chúng ta đang yêu cầu Keras tính toán khoản mất mát của mình, nhưng chúng ta đã chuyển nhãn của mình làm đầu vào cho mô hình, không phải là nhãn ở nơi Keras mong đợi chúng! Chúng ta cần chọn cái này hay cái kia: hoặc chúng ta sử dụng tổn thất bên trong của mô hình và giữ các nhãn ở vị trí của chúng, hoặc chúng ta tiếp tục sử dụng tổn thất Keras, nhưng chúng ta chuyển các nhãn đến nơi mà Keras mong đợi chúng. Để đơn giản, chúng ta hãy thực hiện cách tiếp cận đầu tiên. Thay đổi lệnh gọi thành `compile()` để đọc:

```py
model.compile(optimizer="adam")
```

Bây giờ chúng ta sẽ sử dụng mất mát bên trong của mô hình và vấn đề này sẽ được giải quyết!

<Tip>

✏️ **Đến lượt bạn!** Là một thử thách không bắt buộc sau khi chúng ta đã giải quyết xong các vấn đề khác, bạn có thể thử quay lại bước này và làm cho mô hình hoạt động với mất mát do Keras tính toán ban đầu thay vì mất mát nội bộ. Bạn sẽ cần phải thêm `"labels"` vào `label_cols` của `to_tf_dataset()` để đảm bảo rằng các nhãn được xuất chính xác, điều này sẽ giúp bạn có được độ dốc - nhưng có một vấn đề nữa với sự mất mát mà chúng ta đã chỉ định. Việc huấnl uyện vẫn sẽ diễn ra với vấn đề này, nhưng việc học sẽ rất chậm và sẽ khả năng mất mát huấn luyện cao. Bạn có thể tìm ra nó là gì không?

Một gợi ý mã hoá ROT13, nếu bạn bế tắc: Vs lbh ybbx ng gur bhgchgf bs FrdhraprPynffvsvpngvba zbqryf va Genafsbezref, gurve svefg bhgchg vf `ybtvgf`. Jung ner ybtvgf?

Và một gợi ý thứ hai: Jura lbh fcrpvsl bcgvzvmref, npgvingvbaf be ybffrf jvgu fgevatf, Xrenf frgf nyy gur nethzrag inyhrf gb gurve qrsnhygf. Jung nethzragf qbrf FcnefrPngrtbevpnyPebffragebcl unir, naq jung ner gurve qrsnhygf?

</Tip>

Bây giờ, chúng ta hãy thử huấn luyện. Bây giờ chúng ta sẽ nhận được gradient, vì vậy hy vọng (nhạc đáng ngại phát ở đây) chúng ta có thể gọi `model.fit()` và mọi thứ sẽ hoạt động tốt!

```python out
  246/24543 [..............................] - ETA: 15:52 - loss: nan
```

Ôi không.

`nan` không phải là một giá trị mất mát đáng khích lệ. Tuy nhiên, chúng ta đã kiểm tra dữ liệu của mình và nó trông khá ổn. Nếu đó không phải là vấn đề, chúng ta có thể đi đâu tiếp theo? Bước tiếp theo rõ ràng là ...

### Kiểm tra mô hình của bạn

`model.fit()` là một hàm tiện lợi thực sự tuyệt vời trong Keras, nhưng nó làm được rất nhiều thứ cho bạn và điều đó có thể khiến việc tìm chính xác vị trí đã xảy ra sự cố trở nên khó khăn hơn. Nếu bạn đang gỡ lỗi mô hình của mình, một chiến lược thực sự có thể hữu ích là chỉ chuyển một lô duy nhất cho mô hình và xem xét chi tiết kết quả đầu ra của một lô đó. Một mẹo thực sự hữu ích khác nếu mô hình đang gặp lỗi là `compile()` mô hình với `run_eagerly=True`. Điều này sẽ làm cho nó chậm hơn rất nhiều, nhưng nó sẽ làm cho các thông báo lỗi dễ hiểu hơn nhiều, bởi vì chúng sẽ chỉ ra chính xác vị trí xảy ra sự cố trong mã mô hình của bạn.

Tuy nhiên, hiện tại, chúng ta chưa cần đến `run_eagerly`. Hãy chạy `batch` mà chúng ta đã có trước đó thông qua mô hình và xem kết quả đầu ra trông như thế nào:

```py
model(batch)
```

```python out
TFSequenceClassifierOutput(loss=<tf.Tensor: shape=(16,), dtype=float32, numpy=
array([nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan, nan, nan], dtype=float32)>, logits=<tf.Tensor: shape=(16, 2), dtype=float32, numpy=
array([[nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan]], dtype=float32)>, hidden_states=None, attentions=None)
```

Chà, điều này thật khó. Mọi thứ đều là `nan`! Nhưng thật lạ phải không? Làm thế nào mà tất cả nhật ký của chúng ta sẽ trở thành `nan`? `nan` có nghĩa là "không phải là số". Giá trị `nan` thường xảy ra khi bạn thực hiện một thao tác bị cấm, chẳng hạn như chia cho số không. Nhưng một điều rất quan trọng cần biết về `nan` trong học máy là giá trị này có xu hướng *lan truyền*. Nếu bạn nhân một số với `nan`, kết quả cũng là `nan`. Và nếu bạn nhận được một `nan` ở bất kỳ đâu trong đầu ra, sự mất mát hoặc độ dốc của bạn, thì nó sẽ nhanh chóng lan rộng ra toàn bộ mô hình của bạn - bởi vì khi giá trị `nan` đó được truyền trở lại qua mạng của bạn, bạn sẽ nhận được `nan` gradient và khi cập nhật trọng số được tính toán với những gradient đó, bạn sẽ nhận được trọng số `nan` và những trọng số đó sẽ tính toán nhiều kết quả đầu ra `nan` hơn nữa! Chẳng bao lâu nữa, toàn bộ mạng lưới sẽ chỉ là một khối lớn gồm các giá trị `nan`. Một khi điều đó xảy ra, thật khó để xem vấn đề bắt đầu từ đâu. Làm thế nào chúng ta có thể cô lập nơi mà `nan` len lỏi đầu tiên?

Câu trả lời là hãy thử *khởi động lại* mô hình. Khi chúng ta bắt đầu huấn luyện, chúng ta có một `nan` ở đâu đó và nó nhanh chóng được truyền bá qua toàn bộ mô hình. Vì vậy, hãy tải mô hình từ một checkpoint và không thực hiện bất kỳ cập nhật trọng số nào và xem nơi chúng ta nhận được giá trị `nan`:

```py
model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint)
model(batch)
```

When we run that, we get:

```py out
TFSequenceClassifierOutput(loss=<tf.Tensor: shape=(16,), dtype=float32, numpy=
array([0.6844486 ,        nan,        nan, 0.67127866, 0.7068601 ,
              nan, 0.69309855,        nan, 0.65531296,        nan,
              nan,        nan, 0.675402  ,        nan,        nan,
       0.69831556], dtype=float32)>, logits=<tf.Tensor: shape=(16, 2), dtype=float32, numpy=
array([[-0.04761693, -0.06509043],
       [-0.0481936 , -0.04556257],
       [-0.0040929 , -0.05848458],
       [-0.02417453, -0.0684005 ],
       [-0.02517801, -0.05241832],
       [-0.04514256, -0.0757378 ],
       [-0.02656011, -0.02646275],
       [ 0.00766164, -0.04350497],
       [ 0.02060014, -0.05655622],
       [-0.02615328, -0.0447021 ],
       [-0.05119278, -0.06928903],
       [-0.02859691, -0.04879177],
       [-0.02210129, -0.05791225],
       [-0.02363213, -0.05962167],
       [-0.05352269, -0.0481673 ],
       [-0.08141848, -0.07110836]], dtype=float32)>, hidden_states=None, attentions=None)
```

*Bây giờ* chúng ta đang đến một nơi nào đó! Không có giá trị `nan` nào trong nhật ký của mình, điều này khiến bạn yên tâm. Nhưng chúng ta thấy có một vài giá trị `nan` bị mất! Có điều gì đó đặc biệt về các mẫu đó gây ra vấn đề này không? Hãy xem chúng là những cái nào (lưu ý rằng nếu bạn tự chạy mã này, bạn có thể nhận được các chỉ số khác nhau vì tập dữ liệu đã bị xáo trộn):

```python
import numpy as np

loss = model(batch).loss.numpy()
indices = np.flatnonzero(np.isnan(loss))
indices
```

```python out
array([ 1,  2,  5,  7,  9, 10, 11, 13, 14])
```

Hãy xem các mẫu mà tạo ra chỉ số này:

```python
input_ids = batch["input_ids"].numpy()
input_ids[indices]
```

```python out
array([[  101,  2007,  2032,  2001,  1037, 16480,  3917,  2594,  4135,
        23212,  3070,  2214, 10170,  1010,  2012,  4356,  1997,  3183,
         6838, 12953,  2039,  2000,  1996,  6147,  1997,  2010,  2606,
         1012,   102,  6838,  2001,  3294,  6625,  3773,  1996,  2214,
         2158,  1012,   102,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  1998,  6814,  2016,  2234,  2461,  2153,  1998, 13322,
         2009,  1012,   102,  2045,  1005,  1055,  2053,  3382,  2008,
         2016,  1005,  2222,  3046,  8103,  2075,  2009,  2153,  1012,
          102,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  1998,  2007,  1996,  3712,  4634,  1010,  2057,  8108,
         2025,  3404,  2028,  1012,  1996,  2616, 18449,  2125,  1999,
         1037,  9666,  1997,  4100,  8663, 11020,  6313,  2791,  1998,
         2431,  1011,  4301,  1012,   102,  2028,  1005,  1055,  5177,
         2110,  1998,  3977,  2000,  2832,  2106,  2025,  2689,  2104,
         2122,  6214,  1012,   102,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  1045,  2001,  1999,  1037, 13090,  5948,  2007,  2048,
         2308,  2006,  2026,  5001,  2043,  2026,  2171,  2001,  2170,
         1012,   102,  1045,  2001,  3564,  1999,  2277,  1012,   102,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  2195,  4279,  2191,  2039,  1996,  2181,  2124,  2004,
         1996,  2225,  7363,  1012,   102,  2045,  2003,  2069,  2028,
         2451,  1999,  1996,  2225,  7363,  1012,   102,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  2061,  2008,  1045,  2123,  1005,  1056,  2113,  2065,
         2009,  2428, 10654,  7347,  2030,  2009,  7126,  2256,  2495,
         2291,   102,  2009,  2003,  5094,  2256,  2495,  2291,  2035,
         2105,  1012,   102,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  2051,  1010,  2029,  3216,  2019,  2503,  3444,  1010,
         6732,  1996,  2265,  2038, 19840,  2098,  2125,  9906,  1998,
         2003,  2770,  2041,  1997,  4784,  1012,   102,  2051,  6732,
         1996,  2265,  2003,  9525,  1998,  4569,  1012,   102,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  1996, 10556,  2140, 11515,  2058,  1010,  2010,  2162,
         2252,  5689,  2013,  2010,  7223,  1012,   102,  2043,  1996,
        10556,  2140, 11515,  2058,  1010,  2010,  2252,  3062,  2000,
         1996,  2598,  1012,   102,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101, 13543,  1999,  2049,  6143,  2933,  2443,   102,  2025,
        13543,  1999,  6143,  2933,  2003,  2443,   102,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0]])
```

Chà, có rất nhiều thứ ở đây, nhưng không có gì nổi bật là bất thường. Hãy xem các nhãn:

```python out
labels = batch['labels'].numpy()
labels[indices]
```

```python out
array([2, 2, 2, 2, 2, 2, 2, 2, 2])
```

Ah! Các mẫu `nan` đều có cùng một nhãn, và đó là nhãn 2. Đây là một gợi ý rất mạnh mẽ. Thực tế là chúng ta chỉ bị mất `nan` khi nhãn của chúng ta là 2 cho thấy rằng đây là thời điểm rất tốt để kiểm tra số lượng nhãn trong mô hình của mình:

```python
model.config.num_labels
```

```python out
2
```

Bây giờ chúng ta thấy vấn đề: mô hình cho rằng chỉ có hai lớp, nhưng các nhãn tăng lên 2, có nghĩa là thực tế có ba lớp (vì 0 cũng là một lớp). Đây là cách chúng ta có một `nan` - bằng cách cố gắng tính toán mất mát cho một lớp không tồn tại! Hãy thử thay đổi điều đó và lắp lại mô hình:

```
model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=3)
model.compile(optimizer='adam')
model.fit(train_dataset)
```

```python out
  869/24543 [>.............................] - ETA: 15:29 - loss: 1.1032
```

Chúng ta đang huấn luyện! Không còn `nan` nữa, và mất mát của chúng ta đang giảm dần ... đại loại vậy. Nếu bạn quan sát nó một lúc, bạn có thể bắt đầu hơi mất kiên nhẫn, bởi vì giá trị tổn thất vẫn ở mức cao. Chúng ta hãy dừng huấn luyện ở đây và thử nghĩ xem điều gì có thể gây ra vấn đề này. Tại thời điểm này, chúng ta khá chắc chắn rằng cả dữ liệu và mô hình đều ổn, nhưng mô hình của chúng ta không hoạt động tốt. Còn lại gì nữa? Đến lúc để...

### Kiểm tra siêu tham số của bạn

Nếu bạn nhìn lại đoạn mã ở trên, bạn có thể không nhìn thấy bất kỳ siêu tham số nào, có lẽ ngoại trừ `batch_size`, và điều đó dường như không phải là thủ phạm. Tuy nhiên, đừng để bị lừa; luôn có siêu tham số và nếu bạn không thể nhìn thấy chúng, điều đó có nghĩa là bạn không biết chúng được đặt thành gì. Đặc biệt, hãy nhớ một điều quan trọng về Keras: nếu bạn đặt hàm mất mát, trình tối ưu hóa hoặc kích hoạt bằng một chuỗi, _tất cả các đối số của nó sẽ được đặt thành giá trị mặc định của chúng_. Điều này có nghĩa là mặc dù việc sử dụng chuỗi ký tự rất tiện lợi, nhưng bạn nên hết sức cẩn thận khi làm như vậy, vì nó có thể dễ dàng che giấu những thứ quan trọng với bạn. (Bất kỳ ai đang thử thách thức tùy chọn ở trên nên lưu ý cẩn thận về thực tế này.)

Trong trường hợp này, chúng ta đã đặt tham số bằng chuỗi ở đâu? Ban đầu, chúng ta đã đặt giá trị mất mát bằng một chuỗi, nhưng chúng ta không làm điều đó nữa. Tuy nhiên, chúng ta đang thiết lập trình tối ưu hóa bằng một chuỗi. Điều đó có thể đang che giấu bất cứ điều gì với mình? Hãy xem [các tham số của nó](https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/Adam).

Có gì nổi bật ở đây không? Đúng vậy - tốc độ học! Khi chúng ta chỉ sử dụng chuỗi `'adam'`, chúng ta sẽ nhận được tốc độ học mặc định, là 0.001 hoặc 1e-3. Đây là mức quá cao đối với một mô hình Transformer! Nói chung, chúng tôi khuyên bạn nên thử tốc độ học từ 1e-5 đến 1e-4 cho các mô hình của bạn; đó là một nơi nào đó nhỏ hơn từ 10X đến 100X so với giá trị mà ta thực sự đang sử dụng ở đây. Điều đó nghe có vẻ như nó có thể là một vấn đề lớn, vì vậy hãy thử giảm bớt nó. Để làm điều đó, chúng ta cần nhập vào đối tượng `optimizer`. Trong khi chúng ta đang ở đó, hãy bắt đầu lại mô hình từ checkpoint, trong trường hợp huấn luyện với tốc độ học cao làm hỏng trọng số của nó:

```python
from tensorflow.keras.optimizers import Adam

model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint)
model.compile(optimizer=Adam(5e-5))
```

<Tip>

💡 Bạn cũng có thể nhập hàm `create_optimizer()` từ 🤗 Transformers, hàm này sẽ cho bạn một trình tối ưu AdamW với với độ phân rã trọng số chính xác cũng như khởi động và phân rã tốc độ học. Trình này thường sẽ tạo ra kết quả tốt hơn một chút so với kết quả bạn nhận được với trình tối ưu hóa Adam mặc định.

</Tip>

Bây giờ, chúng tôi có thể thử điều chỉnh mô hình với tốc độ học mới, được cải thiện:

```python
model.fit(train_dataset)
```

```python out
319/24543 [..............................] - ETA: 16:07 - loss: 0.9718
```

Bây giờ mất mát của chúng ta thực sự đi đâu đó! Việc huấn luyện cuối cùng có vẻ như nó đã hoạt động. Có một bài học ở đây: khi mô hình của bạn đang chạy nhưng mức hao hụt không giảm và bạn chắc chắn rằng dữ liệu của mình vẫn ổn, bạn nên kiểm tra các siêu tham số như tốc độ học và giảm trọng lượng. Đặt một trong hai giá trị đó quá cao rất có thể khiến quá trình huấn luyện bị "đình trệ" với giá trị tổn thất cao.

## Các vấn đề tiềm ẩn khác

Chúng ta đã đề cập đến các vấn đề trong tập lệnh ở trên, nhưng có một số lỗi phổ biến khác mà bạn có thể gặp phải. Chúng ta hãy nhìn vào một danh sách (không đầy đủ cho lắm).

### Xử lý lỗi hết bộ nhớ

Dấu hiệu cho biết sắp hết bộ nhớ là một lỗi như "OOM when allocating tensor"  - OOM là viết tắt của "hết bộ nhớ." Đây là một nguy cơ rất phổ biến khi xử lý các mô hình ngôn ngữ lớn. Nếu bạn gặp phải điều này, một chiến lược tốt là giảm một nửa kích thước lô của bạn và thử lại. Tuy nhiên, hãy nhớ rằng một số mô hình có kích thước *rất* lớn. Ví dụ: GPT-2 kích thước đầy đủ có thông số 1.5B, có nghĩa là bạn sẽ cần 6GB bộ nhớ chỉ để lưu mô hình và 6GB khác cho độ dốc của nó! Huấn luyện mô hình GPT-2 đầy đủ thường sẽ yêu cầu hơn 20GB VRAM bất kể bạn sử dụng kích thước lô nào, điều mà chỉ một số GPU có. Các mô hình nhẹ hơn như `distilbert-base-cased`  dễ chạy hơn nhiều và huấn luyện cũng nhanh hơn nhiều.

<Tip>

Trong phần tiếp theo của khóa học, chúng ta sẽ xem xét các kỹ thuật nâng cao hơn có thể giúp bạn giảm dung lượng bộ nhớ và cho phép bạn tinh chỉnh các mô hình lớn nhất.

</Tip>

### TensorFlow đói rồi đói rồi🦛

Một điểm đặc biệt của TensorFlow mà bạn nên biết là nó phân bổ *tất cả* bộ nhớ GPU của bạn cho chính nó ngay khi bạn tải một mô hình hoặc thực hiện bất kỳ huấn luyện nào và sau đó nó sẽ phân chia bộ nhớ đó theo yêu cầu. Điều này khác với hành vi của các khung khác, như PyTorch, phân bổ bộ nhớ theo yêu cầu với CUDA thay vì thực hiện nó trong nội bộ. Một ưu điểm của phương pháp TensorFlow là nó thường có thể đưa ra các lỗi hữu ích khi bạn hết bộ nhớ và nó có thể phục hồi từ trạng thái đó mà không làm hỏng toàn bộ nhân CUDA. Nhưng cũng có một nhược điểm quan trọng: nếu bạn chạy hai tiến trình TensorFlow cùng một lúc, thì **bạn sẽ có một khoảng thời gian tồi tệ**.

Nếu bạn đang chạy trên Colab, bạn không cần phải lo lắng về điều này, nhưng nếu bạn đang chạy cục bộ thì đây chắc chắn là điều bạn nên cẩn thận. Đặc biệt, hãy lưu ý rằng việc đóng một tab sổ ghi chép không nhất thiết phải đóng notebook đó lại! Bạn có thể cần chọn notebok đang chạy (notebook có biểu tượng màu xanh lá cây) và tắt chúng theo cách thủ công trong danh sách thư mục. Bất kỳ notebook đang chạy nào đang sử dụng TensorFlow vẫn có thể đang giữ một loạt bộ nhớ GPU của bạn và điều đó có nghĩa là bất kỳ notebook mới nào bạn bắt đầu đều có thể gặp phải một số vấn đề rất kỳ quặc.

Nếu bạn bắt đầu gặp lỗi về CUDA, BLAS hoặc cuBLAS trong mã hoạt động trước đó, đây rất thường là thủ phạm. Bạn có thể sử dụng một lệnh như `nvidia-smi` để kiểm tra - khi bạn tắt hoặc khởi động lại notebook hiện tại của mình, bộ nhớ của bạn còn trống hay vẫn còn sử dụng được? Nếu nó vẫn còn được sử dụng, một cái gì đó khác đang giữ nó!

### Kiểm tra lại dữ liệu của bạn (một lần nữa!)

Mô hình của bạn sẽ chỉ học được điều gì đó nếu nó thực sự có thể học được bất cứ điều gì từ dữ liệu của bạn. Nếu có lỗi làm hỏng dữ liệu hoặc các nhãn được gán ngẫu nhiên, rất có thể bạn sẽ không huấn luyện được mô hình nào về tập dữ liệu của mình. Một công cụ hữu ích ở đây là `tokenizer.decode()`. Thao tác này sẽ biến `input_ids` trở lại thành chuỗi, vì vậy bạn có thể xem dữ liệu và xem liệu dữ liệu huấn luyện của bạn có đang dạy những gì bạn muốn nó dạy hay không. Ví dụ: sau khi bạn nhận được một `batch` từ `tf.data.Dataset` như chúng ta đã làm ở trên, bạn có thể giải mã phần tử đầu tiên như sau:

```py
input_ids = batch["input_ids"].numpy()
tokenizer.decode(input_ids[0])
```

Then you can compare it with the first label, like so:

```py
labels = batch["labels"].numpy()
label = labels[0]
```

Khi bạn có thể xem dữ liệu của mình như thế này, bạn có thể tự hỏi bản thân những câu hỏi sau:

- Dữ liệu được giải mã có dễ hiểu không?
- Bạn có đồng ý với các nhãn?
- Có một nhãn nào phổ biến hơn những nhãn khác không?
- Mất mát/Chỉ số sẽ là bao nhiêu nếu mô hình dự đoán một câu trả lời ngẫu nhiên/luôn là một câu trả lời giống nhau?

Sau khi xem xét dữ liệu của bạn, hãy xem qua một số dự đoán của mô hình - nếu mô hình của bạn xuất ra các token, hãy thử giải mã chúng! Nếu mô hình luôn dự đoán cùng một điều thì đó có thể là do tập dữ liệu của bạn thiên về một loại (đối với các vấn đề phân loại), vì vậy các kỹ thuật như lấy mẫu quá mức các lớp hiếm có thể hữu ích. Ngoài ra, điều này cũng có thể được gây ra bởi các vấn đề huấn luyện như cài đặt siêu tham số tệ.

Nếu phần mất mát/ các chỉ số bạn nhận được trên mô hình ban đầu của mình trước khi huấn luyện rất khác với cái bạn mong đợi cho các dự đoán ngẫu nhiên, hãy kiểm tra kỹ cách tính toán mất mát hoặc chỉ số của bạn, vì có thể có một lỗi ở đó. Nếu bạn đang sử dụng một số khoảng mất mát mà bạn thêm vào cuối, hãy đảm bảo rằng chúng có cùng quy mô.

Khi bạn chắc chắn dữ liệu của mình là hoàn hảo, bạn có thể xem liệu mô hình có khả năng huấn luyện về nó hay không bằng một bài kiểm tra đơn giản.

### Học kĩ mô hình của bạn trong một lô

Việc học quá nhiều thường là điều chúng ta cố gắng tránh khi huấn luyện, vì nó có nghĩa là mô hình không học cách nhận ra các đặc điểm chung ta muốn mà thay vào đó chỉ là ghi nhớ các mẫu huấn luyện. Tuy nhiên, cố gắng huấn luyện mô hình của bạn lặp đi lặp lại là một bài kiểm tra tốt để kiểm tra xem vấn đề như bạn đã định hình có thể được giải quyết bằng mô hình mà bạn đang cố gắng huấn luyện hay không. Nó cũng sẽ giúp bạn xem liệu tốc độ học ban đầu của bạn có quá cao hay không.

Thực hiện điều này khi bạn đã xác định được `model` của mình thực sự dễ dàng; chỉ cần lấy một loạt dữ liệu huấn luyện, sau đó coi `batch` đó là toàn bộ tập dữ liệu của bạn, đưa nó vào mô hình với một lượng epoch lớn:

```py
for batch in train_dataset:
    break

# Đảm bảo rằng bạn đã chạy model.compile() và đặt trình tối ưu hóa của mình,
# và mất mát/chỉ số của bạn nếu bạn đang sử dụng chúng

model.fit(batch, epochs=20)
```

<Tip>

💡 Nếu dữ liệu huấn luyện của bạn không cân bằng, hãy đảm bảo tạo một loạt dữ liệu huấn luyện có chứa tất cả các nhãn.

</Tip>

Mô hình phải có kết quả gần như hoàn hảo trên `batch`, với mức mất mát giảm nhanh về 0 (hoặc giá trị tối thiểu cho khoản mất mát bạn đang sử dụng).

Nếu bạn không quản lý để mô hình của mình có được kết quả hoàn hảo như thế này, điều đó có nghĩa là có điều gì đó không ổn trong cách bạn định khung vấn đề hoặc dữ liệu của mình, vì vậy bạn nên khắc phục điều đó. Chỉ khi bạn vượt qua được bài kiểm tra overfit, bạn mới có thể chắc chắn rằng mô hình của mình thực sự có thể học được điều gì đó.

<Tip warning={true}>

⚠️ Bạn sẽ phải tạo lại mô hình của mình và biên dịch lại sau bài kiểm tra overfitt này, vì mô hình thu được có thể sẽ không thể khôi phục và học được điều gì đó hữu ích trên tập dữ liệu đầy đủ của bạn.

</Tip>

### Không điều chỉnh bất cứ thứ gì cho đến khi bạn có mô hình cơ sở đầu tiên

Điều chỉnh siêu tham số luôn được nhấn mạnh là phần khó nhất của học máy, nhưng nó chỉ là bước cuối cùng giúp bạn hiểu được một chút về chỉ số này. *Các giá trị rất không tốt* cho các siêu tham số của bạn, chẳng hạn như sử dụng tốc độ học Adam mặc định là 1e-3 với mô hình Transformer, tất nhiên sẽ khiến việc học tiến hành rất chậm hoặc hoàn toàn bị đình trệ, nhưng hầu hết thời gian là các siêu tham số "hợp lý", như tốc độ học từ 1e-5 đến 5e-5, sẽ hoạt động tốt để mang lại cho bạn kết quả tốt. Vì vậy đừng khởi chạy tìm kiếm siêu tham số tốn thời gian và tốn kém cho đến khi bạn có thứ gì đó vượt qua mô hình cơ sở mà bạn có trên tập dữ liệu của mình.

Khi bạn đã có một mô hình đủ tốt, bạn có thể bắt đầu điều chỉnh một chút. Đừng thử khởi chạy một nghìn lần chạy với các siêu tham số khác nhau, nhưng hãy so sánh một vài lần chạy với các giá trị khác nhau cho một siêu thông số để có được ý tưởng về giá trị nào có tác động lớn nhất.

Nếu bạn đang điều chỉnh chính mô hình, hãy giữ nó đơn giản và đừng thử bất cứ điều gì mà bạn không thể biện minh một cách hợp lý. Luôn đảm bảo rằng bạn quay lại kiểm tra overfit để xác minh rằng thay đổi của bạn không gây ra bất kỳ hậu quả ngoài ý muốn nào.

### Yêu cầu giúp đỡ

Hy vọng rằng bạn sẽ tìm thấy một số lời khuyên trong phần này để giúp bạn giải quyết vấn đề của mình, nhưng nếu không phải vậy, hãy nhớ rằng bạn luôn có thể hỏi cộng đồng trên [diễn đàn](https://discuss.huggingface.co/).

Dưới đây là một số tài liệu bổ sung có thể hữu ích:

- ["Reproducibility as a vehicle for engineering best practices"](https://docs.google.com/presentation/d/1yHLPvPhUs2KGI5ZWo0sU-PKU3GimAk3iTsI38Z-B5Gw/edit#slide=id.p) bởi Joel Grus
- ["Checklist for debugging neural networks"](https://towardsdatascience.com/checklist-for-debugging-neural-networks-d8b2a9434f21) bởi Cecelia Shao
- ["How to unit test machine learning code"](https://medium.com/@keeper6928/how-to-unit-test-machine-learning-code-57cf6fd81765) bởi Chase Roberts
- ["A Recipe for Training Neural Networks"](http://karpathy.github.io/2019/04/25/recipe/) bởi Andrej Karpathy

Tất nhiên, không phải mọi vấn đề bạn gặp phải khi huấn luyện mạng thần kinh đều là lỗi của chính bạn! Nếu bạn gặp điều gì đó trong thư viện 🤗 Transformers hoặc 🤗 Datasets có vẻ không ổn, có thể bạn đã gặp lỗi. Bạn chắc chắn nên cho chúng tôi biết tất cả về điều đó và trong phần tiếp theo, chúng tôi sẽ giải thích chính xác cách thực hiện điều đó.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter8/4_tf.mdx" />

### Phải làm gì khi bạn gặp lỗi
https://huggingface.co/learn/course/vi/chapter8/2.md

# Phải làm gì khi bạn gặp lỗi

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter8/section2.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter8/section2.ipynb"},
]} />

Trong phần này, chúng ta sẽ xem xét một số lỗi phổ biến có thể xảy ra khi bạn đang cố gắng tạo dự đoán từ mô hình Transformer mới được điều chỉnh của mình. Điều này sẽ giúp bạn chuẩn bị cho [section 4](/course/chapter8/section4), nơi chúng ta sẽ khám phá cách gỡ lỗi chính giai đoạn huấn luyện.

<Youtube id="DQ-CpJn6Rc4"/>

Chúng tôi đã chuẩn bị [kho lưu trữ mô hình mẫu](https://huggingface.co/lewtun/distilbert-base-uncased-finetuned-squad-d5716d28) cho phần này và nếu bạn muốn chạy mã trong chương này, trước tiên, bạn cần sao chép mô hình vào tài khoản của bạn trên [Hugging Face Hub](https://huggingface.co). Để làm như vậy, trước tiên hãy đăng nhập bằng cách chạy một trong hai thao tác sau trong notebook Jupyter:

```python
from huggingface_hub import notebook_login

notebook_login()
```

hoặc sau trong terminal yêu thích của bạn:

```bash
huggingface-cli login
```

Thao tác này sẽ nhắc bạn nhập tên người dùng và mật khẩu của mình, đồng thời sẽ lưu token dưới *~/.cache/huggingface/*. Khi bạn đã đăng nhập, bạn có thể sao chép kho mẫu với hàm sau:

```python
from distutils.dir_util import copy_tree
from huggingface_hub import Repository, snapshot_download, create_repo, get_full_repo_name


def copy_repository_template():
    # Sao chép kho và trích xuất đường dẫn cục bộ
    template_repo_id = "lewtun/distilbert-base-uncased-finetuned-squad-d5716d28"
    commit_hash = "be3eaffc28669d7932492681cd5f3e8905e358b4"
    template_repo_dir = snapshot_download(template_repo_id, revision=commit_hash)
    # Tạo ra một kho rỗng trên Hub
    model_name = template_repo_id.split("/")[1]
    create_repo(model_name, exist_ok=True)
    # Sao chép kho rỗng
    new_repo_id = get_full_repo_name(model_name)
    new_repo_dir = model_name
    repo = Repository(local_dir=new_repo_dir, clone_from=new_repo_id)
    # Sao chép các tệp
    copy_tree(template_repo_dir, new_repo_dir)
    # Đẩy lên Hub
    repo.push_to_hub()
```

Giờ khi bạn gọi `copy_repository_template()`, nó sẽ tạo ra một bản sao kho lưu trữ mẫu dưới tài khoản của bạn.

## Gỡ lỗi pipeline 🤗 Transformers

Để bắt đầu cuộc hành trình của chúng ta vào thế giới tuyệt vời của việc gỡ lỗi các mô hình Transformer, hãy xem xét tình huống sau: bạn đang làm việc với một đồng nghiệp trong một dự án hỏi đáp để giúp khách hàng của một trang web thương mại điện tử tìm thấy câu trả lời về các sản phẩm tiêu dùng. Đồng nghiệp của bạn gửi cho bạn một tin nhắn như:

> Chúc bạn một ngày tốt lành! Tôi vừa chạy một thử nghiệm bằng cách sử dụng các kỹ thuật trong [Chương 7](/course/chapter7/7) của khóa học Hugging Face và nhận được một số kết quả tuyệt vời trên SQuAD! Tôi nghĩ chúng ta có thể sử dụng mô hình này như một điểm khởi đầu cho dự án của mình. ID mô hình trên Hub là "lewtun/distillbert-base-uncased-finetuned-squad-d5716d28". Hãy thử nghiệm nó xem :)

và điều đầu tiên bạn nghĩ đến là tải mô hình bằng cách sử dụng `pipeline` từ 🤗 Transformers:

```python
from transformers import pipeline

model_checkpoint = get_full_repo_name("distillbert-base-uncased-finetuned-squad-d5716d28")
reader = pipeline("question-answering", model=model_checkpoint)
```

```python out
"""
OSError: Can't load config for 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28'. Make sure that:

- 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models'

- or 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
```

Ôi không, có vẻ như có gì đó không ổn! Nếu bạn là người mới lập trình, những lỗi kiểu này thoạt đầu có vẻ hơi khó hiểu (thậm chí `OSError` là gì ?!). Lỗi được hiển thị ở đây chỉ là phần cuối cùng của một báo cáo lỗi lớn hơn nhiều được gọi là _Python traceback_ (hay còn gọi là đáu vết ngăn xếp). Ví dụ: nếu bạn đang chạy đoạn mã này trên Google Colab, bạn sẽ thấy một cái gì đó giống như ảnh chụp màn hình sau:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/traceback.png" alt="A Python traceback." width="100%"/>
</div>

Có rất nhiều thông tin có trong các báo cáo này, vì vậy chúng ta hãy cùng nhau xem qua các phần chính. Điều đầu tiên cần lưu ý là theo dõi phải được đọc _từ dưới lên trên_. Điều này nghe có vẻ kỳ lạ nếu bạn đã quen đọc văn bản tiếng Anh từ trên xuống dưới, nhưng nó phản ánh thực tế là bản truy xuất hiển thị chuỗi các lệnh gọi hàm mà `pipeline` thực hiện khi tải xuống mô hình và trình tokenizer. (Xem [Chương 2](/course/chapter2) để biết thêm chi tiết về cách hoạt động của `pipeline`.)

<Tip>

🚨 Bạn có thấy hộp màu xanh lam xung quanh "6 frames" trong phần truy xuất từ Google Colab không? Đó là một tính năng đặc biệt của Colab, nén phần truy xuất vào các "frames". Nếu bạn dường như không thể tìm ra nguồn gốc của lỗi, hãy đảm bảo rằng bạn mở rộng toàn bộ theo dõi bằng cách nhấp vào hai mũi tên nhỏ đó.

</Tip>

Điều này có nghĩa là dòng cuối cùng của truy xuất cho biết thông báo lỗi cuối cùng và cung cấp tên của ngoại lệ đã được nêu ra. Trong trường hợp này, loại ngoại lệ là `OSError`, cho biết lỗi liên quan đến hệ thống. Nếu chúng ta đọc thông báo lỗi kèm theo, chúng ta có thể thấy rằng dường như có sự cố với tệp *config.json* của mô hình và ta sẽ đưa ra hai đề xuất để khắc phục:

```python out
"""
Make sure that:

- 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models'

- or 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
```

<Tip>

💡 Nếu bạn gặp phải thông báo lỗi khó hiểu, chỉ cần sao chép và dán thông báo đó vào thanh tìm kiếm Google hoặc [Stack Overflow](https://stackoverflow.com/) (vâng, thực sự!). Có nhiều khả năng bạn không phải là người đầu tiên gặp phải lỗi và đây là một cách tốt để tìm giải pháp mà những người khác trong cộng đồng đã đăng. Ví dụ: tìm kiếm `OSError: Can't load config for` trên Stack Overflow mang lại nhiều [lần truy cập](https://stackoverflow.com/search?q=OSError%3A+Can%27t+load+config+for+) có thể được sử dụng như một điểm khởi đầu để giải quyết vấn đề.

</Tip>

Đề xuất đầu tiên là yêu cầu ta kiểm tra xem ID mô hình có thực sự chính xác hay không, vì vậy, việc đầu tiên ta làm là sao chép chỉ số nhận dạng và dán nó vào thanh tìm kiếm của Hub:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/wrong-model-id.png" alt="The wrong model name." width="100%"/>
</div>

Rất tiếc, có vẻ như mô hình của anh đồng nghiệp không có trên Hub ... aha, nhưng có một lỗi đánh máy trong tên của mô hình! DistilBERT chỉ có một chữ "l" trong tên của nó, vì vậy hãy sửa lỗi đó và tìm "lewtun/distilbert-base-unsased-finetuned-Squad-d5716d28" thay thế:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/true-model-id.png" alt="The right model name." width="100%"/>
</div>

Được rồi, điều này đã thành công. Bây giờ, hãy thử tải xuống mô hình một lần nữa với đúng ID mô hình:

```python
model_checkpoint = get_full_repo_name("distilbert-base-uncased-finetuned-squad-d5716d28")
reader = pipeline("question-answering", model=model_checkpoint)
```

```python out
"""
OSError: Can't load config for 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28'. Make sure that:

- 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models'

- or 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
```

Argh, lại thất bại - chào mừng bạn đến với cuộc sống hàng ngày của một kỹ sư học máy! Vì chúng ta đã sửa ID mô hình, vấn đề phải nằm ở chính kho lưu trữ. Một cách nhanh chóng để truy cập nội dung của một kho lưu trữ trên 🤗 Hub là thông qua hàm `list_repo_files()` của thư viện `huggingface_hub`:

```python
from huggingface_hub import list_repo_files

list_repo_files(repo_id=model_checkpoint)
```

```python out
['.gitattributes', 'README.md', 'pytorch_model.bin', 'special_tokens_map.json', 'tokenizer_config.json', 'training_args.bin', 'vocab.txt']
```

Thật thú vị - dường như không có tệp *config.json* trong kho lưu trữ! Không có gì ngạc nhiên khi `pipeline` không thể tải mô hình; đồng nghiệp của chúng ta chắc hẳn đã quên đẩy tệp này vào Hub sau khi đã tinh chỉnh nó. Trong trường hợp này, vấn đề có vẻ khá đơn giản để khắc phục: chúng ta có thể yêu cầu họ thêm tệp hoặc, vì chúng ta có thể thấy từ ID mô hình mà mô hình huấn luyện trước đã sử dụng là [`distilbert-base-uncased`](https://huggingface.co/distilbert-base-uncased), chúng ta có thể tải xuống cấu hình cho mô hình này và đẩy nó vào kho lưu trữ của mình để xem liệu điều đó có giải quyết được sự cố hay không. Hãy thử điều đó. Sử dụng các kỹ thuật chúng ta đã học trong [Chương 2](/course/chapter2), chúng ta có thể tải xuống cấu hình của mô hình với lớp `AutoConfig`:

```python
from transformers import AutoConfig

pretrained_checkpoint = "distilbert-base-uncased"
config = AutoConfig.from_pretrained(pretrained_checkpoint)
```

<Tip warning={true}>

🚨 Cách tiếp cận mà chúng tôi đang thực hiện ở đây không phải là hoàn hảo, vì đồng nghiệp của chúng ta có thể đã chỉnh sửa cấu hình của `distilbert-base-uncased` trước khi tinh chỉnh mô hình. Trong thực tế, chúng ta muốn kiểm tra với họ trước, nhưng với mục đích của phần này, chúng ta sẽ giả định rằng họ đã sử dụng cấu hình mặc định.

</Tip>

Sau đó, chúng ta có thể đẩy nó vào kho lưu trữ mô hình của mình bằng hàm `push_to_hub()` của cấu hình:

```python
config.push_to_hub(model_checkpoint, commit_message="Add config.json")
```

Bây giờ chúng ta có thể kiểm tra xem điều này có hoạt động hay không bằng cách tải mô hình từ cam kết mới nhất trên nhánh `main`:

```python
reader = pipeline("question-answering", model=model_checkpoint, revision="main")

context = r"""
Extractive Question Answering is the task of extracting an answer from a text
given a question. An example of a question answering dataset is the SQuAD
dataset, which is entirely based on that task. If you would like to fine-tune a
model on a SQuAD task, you may leverage the
examples/pytorch/question-answering/run_squad.py script.

🤗 Transformers is interoperable with the PyTorch, TensorFlow, and JAX
frameworks, so you can use your favourite tools for a wide variety of tasks!
"""

question = "What is extractive question answering?"
reader(question=question, context=context)
```

```python out
{'score': 0.38669535517692566,
 'start': 34,
 'end': 95,
 'answer': 'the task of extracting an answer from a text given a question'}
```

Tuyệt vời, nó đã hoạt động! Hãy tóm tắt lại những gì bạn vừa học được:

- Các thông báo lỗi trong Python được gọi là _tracebacks_ và được đọc từ dưới lên trên. Dòng cuối cùng của thông báo lỗi thường chứa thông tin bạn cần để xác định nguồn gốc của vấn đề.
- Nếu dòng cuối cùng không chứa đủ thông tin, hãy làm theo cách của bạn để truy xuất lại và xem liệu bạn có thể xác định được lỗi xảy ra ở đâu trong mã nguồn hay không.
- Nếu không có thông báo lỗi nào có thể giúp bạn gỡ lỗi, hãy thử tìm kiếm trực tuyến giải pháp cho vấn đề tương tự.
- Các thư viện `huggingface_hub` // 🤗 Hub? cung cấp một bộ công cụ mà bạn có thể sử dụng để tương tác và gỡ lỗi các kho lưu trữ trên Hub.

Bây giờ bạn đã biết cách gỡ lỗi một đường dẫn, chúng ta hãy xem một ví dụ phức tạp hơn trong bước truyền thẳng của chính mô hình.

## Gỡ lỗi truyền thẳng mô hình của bạn

Mặc dù `pipeline` tuyệt vời cho hầu hết các ứng dụng mà bạn cần nhanh chóng tạo dự đoán, đôi khi bạn sẽ cần truy cập nhật ký của mô hình (giả sử, nếu bạn có một số hậu xử lý tùy chỉnh mà bạn muốn áp dụng). Để xem điều gì có thể sai trong trường hợp này, trước tiên hãy lấy mô hình và trình tokenize từ `pipeline` của mình:

```python
tokenizer = reader.tokenizer
model = reader.model
```

Tiếp theo, chúng ta cần một câu hỏi, vì vậy hãy xem liệu các khung yêu thích của chúng ta có được hỗ trợ không:

```python
question = "Which frameworks can I use?"
```

Như đã thấy trong [Chương 7](/course/chapter7), các bước thông thường ta cần làm đó là tokenize đầu vào, trích xuất các logit của token bắt đầu và kết thúc, rồi sau đó giải mã các khoảng trả lời: 

```python
import torch

inputs = tokenizer(question, context, add_special_tokens=True)
input_ids = inputs["input_ids"][0]
outputs = model(**inputs)
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
# Lấy phần có khả năng là bắt đầu của câu trả lời nhất với argmax của điểm trả về
answer_start = torch.argmax(answer_start_scores)
# Lấy phần có khả năng là kết thúc của câu trả lời nhất với argmax của điểm trả về
answer_end = torch.argmax(answer_end_scores) + 1
answer = tokenizer.convert_tokens_to_string(
    tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
)
print(f"Question: {question}")
print(f"Answer: {answer}")
```

```python out
"""
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/var/folders/28/k4cy5q7s2hs92xq7_h89_vgm0000gn/T/ipykernel_75743/2725838073.py in <module>
      1 inputs = tokenizer(question, text, add_special_tokens=True)
      2 input_ids = inputs["input_ids"]
----> 3 outputs = model(**inputs)
      4 answer_start_scores = outputs.start_logits
      5 answer_end_scores = outputs.end_logits

~/miniconda3/envs/huggingface/lib/python3.8/site-packages/torch/nn/modules/module.py in _call_impl(self, *input, **kwargs)
   1049         if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
   1050                 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1051             return forward_call(*input, **kwargs)
   1052         # Do not call functions when jit is used
   1053         full_backward_hooks, non_full_backward_hooks = [], []

~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, start_positions, end_positions, output_attentions, output_hidden_states, return_dict)
    723         return_dict = return_dict if return_dict is not None else self.config.use_return_dict
    724
--> 725         distilbert_output = self.distilbert(
    726             input_ids=input_ids,
    727             attention_mask=attention_mask,

~/miniconda3/envs/huggingface/lib/python3.8/site-packages/torch/nn/modules/module.py in _call_impl(self, *input, **kwargs)
   1049         if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
   1050                 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1051             return forward_call(*input, **kwargs)
   1052         # Do not call functions when jit is used
   1053         full_backward_hooks, non_full_backward_hooks = [], []

~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, output_attentions, output_hidden_states, return_dict)
    471             raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
    472         elif input_ids is not None:
--> 473             input_shape = input_ids.size()
    474         elif inputs_embeds is not None:
    475             input_shape = inputs_embeds.size()[:-1]

AttributeError: 'list' object has no attribute 'size'
"""
```

Ôi trời, có vẻ như chúng ta có một lỗi trong đoạn mã của mình! Nhưng chúng ta không sợ gỡ lỗi chút nào. Bạn có thể sử dụng trình gỡ lỗi Python trong notebook:

<Youtube id="rSPyvPw0p9k"/>

hoặc trong terminal:

<Youtube id="5PkZ4rbHL6c"/>

Ở đây, khi đọc thông báo lỗi cho chúng ta biết rằng đối tượng `'list' object has no attribute 'size'` và chúng ta có thể thấy một mũi tên `-->` trỏ đến dòng nơi vấn đề đã được nêu ra trong `model(** input)`. Bạn có thể gỡ lỗi điều này một cách tương tự bằng cách sử dụng trình gỡ lỗi Python, nhưng bây giờ chúng ta chỉ cần in ra một phần của `inputs` để xem những gì chúng ta có:

Here, reading the error message tells us that `'list' object has no attribute 'size'`, and we can see a `-->` arrow pointing to the line where the problem was raised in `model(**inputs)`.You can debug this interactively using the Python debugger, but for now we'll simply print out a slice of `inputs` to see what we have:

```python
inputs["input_ids"][:5]
```

```python out
[101, 2029, 7705, 2015, 2064]
```

Điều này chắc chắn trông giống như một `list` Python bình thường, nhưng hãy kiểm tra kỹ loại:

```python
type(inputs["input_ids"])
```

```python out
list
```

Vâng, đó chắc chắn là một `list` Python. Vậy điều gì đã xảy ra? Nhớ lại từ [Chương 2](/course/chapter2) rằng các lớp `AutoModelForXxx` trong 🤗 Transformers hoạt động trên _tensors_ (trong PyTorch hoặc TensorFlow) và hoạt động phổ biến là trích xuất các kích thước của tensor bằng cách sử dụng `Tensor.size()` trong PyTorch. Chúng ta hãy xem xét lại quá trình truy vết, để xem dòng nào đã kích hoạt ngoại lệ:

```
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, output_attentions, output_hidden_states, return_dict)
    471             raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
    472         elif input_ids is not None:
--> 473             input_shape = input_ids.size()
    474         elif inputs_embeds is not None:
    475             input_shape = inputs_embeds.size()[:-1]

AttributeError: 'list' object has no attribute 'size'
```

Có vẻ như mã của chúng ta đã cố gắng gọi `input_ids.size()`, nhưng điều này rõ ràng sẽ không hoạt động đối với một `list` Python, vốn chỉ là một vùng chứa. Làm thế nào chúng ta có thể giải quyết vấn đề này? Tìm kiếm thông báo lỗi trên Stack Overflow đưa ra một số [lượt truy cập](https://stackoverflow.com/search?q=AttributeError%3A+%27list%27+object+has+no+attribute+%27size%27&s=c15ec54c-63cb-481d-a749-408920073e8f) liên quan. Nhấp vào câu hỏi đầu tiên sẽ hiển thị một câu hỏi tương tự như câu hỏi của chúng ta, với câu trả lời được hiển thị trong ảnh chụp màn hình bên dưới:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/stack-overflow.png" alt="An answer from Stack Overflow." width="100%"/>
</div>

Câu trả lời khuyên chúng ta nên thêm `return_tensors='pt'` vào tokenizer, vì vậy hãy xem điều đó có phù hợp với chúng ta không:

```python out
inputs = tokenizer(question, context, add_special_tokens=True, return_tensors="pt")
input_ids = inputs["input_ids"][0]
outputs = model(**inputs)
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
# Lấy phần có khả năng là bắt đầu của câu trả lời nhất với argmax của điểm trả về 
answer_start = torch.argmax(answer_start_scores)
# Lấy phần có khả năng là kết thúc của câu trả lời nhất với argmax của điểm trả về 
answer_end = torch.argmax(answer_end_scores) + 1
answer = tokenizer.convert_tokens_to_string(
    tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
)
print(f"Question: {question}")
print(f"Answer: {answer}")
```

```python out
"""
Question: Which frameworks can I use?
Answer: pytorch, tensorflow, and jax
"""
```

Tốt, nó đã hoạt động! Đây là một ví dụ tuyệt vời về mức độ hữu ích của Stack Overflow: bằng cách xác định một vấn đề tương tự, chúng ta có thể hưởng lợi từ kinh nghiệm của những người khác trong cộng đồng. Tuy nhiên, một tìm kiếm như thế này không phải lúc nào cũng mang lại câu trả lời phù hợp, vậy bạn có thể làm gì trong những trường hợp như vậy? May mắn thay, có một cộng đồng các nhà phát triển chào đón trên [diễn đàn Hugging Face](https://discuss.huggingface.co/) có thể giúp bạn! Trong phần tiếp theo, chúng ta sẽ xem xét cách bạn có thể tạo ra các câu hỏi tốt trên diễn đàn có khả năng được trả lời.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter8/2.mdx" />

### Đố vui cuối chương
https://huggingface.co/learn/course/vi/chapter9/9.md

# Đố vui cuối chương

Hãy kiểm tra những gì bạn đã học được trong chương này!

### 1. Bạn có thể sử dụng Gradio để làm gì?

<Question
	choices={[
        {
			text: "Tạo bản demo cho mô hình học máy của bạn",
			explain: "Với một vài dòng mã python, bạn có thể tạo bản demo cho mô hình ML của mình bằng cách sử dụng thư viện các thành phần được tạo sẵn của chúng tôi.",
			correct: true
		},
		{
			text: "Chia sẻ mô hình học máy của bạn với những người khác",
			explain: "Sử dụng tham số <code>share=True</code> trong phương thức khởi chạy, bạn có thể tạo liên kết chia sẻ để gửi cho bất kỳ ai.",
            correct: true
		},
		{
			text: "Gỡ lỗi mô hình của bạn",
			explain: "Một lợi thế của bản demo gradio là có thể kiểm tra mô hình của bạn với dữ liệu thực mà bạn có thể thay đổi và quan sát sự thay đổi dự đoán của mô hình trong thời gian thực, giúp bạn gỡ lỗi mô hình của mình.",
			correct: true
		},
		{
			text: "Huấn luyện mô hình của bạn",
			explain: "Gradio được thiết kể để sử dụng cho việc luận suy mô hình, SAU KHI mô hình của bạn đã được huấn luyện.",
		}
	]}
/>

### 2. Gradio CHỈ hoạt động với các mô hình PyTorch

<Question
	choices={[
        {
			text: "Đúng",
			explain: "Gradio hoạt động với các mô hình PyTorch, nhưng cũng hoạt động với bất kỳ loại mô hình học máy nào!"
        },
        {
			text: "Sai",
			explain: "Gradio là mô hình bất khả tri, có nghĩa là bạn có thể tạo bản demo cho bất kỳ loại mô hình học máy nào.",
			correct: true
        }
	]}
/>

### 3. Bạn có thể khởi chạy bản demo Gradio từ đâu?

<Question
	choices={[
        {
			text: "IDE python chuẩn",
			explain: "Gradio hoạt động hiệu quả với IDE yêu thích của bạn.",
            correct: true
        },
        {
			text: "Google Colab notebooks",
			explain: "Bạn có thể tạo và khởi chạy bản trình diễn trong notebook Google colab của mình.",
			correct: true
        },
        {
			text: "Jupyter notebooks",
			explain: "Lựa chọn tốt - Bạn có thể tạo và khởi chạy bản demo trong Jupyter notebook của mình.",
			correct: true
        }
	]}
/>

### 4. Gradio được thiết kế chủ yếu cho các mô hình NLP

<Question
	choices={[
        {
			text: "Đúng",
			explain: "Gradio hoạt động với khá nhiều kiểu dữ liệu, không chỉ NLP."
        },
        {
			text: "Sai",
			explain: "Gradio cung cấp cho các nhà phát triển thư viện các thành phần được tạo sẵn cho hầu hết các loại dữ liệu.",
            correct: true
        }
	]}
/>

### 5. Tính năng nào sau đây được hỗ trợ bởi Gradio?

<Question
	choices={[
        {
			text: "Nhiều đầu vào và đầu ra",
			explain: "Có thể có nhiều đầu vào và đầu ra với gradio. Tất cả những gì bạn cần làm là chuyển danh sách đầu vào và đầu ra cho các tham số tương ứng của chúng",
            correct: true
        },
        {
			text: "Trạng thái duy trì dữ liệu",
			explain: "Gradio có khả năng thêm trạng thái vào giao diện của bạn.",
			correct: true
        },
        {
			text: "Xác thực tên người dùng và mật khẩu",
			explain: "Chuyển danh sách tên người dùng/mật khẩu vào phương thức khởi chạy để thêm xác thực.",
			correct: true
        },
        {
			text: "Phân tích tự động cho những ai sử dụng bản demo gradio của bạn",
			explain: "Hãy thử lại - Gradio không cung cấp phân tích cho các nhà phát triển về những người sử dụng bản demo của họ."
        },
        {
			text: "Tải mô hình từ Model Hub của Hugging Face hoặc Hugging Face Spaces",
			explain: "Chắc chắn rồi - tải bất kỳ mô hình Hugging Face nào qua phương thức <code>gr.Interface.load()</code>",
			correct: true
        }
	]}
/>

### 6. Cách nào sau đây là cách hợp lệ để tải mô hìnhHugging Face từ Hub hoặc Spaces?

<Question
	choices={[
        {
			text: "gr.Interface.load('huggingface/{user}/{model_name}')",
			explain: "Đây là phương pháp hợp lệ để tải mô hình Hugging Face từ Hub",
            correct: true
        },
        {
			text: "gr.Interface.load('model/{user}/{model_name}')",
			explain: "Đây là phương pháp hợp lệ để tải mô hình Hugging Face từ Hub",
			correct: true
        },
        {
			text: "gr.Interface.load('demos/{user}/{model_name}')",
			explain: "Hãy thử lại - bạn không thể tải mô hình bằng cách sử dụng tiền tố 'demos'."
        },
        {
			text: "gr.Interface.load('spaces/{user}/{model_name}')",
			explain: "Đây là phương pháp hợp lệ để tải mô hình Hugging Face từ Spaces",
			correct: true
        }
	]}
/>

### 7. Chọn tất cả các bước cần thiết để thêm trạng thái vào giao diện Gradio của bạn

<Question
	choices={[
        {
			text: "Truyền một tham số bổ sung vào hàm dự đoán của bạn, thể hiện trạng thái của giao diện.",
			explain: "Cần có thêm một tham số lưu trữ lịch sử hoặc trạng thái giao diện của bạn.",
            correct: true
        },
        {
			text: "Ở cuối hàm dự đoán, hãy trả về giá trị đã cập nhật của trạng thái dưới dạng giá trị trả về bổ sung.",
			explain: "Giá trị lịch sử hoặc trạng thái này cần được hàm của bạn trả về.",
            correct: true
        },
        {
			text: "Thêm thành phần đầu vào trạng thái và thành phần đầu ra trạng thái khi tạo Interface của bạn",
			explain: "Gradio cung cấp thành phần đầu vào và đầu ra trạng thái để duy trì dữ liệu.",
            correct: true
        }
	]}
/>

### 8. Những thành phần nào sau đây có trong thư viện Gradio?

<Question
	choices={[
        {
			text: "Textbox.",
			explain: "Đúng, bạn có thể tạo hộp văn bản với thành phần Hộp văn bản.",
            correct: true
        },
        {
			text: "Graph.",
			explain: "Hiện không có thành phần này.",
        },
        {
			text: "Image.",
			explain: "Đúng, bạn có thể tạo tiện ích tải lên hình ảnh bằng thành phần Image.",
            correct: true
        },
        {
			text: "Audio.",
			explain: "Đúng, bạn có thể tạo tiện ích tải lên âm thanh bằng thành phần Audio.",
            correct: true
        },
	]}
/>

### 9. Gradio `Blocks` cho phép bạn làm gì?

<Question
	choices={[
        {
			text: "Kết hợp nhiều bản demo vào một ứng dụng web",
			explain: "Bạn có thể sử dụng `with gradio.Tabs():` để thêm các tab cho nhiều bản demo",
			correct: true
        },
        {
			text: "Gán các trình kích hoạt sự kiện, chẳng hạn như được nhấp/thay đổi/ v.v. cho các thành phần `Blocks`",
			explain: "Khi bạn gán một sự kiện, bạn truyền vào ba tham số: fn: hàm cần được gọi, inputs: (danh sách) của (các) thành phần đầu vào và outputs: (danh sách) các thành phần đầu ra nên gọi là.",
			correct: true
        },
        {
			text: "Tự động xác định thành phần `Blocks` nào nên tương tác so với tĩnh",
			explain: "Dựa trên các trình kích hoạt sự kiện mà bạn xác định,`Blocks` tự động tìm ra liệu một thành phần có nên chấp nhận đầu vào của người dùng hay không.",
			correct: true
        },
		 {
			text: "Tạo bản demo đa bước; nghĩa là cho phép bạn sử dụng lại đầu ra của một thành phần làm đầu vào cho thành phần tiếp theo",
			explain: "Bạn có thể sử dụng một thành phần cho đầu vào của một trình kích hoạt sự kiện nhưng là đầu ra của một thành phần khác.",
            correct: true
        },
	]}
/>

### 10. Bạn có thể chia sẻ liên kết công khai tới `Blocks` demo và tổ chức lưu trữ `Blocks` demo trên Hugging Face Spaces.

<Question
	choices={[
        {
			text: "Đúng",
			explain: "Cũng giống như `Interface`, tất cả các khả năng chia sẻ và lưu trữ các bản demo của `Blocks` đều giống nhau!",
			correct: true
        },
        {
			text: "Sai",
			explain: "Cũng giống như `Interface`,tất cả các khả năng chia sẻ và lưu trữ các bản demo của `Blocks` đều giống nhau!",
			correct: false
        }
	]}
/>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter9/9.mdx" />

### Tích hợp với Hugging Face Hub
https://huggingface.co/learn/course/vi/chapter9/5.md

# Tích hợp với Hugging Face Hub

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter9/section5.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter9/section5.ipynb"},
]} />

Để làm cho cuộc sống của bạn trở nên dễ dàng hơn, Gradio tích hợp trực tiếp với Hugging Face Hub và Hugging Face Spaces. Bạn có thể tải các bản demo từ Hub và Spaces chỉ với *một dòng mã*.

### Tải mô hình từ Hugging Face Hub

Để bắt đầu, hãy chọn một trong số hàng nghìn mô hình Hugging Face được cung cấp thông qua Hub, như được mô tả trong [Chương 4](/course/chapter4/2).

Sử dụng phương thức `Interface.load()` đặc biệt, bạn truyền `"model/"` (hoặc, tương đương, `"huggingface/"`) theo sau là tên mô hình. Ví dụ: đây là mã để tạo bản demo cho [GPT-J](https://huggingface.co/EleutherAI/gpt-j-6B), một mô hình ngôn ngữ lớn, hãy thêm một số đầu vào mẫu:

```py
import gradio as gr

title = "GPT-J-6B"
description = "Gradio Demo for GPT-J 6B, a transformer model trained using Ben Wang's Mesh Transformer JAX. 'GPT-J' refers to the class of model, while '6B' represents the number of trainable parameters. To use it, simply add your text, or click one of the examples to load them. Read more at the links below."
article = "<p style='text-align: center'><a href='https://github.com/kingoflolz/mesh-transformer-jax' target='_blank'>GPT-J-6B: A 6 Billion Parameter Autoregressive Language Model</a></p>"
examples = [
    ["The tower is 324 metres (1,063 ft) tall,"],
    ["The Moon's orbit around Earth has"],
    ["The smooth Borealis basin in the Northern Hemisphere covers 40%"],
]
gr.Interface.load(
    "huggingface/EleutherAI/gpt-j-6B",
    inputs=gr.Textbox(lines=5, label="Input Text"),
    title=title,
    description=description,
    article=article,
    examples=examples,
    enable_queue=True,
).launch()
```

Đoạn mã trên sẽ tạo ra giao diện bên dưới:

<iframe src="https://course-demos-gpt-j-6B.hf.space" frameBorder="0" height="750" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Tải mô hình theo cách này sử dụng [API luận suy](https://huggingface.co/inference-api) của Hugging Face, thay vì tải mô hình trong bộ nhớ. Điều này lý tưởng cho các mô hình lớn như GPT-J hoặc T0pp, những mô hình yêu cầu nhiều RAM.

### Tải từ Hugging Face Spaces

Để tải bất kỳ Space nào từ Hugging Face Hub và tạo lại nó cục bộ, bạn có thể truyền  `spaces/` vào `Interface`, theo sau là tên của Space.

Bạn có nhớ bản demo từ phần 1 xóa nền của hình ảnh không? Hãy tải nó từ Hugging Face Spaces:

```py
gr.Interface.load("spaces/abidlabs/remove-bg").launch()
```

<iframe src="https://course-demos-remove-bg-original.hf.space" frameBorder="0" height="650" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Một trong những điều thú vị khi tải các bản demo từ Hub hoặc Spaces là bạn tùy chỉnh chúng bằng cách ghi đè bất kỳ thông số nào. Ở đây, chúng ta thêm tiêu đề và làm cho tiêu đề đó hoạt động với webcam:

```py
gr.Interface.load(
    "spaces/abidlabs/remove-bg", inputs="webcam", title="Remove your webcam background!"
).launch()
```

<iframe src="https://course-demos-Remove-bg.hf.space" frameBorder="0" height="550" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Bây giờ chúng ta đã khám phá một số cách để tích hợp Gradio với Hugging Face Hub, hãy cùng xem xét một số tính năng nâng cao của lớp `Interface`. Đó là chủ đề của phần tiếp theo!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter9/5.mdx" />

### Giới thiệu
https://huggingface.co/learn/course/vi/chapter9/1.md

# Giới thiệu

Trong chương này, chúng ta sẽ tìm hiểu về cách tạo **demo tương tác** cho các mô hình học máy của bạn.

Tại sao ta nên xây dựng bản demo hoặc GUI cho mô hình học máy của bạn? Bản demo cho phép:

- **Các nhà phát triển học máy** dễ dàng trình bày công việc của họ cho nhiều đối tượng bao gồm cả các nhóm không chuyên về kỹ thuật hoặc khách hàng
- **Các nhà nghiên cứu** dễ dàng tái tạo các mô hình và hành vi học máy hơn
- **Người kiểm tra chất lượng** hoặc **người dùng cuối** dễ dàng xác định và gỡ lỗi các điểm hỏng hóc của mô hình
- **Người dùng đa dạng** khám phá các sai lệch của ​​thuật toán trong các mô hình

Chúng ta sẽ sử dụng thư viện Gradio để xây dựng các bản demo cho các mô hình của mình. Gradio cho phép bạn xây dựng, tùy chỉnh và chia sẻ các bản demo trên web cho bất kỳ mô hình học máy nào, hoàn toàn bằng Python.

Dưới đây là một số ví dụ về demo học máy được xây dựng với Gradio:

* Một mô hình **nhận dạng phác thảo** nhận bản phác thảo và xuất ra các nhãn của những gì nó cho là đang được vẽ:

<iframe src="https://course-demos-draw2.hf.space" frameBorder="0" height="450" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

* Mô hình **hỏi đáp** khai thác lấy trong một đoạn ngữ cảnh và một câu hỏi và đưa ra một câu trả lời và điểm xác suất (chúng ta đã thảo luận về loại mô hình này [trong Chương 7](/course/chapter7/7)):

<iframe src="https://course-demos-question-answering-simple.hf.space" frameBorder="0" height="640" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

* Một mô hình **xóa nền** nhận vào một hình ảnh và xuất ra hình ảnh với nền đã bị xóa:

<iframe src="https://course-demos-remove-bg-original.hf.space" frameBorder="0" height="640" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Chương này được chia thành các phần bao gồm cả _khái niệm_ và _ứng dụng_. Sau khi bạn tìm hiểu khái niệm trong mỗi phần, bạn sẽ áp dụng nó để xây dựng một loại bản demo cụ thể, từ phân loại hình ảnh đến nhận dạng giọng nói. Vào thời điểm bạn hoàn thành chương này, bạn sẽ có thể xây dựng các bản demo này (và nhiều hơn nữa!) Chỉ trong một vài dòng mã Python.

<Tip>
👀 Hãy ngó thử <a href="https://huggingface.co/spaces" target="_blank">Hugging Face Spaces</a> để xem nhiều ví dụ gần đây về các bản demo học máy do cộng đồng học máy xây dựng!

</Tip>

## Bữa tiệc Gradio

Nếu bạn muốn vận dụng tốt kiến thức từ chương này, hãy tham gia bữa tiệc Gradio! Đây là sự kiện cộng đồng do Hugging Face tổ chức vào ngày **16-31 tháng 5**. Trong sự kiện này, bạn sẽ xây dựng các bản demo học máy thú vị với Gradio và đang chạy để giành chiến thắng và giải thưởng Hugging Face swag!

Xem [mô tả sự kiện](https://github.com/AK391/community-events/blob/main/gradio-blocks/README.md) để biết chi tiết về cách tham gia - chúng tôi rất nóng lòng được biết các bản demo bạn sẽ xây dựng 🤗!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter9/1.mdx" />

### Hiểu lớp Interface
https://huggingface.co/learn/course/vi/chapter9/3.md

# Hiểu lớp Interface

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter9/section3.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter9/section3.ipynb"},
]} />

Trong phần này, chúng ta sẽ xem xét kỹ hơn về lớp `Interface` và hiểu các tham số chính được sử dụng để tạo ra nó.

## Cách tạo một Interface

Bạn sẽ nhận thấy rằng lớp `Interface` có 3 tham số bắt buộc:

`Interface(fn, inputs, outputs, ...)`

Các tham số này là:

  - `fn`: hàm dự đoán được bao bọc bởi giao diện Gradio. Hàm này có thể nhận một hoặc nhiều tham số và trả về một hoặc nhiều giá trị
  - `inputs`: (các) loại thành phần đầu vào. Gradio cung cấp nhiều thành phần được tạo sẵn như`"image"` hay `"mic"`.
  - `outputs`: (các) loại thành phần đầu ra. Một lần nữa, Gradio cung cấp nhiều thành phần được tạo sẵn, ví dụ: `"image"` hay `"label"`.

Để có danh sách đầy đủ các thành phần, [xem tài liệu Gradio](https://gradio.app/docs). Mỗi thành phần được tạo sẵn có thể được tùy chỉnh bằng cách khởi tạo lớp tương ứng với thành phần.

Ví dụ: như chúng ta đã thấy trong [phần trước](/course/chapter9/2), thay vì truyền tham số `input` vào trong `"textbox"`, bạn có thể truyền vào `Textbox(lines=7, label="Prompt")` để tạo một hộp văn bản có 7 dòng và một nhãn.

Hãy xem một ví dụ khác, lần này với thành phần `Audio`.

## Một ví dụ đơn giản với âm thanh

Như đã đề cập trước đó, Gradio cung cấp nhiều đầu vào và đầu ra khác nhau.
Vì vậy, hãy xây dựng một  `Interface` hoạt động với âm thanh.

Trong ví dụ này, chúng tôi sẽ xây dựng một hàm chuyển đổi âm thanh sang âm thanh mà nhận tập tin âm thanh và chỉ cần đảo ngược nó.

Chúng ta sẽ sử dụng thành phần `Audio` cho đầu vào. Khi sử dụng thành phần `Audio`, bạn có thể chỉ định xem bạn có muốn `source` của âm thanh là một tệp mà người dùng
tải lên hoặc micrô mà người dùng ghi lại giọng nói của họ. Trong trường hợp này, hãy đặt nó thành `"microphone"`. Chỉ cho vui thôi, chúng ta sẽ thêm một nhãn vào phần  `Audio` của mình có nội dung "Speak here...", nghĩa là "Nói ở đây ...".

Ngoài ra, chúng ta muốn nhận âm thanh dưới dạng mảng numpy để ta có thể dễ dàng "đảo ngược" nó lại. Vì vậy, chúng ta sẽ đặt `"type"` là `"numpy"`, chuyển đầu vào
dữ liệu dưới dạng một bộ (`sample_rate`, `data`) trong hàm của chúng ta.

Chúng ta cũng sẽ sử dụng thành phần đầu ra `Audio` có thể tự động hiển thị một bộ tuple với tốc độ mẫu và mảng dữ liệu phức tạp dưới dạng tệp âm thanh có thể phát. Trong trường hợp này, chúng ta không cần thực hiện bất kỳ tùy chỉnh nào, vì vậy cta sẽ sử dụng chuỗi phím tắt `"audio"`.


```py
import numpy as np
import gradio as gr


def reverse_audio(audio):
    sr, data = audio
    reversed_audio = (sr, np.flipud(data))
    return reversed_audio


mic = gr.Audio(source="microphone", type="numpy", label="Speak here...")
gr.Interface(reverse_audio, mic, "audio").launch()
```

Đoạn mã trên sẽ tạo ra một giao diện giống như bên dưới (nếu trình duyệt của bạn không yêu cầu bạn cấp quyền đối với micrô, <a href="https://huggingface.co/spaces/course-demos/audio-reverse" target="_blank">mở bản demo sang một tab khác</a>.)

<iframe src="https://course-demos-audio-reverse.hf.space" frameBorder="0" height="250" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Bây giờ bạn có thể ghi lại giọng nói của mình và nghe thấy chính mình đang nói ngược lại - thật ma quái 👻!

## Xử lý nhiều đầu vào và đầu ra

Giả sử chúng ta có một hàm phức tạp hơn, với nhiều đầu vào và đầu ra. Trong ví dụ dưới đây, chúng ta có một hàm lấy chỉ mục thả xuống, giá trị thanh trượt và số, và trả về một mẫu âm thanh của một giai điệu âm nhạc.

Hãy xem cách chúng ta chuyển danh sách các thành phần đầu vào và đầu ra, và xem liệu bạn có thể theo dõi những gì đang xảy ra không.

Chìa khóa ở đây là khi bạn truyền vào:
* danh sách các thành phần đầu vào, mỗi thành phần tương ứng với một tham số theo thứ tự.
* danh sách các thành phần đầu ra, mỗi thành phần tương ứng với một giá trị trả về.

Đoạn mã bên dưới cho thấy cách ba thành phần đầu vào xếp hàng với ba tham số của hàm `generate_tone()`:

```py
import numpy as np
import gradio as gr

notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]


def generate_tone(note, octave, duration):
    sr = 48000
    a4_freq, tones_from_a4 = 440, 12 * (octave - 4) + (note - 9)
    frequency = a4_freq * 2 ** (tones_from_a4 / 12)
    duration = int(duration)
    audio = np.linspace(0, duration, duration * sr)
    audio = (20000 * np.sin(audio * (2 * np.pi * frequency))).astype(np.int16)
    return (sr, audio)


gr.Interface(
    generate_tone,
    [
        gr.Dropdown(notes, type="index"),
        gr.Slider(minimum=4, maximum=6, step=1),
        gr.Textbox(type="number", value=1, label="Duration in seconds"),
    ],
    "audio",
).launch()
```

<iframe src="https://course-demos-generate-tone.hf.space" frameBorder="0" height="450" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

### Phương thức `launch()`

Cho đến nay, chúng tôi đã sử dụng phương thức `launch()` để khởi chạy giao diện, nhưng chúng ta chưa thực sự thảo luận về những gì nó làm.

Theo mặc định, phương thức `launch()` sẽ khởi chạy bản demo trong một máy chủ web đang chạy cục bộ. Nếu bạn đang chạy mã của mình trong notebook Jupyter hoặc Colab, thì Gradio sẽ nhúng GUI demo vào notebook để bạn có thể dễ dàng sử dụng.

Bạn có thể tùy chỉnh hành vi của `launch()` thông qua các tham số khác nhau:

   - `inline` - có hiển thị giao diện nội tuyến trên notebook Python hay không.
   - `inbrowser` - có tự động khởi chạy giao diện trong tab mới trên trình duyệt mặc định hay không.
   - `share` - có tạo một liên kết có thể chia sẻ công khai từ máy tính của bạn cho giao diện hay không. Giống như một liên kết Google Drive!

Chúng tôi sẽ trình bày chi tiết hơn về tham số `share` trong phần tiếp theo!

## ✏️ Hãy áp dụng nó!

Hãy xây dựng một giao diện cho phép bạn giới thiệu mô hình **nhận dạng giọng nói**. Để làm cho nó thú vị, chúng ta sẽ chấp nhận hoặc đầu vào micrô hoặc một tệp đã tải lên.

Như thường lệ, chúng ta sẽ tải mô hình nhận dạng giọng nói của mình bằng cách sử dụng hàm `pipeline()` từ 🤗 Transformers.
Nếu bạn cần cập nhật nhanh, bạn có thể quay lại [phần đó trong Chương 1](/course/chapter1/3). Tiếp theo, chúng ta sẽ triển khai một hàm `transcribe_audio()` để xử lý âm thanh và trả về phiên âm. Cuối cùng, chúng ta sẽ gói hàm này trong một `Interface` với các thành phần `Audio` cho đầu vào và chỉ văn bản cho đầu ra. Nhìn chung, mã cho ứng dụng này như sau:

```py
from transformers import pipeline
import gradio as gr

model = pipeline("automatic-speech-recognition")


def transcribe_audio(mic=None, file=None):
    if mic is not None:
        audio = mic
    elif file is not None:
        audio = file
    else:
        return "You must either provide a mic recording or a file"
    transcription = model(audio)["text"]
    return transcription


gr.Interface(
    fn=transcribe_audio,
    inputs=[
        gr.Audio(source="microphone", type="filepath", optional=True),
        gr.Audio(source="upload", type="filepath", optional=True),
    ],
    outputs="text",
).launch()
```

Nếu trình duyệt của bạn không yêu cầu bạn cấp quyền đối với micrô, hãy <a href="https://huggingface.co/spaces/course-demos/audio-reverse" target="_blank">mở bản demo trong một tab riêng</a>.

<iframe src="https://course-demos-asr.hf.space" frameBorder="0" height="550" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Nó đó! Bây giờ bạn có thể sử dụng giao diện này để phiên âm âm thanh. Chú ý ở đây rằng bằng cách đặt tham số `option` là `True`, chúng ta cho phép người dùng cung cấp micrô hoặc tệp âm thanh (hoặc không, nhưng điều đó sẽ trả lại thông báo lỗi).

Tiếp tục xem cách chia sẻ giao diện của bạn với những người khác!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter9/3.mdx" />

### Các tính năng nâng cao của Interface
https://huggingface.co/learn/course/vi/chapter9/6.md

# Các tính năng nâng cao của Interface

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter9/section6.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter9/section6.ipynb"},
]} />

Bây giờ chúng ta có thể xây dựng và chia sẻ giao diện cơ bản, hãy cùng khám phá một số tính năng nâng cao hơn như trạng thái và diễn giải.

### Sử dụng trạng thái để duy trì dữ liệu

Gradio hỗ trợ *trạng thái phiên*, nơi dữ liệu tồn tại qua nhiều lần gửi trong một tải trang. Trạng thái phiên hữu ích khi xây dựng các bản demo, chẳng hạn như chatbot ở nơi bạn muốn giữ nguyên dữ liệu khi người dùng tương tác với mô hình. Lưu ý rằng trạng thái phiên không chia sẻ dữ liệu giữa những người dùng khác nhau trong mô hình của bạn.

Để lưu trữ dữ liệu ở trạng thái phiên, bạn cần thực hiện ba việc:

1. Truyền một *tham số bổ sung* vào hàm của bạn, nó thể hiện trạng thái của giao diện.
1. Khi kết thúc hàm, trả về giá trị đã cập nhật của trạng thái dưới dạng *giá trị trả về bổ sung*.
1. Thêm thành phần đầu vào 'state' và đầu ra 'state' khi tạo `Interface` của bạn.

Xem ví dụ về chatbot bên dưới:

```py
import random

import gradio as gr


def chat(message, history):
    history = history or []
    if message.startswith("How many"):
        response = random.randint(1, 10)
    elif message.startswith("How"):
        response = random.choice(["Great", "Good", "Okay", "Bad"])
    elif message.startswith("Where"):
        response = random.choice(["Here", "There", "Somewhere"])
    else:
        response = "I don't know"
    history.append((message, response))
    return history, history


iface = gr.Interface(
    chat,
    ["text", "state"],
    ["chatbot", "state"],
    allow_screenshot=False,
    allow_flagging="never",
)
iface.launch()
```

<iframe src="https://course-demos-Chatbot-Demo.hf.space" frameBorder="0" height="350" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Lưu ý trạng thái của thành phần đầu ra vẫn tồn tại qua các lần gửi.
Lưu ý: bạn có thể chuyển giá trị mặc định vào tham số trạng thái, được sử dụng làm giá trị ban đầu của trạng thái.

### Sử dụng diễn giải để hiểu các dự đoán

Hầu hết các mô hình học máy là hộp đen và logic bên trong của hàm bị ẩn khỏi người dùng cuối. Để khuyến khích tính minh bạch, chúng tôi đã giúp bạn dễ dàng thêm thông dịch vào mô hình của mình bằng cách chỉ cần đặt từ khóa thông dịch trong lớp Interface thành mặc định. Điều này cho phép người dùng của bạn hiểu những phần nào của đầu vào chịu trách nhiệm cho đầu ra. Hãy xem giao diện đơn giản bên dưới hiển thị bộ phân loại hình ảnh đồng thời bao gồm phần diễn giải:

```py
import requests
import tensorflow as tf

import gradio as gr

inception_net = tf.keras.applications.MobileNetV2()  # tải mô hình

# Tải nhãn con người đọc được cho ImageNet.
response = requests.get("https://git.io/JJkYN")
labels = response.text.split("\n")


def classify_image(inp):
    inp = inp.reshape((-1, 224, 224, 3))
    inp = tf.keras.applications.mobilenet_v2.preprocess_input(inp)
    prediction = inception_net.predict(inp).flatten()
    return {labels[i]: float(prediction[i]) for i in range(1000)}


image = gr.Image(shape=(224, 224))
label = gr.Label(num_top_classes=3)

title = "Gradio Image Classifiction + Interpretation Example"
gr.Interface(
    fn=classify_image, inputs=image, outputs=label, interpretation="default", title=title
).launch()
```

Kiểm tra hàm thông dịch bằng cách gửi đầu vào, sau đó nhấp vào Interpret tương ứng diễn giải bên dưới thành phần đầu ra.

<iframe src="https://course-demos-gradio-image-interpretation.hf.space" frameBorder="0" height="570" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Bên cạnh phương pháp diễn giải mặc định mà Gradio cung cấp, bạn cũng có thể chỉ định `shap` cho tham số `interpretation` và đặt tham số `num_shap`. Điều này sử dụng diễn giải dựa trên Shapley, bạn có thể đọc thêm về [tại đây](https://christophm.github.io/interpretable-ml-book/shap.html).
Cuối cùng, bạn cũng có thể chuyển hàm thông dịch của riêng mình vào tham số `interpretation`. Xem ví dụ trong trang bắt đầu của Gradio [tại đây](https://gradio.app/getting_started/).

Điều này kết thúc việc đi sâu vào lớp `Interface` của Gradio. Như chúng ta đã thấy, lớp này giúp việc tạo trình diễn học máy trở nên đơn giản trong một vài dòng mã Python. Tuy nhiên, đôi khi bạn sẽ muốn tùy chỉnh bản demo của mình bằng cách thay đổi bố cục hoặc xâu chuỗi nhiều hàm dự đoán lại với nhau. Sẽ thật tuyệt nếu bằng cách nào đó chúng ta có thể chia `Interface` thành các "khối" có thể tùy chỉnh? May mắn thay, có! Đó là chủ đề của phần cuối cùng.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter9/6.mdx" />

### Gradio, kiểm tra nào!
https://huggingface.co/learn/course/vi/chapter9/8.md

# Gradio, kiểm tra nào!

Điều này kết thúc chương về xây dựng các bản demo ML thú vị với Gradio - chúng tôi hy vọng bạn thích nó! Tóm lại, trong chương này, chúng ta đã học:

- Cách tạo bản demo Gradio với API `Interface` cấp cao và cách định cấu hình các phương thức đầu vào và đầu ra khác nhau.
- Các cách khác nhau để chia sẻ bản demo Gradio, thông qua các liên kết tạm thời và lưu trữ trên [Hugging Face Spaces](https://huggingface.co/spaces).
- Cách tích hợp bản demo Gradio với mô hình và Hugging Face Spaces.
- Các tính năng nâng cao như lưu trữ trạng thái trong bản demo hoặc cung cấp xác thực.
- Làm thế nào để có toàn quyền kiểm soát luồng dữ liệu và bố cục của bản demo của bạn với Gradio Blocks.

Nếu bạn muốn kiểm tra sự hiểu biết của mình về các khái niệm được đề cập trong chương này, hãy xem bài kiểm tra trong phần tiếp theo!

## Tiếp theo là đâu?

Nếu bạn muốn tìm hiểu thêm về Gradio, bạn có thể

- Hãy xem [Demo](https://github.com/gradio-app/gradio/tree/main/demo) trong kho, có khá nhiều ví dụ ở đó.
- Xem trang [Hướng dẫn](https://gradio.app/guides/), nơi bạn có thể tìm thấy hướng dẫn về các tính năng thú vị và nâng cao.
- Xem trang [Tài liệu](https://gradio.app/docs/) để biết thêm chi tiết.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter9/8.mdx" />

### Chia sẻ các bản demo với người khác
https://huggingface.co/learn/course/vi/chapter9/4.md

# Chia sẻ các bản demo với người khác

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter9/section4.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter9/section4.ipynb"},
]} />

Bây giờ bạn đã xây dựng một bản demo, có thể bạn sẽ muốn chia sẻ nó với những người khác. Các bản demo Gradio có thể được chia sẻ theo hai cách: sử dụng một ***đường dẫn chia sẻ tạm thời*** hoặc ***lưu trữ vĩnh viễn trên Spaces***.

Chúng tôi sẽ đề cập đến cả hai cách tiếp cận này ngay sau đây. Nhưng trước khi chia sẻ bản demo của mình, bạn có thể muốn đánh bóng nó 💅.

### Đánh bóng Gradio demo của bạn:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter9/gradio-demo-overview.png" alt="Overview of a gradio interface">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter9/gradio-demo-overview-dark.png" alt="Overview of a gradio interface">
</div>

Để thêm nội dung bổ sung vào bản demo của bạn, lớp `Interface` hỗ trợ một số tham số tùy chọn:
    - `title`: bạn có thể đặt tiêu đề cho bản demo của mình, xuất hiện _phía trên_ các thành phần đầu vào và đầu ra.
    - `description`: bạn có thể đưa ra mô tả (bằng văn bản, Markdown hoặc HTML) cho giao diện, xuất hiện phía trên các thành phần đầu vào và đầu ra và bên dưới tiêu đề.
    - `article`: bạn cũng có thể viết một bài báo mở rộng (bằng văn bản, Markdown hoặc HTML) giải thích về giao diện. Nếu được cung cấp, nó sẽ xuất hiện _bên dưới_ các thành phần đầu vào và đầu ra.
    - `theme`: bạn không thích màu mặc định? Đặt chủ đề để sử dụng một trong các `default`, `huggingface`, `grass`, `peach`. Bạn cũng có thể thêm tiền tố `dark-`, ví dụ: `dark-peach` cho chủ đề tối (hoặc chỉ `dark` cho chủ đề tối mặc định).
    - `examples`: để làm cho bản demo của bạn *dễ sử dụng hơn*, bạn có thể cung cấp một số đầu vào ví dụ cho hàm. Chúng xuất hiện bên dưới các thành phần giao diện người dùng và có thể được sử dụng để điền vào giao diện. Chúng phải được cung cấp dưới dạng danh sách lồng nhau, trong đó danh sách bên ngoài bao gồm các mẫu và mỗi danh sách bên trong bao gồm một đầu vào tương ứng với mỗi thành phần đầu vào.
    - `live`: nếu bạn muốn làm cho bản demo của mình "sống động", nghĩa là mô hình của bạn chạy lại mỗi khi đầu vào thay đổi, bạn có thể đặt `live=True`. Điều này hợp lý khi sử dụng với các mô hình nhanh (chúng ta sẽ xem một ví dụ ở cuối phần này)
Sử dụng các tùy chọn ở trên, chúng ta kết thúc với một giao diện hoàn chỉnh hơn. Chạy đoạn mã dưới đây để bạn có thể trò chuyện với Rick và Morty:

```py
title = "Ask Rick a Question"
description = """
The bot was trained to answer questions based on Rick and Morty dialogues. Ask Rick anything!
<img src="https://huggingface.co/spaces/course-demos/Rick_and_Morty_QA/resolve/main/rick.png" width=200px>
"""

article = "Check out [the original Rick and Morty Bot](https://huggingface.co/spaces/kingabzpro/Rick_and_Morty_Bot) that this demo is based off of."

gr.Interface(
    fn=predict,
    inputs="textbox",
    outputs="text",
    title=title,
    description=description,
    article=article,
    examples=[["What are you doing?"], ["Where should we time travel to?"]],
).launch()
```

Sử dụng các tùy chọn ở trên, chúng ta kết thúc với một giao diện hoàn chỉnh hơn. Hãy thử giao diện bên dưới:

<iframe src="https://course-demos-Rick-and-Morty-QA.hf.space" frameBorder="0" height="800" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

### Chia sẻ bản demo của bạn với các liên kết tạm thời

Bây giờ chúng ta đã có bản demo hoạt động của mô hình học máy của mình, hãy tìm hiểu cách dễ dàng chia sẻ liên kết đến giao diện của chúng ta.
Các giao diện có thể dễ dàng được chia sẻ công khai bằng cách đặt `share=True` trong phương thức `launch()`:

```python
gr.Interface(classify_image, "image", "label").launch(share=True)
```

Điều này tạo ra một liên kết công khai, có thể chia sẻ mà bạn có thể gửi cho bất kỳ ai! Khi bạn gửi liên kết này, người dùng ở phía bên kia có thể dùng thử mô hình trong trình duyệt của họ trong tối đa 72 giờ. Vì quá trình xử lý diễn ra trên thiết bị của bạn (miễn là thiết bị của bạn vẫn bật!), Bạn không phải lo lắng về việc đóng gói bất kỳ thư viện phụ thuộc nào. Nếu bạn đang làm việc với notebook Google Colab, liên kết chia sẻ luôn được tạo tự động. Nó thường trông giống như sau: **XXXXX.gradio.app**. Mặc dù liên kết được cung cấp thông qua liên kết Gradio, ta chỉ là một proxy cho máy chủ cục bộ của mình và không lưu trữ bất kỳ dữ liệu nào được gửi qua các giao diện.

Tuy nhiên, hãy nhớ rằng các liên kết này có thể truy cập công khai, có nghĩa là bất kỳ ai cũng có thể sử dụng mô hình của bạn để dự đoán! Do đó, hãy đảm bảo không để lộ bất kỳ thông tin nhạy cảm nào thông qua các hàm bạn viết hoặc cho phép bất kỳ thay đổi quan trọng nào xảy ra trên thiết bị của bạn. Nếu bạn đặt `share=False` (mặc định), chỉ một liên kết cục bộ được tạo.

### Lưu trữ bản demo của bạn trên Hugging Face Spaces

Một đường dẫn liên kết mà bạn có thể chia sẻ cho các đồng nghiệp thật tuyệt, nhưng làm thế nào bạn có thể lưu trữ vĩnh viễn bản demo của mình và để bản demo tồn tại trong "không gian" riêng của nó trên internet?

Hugging Face Space cung cấp cơ sở hạ tầng để lưu trữ vĩnh viễn mô hình Gradio của bạn trên internet, **miễn phí**! Spaces cho phép bạn tạo và đẩy lên kho (công khai hoặc riêng tư), nơi giao diện Gradio của bạn sẽ tồn tại trong tệp `app.py`. [Đọc hướng dẫn từng bước](https://huggingface.co/blog/gradio-spaces) để bắt đầu hoặc xem video ví dụ bên dưới.

<Youtube id="LS9Y2wDVI0k" />

## ✏️ Cùng áp dụng nhé!

Sử dụng những gì chúng ta vừa học được trong các phần cho đến nay, hãy tạo bản demo nhận dạng phác thảo mà ta đã thấy trong [phần một của chương này](/course/chapter9/1). Hãy thêm một số tùy chỉnh vào giao diện và đặt `share=True` để tạo một liên kết công khai mà ta có thể chia sẻ.

Chúng ra có thể tải các nhãn từ [class_names.txt](https://huggingface.co/spaces/dawood/Sketch-Recognition/blob/main/class_names.txt) và tải mô hình pytorch được huấn luyện trước từ [pytorch_model.bin] (https://huggingface.co/spaces/dawood/Sketch-Recognition/blob/main/pytorch_model.bin). Tải xuống các tệp này bằng cách nhấp vào liên kết và nhấp vào tải xuống ở góc trên cùng bên trái của bản xem trước tệp. Hãy xem đoạn mã bên dưới để biết cách chúng ta sử dụng các tệp này để tải mô hình của mình và tạo hàm `predict()`:

```py
from pathlib import Path
import torch
import gradio as gr
from torch import nn

LABELS = Path("class_names.txt").read_text().splitlines()

model = nn.Sequential(
    nn.Conv2d(1, 32, 3, padding="same"),
    nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Conv2d(32, 64, 3, padding="same"),
    nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Conv2d(64, 128, 3, padding="same"),
    nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Flatten(),
    nn.Linear(1152, 256),
    nn.ReLU(),
    nn.Linear(256, len(LABELS)),
)
state_dict = torch.load("pytorch_model.bin", map_location="cpu")
model.load_state_dict(state_dict, strict=False)
model.eval()


def predict(im):
    x = torch.tensor(im, dtype=torch.float32).unsqueeze(0).unsqueeze(0) / 255.0
    with torch.no_grad():
        out = model(x)
    probabilities = torch.nn.functional.softmax(out[0], dim=0)
    values, indices = torch.topk(probabilities, 5)
    return {LABELS[i]: v.item() for i, v in zip(indices, values)}
```

Giờ ta đã có hàm `predict()`. Bước tiếp theo là định nghỉa và khởi chạy giao diện gradio:

```py
interface = gr.Interface(
    predict,
    inputs="sketchpad",
    outputs="label",
    theme="huggingface",
    title="Sketch Recognition",
    description="Who wants to play Pictionary? Draw a common object like a shovel or a laptop, and the algorithm will guess in real time!",
    article="<p style='text-align: center'>Sketch Recognition | Demo Model</p>",
    live=True,
)
interface.launch(share=True)
```

<iframe src="https://course-demos-Sketch-Recognition.hf.space" frameBorder="0" height="650" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Lưu ý tham số `live=True` trong `Interface`, có nghĩa là bản demo phác thảo tạo ra dự đoán mỗi khi ai đó vẽ trên sketchpad (không có nút gửi!).

Hơn nữa, chúng ta cũng đặt tham số `share=True` trong phương thức `launch()`.
Điều này sẽ tạo ra một liên kết công khai mà bạn có thể
gửi cho bất cứ ai! Khi bạn gửi liên kết này, người dùng ở phía bên kia có thể thử mô hình nhận dạng phác thảo. Để nhắc lại, bạn cũng có thể tổ chức mô hình trên Hugging Face Spaces, đó là cách chúng ta có thể nhúng bản demo ở trên.

Tiếp theo, chúng tôi sẽ đề cập đến các cách khác mà Gradio có thể được sử dụng với hệ sinh thái Hugging Face!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter9/4.mdx" />

### Giới thiệu về Gradio Blocks
https://huggingface.co/learn/course/vi/chapter9/7.md

# Giới thiệu về Gradio Blocks

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter9/section7.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter9/section7.ipynb"},
]} />

Trong các phần trước, chúng ta đã tìm hiểu và tạo các bản demo bằng cách sử dụng lớp `Interface`. Trong phần này, chúng tôi sẽ giới thiệu API cấp thấp **mới được phát triển** của mình có tên là `gradio.Blocks`.

Bây giờ, sự khác biệt giữa `Interface` và `Blocks`là gì?

- ⚡ `Interface`: một API cấp cao cho phép bạn tạo một bản demo học máy đầy đủ chỉ đơn giản bằng cách cung cấp danh sách các đầu vào và đầu ra.

- 🧱 `Blocks`: một API cấp thấp cho phép bạn có toàn quyền kiểm soát các luồng dữ liệu và bố cục của ứng dụng của mình. Bạn có thể xây dựng các ứng dụng nhiều bước, rất phức tạp bằng cách sử dụng `Blocks` (như trong "các khối xây dựng").

### Tại sao lại là Blocks 🧱?

Như chúng ta đã thấy trong các phần trước, lớp `Interface` cho phép bạn dễ dàng tạo các bản demo học máy chính thức chỉ với một vài dòng mã. API `Interface` cực kỳ dễ sử dụng nhưng thiếu tính linh hoạt mà API `Blocks` cung cấp. Ví dụ: bạn có thể muốn:

- Nhóm các bản demo có liên quan lại với nhau dưới dạng nhiều tab trong một ứng dụng web
- Thay đổi bố cục của bản demo của bạn, ví dụ: để chỉ định vị trí của các đầu vào và đầu ra
- Có giao diện nhiều bước, trong đó đầu ra của một mô hình trở thành đầu vào cho mô hình tiếp theo hoặc có các luồng dữ liệu linh hoạt hơn nói chung
- Thay đổi thuộc tính của một thành phần (ví dụ: các lựa chọn trong danh sách thả xuống) hoặc khả năng hiển thị của nó dựa trên đầu vào của người dùng

Chúng ta sẽ khám phá tất cả các khái niệm này trong các phần tiếp theo.

### Tạo một bản demo đơn giản bằng cách sử dụng Blocks

Sau khi bạn đã cài đặt Gradio, hãy chạy mã bên dưới dưới dạng tập lệnh Python, notebook Jupyter hoặc sổ ghi chép Colab.

```py
import gradio as gr


def flip_text(x):
    return x[::-1]


demo = gr.Blocks()

with demo:
    gr.Markdown(
        """
    # Flip Text!
    Start typing below to see the output.
    """
    )
    input = gr.Textbox(placeholder="Flip this text")
    output = gr.Textbox()

    input.change(fn=flip_text, inputs=input, outputs=output)

demo.launch()
```

<iframe src="https://course-demos-flip-text.hf.space" frameBorder="0" height="400" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Ví dụ đơn giản ở trên giới thiệu 4 khái niệm làm nền tảng cho Blocks:

1. Blocks cho phép bạn xây dựng các ứng dụng web kết hợp markdown, HTML, các nút và các thành phần tương tác đơn giản bằng cách khởi tạo các đối tượng bằng Python bên trong ngữ cảnh `with gradio.Blocks`.

<Tip>
🙋Nếu bạn không quen với câu lệnh `with` trong Python, chúng tôi khuyên bạn nên xem [hướng dẫn](https://realpython.com/python-with-statement/)  tuyệt vời từ Real Python. Quay lại đây sau khi đọc xong 🤗
</Tip>

Thứ tự mà bạn khởi tạo các thành phần quan trọng khi mỗi phần tử được hiển thị vào ứng dụng web theo thứ tự nó được tạo. (Các bố cục phức tạp hơn được thảo luận bên dưới)

2. Bạn có thể xác định các hàm Python thông thường ở bất kỳ đâu trong mã của mình và chạy chúng với đầu vào của người dùng bằng cách sử dụng `Blocks`. Trong ví dụ của mình, chúng ta có một hàm đơn giản "lật" văn bản đầu vào, nhưng bạn có thể viết bất kỳ hàm Python nào, từ một phép tính đơn giản đến xử lý các dự đoán từ một mô hình học máy.

3. Bạn có thể gán các sự kiện cho bất kỳ thành phần `Blocks` nào. Điều này sẽ chạy hàm của bạn khi thành phần được nhấp, thay đổi, v.v. Khi bạn gán một sự kiện, bạn truyền vào ba tham số: `fn`: hàm cần được gọi,`inputs`: (danh sách) thành phần đầu vào (s), và `outputs`: (danh sách) các thành phần đầu ra cần được gọi.

   Trong ví dụ trên, chúng tôi chạy hàm `flip_text()` khi giá trị trong `Textbox` có tên đầu vào `input` thay đổi. Sự kiện đọc giá trị trong `input`, truyền nó làm tham số tên cho `flip_text()`, sau đó trả về một giá trị được gán cho `Textbox` thứ hai của chúng ta có tên là `output`.

   Để xem danh sách các sự kiện mà mỗi thành phần hỗ trợ, hãy xem [tài liệu](https://www.gradio.app/docs/) Gradio.

4. Các khối tự động tìm ra liệu một thành phần có nên tương tác (chấp nhận đầu vào của người dùng) hay không, dựa trên các trình kích hoạt sự kiện mà bạn xác định. Trong ví dụ của chúng ta, hộp văn bản đầu tiên là tương tác, vì giá trị của nó được sử dụng bởi hàm `flip_text()`. Hộp văn bản thứ hai không tương tác, vì giá trị của nó không bao giờ được sử dụng làm đầu vào. Trong một số trường hợp, bạn có thể muốn ghi đè điều này, bạn có thể thực hiện bằng cách truyền một boolean đến tham số `interactive` của thành phần (ví dụ:`gr.Textbox(placeholder="Flip this text", interactive=True)`).

### Tùy chỉnh bố cục của bản demo của bạn

Làm cách nào chúng ta có thể sử dụng `Blocks` để tùy chỉnh bố cục bản demo của mình? Theo mặc định, `Blocks` hiển thị các thành phần mà bạn tạo theo chiều dọc trong một cột. Bạn có thể thay đổi điều đó bằng cách tạo các cột bổ sung `with gradio.Column():` hoặc các hàng `with gradio.Row():` và tạo các thành phần trong các ngữ cảnh đó.

Đây là những gì bạn nên ghi nhớ: bất kỳ thành phần nào được tạo trong một `Column` (đây cũng là mặc định) sẽ được bố trí theo chiều dọc. Bất kỳ thành phần nào được tạo trong một `Row` sẽ được bố trí theo chiều ngang, tương tự như [mô hình flexbox trong phát triển web](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concept_of_Flexbox).

Cuối cùng, bạn cũng có thể tạo các tab cho bản demo của mình bằng cách sử dụng trình quản lý ngữ cảnh `with gradio.Tabs()`. Trong ngữ cảnh này, bạn có thể tạo nhiều tab bằng cách chỉ định `with gradio.TabItem(name_of_tab):` children. Bất kỳ thành phần nào được tạo bên trong ngữ cảnh `with gradio.TabItem(name_of_tab):` sẽ xuất hiện trong tab đó.

Bây giờ, hãy thêm một hàm `flip_image()` vào bản demo của chúng ta và thêm một tab mới để lật hình ảnh. Dưới đây là một ví dụ với 2 tab và cũng sử dụng Row:

```py
import numpy as np
import gradio as gr

demo = gr.Blocks()


def flip_text(x):
    return x[::-1]


def flip_image(x):
    return np.fliplr(x)


with demo:
    gr.Markdown("Flip text or image files using this demo.")
    with gr.Tabs():
        with gr.TabItem("Flip Text"):
            with gr.Row():
                text_input = gr.Textbox()
                text_output = gr.Textbox()
            text_button = gr.Button("Flip")
        with gr.TabItem("Flip Image"):
            with gr.Row():
                image_input = gr.Image()
                image_output = gr.Image()
            image_button = gr.Button("Flip")

    text_button.click(flip_text, inputs=text_input, outputs=text_output)
    image_button.click(flip_image, inputs=image_input, outputs=image_output)

demo.launch()
```

<iframe src="https://course-demos-flip-text-image.hf.space" frameBorder="0" height="450" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Bạn sẽ nhận thấy rằng trong ví dụ này, chúng ta cũng đã tạo ra một thành phần `Button` trong mỗi tab và chỉ định một sự kiện nhấp chuột cho mỗi nút, đó là những gì thực sự chạy hàm.

### Khám phá các sự kiện và trạng thái

Cũng giống như bạn có thể kiểm soát bố cục, `Blocks` cung cấp cho bạn khả năng kiểm soát chi tiết đối với những sự kiện nào kích hoạt hàm gọi. Mỗi thành phần và nhiều bố cục có các sự kiện cụ thể mà chúng hỗ trợ.

Ví dụ: thành phần `Textbox` có 2 sự kiện: `change()` (khi giá trị bên trong hộp văn bản thay đổi) và `submit()` (khi người dùng nhấn phím enter trong khi tập trung vào hộp văn bản). Các thành phần phức tạp hơn có thể có nhiều sự kiện hơn nữa: ví dụ: thành phần `Audio` cũng có các sự kiện riêng biệt khi tệp âm thanh được phát, xóa, tạm dừng, v.v. Xem tài liệu về các sự kiện mà mỗi thành phần hỗ trợ.

Bạn có thể đính kèm trình kích hoạt sự kiện cho không, một hoặc nhiều sự kiện này. Bạn tạo một trình kích hoạt sự kiện bằng cách gọi tên của sự kiện trên cá thể thành phần dưới dạng một hàm - ví dụ: `textbox.change(...)` hoặc `btn.click(...)`. Hàm nhận ba tham số, như đã thảo luận ở trên:

- `fn`: hàm để chạy
- `input`: một (danh sách) (các) thành phần có giá trị sẽ được cung cấp làm tham số đầu vào cho hàm. Giá trị của mỗi thành phần được ánh xạ tới tham số hàm tương ứng, theo thứ tự. Tham số này có thể là None nếu hàm không nhận bất kỳ tham số nào.
- `outputs`: một (danh sách) (các) thành phần có giá trị cần được cập nhật dựa trên các giá trị được trả về bởi hàm. Mỗi giá trị trả về đặt giá trị của thành phần tương ứng, theo thứ tự. Tham số này có thể là None nếu hàm không trả về bất kỳ thứ gì.

Bạn thậm chí có thể đặt thành phần đầu vào và đầu ra là thành phần giống nhau, như chúng ta làm trong ví dụ này sử dụng mô hình GPT để hoàn thành văn bản:

```py
import gradio as gr

api = gr.Interface.load("huggingface/EleutherAI/gpt-j-6B")


def complete_with_gpt(text):
    # Sử dụng 50 kí tự cuối của văn bản làm ngữ cảnh
    return text[:-50] + api(text[-50:])


with gr.Blocks() as demo:
    textbox = gr.Textbox(placeholder="Type here and press enter...", lines=4)
    btn = gr.Button("Generate")

    btn.click(complete_with_gpt, textbox, textbox)

demo.launch()
```

<iframe src="https://course-demos-blocks-gpt.hf.space" frameBorder="0" height="300" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

### Tạo bản demo đa bước

Trong một số trường hợp, bạn có thể muốn có _bản demo đa bước_, trong đó bạn sử dụng lại đầu ra của một hàm làm đầu vào cho hàm tiếp theo. Điều này thực sự dễ thực hiện với `Blocks`, vì bạn có thể sử dụng một thành phần cho đầu vào của một trình kích hoạt sự kiện nhưng lại là đầu ra của một thành phần khác. Hãy xem thành phần văn bản trong ví dụ bên dưới, giá trị của nó là kết quả đầu ra của mô hình nhận dạng giọng nói, nhưng cũng được truyền vào mô hình phân tích tình cảm:

```py
from transformers import pipeline

import gradio as gr

asr = pipeline("automatic-speech-recognition", "facebook/wav2vec2-base-960h")
classifier = pipeline("text-classification")


def speech_to_text(speech):
    text = asr(speech)["text"]
    return text


def text_to_sentiment(text):
    return classifier(text)[0]["label"]


demo = gr.Blocks()

with demo:
    audio_file = gr.Audio(type="filepath")
    text = gr.Textbox()
    label = gr.Label()

    b1 = gr.Button("Recognize Speech")
    b2 = gr.Button("Classify Sentiment")

    b1.click(speech_to_text, inputs=audio_file, outputs=text)
    b2.click(text_to_sentiment, inputs=text, outputs=label)

demo.launch()
```

<iframe src="https://course-demos-blocks-multi-step.hf.space" frameBorder="0" height="600" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

### Cập nhật Thuộc tính Thành phần

Cho đến nay, chúng ta đã thấy cách tạo các sự kiện để cập nhật giá trị của một thành phần khác. Nhưng điều gì sẽ xảy ra nếu bạn muốn thay đổi các thuộc tính khác của một thành phần, như khả năng hiển thị của hộp văn bản hoặc các lựa chọn trong nhóm nút radio? Bạn có thể thực hiện việc này bằng cách trả về phương thức `update()` của lớp thành phần thay vì giá trị trả về thông thường từ hàm của bạn.

Điều này được minh họa dễ dàng nhất bằng một ví dụ:

```py
import gradio as gr


def change_textbox(choice):
    if choice == "short":
        return gr.Textbox.update(lines=2, visible=True)
    elif choice == "long":
        return gr.Textbox.update(lines=8, visible=True)
    else:
        return gr.Textbox.update(visible=False)


with gr.Blocks() as block:
    radio = gr.Radio(
        ["short", "long", "none"], label="What kind of essay would you like to write?"
    )
    text = gr.Textbox(lines=2, interactive=True)

    radio.change(fn=change_textbox, inputs=radio, outputs=text)
    block.launch()
```

<iframe src="https://course-demos-blocks-update-component-properti-833c723.hf.space" frameBorder="0" height="300" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Chúng ta vừa khám phá tất cả các khái niệm cốt lõi của `Blocks`! Cũng giống như với `Interfaces`, bạn có thể tạo các bản demo thú vị có thể được chia sẻ bằng cách sử dụng `share=True` trong phương thức `launch()` hoặc triển khai trên [Hugging Face Spaces](https://huggingface.co/spaces).


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter9/7.mdx" />

### Xây dựng bản demo đầu tiên của bạn
https://huggingface.co/learn/course/vi/chapter9/2.md

# Xây dựng bản demo đầu tiên của bạn

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter9/section2.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter9/section2.ipynb"},
]} />

Hãy bắt đầu bằng cách cài đặt Gradio! Vì nó là một gói Python, chỉ cần chạy:

`$ pip install gradio `

Bạn có thể chạy Gradio ở bất cứ đâu, từ IDE Python yêu thích của bạn, đến notebook Jupyter hoặc thậm chí trong Google Colab 🤯!
Vì vậy, hãy cài đặt Gradio ở bất cứ đâu bạn chạy Python!

Hãy bắt đầu với một ví dụ “Hello World” đơn giản để làm quen với cú pháp Gradio:

```py
import gradio as gr


def greet(name):
    return "Hello " + name


demo = gr.Interface(fn=greet, inputs="text", outputs="text")

demo.launch()
```

Hãy xem qua đoạn mã trên:

- Đầu tiên, chúng ta định nghĩa một hàm có tên là `welcome()`. Trong trường hợp này, nó là một hàm đơn giản có thêm "Hello" trước tên của bạn, nhưng nó có thể là *bất kỳ* hàm Python nào nói chung. Ví dụ: trong các ứng dụng học máy, hàm này sẽ *gọi một mô hình để đưa ra dự đoán* trên một đầu vào và trả lại đầu ra.
- Sau đó, chúng ta tạo một Giao diện Gradio với ba tham số `fn`, `inputs`, và `outputs`. Các tham số này xác định hàm dự đoán, cũng như _kiểu_ của các thành phần đầu vào và đầu ra mà ta muốn. Trong trường hợp của mình, cả hai thành phần đều là các hộp văn bản đơn giản.
- Sau đó, chúng ta gọi phương thức `launch()` trên `Interface` đã tạo.

Nếu bạn chạy đoạn mã này, giao diện bên dưới sẽ tự động xuất hiện trong notebook Jupyter/Colab hoặc bật trong trình duyệt trên **[http://localhost:7860](http://localhost:7860/)** nếu đang chạy từ một tập lệnh.

<iframe src="https://course-demos-hello-world.hf.space" frameBorder="0" height="250" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Hãy thử sử dụng GUI này ngay bây giờ với tên của chính bạn hoặc một số đầu vào khác!

Bạn sẽ nhận thấy rằng trong GUI này, Gradio tự động suy ra tên của tham số đầu vào (`name`) và lấy nó làm nhãn trên đầu hộp văn bản. Điều gì xảy ra nếu bạn muốn thay đổi điều đó? Hoặc nếu bạn muốn tùy chỉnh hộp văn bản theo một số cách khác? Trong trường hợp đó, bạn có thể khởi tạo một đối tượng lớp đại diện cho thành phần đầu vào.

```py
import gradio as gr


def greet(name):
    return "Hello " + name


# Chúng tôi khởi tạo lớp Textbox
textbox = gr.Textbox(label="Type your name here:", placeholder="John Doe", lines=2)

gr.Interface(fn=greet, inputs=textbox, outputs="text").launch()
```

<iframe src="https://course-demos-hello-world-custom.hf.space" frameBorder="0" height="300" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Ở đây, chúng ta đã tạo một hộp văn bản đầu vào với nhãn, trình giữ chỗ và một số dòng. Bạn có thể làm tương tự đối với hộp văn bản đầu ra, nhưng chúng ta sẽ để lại điều đó ngay bây giờ.

Chúng ta thấy rằng chỉ với một vài dòng mã, Gradio cho phép bạn tạo một giao diện đơn giản xung quanh bất kỳ chức năng nào
với bất kỳ loại đầu vào hoặc đầu ra nào. Trong phần này, chúng ta đã bắt đầu với hộp văn bản đơn giản, nhưng trong các phần tiếp theo, chúng ta sẽ đề cập đến các loại đầu vào và đầu ra khác. Bây giờ chúng ta hãy xem bao gồm một số NLP trong một ứng dụng Gradio thì sao.

## 🤖 Bao gồm các dự đoán mô hình

Bây giờ chúng ta hãy xây dựng một giao diện đơn giản cho phép bạn demo mô hình **tạo văn bản** như GPT-2.

Chúng ta sẽ tải mô hình của mình bằng cách sử dụng hàm `pipeline()` từ 🤗 Transformers.
Nếu bạn cần cập nhật nhanh, bạn có thể quay lại [phần đó trong Chương 1](/course/chapter1/3#text-generation)

Đầu tiên, chúng ta định nghĩa một hàm dự đoán nhận lời nhắc văn bản và trả về văn bản đã hoàn thiện:

```py
from transformers import pipeline

model = pipeline("text-generation")


def predict(prompt):
    completion = model(prompt)[0]["generated_text"]
    return completion
```

Hàm này hoàn thành các lời nhắc mà bạn cung cấp và bạn có thể chạy nó với lời nhắc đầu vào của riêng mình để xem nó hoạt động như thế nào. Đây là một ví dụ (bạn có thể nhận được một kết quả khác):

```
predict("My favorite programming language is")
```

```
>> My favorite programming language is Haskell. I really enjoyed the Haskell language, but it doesn't have all the features that can be applied to any other language. For example, all it does is compile to a byte array.
```

Bây giờ chúng ta có một hàm để tạo các dự đoán, chúng ta có thể tạo và khởi chạy một `Interface` theo cách giống như cách chúng ta đã làm trước đó:

```py
import gradio as gr

gr.Interface(fn=predict, inputs="text", outputs="text").launch()
```

Nó đó! Bây giờ bạn có thể sử dụng giao diện này để tạo văn bản bằng mô hình GPT-2 như hình bên dưới 🤯.

<iframe src="https://course-demos-gpt-2.hf.space" frameBorder="0" height="300" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

Hãy tiếp tục đọc để biết cách tạo các loại demo khác với Gradio!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter9/2.mdx" />

### Sự kiện Phát hành Phần 2
https://huggingface.co/learn/course/vi/events/2.md

# Sự kiện Phát hành Phần 2

Để phát hành phần 2 của khóa học, chúng tôi đã tổ chức một sự kiện trực tiếp với hai ngày chia sẻ. Nếu bạn đã bỏ lỡ nó, bạn có thể theo dõi các bài nói chuyện được liệt kê dưới đây!

## Ngày 1: Một cái nhìn cấp cao về Transformer và cách huấn luyện chúng

**Thomas Wolf:** *Học chuyển giao và sự ra đời của thư viện Transformers*

<div class="flex justify-center">
<Youtube id="wCYVeahJES0"/>
</div>

<p align="center">
<img src="https://i.imgur.com/9eq8oUi.png" alt="Tóm tắt hình ảnh về bài chia sẻ của Thom" width="80%"/>
</p>

Thomas Wolf là đồng sáng lập và Giám đốc Khoa học của Hugging Face. Các công cụ do Thomas Wolf và nhóm Hugging Face tạo ra được sử dụng trên hơn 5,000 tổ chức nghiên cứu bao gồm Facebook Artificial Intelligence Research, Google Research, DeepMind, Amazon Research, Apple, Allen Institute for Artificial Intelligence cũng như hầu hết các khoa của trường đại học. Thomas Wolf là người khởi xướng và là chủ tịch cấp cao của sự hợp tác nghiên cứu lớn nhất từng tồn tại trong Trí tuệ nhân tạo: [“BigScience”](https://bigscience.huggingface.co), cũng như một bộ [các thư viện và công cụ được sử dụng rộng rãi](https://github.com/huggingface/). Thomas Wolf cũng là một nhà giáo dục xuất sắc, một nhà lãnh đạo tư tưởng trong lĩnh vực Trí tuệ Nhân tạo và Xử lý Ngôn ngữ Tự nhiên, và là một diễn giả thường xuyên được mời tham dự các hội nghị trên toàn thế giới [https://thomwolf.io](https://thomwolf.io ).

**Jay Alammar:** *Phần giới thiệu trực quan nhẹ nhàng về các mô hình Transformer*

<div class="flex justify-center">
<Youtube id="VzvG23gmcYU"/>
</div>

<p align="center">
<img src="https://i.imgur.com/rOZAuE9.png" alt="Tóm tắt hình ảnh về bài chia sẻ của Jay" width="80%"/>
</p>

Thông qua blog Học máy (ML) nổi tiếng của mình, Jay đã giúp hàng triệu nhà nghiên cứu và kỹ sư hiểu trực quan các công cụ và khái niệm ML từ cơ bản (với các tài liệu về NumPy, Pandas) đến tiên tiến (Transformers, BERT, GPT-3).

**Margaret Mitchell:** *Về giá trị trong phát triển Học máy*

<div class="flex justify-center">
<Youtube id="8j9HRMjh_s8"/>
</div>

<p align="center">
<img src="https://i.imgur.com/NuIsnY3.png" alt="Tóm tắt hình ảnh về bài chia sẻ của Margaret" width="80%"/>
</p>

Margaret Mitchell là một nhà nghiên cứu làm việc về Đạo đức Trí tuệ nhân tạo, hiện đang tập trung vào những điểm cần thiết của sự phát triển AI có nhận thức về đạo đức trong công nghệ. Cô đã xuất bản hơn 50 bài báo về tạo ngôn ngữ tự nhiên, công nghệ hỗ trợ, thị giác máy tính, và đạo đức AI, đồng thời nắm giữ nhiều bằng sáng chế trong các lĩnh vực tạo hội thoại và phân loại cảm xúc. Trước đây, cô đã làm việc tại Google AI với tư cách là Chuyên viên nghiên cứu khoa học, nơi cô thành lập và đồng lãnh đạo nhóm đạo đức AI của Google, tập trung vào nghiên cứu nền tảng đạo đức AI và vận hành đạo đức AI trong nội bộ Google. Trước khi gia nhập Google, cô ấy là nhà nghiên cứu tại Microsoft Research, tập trung vào việc chuyển đổi hình ảnh sang ngôn ngữ; và là một hậu nghiên cứu sinh tại Johns Hopkins, tập trung vào mô hình Bayes và trích xuất thông tin. Cô có bằng Tiến sĩ Khoa học Máy tính tại Đại học Aberdeen và Thạc sĩ ngôn ngữ học máy tính của Đại học Washington. Trong lúc chờ lấy bằng, cô cũng đã làm việc từ năm 2005-2012 về học máy, rối loạn thần kinh, và công nghệ hỗ trợ tại Đại học Khoa học và Sức khỏe Oregon. Cô ấy đã dẫn đầu một số hội thảo và sáng kiến ​​về giao điểm của sự đa dạng, hòa nhập, khoa học máy tính, và đạo đức. Công việc của cô đã nhận được giải thưởng từ Bộ trưởng Quốc phòng Ash Carter và Quỹ Người mù Hoa Kỳ, đồng thời được thực hiện bởi nhiều công ty công nghệ. Cô ấy thích làm vườn, nuôi chó và mèo.

**Matthew Watson and Chen Qian:** *Quy trình NLP với Keras*

<div class="flex justify-center">
<Youtube id="gZIP-_2XYMM"/>
</div>

<p align="center">
<img src="https://i.imgur.com/1vD2az8.png" alt="Tóm tắt hình ảnh về bài chia sẻ của Matt và Chen" width="80%"/>
</p>

Matthew Watson là một kỹ sư học máy trong nhóm Keras, tập trung vào các API mô hình hóa cấp cao. Anh học Đồ họa máy tính ở đại học và có bằng Thạc sĩ tại Đại học Stanford. Là một sinh viên gần như chuyên ngành tiếng Anh chuyển sang ngành khoa học máy tính, anh ấy đam mê làm việc trên nhiều lĩnh vực và giúp cho NLP có thể tiếp cận với nhiều đối tượng hơn.

Chen Qian là kỹ sư phần mềm từ nhóm Keras, tập trung vào các API mô hình hóa cấp cao. Chen có bằng Thạc sĩ Kỹ thuật Điện tại Đại học Stanford và anh ấy đặc biệt quan tâm đến việc đơn giản hóa việc triển khai mã của các tác vụ ML và ML quy mô lớn.

**Mark Saroufim:** *Cách Huấn luyện một Mô hình với Pytorch*

<div class="flex justify-center">
<Youtube id="KmvPlW2cbIo"/>
</div>

<p align="center">
<img src="https://i.imgur.com/TPmlkm8.png" alt="Tóm tắt hình ảnh về bài chia sẻ của Mark" width="80%"/>
</p>

Mark Saroufim là Kỹ sư đối tác tại Pytorch làm việc trên các công cụ sản xuất OSS bao gồm TorchServe và Pytorch Enterprise. Trước đó, Mark là Nhà khoa học ứng dụng và Giám đốc sản phẩm tại Graphcore, [yuri.ai](http://yuri.ai/), Microsoft và NASA's JPL. Niềm đam mê chính của anh ấy là làm cho việc lập trình trở nên thú vị hơn.

**Jakob Uszkoreit:** *Nó không hỏng nên <del>Đừng sửa</del> Hãy phá nó đi*

<div class="flex justify-center">
<Youtube id="C6jweXYFHSA"/>
</div>

<p align="center">
<img src="https://i.imgur.com/5dWQeNB.png" alt="Tóm tắt hình ảnh về bài chia sẻ của Jakob" width="80%"/>
</p>

Jakob Uszkoreit là đồng sáng lập của Inception. Inception thiết kế các phân tử RNA cho vắc-xin và liệu pháp điều trị bằng cách sử dụng học sâu quy mô lớn trong một vòng lặp chặt chẽ với các thí nghiệm thông lượng cao với mục tiêu làm cho các loại thuốc dựa trên RNA trở nên dễ tiếp cận hơn, hiệu quả hơn và có thể áp dụng rộng rãi hơn. Trước đây, Jakob đã làm việc tại Google hơn một thập kỷ, lãnh đạo các nhóm nghiên cứu và phát triển trong Google Brain, Nghiên cứu và Tìm kiếm, làm việc về các nguyên tắc cơ bản về học sâu, thị giác máy tính, và hiểu ngôn ngữ và dịch máy.

## Ngày 2: Các công cụ sử dụng

**Lewis Tunstall:** *Huấn luyện đơn giản với 🤗 Transformers Trainer*

<div class="flex justify-center">
<Youtube id="u--UVvH-LIQ"/>
</div>

Lewis là một kỹ sư máy học tại Hugging Face, tập trung vào việc phát triển các công cụ mã nguồn mở và giúp chúng có thể tiếp cận với cộng đồng rộng lớn hơn. Anh cũng là đồng tác giả của cuốn sách O’Reilly [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). Bạn có thể theo dõi anh ấy trên Twitter (@_lewtun) để biết các mẹo và thủ thuật NLP!

**Matthew Carrigan:** * Các tính năng TensorFlow mới cho 🤗 Transformers và 🤗 Datasets*

<div class="flex justify-center">
<Youtube id="gQUlXp1691w"/>
</div>

Matt chịu trách nhiệm bảo trì TensorFlow tại Transformers, và cuối cùng sẽ dẫn đầu một cuộc đảo chính chống lại phe PyTorch đương nhiệm, có khả năng thông qua tài khoản Twitter @carrigmat của anh ta.

** Lysandre Debut: ** *Hugging Face Hub như một phương tiện để cộng tác và chia sẻ các dự án Học máy*

<div class="flex justify-center">
<Youtube id="RBw1TmdEZp0"/>
</div>

<p align="center">
<img src="https://i.imgur.com/TarIPCz.png" alt="Tóm tắt hình ảnh về bài chia sẻ của Lysandre" width="80%"/>
</p>

Lysandre là Kỹ sư Học máy tại Hugging Face, nơi anh ấy tham gia vào nhiều dự án mã nguồn mở. Mục đích của ông là làm cho Học máy có thể truy cập được với tất cả mọi người bằng cách phát triển các công cụ mạnh mẽ với một API rất đơn giản.

**Lucile Saulnier:** *Tạo ra tokenizer của riêng bạn🤗 Transformers & 🤗 Tokenizers*

<div class="flex justify-center">
<Youtube id="UkNmyTFKriI"/>
</div>

Lucile là một kỹ sư học máy tại Hugging Face, phát triển và hỗ trợ việc sử dụng các công cụ mã nguồn mở. Cô cũng tích cực tham gia vào nhiều dự án nghiên cứu trong lĩnh vực Xử lý ngôn ngữ tự nhiên như huấn luyện hợp tác và BigScience.

**Sylvain Gugger:** *Tăng cường vòng lặp huấn luyện PyTorch của bạn với 🤗 Accelerate*

<div class="flex justify-center">
<Youtube id="t8Krzu-nSeY"/>
</div>

Sylvain là Kỹ sư nghiên cứu tại Hugging Face và là một trong những người bảo trì cốt lõi của 🤗 Transformers và là nhà phát triển đằng sau 🤗 Accelerate. Anh ấy thích làm cho những mô hình huấn luyện trở nên dễ tiếp cận hơn.

**Merve Noyan:** *Giới thiệu các bản demo mô hình của bạn với 🤗 Spaces*

<div class="flex justify-center">
<Youtube id="vbaKOa4UXoM"/>
</div>

Merve là chuyên gia về quan hệ lập trình viên tại Hugging Face, đang làm việc để phát triển các công cụ và xây dựng nội dung xung quanh chúng để giúp học máy có thể tiếp cận tới tất cả mọi người.

**Abubakar Abid:** *Xây dựng Ứng dụng Học máy nhanh chóng*

<div class="flex justify-center">
<Youtube id="c7mle2yYpwQ"/>
</div>

<p align="center">
<img src="https://i.imgur.com/qWIFeiF.png" alt="Tóm tắt hình ảnh về bài chia sẻ của Abubakar" width="80%"/>
</p>

Abubakar Abid là Giám đốc điều hành của [Gradio](www.gradio.app). Anh ấy nhận bằng Cử nhân Khoa học về Kỹ thuật Điện và Khoa học Máy tính từ MIT vào năm 2015 và Tiến sĩ về Máy học Ứng dụng từ Stanford vào năm 2021. Với vai trò là Giám đốc điều hành của Gradio, Abubakar làm việc để làm cho các mô hình học máy dễ dàng demo, gỡ lỗi và triển khai hơn.

**Mathieu Desvé:** *AWS ML Vision: Làm cho Máy học có thể dễ dàng truy cập được bởi tất cả khách hàng*

<div class="flex justify-center">
<Youtube id="O2e3pXO4aRE"/>
</div>

<p align="center">
<img src="https://i.imgur.com/oLdZTKy.png" alt="Tóm tắt hình ảnh về bài chia sẻ của Mathieu" width="80%"/>
</p>

Mathieu Desvé là người đam mê công nghệ, nhà sản xuất vào thời gian rảnh. Ạnh thích thử thách và giải quyết vấn đề của khách hàng và người dùng, đồng thời làm việc với những người tài năng để học hỏi mỗi ngày. Kể từ năm 2004, anh làm việc ở nhiều vị trí chuyển đổi từ frontend, backend, cơ sở hạ tầng, hoạt động và quản lý. Anh cố gắng giải quyết các vấn đề liên quan đến kỹ thuật và quản lý theo cách nhanh nhẹn.

**Philipp Schmid:** *Quản lý huấn luyện với Amazon SageMaker và 🤗 Transformers*

<div class="flex justify-center">
<Youtube id="yG6J2Zfo8iw"/>
</div>

Philipp Schmid là Kỹ sư Máy học và Trưởng nhóm Công nghệ tại Hugging Face, nơi anh lãnh đạo sự hợp tác với nhóm Amazon SageMaker. Anh ấy đam mê sản xuất các mô hình NLP tiên tiến và cải thiện tính dễ sử dụng cho Học sâu.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/events/2.mdx" />

### Tokenizers, kiểm tra nào!
https://huggingface.co/learn/course/vi/chapter6/9.md

# Tokenizers, kiểm tra nào!

<CourseFloatingBanner
    chapter={6}
    classNames="absolute z-10 right-0 top-0"
/>

Chúc mừng bạn đã hoàn thành chương này!

Sau khi tìm hiểu sâu về tokenizer, bạn nên:

- Có thể huấn luyện một tokenizer mới bằng cách sử dụng một cái cũ làm mẫu
- Hiểu cách sử dụng hiệu số để ánh xạ vị trí của token với khoảng văn bản ban đầu của chúng
- Biết sự khác biệt giữa BPE, WordPiece và Unigram
- Có thể trộn và kết hợp các khối được cung cấp bởi thư viện 🤗 Tokenizers để xây dựng tokenizer của riêng bạn
- Có thể sử dụng tokenizer đó trong thư viện 🤗 Transformers


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter6/9.mdx" />

### Byte-Pair Encoding tokenization
https://huggingface.co/learn/course/vi/chapter6/5.md

# Byte-Pair Encoding tokenization

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter6/section5.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter6/section5.ipynb"},
]} />

Mã hóa theo cặp (BPE) tiền thân được phát triển như một thuật toán để nén văn bản, sau đó được OpenAI sử dụng để tokenize khi huấn luyện trước mô hình GPT. Nó được sử dụng bởi rất nhiều mô hình Transformer, bao gồm GPT, GPT-2, RoBERTa, BART và DeBERTa.

<Youtube id="HEikzVL-lZU"/>

<Tip>

💡 Phần này trình bày sâu hơn về BPE, đi xa hơn nữa là trình bày cách triển khai đầy đủ. Bạn có thể bỏ qua phần cuối nếu bạn chỉ muốn có một cái nhìn tổng quan chung về thuật toán tokenize.

</Tip>

## Thuật toán huấn luyện

Huấn luyện BPE bắt đầu bằng cách tính toán tập hợp các từ duy nhất được sử dụng trong kho ngữ liệu (sau khi hoàn thành các bước chuẩn hóa và pre-tokenization), sau đó xây dựng từ vựng bằng cách lấy tất cả các ký hiệu được sử dụng để viết những từ đó. Ví dụ rất đơn giản, giả sử kho dữ liệu của chúng ta sử dụng năm từ sau:

```
"hug", "pug", "pun", "bun", "hugs"
```

Từ vựng cơ sở khi đó sẽ là `["b", "g", "h", "n", "p", "s", "u"]`. Đối với các trường hợp trong thực tế, từ vựng cơ sở đó sẽ chứa tất cả các ký tự ASCII, ít nhất và có thể là một số ký tự Unicode. Nếu một mẫu bạn đang tokenize sử dụng một ký tự không có trong kho dữ liệu huấn luyện, thì ký tự đó sẽ được chuyển đổi thành token không xác định. Đó là một lý do tại sao nhiều mô hình NLP rất kém trong việc phân tích nội dung bằng biểu tượng cảm xúc.

<Tip>

GPT-2 và RoBERTa tokenizer (khá giống nhau) có một cách thông minh để giải quyết vấn đề này: chúng không xem các từ được viết bằng các ký tự Unicode mà là các byte. Bằng cách này, từ vựng cơ sở có kích thước nhỏ (256), nhưng mọi ký tự bạn có thể nghĩ đến sẽ vẫn được bao gồm và không bị chuyển đổi thành token không xác định. Thủ thuật này được gọi là *BPE cấp byte*.

</Tip>

Sau khi có được bộ từ vựng cơ bản này, chúng ta thêm các token mới cho đến khi đạt được kích thước từ vựng mong muốn bằng cách học *hợp nhất*, đây là các quy tắc để hợp nhất hai yếu tố của từ vựng hiện có với nhau thành một từ mới. Vì vậy, lúc đầu sự hợp nhất này sẽ tạo ra các token có hai ký tự và sau đó, khi quá trình huấn luyện tiến triển, các từ phụ sẽ dài hơn.

Tại bất kỳ bước nào trong quá trình huấn luyện token, thuật toán BPE sẽ tìm kiếm cặp token hiện có thường xuyên nhất (theo "cặp", ở đây có nghĩa là hai token liên tiếp trong một từ). Cặp thường xuyên nhất đó là cặp sẽ được hợp nhất, và chúng ta xả và lặp lại cho bước tiếp theo.

Quay trở lại ví dụ trước, giả sử các từ có tần số như sau:

```
("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
```

nghĩa là `"hug"` có mặt 10 lần trong kho ngữ liệu, `"pug"` 5 lần, `"pun"` 12 lần, `"bun"` 4 lần và `"hug"` 5 lần. Chúng ta bắt đầu huấn luyện bằng cách tách từng từ thành các ký tự (những ký tự hình thành từ vựng ban đầu của chúng ta) để có thể xem mỗi từ như một danh sách các token:

```
("h" "u" "g", 10), ("p" "u" "g", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "u" "g" "s", 5)
```


Sau đó, chúng ta xem xét các cặp. Cặp `("h", "u")` có trong các từ  `"hug"` và  `"hugs"`, vì vậy tổng cộng là 15 lần trong ngữ liệu. Tuy nhiên, đây không phải là cặp thường xuyên nhất: vinh dự đó thuộc về `("u", "g")`, có trong `"hug"`, `"pug"`, và `"hugs"`, với tổng cộng 20 lần xuất hiện trong bộ từ vựng.

Do đó, quy tắc hợp nhất đầu tiên được học bởi tokenizer là `("u", "g") -> "ug"`, có nghĩa là `"ug"` sẽ được thêm vào từ vựng và cặp này sẽ được hợp nhất trong tất cả các từ của ngữ liệu. Vào cuối giai đoạn này, từ vựng và ngữ liệu sẽ giống như sau:

```
Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug"]
Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "ug" "s", 5)
```

Bây giờ chúng ta có một số cặp dẫn đến một token dài hơn hai ký tự: ví dụ: cặp `("h", "ug")`, (hiện diện 15 lần trong kho ngữ liệu). Cặp thường gặp nhất ở giai đoạn này là `("u", "n")`, xuất hiện 16 lần trong kho ngữ liệu, vì vậy quy tắc hợp nhất thứ hai đã học là `("u", "n") -> "un"`. Thêm nó vào bộ từ vựng và hợp nhất tất cả các lần xuất hiện hiện có sẽ dẫn chúng ta đến:

```
Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un"]
Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("h" "ug" "s", 5)
```

Giờ thì cặp xuất hiện nhiều nhất là `("h", "ug")`, nên chúng ta hợp nhất `("h", "ug") -> "hug"`, trả về cho chúng ta token gồn ba kí tự đầu tiên. Sau sự hợp nhất này, kho ngữ liệu sẽ như sau:

```
Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un", "hug"]
Corpus: ("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5)
```

Và chúng ta tiếp túc làm vậy cho đến khi chúng ta chạm đến kích thước bộ tự điển ta mong muốn.

<Tip>

✏️ **Giờ thì đến lượt bạn!** Bạn nghĩ bước hợp nhất tiếp theo sẽ là gì?

</Tip>

## Thuật toán tokenize

Tokenize tuân thủ chặt chẽ quá trình huấn luyện, theo nghĩa là các đầu vào mới được tokenize bằng cách áp dụng các bước sau:

1. Chuẩn hoá
2. Pre-tokenization
3. Tách các từ thành các ký tự riêng lẻ
4. Áp dụng các quy tắc hợp nhất đã học theo thứ tự trên các phần tách đó

Lấy ví dụ mà ta đã sử dụng trong quá trình huấn luyện, với ba quy tắc hợp nhất đã học:

```
("u", "g") -> "ug"
("u", "n") -> "un"
("h", "ug") -> "hug"
```

Từ `"bug"` sẽ được tokenize thành `["b", "ug"]`. `"mug"`, tuy nhiên, sẽ tokenize thành `["[UNK]", "ug"]` vì kí tự `"m"` không có trong bộ tự vựng gốc. Tương tự, từ `"thug"` sẽ được tokenize thành  `["[UNK]", "hug"]`: kí tự `"t"` không có trong bộ tự vựng gốc, và áp dụng quy tắc hợp nhất ở `"u"` và `"g"` và sau đó `"hu"` và `"g"`.

<Tip>

✏️ **Giờ tới lượt bạn!** Bạn nghĩ rằng `"unhug"` sẽ được tokenize như thế nào?

</Tip>

## Triển khai BPE

Hãy cùng xem các thuật toán BPE được triển khai. Đây không phải là phiên bản tối ưu mà bạn có thể thực sự sử dụng cho một kho ngữ liệu lớn; chúng tôi chỉ muốn cho bạn xem đoạn mã để bạn có thể hiểu thuật toán này tốt hơn.

Đầu tiên chúng ta cần một kho ngữ liệu, vậy nên hay tạo ra một bản đơn giản với một vài câu:

```python
corpus = [
    "This is the Hugging Face Course.",
    "This chapter is about tokenization.",
    "This section shows several tokenizer algorithms.",
    "Hopefully, you will be able to understand how they are trained and generate tokens.",
]
```

Tiếp theo, ta cần tiền tokenize kho ngữ liệu này thành các từ. Vì ta đang sao chép một bản BPE tokenizer (như GPT-2), ta vẫn có thể sử dụng `gpt2` tokenize cho bước pre-tokenization:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("gpt2")
```

Sau đó ta tính tần suất của từng từ trong kho ngữ liệu như khi làm với pre-tokenization:

```python
from collections import defaultdict

word_freqs = defaultdict(int)

for text in corpus:
    words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
    new_words = [word for word, offset in words_with_offsets]
    for word in new_words:
        word_freqs[word] += 1

print(word_freqs)
```

```python out
defaultdict(int, {'This': 3, 'Ġis': 2, 'Ġthe': 1, 'ĠHugging': 1, 'ĠFace': 1, 'ĠCourse': 1, '.': 4, 'Ġchapter': 1,
    'Ġabout': 1, 'Ġtokenization': 1, 'Ġsection': 1, 'Ġshows': 1, 'Ġseveral': 1, 'Ġtokenizer': 1, 'Ġalgorithms': 1,
    'Hopefully': 1, ',': 1, 'Ġyou': 1, 'Ġwill': 1, 'Ġbe': 1, 'Ġable': 1, 'Ġto': 1, 'Ġunderstand': 1, 'Ġhow': 1,
    'Ġthey': 1, 'Ġare': 1, 'Ġtrained': 1, 'Ġand': 1, 'Ġgenerate': 1, 'Ġtokens': 1})
```

Tiếp theo chúng ta sẽ tính bộ từ vựng cơ sở từ các kí tự sử dụng trong kho ngữ liệu:

```python
alphabet = []

for word in word_freqs.keys():
    for letter in word:
        if letter not in alphabet:
            alphabet.append(letter)
alphabet.sort()

print(alphabet)
```

```python out
[ ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's',
  't', 'u', 'v', 'w', 'y', 'z', 'Ġ']
```

Ta cũng có thể thêm các token đặc biệt từ mô hình ở đầu của bộ tự vựng. Trong trường hợp của GPT-2, token đặc biệt duy nhất đó là `"<|endoftext|>"`:

```python
vocab = ["<|endoftext|>"] + alphabet.copy()
```

Ta giờ cần phải chia mỗi từ thành các kí tự riêng lẻ để có thể bắt đầu huấn luyện

```python
splits = {word: [c for c in word] for word in word_freqs.keys()}
```

Giờ ta đã sẵn sàng để huấn luyện, hãy cùng viết một hàm tính tần suất mỗi cặp. Ta sẽ cần sử dụng nó ở bước huấn luyện:

```python
def compute_pair_freqs(splits):
    pair_freqs = defaultdict(int)
    for word, freq in word_freqs.items():
        split = splits[word]
        if len(split) == 1:
            continue
        for i in range(len(split) - 1):
            pair = (split[i], split[i + 1])
            pair_freqs[pair] += freq
    return pair_freqs
```

Hãy nhìn vào một phần từ điẻn sau khi tách:

```python
pair_freqs = compute_pair_freqs(splits)

for i, key in enumerate(pair_freqs.keys()):
    print(f"{key}: {pair_freqs[key]}")
    if i >= 5:
        break
```

```python out
('T', 'h'): 3
('h', 'i'): 3
('i', 's'): 5
('Ġ', 'i'): 2
('Ġ', 't'): 7
('t', 'h'): 3
```

Giờ thì, tìm xem cặp xuất hiện nhiều nhất bằng một vòng lặp nhanh:

```python
best_pair = ""
max_freq = None

for pair, freq in pair_freqs.items():
    if max_freq is None or max_freq < freq:
        best_pair = pair
        max_freq = freq

print(best_pair, max_freq)
```

```python out
('Ġ', 't') 7
```

Vậy phép hợp nhất đầu tiên là `('Ġ', 't') -> 'Ġt'`, và ta thêm `'Ġt'` vào bộ từ vựng:

```python
merges = {("Ġ", "t"): "Ġt"}
vocab.append("Ġt")
```

Để tiếp tục, ta cần áp dụng sự hợp nhất ở từ điển `splits`. Hãy cùng viết một hàm khác cho nó:

```python
def merge_pair(a, b, splits):
    for word in word_freqs:
        split = splits[word]
        if len(split) == 1:
            continue

        i = 0
        while i < len(split) - 1:
            if split[i] == a and split[i + 1] == b:
                split = split[:i] + [a + b] + split[i + 2 :]
            else:
                i += 1
        splits[word] = split
    return splits
```

Giờ ta có thể nhìn xem kết quả của lần hợp nhất đầu tiên:

```py
splits = merge_pair("Ġ", "t", splits)
print(splits["Ġtrained"])
```

```python out
['Ġt', 'r', 'a', 'i', 'n', 'e', 'd']
```

Giờ thì ta có tất cả những gì mình cần để lặp cho đến khi ta học tất các các hợp nhất mà ta muốn. Hãy cũng nhắm tới bộ tự vựng có kích cỡ là 50:

```python
vocab_size = 50

while len(vocab) < vocab_size:
    pair_freqs = compute_pair_freqs(splits)
    best_pair = ""
    max_freq = None
    for pair, freq in pair_freqs.items():
        if max_freq is None or max_freq < freq:
            best_pair = pair
            max_freq = freq
    splits = merge_pair(*best_pair, splits)
    merges[best_pair] = best_pair[0] + best_pair[1]
    vocab.append(best_pair[0] + best_pair[1])
```

Kết quả là, chúng ta đã học 19 quy tắc hợp nhất (bộ từ điển gốc có kích cỡ là 31 tương ứng 30 kí tự trong bảng chữ cái cùng một token đặt biệt):

```py
print(merges)
```

```python out
{('Ġ', 't'): 'Ġt', ('i', 's'): 'is', ('e', 'r'): 'er', ('Ġ', 'a'): 'Ġa', ('Ġt', 'o'): 'Ġto', ('e', 'n'): 'en',
 ('T', 'h'): 'Th', ('Th', 'is'): 'This', ('o', 'u'): 'ou', ('s', 'e'): 'se', ('Ġto', 'k'): 'Ġtok',
 ('Ġtok', 'en'): 'Ġtoken', ('n', 'd'): 'nd', ('Ġ', 'is'): 'Ġis', ('Ġt', 'h'): 'Ġth', ('Ġth', 'e'): 'Ġthe',
 ('i', 'n'): 'in', ('Ġa', 'b'): 'Ġab', ('Ġtoken', 'i'): 'Ġtokeni'}
```

Và bộ tự vựng cấu thành bởi token đặc biết, các kí tự trong bảng chữ cái, và tất cả kết quả từ các quy tắc hợp nhất:

```py
print(vocab)
```

```python out
['<|endoftext|>', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o',
 'p', 'r', 's', 't', 'u', 'v', 'w', 'y', 'z', 'Ġ', 'Ġt', 'is', 'er', 'Ġa', 'Ġto', 'en', 'Th', 'This', 'ou', 'se',
 'Ġtok', 'Ġtoken', 'nd', 'Ġis', 'Ġth', 'Ġthe', 'in', 'Ġab', 'Ġtokeni']
```

<Tip>

💡 Sử dụng `train_new_from_iterator()` trên cùng kho ngữ liệu sẽ không mang về kết quả kho ngữ liệu y hệt. Đó là bởi khi có sự lựa chọn về cặp có tần suất cao nhất, ta đã chọn cái đầu tiên xuất hiện, trong khi thư viện 🤗 Tokenizers chọn cái đầu tiên dựa trên ID bên trong của nó.

</Tip>

Để tokenize văn bản mới, chúng ta tiền tokenize nó, tách ra, rồi áp dụng quy tắc hợp nhất được học:

```python
def tokenize(text):
    pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
    pre_tokenized_text = [word for word, offset in pre_tokenize_result]
    splits = [[l for l in word] for word in pre_tokenized_text]
    for pair, merge in merges.items():
        for idx, split in enumerate(splits):
            i = 0
            while i < len(split) - 1:
                if split[i] == pair[0] and split[i + 1] == pair[1]:
                    split = split[:i] + [merge] + split[i + 2 :]
                else:
                    i += 1
            splits[idx] = split

    return sum(splits, [])
```
t
Ta có thể thử các này với bất kì đoạn văn nào khác được tạo thành từ các kí tự trong bảng chữ cái:

```py
tokenize("This is not a token.")
```

```python out
['This', 'Ġis', 'Ġ', 'n', 'o', 't', 'Ġa', 'Ġtoken', '.']
```

<Tip warning={true}>

⚠️ Các triển khai của chúng ta sẽ gặp lỗi nếu có những kí tự vô danh vì chúng ta đã không làm gì để xử lý chúng. GPT-2 không thực sự có những token vô danh (không thể có kí tự vô danh khi sử dụng BPE cấp byte), nhưng nó có thể xảy ra ở đây vì ta không bao gồm tất cả các byte có thể có trong bộ từ vựng gốc. Khía cạnh này của BPE nằm ngoài phạm vi phần này, nên chúng tôi sẽ không đi sau vào chi tiết.

</Tip>

Đó là những gì ta cần biết về thuật toán BPE! Tiếp theo, chúng ta sẽ cùng tìm hiểu về WordPiece.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter6/5.mdx" />

### Giới thiệu
https://huggingface.co/learn/course/vi/chapter6/1.md

# Giới thiệu

<CourseFloatingBanner
    chapter={6}
    classNames="absolute z-10 right-0 top-0"
/>

Trong [Chương 3](/course/chapter3), chúng ta đã xem xét cách tinh chỉnh một mô hình trong một tác vụ nhất định. Khi làm điều đó, chúng ta sử dụng cùng một trình tokenizer mà mô hình đã được huấn luyện trước - nhưng chúng ra phải làm gì khi muốn huấn luyện một mô hình từ đầu? Trong những trường hợp này, việc sử dụng trình tokenizer đã được huấn luyện trước trên một kho ngữ liệu từ một lĩnh vực hoặc ngôn ngữ khác thường là không tối ưu. Ví dụ: một tokenizer được huấn luyện trên ngữ liệu tiếng Anh sẽ hoạt động kém trên ngữ liệu văn bản tiếng Nhật vì việc sử dụng dấu cách và dấu câu trong hai ngôn ngữ rất khác nhau.

Trong chương này, bạn sẽ học cách huấn luyện một trình tokenize hoàn toàn mới trên kho ngữ liệu văn bản, do đó, nó có thể được sử dụng để huấn luyện trước một mô hình ngôn ngữ. Tất cả điều này sẽ được thực hiện với sự trợ giúp của thư viện [🤗 Tokenizers](https://github.com/huggingface/tokenizers), nơi cung cấp các tokenizer "nhanh" trong thư viện [🤗 Transformers](https://github.com/huggingface/transformers). Chúng ta sẽ xem xét kỹ các tính năng mà thư viện này cung cấp và khám phá cách các bản tokenizer nhanh khác so với các phiên bản "chậm".

Các chủ đề chúng ta sẽ đề cập bao gồm:

- Cách huấn luyện một trình tokenize mới tương tự như một trình được sử dụng bởi một checkpoint nhất định trên một kho văn bản mới
- Các tính năng đặc biệt của tokenizer nhanh
- Sự khác biệt giữa ba thuật toán tokenize từ phụ được sử dụng trong NLP ngày nay
- Cách xây dựng một tokenizer từ đầu với thư viện 🤗 Tokenizer và huấn luyện nó trên một số dữ liệu

Các kỹ thuật được giới thiệu trong chương này sẽ giúp bạn chuẩn bị cho phần trong [Chương 7](/course/chapter7/6), nơi chúng ta xem xét việc tạo mô hình ngôn ngữ cho mã nguồn Python. Hãy bắt đầu bằng cách xem xét ý nghĩa của việc "huấn luyện" một tokenizer ngay từ đầu.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter6/1.mdx" />

### Sức mạnh đặc biệt của tokenizer nhanh
https://huggingface.co/learn/course/vi/chapter6/3.md

# Sức mạnh đặc biệt của tokenizer nhanh

{#if fw === 'pt'}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter6/section3_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter6/section3_pt.ipynb"},
]} />

{:else}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter6/section3_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter6/section3_tf.ipynb"},
]} />

{/if}

Trong phần này, chúng ta sẽ xem xét kỹ hơn các khả năng của tokenizer trong 🤗 Transformers. Cho đến nay, chúng ta chỉ sử dụng chúng để tokenize đầu vào hoặc giải mã ID trở lại thành văn bản, nhưng các trình tokenize - đặc biệt là những trình tokenize được hỗ trợ bởi thư viện 🤗 Tokenizers - có thể làm được nhiều hơn thế. Để minh họa các tính năng bổ sung này, chúng ta sẽ khám phá cách tái tạo kết quả của `token-classification` (mà chúng ta gọi là `ner`) và `question-answering` chúng ta gặp phải lần đầu tiên trong [Chương 1](/course/chapter1).

<Youtube id="g8quOxoqhHQ"/>

Trong phần thảo luận kế tiếp, chúng ta sẽ phân biệt giữa các loại tokenizer "chậm" và "nhanh". Phiên bản chậm là những phiên bản được viết bằng Python bên trong thư viện 🤗 Transformers, trong khi phiên bản nhanh là những phiên bản được cung cấp bởi 🤗 Tokenizers, được viết bằng Rust. Nếu bạn nhớ bảng từ [Chương 5](/course/chapter5/3) báo cáo khoảng thời gian tokenize nhanh và chậm cần để tokenize Drug Review Dataset, bạn nên biết lý do tại sao chúng tôi gọi chúng là nhanh và chậm :

                |  Tokenizer nhanh | Tokenizer chậm
:--------------:|:--------------:|:-------------:
`batched=True`  | 10.8s          | 4min41s
`batched=False` | 59.2s          | 5min3s

<Tip warning={true}>

⚠️ Khi tokenize một câu, bạn không phải lúc nào cũng thấy sự khác biệt về tốc độ giữa các phiên bản chậm và nhanh của cùng một trình tokenize. Trên thực tế, phiên bản nhanh có thể chậm hơn! Chỉ khi tokenize nhiều văn bản song song cùng một lúc, bạn mới có thể thấy rõ sự khác biệt.

</Tip>

## Mã hoá theo lô

<Youtube id="3umI3tm27Vw"/>

Đầu ra của tokenizer không phải là một từ điển Python đơn giản; những gì chúng ta nhận được thực sự là một đối tượng `BatchEncoding` đặc biệt. Đó là một lớp con của từ điển (đó là lý do tại sao trước đây chúng ta có thể lập chỉ mục vào kết quả đó mà không gặp bất kỳ vấn đề gì), nhưng với các phương thức bổ sung hầu hết được sử dụng bởi các trình tokenize nhanh.

Bên cạnh khả năng song song hóa của chúng, chức năng chính của các trình tokenize nhanh là chúng luôn theo dõi khoảng văn bản ban đầu mà ta tokenize - một tính năng được gọi là *offset mapping* hay *ánh xạ bù trừ*. Điều này lần lượt mở khóa các tính năng như ánh xạ từng từ với token mà nó tạo ra hoặc ánh xạ từng ký tự của văn bản gốc với token bên trong và ngược lại.

Cùng xem một mẫu:

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
encoding = tokenizer(example)
print(type(encoding))
```

Như đã đề cập trước đây, chúng ta nhận được một đối tượng `BatchEncoding` trong đầu ra của trình tokenize:

```python out
<class 'transformers.tokenization_utils_base.BatchEncoding'>
```

Vì lớp `AutoTokenizer` chọn một trình tokenizer nhanh theo mặc định, chúng ta có thể sử dụng các phương thức bổ sung mà đối tượng `BatchEncoding` này cung cấp. Chúng ta có hai cách để kiểm tra xem trình tokenize là nhanh hay chậm. Chúng ta có thể kiểm tra bằng thuộc tính `is_fast` của `tokenizer`:

```python
tokenizer.is_fast
```

```python out
True
```

hoặc kiểm tra cùng thuộc tính đó của `encoding`:

```python
encoding.is_fast
```

```python out
True
```

Hãy xem những gì một tokenizer nhanh cho phép chúng ta làm. Đầu tiên, chúng tôi có thể truy cập token mà không cần phải chuyển đổi ID trở lại token:

```py
encoding.tokens()
```

```python out
['[CLS]', 'My', 'name', 'is', 'S', '##yl', '##va', '##in', 'and', 'I', 'work', 'at', 'Hu', '##gging', 'Face', 'in',
 'Brooklyn', '.', '[SEP]']
```

Trong trường hợp này, token ở chỉ mục 5 là `##yl`, là một phần của từ "Sylvain" trong câu gốc. Chúng ta cũng có thể sử dụng phương thức `word_ids()` để lấy chỉ mục của từ mà mỗi token đến từ:

```py
encoding.word_ids()
```

```python out
[None, 0, 1, 2, 3, 3, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, None]
```

Chúng ta có thể thấy rằng các token đặc biệt của trình tokenize như `[CLS]` và `[SEP]` được ánh xạ thành `None`, và sau đó mỗi token được ánh xạ tới từ mà nó bắt nguồn. Điều này đặc biệt hữu ích để xác định xem một token nằm ở đầu một từ hay nếu hai token có trong cùng thuộc một từ. Chúng ta có thể dựa vào tiền tố `##` cho điều đó, nhưng nó chỉ hoạt động đối với các tokenize kiểu BERT; phương pháp này hoạt động với bất kỳ loại tokenizer nào miễn nó là phiên bản nhanh. Trong chương tiếp theo, chúng ta sẽ xem cách chúng ta có thể sử dụng khả năng này để áp dụng nhãn chúng ta có cho mỗi từ đúng cách với các token trong các tác vụ như nhận dạng thực thể được đặt tên (NER) và  gán nhãn từ loại (POS). Chúng ta cũng có thể sử dụng nó để che giấu tất cả các token đến từ cùng một từ trong mô hình ngôn ngữ được che (một kỹ thuật được gọi là _whole word masking_).

<Tip>

Khái niệm về một từ rất là phức tạp. Ví dụ: "I'll" (từ rút gọn của "I will") có được tính là một hay hai từ? Nó thực sự phụ thuộc vào trình tokenize và hoạt động tiền tokenize mà nó áp dụng. Một số tokenizer chỉ tách ra trên khoảng trắng, vì vậy họ sẽ coi đây là một từ. Những người khác sử dụng dấu câu ở đầu khoảng trắng, vì vậy sẽ coi nó là hai từ.

✏️ **Thử nghiệm thôi!** Tạo tokenizer từ các checkpoints `bert-base-cased` và` roberta-base` và tokenize "81s" với chúng. Bạn quan sát thấy gì? ID từ là gì?

</Tip>

Tương tự, có một phương thức `question_ids()` mà chúng ta có thể sử dụng để ánh xạ token đến câu mà nó bắt nguồn (mặc dù trong trường hợp này, `token_type_ids` được trả về bởi tokenizer có thể cung cấp cho chúng ta cùng một thông tin).

Cuối cùng, chúng ta có thể ánh xạ bất kỳ từ hoặc token nào với các ký tự trong văn bản gốc và ngược lại, thông qua các phương thức `word_to_chars()` hoặc `token_to_chars()` và `char_to_word()` hoặc `char_to_token()`. Ví dụ: phương thức `word_ids()` cho chúng ta biết rằng `##yl` là một phần của từ ở chỉ mục 3, nhưng từ đó nằm trong câu nào? Chúng ta có thể tìm hiểu như thế này:

```py
start, end = encoding.word_to_chars(3)
example[start:end]
```

```python out
Sylvain
```

Như đã đề cập trước đó, tất cả điều này thực tế được hỗ trợ bởi là trình tokenizer nhanh kết hợp khoảng văn bản mà mỗi token đến từ danh sách *offset* hay *offset*. Để minh họa việc sử dụng chúng, tiếp theo, chúng tôi sẽ hướng dẫn bạn cách sao chép các kết quả của `token-classification` theo cách thủ công.

<Tip>

✏️ **Thử nghiệm thôi** Tạo văn bản mẫu của riêng bạn và xem liệu bạn có thể hiểu những token nào được liên kết với ID từ, cũng như cách trích xuất ký tự kéo dài cho một từ. Để có điểm thưởng, hãy thử sử dụng hai câu làm đầu vào và xem liệu ID câu có phù hợp với bạn không.

</Tip>

## Bên trong pipeline `token-classification`

Trong [Chương 1](/course/chapter1), chúng ta lần đầu được thử áp dụng NER - tác vụ xác định những phần nào của văn bản tương ứng với các thực thể như người, địa điểm hoặc tổ chức - với `pipeline()` của 🤗 Transformers. Sau đó, trong [Chương 2](/course/chapter2), chúng ta đã thấy cách một pipeline nhóm ba giai đoạn cần thiết lại với nhau để nhận các dự đoán từ một văn bản thô: tokenize, chuyển các đầu vào qua mô hình và hậu xử lý. Hai bước đầu tiên trong quy trình `token-classification` cũng giống như trong bất kỳ quy trình nào khác, nhưng quá trình hậu xử lý phức tạp hơn một chút - hãy cùng xem cách thực hiện!

{#if fw === 'pt'}

<Youtube id="0E7ltQB7fM8"/>

{:else}

<Youtube id="PrX4CjrVnNc"/>

{/if}

### Nhận kết quả cơ sở với baseline

Trước tiên, hãy lấy một pipeline phân loại token chúng ta có thể nhận được một số kết quả để so sánh theo cách thủ công. Mô hình được sử dụng theo mặc định là [`dbmdz/bert-large-cased-finetuned-conll03-english`](https://huggingface.co/dbmdz/bert-large-cased-finetuned-conll03-english); nó thực hiện NER trên các câu:

```py
from transformers import pipeline

token_classifier = pipeline("token-classification")
token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
```

```python out
[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
 {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
 {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
 {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
 {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
 {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
 {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
 {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
```

Mô hình đã xác định đúng mỗi token do "Sylvain" tạo ra là một người, mỗi token được tạo bởi "Hugging Face" là một tổ chức và token "Brooklyn" là một địa điểm. Chúng ta cũng có thể yêu cầu pipeline nhóm các token tương ứng với cùng một thực thể lại với nhau:

```py
from transformers import pipeline

token_classifier = pipeline("token-classification", aggregation_strategy="simple")
token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
```

```python out
[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
 {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
 {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
```

`aggregation_strategy` được chọn sẽ thay đổi điểm được tính cho mỗi thực thể được nhóm lại, Với `"simple"`, điểm chỉ là giá trị trung bình của điểm của mỗi token trong thực thể đã cho: ví dụ: điểm của "Sylvain" là trung bình điểm mà chúng ta đã thấy trong ví dụ trước cho các token `S` , `##yl`,`## va` và `##in`. Các chiến lược có sẵn khác là:

- `"first"`, trong đó điểm của mỗi thực thể là điểm của token đầu tiên của thực thể đó (vì vậy đối với "Sylvain", nó sẽ là 0.993828, điểm của token `S`)
- `"max"`, trong đó điểm của mỗi thực thể là điểm tối đa của các token trong thực thế đó (vì vậy với "Hugging Face" sẽ là 0.98879766, điểm của "Face")
- `"average"`, trong đó điểm của mỗi thực thể là điểm trung bình của các từ tạo nên thực thể đó (ví dụ với "Sylvain" thì phương pháp này sẽ không có sự khác biệt so với phương pháp `"simple"`, nhưng với "Hugging Face", điểm trả về sẽ là 0.9819, điểm trung bình của "Hugging", 0.975, và "Face", 0.98879)

Giờ chúng ta hãy xem làm thế nào để có được những kết quả này mà không cần sử dụng hàm`pipeline()`!

### Từ đầu vào tới dự đoán

{#if fw === 'pt'}

Đầu tiên chúng ta cần tokenize đầu vào của chúng ta và truyền chúng vào mô hình. Đây chính xác là những gì ta đã làm ở [Chương 2](/course/chapter2); ta khởi tạo tokenizer và mô hình sử dụng lớp `AutoXxx` và sau đó dùng chúng vào các mẫu của mình:

```py
from transformers import AutoTokenizer, AutoModelForTokenClassification

model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
model = AutoModelForTokenClassification.from_pretrained(model_checkpoint)

example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
inputs = tokenizer(example, return_tensors="pt")
outputs = model(**inputs)
```

Vì chúng ta sử dụng `AutoModelForTokenClassification` ở đây,ta sẽ nhận được tập hợp các logits cho từng token của chuỗi đầu vào:

```py
print(inputs["input_ids"].shape)
print(outputs.logits.shape)
```

```python out
torch.Size([1, 19])
torch.Size([1, 19, 9])
```

{:else}

Đầu tiên chúng ta cần tokenize đầu vào của chúng ta và truyền chúng vào mô hình. Đây chính xác là những gì ta đã làm ở [Chương 2](/course/chapter2); ta khởi tạo tokenizer và mô hình sử dụng lớp `TFAutoXxx` và sau đó dùng chúng vào các mẫu của mình:

```py
from transformers import AutoTokenizer, TFAutoModelForTokenClassification

model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
model = TFAutoModelForTokenClassification.from_pretrained(model_checkpoint)

example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
inputs = tokenizer(example, return_tensors="tf")
outputs = model(**inputs)
```

Vì ta dùng `TFAutoModelForTokenClassification` ở đây, ta sẽ nhận được tập hợp các logits cho từng token của chuỗi đầu vào:

```py
print(inputs["input_ids"].shape)
print(outputs.logits.shape)
```

```python out
(1, 19)
(1, 19, 9)
```

{/if}

Chúng ta có một lô với 1 chuỗi gồm 19 token và mô hình có 9 nhãn khác nhau, vì vậy đầu ra của mô hình có hình dạng 1 x 19 x 9. Giống như đối với pipeline phân loại văn bản, chúng ta sử dụng hàm softmax để chuyển đổi các logits đó theo xác suất, và chúng ta lấy argmax để nhận dự đoán (lưu ý rằng ta có thể lấy argmax trên logits vì softmax không thay đổi thứ tự):

{#if fw === 'pt'}

```py
import torch

probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1)[0].tolist()
predictions = outputs.logits.argmax(dim=-1)[0].tolist()
print(predictions)
```

{:else}

```py
import tensorflow as tf

probabilities = tf.math.softmax(outputs.logits, axis=-1)[0]
probabilities = probabilities.numpy().tolist()
predictions = tf.math.argmax(outputs.logits, axis=-1)[0]
predictions = predictions.numpy().tolist()
print(predictions)
```

{/if}

```python out
[0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 6, 6, 6, 0, 8, 0, 0]
```

Thuộc tính `model.config.id2label` chứa ánh xạ các chỉ mục tới các nhãn mà chúng ta có thể sử dụng để hiểu các dự đoán: 

```py
model.config.id2label
```

```python out
{0: 'O',
 1: 'B-MISC',
 2: 'I-MISC',
 3: 'B-PER',
 4: 'I-PER',
 5: 'B-ORG',
 6: 'I-ORG',
 7: 'B-LOC',
 8: 'I-LOC'}
```

Như chúng ta đã thấy trước đó, có 9 nhãn: `O` là nhãn cho các token không nằm trong bất kỳ thực thể được đặt tên nào (nó là viết tắt của "outside" hay "bên ngoài") và sau đó chúng ta có hai nhãn cho mỗi loại thực thể (linh tinh, người , tổ chức và vị trí). Nhãn `B-XXX` cho biết token nằm ở đầu thực thể `XXX` và nhãn `I-XXX` cho biết token nằm bên trong thực thể `XXX`. Ví dụ: trong mẫu hiện tại, chúng ta kì vọng mô hình phân loại token `S` là `B-PER` (bắt đầu của một thực thể người) và các token `##yl`,`##va` và `##in` là `I-PER` (bên trong một thực thể người).

Bạn có thể nghĩ rằng mô hình đã sai trong trường hợp này vì nó đã gắn nhãn `I-PER` cho cả bốn token này, nhưng điều đó không hoàn toàn đúng. Thực tế có hai định dạng cho các nhãn `B-` và `I-` đó là: *IOB1* và *IOB2*. Định dạng IOB2 (màu hồng bên dưới), là định dạng chúng ta đã giới thiệu trong khi ở định dạng IOB1 (màu xanh lam), các nhãn bắt đầu bằng `B-` chỉ được sử dụng để phân tách hai thực thể liền kề cùng loại. Mô hình chúng tôi đang sử dụng đã được tinh chỉnh trên tập dữ liệu bằng cách sử dụng định dạng đó, đó là lý do tại sao nó gán nhãn `I-PER` cho mã thông báo `S`.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/IOB_versions.svg" alt="IOB1 vs IOB2 format"/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/IOB_versions-dark.svg" alt="IOB1 vs IOB2 format"/>
</div>

Với phép ánh xạ này, chúng ta đã sẵn sàng đề tái tạo lại (gần như hoàn toàn) kết quả của pipeline đầu -- ta chỉ cần lấy điểm và nhãn của mỗi token mà không được phân vào `O`:

```py
results = []
tokens = inputs.tokens()

for idx, pred in enumerate(predictions):
    label = model.config.id2label[pred]
    if label != "O":
        results.append(
            {"entity": label, "score": probabilities[idx][pred], "word": tokens[idx]}
        )

print(results)
```

```python out
[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S'},
 {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl'},
 {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va'},
 {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in'},
 {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu'},
 {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging'},
 {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face'},
 {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn'}]
```

Điều này rất giống với những gì ta đã có trước đây, ngoại trừ một ngoại lệ: pipeline cũng cung cấp thông tin về điểm `start` hay `bắt đầu` và `end`  hay `kết thúc` của mỗi thực thể trong câu gốc. Đây là lúc ánh xạ bù trừ của chúng ta sẽ phát huy tác dụng. Để có được offset, chúng ta chỉ cần đặt `return_offsets_mapping=True` khi chúng ta áp dụng tokenizer cho các đầu vào của mình:

```py
inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
inputs_with_offsets["offset_mapping"]
```

```python out
[(0, 0), (0, 2), (3, 7), (8, 10), (11, 12), (12, 14), (14, 16), (16, 18), (19, 22), (23, 24), (25, 29), (30, 32),
 (33, 35), (35, 40), (41, 45), (46, 48), (49, 57), (57, 58), (0, 0)]
```

Mỗi tuple là khoảng văn bản tương ứng với mỗi token, trong đó `(0, 0)` được dành riêng cho các token đặc biệt. Trước đây, chúng ta đã thấy rằng token ở chỉ mục 5 là `##yl`, có `(12, 14)` là các phần bù ở đây. Nếu chúng ta lấy phần tương ứng trong mẫu của mình:


```py
example[12:14]
```

ta nhận được khoảng văn bản thích hợp mà không có `##`:

```python out
yl
```

Sử dụng điều này, bây giờ chúng ta có thể hoàn thành các kết quả trước đó:

```py
results = []
inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
tokens = inputs_with_offsets.tokens()
offsets = inputs_with_offsets["offset_mapping"]

for idx, pred in enumerate(predictions):
    label = model.config.id2label[pred]
    if label != "O":
        start, end = offsets[idx]
        results.append(
            {
                "entity": label,
                "score": probabilities[idx][pred],
                "word": tokens[idx],
                "start": start,
                "end": end,
            }
        )

print(results)
```

```python out
[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
 {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
 {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
 {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
 {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
 {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
 {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
 {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
```

Đây giống như những gì chúng ta có được từ pipeline đầu tiên!

### Nhóm các thực thể

Sử dụng các offset để xác định điểm bắt đầu và kết thúc cho mỗi thực thể là rất tiện dụng, nhưng thông tin đó không hoàn toàn cần thiết. Tuy nhiên, khi chúng ta muốn nhóm các thực thể lại với nhau, việc offset sẽ giúp chúng ta tiết kiệm rất nhiều đoạn mã lộn xộn. Ví dụ: nếu chúng ta muốn nhóm các token `Hu`, `##gging` và `Face` lại với nhau, chúng ta có thể đưa ra các quy tắc đặc biệt nói rằng hai token đầu tiên phải được đính kèm trong khi xóa dấu `##` và `Face` nên được thêm một khoảng trắng vì nó không bắt đầu bằng `##` - nhưng điều đó sẽ chỉ hoạt động đối với loại tokenizer cụ thể này. Chúng ta sẽ phải viết một bộ quy tắc khác cho SentencePiece hoặc Byte-Pair-Encoding (sẽ được thảo luận ở phần sau của chương này).

Với offset, tất cả mã tùy chỉnh đó sẽ biến mất: chúng ta chỉ có thể lấy khoảng trong văn bản gốc bắt đầu bằng token đầu tiên và kết thúc bằng token cuối cùng. Vì vậy, trong trường hợp các mã thông báo `Hu`, `##gging` và `Face`, chúng ta nên bắt đầu ở ký tự 33 (đầu của `Hu`) và kết thúc trước ký tự 45 (cuối của `Face`) :

```py
example[33:45]
```

```python out
Hugging Face
```

Để viết đoạn mã hậu xử lý các dự đoán trong khi nhóm các thực thể, ta sẽ nhóm các thực thể liên tiếp và có nhãn `I-XXX` với nhau trừ khi nó là từ đầu tiên, được gán nhãn `B-XXX` hoặc `I-XXX` (để ta có thể dừng nhóm một thực thể khi nhận được `O`, một kiểu thực thể mới, hoặc một `B-XXX` cho ta biết thực thể có kiểu giống với điểm bắt đầu):

```py
import numpy as np

results = []
inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
tokens = inputs_with_offsets.tokens()
offsets = inputs_with_offsets["offset_mapping"]

idx = 0
while idx < len(predictions):
    pred = predictions[idx]
    label = model.config.id2label[pred]
    if label != "O":
        # Xoá B- hoặc I-
        label = label[2:]
        start, _ = offsets[idx]

        # Lấy tất cả các tokens có nhãn I-
        all_scores = []
        while (
            idx < len(predictions)
            and model.config.id2label[predictions[idx]] == f"I-{label}"
        ):
            all_scores.append(probabilities[idx][pred])
            _, end = offsets[idx]
            idx += 1

        # Điểm là giá trị trung bình của tất cả điểm của các token trong thực thể được nhóm đó
        score = np.mean(all_scores).item()
        word = example[start:end]
        results.append(
            {
                "entity_group": label,
                "score": score,
                "word": word,
                "start": start,
                "end": end,
            }
        )
    idx += 1

print(results)
```

Và chúng ta nhận được kết quả tương tự như với pipeline thứ hai của mình!

```python out
[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
 {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
 {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
```

Một ví dụ khác về tác vụ mà những offset này cực kỳ hữu ích là hỏi đáp. Đào sâu vào pipeline này, chúng ta sẽ thực hiện trong phần tiếp theo, cũng sẽ cho phép chúng ta xem xét một tính năng cuối cùng của các tokenizers trong thư viện 🤗 Transformers: xử lý các token tràn khi chúng ta cắt bớt một đầu vào đến một độ dài nhất định.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter6/3.mdx" />

### WordPiece tokenization
https://huggingface.co/learn/course/vi/chapter6/6.md

# WordPiece tokenization

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter6/section6.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter6/section6.ipynb"},
]} />

WordPiece là một thuật toán tokenize được Google phát triển để huấn luyện trước BERT. Nó đã được tái sử dụng trong một vài mô hình Transformer dựa trên BERT, như DistilBERT, MobileBERT, Funnel Transformers, và MPNET. Nó khá tương tự với BPE về mặt huấn luyện, nhưng tokenize thực sự được thực hiện hoàn toàn khác.

<Youtube id="qpv6ms_t_1A"/>

<Tip>

💡 Phần này sẽ đi sâu vào WordPiece, cũng như các triển khai đầy đủ của nó. Bạn có thể bỏ qua phần cuối nếu bạn chỉ muốn có một cái nhìn tổng quan về thuật toán tokenize này.

</Tip>

## Thuật toán huấn luyện

<Tip warning={true}>

⚠️ Google không bao giờ có nguồn mở về cách triển khai các thuật toán huấn luyện của WordPiece,vì vậy những gì dưới đây là phỏng đoán tốt nhất của chúng tôi dựa trên các tài liệu đã xuất bản. Nó có thể không chính xác 100%.

</Tip>

Giống như BPE, WordPiece bắt đầu từ một từ vựng nhỏ bao gồm các token đặc biệt được sử dụng bởi mô hình và bảng chữ cái đầu tiên. Vì nó xác định các từ phụ bằng cách thêm tiền tố (như `##` cho BERT), ban đầu mỗi từ được tách bằng cách thêm tiền tố đó vào tất cả các ký tự bên trong từ. Vì vậy, ví dụ, `"word"` được chia như thế này:

```
w ##o ##r ##d
```

Vì vậy, bảng chữ cái chứa tất cả các kí tự xuất hiện ở đầu của một từ và các kí tự xuất hiện bên trong của từ được thêm một tiền tố của WordPiece phía trước.

Sau đó, một lần nữa, giống như BPE, WordPiece học các quy tắc hợp nhất. Sự khác biệt chính là cách chọn cặp được hợp nhất. Thay vì chọn cặp phổ biến nhất, WordPiece tính điểm cho từng cặp, sử dụng công thức sau:

$$\mathrm{score} = (\mathrm{freq\_of\_pair}) / (\mathrm{freq\_of\_first\_element} \times \mathrm{freq\_of\_second\_element})$$

Bằng cách chia tần suất của cặp cho tích tần suất của từng con của nó, thuật toán ưu tiên hợp nhất các cặp mà các bộ phận riêng lẻ ít thường xuyên hơn trong từ vựng. Ví dụ: nó sẽ không nhất thiết phải hợp nhất `("un", "##able")` ngay cả khi cặp đó xuất hiện rất thường xuyên trong từ vựng, vì hai cặp `"un"` và `"##able"` mỗi từ có thể sẽ xuất hiện bằng nhiều từ khác và có tần suất cao. Ngược lại, một cặp như `("hu", "##gging")` có thể sẽ được hợp nhất nhanh hơn (giả sử từ "hugging" xuất hiện thường xuyên trong từ vựng) vì `"hu"` và `"##gging"`có khả năng ít xuất hiện hơn với từng cá thể.

Hãy cùng nhìn vào cùng bộ tự vựng chúng ta sử dụng cho BPE:

```
("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
```

Nó sẽ được chia ra như sau:

```
("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##g" "##s", 5)
```

vì vậy từ vựng ban đầu sẽ là `["b", "h", "p", "##g", "##n", "##s", "##u"]` (nếu ta tạm quên các token đặc biệt). Cặp thường gặp nhất là `("##u", "##g")` (xuất hiện 20 lần), nhưng tần suất xuất hiện riêng của `"##u"` rất cao, vì vậy điểm của nó không phải là cao nhất (đó là 1 / 36). Tất cả các cặp có `"##u"`thực sự có cùng điểm (1 / 36), vì vậy điểm tốt nhất thuộc về cặp `("##g", "##s")` -- cặp duy nhất không có `"##u"` -- là 1 / 20, và phép hợp nhất đầu tiên đã học là `("##g", "##s") -> ("##gs")`.

Lưu ý rằng khi hợp nhất, chúng ta loại bỏ `##` giữa hai token, vì vậy chúng ta thêm `"##gs"` vào từ vựng và áp dụng hợp nhất trong các từ của ngữ liệu:

```
Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs"]
Corpus: ("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##gs", 5)
```

Tại thời điểm này, `"##u"` nằm trong tất cả các cặp có thể có, vì vậy tất cả chúng đều có cùng điểm. Giả sử trong trường hợp này, cặp đầu tiên được hợp nhất, vì vậy `("h", "##u") -> "hu"`. Điều này đưa chúng ta đến:

```
Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu"]
Corpus: ("hu" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)
```

Sau đó, điểm số tốt nhất tiếp theo được chia sẻ bởi `("hu", "##g")` and `("hu", "##gs")` (với 1/15, so với 1/21 của các cặp khác), vì vậy cặp đầu tiên có điểm lớn nhất được hợp nhất:

```
Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu", "hug"]
Corpus: ("hug", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)
```

và tiếp tục như vậy cho đến khi chúng ta đạt được kích thước bộ từ vựng mong muốn.

<Tip>

✏️ **Giờ đến lượt bạn!** Bộ quy luật hợp nhất tiếp theo là gì?

</Tip>

## Thuật toán tokenize

Tokenize của WordPiece khác BPE ở chỗ WordPiece chỉ lưu từ vựng cuối cùng, không lưu các quy tắc hợp nhất đã học. Bắt đầu từ từ cần tokenize, WordPiece tìm từ con dài nhất có trong từ vựng, sau đó tách từ đó ra. Ví dụ: nếu chúng ta sử dụng từ vựng đã học trong ví dụ trên, đối với từ `"hugs"` từ phụ dài nhất bắt đầu từ đầu mà bên trong từ vựng là `"hug"`, vì vậy chúng ta tách ở đó và nhận được `["hug","##s"]`. Sau đó, chúng ta tiếp tục với `"##s"`, trong từ vựng, vì vậy tokenize của `"hugs"` là  `["hug","##s"]`.

Với BPE, chúng ta sẽ áp dụng các phép hợp nhất đã học theo thứ tự và token điều này thành `["hu", "##gs"]`, do đó, cách mã hoá sẽ khác.

Ví dụ khác, hãy xem từ `"bugs"` sẽ được tokenize như thế nào. `"b"`  là từ phụ dài nhất bắt đầu từ đầu của từ có trong từ vựng, vì vậy chúng tôi tách ở đó và nhận được `["b", "##ugs"]`. Sau đó, `"##u"` là từ con dài nhất bắt đầu ở đầu `"##ugs"` có trong từ vựng, vì vậy chúng ta tách ở đó và nhận được `["b", "##u, "##gs"]`. Cuối cùng, `"##gs"` có trong từ vựng, vì vậy danh sách cuối cùng này là mã hóa của `"bugs"`.

Khi quá trình tokenize đến giai đoạn không thể tìm thấy một từ khóa phụ trong từ vựng, toàn bộ từ được tokenize thành không xác định - vì vậy, ví dụ: `"mug"` sẽ được tokenize là  `["[UNK]"]`, cũng như `"bum"` (ngay cả khi chúng ta có thể bắt đầu bằng `"b"` và `"##u"`, `"##m"` không phải thuộc bộ từ vựng và kết quả tokenize sẽ chỉ là `["[UNK]"]`, không phải `["b", "##u", "[UNK]"]`). Đây là một điểm khác biệt so với BPE, chỉ phân loại các ký tự riêng lẻ không có trong từ vựng là không xác định.

<Tip>

✏️ **Giờ đến lượt bạn!** `"pugs"` sẽ được tokenize như thế nào?

</Tip>

## Triển khai WordPiece

Bây giờ chúng ta hãy xem xét việc triển khai thuật toán WordPiece. Giống như với BPE, đây chỉ là phương pháp sư phạm và bạn sẽ không thể sử dụng nó trên một kho ngữ liệu lớn.

Chúng tôi sẽ sử dụng cùng một kho dữ liệu như trong ví dụ BPE:

```python
corpus = [
    "This is the Hugging Face Course.",
    "This chapter is about tokenization.",
    "This section shows several tokenizer algorithms.",
    "Hopefully, you will be able to understand how they are trained and generate tokens.",
]
```

Đầu tiên, ta cần tiền tokenize kho ngữ liệu thành các từ, Vì ta sao chép lại WordPiece tokenizer (như BERT), ta sẽ sử dụng `bert-base-cased` tokenizer cho pre-tokenization:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
```

sau đó ta sẽ tính tần suất của mỗi từ trong kho ngữ liệu như cách ta làm với pre-tokenization:

```python
from collections import defaultdict

word_freqs = defaultdict(int)
for text in corpus:
    words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
    new_words = [word for word, offset in words_with_offsets]
    for word in new_words:
        word_freqs[word] += 1

word_freqs
```

```python out
defaultdict(
    int, {'This': 3, 'is': 2, 'the': 1, 'Hugging': 1, 'Face': 1, 'Course': 1, '.': 4, 'chapter': 1, 'about': 1,
    'tokenization': 1, 'section': 1, 'shows': 1, 'several': 1, 'tokenizer': 1, 'algorithms': 1, 'Hopefully': 1,
    ',': 1, 'you': 1, 'will': 1, 'be': 1, 'able': 1, 'to': 1, 'understand': 1, 'how': 1, 'they': 1, 'are': 1,
    'trained': 1, 'and': 1, 'generate': 1, 'tokens': 1})
```

Như chúng ta đã thấy trước đây, bảng chữ cái là tập hợp duy nhất bao gồm tất cả các chữ cái đầu tiên của từ và tất cả các chữ cái khác xuất hiện trong các từ có tiền tố là `##`:

```python
alphabet = []
for word in word_freqs.keys():
    if word[0] not in alphabet:
        alphabet.append(word[0])
    for letter in word[1:]:
        if f"##{letter}" not in alphabet:
            alphabet.append(f"##{letter}")

alphabet.sort()
alphabet

print(alphabet)
```

```python out
['##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k', '##l', '##m', '##n', '##o', '##p', '##r', '##s',
 '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u',
 'w', 'y']
```

Ta cũng thêm các kí tự đặc biệt từ mô hình ở đầu bộ tự vựng. Trong trường hợp của BERT, ta có danh sách `["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"]`:

```python
vocab = ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] + alphabet.copy()
```

Tiếp theo, chúng ta cần tách từng từ, với tất cả các chữ cái không phải là tiền tố đầu tiên bởi `##`:

```python
splits = {
    word: [c if i == 0 else f"##{c}" for i, c in enumerate(word)]
    for word in word_freqs.keys()
}
```

Bây giờ chúng ta đã sẵn sàng để luyện tập, hãy viết một hàm tính điểm của từng cặp. Chúng ta sẽ cần sử dụng điều này ở mỗi bước huấn luyện:

```python
def compute_pair_scores(splits):
    letter_freqs = defaultdict(int)
    pair_freqs = defaultdict(int)
    for word, freq in word_freqs.items():
        split = splits[word]
        if len(split) == 1:
            letter_freqs[split[0]] += freq
            continue
        for i in range(len(split) - 1):
            pair = (split[i], split[i + 1])
            letter_freqs[split[i]] += freq
            pair_freqs[pair] += freq
        letter_freqs[split[-1]] += freq

    scores = {
        pair: freq / (letter_freqs[pair[0]] * letter_freqs[pair[1]])
        for pair, freq in pair_freqs.items()
    }
    return scores
```

Hãy cùng nhìn vào một phần của bộ từ điển sau lần chia đầu:

```python
pair_scores = compute_pair_scores(splits)
for i, key in enumerate(pair_scores.keys()):
    print(f"{key}: {pair_scores[key]}")
    if i >= 5:
        break
```

```python out
('T', '##h'): 0.125
('##h', '##i'): 0.03409090909090909
('##i', '##s'): 0.02727272727272727
('i', '##s'): 0.1
('t', '##h'): 0.03571428571428571
('##h', '##e'): 0.011904761904761904
```

Giờ thì tìm cặp có điểm cao nhất chỉ cần một vòng lắp nhanh:

```python
best_pair = ""
max_score = None
for pair, score in pair_scores.items():
    if max_score is None or max_score < score:
        best_pair = pair
        max_score = score

print(best_pair, max_score)
```

```python out
('a', '##b') 0.2
```

Vậy quy tắc hợp nhất đầu tiên là `('a', '##b') -> 'ab'`, và ta thêm `'ab'` vào bộ từ vựng:

```python
vocab.append("ab")
```

Tiếp theo, ta cần áp dụng hợp nhất trong từ điển `splits`, Hãy cùng viết một hàm cho nó:

```python
def merge_pair(a, b, splits):
    for word in word_freqs:
        split = splits[word]
        if len(split) == 1:
            continue
        i = 0
        while i < len(split) - 1:
            if split[i] == a and split[i + 1] == b:
                merge = a + b[2:] if b.startswith("##") else a + b
                split = split[:i] + [merge] + split[i + 2 :]
            else:
                i += 1
        splits[word] = split
    return splits
```

Và ta có thể thấy kết quả lần hợp nhất đầu tiện:

```py
splits = merge_pair("a", "##b", splits)
splits["about"]
```

```python out
['ab', '##o', '##u', '##t']
```

Giờ thì ta đã có tất cả những gì ta cần để lặp cho đến khi học hết tất cả các hợp nhất ta muốn. Hãy cũng hướng tới bộ từ vựng có kích thước là 70:

```python
vocab_size = 70
while len(vocab) < vocab_size:
    scores = compute_pair_scores(splits)
    best_pair, max_score = "", None
    for pair, score in scores.items():
        if max_score is None or max_score < score:
            best_pair = pair
            max_score = score
    splits = merge_pair(*best_pair, splits)
    new_token = (
        best_pair[0] + best_pair[1][2:]
        if best_pair[1].startswith("##")
        else best_pair[0] + best_pair[1]
    )
    vocab.append(new_token)
```

Giờ ta có thể nhìn vào bộ từ điển được tạo ra:

```py
print(vocab)
```

```python out
['[PAD]', '[UNK]', '[CLS]', '[SEP]', '[MASK]', '##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k',
 '##l', '##m', '##n', '##o', '##p', '##r', '##s', '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H',
 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u', 'w', 'y', 'ab', '##fu', 'Fa', 'Fac', '##ct', '##ful', '##full', '##fully',
 'Th', 'ch', '##hm', 'cha', 'chap', 'chapt', '##thm', 'Hu', 'Hug', 'Hugg', 'sh', 'th', 'is', '##thms', '##za', '##zat',
 '##ut']
```

Như có thể thấy, so với BPE, tokenizer này học các phần của từ như là token nhanh hơn một chút.

<Tip>

💡 Sử dụng `train_new_from_iterator()` trên cùng kho ngữ liệu sẽ không mang về kết quả kho ngữ liệu y hệt. Đó là bởi thư viện 🤗 Tokenizers không triển khai WordPiece cho huấn luyện (vì chúng ta không hoàn toàn nằm rõ bên trong), và sử dụng BPE thay vào đó.

</Tip>

Để tokenize những đoạn văn mới, ta tiền tokenize nó, chia nhỏ và áp dụng thuật toán tokenize cho từng từ. Vậy đó, chúng ta nhìn vào cụm từ con dài nhất bắt đầu từ đầu từ đầu tiên và chia nhỏ nó, sau đó lặp lại quy trình với phần thứ hai, và tiếp tục cho đến hết từ và các từ tiếp theo trong văn bản: 

```python
def encode_word(word):
    tokens = []
    while len(word) > 0:
        i = len(word)
        while i > 0 and word[:i] not in vocab:
            i -= 1
        if i == 0:
            return ["[UNK]"]
        tokens.append(word[:i])
        word = word[i:]
        if len(word) > 0:
            word = f"##{word}"
    return tokens
```

Hãy cũng kiểm tra trên một từ có tronng và không có trong bộ từ vựng:

```python
print(encode_word("Hugging"))
print(encode_word("HOgging"))
```

```python out
['Hugg', '##i', '##n', '##g']
['[UNK]']
```

Giờ hãy cùng viết một hàm tokenize văn bản:

```python
def tokenize(text):
    pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
    pre_tokenized_text = [word for word, offset in pre_tokenize_result]
    encoded_words = [encode_word(word) for word in pre_tokenized_text]
    return sum(encoded_words, [])
```

Ta có thể thử trên bất kì văn bản nào:

```python
tokenize("This is the Hugging Face course!")
```

```python out
['Th', '##i', '##s', 'is', 'th', '##e', 'Hugg', '##i', '##n', '##g', 'Fac', '##e', 'c', '##o', '##u', '##r', '##s',
 '##e', '[UNK]']
```

Đó là những gì ta cần biết về thuật toán WordPiece! Tiếp theo, chúng ta sẽ cùng tìm hiểu về Unigram.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter6/6.mdx" />

### Đố vui cuối chương
https://huggingface.co/learn/course/vi/chapter6/10.md

# Đố vui cuối chương

Cùng kiểm tra xem bạn đã học được những gì trong chương này!

### 1. Khi nào ta nên huấn luyện 1 tokenizer mới?

<Question
	choices={[
		{
			text: "Khi tập dữ liệu của bạn tương tự với tập dữ liệu được sử dụng bởi một mô hình huấn luyện trước hiện có và bạn muốn huấn luyện trước một mô hình mới",
			explain: "Trong trường hợp này, để tiết kiệm thời gian và tính toán tài nguyên, lựa chọn tốt hơn sẽ là sử dụng cùng một trình tokenize như mô hình được huấn luyện trước và thay vào đó tinh chỉnh mô hình đó."
		},
		{
			text: "Khi tập dữ liệu của bạn tương tự với tập dữ liệu được sử dụng bởi một mô hình huấn luyện trước hiện có và bạn muốn tinh chỉnh một mô hình mới bằng cách sử dụng mô hình huấn luyện trước này",
			explain: "Để tinh chỉnh một mô hình từ một mô hình đã được huấn luyện trước, bạn nên luôn sử dụng cùng một trình tokenizer."
		},
		{
			text: "Khi tập dữ liệu của bạn khác với tập dữ liệu được sử dụng bởi mô hình huấn luyện trước hiện có và bạn muốn huấn luyện trước một mô hình mới",
			explain: "Đúng! Trong trường hợp này, không có lợi ích gì khi sử dụng cùng một tokenizer.",
            correct: true
		},
        {
			text: "Khi tập dữ liệu của bạn khác với tập dữ liệu được sử dụng bởi một mô hình huấn luyện trước hiện có, nhưng bạn muốn tinh chỉnh một mô hình mới bằng cách sử dụng mô hình huấn luyện trước này",
			explain: "Để tinh chỉnh một mô hình từ một mô hình đã được huấn luyện trước, bạn nên luôn sử dụng cùng một trình tokenizer."
		}
	]}
/>

### 2. Ưu điểm của việc sử dụng trình tạo danh sách văn bản so với danh sách các danh sách văn bản khi sử dụng `train_new_from_iterator()` là gì?

<Question
	choices={[
		{
			text: "Đó là kiểu duy nhất mà phương thức <code>train_new_from_iterator()</code> chấp nhận.",
			explain: "Danh sách các danh sách văn bản là một loại trình tạo danh sách văn bản cụ thể, vì vậy phương pháp cũng sẽ chấp nhận điều này. Hãy thử lại!"
		},
		{
			text: "Bạn sẽ tránh tải toàn bộ tập dữ liệu vào bộ nhớ cùng một lúc.",
			explain: "Đúng vậy! Mỗi loạt văn bản sẽ được giải phóng khỏi bộ nhớ khi bạn lặp lại và phần thu được sẽ đặc biệt rõ ràng nếu bạn sử dụng 🤗 Datasets để lưu trữ văn bản của mình.",
			correct: true
		},
		{
			text: "Điều này sẽ cho phép thư viện 🤗 Tokenizers sử dụng quá trình xử lý đa luồng.",
			explain: "Không, với cách nào xử lý đa luồng cũng sẽ được sử dụng."
		},
        {
			text: "Tokenizer mà bạn huấn luyện sẽ tạo ra các văn bản tốt hơn.",
			explain: "Tokenize không tạo ra văn bản -- bạn có đang nhầm lẫn với mô hình ngôn ngữ không?"
		}
	]}
/>

### 3. Ưu điểm của tokenize "nhanh" là gì?

<Question
	choices={[
		{
			text: "Nó có thể xử lý dữ liệu đầu vào nhanh hơn tokenizer chậm khi bạn gộp nhiều đầu vào với nhau.",
			explain: "Đúng! Nhờ tính năng song song được triển khai trong Rust, nó sẽ nhanh hơn trên các lô đầu vào. Bạn có thể nghĩ đến lợi ích nào khác không?",
			correct: true
		},
		{
			text: "Tokenizer nhanh luôn tokenize nhanh hơn các trình chậm khác.",
			explain: "Một tokenizer nhanh có thể chậm hơn khi bạn chỉ nhận một hoặc một vài đoạn văn bản, vì nó không thể sử dụng song song."
		},
		{
			text: "Nó có thể áp dụng đệm và cắt bớt.",
			explain: "Đúng, nhưng tokenize chậm cũng làm được điều đó."
		},
        {
			text: "Nó có một số tính năng bổ sung cho phép bạn ánh xạ các tokenize với khoảng văn bản đã tạo ra chúng.",
			explain: "Thật vậy - chúng được gọi là ánh xạ offset. Tuy nhiên, đó không phải là lợi thế duy nhất.",
			correct: true
		}
	]}
/>

### 4. Pipeline `token-classification`  xử lý các thực thể trải dài trên nhiều token như thế nào?

<Question
	choices={[
		{
			text: "Các thực thể có cùng nhãn được hợp nhất thành một thực thể.",
			explain: "Đó đơn giản hóa mọi thứ một chút rồi. Hãy thử lại!"
		},
		{
			text: "Có một nhãn cho sự bắt đầu của một thực thể và một nhãn cho sự tiếp tục của một thực thể.",
			explain: "Chính xác!",
			correct: true
		},
		{
			text: "Trong một từ nhất định, miễn là token đầu tiên có nhãn của thực thể, toàn bộ từ được coi là được gắn nhãn với thực thể đó.",
			explain: "Đó là một chiến lược để xử lý các thực thể. Còn những câu trả lời nào khác ở đây áp dụng không?",
			correct: true
		},
        {
			text: "Khi token có nhãn của một thực thể nhất định, bất kỳ token nào khác phía sau có cùng nhãn được coi là một phần của cùng một thực thể, trừ khi nó được gắn nhãn là phần bắt đầu của một thực thể mới.",
			explain: "Đó là cách phổ biến nhất để nhóm các thực thể lại với nhau - tuy nhiên, đó không phải là câu trả lời đúng duy nhất.",
			correct: true
		}
	]}
/>

### 5. Pipeline `question-answering` xử lý ngữ cảnh dài như thế nào?

<Question
	choices={[
		{
			text: "Nó không thực sự xử lý, vì nó cắt đi những ngữ cảnh dài quá độ dài tối đa được chấp nhận của mô hình.",
			explain: "Có một thủ thuật bạn có thể sử dụng để xử lý các ngữ cảnh dài. Bạn có nhớ nó là gì không?"
		},
		{
			text: "Nó chia ngữ cảnh thành nhiều phần và trung bình kết quả thu được từ các phần.",
			explain: "Không, nó khá vô nghĩa khi lấy trung bình các kết quả, do có một số phần của ngữ cảnh không chứa câu trả lời."
		},
		{
			text: "Nó chia ngữ cảnh thành nhiều phần (có giao thoa) và tìm điểm cao nhất cho câu trả lời mỗi phần.",
			explain: "Đó là câu trả lời chính xác!",
			correct: true
		},
        {
			text: "Nó chia ngữ cảnh thành nhiều phần (không giao thoa, để tăng hiệu quả) và tìm điểm cao nhất cho câu trả lời mỗi phần.",
			explain: "Không, nó bao gồm một số trùng lặp giữa các phần để tránh tình huống câu trả lời sẽ bị chia thành hai phần."
		}
	]}
/>

### 6. Chuẩn hoá là gì?

<Question
	choices={[
		{
			text: "Đó là bất kỳ thao tác dọn dẹp nào mà tokenizer thực hiện trên các văn bản trong giai đoạn đầu.",
			explain: "Đúng vậy - ví dụ: nó có thể liên quan đến việc xóa dấu hoặc khoảng trắng, hoặc viết thường các đầu vào.",
			correct: true
		},
		{
			text: "Đó là kĩ thuật gia tăng dữ liệu bao gồm khả năng tạo ta các văn bản chuẩn hơn bằng cách loại bỏ từ hiếm.",
			explain: "Điều đó không chính xác! Hãy thử lại."
		},
		{
			text: "Đó là bước hậu xử lý cuối cùng khi tokenizer thêm các token đặc biệt.",
			explain: "Bước mô tả trên chỉ đơn giản là hậu xử lý."
		},
        {
			text: "Đó là khi biểu diễn từ được tạo ra với trung bình bằng 0 và độ lệch chuẩn bằng 1, được tính bằng cách trừ trung bình và chia cho độ lệch chuẩn.",
			explain: "Quá trình này thường được gọi là chuẩn hoá khi áp dụng lên các giá trị pixel trong thị giác máy tính, nhưng nó không phải ý nghĩa của chuẩn hoá trong NLP."
		}
	]}
/>

### 7. Pre-tokenization cho một tokenizer từ phụ là sao?

<Question
	choices={[
		{
			text: "Đó là bước trước khi tokenize, áp dụng phương pháp tăng dữ liệu (như tạo các lớp che ngẫu nhiên).",
			explain: "Không, bước này chỉ là một phần của tiền xử lý thôi."
		},
		{
			text: "Đó là bước trước khi tokenize, áp dụng các bước dọn dẹp cho văn bản.",
			explain: "Không, bước này chỉ là bước chuẩn hoá thôi."
		},
		{
			text: "Đó là bước trước khi tokenize, áo dụng mô hình để chia đầu vào thành các từ.",
			explain: "Chính xác!",
			correct: true
		},
        {
			text: "Đó là bước trước khi tokenize, áp dụng mô hình để chia đầu vào thành các token.",
			explain: "Không, chia đầu vào thành các token là nhiệm vụ của mô hình tokenize."
		}
	]}
/>

### 8. Chọn các câu áp dụng mô hình BPE để tokenize?

<Question
	choices={[
		{
			text: "BPE là một thuật toán tokenize từ phụ bắt đầu với một từ vựng nhỏ và học các quy tắc hợp nhất.",
			explain: "Đây quả thực là câu trả lời!",
			correct: true
		},
		{
			text: "BPE là một thuật tokenize từ phụ bắt đầu với một lượng từ vựng lớn và loại bỏ dần các token khỏi nó.",
			explain: "Không, đây là hướng tiếp cận của thuật toán tokenize khác."
		},
		{
			text: "BPE tokenizer học các quy tắc hợp nhất bằng cách gộp các cặp token có tần suất cao nhất.",
			explain: "Chính xác!",
			correct: true
		},
		{
			text: "BPE tokenizer học các quy tắc hợp nhất bằng cách gộp các cặp token tối đa hoá điểm của các cặp tần suất cao so với các phần cá nhân có tần suất ít hơn.",
			explain: "Không, đây là chiến lược của thuật toán tokenie khác."
		},
		{
			text: "BPE tokenize các từ thành các từ phụ bằng cách chia chúng thành các ký tự và áp dụng quy tắc hợp nhất.",
			explain: "Chính xác!",
			correct: true
		},
		{
			text: "BPE tokenize các từ thành các từ phụ bằng cách tìm từ phụ dài nhất bắt đầu từ phần đầu có trong từ vựng, sau đó lặp lại quy trình cho phần còn lại của văn bản.",
			explain: "Không, đây là cách phương pháp tokenize khác làm."
		},
	]}
/>

### 9. Chọn các câu áp dụng mô hình WordPiece để tokenize?

<Question
	choices={[
		{
			text: "WordPiece là một thuật toán tokenize từ phụ bắt đầu với một từ vựng nhỏ và học các quy tắc hợp nhất.",
			explain: "Đây quả thực là câu trả lời!",
			correct: true
		},
		{
			text: "WordPiece là một thuật tokenize từ phụ bắt đầu với một lượng từ vựng lớn và loại bỏ dần các token khỏi nó.",
			explain: "Không, đây là hướng tiếp cận của thuật toán tokenize khác."
		},
		{
			text: "WordPiece tokenizer học các quy tắc hợp nhất bằng cách gộp các cặp token có tần suất cao nhất.",
			explain: "Không, đây là chiến lược của thuật toán tokenie khác."
		},
		{
			text: "WordPiece tokenizer học các quy tắc hợp nhất bằng cách gộp các cặp token tối đa hoá điểm của các cặp tần suất cao với các token cá nhân của nó có tần suất ít hơn.",
			explain: "Chính xác!",
			correct: true
		},
		{
			text: "WordPiece tokenize từ thành các từ phụ bằng cách tìm ra những phân đoạn có khả năng tách thành token nhất dựa theo mô hình.",
			explain:  "Không, đây là cách phương pháp tokenize khác làm."
		},
		{
			text: "WordPiece tokenize các từ thành các từ phụ bằng cách tìm từ phụ dài nhất bắt đầu từ phần đầu có trong từ vựng, sau đó lặp lại quy trình cho phần còn lại của văn bản.",
			explain: "Đúng, đây chính là cách WordPiece xử lý mã hoá.",
			correct: true
		},
	]}
/>

### 10. Chọn các câu áp dụng mô hình Unigram để tokenize?

<Question
	choices={[
		{
			text: "Unigram là một thuật toán tokenize từ phụ bắt đầu với một từ vựng nhỏ và học các quy tắc hợp nhất.",
			explain: "Không, đây là chiến lược của thuật toán tokenie khác."
		},
		{
			text: "Unigram là một thuật tokenize từ phụ bắt đầu với một lượng từ vựng lớn và loại bỏ dần các token khỏi nó.",
			explain: "Chính xác!",
			correct: true
		},
		{
			text: "Unigram điều chỉnh vốn từ vựng của nó bằng cách giảm thiểu sự mất mát được tính trên toàn bộ kho ngữ liệu.",
			explain: "Chính xác!",
			correct: true
		},
		{
			text: "Unigram điều chỉnh vốn từ vựng của nó bằng cách chỉ giữ lại những từ phụ hay xuất hiện.",
			explain:  "Không hính xác!",
		},
		{
			text: "Unigram tokenize từ thành các từ phụ bằng cách tìm ra những phân đoạn có khả năng tách thành token nhất dựa theo mô hình.",
			explain:  "Chính xác!",
			correct: true
		},
		{
			text: "Unigram tokenizes từ thành các từ phụ bằng cách tách từ đó thành kí tự rồi áp dụng quy tắc hợp nhất.",
			explain: "Không, đây là chiến lược của thuật toán tokenie khác."
		},
	]}
/>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter6/10.mdx" />

### Xây dựng từng khối tokenizer
https://huggingface.co/learn/course/vi/chapter6/8.md

# Xây dựng từng khối tokenizer

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter6/section8.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter6/section8.ipynb"},
]} />

Như chúng ta đã thấy trong các phần trước, tokenize bao gồm một số bước:

- Chuẩn hóa (mọi thao tác dọn dẹp văn bản được cho là cần thiết, chẳng hạn như xóa dấu cách hoặc dấu, chuẩn hóa Unicode, v.v.)
- Tiền tokenize (chia nhỏ đầu vào thành các từ)
- Đưa đầu vào thông qua mô hình (sử dụng các từ được tiền tokenize để tạo ra một chuỗi token)
- Hậu xử lý (thêm token đặc biệt của trình tokenize, tạo attention mask và ID token)

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/tokenization_pipeline.svg" alt="The tokenization pipeline.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/tokenization_pipeline-dark.svg" alt="The tokenization pipeline.">
</div>

Thư viện 🤗 Tokenizers đã được xây dựng để cung cấp nhiều sự lựa chọn cho các bước này, và ta có thể kết hợp và nối chúng với nhau. Trong phần này, chúng ta sẽ xem các có thể xây một tokenizer từ đầu, trái ngược với cách huấn luyện một tokenizer mới từ cái cũ như ta đã làm ở [phần 2](/course/chapter6/2). Chúng ta sẽ có thể xây bất kì kiểu tokenizer nào ta có thể nghĩ ra!

<Youtube id="MR8tZm5ViWU"/>

Chính xác hơn, thư viện được xây dựng tập trung vào lớp `Tokenizer` với các khối được tập hợp lại trong các mô-đun con:

- `normalizers` chứa tất cả các kiểu `Normalizer` bạn có thể sử dụng (hoàn thiện danh sách tại [đây](https://huggingface.co/docs/tokenizers/api/normalizers)).
- `pre_tokenizers` chứa tất cả các kiểu `PreTokenizer` bạn có thể sử dụng (hoàn thiện danh sách tại [đây](https://huggingface.co/docs/tokenizers/api/pre-tokenizers)).
- `models` chứa tất cả các kiểu `Model` bạn có thể sử dụng, như `BPE`, `WordPiece`, and `Unigram` (hoàn thiện danh sách tại [đây](https://huggingface.co/docs/tokenizers/api/models)).
- `trainers` chứa tất cả các kiểu `Trainer` khác nhau bạn có thể sử dụng để huấn luyện mô hình của bạn trên kho ngữ liệu (một cho mỗi loại mô hình; hoàn thiện danh sách tại [đây](https://huggingface.co/docs/tokenizers/api/trainers)).
- `post_processors` chứa tất cả các kiểu `PostProcessor` bạn có thể sử dụng (hoàn thiện danh sách tại [đây](https://huggingface.co/docs/tokenizers/api/post-processors)).
- `decoders` chứa tất cả các kiểu `Decoder` đa dạng bạn có thể sử dụng để giải mã đầu ra của tokenize (hoàn thiện danh sách tại [đây](https://huggingface.co/docs/tokenizers/components#decoders)).

Bạn có thể tìm được toàn bộ danh sách các khối tại [đây](https://huggingface.co/docs/tokenizers/components).

## Thu thập một kho ngữ liệu

Để huấn luyện tokenizer mới của mình, chúng ta sẽ sử dụng một kho ngữ liệu nhỏ chứa các đoạn văn (cho nhanh). Các bước để có được kho ngữ liệu tương tự như chúng ta đã làm ở [phần đầu của chương này](/course/chapter6/2), nhưng lần này chúng ta sẽ sử dụng [WikiText-2](https://huggingface.co/datasets/wikitext):

```python
from datasets import load_dataset

dataset = load_dataset("wikitext", name="wikitext-2-raw-v1", split="train")


def get_training_corpus():
    for i in range(0, len(dataset), 1000):
        yield dataset[i : i + 1000]["text"]
```

Hàm `get_training_corpus()` là một hàm tạo có thể trả về các lô với mỗi lô là 1,000 đoạn văn, cái mà ta sẽ sử dụng để huấn luyện  tokenizer.

🤗 Tokenizers cũng có thể được huấn luyện trực tiếp trên các tệp văn bản. Đây là cách chúng ta tạo ra một tệp văn bản bao gồm các đoạn văn/đầu vào từ WikiText-2 mà ta có thể sử dụng cục bộ:

```python
with open("wikitext-2.txt", "w", encoding="utf-8") as f:
    for i in range(len(dataset)):
        f.write(dataset[i]["text"] + "\n")
```

Tiếp theo chúng tôi sẽ hướng dẫn bạn cách tự xây dựng từng khối BERT, GPT-2, và XLNet tokenizer của riêng mình. Điều này sẽ cung cấp cho chúng ta một ví dụ về từng thuật toán trong số ba thuật toán tokenize chính: WordPiece, BPE, và Unigram. Hãy cũng bắt đầu với BERT!

## Xây dựng một WordPiece tokenizer từ đầu

Để xây dựng một tokenizer với thư viện 🤗 Tokenizers, chúng ta sẽ bắt đầu với việc khởi tạo một đối tượng `Tokenizer` với `model`, sau đó thiết lập `normalizer`, `pre_tokenizer`, `post_processor`, và `decoder` tới các giá trị ta muốn.

Với ví dụ này, ta sẽ tạo ra một `Tokenizer` với một mô hình WordPiece:

```python
from tokenizers import (
    decoders,
    models,
    normalizers,
    pre_tokenizers,
    processors,
    trainers,
    Tokenizer,
)

tokenizer = Tokenizer(models.WordPiece(unk_token="[UNK]"))
```

Chúng ta phải chỉ rõ `unk_token` để mô hình biết phải trả về gì khi gặp các kí tự chưa từng gặp trước đó. Các tham số khác chúng ta có thể cài đặt gồm `vocab` của mô hình (ta sẽ huấn luyện mô hình nên không cần thiết lập nó) và `max_input_chars_per_word`, tương ứng độ dài tối đa cho một từ (từ dài hơn giá trị này sẽ bị chia nhỏ)

Bước đầu tiên để tokenize đó là chuẩn hoá, vì vậy hãy cũng bắt đầu với bước này. Vì BERT được sử dụng rộng tãi, ta có thể sử dụng `BertNormalizer` với tuỳ chọn kinh điển để thiết lập cho BERT: `lowercase` và `strip_accents`, tự cái tên đã giải thích mục đích của chúng; `clean_text` để loại bỏ tất cả các kí tự kiểm soát và dấu cách lặp lại thành một; và `handle_chinese_chars` thêm dấu cách giữa các kí tự tiếng Trung. Để , which places spaces around Chinese characters. To tái tạo tokenizer `bert-base-uncased`, ta có thể thiết lập chuẩn hoá sau:

```python
tokenizer.normalizer = normalizers.BertNormalizer(lowercase=True)
```

Thông thường, khi xây dựng một tokenizer, bạn không cần phải truy cập vào một hàm chuẩn hoá thủ công vì nó đã có sẵn trong thư viện 🤗 Tokenizers library -- tuy nhiên, hãy cùng tạo ra chuẩn hoá BERT thủ công. Thư viện cung câp trình chuẩn hoá `Lowercase` và `StripAccents`, bạn hoàn toàn có thể kết hợp nhiều trình chuẩn hoá với nhau thông qua `Sequence`:

```python
tokenizer.normalizer = normalizers.Sequence(
    [normalizers.NFD(), normalizers.Lowercase(), normalizers.StripAccents()]
)
```

Ta cũng có thể sử dụng chuẩn hoá Unicode `NFD` Unicode normalizer, vì nếu không chuẩn hoá `StripAccents` sẽ không nhận diện được những kí tự có dấu và không thể tách nó đúng như ta muốn.

Như đã thấy ở trên, ta có thể sử dụng phương thức `normalize_str()` của `normalizer` để kiểm tra tác động của nó lên một chuỗi văn bản:

```python
print(tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
```

```python out
hello how are u?
```

<Tip>

**Đào sâu hơn** Nếu bạn muốn kiểm tra xem hai phiên bản chuẩn hoá trước đó trên cũng một chuỗi chứa kí tự unicode `u"\u0085"`, bạn chắc chắn sẽ nhận thấy rằng hai cách chuẩn hoá này không hoàn toàn giống nhau.
Để tránh phức tạp hoá phiên bản với `normalizers.Sequence` quá nhiều, chúng tôi sẽ không bao gồm các sự thay thế theo Regex mà `BertNormalizer` yêu cầu khi tham số `clean_text` được thiết lập là `True` - đây cũng là giá trị mặc định. Nhưng đừng lo: có khả năng ta sẽ nhận được kết quả chuẩn hoá giống nhau mà không cần sử dụng `BertNormalizer` thủ công bằng cách thêm hai `normalizers.Replace` vào chuỗi chuẩn hoá.

</Tip>

Tiếp theo là bước pre-tokenization. Một lần nữa, ta có `BertPreTokenizer` được xây dựng sẵn để dùng:

```python
tokenizer.pre_tokenizer = pre_tokenizers.BertPreTokenizer()
```

Hoặc ta có thể xây từ đầu:

```python
tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
```

Lưu ý rằng `Whitespace` sẽ tách theo dấu cách và các kí tự không phải chữ cái, số, hoặc dấu gạch dưới, nên về mặt kỹ thuật nó sẽ tách theo dấu cách và dấu câu:

```python
tokenizer.pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
```

```python out
[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
 ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
```

Nêu sbanj chỉ muốn tách theo dấu cách, bạn có thể sử dụng `WhitespaceSplit` thay thế:

```python
pre_tokenizer = pre_tokenizers.WhitespaceSplit()
pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
```

```python out
[("Let's", (0, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre-tokenizer.', (14, 28))]
```

Giống như chuẩn hoá, bản có thể sử dụng `Sequence` để kết hợp các tiền tokenizer với nhau:

```python
pre_tokenizer = pre_tokenizers.Sequence(
    [pre_tokenizers.WhitespaceSplit(), pre_tokenizers.Punctuation()]
)
pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
```

```python out
[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
 ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
```

Bước tiếp theo trong pipeline tokenize là đưa đầu vào qua mô hình. Ta đã chỉ định mô hình của mình khi khởi tạo, nhưng ta vẫn cần huấn luyện nó, điều này cần tới `WordPieceTrainer`.  Vấn đề chính ở đây là khi khởi tạo một trình huấn luyện trong 🤗 Tokenizers thì bạn cần phải truyền tất cả các token đặc biệt bạn có ý định sử dụng, nếu không nó sẽ không thêm vào bộ từ vựng, vì chúng không có trong kho ngữ liệu huấn luyện:

```python
special_tokens = ["[UNK]", "[PAD]", "[CLS]", "[SEP]", "[MASK]"]
trainer = trainers.WordPieceTrainer(vocab_size=25000, special_tokens=special_tokens)
```

Cũng như việc chỉ định `vocab_size` và `special_tokens`, ta cần thiết lập `min_frequency` (số lần một token phải xuất hiện để được thêm vào bộ từ vựng) hoặc thay đổi `continuing_subword_prefix` (nếu ta muốn sử dụng thứ gì khác ngoài `##`).

Để huấn luyện một mô hình sử dụng trình lặp ta định nghĩa trước đó, ta chỉ cần thực hiện lệnh này:

```python
tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
```

Chúng ta cũng có thể sử dụng các tệp văn bản để huấn luyện tokenizer của mình như sau (ta tái khởi tạo mô hình với một `WordPiece` rỗng):

```python
tokenizer.model = models.WordPiece(unk_token="[UNK]")
tokenizer.train(["wikitext-2.txt"], trainer=trainer)
```

Trong cả hai trường hợp, ta có thể kiểm tra xem tokenizer trên một đoạn văn bản bằng cách sử dụng phương thức `encode()`:

```python
encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
```

```python out
['let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.']
```

`encoding` thu được là một `Encoding` gồm tất cả các đầu ra cần thiết của một tokenizer trong tất cả các thông số đa dạng của nó: `ids`, `type_ids`, `tokens`, `offsets`, `attention_mask`, `special_tokens_mask`, và `overflowing`.

Bước cuối của quy trình đó là hậu xử lý. Ta cần thêm token `[CLS]` token at the beginning and the `[SEP]` ở cuối (hoặc sau mỗi câu, nếu ta có cặp câu). Chúng ta sẽ sử dụng `TemplateProcessor` để thực hiện điều này, nhưng trước hết ta cần biết các ID của token `[CLS]` và `[SEP]` trong bộ từ vựng.

```python
cls_token_id = tokenizer.token_to_id("[CLS]")
sep_token_id = tokenizer.token_to_id("[SEP]")
print(cls_token_id, sep_token_id)
```

```python out
(2, 3)
```

Để viết bản mẫu cho `TemplateProcessor`, chúng ta phải chỉ định cách xử lý một câu đơn và một cặp câu. Đối với cả hai, chúng tôi viết các token đặc biệt muốn sử dụng; câu đầu tiên (hoặc câu đơn) được biểu thị bằng `$A`, trong khi câu thứ hai (nếu token một cặp) được biểu thị bằng `$B`. Đối với mỗi loại trong số này (token và câu đặc biệt), chúng ta cũng chỉ định loại token ID tương ứng sau dấu hai chấm.

Do đó, bản mẫu BERT cổ điển được định nghĩa như sau:

```python
tokenizer.post_processor = processors.TemplateProcessing(
    single=f"[CLS]:0 $A:0 [SEP]:0",
    pair=f"[CLS]:0 $A:0 [SEP]:0 $B:1 [SEP]:1",
    special_tokens=[("[CLS]", cls_token_id), ("[SEP]", sep_token_id)],
)
```

Lưu ý rằng chúng ta cần truyền vào tất cả các IDs của các kí tự đặc biệt, nên các tokenize có thể chuyển đổi chúng thành các cặp ID.

Một khi đã được thêm vào, chúng ta có thể quay lại ví dụ trước đó và sẽ nhận được:

```python
encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
```

```python out
['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.', '[SEP]']
```

Và trên một cặp câu, chúng ta có thể có được kết quả sau:

```python
encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences.")
print(encoding.tokens)
print(encoding.type_ids)
```

```python out
['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '...', '[SEP]', 'on', 'a', 'pair', 'of', 'sentences', '.', '[SEP]']
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
```

Chúng ta đã gần như hoàn thành việc xây dựng tokenizer này từ đầu -- bước cuối cùng là thêm vào một trình giải mã:

```python
tokenizer.decoder = decoders.WordPiece(prefix="##")
```

Hãy cũng kiểm thử với `encoding`:

```python
tokenizer.decode(encoding.ids)
```

```python out
"let's test this tokenizer... on a pair of sentences."
```

Tuyệt vời! Ta có thể lưu tokenizer của mình vào trong một tệp JSON như dưới đây:

```python
tokenizer.save("tokenizer.json")
```

Ta sau đó có thể tải lại tệp này trong đối tượng `Tokenizer` với phương thức `from_file()`:

```python
new_tokenizer = Tokenizer.from_file("tokenizer.json")
```

Để sử dụng tokenizer này trong 🤗 Transformers, chúng ta phải bọc nó trong `PreTrainedTokenizerFast`. Chúng ta có thể sử dụng lớp chung hoặc, nếu tokenizer của chúng ta tương ứng với một mô hình hiện có, hãy sử dụng lớp đó (ở đây là `BertTokenizerFast`). Nếu bạn áp dụng bài học này để xây dựng một tokenizer hoàn toàn mới, bạn sẽ phải sử dụng tùy chọn đầu tiên.

Để bọc tokenizer trong một `PreTrainedTokenizerFast`, chúng ta có thể chuyển tokenizer mà chúng ta đã xây dựng dưới dạng `tokenizer_object` hoặc truyền tệp tokenizer mà chúng ta đã lưu dưới dạng `tokenizer_file`. Điều quan trọng cần nhớ là chúng ta phải đặt thủ công tất cả các token đặc biệt, vì lớp đó không thể suy ra từ đối tượng `tokenizer` token nào là token bị che, `[CLS]`, v.v.:

```python
from transformers import PreTrainedTokenizerFast

wrapped_tokenizer = PreTrainedTokenizerFast(
    tokenizer_object=tokenizer,
    # tokenizer_file="tokenizer.json", # Bạn có thể tải từ tệp tokenizer
    unk_token="[UNK]",
    pad_token="[PAD]",
    cls_token="[CLS]",
    sep_token="[SEP]",
    mask_token="[MASK]",
)
```

Nếu bạn đang sự dụng một lớp tokenizer đặc biệt (như `BertTokenizerFast`), bạn chỉ cần chỉ định một token đặc biết khác so với mặc định (ở đây là không xác định):

```python
from transformers import BertTokenizerFast

wrapped_tokenizer = BertTokenizerFast(tokenizer_object=tokenizer)
```

Bạn có thể sử dụng tokenizer như bất kỳ tokenizer nào khác của 🤗 Transformers. Bạn có thể lưu nó với phương thức `save_pretrained()`, hoặc lại nó lên Hub sử dụng phương thức `push_to_hub()`.

Giờ chúng ta đã thấy cách xây dựng bộ WordPiece tokenizer, hãy làm tương tự đối với BPE tokenizer. Chúng ta sẽ tiến hành nhanh hơn một chút vì bạn đã biết tất cả các bước và chỉ làm nổi bật những điểm khác biệt.

## Xây dựng một BPE tokenizer từ đầu

Giờ hãy cũng nhau xây dựng GPT-2 tokenizer. Giống như BERT tokenizer, chúng ta bắt đầu bằng việc khởi tạo `Tokenizer` với mô hình BPE:

```python
tokenizer = Tokenizer(models.BPE())
```

Cũng giống như BERT, chúng ta có thể khởi tạo mô hình này với một bộ từ vựng nếu ta đã có (ta sẽ cần truyền vào `vocab` và `merges` trong trường hợp này), nhưng vì ta sẽ huấn luyện từ đầu, chúng ta không cần làm vậy. Ta cũng không cần chỉ định `unk_token` vì GPT-2 sử dụng BPE cấp byte, phương pháp không cần đến nó.

GPT-2 không sử dụng một trình chuẩn hoá, nên ta có thể bỏ qua bước này và đi trực tiếp vào bước pre-tokenization:

```python
tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
```

Tuỳ chọn `ByteLevel` chúng ta thêm vào ở đây không thêm dấu cách vào đầu của một câu (thường nó là mặc định). Ta có thể nhìn các pre-tokenization từ ví dụ tương tự ở trên:

```python
tokenizer.pre_tokenizer.pre_tokenize_str("Let's test pre-tokenization!")
```

```python out
[('Let', (0, 3)), ("'s", (3, 5)), ('Ġtest', (5, 10)), ('Ġpre', (10, 14)), ('-', (14, 15)),
 ('tokenization', (15, 27)), ('!', (27, 28))]
```

Tiếp theo là mô hình mà ta cần huấn luyện. Với GPT-2, token đặc biệt duy nhất ta cần là token kết thúc văn bản: 

```python
trainer = trainers.BpeTrainer(vocab_size=25000, special_tokens=["<|endoftext|>"])
tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
```

Như với `WordPieceTrainer`, cũng như `vocab_size` và `special_tokens`, ta có thể chỉ định `min_frequency` nếu muốn, hoặc nếu ta có hậu tố kết thúc từ (như `</w>`), ta có thể thiết lập nó với `end_of_word_suffix`.

Tokenizer này cũng có thể được huấn luyện trên các tệp văn bản:

```python
tokenizer.model = models.BPE()
tokenizer.train(["wikitext-2.txt"], trainer=trainer)
```

Hãy cũng xem kết quả tokenize trên một văn bản mẫu:

```python
encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
```

```python out
['L', 'et', "'", 's', 'Ġtest', 'Ġthis', 'Ġto', 'ken', 'izer', '.']
```

Ta áp dụng hậu xử lý cấp byte cho GPT-2 tokenizer như sau:

```python
tokenizer.post_processor = processors.ByteLevel(trim_offsets=False)
```

Tuỳ chọn `trim_offsets = False` chỉ cho trình hậu xử lý biết rằng ta cần bỏ mốt số offset token bắt đầu với 'Ġ': theo cách này, điểm bắt đầu của offset sẽ trỏ vào vùng không gian phía trước của từ, không phải kí tự đầu tiên của từ (vì không gian này về mặt kỹ thuật là một phần của từ). Hãy cùng nhìn xem kết quả với chuỗi văn bản ta vừa mã hoá với `'Ġtest'` là token ở chỉ mục 4:

```python
sentence = "Let's test this tokenizer."
encoding = tokenizer.encode(sentence)
start, end = encoding.offsets[4]
sentence[start:end]
```

```python out
' test'
```

Cuối cùng, ta thêm một trình giải mãi cấp byte:

```python
tokenizer.decoder = decoders.ByteLevel()
```

và ta kiểm tra lại xem nó hoạt động đúng chưa:

```python
tokenizer.decode(encoding.ids)
```

```python out
"Let's test this tokenizer."
```

Tuyệt vời! Giờ ta đã xong rồi, ta có thể lưu tokenizer như trên, và bao nó lại trong `PreTrainedTokenizerFast` hoặc  `GPT2TokenizerFast` nếu ta muốn nó trong 🤗 Transformers:

```python
from transformers import PreTrainedTokenizerFast

wrapped_tokenizer = PreTrainedTokenizerFast(
    tokenizer_object=tokenizer,
    bos_token="<|endoftext|>",
    eos_token="<|endoftext|>",
)
```

or:

```python
from transformers import GPT2TokenizerFast

wrapped_tokenizer = GPT2TokenizerFast(tokenizer_object=tokenizer)
```

Như một ví dụ cuối, chúng tôi sẽ chỉ bạn cách xây dựng một Unigram tokenizer từ đầu.

## Xây dựng một Unigram tokenizer từ đầu

Hãy cùng nhau xây dựng một XLNet tokenizer. Cũng giống như các tokenizer trước đó, ta có thể bắt đầu khởi tạo `Tokenizer` với một mô hình Unigram:

```python
tokenizer = Tokenizer(models.Unigram())
```

Một lần nữa, chúng ta có thể khởi tạo mô hình này với một từ vựng nếu có.

Với sự chuẩn hoá này, XLNet sử dụng một vài phương pháp thay thế (đến từ SentencePiece):

```python
from tokenizers import Regex

tokenizer.normalizer = normalizers.Sequence(
    [
        normalizers.Replace("``", '"'),
        normalizers.Replace("''", '"'),
        normalizers.NFKD(),
        normalizers.StripAccents(),
        normalizers.Replace(Regex(" {2,}"), " "),
    ]
)
```

Điều này thay thế <code>``</code> and <code>''</code> bằng <code>"</code> và thay thế bất kì chuỗi nào chứa hai hoặc nhiều hơn dấu cách liền nhau thành một dấu duy nhất, cũng như loại bỏ các dấu có trong văn bản để tokenize.

Tiền tokenizer được sử dụng cho bất kỳ SentencePiece tokenizer là `Metaspace`:

```python
tokenizer.pre_tokenizer = pre_tokenizers.Metaspace()
```

Ta có thể nhìn vào đầu ra quy trình tiền tokenize qua ví dụ văn bản ở dưới:

```python
tokenizer.pre_tokenizer.pre_tokenize_str("Let's test the pre-tokenizer!")
```

```python out
[("▁Let's", (0, 5)), ('▁test', (5, 10)), ('▁the', (10, 14)), ('▁pre-tokenizer!', (14, 29))]
```

Tiếp theo là mô hình ta cần huấn luyện. XLNet có một số token đặc biệt:

```python
special_tokens = ["<cls>", "<sep>", "<unk>", "<pad>", "<mask>", "<s>", "</s>"]
trainer = trainers.UnigramTrainer(
    vocab_size=25000, special_tokens=special_tokens, unk_token="<unk>"
)
tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
```

Một tham số vô cùng quan trong mà ta không thể quên của `UnigramTrainer` là `unk_token`. Ta có thể truyền vào các tham số cụ thể khác tới thuật toán Unigram, ví dụ `shrinking_factor` cho các bước mà ta xoá token (mặc định là 0.75) hoặc `max_piece_length` để chỉ định độ dài tối đa của một token (mặc định là 16).

Tokenizer này có thể được huấn luyện trên các tệp văn bản:

```python
tokenizer.model = models.Unigram()
tokenizer.train(["wikitext-2.txt"], trainer=trainer)
```

Hãy cùng nhìn xem kết quả tokenize trên tập mẫu:

```python
encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
```

```python out
['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.']
```

Một điểm đặc biệt của XLNet đó là nó thêm token `<cls>` ở cuối mỗi câu, với kiểu ID là 2 (để phân biết với các token khác). Nó đệm thêm vào phía bên tay trái giống như kết quả ở trên. Ta có thể xử lý tất cả các token đặc biệt và các token kiểu ID với cùng một bản mẫu, như BERT, nhưng đầu tiên ta phải lấy các ID của token `<cls>` và `<sep>`:

```python
cls_token_id = tokenizer.token_to_id("<cls>")
sep_token_id = tokenizer.token_to_id("<sep>")
print(cls_token_id, sep_token_id)
```

```python out
0 1
```

Bản mẫu sẽ trông như sau:

```python
tokenizer.post_processor = processors.TemplateProcessing(
    single="$A:0 <sep>:0 <cls>:2",
    pair="$A:0 <sep>:0 $B:1 <sep>:1 <cls>:2",
    special_tokens=[("<sep>", sep_token_id), ("<cls>", cls_token_id)],
)
```

Và ta có thể kiểm tra xem nó hoạt động không bằng cách mã hoá cặp câu:

```python
encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences!")
print(encoding.tokens)
print(encoding.type_ids)
```

```python out
['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.', '.', '.', '<sep>', '▁', 'on', '▁', 'a', '▁pair', 
  '▁of', '▁sentence', 's', '!', '<sep>', '<cls>']
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2]
```

Cuối cùng, ta sẽ thêm trình giải mã `Metaspace`:

```python
tokenizer.decoder = decoders.Metaspace()
```

và ta đã xong với tokenizer này! Ta có thể lưu tokenizer như trên, và bao nó lại trong `PreTrainedTokenizerFast` hoặc  `XLNetTokenizerFast` nếu ta muốn nó trong 🤗 Transformers. Một điểm cần lưu ý là khi sử dụng `PreTrainedTokenizerFast` thì trên đầu của các token đặc biệt ta cần nói cho thư viện 🤗 Transformers viết ta cần đệm vào phía bên trái:

```python
from transformers import PreTrainedTokenizerFast

wrapped_tokenizer = PreTrainedTokenizerFast(
    tokenizer_object=tokenizer,
    bos_token="<s>",
    eos_token="</s>",
    unk_token="<unk>",
    pad_token="<pad>",
    cls_token="<cls>",
    sep_token="<sep>",
    mask_token="<mask>",
    padding_side="left",
)
```

Hoặc một cách khác:

```python
from transformers import XLNetTokenizerFast

wrapped_tokenizer = XLNetTokenizerFast(tokenizer_object=tokenizer)
```

Bây giờ bạn đã thấy cách các khối khác nhau được sử dụng để xây dựng các tokenizer hiện nay, bạn sẽ có thể viết bất kỳ trình tokenize nào mà bạn muốn với thư viện 🤗 Tokenizers và có thể sử dụng nó trong 🤗 Transformers.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter6/8.mdx" />

### Chuẩn hoá và tiền tokenize
https://huggingface.co/learn/course/vi/chapter6/4.md

# Chuẩn hoá và tiền tokenize

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter6/section4.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter6/section4.ipynb"},
]} />

Trước khi đi sâu hơn vào ba thuật toán tokenize từ phụ phổ biến nhất được sử dụng với các mô hình Transformer (Mã hóa theo cặp [BPE], WordPiece và Unigram), trước tiên chúng ta sẽ xem xét tiền xử lý mà mỗi trình tokenize áp dụng cho văn bản. Dưới đây là tổng quan cấp cao về các bước trong pipeline tokenize:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/tokenization_pipeline.svg" alt="The tokenization pipeline.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/tokenization_pipeline-dark.svg" alt="The tokenization pipeline.">
</div>

Trước khi tách một đoạn văn bản thành các token phụ (dựa theo mô hình)tokenizer sẽ thực hiện 2 bước: _normalization_ (chuẩn hoá) và _pre-tokenization_ (tiền tokenize).

## Chuẩn hoá

<Youtube id="4IIC2jI9CaU"/>

Bước chuẩn hóa bao gồm một số thao tác dọn dẹp, chẳng hạn như loại bỏ khoảng trắng không cần thiết, viết thường tất cả các chữ, và/hoặc xóa dấu. Nếu bạn đã quen với [chuẩn hóa Unicode](http://www.unicode.org/reports/tr15/) (chẳng hạn như NFC hoặc NFKC), thì đây cũng là điều mà tokenizer có thể áp dụng.

`tokenizer` của 🤗 Transformers có một thuộc tính gọi là `backend_tokenizer` cung cấp quyền truy cập vào tokenizer bên dưới từ thư viện 🤗 Tokenizers:

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
print(type(tokenizer.backend_tokenizer))
```

```python out
<class 'tokenizers.Tokenizer'>
```

Thuộc tính `normalizer` của đối tượng `tokenizer` có phương thức `normalize_str()` mà ta có thể dùng để thấy cách bước chuẩn hoá được thực hiện:

```py
print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
```

```python out
'hello how are u?'
```

Trong ví dụ này, vì chúng ta chọn checkpoint `bert-base-uncased`, bước chuẩn hoá sẽ thực hiện viết thường và loại bỏ các dấu.

<Tip>

✏️ **Try it out!** Tải tokenizer từ checkpoint `bert-base-cased` và truyền vào cùng một ví dụ vào.Sự khác biệt chính mà bạn có thể thấy giữa các phiên bản có dấu và không dấu của tokenizer là gì?

</Tip>

## Pre-tokenization

<Youtube id="grlLV8AIXug"/>

Như chúng ta sẽ thấy trong các phần tiếp theo, một tokenizer không thể được huấn luyện trên văn bản thô. Thay vào đó, trước tiên chúng ta cần chia các văn bản thành các thực thể nhỏ, như các từ. Đó là khi bước pre-tokenization bắt đầu. Như chúng ta đã thấy trong [Chương 2](/course/chapter2), trình tokenize dựa trên từ có thể chỉ cần tách một văn bản thô thành các từ dựa trên khoảng trắng và dấu câu. Những từ đó sẽ là ranh giới của các token con mà tokenizer có thể học được trong quá trình huấn luyện của nó.

Để xem cách một tokenizer nhanh thực hiện pre-tokenization, chúng ta có thể sử dụng phương thức `pre_tokenize_str()` của thuộc tính `pre_tokenizer` của đối tượng `tokenizer`:

```py
tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")
```

```python out
[('Hello', (0, 5)), (',', (5, 6)), ('how', (7, 10)), ('are', (11, 14)), ('you', (16, 19)), ('?', (19, 20))]
```

Lưu ý cách tokenizer đã theo dõi các offset, đó là cách nó có thể cung cấp cho chúng ta ánh xạ offset mà ta đã sử dụng trong phần trước. Ở đây tokenizer bỏ qua hai khoảng trắng và thay thế chúng bằng chỉ một, nhưng các offset xen giữa `are` và `you` để giải thích điều đó.

Vì chúng ta đang sử dụng BERT tokenizer, pre-tokenization liên quan đến việc phân tách dựa trên khoảng trắng và dấu chấm câu. Các tokenizer khác có thể có các quy tắc khác nhau cho bước này. Ví dụ: nếu sử dụng GPT-2 tokenizer:

```py
tokenizer = AutoTokenizer.from_pretrained("gpt2")
tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")
```

nó sẽ tách dựa trên dấu cách và dấu câu, nhưng sẽ giữa dấu cách và thay thế chúng bởi kí hiệu `Ġ`, cho phép nó khôi phục không gian ban đầu nếu chúng tôi giải mã các token:

```python out
[('Hello', (0, 5)), (',', (5, 6)), ('Ġhow', (6, 10)), ('Ġare', (10, 14)), ('Ġ', (14, 15)), ('Ġyou', (15, 19)),
 ('?', (19, 20))]
```

Cần lưu ý thêm rằng không như BERT tokenizer, tokenizer này bỏ qua dấu cách kép.

Ở ví dụ cuối, hãy cùng xem T5 tokenizer dựa trên thuật toán SentencePiece:

```py
tokenizer = AutoTokenizer.from_pretrained("t5-small")
tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")
```

```python out
[('▁Hello,', (0, 6)), ('▁how', (7, 10)), ('▁are', (11, 14)), ('▁you?', (16, 20))]
```

Giống như GPT-2 tokenizer, phương pháp này giữ các dấu cách và thay thế chúng bởi một tí tự đặc biệt (`_`), nhưng T5 tokenizer chỉ tách dựa theo dấu cách, không dựa theo dấu câu. Một lưu ý nữa đó là nó cũng mặc định thêm dấu cách ở phía đầu câu (trước `Hello`) và bỏ qua những dấu cách kẹp ở giữa `are` và `you`.

Bây giờ chúng ta đã biết một chút về cách một số loại tokenizers khác nhau để xử lý văn bản, chúng ta có thể bắt đầu tự khám phá các thuật toán cơ bản. Chúng ta sẽ bắt đầu bằng một cái nhìn nhanh về SentencePiece được áp dụng rộng rãi; sau đó, trong ba phần tiếp theo, chúng ta sẽ xem xét cách thức hoạt động của ba thuật toán chính được sử dụng để mã hóa từ phụ.

## SentencePiece

[SentencePiece](https://github.com/google/sentencepiece) là một thuật toán tokenize để tiền xử lý văn bản mà bạn có thể sử dụng với bất kỳ mô hình nào chúng ta sẽ thấy trong ba phần tiếp theo. Nó coi văn bản là một chuỗi các ký tự Unicode và thay thế dấu cách bằng một ký tự đặc biệt, `▁`. Được sử dụng cùng với thuật toán Unigram (xem [phần 7](/course/chapter7/7)), nó thậm chí không yêu cầu bước pre-tokenization, rất hữu ích cho các ngôn ngữ không sử dụng dấu cách (như Trung Quốc hoặc Nhật Bản).

Tính năng chính khác của SentencePiece là *reversible tokenization* hay *tokenize có thể đảo ngược*: vì không có cách xử lý đặc biệt nào cho dấu cách, nên việc giải mã các token được thực hiện đơn giản bằng cách nối chúng và thay thế các dấu `_` bằng dấu cách - điều này giúp văn bản được chuẩn hóa. Như chúng ta đã thấy trước đó, BERT tokenizer loại bỏ các dấu cách lặp lại, vì vậy token của nó không thể đảo ngược.

## Tổng quan thuật toán

Trong các phần tiếp theo, chúng ta sẽ đi sâu vào ba thuật toán tokenize từ phụ tiêu biểu: BPE (được sử dụng bởi GPT-2 và các thuật toán khác), WordPiece (được sử dụng bởi BERT) và Unigram (được sử dụng bởi T5 và các thuật toán khác). Trước khi chúng ta bắt đầu, đây là tổng quan nhanh về cách hoạt động của từng loại. Đừng ngần ngại quay lại bảng này sau khi đọc từng phần tiếp theo nếu bạn chưa hiểu hết.

Mô hình | BPE | WordPiece | Unigram
:----:|:---:|:---------:|:------:
Huấn luyện | Bắt đầu với một bộ từ vựng nhỏ và học bộ quy tắc hợp nhất token |  Bắt đầu với một bộ từ vựng nhỏ và học bộ quy tắc hợp nhất token | Bắt đầu với một bộ từ vựng lớn và học bộ quy tắc để loại bỏ token
Bước huấn luyện | Gộp các token liên quan đến cặp phổ biến nhất | Gộp các token liên quan đến cặp có điểm cao nhất dựa trên tần suất của cặp, with the best score based on the frequency of the pair,  ưu tiên các cặp mà mỗi token cá nhân tần suất thấp hơn| Loại bỏ tất cả các token trong bộ từ điển giảm thiểu tối đa độ mất mát được tính trên toàn bộ kho ngữ liệu
Học | Gộp bộ quy tắc và bộ từ vựng | Chỉ bộ từ vựng  | Một bộ tự vựng với điểm cho mỗi token
Mã hoá | Chia từ thành các kí tự và áp dụng bước gộp từ quá trình huấn luyện | Tìm ra chuỗi từ phụ dài nhất bắt đầu từ phần bắt đầu có trong bộ từ vựng, sau đó làm tương tự với các phần còn lại của từ | Tìm từ có khả năng chia thành token cao nhất sử dụng điểm có được từ quá trình huấn luyện

Giờ chúng ta hãy đi sâu vào BPE thôi!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter6/4.mdx" />

### Unigram tokenization
https://huggingface.co/learn/course/vi/chapter6/7.md

# Unigram tokenization

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter6/section7.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter6/section7.ipynb"},
]} />

Thuật toán Unigram thường được sử dung trong SentencePiece,  đây là một thuật toán tokenize cho các mô hình như AlBERT, T5, mBART, Big Bird, và XLNet.

<Youtube id="TGZfZVuF9Yc"/>

<Tip>

💡 Phần này sẽ đi sâu vào Unigram cũng như toàn bộ cách triển khai. Bạn có thể bỏ qua phần cuối nếu bạn chỉ quan tâm tổng quan thuật toán tokenize.

</Tip>

## Thuật toán huấn luyện

So với BPE và WordPiece, Unigram hoạt động theo hướng khác: nó bắt đầu từ một từ vựng lớn và loại bỏ các token cho đến khi nó đạt đến kích thước từ vựng mong muốn. Có một số tùy chọn để sử dụng để xây dựng vốn từ vựng cơ bản: ví dụ: chúng ta có thể lấy các chuỗi con phổ biến nhất trong các từ được tiền tokenize hoặc áp dụng BPE trên kho ngữ liệu ban đầu có kích thước từ vựng lớn.

Tại mỗi bước của quá trình huấn luyện, thuật toán Unigram tính toán sự mất mát trên kho ngữ liệu được cung cấp từ vựng hiện tại. Sau đó, đối với mỗi ký hiệu trong từ vựng, thuật toán sẽ tính toán mức độ tổn thất tổng thể sẽ tăng lên bao nhiêu nếu ký hiệu bị xóa và tìm kiếm các ký hiệu làm tăng nó ít nhất. Những biểu tượng đó có ảnh hưởng thấp hơn đến sự mất mát tổng thể đối với kho dữ liệu, vì vậy theo một nghĩa nào đó, chúng "ít cần thiết hơn" và là những ứng cử viên tốt nhất để loại bỏ.

Đây là một hoạt động rất tốn kém, vì vậy chúng tôi không chỉ loại bỏ một biểu tượng liên quan đến mức tăng tổn thất thấp nhất, mà \\(p\\) (\\(p\\) là một siêu tham số bạn có thể kiểm soát, thường là 10 hoặc 20) phần trăm các ký hiệu liên quan đến mức tăng tổn thất thấp nhất. Quá trình này sau đó được lặp lại cho đến khi từ vựng đạt được kích thước mong muốn.

Lưu ý rằng chúng ta không bao giờ xóa các ký tự cơ sở, để đảm bảo rằng bất kỳ từ nào cũng có thể được tokenize.

Bây giờ, điều này vẫn còn hơi mơ hồ: phần chính của thuật toán là tính toán sự mất mát trong kho ngữ liệu và xem nó thay đổi như thế nào khi chúng tôi xóa một số token khỏi từ vựng, nhưng chúng ta chưa giải thích cách thực hiện điều này. Bước này dựa trên thuật toán tokenize của mô hình Unigram, vì vậy chúng ta sẽ đi sâu vào phần tiếp theo.

Chúng ta sẽ tái sử dụng kho ngữ liệu từ các ví dụ trước:

```
("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
```

and for this example, we will take all strict substrings for the initial vocabulary :

```
["h", "u", "g", "hu", "ug", "p", "pu", "n", "un", "b", "bu", "s", "hug", "gs", "ugs"]
```

## Thuật toán tokenize

Mô hình Unigram là một loại mô hình ngôn ngữ coi mỗi token là độc lập với các token trước nó. Đó là mô hình ngôn ngữ đơn giản nhất, theo nghĩa xác suất của token X trong bối cảnh trước đó chỉ là xác suất của token X. Vì vậy, nếu chúng ta sử dụng mô hình ngôn ngữ Unigram để tạo văn bản, chúng ta sẽ luôn dự đoán token phổ biến nhất.

Xác suất của một token nhất định là tần suất của nó (số lần chúng ta tìm thấy nó) trong kho tài liệu gốc, chia cho tổng tất cả các tần số của tất cả các token trong từ vựng (để đảm bảo xác suất tổng bằng 1). Ví dụ: `"ug"` có trong `"hug"`, `"pug"` và `"hugs"`, vì vậy nó có tần suất là 20 trong kho ngữ liệu của chúng tôi.

Dưới đây là tần suất của tất cả các từ phụ có thể có trong từ vựng:

```
("h", 15) ("u", 36) ("g", 20) ("hu", 15) ("ug", 20) ("p", 17) ("pu", 17) ("n", 16)
("un", 16) ("b", 4) ("bu", 4) ("s", 5) ("hug", 15) ("gs", 5) ("ugs", 5)
```

Vậy nên, tổng của tất cả các tần suất là 210, và xác suất của từ phụ `"ug"` là 20/210.

<Tip>

✏️ **Giờ đến lượt bạn!** Viết đoạn mã để tính tần suất trên và kiểm tra lại kết quả hiển thị cũng như tổng đã đúng chưa.

</Tip>

Giờ, để tokenize một từ cho trước, chúng ta sẽ nhìn vào tất cả các phần đoạn thành token và tính xác suất của từng cái theo mô hình Unigram. Vì tất cả token được cho là độc lập, xác suất này chỉ là tích của xác suất mỗi token. Ví dụ, `["p", "u", "g"]` của `"pug"` có xác suất:

$$P([``p", ``u", ``g"]) = P(``p") \times P(``u") \times P(``g") = \frac{5}{210} \times \frac{36}{210} \times \frac{20}{210} = 0.000389$$

Tương tự, `["pu", "g"]` có xác suất:

$$P([``pu", ``g"]) = P(``pu") \times P(``g") = \frac{5}{210} \times \frac{20}{210} = 0.0022676$$

Nói chung, việc tokenize có ít token nhất có thể sẽ có xác suất cao nhất (vì phép chia cho 210 lặp lại cho mỗi token), tương ứng với những gì chúng ta muốn trực quan: chia một từ thành số lượng token nhất có thể.

Tokenize của một từ với mô hình Unigram sau đó là token có xác suất cao nhất. Trong ví dụ về `"pug"`, đây là các xác suất mà chúng ta sẽ nhận được cho mỗi phân đoạn có thể có:

```
["p", "u", "g"] : 0.000389
["p", "ug"] : 0.0022676
["pu", "g"] : 0.0022676
```

Vì vậy, `"pug"` sẽ được tokenize là `["p", "ug"]`  hoặc `["pu", "g"]`, tùy thuộc vào phân đoạn nào trong số đó được gặp đầu tiên (lưu ý rằng trong một phân đoạn lớn hơn ngữ liệu, những trường hợp bình đẳng như thế này sẽ rất hiếm).

Trong trường hợp này, thật dễ dàng để tìm tất cả các phân đoạn có thể có và tính toán xác suất của chúng, nhưng nói chung sẽ khó hơn một chút. Có một thuật toán cổ điển được sử dụng cho việc này, được gọi là thuật toán *Viterbi*. Về cơ bản, chúng ta có thể xây dựng một biểu đồ để phát hiện các phân đoạn có thể có của một từ nhất định bằng cách nói rằng có một nhánh từ ký tự _a_ đến ký tự _b_ nếu từ phụ từ _a_ đến _b_ nằm trong từ vựng và quy cho nhánh đó xác suất của từ phụ .

Để tìm đường dẫn trong biểu đồ đó sẽ có điểm tốt nhất, thuật toán Viterbi xác định, đối với mỗi vị trí trong từ, phân đoạn có điểm tốt nhất kết thúc tại vị trí đó. Vì chúng ta đi từ đầu đến cuối, điểm tốt nhất có thể được tìm thấy bằng cách lặp qua tất cả các từ phụ kết thúc ở vị trí hiện tại và sau đó sử dụng điểm token tốt nhất từ ​​vị trí mà từ phụ này bắt đầu. Sau đó, chúng ta chỉ cần bỏ qua con đường đã thực hiện để đến cuối.

Hãy xem một ví dụ sử dụng từ vựng của chúng ta và từ `"unhug"`. Đối với mỗi vị trí, các từ phụ có điểm số tốt nhất kết thúc ở đó như sau:

```
Character 0 (u): "u" (score 0.171429)
Character 1 (n): "un" (score 0.076191)
Character 2 (h): "un" "h" (score 0.005442)
Character 3 (u): "un" "hu" (score 0.005442)
Character 4 (g): "un" "hug" (score 0.005442)
```

Vậy `"unhug"` có thể tokenize thành `["un", "hug"]`.

<Tip>

✏️ **Giờ đến lượt bạn!** Xác định token của từ `"huggun"`, và điểm cảu chúng.

</Tip>

## Quay lại huấn luyện

Bây giờ chúng ta đã thấy cách thức hoạt động của tokenize, chúng ta có thể tìm hiểu sâu hơn một chút về sự mất mát được sử dụng trong quá trình huấn luyện. Ở bất kỳ giai đoạn nhất định nào, sự mất mát này được tính toán bằng cách tokenize mọi từ trong kho ngữ liệu, sử dụng từ vựng hiện tại và mô hình Unigram được xác định bởi tần số của mỗi token trong kho ngữ liệu (như đã thấy trước đây).

Mỗi từ trong kho ngữ liệu đều có một điểm và sự mất mát là khả năng bị âm của những điểm số đó - nghĩa là tổng cho tất cả các từ trong kho ngữ liệu của tất cả các `-log(P(word))`.

Cùng xem ví dụ của chúng ta với kho ngữ liệu dưới đây:

```
("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
```

Kết quả tokenize mỗi từ và điểm tương ứng của chúng như sau:

```
"hug": ["hug"] (score 0.071428)
"pug": ["pu", "g"] (score 0.007710)
"pun": ["pu", "n"] (score 0.006168)
"bun": ["bu", "n"] (score 0.001451)
"hugs": ["hug", "s"] (score 0.001701)
```

Và sự mất mát bằng:

```
10 * (-log(0.071428)) + 5 * (-log(0.007710)) + 12 * (-log(0.006168)) + 4 * (-log(0.001451)) + 5 * (-log(0.001701)) = 169.8
```

Giờ thì ta cần tính xem việc loại bỏ mỗi token sẽ ảnh hưởng thế nào tới sự mất mát. Điều này khá tẻ nhạt, vì vậy chúng ta sẽ chỉ làm điều đó cho hai token ở đây và lưu toàn bộ quá trình khi chúng ta có đoạn mã trợ giúp. Trong trường hợp (rất) cụ thể này, chúng tôi có hai token tương đương của tất cả các từ: như chúng ta đã thấy trước đó, ví dụ: `"pug"` có thể được tokenize `["p","ug"]` với cùng số điểm. Do đó, loại bỏ mã thông báo `"pu"` khỏi từ vựng sẽ gây ra sự mất mát tương tự.

Mặt khác, việc loại bỏ `"hug"` sẽ làm cho sự mất mát trở nên tồi tệ hơn, bởi vì token của `"hug"` và `"hugs"` sẽ trở thành:

```
"hug": ["hu", "g"] (score 0.006802)
"hugs": ["hu", "gs"] (score 0.001701)
```

Những thay đổi này sẽ ảnh hưởng đến và làm sự mất mát tăng lên

```
- 10 * (-log(0.071428)) + 10 * (-log(0.006802)) = 23.5
```

Vì vậy, token `"pu"` chắc chắn sẽ bị loại khỏi bộ từ vựng, nhưng `"hug"` thì không.

## Triển khai Unigram

Giờ hãy cùng triển khai mọi thứ ta đã cùng xem thông qua đaonj mã. Giống như BPE và WordPiece, đây không phải là một cách triển khai hiểu quả của thuật toán (khá ngược lại), nhưng nó sẽ giúp bạn hiểu hơn về Unigram.

Ta sẽ sử dùng cùng bộ ngữ liệu đã sử dụng như một ví dụ:

```python
corpus = [
    "This is the Hugging Face Course.",
    "This chapter is about tokenization.",
    "This section shows several tokenizer algorithms.",
    "Hopefully, you will be able to understand how they are trained and generate tokens.",
]
```

Lần này, ta sẽ sử dụng `xlnet-base-cased` như mô hình:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("xlnet-base-cased")
```

Tương tự BPE và WordPiece, ta sẽ bắt đầu đếm tần suất xuất hiện của mỗi từ trong kho ngữ liệu:

```python
from collections import defaultdict

word_freqs = defaultdict(int)
for text in corpus:
    words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
    new_words = [word for word, offset in words_with_offsets]
    for word in new_words:
        word_freqs[word] += 1

word_freqs
```

Sau đó, ta cần khởi tạo bộ từ vựng với số lượng lớn hơn kích thước ta muốn cuối cùng. Ta phải tổng kết tất cả các kí tự cơ bản (nếu không ta sẽ không thể tokenize tất cả các từ), nhưng với các chuỗi con lớn hơn ta sẽ giữ phần thông dụng nhất và sắp xếp chúng theo tần suất:

```python
char_freqs = defaultdict(int)
subwords_freqs = defaultdict(int)
for word, freq in word_freqs.items():
    for i in range(len(word)):
        char_freqs[word[i]] += freq
        # Lặp qua các từ con có độ dài >= 2
        for j in range(i + 2, len(word) + 1):
            subwords_freqs[word[i:j]] += freq

# Sắp xếp các từ con theo tần suất
sorted_subwords = sorted(subwords_freqs.items(), key=lambda x: x[1], reverse=True)
sorted_subwords[:10]
```

```python out
[('▁t', 7), ('is', 5), ('er', 5), ('▁a', 5), ('▁to', 4), ('to', 4), ('en', 4), ('▁T', 3), ('▁Th', 3), ('▁Thi', 3)]
```

Ta nhóm các kí tự có các từ con tốt nhất vào bộ từ vựng ban đầu có kích thước là 300:

```python
token_freqs = list(char_freqs.items()) + sorted_subwords[: 300 - len(char_freqs)]
token_freqs = {token: freq for token, freq in token_freqs}
```

<Tip>

💡 SentencePiece sử dụng một thuật toán hiệu quả hơn gọi là Enhanced Suffix Array (ESA) để tạo ra bộ từ vựng ban đầu.

</Tip>

Tiếp theo, chúng ta tính tổng tần suất để biến đổi các tần suất này thành xác suất. Với mô hình, chúng ta sẽ lưu các log của xác xuất, vì nó ổn định hơn về mặt số học khi cộng logarit hơn là nhân các số nhỏ và điều này sẽ đơn giản hóa việc tính toán mất mát của mô hình: 

```python
from math import log

total_sum = sum([freq for token, freq in token_freqs.items()])
model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()}
```

Giờ thì các hàm chính là hàm tokenize từ sử dụng thuật toán Viterbi. Như đã thấy trước đó, thuật toán tính phân đoạn tốt nhất của mỗi chuỗi con của từ, được lưu dưới biến  `best_segmentations`. Chúng ta sẽ lưu mỗi vị trí một từ điển trong từ (từ 0 cho tới độ dài của nó), với hai khoá: chỉ mục điểm bắt đầu của token cuối trong phần đoạn tốt nhất, và điểm của phân đoạn tốt nhất. Với chỉ mục của điểm bắt đầu của token cuối trong phần đoạn tốt nhất, ta sẽ có thể truy vấn toàn bộ phân đoạn một khi danh sách được điền đủ.

Việc điền danh sách được thực hiện chỉ với hai vòng lặp: vòng lặp chính đi qua từng vị trí bắt đầu và vòng lặp thứ hai thử tất cả các chuỗi con bắt đầu từ vị trí bắt đầu đó. Nếu chuỗi con có trong từ vựng, chúng ta có một phân đoạn mới của từ cho đến vị trí kết thúc đó, và so sánh với những gì có trong `best_segmentations`.

Khi vòng lặp chính kết thúc, chúng ta chỉ bắt đầu từ cuối và nhảy từ vị trí bắt đầu này sang vị trí tiếp theo, ghi lại các token khi chúng ta đi, cho đến khi chúng ta đến vị trí đầu của từ:

```python
def encode_word(word, model):
    best_segmentations = [{"start": 0, "score": 1}] + [
        {"start": None, "score": None} for _ in range(len(word))
    ]
    for start_idx in range(len(word)):
        # Nó nên được lấp đầy bởi các bước phía trước của vòng lặp
        best_score_at_start = best_segmentations[start_idx]["score"]
        for end_idx in range(start_idx + 1, len(word) + 1):
            token = word[start_idx:end_idx]
            if token in model and best_score_at_start is not None:
                score = model[token] + best_score_at_start
                # Nếu chúng ta tìm thấy một phân đoạn kết thúc tốt hơn tại end_idx, chúng ta cập nhật
                if (
                    best_segmentations[end_idx]["score"] is None
                    or best_segmentations[end_idx]["score"] > score
                ):
                    best_segmentations[end_idx] = {"start": start_idx, "score": score}

    segmentation = best_segmentations[-1]
    if segmentation["score"] is None:
        # Ta đã không tìm thấy tokenize của từ -> không xác định
        return ["<unk>"], None

    score = segmentation["score"]
    start = segmentation["start"]
    end = len(word)
    tokens = []
    while start != 0:
        tokens.insert(0, word[start:end])
        next_start = best_segmentations[start]["start"]
        end = start
        start = next_start
    tokens.insert(0, word[start:end])
    return tokens, score
```

Ta có thể sẵn sàng thử mô hình ban đầu lên một số từ:

```python
print(encode_word("Hopefully", model))
print(encode_word("This", model))
```

```python out
(['H', 'o', 'p', 'e', 'f', 'u', 'll', 'y'], 41.5157494601402)
(['This'], 6.288267030694535)
```

Giờ thì thật dễ dàng để tính sự mất mát của mô hình trên kho ngữ liệu!

```python
def compute_loss(model):
    loss = 0
    for word, freq in word_freqs.items():
        _, word_loss = encode_word(word, model)
        loss += freq * word_loss
    return loss
```

Ta có thể kiểm tra cách nó hoạt động trên mô hình ta có:

```python
compute_loss(model)
```

```python out
413.10377642940875
```

Việc tính điểm cho mỗi token không quả khó; ta chỉ phải tính sự mất mát của mô hình khi xoá mỗi token:

```python
import copy


def compute_scores(model):
    scores = {}
    model_loss = compute_loss(model)
    for token, score in model.items():
        # Ta luôn giữ độ dài các token bằng 1
        if len(token) == 1:
            continue
        model_without_token = copy.deepcopy(model)
        _ = model_without_token.pop(token)
        scores[token] = compute_loss(model_without_token) - model_loss
    return scores
```

Ta có thể thử với token cho trước:

```python
scores = compute_scores(model)
print(scores["ll"])
print(scores["his"])
```

Vì `"ll"` được sử dụng trong quá trình tokenize  `"Hopefully"`, và loại bỏ nó chắc chắn sẽ làm ta thay vào đó sử dụng `"l"` hai lần, ta kì vọng nó sẽ đem lại sự mất mát dương. `"his"` chỉ được sử dụng trong từ `"This"`, nó được tokenize thành chính nó, nên ta kì vọng nó sẽ không có mất mát. Và đây là kết quả:

```python out
6.376412403623874
0.0
```

<Tip>

💡 Phương pháp này rất không hiệu quả, nên SentencePiece  sử dụng một xấp xỉ của hàm mất mát của mô hình mà không dùng token X: thay vì bắt đầu từ đầu, nó chỉ thay thế token X bởi phân đoạn bên trái của nó trong bộ từ vựng. Bằng cách này, tất cả điểm có thể được tính trong cùng một lần đồng thời với sự mất mát của mô hình.

</Tip>

Với tất cả những điều trên, điều cuối cùng ta cần phải làm là thêm các token đặc biệt của mô hình vào bộ từ vựng, sau đó lặp cho đến khi chúng ta cắt đủ số token ta mong muốn cho kích cỡ bộ từ vựng:

```python
percent_to_remove = 0.1
while len(model) > 100:
    scores = compute_scores(model)
    sorted_scores = sorted(scores.items(), key=lambda x: x[1])
    # Loại token percent_to_remove với điểm thấp nhất.
    for i in range(int(len(model) * percent_to_remove)):
        _ = token_freqs.pop(sorted_scores[i][0])
    total_sum = sum([freq for token, freq in token_freqs.items()])
    model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()}
```

Sau đó, để tokenize các đoạn văn bản, ta chỉ cần áp dụng pre-tokenization và sau đỏ sử dụng hàm `encode_word()`:

```python
def tokenize(text, model):
    words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
    pre_tokenized_text = [word for word, offset in words_with_offsets]
    encoded_words = [encode_word(word, model)[0] for word in pre_tokenized_text]
    return sum(encoded_words, [])


tokenize("This is the Hugging Face course.", model)
```

```python out
['▁This', '▁is', '▁the', '▁Hugging', '▁Face', '▁', 'c', 'ou', 'r', 's', 'e', '.']
```

Và đó là Unigram! Hy vọng rằng bây giờ bạn cảm thấy như một chuyên gia trong tất cả mọi tokenizer. Trong phần tiếp theo, chúng ta sẽ đi sâu vào các khối của thư viện 🤗 Tokenizers và chỉ cho bạn cách bạn có thể sử dụng chúng để tạo tokenizer của riêng mình.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter6/7.mdx" />

### Fast tokenizers in the QA pipeline
https://huggingface.co/learn/course/vi/chapter6/3b.md

# Fast tokenizers in the QA pipeline

{#if fw === 'pt'}

<CourseFloatingBanner chapter={6}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter6/section3b_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter6/section3b_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={6}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter6/section3b_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter6/section3b_tf.ipynb"},
]} />

{/if}

Giờ chúng ta sẽ đi sâu vào pipeline `question-answering` và xem cách tận dụng các offset để lấy câu trả lời cho các câu hỏi dựa theo từ ngữ cảnh, giống như chúng ta đã làm với các thực thể được nhóm trong phần trước. Sau đó, chúng ta sẽ xem làm thế nào có thể đối phó với những ngữ cảnh rất dài mà cuối cùng lại bị cắt bớt. Bạn có thể bỏ qua phần này nếu không quan tâm đến tác vụ hỏi đáp.

{#if fw === 'pt'}

<Youtube id="_wxyB3j3mk4"/>

{:else}

<Youtube id="b3u8RzBCX9Y"/>

{/if}

## Sử dụng pipeline `question-answering`

Như đã thấy trong [Chương 1](/course/chapter1), ta có thể sử dụng pipeline `question-answering` như sau để nhận được câu trả lời cho câu hỏi:

```py
from transformers import pipeline

question_answerer = pipeline("question-answering")
context = """
🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch, and TensorFlow — with a seamless integration
between them. It's straightforward to train your models with one before loading them for inference with the other.
"""
question = "Which deep learning libraries back 🤗 Transformers?"
question_answerer(question=question, context=context)
```

```python out
{'score': 0.97773,
 'start': 78,
 'end': 105,
 'answer': 'Jax, PyTorch and TensorFlow'}
```

Không như các pipeline khác không thể cắt gọn và chia văn bản dài hơn độ dài tối đa cho phép của mô hình (dẫn đến bỏ lỡ những thông tin ở phần cuối văn bản), pipeline này có thể xử lý tốt với những ngữ cảnh dài và sẽ trả về câu trả lời kể cả khi nó nằm ở cuối văn bản:

```py
long_context = """
🤗 Transformers: State of the Art NLP

🤗 Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction,
question answering, summarization, translation, text generation and more in over 100 languages.
Its aim is to make cutting-edge NLP easier to use for everyone.

🤗 Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and
then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and
can be modified to enable quick research experiments.

Why should I use transformers?

1. Easy-to-use state-of-the-art models:
  - High performance on NLU and NLG tasks.
  - Low barrier to entry for educators and practitioners.
  - Few user-facing abstractions with just three classes to learn.
  - A unified API for using all our pretrained models.
  - Lower compute costs, smaller carbon footprint:

2. Researchers can share trained models instead of always retraining.
  - Practitioners can reduce compute time and production costs.
  - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages.

3. Choose the right framework for every part of a model's lifetime:
  - Train state-of-the-art models in 3 lines of code.
  - Move a single model between TF2.0/PyTorch frameworks at will.
  - Seamlessly pick the right framework for training, evaluation and production.

4. Easily customize a model or an example to your needs:
  - We provide examples for each architecture to reproduce the results published by its original authors.
  - Model internals are exposed as consistently as possible.
  - Model files can be used independently of the library for quick experiments.

🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch and TensorFlow — with a seamless integration
between them. It's straightforward to train your models with one before loading them for inference with the other.
"""
question_answerer(question=question, context=long_context)
```

```python out
{'score': 0.97149,
 'start': 1892,
 'end': 1919,
 'answer': 'Jax, PyTorch and TensorFlow'}
```

Hãy cùng nhau xem nó làm thế nào!

## Sử dụng mô hình cho tác vụ hỏi đáp

Như những pipeline khác, ta sẽ bắt đầu với việc tokenize đầu vào và sau đó truyền chúng vào trong mô hình. Mặc định checkpoint được sử dụng cho pipeline `question-answering` là [`distilbert-base-cased-distilled-squad`](https://huggingface.co/distilbert-base-cased-distilled-squad) ( "squad" trong tên bắt nguồn từ bộ dữ liệu mà mô hình sử dụng để tinh chỉnh; ta sẽ nói sâu hơn về bộ dữ liệu SQuAD này ở  [Chương 7](/course/chapter7/7)):

{#if fw === 'pt'}

```py
from transformers import AutoTokenizer, AutoModelForQuestionAnswering

model_checkpoint = "distilbert-base-cased-distilled-squad"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)

inputs = tokenizer(question, context, return_tensors="pt")
outputs = model(**inputs)
```

{:else}

```py
from transformers import AutoTokenizer, TFAutoModelForQuestionAnswering

model_checkpoint = "distilbert-base-cased-distilled-squad"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
model = TFAutoModelForQuestionAnswering.from_pretrained(model_checkpoint)

inputs = tokenizer(question, context, return_tensors="tf")
outputs = model(**inputs)
```

{/if}

Lưu ý rằng chúng ta tokenize câu hỏi và ngữ cảnh như một cặp, với câu hỏi đứng trước.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/question_tokens.svg" alt="An example of tokenization of question and context"/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/question_tokens-dark.svg" alt="An example of tokenization of question and context"/>
</div>

Các mô hình hỏi đáp hoạt động hơi khác so với các mô hình mà ta đã thấy cho đến nay. Sử dụng hình trên làm ví dụ, mô hình đã được huấn luyện để dự đoán chỉ mục của token bắt đầu câu trả lời (ở đây là 21) và chỉ mục của token nơi câu trả lời kết thúc (ở đây là 24). Đây là lý do tại sao các mô hình đó không trả về một tensor logit mà là hai: một cho các logit tương ứng với token bắt đầu của câu trả lời và một cho các các logit tương ứng với token kết thúc của câu trả lời. Vì trong trường hợp này, chúng ta chỉ có một đầu vào chứa 66 token, ta nhận được:

```py
start_logits = outputs.start_logits
end_logits = outputs.end_logits
print(start_logits.shape, end_logits.shape)
```

{#if fw === 'pt'}

```python out
torch.Size([1, 66]) torch.Size([1, 66])
```

{:else}

```python out
(1, 66) (1, 66)
```

{/if}

Để chuyển đổi các logit đó thành xác suất, chúng ta sẽ áp dụng một hàm softmax - nhưng trước đó, chúng ta cần đảm bảo rằng chúng ta che dấu các chỉ mục không phải là một phần của ngữ cảnh. Đầu vào của chúng tôi là `[CLS] question [SEP] context [SEP]`, vì vậy chúng ta cần che dấu các token của câu hỏi cũng như token `[SEP]`. Tuy nhiên, chúng ta sẽ giữ token `[CLS]` vì một số mô hình sử dụng nó để chỉ ra rằng câu trả lời không nằm trong ngữ cảnh.

Vì chúng ta sẽ áp dụng softmax sau đó, chúng ta chỉ cần thay thế các logit muốn che bằng một số âm lớn. Ở đây, chúng ta sử dụng `-10000`:

{#if fw === 'pt'}

```py
import torch

sequence_ids = inputs.sequence_ids()
# Che tất cả mọi thứ trừ token của ngữ cảnh
mask = [i != 1 for i in sequence_ids]
# Hiển thị token [CLS]
mask[0] = False
mask = torch.tensor(mask)[None]

start_logits[mask] = -10000
end_logits[mask] = -10000
```

{:else}

```py
import tensorflow as tf

sequence_ids = inputs.sequence_ids()
# Che tất cả mọi thứ trừ token của ngữ cảnh
mask = [i != 1 for i in sequence_ids]
# Hiển thị token [CLS]
mask[0] = False
mask = tf.constant(mask)[None]

start_logits = tf.where(mask, -10000, start_logits)
end_logits = tf.where(mask, -10000, end_logits)
```

{/if}

Giờ chúng ta đã che các logit tương ứng với các vị trí mà chúng ta không muốn dự đoán, chúng ta có thể áp dụng softmax:

{#if fw === 'pt'}

```py
start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)[0]
end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)[0]
```

{:else}

```py
start_probabilities = tf.math.softmax(start_logits, axis=-1)[0].numpy()
end_probabilities = tf.math.softmax(end_logits, axis=-1)[0].numpy()
```

{/if}

Ở giai đoạn này, chúng ta có thể lấy argmax xác suất bắt đầu và kết thúc - nhưng chúng ta có thể kết thúc với chỉ mục bắt đầu lớn hơn kết thúc, vì vậy chúng ta cần thực hiện thêm một số biện pháp phòng ngừa. Chúng ta sẽ tính toán xác suất của từng `start_index` và `end_index` có thể trong đó `start_index <= end_index`, sau đó lấy `(start_index, end_index)` với xác suất cao nhất.

Giả sử các sự kiện "Câu trả lời bắt đầu ở `start_index`" và "Câu trả lời kết thúc ở `end_index`" là độc lập, xác suất để câu trả lời bắt đầu tại `start_index` và kết thúc tại `end_index` là:

$$\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]$$ 

Vì vậy, để tính tất cả các điểm, chúng ta chỉ cần tính tích \\(\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]\\) với `start_index <= end_index`.

Đầu tiên, hãy tính toán tất cả các đầu ra có thể có:

```py
scores = start_probabilities[:, None] * end_probabilities[None, :]
```

{#if fw === 'pt'}

Sau đó, chúng tôi sẽ che các giá trị trong đó `start_index > end_index` bằng cách đặt chúng thành `0` (các xác suất khác đều là số dương). Hàm `torch.triu()` trả về phần tam giác phía trên của tensor 2D được truyền dưới dạng tham số, vì vậy nó sẽ thực hiện việc che đó cho chúng ta:

```py
scores = torch.triu(scores)
```

{:else}

Sau đó, chúng tôi sẽ che các giá trị trong đó `start_index > end_index` bằng cách đặt chúng thành `0` (các xác suất khác đều là số dương). Hàm `np.triu()` trả về phần tam giác phía trên của tensor 2D được truyền dưới dạng tham số, vì vậy nó sẽ thực hiện việc che đó cho chúng ta:

```py
import numpy as np

scores = np.triu(scores)
```

{/if}

Bây giờ chúng ta chỉ cần lấy chỉ mục tối đa. Vì PyTorch sẽ trả về chỉ mục trong tensor phẳng, chúng ta cần sử dụng phép chia làm tròn xuống `//` và lấy dư `%` để nhận được `start_index` và `end_index`:

```py
max_index = scores.argmax().item()
start_index = max_index // scores.shape[1]
end_index = max_index % scores.shape[1]
print(scores[start_index, end_index])
```

Chúng ta chưa xong đâu, nhưng ít nhất chúng ta đã có điểm chính xác cho câu trả lời (bạn có thể kiểm tra điều này bằng cách so sánh nó với kết quả đầu tiên trong phần trước):

```python out
0.97773
```

<Tip>

✏️ **Thử nghiệm thôi!** Tính chỉ mục bắt đầu và kết thúc cho năm cấu trả lời đầu tiện.

</Tip>

Ta có `start_index` và `end_index` của câu trả lời theo token nên ta chỉ cần chuyển đổi các chỉ mục kí tự trong ngữ cảnh. Đấy là nơi offset sẽ cực kì hữu ích. Ta có thể lấy và sử dụng chúng như cách ta làm trong tác vụ phân loại token:

```py
inputs_with_offsets = tokenizer(question, context, return_offsets_mapping=True)
offsets = inputs_with_offsets["offset_mapping"]

start_char, _ = offsets[start_index]
_, end_char = offsets[end_index]
answer = context[start_char:end_char]
```

Bây giờ chúng ta chỉ cần định dạng mọi thứ để có được kết quả:

```py
result = {
    "answer": answer,
    "start": start_char,
    "end": end_char,
    "score": scores[start_index, end_index],
}
print(result)
```

```python out
{'answer': 'Jax, PyTorch and TensorFlow',
 'start': 78,
 'end': 105,
 'score': 0.97773}
```

Tuyệt quá! Kết quả đó giống như trong ví dụ đầu tiên của chúng ta!

<Tip>

✏️ **Thử nghiệm thôi!** Sử dụng điểm tốt nhất mà bạn đã tính toán trước đó để hiển thị năm câu trả lời có khả năng nhất. Để kiểm tra kết quả của bạn, hãy quay lại đường dẫn đầu tiên và truyền vào `top_k=5` khi gọi nó.

</Tip>

## Xử lý các ngữ cảnh dài

Nếu chúng ta cố gắng tokenize các câu hỏi và ngữ cảnh dài ta từng lấy làm ví dụ trước đó, ta sẽ nhận được số token nhiều hơn độ dài tối da sử dụng trong pipeline `question-answering` (đó là 384):

```py
inputs = tokenizer(question, long_context)
print(len(inputs["input_ids"]))
```

```python out
461
```

Vì vậy, chúng ta sẽ cần phải cắt bớt đầu vào của mình ở độ dài tối đa đó. Có một số cách ta có thể làm điều này, nhưng chúng ta không muốn cắt ngắn câu hỏi, chỉ cắt bỏ ngữ cảnh. Vì ngữ cảnh là câu thứ hai, chúng ta sẽ sử dụng chiến lược cắt ngắn `"only_second"`. Vấn đề nảy sinh sau đó là câu trả lời cho câu hỏi có thể không nằm trong ngữ cảnh đã bị cắt ngắn. Ví dụ: ở đây, chúng ta đã chọn một câu hỏi trong đó câu trả lời nằm ở cuối ngữ cảnh và khi cắt ngắn câu trả lời đó thì câu trả lời không còn:

```py
inputs = tokenizer(question, long_context, max_length=384, truncation="only_second")
print(tokenizer.decode(inputs["input_ids"]))
```

```python out
"""
[CLS] Which deep learning libraries back [UNK] Transformers? [SEP] [UNK] Transformers : State of the Art NLP

[UNK] Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction,
question answering, summarization, translation, text generation and more in over 100 languages.
Its aim is to make cutting-edge NLP easier to use for everyone.

[UNK] Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and
then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and
can be modified to enable quick research experiments.

Why should I use transformers?

1. Easy-to-use state-of-the-art models:
  - High performance on NLU and NLG tasks.
  - Low barrier to entry for educators and practitioners.
  - Few user-facing abstractions with just three classes to learn.
  - A unified API for using all our pretrained models.
  - Lower compute costs, smaller carbon footprint:

2. Researchers can share trained models instead of always retraining.
  - Practitioners can reduce compute time and production costs.
  - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages.

3. Choose the right framework for every part of a model's lifetime:
  - Train state-of-the-art models in 3 lines of code.
  - Move a single model between TF2.0/PyTorch frameworks at will.
  - Seamlessly pick the right framework for training, evaluation and production.

4. Easily customize a model or an example to your needs:
  - We provide examples for each architecture to reproduce the results published by its original authors.
  - Model internal [SEP]
"""
```

Điều này có nghĩa là mô hình sẽ gặp khó khăn trong việc chọn ra câu trả lời chính xác. Để khắc phục điều này, pipeline hỏi đáp cho phép chúng ta chia ngữ cảnh thành các phần nhỏ hơn, chỉ định độ dài tối đa. Để đảm bảo rằng chúng ta không chia bối cảnh chính xác ở vị trí sai để có thể tìm ra câu trả lời, nó cũng bao gồm một số phần trùng lặp giữa các phần.

Chúng ta có thể yêu cầu tokenizer (nhanh hoặc chậm) thực hiện việc này bằng cách thêm `return_overflowing_tokens=True` và ta có thể chỉ định sự giao thoa mà ta muốn qua than số `stride`. Đây là một ví dụ, sử dụng một câu nhỏ hơn:

```py
sentence = "This sentence is not too long but we are going to split it anyway."
inputs = tokenizer(
    sentence, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
)

for ids in inputs["input_ids"]:
    print(tokenizer.decode(ids))
```

```python out
'[CLS] This sentence is not [SEP]'
'[CLS] is not too long [SEP]'
'[CLS] too long but we [SEP]'
'[CLS] but we are going [SEP]'
'[CLS] are going to split [SEP]'
'[CLS] to split it anyway [SEP]'
'[CLS] it anyway. [SEP]'
```

Có thể thấy, câu đã bị chia thành các đoạn sao cho mỗi phần trong `inputs["input_ids"]` có nhiều nhất 6 token (ta sẽ cần thêm đệm để đảm bảo chúng có cùng kích thước) và sẽ có sử giao thoa của 2 token giữa các phần.

Hãy cùng nhìn kĩ hơn vào kết quả tokenize:

```py
print(inputs.keys())
```

```python out
dict_keys(['input_ids', 'attention_mask', 'overflow_to_sample_mapping'])
```

Như dự đoán, ta nhận được ID đầu vào và attention mask.Ở đây, `overflow_to_sample_mapping` là một phép ánh xạ cho ta biết câu nào trong kết quả liên quan -- ta có 7 kết quả dều từ câu mà ta truyền vào tokenizer:

```py
print(inputs["overflow_to_sample_mapping"])
```

```python out
[0, 0, 0, 0, 0, 0, 0]
```

Điều này hữu ích hơn khi ta tokenize nhiều câu cùng nhau, Ví dụ:

```py
sentences = [
    "This sentence is not too long but we are going to split it anyway.",
    "This sentence is shorter but will still get split.",
]
inputs = tokenizer(
    sentences, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
)

print(inputs["overflow_to_sample_mapping"])
```

trả cho ta:

```python out
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]
```

nghĩa là câu đầu tiên được chia thành 7 đoạn như phần phía trước, và 4 đoạn tiếp theo đến từ câu thứ hai.

Bây giờ chúng ta hãy cùng quay trở lại ngữ cảnh dài. Theo mặc định, pipeline ``question-answering` sử dụng độ dài tối đa là 384, như đã đề cập trước đó và khoảng cách 128, tương ứng với cách mô hình được tinh chỉnh (bạn có thể điều chỉnh các tham số đó bằng cách truyền `max_seq_len` và `stride` khi gọi pipeline). Do đó, chúng ta sẽ sử dụng các tham số đó khi tokenize. Chúng ta cũng sẽ thêm phần đệm (để có các mẫu có cùng chiều dài, vì vậy chúng ta có thể tạo ra các tensor) cũng như yêu cầu các offset:

```py
inputs = tokenizer(
    question,
    long_context,
    stride=128,
    max_length=384,
    padding="longest",
    truncation="only_second",
    return_overflowing_tokens=True,
    return_offsets_mapping=True,
)
```

Các `inputs` sẽ chứa các ID đầu vào và các attention mask mà mô hình kì vọng, cũng như offset và `overflow_to_sample_mapping` ta vừa trao đổi ở trên. Vì hai tham số đó không phải là tham số được sử dụng bởi mô hình, chúng ta sẽ đưa chúng ra khỏi `inputs` (và không lưu trữ ánh xạ, vì nó không hữu ích ở đây) trước khi chuyển đổi nó thành tensor:

{#if fw === 'pt'}

```py
_ = inputs.pop("overflow_to_sample_mapping")
offsets = inputs.pop("offset_mapping")

inputs = inputs.convert_to_tensors("pt")
print(inputs["input_ids"].shape)
```

```python out
torch.Size([2, 384])
```

{:else}

```py
_ = inputs.pop("overflow_to_sample_mapping")
offsets = inputs.pop("offset_mapping")

inputs = inputs.convert_to_tensors("tf")
print(inputs["input_ids"].shape)
```

```python out
(2, 384)
```

{/if}

Bối cảnh dài của chúng ta được chia làm hai, đồng nghĩa sau khi nó đi qua mô hình, chúng ta sẽ có hai bộ logit bắt đầu và kết thúc:

```py
outputs = model(**inputs)

start_logits = outputs.start_logits
end_logits = outputs.end_logits
print(start_logits.shape, end_logits.shape)
```

{#if fw === 'pt'}

```python out
torch.Size([2, 384]) torch.Size([2, 384])
```

{:else}

```python out
(2, 384) (2, 384)
```

{/if}

Giống như trước đây, đầu tiên chúng ta che các token không phải là một phần của ngữ cảnh trước khi sử dụng softmax. Chúng ta cũng che tất cả các token đệm (được gắn mác bởi attention mask):

{#if fw === 'pt'}

```py
sequence_ids = inputs.sequence_ids()
# Che tất cả mọi thứ trừ token của ngữ cảnh
mask = [i != 1 for i in sequence_ids]
# Hiển thị token [CLS]
mask[0] = False
# Che tất cả token [PAD]
mask = torch.logical_or(torch.tensor(mask)[None], (inputs["attention_mask"] == 0))

start_logits[mask] = -10000
end_logits[mask] = -10000
```

{:else}

```py
sequence_ids = inputs.sequence_ids()
# Che tất cả mọi thứ trừ token của ngữ cảnh
mask = [i != 1 for i in sequence_ids]
# Hiển thị token [CLS]
mask[0] = False
# Che tất cả token [PAD]
mask = tf.math.logical_or(tf.constant(mask)[None], inputs["attention_mask"] == 0)

start_logits = tf.where(mask, -10000, start_logits)
end_logits = tf.where(mask, -10000, end_logits)
```

{/if}

Sau đó, chúng ta có thể sử dụng softmax để chuyển đổi các logit của chúng ta thành xác suất:

{#if fw === 'pt'}

```py
start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)
end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)
```

{:else}

```py
start_probabilities = tf.math.softmax(start_logits, axis=-1).numpy()
end_probabilities = tf.math.softmax(end_logits, axis=-1).numpy()
```

{/if}

Bước tiếp theo tương tự như những gì chúng ta đã làm cho bối cảnh nhỏ, nhưng chúng ta lặp lại nó cho mỗi phần trong hai phần của mình. Chúng ta tính điểm cho tất cả các khoảng câu trả lời có thể có, sau đó lấy phần có điểm tốt nhất:

{#if fw === 'pt'}

```py
candidates = []
for start_probs, end_probs in zip(start_probabilities, end_probabilities):
    scores = start_probs[:, None] * end_probs[None, :]
    idx = torch.triu(scores).argmax().item()

    start_idx = idx // scores.shape[1]
    end_idx = idx % scores.shape[1]
    score = scores[start_idx, end_idx].item()
    candidates.append((start_idx, end_idx, score))

print(candidates)
```

{:else}

```py
candidates = []
for start_probs, end_probs in zip(start_probabilities, end_probabilities):
    scores = start_probs[:, None] * end_probs[None, :]
    idx = np.triu(scores).argmax().item()

    start_idx = idx // scores.shape[1]
    end_idx = idx % scores.shape[1]
    score = scores[start_idx, end_idx].item()
    candidates.append((start_idx, end_idx, score))

print(candidates)
```

{/if}

```python out
[(0, 18, 0.33867), (173, 184, 0.97149)]
```

Hai ứng cử viên đó tương ứng với các câu trả lời tốt nhất mà mô hình có thể tìm thấy trong mỗi đoạn. Mô hình chắc chắn hơn rằng câu trả lời đúng nằm ở phần thứ hai (đó là một dấu hiệu tốt!). Bây giờ chúng ta chỉ cần ánh xạ khoảng hai token đó với khoảng các ký tự trong ngữ cảnh (chúng ta chỉ cần lập ánh xạ cái thứ hai để có câu trả lời, nhưng thật thú vị khi xem mô hình đã chọn những gì trong đoạn đầu tiên).

<Tip>

✏️ **Thử nghiệm thôi!** Hãy điều chỉnh đoạn mã trên để trả về điểm và khoảng cho năm câu trả lời có nhiều khả năng nhất (tổng cộng, không phải cho mỗi đoạn).

</Tip>

`offsets` mà chúng ta đã nắm được trước đó thực sự là một danh sách các offset, với một danh sách trên mỗi đoạn văn bản:

```py
for candidate, offset in zip(candidates, offsets):
    start_token, end_token, score = candidate
    start_char, _ = offset[start_token]
    _, end_char = offset[end_token]
    answer = long_context[start_char:end_char]
    result = {"answer": answer, "start": start_char, "end": end_char, "score": score}
    print(result)
```

```python out
{'answer': '\n🤗 Transformers: State of the Art NLP', 'start': 0, 'end': 37, 'score': 0.33867}
{'answer': 'Jax, PyTorch and TensorFlow', 'start': 1892, 'end': 1919, 'score': 0.97149}
```

Nếu chúng ta bỏ qua kết quả đầu tiên, chúng ta sẽ nhận được kết quả tương tự như pipeline cho ngữ cảnh dài này - yayy!

<Tip>

✏️ **Thử nghiệm thôi!** Sử dụng điểm tốt nhất bạn đã tính toán trước đó để hiển thị năm câu trả lời có khả năng xảy ra nhất (cho toàn bộ ngữ cảnh, không phải từng đoạn). Để kiểm tra kết quả của bạn, hãy quay lại pipeline đầu tiên và truyền vào `top_k=5` khi gọi nó.

</Tip>

Điều này kết thúc phần đi sâu vào các khả năng của tokenizer. Chúng ta sẽ đưa tất cả những điều này vào thực tế một lần nữa trong chương tiếp theo, khi chúng tôi hướng dẫn bạn cách tinh chỉnh một mô hình về một loạt các tác vụ NLP phổ biến.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter6/3b.mdx" />

### Huấn luyện một tokenizer mới từ cái cũ
https://huggingface.co/learn/course/vi/chapter6/2.md

# Huấn luyện một tokenizer mới từ cái cũ

<CourseFloatingBanner chapter={6}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter6/section2.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter6/section2.ipynb"},
]} />

Nếu mô hình ngôn ngữ không có sẵn ngôn ngữ bạn quan tâm hoặc nếu kho tài liệu của bạn rất khác với kho mà mô hình ngôn ngữ của bạn đã huấn luyện, bạn rất có thể sẽ muốn huấn luyện lại mô hình từ đầu bằng cách sử dụng trình tokenize phù hợp với dữ liệu của bạn. Điều đó sẽ yêu cầu huấn luyện một trình tokenize mới trên tập dữ liệu của bạn. Nhưng chính xác thì điều đó có nghĩa là gì? Khi chúng ta lần đầu xem xét các tokenizer trong [Chương 2](/course/chapter2), chúng ta thấy rằng hầu hết các mô hình Transformer sử dụng thuật toán tokenize _từ phụ_. Để xác định những từ phụ nào được quan tâm và xuất hiện thường xuyên nhất trong kho ngữ liệu hiện có, trình tokenize cần phải xem xét kỹ tất cả các văn bản trong kho ngữ liệu - một quá trình mà chúng ta gọi là *huấn luyện*. Các quy tắc chi phối việc huấn luyện này phụ thuộc vào loại tokenizer được sử dụng và chúng ta sẽ xem xét ba thuật toán chính ở phần sau của chương này.

<Youtube id="DJimQynXZsQ"/>

<Tip warning={true}>

⚠️ Huấn luyện một tokenizer không giống như huấn luyện một mô hình! Huấn luyện mô hình sử dụng giảm độ dốc ngẫu nhiên để làm cho tổn thất nhỏ hơn một chút cho mỗi đợt. Nó được ngẫu nhiên hóa bởi tự nhiên (có nghĩa là bạn phải đặt một giá trị seed để có được kết quả tương tự khi thực hiện cùng thực hiện huấn luyện hai lần). Huấn luyện một trình tokenize là một quy trình thống kê cố gắng xác định những từ phụ nào tốt nhất để chọn cho một kho dữ liệu nhất định, và các quy tắc được sử dụng để chọn chúng dựa trên thuật toán tokenize. Nó mang tính cố định, nghĩa là bạn luôn nhận được cùng một kết quả khi huấn luyện với cùng một thuật toán trên cùng một kho tài liệu.

</Tip>

## Tập hợp một kho ngữ liệu

Có một API rất đơn giản trong 🤗 Transformers mà bạn có thể sử dụng để huấn luyện một tokenizer mới có cùng đặc điểm với cái hiện có: `AutoTokenizer.train_new_from_iterator()`. Để thấy điều này trong thực tế, giả sử chúng ta muốn huấn luyện GPT-2 từ đầu, nhưng bằng một ngôn ngữ khác ngoài tiếng Anh. Nhiệm vụ đầu tiên của chúng ta sẽ là thu thập nhiều dữ liệu bằng ngôn ngữ đó trong một kho dữ liệu huấn luyện. Để cung cấp các mẫu mà mọi người có hiểu được, chúng ta sẽ không sử dụng ngôn ngữ như tiếng Nga hoặc tiếng Trung ở đây, mà là ngôn ngữ tiếng Anh chuyên dụng: đoạn mã Python.

Thư viện [🤗 Datasets](https://github.com/huggingface/datasets) có thể giúp chúng ta tập hợp một kho dữ liệu mã nguồn Python. Chúng ta sẽ sử dụng hàm `load_dataset()` thông thường để tải xuống và lưu vào bộ nhớ cache của tập dữ liệu [CodeSearchNet](https://huggingface.co/datasets/code_search_net). Tập dữ liệu này được tạo cho [thử thách CodeSearchNet](https://wandb.ai/github/CodeSearchNet/benchmark) và chứa hàng triệu hàm từ các thư viện mã nguồn mở trên GitHub bằng một số ngôn ngữ lập trình. Ở đây, chúng ta sẽ tải phần Python của tập dữ liệu này:

```py
from datasets import load_dataset

# Quá trình này có thể mất một vài phút để tải, vì vậy hãy lấy cà phê hoặc trà trong khi chờ đợi!
raw_datasets = load_dataset("code_search_net", "python")
```

Chúng ta có thể xem xét phần tách huấn luyện để xem ta có quyền truy cập vào những cột nào:

```py
raw_datasets["train"]
```

```python out
Dataset({
    features: ['repository_name', 'func_path_in_repository', 'func_name', 'whole_func_string', 'language', 
      'func_code_string', 'func_code_tokens', 'func_documentation_string', 'func_documentation_tokens', 'split_name', 
      'func_code_url'
    ],
    num_rows: 412178
})
```

Chúng ta có thể thấy tập dữ liệu tách chuỗi tài liệu mô tả khỏi đoạn mã và đề xuất tokenize cả hai. Ở đây, chúng ta sẽ chỉ sử dụng cột `whole_func_string` để huấn luyện trình tokenize. Chúng ta có thể xem xét mẫu một trong những hàm này bằng cách lập chỉ mục vào phần `train`:

```py
print(raw_datasets["train"][123456]["whole_func_string"])
```

nó nên trả về kết quả như dưới đây:

```out
def handle_simple_responses(
      self, timeout_ms=None, info_cb=DEFAULT_MESSAGE_CALLBACK):
    """Accepts normal responses from the device.

    Args:
      timeout_ms: Timeout in milliseconds to wait for each response.
      info_cb: Optional callback for text sent from the bootloader.

    Returns:
      OKAY packet's message.
    """
    return self._accept_responses('OKAY', info_cb, timeout_ms=timeout_ms)
```

Điều đầu tiên chúng ta cần làm là chuyển đổi tập dữ liệu thành một _iterator_ danh sách các văn bản - ví dụ, một danh sách các văn bản. Việc sử dụng danh sách văn bản sẽ cho phép tokenizer hoạt động nhanh hơn (huấn luyện hàng loạt văn bản thay vì xử lý từng văn bản riêng lẻ) và nó phải là một trình lặp nếu chúng ta muốn tránh có mọi thứ trong bộ nhớ cùng một lúc. Nếu kho dữ liệu của bạn lớn, bạn sẽ muốn tận dụng lợi thế thực tiễn là 🤗 Datasets không tải mọi thứ vào RAM mà lưu trữ các phần tử của tập dữ liệu trên đĩa.

Làm như sau sẽ tạo một danh sách các danh sách với mỗi danh sách gồm 1,000 văn bản, nhưng sẽ tải mọi thứ vào bộ nhớ:

```py
# Đừng bỏ ghi chú dòng bên dưới trừ khi tập dữ liệu của bạn nhỏ!
# training_corpus = [raw_datasets["train"][i: i + 1000]["whole_func_string"] for i in range(0, len(raw_datasets["train"]), 1000)]
```

Sử dụng trình tạo Python, chúng ta có thể tránh việc Python tải bất kỳ thứ gì vào bộ nhớ cho đến khi nó thực sự cần thiết. Để tạo một trình tạo như vậy, bạn chỉ cần thay dấu ngoặc vuông bằng dấu ngoặc đơn:

```py
training_corpus = (
    raw_datasets["train"][i : i + 1000]["whole_func_string"]
    for i in range(0, len(raw_datasets["train"]), 1000)
)
```

Dòng mã này không tìm nạp bất kỳ phần tử nào của tập dữ liệu; nó chỉ tạo một đối tượng mà bạn có thể sử dụng trong vòng lặp Python `for`. Các văn bản sẽ chỉ được tải khi bạn cần (nghĩa là khi bạn đang ở bước của vòng lặp `for` mà yêu cầu chúng) và chỉ 1,000 văn bản sẽ được tải mỗi lần. Bằng cách này, bạn sẽ không sử dụng hết bộ nhớ của mình ngay cả khi bạn đang xử lý một tập dữ liệu lớn.

Vấn đề với một đối tượng tạo là nó chỉ có thể được sử dụng một lần. Vì vậy, thay vì điều này cho ta danh sách 10 chữ số đầu tiên hai lần:

```py
gen = (i for i in range(10))
print(list(gen))
print(list(gen))
```

chúng ta có thể lấy chúng trong một lần và sau đó danh sáng sẽ trống:

```python out
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[]
```

Đó là lí do chúng ta định nghĩa một hàm thay vào đó trả về một trình tạo:

```py
def get_training_corpus():
    return (
        raw_datasets["train"][i : i + 1000]["whole_func_string"]
        for i in range(0, len(raw_datasets["train"]), 1000)
    )


training_corpus = get_training_corpus()
```

Ta có thể định nghĩa trình tạo bên trong vòng lặp `for` sử dụng `yield`:

```py
def get_training_corpus():
    dataset = raw_datasets["train"]
    for start_idx in range(0, len(dataset), 1000):
        samples = dataset[start_idx : start_idx + 1000]
        yield samples["whole_func_string"]
```

sẽ tạo ra trình tạo hoàn toàn giống như trước đây, nhưng cho phép bạn sử dụng logic phức tạp hơn bạn có thể trong một bao hàm.

## Huấn luyện một tokenizer mới

Bây giờ chúng ta đã có kho văn bản của mình dưới dạng một trình lặp các loạt văn bản, chúng ta đã sẵn sàng để huấn luyện một trình tokenize mới. Để thực hiện việc này, trước tiên chúng ta cần tải tokenizer mà chúng ta muốn ghép nối với mô hình của mình (ở đây, GPT-2):

```py
from transformers import AutoTokenizer

old_tokenizer = AutoTokenizer.from_pretrained("gpt2")
```

Mặc dù chúng ta sẽ huấn luyện một tokenizer, nhưng bạn nên làm điều này để tránh bắt đầu hoàn toàn từ đầu. Bằng cách này, chúng ta sẽ không phải chỉ định bất kỳ điều gì về thuật toán tokenize hoặc các token đặc biệt mà ta muốn sử dụng; tokenizer mới sẽ giống hệt như GPT-2 và điều duy nhất sẽ thay đổi là từ vựng, sẽ được xác định bởi quá trình huấn luyện trên kho ngữ liệu của chúng tôi.

Đầu tiên, chúng ta hãy xem cách mà tokenizer này sẽ xử lý một hàm mẫu thế nào:

```py
example = '''def add_numbers(a, b):
    """Add the two numbers `a` and `b`."""
    return a + b'''

tokens = old_tokenizer.tokenize(example)
tokens
```

```python out
['def', 'Ġadd', '_', 'n', 'umbers', '(', 'a', ',', 'Ġb', '):', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo',
 'Ġnumbers', 'Ġ`', 'a', '`', 'Ġand', 'Ġ`', 'b', '`', '."', '""', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
```

Tokenizer này có một số ký hiệu đặc biệt, như `Ġ` và `Ċ`, tương ứng biểu thị dấu cách và dòng mới. Như chúng ta có thể thấy, điều này không quá hiệu quả: tokenizer trả về các mã thông báo riêng lẻ cho từng khoảng trắng, khi nó có thể nhóm các mức thụt lề lại với nhau (vì có bộ bốn hoặc tám dấu cách sẽ rất phổ biến trong mã). Nó cũng tách tên hàm hơi kỳ lạ, nhìn không quen các từ có ký tự `_`.

Hãy huấn luyện một tokenizer mới và xem liệu nó có giải quyết được những vấn đề đó không. Đối với điều này, chúng ta sẽ sử dụng phương thức `train_new_from_iterator()`:

```py
tokenizer = old_tokenizer.train_new_from_iterator(training_corpus, 52000)
```

Lệnh này có thể mất một chút thời gian nếu kho dữ liệu của bạn rất lớn, nhưng đối với tập dữ liệu 1.6GB văn bản này, nó rất nhanh (1 phút 16 giây trên CPU AMD Ryzen 9 3900X với 12 lõi).

Lưu ý rằng `AutoTokenizer.train_new_from_iterator()` chỉ hoạt động nếu tokenizer bạn đang sử dụng là tokenizer "nhanh". Như bạn sẽ thấy trong phần tiếp theo, thư viện 🤗 Transformers chứa hai loại tokenizers: một số được viết hoàn toàn bằng Python và những loại khác (loại nhanh) được hỗ trợ bởi thư viện 🤗 Tokenizers, được viết bằng ngôn ngữ lập trình [Rust](https://www.rust-lang.org). Python là ngôn ngữ thường được sử dụng nhất cho các ứng dụng khoa học dữ liệu và học sâu, nhưng khi bất kỳ thứ gì cần được song song hóa cho nhanh, nó phải được viết bằng một ngôn ngữ khác. Ví dụ, các phép nhân ma trận là cốt lõi của tính toán mô hình được viết bằng CUDA, một thư viện C được tối ưu hóa cho GPU.

Việc huấn luyện một tokenizer hoàn toàn mới bằng Python thuần túy sẽ rất chậm, đó là lý do tại sao chúng tôi đã phát triển thư viện 🤗 Tokenizer. Lưu ý rằng cũng giống như bạn không phải học ngôn ngữ CUDA để có thể thực thi mô hình của mình trên một loạt đầu vào trên GPU, bạn sẽ không cần phải học Rust để sử dụng trình tokenizer nhanh. Thư viện 🤗 Tokenizers cung cấp các liên kết Python cho nhiều phương thức gọi nội bộ một số đoạn mã trong Rust; ví dụ: để song song huấn luyện trình tokenize mới của bạn hoặc, như chúng ta đã thấy trong [Chương 3](/course/chapter3), tokenize một loạt đầu vào.

Hầu hết các mô hình Transformer đều có sẵn công cụ tokenize nhanh (có một số ngoại lệ mà bạn có thể kiểm tra [tại đây](https://huggingface.co/transformers/#supported-frameworks)) và API `AutoTokenizer` luôn chọn tốc tokenizer nhanh cho bạn nếu nó có sẵn. Trong phần tiếp theo, chúng ta sẽ xem xét một số tính năng đặc biệt khác mà các tokenize nhanh có mà thực sự hữu ích cho các tác vụ như phân loại token và hỏi đáp. Tuy nhiên, trước khi đi sâu vào vấn đề đó, chúng ta hãy thử tokenizer hoàn toàn mới của chúng ta trên mẫu trước:

```py
tokens = tokenizer.tokenize(example)
tokens
```

```python out
['def', 'Ġadd', '_', 'numbers', '(', 'a', ',', 'Ġb', '):', 'ĊĠĠĠ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo', 'Ġnumbers', 'Ġ`',
 'a', '`', 'Ġand', 'Ġ`', 'b', '`."""', 'ĊĠĠĠ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
```

Ở đây chúng ta lại thấy các ký hiệu đặc biệt `Ġ` và `Ċ` biểu thị dấu cách và dòng mới, nhưng chúng ta cũng có thể thấy rằng trình tokenize đã học được một số token rất cụ thể cho một kho các hàm Python: ví dụ: có một token `ĊĠĠĠ` đại diện cho một thụt lề và token `Ġ"""` đại diện cho ba dấu ngoặc kép bắt đầu một chuỗi tài liệu. Tokenizer cũng phân chia chính xác tên hàm trên `_`. Đây là một biễu diễn khá nhỏ gọn; tương đối, sử dụng tokenizer đơn giản bằng tiếng Anh trên cùng một mẫu sẽ cho ta một câu dài hơn:

```py
print(len(tokens))
print(len(old_tokenizer.tokenize(example)))
```

```python out
27
36
```

Hãy cùng nhìn vào ví dụ sau:

```python
example = """class LinearLayer():
    def __init__(self, input_size, output_size):
        self.weight = torch.randn(input_size, output_size)
        self.bias = torch.zeros(output_size)

    def __call__(self, x):
        return x @ self.weights + self.bias
    """
tokenizer.tokenize(example)
```

```python out
['class', 'ĠLinear', 'Layer', '():', 'ĊĠĠĠ', 'Ġdef', 'Ġ__', 'init', '__(', 'self', ',', 'Ġinput', '_', 'size', ',',
 'Ġoutput', '_', 'size', '):', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'weight', 'Ġ=', 'Ġtorch', '.', 'randn', '(', 'input', '_',
 'size', ',', 'Ġoutput', '_', 'size', ')', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'bias', 'Ġ=', 'Ġtorch', '.', 'zeros', '(',
 'output', '_', 'size', ')', 'ĊĊĠĠĠ', 'Ġdef', 'Ġ__', 'call', '__(', 'self', ',', 'Ġx', '):', 'ĊĠĠĠĠĠĠĠ',
 'Ġreturn', 'Ġx', 'Ġ@', 'Ġself', '.', 'weights', 'Ġ+', 'Ġself', '.', 'bias', 'ĊĠĠĠĠ']
```

Ngoài token tương ứng với thụt lề, ở đây chúng ta cũng có thể thấy token cho thụt lề kép:`ĊĠĠĠĠĠĠĠ`. Các từ đặc biệt trong Python như `class`, `init`, `call`, `self`, và `return`, mỗi từ được tokenize thành một token và chúng ta có thể thấy cũng như tách `_`  và `.`,  tokenizer phân chia chính xác các tên: `LinearLayer` được tokenize là `["ĠLinear", "Layer"]`.

## Lưu tokenizer

Để đảm bảo rằng chúng ta có thể sử dụng nó sau này, chúng ta cần phải lưu tokenizer mới của mình. Giống như đối với các mô hình, điều này được thực hiện với phương thức `save_pretrained()`:

```py
tokenizer.save_pretrained("code-search-net-tokenizer")
```

Thao tác này sẽ tạo một thư mục mới có tên *code-search-net-tokenizer*, sẽ chứa tất cả các tệp mà tokenizer cần được tải lại. Nếu bạn muốn chia sẻ tokenizer này với đồng nghiệp và bạn bè của mình, bạn có thể tải nó lên Hub bằng cách đăng nhập vào tài khoản của mình. Nếu bạn đang làm việc trên notebook, có một hàm tiện ích giúp bạn làm điều này:

```python
from huggingface_hub import notebook_login

notebook_login()
```

Thao tác này sẽ hiển thị một tiện ích mà bạn có thể nhập thông tin đăng nhập Hugging Face của mình. Nếu bạn không làm việc trong notebook, chỉ cần nhập dòng sau vào thiết bị đầu cuối của bạn:

```bash
huggingface-cli login
```

Khi bạn đã đăng nhập, bạn có thể đẩy tokenizer của mình bằng cách thực hiện lệnh sau:

```py
tokenizer.push_to_hub("code-search-net-tokenizer")
```

Thao tác này sẽ tạo một kho lưu trữ mới trong không gian tên của bạn với tên `code-search-net-tokenizer`, chứa tệp tokenizer. Sau đó bạn có thể tải tokenizer từ bất kì đâu với phương thức `from_pretrained()`:

```py
# Thay "huggingface-course" dưới đấy với tên không gian thực sự sử dụng tokenizer riêng của bạn
tokenizer = AutoTokenizer.from_pretrained("huggingface-course/code-search-net-tokenizer")
```

Giờ bạn đã sẵn sàng để huấn luyện một mô hình ngôn ngữ từ đầu và việc tinh chỉnh nó trong tầm tay của bạn! Chúng ta sẽ tìm hiểu điều đó trong [Chương 7](/course/chap7), nhưng trước tiên, trong phần còn lại của chương này, chúng ta sẽ xem xét kỹ hơn về các trình tokenize nhanh và khám phá chi tiết những gì thực sự xảy ra khi chúng ta gọi phương thức `train_new_from_iterator()`.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter6/2.mdx" />

### Tạo tập dữ liệu của riêng bạn
https://huggingface.co/learn/course/vi/chapter5/5.md

# Tạo tập dữ liệu của riêng bạn

<CourseFloatingBanner chapter={5}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter5/section5.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter5/section5.ipynb"},
]} />

Đôi khi tập dữ liệu mà bạn cần để xây dựng một ứng dụng NLP không tồn tại, vì vậy bạn sẽ cần phải tự tạo. Trong phần này, chúng tôi sẽ hướng dẫn bạn cách tạo một kho tài liệu gồm [Các vấn đề về GitHub](https://github.com/features/issues/), thường được sử dụng để theo dõi các lỗi hoặc tính năng trong kho lưu trữ GitHub. Kho tài liệu này có thể được sử dụng cho các mục đích khác nhau, bao gồm:

* Khám phá mất bao lâu để đóng các vấn đề đang mở hoặc yêu cầu kéo về (pull requests)
* Đào tạo _multilabel classifier_ hay _trình phân loại đa nhãn_ có thể gắn thẻ các vấn đề với siêu dữ liệu dựa trên mô tả của vấn đề (ví dụ: "lỗi", "cải tiến" hoặc "câu hỏi")
* Tạo công cụ tìm kiếm ngữ nghĩa để tìm những vấn đề nào phù hợp với truy vấn của người dùng

Ở đây chúng ta sẽ tập trung vào việc tạo kho ngữ liệu và trong phần tiếp theo chúng ta sẽ giải quyết ứng dụng tìm kiếm ngữ nghĩa. Để giữ mọi thứ đúng meta, chúng ta sẽ sử dụng các vấn đề GitHub liên quan đến một dự án nguồn mở phổ biến: 🤗 Datasets! Chúng ta hãy xem cách lấy dữ liệu và khám phá thông tin có trong những vấn đề này.

## Lấy dữ liệu

Bạn có thể tìm thấy tất cả các vấn đề trong 🤗 Datasets bằng cách điều hướng đến [tab Issues](https://github.com/huggingface/datasets/issues). Như thể hiện trong ảnh chụp màn hình bên dưới, tại thời điểm viết bài, có 331 vấn đề đang mở và 668 vấn đề đã đóng.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/datasets-issues.png" alt="The GitHub issues associated with 🤗 Datasets." width="80%"/>
</div>

Nếu bạn nhấp vào một trong những vấn đề này, bạn sẽ thấy nó chứa tiêu đề, mô tả và một bộ nhãn mô tả đặc trưng cho vấn đề. Một ví dụ được hiển thị trong ảnh chụp màn hình bên dưới.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/datasets-issues-single.png" alt="A typical GitHub issue in the 🤗 Datasets repository." width="80%"/>
</div>

Để tải xuống tất cả các vấn đề của kho lưu trữ, chúng tôi sẽ sử dụng [GitHub REST API](https://docs.github.com/en/rest) để thăm dò điểm cuối [`Issues`](https://docs.github.com/en/rest/reference/issues#list-repository-issues). Điểm cuối này trả về danh sách các đối tượng JSON, với mỗi đối tượng chứa một số lượng lớn các trường bao gồm tiêu đề và mô tả cũng như siêu dữ liệu về trạng thái của vấn đề, v.v.

Một cách thuận tiện để tải các vấn đề xuống là thông qua thư viện `requests`, đây là cách tiêu chuẩn để thực hiện các yêu cầu HTTP trong Python. Bạn có thể cài đặt thư viện bằng cách chạy:


```python
!pip install requests
```

Sau khi thư viện được cài đặt, bạn có thể thực hiện các yêu cầu GET tới điểm cuối `Issues` bằng cách gọi hàm `requests.get()`. Ví dụ: bạn có thể chạy lệnh sau để truy xuất vấn đề đầu tiên trên trang đầu tiên:

```py
import requests

url = "https://api.github.com/repos/huggingface/datasets/issues?page=1&per_page=1"
response = requests.get(url)
```

Đối tượng `response` chứa nhiều thông tin hữu ích về yêu cầu, bao gồm mã trạng thái HTTP:

```py
response.status_code
```

```python out
200
```

trong đó trạng thái `200` có nghĩa là yêu cầu đã thành công (bạn có thể tìm thấy danh sách các mã trạng thái HTTP có thể có [tại đây](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)). Tuy nhiên, điều chúng ta thực sự quan tâm là _payload_ hay _tải trọng_, có thể được truy cập ở nhiều định dạng khác nhau như byte, chuỗi hoặc JSON. Vì chúng ta biết các vấn đề của ta ở định dạng JSON, hãy kiểm tra tải trọng như sau:

```py
response.json()
```

```python out
[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
  'repository_url': 'https://api.github.com/repos/huggingface/datasets',
  'labels_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/labels{/name}',
  'comments_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/comments',
  'events_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/events',
  'html_url': 'https://github.com/huggingface/datasets/pull/2792',
  'id': 968650274,
  'node_id': 'MDExOlB1bGxSZXF1ZXN0NzEwNzUyMjc0',
  'number': 2792,
  'title': 'Update GooAQ',
  'user': {'login': 'bhavitvyamalik',
   'id': 19718818,
   'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
   'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
   'gravatar_id': '',
   'url': 'https://api.github.com/users/bhavitvyamalik',
   'html_url': 'https://github.com/bhavitvyamalik',
   'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
   'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
   'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
   'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
   'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
   'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
   'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
   'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
   'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
   'type': 'User',
   'site_admin': False},
  'labels': [],
  'state': 'open',
  'locked': False,
  'assignee': None,
  'assignees': [],
  'milestone': None,
  'comments': 1,
  'created_at': '2021-08-12T11:40:18Z',
  'updated_at': '2021-08-12T12:31:17Z',
  'closed_at': None,
  'author_association': 'CONTRIBUTOR',
  'active_lock_reason': None,
  'pull_request': {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/2792',
   'html_url': 'https://github.com/huggingface/datasets/pull/2792',
   'diff_url': 'https://github.com/huggingface/datasets/pull/2792.diff',
   'patch_url': 'https://github.com/huggingface/datasets/pull/2792.patch'},
  'body': '[GooAQ](https://github.com/allenai/gooaq) dataset was recently updated after splits were added for the same. This PR contains new updated GooAQ with train/val/test splits and updated README as well.',
  'performed_via_github_app': None}]
```

Ồ, đó là rất nhiều thông tin! Chúng ta có thể thấy các trường hữu ích như `title`, `body`,  và `number` mô tả sự cố cũng như thông tin về người dùng GitHub đã mở sự cố.

<Tip>

✏️ **Thử nghiệm thôi!** Nhấp vào một vài URL trong tải trọng JSON ở trên để biết loại thông tin mà mỗi vấn đề GitHub được liên kết với.

</Tip>

Như đã mô tả trong [tài liệu](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting) GitHub, các yêu cầu chưa được xác thực được giới hạn ở 60 yêu cầu mỗi giờ. Mặc dù bạn có thể tăng tham số truy vấn `per_page` để giảm số lượng yêu cầu bạn thực hiện, nhưng bạn vẫn sẽ đạt đến giới hạn tỷ lệ trên bất kỳ kho lưu trữ nào có nhiều hơn một vài nghìn vấn đề. Vì vậy, thay vào đó, bạn nên làm theo [hướng dẫn](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) của GitHub về cách tạo _personal access token_ hay _token truy cập cá nhân_ để bạn có thể tăng giới hạn tốc độ lên 5,000 yêu cầu mỗi giờ. Khi bạn có token của riêng mình, bạn có thể bao gồm nó như một phần của tiêu đề yêu cầu:

```py
GITHUB_TOKEN = xxx  # Sao chép token GitHub của bạn tại đây
headers = {"Authorization": f"token {GITHUB_TOKEN}"}
```

<Tip warning={true}>

⚠️ Không dùng chung notebook có dán `GITHUB_TOKEN` của bạn trong đó. Chúng tôi khuyên bạn nên xóa ô cuối cùng sau khi bạn đã thực thi nó để tránh vô tình làm rò rỉ thông tin này. Tốt hơn nữa, hãy lưu trữ token trong tệp *.env* và sử dụng [thư viện `python-dotenv`](https://github.com/theskumar/python-dotenv) để tải tự động cho bạn dưới dạng biến môi trường.

</Tip>

Bây giờ chúng ta đã có token truy cập của mình, hãy tạo một hàm có thể tải xuống tất cả các vấn đề từ kho lưu trữ GitHub:

```py
import time
import math
from pathlib import Path
import pandas as pd
from tqdm.notebook import tqdm


def fetch_issues(
    owner="huggingface",
    repo="datasets",
    num_issues=10_000,
    rate_limit=5_000,
    issues_path=Path("."),
):
    if not issues_path.is_dir():
        issues_path.mkdir(exist_ok=True)

    batch = []
    all_issues = []
    per_page = 100  # Số vấn đề phải trả về trên mỗi trang
    num_pages = math.ceil(num_issues / per_page)
    base_url = "https://api.github.com/repos"

    for page in tqdm(range(num_pages)):
        # Truy vấn với trạng thái state=all để nhận được cả vấn đề mở và đóng
        query = f"issues?page={page}&per_page={per_page}&state=all"
        issues = requests.get(f"{base_url}/{owner}/{repo}/{query}", headers=headers)
        batch.extend(issues.json())

        if len(batch) > rate_limit and len(all_issues) < num_issues:
            all_issues.extend(batch)
            batch = []  # Xả lô cho khoảng thời gian tiếp theo
            print(f"Reached GitHub rate limit. Sleeping for one hour ...")
            time.sleep(60 * 60 + 1)

    all_issues.extend(batch)
    df = pd.DataFrame.from_records(all_issues)
    df.to_json(f"{issues_path}/{repo}-issues.jsonl", orient="records", lines=True)
    print(
        f"Downloaded all the issues for {repo}! Dataset stored at {issues_path}/{repo}-issues.jsonl"
    )
```

Bây giờ khi ta gọi `fetch_issues ()`, nó sẽ tải xuống tất cả các vấn đề theo lô để tránh vượt quá giới hạn của GitHub về số lượng yêu cầu mỗi giờ; kết quả sẽ được lưu trữ trong tệp _repository_name-issues.jsonl_, trong đó mỗi dòng là một đối tượng JSON đại diện cho một vấn đề. Hãy sử dụng chức năng này để lấy tất cả các vấn đề từ 🤗 Datasets:

```py
# Tùy thuộc vào kết nối internet của bạn, quá trình này có thể mất vài phút để chạy ...
fetch_issues()
```

Sau khi các vấn đề được tải xuống, chúng tôi có thể lôi chúng cục bộ bằng cách sử dụng các kỹ năng mới khai phá từ [phần 2](/course/chapter5/2):

```py
issues_dataset = load_dataset("json", data_files="datasets-issues.jsonl", split="train")
issues_dataset
```

```python out
Dataset({
    features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'timeline_url', 'performed_via_github_app'],
    num_rows: 3019
})
```

Tuyệt vời, chúng ta đã tạo tập dữ liệu đầu tiên của mình từ đầu! Nhưng tại sao lại có vài nghìn vấn đề khi [tab Sự cố](https://github.com/huggingface/datasets/issues) của kho lưu trữ 🤗 Datasets chỉ hiển thị tổng số 1,000 vấn đề 🤔? Như được mô tả trong [tài liệu](https://docs.github.com/en/rest/reference/issues#list-issues-assigned-to-the-authenticated-user) GitHub, đó là vì chúng ta đã tải xuống tất cả kéo các yêu cầu:

> REST API v3 của GitHub coi mọi yêu cầu kéo về là một vấn đề, nhưng không phải mọi vấn đề đều là yêu cầu kéo. Vì lý do này, điểm cuối "Issues" có thể trả về cả hai sự cố và kéo các yêu cầu trong phản hồi. Bạn có thể xác định các yêu cầu kéo bằng phím `pull_request`. Lưu ý rằng `id` của một yêu cầu kéo được trả về từ các điểm cuối "Issues" sẽ là một id vấn đề.

Vì nội dung của các vấn đề và yêu cầu kéo khá khác nhau, chúng ta hãy thực hiện một số xử lý trước nhỏ để cho phép chúng ta phân biệt giữa chúng.

## Làm sạch dữ liệu

Đoạn mã trên từ tài liệu của GitHub cho chúng ta biết rằng cột `pull_request` có thể được sử dụng để phân biệt giữa các vấn đề và các yêu cầu kéo. Hãy xem xét một mẫu ngẫu nhiên để xem sự khác biệt là gì. Như chúng ta đã làm trong [phần 3](/course/chapter5/3), chúng ta sẽ xâu chuỗi `Dataset.shuffle()` và `Dataset.select()` để tạo một mẫu ngẫu nhiên và sau đó nén cột `html_url` và `pull_request` để chúng tôi có thể so sánh các URL khác nhau:

```py
sample = issues_dataset.shuffle(seed=666).select(range(3))

# In ra URL và kéo về các mục yêu cầu
for url, pr in zip(sample["html_url"], sample["pull_request"]):
    print(f">> URL: {url}")
    print(f">> Pull request: {pr}\n")
```

```python out
>> URL: https://github.com/huggingface/datasets/pull/850
>> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/850', 'html_url': 'https://github.com/huggingface/datasets/pull/850', 'diff_url': 'https://github.com/huggingface/datasets/pull/850.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/850.patch'}

>> URL: https://github.com/huggingface/datasets/issues/2773
>> Pull request: None

>> URL: https://github.com/huggingface/datasets/pull/783
>> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/783', 'html_url': 'https://github.com/huggingface/datasets/pull/783', 'diff_url': 'https://github.com/huggingface/datasets/pull/783.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/783.patch'}
```

Ở đây, chúng ta có thể thấy rằng mỗi yêu cầu kéo được liên kết với các URL khác nhau, trong khi các vấn đề thông thường có mục nhập `None`. Chúng ta có thể sử dụng sự phân biệt này để tạo một cột `is_pull_request` mới để kiểm tra xem trường `pull_request` có phải là `None` hay không:

```py
issues_dataset = issues_dataset.map(
    lambda x: {"is_pull_request": False if x["pull_request"] is None else True}
)
```

<Tip>

✏️ **Thử nghiệm thôi!** Tính thời gian trung bình cần để đóng các vấn đề trong 🤗 Datasets. Bạn có thể thấy hàm `Dataset.filter()` hữu ích để lọc ra các yêu cầu kéo và các vấn đề đang mở, đồng thời bạn có thể sử dụng hàm `Dataset.set_format()` để chuyển đổi tập dữ liệu thành `DataFrame` để bạn có thể dễ dàng thao tác dấu thời gian `create_at` và `closed_at`. Đối với điểm thưởng, hãy tính thời gian trung bình cần để đóng các yêu cầu kéo.

</Tip>

Mặc dù chúng ta có thể tiếp tục dọn dẹp tập dữ liệu bằng cách loại bỏ hoặc đổi tên một số cột, nhưng thông thường tốt nhất là giữ tập dữ liệu ở trạng thái "thô" nhất có thể ở giai đoạn này để có thể dễ dàng sử dụng trong nhiều ứng dụng.

Trước khi chúng tôi đẩy tập dữ liệu của mình sang Hugging Face Hub, hãy giải quyết một thứ còn thiếu trong nó: các nhận xét liên quan đến từng vấn đề và yêu cầu kéo. Chúng ta sẽ thêm chúng vào phầi tiếp theo với - bạn đoán được không - chính là API GitHub REST!

## Bổ sung tập dữ liệu

Mặc dù chúng tôi có thể tiếp tục dọn dẹp tập dữ liệu bằng cách loại bỏ hoặc đổi tên một số cột, nhưng thông thường tốt nhất là giữ tập dữ liệu ở trạng thái "thô" nhất có thể ở giai đoạn này để có thể dễ dàng sử dụng trong nhiều ứng dụng.

Trước khi chúng tôi đẩy tập dữ liệu của mình sang Trung tâm khuôn mặt ôm, hãy giải quyết một thứ còn thiếu trong nó: các nhận xét liên quan đến từng vấn đề và yêu cầu kéo. Chúng tôi sẽ thêm chúng vào lần tiếp theo với - bạn đoán không - API GitHub REST!

## Bổ sung tập dữ liệu

Như được hiển thị trong ảnh chụp màn hình sau, các nhận xét liên quan đến vấn đề hoặc yêu cầu kéo cung cấp nguồn thông tin phong phú, đặc biệt nếu chúng ta quan tâm đến việc xây dựng một công cụ tìm kiếm để trả lời các truy vấn của người dùng về thư viện.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/datasets-issues-comment.png" alt="Comments associated with an issue about 🤗 Datasets." width="80%"/>
</div>

API GitHub REST cung cấp điểm cuối [`Comments`](https://docs.github.com/en/rest/reference/issues#list-issue-comments) trả về tất cả các nhận xét được liên kết với số vấn đề. Hãy kiểm tra điểm cuối để xem nó trả về những gì:

```py
issue_number = 2792
url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
response = requests.get(url, headers=headers)
response.json()
```

```python out
[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/comments/897594128',
  'html_url': 'https://github.com/huggingface/datasets/pull/2792#issuecomment-897594128',
  'issue_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
  'id': 897594128,
  'node_id': 'IC_kwDODunzps41gDMQ',
  'user': {'login': 'bhavitvyamalik',
   'id': 19718818,
   'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
   'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
   'gravatar_id': '',
   'url': 'https://api.github.com/users/bhavitvyamalik',
   'html_url': 'https://github.com/bhavitvyamalik',
   'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
   'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
   'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
   'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
   'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
   'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
   'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
   'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
   'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
   'type': 'User',
   'site_admin': False},
  'created_at': '2021-08-12T12:21:52Z',
  'updated_at': '2021-08-12T12:31:17Z',
  'author_association': 'CONTRIBUTOR',
  'body': "@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n    def test_load_dataset(self, dataset_name):\r\n        configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n>       self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n    self.parent.assertTrue(len(dataset[split]) > 0)\r\nE   AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?",
  'performed_via_github_app': None}]
```

Chúng ta có thể thấy rằng nhận xét được lưu trữ trong trường `body`, vì vậy hãy viết một hàm đơn giản trả về tất cả các nhận xét liên quan đến một vấn đề bằng cách chọn nội dung `body` cho mỗi phần tử trong `response.json()`:

```py
def get_comments(issue_number):
    url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
    response = requests.get(url, headers=headers)
    return [r["body"] for r in response.json()]


# Kiểm tra hàm có hoạt động như mong đợi không
get_comments(2792)
```

```python out
["@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n    def test_load_dataset(self, dataset_name):\r\n        configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n>       self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n    self.parent.assertTrue(len(dataset[split]) > 0)\r\nE   AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?"]
```

Điều này có vẻ ổn, vì vậy hãy sử dụng `Dataset.map()` để thêm cột `comments` mới cho từng vấn đề trong tập dữ liệu của mình:

```py
# Tùy thuộc vào kết nối internet của bạn, quá trình này có thể mất vài phút ...
issues_with_comments_dataset = issues_dataset.map(
    lambda x: {"comments": get_comments(x["number"])}
)
```

Bước cuối cùng là lưu tập dữ liệu tăng cường cùng với dữ liệu thô của chúng ta để ta có thể đẩy cả hai vào Hub:

```py
issues_with_comments_dataset.to_json("issues-datasets-with-comments.jsonl")
```

## Tải tập dữ liệu lên Hugging Face Hub

<Youtube id="HaN6qCr_Afc"/>

Bây giờ chúng ta đã có tập dữ liệu tăng cường của mình, đã đến lúc chuyển nó vào Hub để chúng ta có thể chia sẻ nó với cộng đồng! Để tải tập dữ liệu lên, chúng tôi sẽ sử dụng [🤗 thư viện Hub](https://github.com/huggingface/huggingface_hub), cho phép chúng ta tương tác với Hugging Face Hub thông qua API Python. 🤗 Hub được cài đặt sẵn với 🤗 Transformers, vì vậy chúng ta có thể sử dụng trực tiếp. Ví dụ: chúng ta có thể sử dụng hàm `list_datasets()` để lấy thông tin về tất cả các tập dữ liệu công khai hiện được lưu trữ trên Hub:

```py
from huggingface_hub import list_datasets

all_datasets = list_datasets()
print(f"Number of datasets on Hub: {len(all_datasets)}")
print(all_datasets[0])
```

```python out
Number of datasets on Hub: 1487
Dataset Name: acronym_identification, Tags: ['annotations_creators:expert-generated', 'language_creators:found', 'languages:en', 'licenses:mit', 'multilinguality:monolingual', 'size_categories:10K<n<100K', 'source_datasets:original', 'task_categories:structure-prediction', 'task_ids:structure-prediction-other-acronym-identification']
```

Chúng ta có thể thấy rằng hiện có gần 1,500 tập dữ liệu trên Hub và hàm `list_datasets()` cũng cung cấp một số siêu dữ liệu cơ bản về từng kho lưu trữ tập dữ liệu.

Đối với mục đích của mình, điều đầu tiên chúng ta cần làm là tạo một kho lưu trữ tập dữ liệu mới trên Hub. Để làm điều đó, chúng ta cần một token xác thực, có thể nhận được bằng cách đầu tiên đăng nhập vào Hugging Face Hub bằng hàm `notebook_login()`:

```py
from huggingface_hub import notebook_login

notebook_login()
```

Thao tác này sẽ tạo một tiện ích mà bạn có thể nhập tên người dùng và mật khẩu của mình và token API sẽ được lưu trong *~/.huggingface/token*. Nếu bạn đang chạy mã trong một thiết bị đầu cuối, bạn có thể đăng nhập qua CLI để thay thế:

```bash
huggingface-cli login
```

Sau khi thực hiện xong việc này, chúng ta có thể tạo một kho lưu trữ tập dữ liệu mới với hàm `create_repo()`:

```py
from huggingface_hub import create_repo

repo_url = create_repo(name="github-issues", repo_type="dataset")
repo_url
```

```python out
'https://huggingface.co/datasets/lewtun/github-issues'
```

Trong ví dụ này, chúng ta đã tạo một kho lưu trữ tập dữ liệu trống có tên là `github-issue` với tên người dùng `lewtun` (tên người dùng phải là tên người dùng Hub của bạn khi bạn đang chạy đoạn mã này!)

<Tip>

✏️ **Thử nghiệm thôi!** Sử dụng tên người dùng và mật khẩu Hugging Face Hub của bạn để lấy token và tạo một kho lưu trữ trống có tên là `github-issue`. Hãy nhớ **không bao giờ lưu thông tin đăng nhập của bạn** trong Colab hoặc bất kỳ kho lưu trữ nào khác, vì thông tin này có thể bị kẻ xấu lợi dụng.

</Tip>

Tiếp theo, hãy sao chép kho lưu trữ từ Hub vào máy cục bộ của chúng ta và sao chép tệp tập dữ liệu của chúng ta vào đó. 🤗 Hub cung cấp một lớp `Repository` tiện dụng bao bọc nhiều lệnh Git phổ biến, do đó, để sao chép kho lưu trữ từ xa, chúng ta chỉ cần cung cấp URL và đường dẫn cục bộ mà ta muốn sao chép tới:

```py
from huggingface_hub import Repository

repo = Repository(local_dir="github-issues", clone_from=repo_url)
!cp issues-datasets-with-comments.jsonl github-issues/
```

Theo mặc định, các phần mở rộng tệp khác nhau (chẳng hạn như *.bin*, *.gz*, và *.zip*) được theo dõi bằng Git LFS để các tệp lớn có thể được tạo phiên bản trong cùng một quy trình làm việc của Git. Bạn có thể tìm thấy danh sách các phần mở rộng tệp được theo dõi bên trong tệp *.gitattributes* của kho lưu trữ. Để đưa định dạng JSON Lines vào danh sách, chúng ta có thể chạy lệnh sau:

```py
repo.lfs_track("*.jsonl")
```

Sau đó ta có thể dùng `Repository.push_to_hub()` để đẩy dữ liệu lên Hub:

```py
repo.push_to_hub()
```

Nếu chúng ta điều hướng đến URL có trong `repo_url`, bây giờ chúng ta sẽ thấy rằng tệp tập dữ liệu của chúng ta đã được tải lên.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/hub-repo.png" alt="Our dataset repository on the Hugging Face Hub." width="80%"/>
</div>

Từ đây, bất kỳ ai cũng có thể tải xuống tập dữ liệu bằng cách chỉ cần cung cấp `load_dataset()` với ID kho lưu trữ dưới dạng tham số `path`:

```py
remote_dataset = load_dataset("lewtun/github-issues", split="train")
remote_dataset
```

```python out
Dataset({
    features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
    num_rows: 2855
})
```

Tuyệt vời, chúng ta đã đưa tập dữ liệu của mình vào Hub và nó có sẵn cho những người khác sử dụng! Chỉ còn một việc quan trọng cần làm: thêm _dataset card_  hay _thẻ dữ liệu_ giải thích cách tạo kho tài liệu và cung cấp thông tin hữu ích khác cho cộng đồng.

<Tip>

💡 Bạn cũng có thể tải tập dữ liệu lên Hugging Face Hub trực tiếp từ thiết bị đầu cuối bằng cách sử dụng `huggingface-cli` và một chút phép thuật từ Git. Tham khảo [hướng dẫn 🤗 Datasets](https://huggingface.co/docs/datasets/share#share-a-dataset-using-the-cli) để biết chi tiết về cách thực hiện việc này.

</Tip>

## Tạo thẻ dữ liệu

Tập dữ liệu được ghi chép đầy đủ có nhiều khả năng hữu ích hơn cho người khác (bao gồm cả tương lai của bạn!), vì chúng cung cấp bối cảnh để cho phép người dùng quyết định xem tập dữ liệu có liên quan đến tác vụ của họ hay không và để đánh giá bất kỳ sai lệch hoặc rủi ro tiềm ẩn nào liên quan đến việc sử dụng tập dữ liệu.

Trên Hugging Face Hub, thông tin này được lưu trữ trong mỗi tệp *README.md* của kho lưu trữ tập dữ liệu. Có hai bước chính bạn nên thực hiện trước khi tạo tệp này:

1. Sử dụng ứng dụng [`datasets-tagging`](https://huggingface.co/datasets/tagging/) để tạo thẻ siêu dữ liệu ở định dạng YAML. Các thẻ này được sử dụng cho nhiều tính năng tìm kiếm trên Hugging Face Hub và đảm bảo các thành viên của cộng đồng có thể dễ dàng tìm thấy tập dữ liệu của bạn. Vì chúng ta đã tạo tập dữ liệu tùy chỉnh ở đây, bạn sẽ cần sao chép kho lưu trữ `datasets-tagging` và chạy ứng dụng cục bộ. Đây là giao diện trông như thế nào:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/datasets-tagger.png" alt="The 'datasets-tagging' interface." width="80%"/>
</div>

2.Đọc [Hướng dẫn 🤗 Datasets](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) về cách tạo thẻ tập dữ liệu thông tin và sử dụng nó làm mẫu.

Bạn có thể tạo tệp *README.md* trực tiếp trên Hub và bạn có thể tìm thấy mẫu thẻ dữ liệu trong kho lưu trữ dữ liệu `lewtun/github-issues`. Ảnh chụp màn hình của thẻ dữ liệu đã điền đầy đủ thông tin được hiển thị bên dưới.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/dataset-card.png" alt="A dataset card." width="80%"/>
</div>

<Tip>

✏️ **Thử nghiệm thôi!** Sử dụng ứng dụng `dataset-tagging` và [hướng dẫn 🤗 Datasets](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) để hoàn thành tệp *README.md* cho vấn đề về dữ liệu trên Github của bạn.

</Tip>

Vậy đó! Trong phần này, chúng ta đã thấy rằng việc tạo một tập dữ liệu tốt có thể khá liên quan, nhưng may mắn thay, việc tải nó lên và chia sẻ nó với cộng đồng thì không. Trong phần tiếp theo, chúng ta sẽ sử dụng bộ dữ liệu mới của mình để tạo một công cụ tìm kiếm ngữ nghĩa với 🤗 Datasets có thể so khớp các câu hỏi với các vấn đề và nhận xét có liên quan nhất.

<Tip>

✏️ **Thử nghiệm thôi!** Thực hiện theo các bước chúng ta đã thực hiện trong phần này để tạo tập dữ liệu về các vấn đề GitHub cho thư viện mã nguồn mở yêu thích của bạn (tất nhiên là chọn thứ khác ngoài 🤗 Datasets!). Để có điểm thưởng, hãy tinh chỉnh bộ phân loại đa nhãn để dự đoán các thẻ có trong trường `labels`.

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter5/5.mdx" />

### Giới thiệu
https://huggingface.co/learn/course/vi/chapter5/1.md

# Giới thiệu

<CourseFloatingBanner
    chapter={5}
    classNames="absolute z-10 right-0 top-0"
/>

Trong [Chương 3](/course/chapter3), bạn sẽ lần đầu được trải nghiệm thư viện 🤗 Datasets và thấy rằng có ba bước chính khi tinh chỉnh một mô hình:

1. Tải tập dữ liệu từ Hugging Face Hub.
2. Tiền xử lý dữ liệu với `Dataset.map()`.
3. Tải và tính toán các chỉ số.

Nhưng đây chỉ là bề nổi của những gì 🤗 Datasets có thể làm! Trong chương này, chúng ta sẽ đi sâu vào thư viện. Trong hành trình này, chúng ta sẽ tìm câu trả lời cho những câu hỏi sau:

* Bạn làm gì khi bộ dữ liệu của bạn không có trên Hub?
* Làm thế nào bạn có thể chia một bộ dữ liệu? (Và điều gì sẽ xảy ra nếu bạn _thực sự_ cần sử dụng Pandas?)
* Bạn sẽ làm gì khi bộ dữ liệu của bạn rất lớn và sẽ làm tràn RAM của máy tính xách tay của bạn?
* "Bản đồ bộ nhớ" và Apache Arrow là cái quái gì vậy?
* Làm cách nào bạn có thể tạo bộ dữ liệu của riêng mình và đẩy nó lên Hub?

Các kỹ thuật bạn học được ở đây sẽ giúp bạn chuẩn bị cho các tác vụ tinh chỉnh và tokenize nâng cao trong [Chương 6](/course/chapter6) và [Chương 7](/course/chapter7) - vì vậy hãy uống một ly cà phê và bắt đầu thôi!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter5/1.mdx" />

### Sắp xếp dữ liệu
https://huggingface.co/learn/course/vi/chapter5/3.md

# Sắp xếp dữ liệu

<CourseFloatingBanner chapter={5}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter5/section3.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter5/section3.ipynb"},
]} />

Hầu hết thời gian, dữ liệu bạn làm việc sẽ chưa được chuẩn bị hoàn hảo cho các mô hình huấn luyện. Trong phần này, chúng ta sẽ khám phá các tính năng khác nhau mà 🤗 Datasets cung cấp để làm sạch các tập dữ liệu của bạn.

<Youtube id="tqfSFcPMgOI"/>

## Sắp xếp dữ liệu của chúng ta

Tương tự như Pandas, 🤗 Datasets cung cấp một số tính năng để thao túng nội dung của `Dataset` và `DatasetDict`. Chúng ta đã gặp phương thức `Dataset.map()` trong [Chương 3](/course/chapter3) và trong phần này, chúng ta sẽ khám phá một số hàm khác theo ý của chúng ta.

Đối với ví dụ này, chúng tôi sẽ sử dụng [Bộ dữ liệu đánh giá thuốc](https://archive.ics.uci.edu/ml/datasets/Drug+Review+Dataset+%28Drugs.com%29) được lưu trữ trên [Kho lưu trữ Học máy UC Irvine](https://archive.ics.uci.edu/ml/index.php), chứa các đánh giá của bệnh nhân về các loại thuốc khác nhau, cùng với tình trạng đang được điều trị và xếp hạng 10 sao về mức độ hài lòng của bệnh nhân.

Trước tiên, chúng ta cần tải xuống và giải nén dữ liệu, có thể thực hiện bằng lệnh `wget` và  `unzip`:

```py
!wget "https://archive.ics.uci.edu/ml/machine-learning-databases/00462/drugsCom_raw.zip"
!unzip drugsCom_raw.zip
```

Vì TSV chỉ là một biến thể của CSV sử dụng dấu tab thay vì dấu phẩy làm dấu phân cách, chúng ta có thể tải các tệp này bằng cách sử dụng tập lệnh tải `csv` và chỉ định đối số `delimiter` trong hàm `load_dataset()` như sau:

```py
from datasets import load_dataset

data_files = {"train": "drugsComTrain_raw.tsv", "test": "drugsComTest_raw.tsv"}
# \t is the tab character in Python
drug_dataset = load_dataset("csv", data_files=data_files, delimiter="\t")
```

Một thực tiễn khi thực hiện bất kỳ loại phân tích dữ liệu nào là lấy một mẫu ngẫu nhiên nhỏ để có thể cảm nhận nhanh về loại dữ liệu bạn đang làm việc. Trong 🤗 Datasets, chúng ta có thể tạo một mẫu ngẫu nhiên bằng cách xâu chuỗi các hàm `Dataset.shuffle()` và `Dataset.select()` với nhau:

```py
drug_sample = drug_dataset["train"].shuffle(seed=42).select(range(1000))
# Xem qua một số ví dụ đầu tiên
drug_sample[:3]
```

```python out
{'Unnamed: 0': [87571, 178045, 80482],
 'drugName': ['Naproxen', 'Duloxetine', 'Mobic'],
 'condition': ['Gout, Acute', 'ibromyalgia', 'Inflammatory Conditions'],
 'review': ['"like the previous person mention, I&#039;m a strong believer of aleve, it works faster for my gout than the prescription meds I take. No more going to the doctor for refills.....Aleve works!"',
  '"I have taken Cymbalta for about a year and a half for fibromyalgia pain. It is great\r\nas a pain reducer and an anti-depressant, however, the side effects outweighed \r\nany benefit I got from it. I had trouble with restlessness, being tired constantly,\r\ndizziness, dry mouth, numbness and tingling in my feet, and horrible sweating. I am\r\nbeing weaned off of it now. Went from 60 mg to 30mg and now to 15 mg. I will be\r\noff completely in about a week. The fibro pain is coming back, but I would rather deal with it than the side effects."',
  '"I have been taking Mobic for over a year with no side effects other than an elevated blood pressure.  I had severe knee and ankle pain which completely went away after taking Mobic.  I attempted to stop the medication however pain returned after a few days."'],
 'rating': [9.0, 3.0, 10.0],
 'date': ['September 2, 2015', 'November 7, 2011', 'June 5, 2013'],
 'usefulCount': [36, 13, 128]}
```

Lưu ý rằng chúng ta đã sửa seed trong `Dataset.shuffle()` cho mục đích tái tạo.  `Dataset.select()` mong đợi một chỉ số có thể lặp lại, vì vậy chúng ta truyền vào khoảng `range(1000)` để lấy 1,000 mẫu đầu tiên từ tập dữ liệu đã xáo trộn. Từ mẫu này, ta đã có thể thấy một số điều kỳ quặc trong tập dữ liệu:

* Cột `Unnamed: 0`  trông đáng ngờ giống như một ID ẩn danh cho mỗi bệnh nhân.
* Cột `condition` bao gồm sự kết hợp giữa các nhãn chữ hoa và chữ thường.
* Các bài đánh giá có độ dài khác nhau và chứa hỗn hợp các dấu phân tách dòng Python (`\r\n`) cũng như các mã ký tự HTML như `&\#039;`.

Hãy xem cách chúng ta có thể sử dụng 🤗 Datasets để giải quyết từng vấn đề này. Để kiểm tra giả thuyết ID bệnh nhân cho cột `Unnamed: 0`, ta có thể sử dụng hàm  `Dataset.unique()` để xác minh rằng số lượng ID khớp với số hàng trong mỗi lần tách:

```py
for split in drug_dataset.keys():
    assert len(drug_dataset[split]) == len(drug_dataset[split].unique("Unnamed: 0"))
```
Điều này dường như xác nhận giả thuyết của chúng tôi, vì vậy hãy dọn dẹp tập dữ liệu một chút bằng cách đổi tên cột `Unname: 0` thành một cái gì đó dễ hiểu hơn một chút. Chúng ta có thể sử dụng hàm `DatasetDict.rename_column()` để đổi tên cột trên cả hai tập con trong một lần:

```py
drug_dataset = drug_dataset.rename_column(
    original_column_name="Unnamed: 0", new_column_name="patient_id"
)
drug_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
        num_rows: 161297
    })
    test: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
        num_rows: 53766
    })
})
```

<Tip>

✏️ **Thử nghiệm thôi!** Sử dụng hàm `Dataset.unique()` để tìm số lượng thuốc độc nhất và điều kiện trong tập huấn luyện và kiểm thử.

</Tip>

Tiếp theo, hãy chuẩn hóa tất cả các nhãn `condition` bằng cách sử dụng `Dataset.map()`. Như chúng ta đã làm với tokenize trong [Chương 3](/course/chapter3), chúng ta có thể xác định một hàm đơn giản có thể được áp dụng trên tất cả các hàng của mỗi tập trong `drug_dataset`:

```py
def lowercase_condition(example):
    return {"condition": example["condition"].lower()}


drug_dataset.map(lowercase_condition)
```

```python out
AttributeError: 'NoneType' object has no attribute 'lower'
```

Ồ không, chúng ta đã gặp sự cố với chức năng nối của mình! Từ lỗi, chúng ta có thể suy ra rằng một số mục nhập trong cột `condition` là `None`, không thể viết thường vì chúng không phải là chuỗi. Hãy bỏ các hàng này bằng cách sử dụng `Dataset.filter()`, hoạt động theo cách tương tự như `Dataset.map()` và mong đợi một hàm nhận được một mẫu về tập dữ liệu. Thay vì viết một hàm rõ ràng như:

```py
def filter_nones(x):
    return x["condition"] is not None
```

và sau đó chạy `drug_dataset.filter(filter_nones)`, chúng ta có thể thực hiện việc này trong một dòng bằng cách sử dụng _hàm lambda_. Trong Python, các hàm lambda là các hàm nhỏ mà bạn có thể định nghĩa mà không cần đặt tên rõ ràng. Chúng có dạng chung:

```
lambda <arguments> : <expression>
```

ở đây `lambda` là một trong những [từ khóa](https://docs.python.org/3/reference/lexical_analysis.html#keywords) đặc biệt của Python, `<arguments>` là danh sách / tập hợp các giá trị được phân tách bằng dấu phẩy xác định các đầu vào cho hàm và `<expression>`  đại diện cho các hoạt động bạn muốn thực hiện. Ví dụ, chúng ta có thể định nghĩa một hàm lambda đơn giản bình phương một số như sau:

```
lambda x : x * x
```

Để áp dụng hàm này cho một đầu vào, chúng ta cần đặt nó và đầu vào trong dấu ngoặc đơn:

```py
(lambda x: x * x)(3)
```

```python out
9
```

Tương tự, chúng ta có thể định nghĩa các hàm lambda với nhiều tham số bằng cách phân tách chúng bằng dấu phẩy. Ví dụ, chúng ta có thể tính diện tích của một tam giác như sau:

```py
(lambda base, height: 0.5 * base * height)(4, 8)
```

```python out
16.0
```

Các hàm Lambda rất hữu ích khi bạn muốn định nghĩa các hàm nhỏ, sử dụng một lần (để biết thêm thông tin về chúng, chúng tôi khuyên bạn nên đọc [Hướng dẫn Python đích thực](https://realpython.com/python-lambda/) của Andre Burgaud). Trong ngữ cảnh 🤗 Datasets, chúng ta có thể sử dụng các hàm lambda để xác định các hoạt động nối và lọc đơn giản, vì vậy hãy sử dụng thủ thuật này để loại bỏ các phần `None` trong tập dữ liệu:

```py
drug_dataset = drug_dataset.filter(lambda x: x["condition"] is not None)
```

Với `None` đã bị xóa, chúng ta  có thể chuẩn hóa cột `condition`:

```py
drug_dataset = drug_dataset.map(lowercase_condition)
# Kiểm tra xem chữ viết thường đã hoạt động chưa
drug_dataset["train"]["condition"][:3]
```

```python out
['left ventricular dysfunction', 'adhd', 'birth control']
```

Nó hoạt động! Vậy là chúng ta đã làm sạch các nhãn, giờ chúng ta hãy xem xét việc làm sạch các bài đánh giá.

## Tạo ra các cột mới

Bất cứ khi nào bạn xử lý các bài đánh giá của khách hàng, một phương pháp hay đó là kiểm tra số lượng từ trong mỗi bài đánh giá. Bài đánh giá có thể chỉ là một từ duy nhất như "Tuyệt vời!" hoặc một bài luận đầy đủ với hàng nghìn từ, và tùy thuộc vào trường hợp sử dụng, bạn sẽ cần xử lý những thái cực này theo cách khác nhau. Để tính toán số lượng từ trong mỗi bài đánh giá, chúng tôi sẽ sử dụng phương pháp phỏng đoán sơ bộ dựa trên việc tách từng văn bản theo khoảng trắng.

Hãy định nghĩa một hàm đơn giản đếm số từ trong mỗi bài đánh giá:

```py
def compute_review_length(example):
    return {"review_length": len(example["review"].split())}
```

Không giống như hàm `lowercase_condition()`, `compute_review_length()` trả về một từ điển có khóa không tương ứng với một trong các tên cột trong tập dữ liệu. Trong trường hợp này, khi `compute_review_length()` được truyền vào `Dataset.map()`, nó sẽ được áp dụng cho tất cả các hàng trong tập dữ liệu để tạo cột mới `review_length`:

```py
drug_dataset = drug_dataset.map(compute_review_length)
# Kiểm tra mẫu huấn luyện đầu tiên
drug_dataset["train"][0]
```

```python out
{'patient_id': 206461,
 'drugName': 'Valsartan',
 'condition': 'left ventricular dysfunction',
 'review': '"It has no side effect, I take it in combination of Bystolic 5 Mg and Fish Oil"',
 'rating': 9.0,
 'date': 'May 20, 2012',
 'usefulCount': 27,
 'review_length': 17}
```

Như mong đợi, chúng ta có thể thấy cột `review_length` đã được thêm vào tập huấn luyện của chúng ta. Chúng ta có thể sắp xếp cột mới này với `Dataset.sort()` để xem các giá trị cực đại trông như thế nào:

```py
drug_dataset["train"].sort("review_length")[:3]
```

```python out
{'patient_id': [103488, 23627, 20558],
 'drugName': ['Loestrin 21 1 / 20', 'Chlorzoxazone', 'Nucynta'],
 'condition': ['birth control', 'muscle spasm', 'pain'],
 'review': ['"Excellent."', '"useless"', '"ok"'],
 'rating': [10.0, 1.0, 6.0],
 'date': ['November 4, 2008', 'March 24, 2017', 'August 20, 2016'],
 'usefulCount': [5, 2, 10],
 'review_length': [1, 1, 1]}
```

Như ta đã nghi vấn, một số đánh giá chỉ chứa một từ duy nhất, mặc dù có thể ổn để phân tích sắc thái, nhưng sẽ không có nhiều thông tin nếu chúng tôi muốn dự đoán tình trạng bệnh.

<Tip>

🙋 Một cách thay thế để thêm các cột mới vào tập dữ liệu là sử dụng hàm `Dataset.add_column()`. Điều này cho phép bạn cung cấp cột dưới dạng danh sách Python hoặc mảng NumPy và có thể hữu ích trong các trường hợp mà `Dataset.map()` không phù hợp cho phân tích của bạn.

</Tip>

Hãy sử dụng hàm `Dataset.filter()` để xóa các bài đánh giá có ít hơn 30 từ. Tương tự như những gì chúng ta đã làm với cột `condition`, chúng ta có thể lọc ra các bài đánh giá rất ngắn bằng cách yêu cầu các bài đánh giá có độ dài trên ngưỡng này:

```py
drug_dataset = drug_dataset.filter(lambda x: x["review_length"] > 30)
print(drug_dataset.num_rows)
```

```python out
{'train': 138514, 'test': 46108}
```

Như bạn có thể thấy, điều này đã loại bỏ khoảng 15% bài đánh giá khỏi bộ huấn luyện và kiểm thử ban đầu.

<Tip>

✏️ **Thử nghiệm thôi!** Sử dụng hàm `Dataset.sort()` để kiểm tra các bài đánh giá có số lượng từ lớn nhất. Tham khảo [tài liệu](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.sort) để biết bạn cần sử dụng tham số nào để sắp xếp các bài đánh giá theo thứ tự giảm dần.

</Tip>

Điều cuối cùng chúng ta cần giải quyết là sự hiện diện của ký tự HTML trong các bài đánh giá của chúng ta. Chúng ta có thể sử dụng mô-đun `html` của Python để loại bỏ qua các ký tự này, như sau:

```py
import html

text = "I&#039;m a transformer called BERT"
html.unescape(text)
```

```python out
"I'm a transformer called BERT"
```

Ta sẽ sử dụng `Dataset.map()` để hủy tất cả các ký tự HTML trong kho tài liệu của mình:

```python
drug_dataset = drug_dataset.map(lambda x: {"review": html.unescape(x["review"])})
```

Như bạn có thể thấy, phương thức `Dataset.map()` khá hữu ích để xử lý dữ liệu - và chúng ta thậm chí còn chưa rõ tất mọi thứ mà nó có thể làm!

## Siêu sức mạnh của hàm `map()`

Phương thức `Dataset.map ()` nhận tham số `batched`, nếu được đặt thành `True`, nó sẽ gửi một loạt các mẫu đến hàm map cùng một lúc (ta có thể cấu hình kích thước lô nhưng mặc định là 1,000). Ví dụ: hàm map trước đó loại bỏ tất cả HTML đã mất một chút thời gian để chạy (bạn có thể đọc thời gian thực hiện từ các thanh tiến trình). Chúng ta có thể tăng tốc độ này bằng cách xử lý một số phần tử cùng lúc thông qua sử dụng bao hàm.

Khi bạn chỉ định `batched=True`, hàm sẽ nhận một từ điển với các trường của tập dữ liệu, nhưng mỗi giá trị bây giờ là một _danh sách các giá trị_ và không chỉ là một giá trị duy nhất. Giá trị trả về của `Dataset.map()` phải giống nhau: một từ điển với các trường ta muốn cập nhật hoặc thêm vào tập dữ liệu của mình và một danh sách các giá trị. Ví dụ: đây là một cách khác để hủy tất cả các ký tự HTML, nhưng sử dụng `batched=True`:

```python
new_drug_dataset = drug_dataset.map(
    lambda x: {"review": [html.unescape(o) for o in x["review"]]}, batched=True
)
```

Nếu bạn đang chạy đoạn mã này trên notebook, bạn sẽ thấy rằng lệnh này thực thi nhanh hơn lệnh trước đó. Và đó không phải là do các bài đánh giá của chúng tôi đã được loại đi HTML - nếu bạn thực hiện lại hướng dẫn từ phần trước (không có `batch = True`), nó sẽ mất cùng một khoảng thời gian như trước. Điều này là do việc bao hàm thường nhanh hơn việc thực thi cùng một đoạn mã trong vòng lặp `for` và chúng ta cũng đạt được một số hiệu suất bằng cách truy cập nhiều phần tử cùng một lúc thay vì từng phần tử một.

Sử dụng `Dataset.map()` với `batched=True` sẽ là điều cần thiết để mở khóa tốc độ của các trình tokenize "nhanh" mà chúng ta sẽ gặp trong [Chương 6](/course/chap6), có thể nhanh chóng tokenize các danh sách lớn các văn bản. Ví dụ: để tokenize tất cả các đánh giá thuốc bằng trình tokenize nhanh, ta có thể sử dụng một hàm như sau:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")


def tokenize_function(examples):
    return tokenizer(examples["review"], truncation=True)
```

Như bạn đã thấy trong [Chương 3](/course/chapter3), chúng ta có thể truyền vào một hoặc nhiều mẫu cho tokenizer, vì vậy ta có thể sử dụng hàm này với `batched=True` hoặc không. Hãy cũng coi đây là một cơ hội để so sánh hiệu năng của hai tuỳ chọn này. Trong một notebook, bạn có thể bấm giờ chỉ với một dòng lệnh `%time` trước dòng mã bạn muốn tình thời gian:

```python no-format
%time tokenized_dataset = drug_dataset.map(tokenize_function, batched=True)
```

Bạn cũng có thể tính thời gian cho toàn bộ ô bằng cách đặt `%%time` ở đầu của ô mã. Trên phần cứng mà chúng ta thực hiện, nó hiển thị 10.8 giây cho lệnh này (đó là số được viết sau "Wall time").

<Tip>

✏️ **Thử nghiệm thôi!** Thực hiện cùng một hướng dẫn có và không có `batched=True`, sau đó thử nó với tokenizer chậm (thêm `use_fast=False` vào `AutoTokenizer.from_pretrained()`) để bạn có thể thấy giá trị bạn nhận được trên phần cứng của mình.

</Tip>

Dưới đây là kết quả thu được khi có và không có tính năng phân lô, với tokenizer nhanh và chậm:

Tuỳ chọn         | Tokenizer nhanh | Tokenizer chậm
:--------------:|:--------------:|:-------------:
`batched=True`  | 10.8s          | 4min41s
`batched=False` | 59.2s          | 5min3s

Điều này có nghĩa là việc sử dụng một tokenizer nhanh với tùy chọn `batched=True` sẽ nhanh hơn 30 lần so với phiên bản chậm mà không có lô - điều này thực sự tuyệt vời! Đó là lý do chính tại sao tokenizer nhanh là mặc định khi sử dụng `AutoTokenizer` (và tại sao chúng được gọi là "nhanh"). Chúng có thể đạt được tốc độ như vậy bởi vì phía sau, đoạn mã token hóa được thực thi bằng Rust, đây là một ngôn ngữ giúp dễ dàng thực hiện đoạn mã song song.

Song song hóa cũng là lý do giải thích cho tốc độ tăng gần gấp 6 lần mà trình tokenize nhanh đạt được với việc phân lô: bạn không thể song song một thao tác tokenize đơn lẻ, nhưng khi bạn muốn tokenize nhiều văn bản cùng một lúc, bạn có thể chỉ cần chia nhỏ việc thực thi trên nhiều quy trình, mỗi người chịu trách nhiệm về các văn bản của riêng mình.

`Dataset.map()` cũng tự có một số khả năng tính toán song song. Vì chúng không được hỗ trợ bởi Rust, nên chúng sẽ không để một trình tokenizer chậm bắt kịp với một tokenizer nhanh, nhưng chúng vẫn có thể hữu ích (đặc biệt nếu bạn đang sử dụng một tokenizer không có phiên bản nhanh). Để bật xử lý đa luồng, hãy sử dụng tham số `num_proc` và chỉ định số lượng quy trình sẽ sử dụng trong lệnh gọi của bạn tới `Dataset.map()`:

```py
slow_tokenizer = AutoTokenizer.from_pretrained("bert-base-cased", use_fast=False)


def slow_tokenize_function(examples):
    return slow_tokenizer(examples["review"], truncation=True)


tokenized_dataset = drug_dataset.map(slow_tokenize_function, batched=True, num_proc=8)
```

Bạn có thể thử nghiệm một chút với thời gian để xác định số lượng quy trình tối ưu để sử dụng; trong trường hợp của chúng ta, 8 dường như tạo ra tốc độ tăng tốt nhất. Dưới đây là những con số chúng tôi nhận được khi có và không có xử lý đa luồng:

Tuỳ chọn         | Tokenizer nhanh | Tokenizer chậm
:--------------:|:--------------:|:-------------:
`batched=True`  | 10.8s          | 4min41s
`batched=False` | 59.2s          | 5min3s
`batched=True`, `num_proc=8`  | 6.52s          | 41.3s
`batched=False`, `num_proc=8` | 9.49s          | 45.2s

Đó là những kết quả hợp lý hơn nhiều đối với tokenizer chậm, nhưng hiệu suất của tokenizer nhanh cũng đã được cải thiện đáng kể. Tuy nhiên, lưu ý rằng điều đó không phải lúc nào cũng đúng - đối với các giá trị của `num_proc` khác 8, các thử nghiệm của chúng tôi cho thấy rằng sử dụng `batched=True` mà không có tùy chọn này sẽ nhanh hơn. Nói chung, chúng tôi khuyên bạn không nên sử dụng xử lý đa luồng Python cho các trình tokenize nhanh với `batched=True`.

<Tip>

Sử dụng `num_proc` để tăng tốc quá trình xử lý của bạn thường là một ý tưởng tuyệt vời, miễn là hàm bạn đang sử dụng chưa thực hiện một số kiểu xử lý đa xử lý của riêng nó.

</Tip>

Tất cả các chức năng này được cô đọng trong một phương pháp đã khá tuyệt vời, nhưng còn nhiều hơn thế nữa! Với `Dataset.map()` và `batched=True`, bạn có thể thay đổi số lượng phần tử trong tập dữ liệu của mình. Điều này cực kỳ hữu ích trong nhiều trường hợp mà bạn muốn tạo một số đặc trưng huấn luyện từ một mẫu và chúng ta sẽ cần thực hiện điều này như một phần của quá trình tiền xử lý cho một số tác vụ NLP sẽ thực hiện trong [Chương 7](/course/chapter7).

<Tip>

💡 Trong học máy, một _mẫu_ thường được định nghĩa là tập hợp _đặc trưng_ mà chúng ta cung cấp cho mô hình. Trong một số ngữ cảnh, các đặc trưng này sẽ là tập hợp thành các cột trong `Dataset`, nhưng trong các trường hợp khác (như ở đây và để phục vụ hỏi đáp), nhiều đặc trưng có thể được trích xuất từ một mẫu và thuộc về một cột duy nhất.

</Tip>

Chúng ta hãy xem nó hoạt động như thế nào! Ở đây, ta sẽ tokenize các mẫu của mình và cắt chúng về độ dài tối đa là 128, nhưng ta sẽ yêu cầu trình tokenize trả về *tất cả* các đoạn văn bản thay vì chỉ đoạn văn bản đầu tiên. Điều này có thể được thực hiện với `return_overflowing_tokens=True`:

```py
def tokenize_and_split(examples):
    return tokenizer(
        examples["review"],
        truncation=True,
        max_length=128,
        return_overflowing_tokens=True,
    )
```

Hãy kiểm tra điều này trên một mẫu trước khi sử dụng `Dataset.map()` trên toàn bộ tập dữ liệu:

```py
result = tokenize_and_split(drug_dataset["train"][0])
[len(inp) for inp in result["input_ids"]]
```

```python out
[128, 49]
```

Vì vậy, mẫu đầu tiên trong tập huấn luyện đã trở thành hai đặc trưng vì nó đã được tokenize nhiều hơn số lượng token tối đa mà chúng tôi đã chỉ định: cái đầu tiên có độ dài 128 và cái thứ hai có độ dài 49. Bây giờ hãy làm điều này cho tất cả các phần tử của tập dữ liệu!

```py
tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
```

```python out
ArrowInvalid: Column 1 named condition expected length 1463 but got length 1000
```

Ôi không! Nó đã không hoạt động! Tại sao không? Nhìn vào thông báo lỗi sẽ cho chúng ta manh mối: có sự không khớp về độ dài của một trong các cột, một cột có độ dài 1,463 và cột còn lại có độ dài 1,000. Nếu bạn đã xem [tài liệu](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map) về `Dataset.map()`, bạn có thể nhớ rằng đó là số các mẫu được truyền vào hàm mà chúng ta đang ánh xạ; ở đây 1,000 mẫu đó đã cung cấp 1,463 đặc trưng mới, dẫn đến lỗi hình dạng.

Vấn đề là chúng ta đang cố gắng kết hợp hai tập dữ liệu khác nhau với các kích thước khác nhau: cột `drug_dataset` sẽ có một số mẫu nhất định (lỗi phía chúng ta là 1,000), nhưng `tokenized_dataset`  mà chúng ta đang xây dựng sẽ có nhiều hơn (1,463 trong thông báo lỗi). Điều này không hoạt động đối với `Dataset`, vì vậy chúng ta cần xóa các cột khỏi tập dữ liệu cũ hoặc làm cho chúng có cùng kích thước với chúng trong tập dữ liệu mới. Chúng ta có thể thực hiện điều đầu thông qua tham số `remove_columns`:

```py
tokenized_dataset = drug_dataset.map(
    tokenize_and_split, batched=True, remove_columns=drug_dataset["train"].column_names
)
```

Bây giờ nó hoạt động mà không có lỗi. Chúng ta có thể kiểm tra xem tập dữ liệu mới của mình có nhiều phần tử hơn tập dữ liệu gốc hay không bằng cách so sánh độ dài:

```py
len(tokenized_dataset["train"]), len(drug_dataset["train"])
```

```python out
(206772, 138514)
```

Chúng tôi đã đề cập rằng chúng ta cũng có thể giải quyết vấn đề chiều dài không khớp bằng cách làm cho các cột cũ có cùng kích thước với các cột mới. Để thực hiện việc này, chúng ta sẽ cần trường `overflow_to_sample_mapping` mà tokenizer trả về khi chúng ta đặt `return_overflowing_tokens=True`. Nó cung cấp cho chúng ta một ánh xạ từ một chỉ mục đặc trưng mới đến chỉ mục của mẫu mà nó bắt nguồn từ đó. Sử dụng điều này, chúng ta có thể liên kết mỗi khóa có trong tập dữ liệu ban đầu với một danh sách các giá trị có kích thước phù hợp bằng cách lặp lại các giá trị của mỗi ví dụ nhiều lần khi nó tạo ra các đặc trưng mới:

```py
def tokenize_and_split(examples):
    result = tokenizer(
        examples["review"],
        truncation=True,
        max_length=128,
        return_overflowing_tokens=True,
    )
    # Extract mapping between new and old indices
    sample_map = result.pop("overflow_to_sample_mapping")
    for key, values in examples.items():
        result[key] = [values[i] for i in sample_map]
    return result
```

Chúng ta có thể thấy nó hoạt động với `Dataset.map()` mà chúng ta không cần xóa các cột cũ:

```py
tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
tokenized_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
        num_rows: 206772
    })
    test: Dataset({
        features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
        num_rows: 68876
    })
})
```

Chúng ta nhận được cùng số đặc trưng huấn luyện như trước đó, nhưng ở đây ta đã giữ lại tất cả các trường cũ. Nếu bạn cần chúng để hậu xử lý sau khi áp dụng mô hình của mình, bạn có thể muốn sử dụng phương pháp này.

Bây giờ bạn đã thấy cách 🤗 Datasets có thể được sử dụng để tiền xử lý một tập dữ liệu theo nhiều cách khác nhau. Mặc dù các chức năng xử lý của 🤗 Datasets sẽ đáp ứng hầu hết các nhu cầu huấn luyện mô hình của bạn,
có thể đôi khi bạn cần chuyển sang Pandas để truy cập các tính năng mạnh mẽ hơn, chẳng hạn như  `DataFrame.groupby()` hoặc các API cấp cao để trực quan hóa. May mắn thay, 🤗 Datasets được thiết kế để có thể tương tác với các thư viện như Pandas, NumPy, PyTorch, TensorFlow và JAX. Chúng ta hãy xem cách này hoạt động như thế nào.

## Từ `Dataset` tới `DataFrame` và ngược lại

<Youtube id="tfcY1067A5Q"/>

Để cho phép chuyển đổi giữa các thư viện bên thứ ba khác nhau, 🤗 Datasets cung cấp hàm `Dataset.set_format()`. Hàm này chỉ thay đổi _định dạng đầu ra_ của tập dữ liệu, vì vậy bạn có thể dễ dàng chuyển sang định dạng khác mà không ảnh hưởng đến _định dạng đầu ra_ bên dưới, đó là Apache Arrow. Việc định dạng được thực hiện tại chỗ. Để chứng minh, hãy chuyển đổi tập dữ liệu của chúng tôi thành Pandas:

```py
drug_dataset.set_format("pandas")
```

Giờ khi chúng ta truy cập các phần tử của tập dữ liệu, ta nhận được `pandas.DataFrame` thay vì từ điển:

```py
drug_dataset["train"][:3]
```

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>patient_id</th>
      <th>drugName</th>
      <th>condition</th>
      <th>review</th>
      <th>rating</th>
      <th>date</th>
      <th>usefulCount</th>
      <th>review_length</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>95260</td>
      <td>Guanfacine</td>
      <td>adhd</td>
      <td>"My son is halfway through his fourth week of Intuniv..."</td>
      <td>8.0</td>
      <td>April 27, 2010</td>
      <td>192</td>
      <td>141</td>
    </tr>
    <tr>
      <th>1</th>
      <td>92703</td>
      <td>Lybrel</td>
      <td>birth control</td>
      <td>"I used to take another oral contraceptive, which had 21 pill cycle, and was very happy- very light periods, max 5 days, no other side effects..."</td>
      <td>5.0</td>
      <td>December 14, 2009</td>
      <td>17</td>
      <td>134</td>
    </tr>
    <tr>
      <th>2</th>
      <td>138000</td>
      <td>Ortho Evra</td>
      <td>birth control</td>
      <td>"This is my first time using any form of birth control..."</td>
      <td>8.0</td>
      <td>November 3, 2015</td>
      <td>10</td>
      <td>89</td>
    </tr>
  </tbody>
</table>

Hãy tạo ra một `pandas.DataFrame` cho toàn bộ tập huấn luyện bằng cách chọn tất cả các phần tử trong `drug_dataset["train"]`:

```py
train_df = drug_dataset["train"][:]
```

<Tip>

🚨 Bên dưới `Dataset.set_format()` thay đổi định dạng trả về cho phương thức `__getitem __()` của tập dữ liệu. Điều này có nghĩa là khi chúng ta muốn tạo một đối tượng mới như `train_df` từ `Dataset` ở định dạng `"pandas"`, chúng ta cần cắt toàn bộ tập dữ liệu để có được một `pandas.DataFrame`. Bạn có thể tự xác minh xem kiểu dữ liệu của `drug_dataset["train"]` có phải là `Dataset`, bất kể định dạng đầu ra là gì.

</Tip>

Từ đây, ta có thể sử dụng tất cả các chức năng của Pandas mà ta muốn. Ví dụ, chúng ta có thể thực hiện chuỗi lạ mắt để tính toán phân phối lớp giữa các `condition`:

```py
frequencies = (
    train_df["condition"]
    .value_counts()
    .to_frame()
    .reset_index()
    .rename(columns={"index": "condition", "count": "frequency"})
)
frequencies.head()
```

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>condition</th>
      <th>frequency</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>birth control</td>
      <td>27655</td>
    </tr>
    <tr>
      <th>1</th>
      <td>depression</td>
      <td>8023</td>
    </tr>
    <tr>
      <th>2</th>
      <td>acne</td>
      <td>5209</td>
    </tr>
    <tr>
      <th>3</th>
      <td>anxiety</td>
      <td>4991</td>
    </tr>
    <tr>
      <th>4</th>
      <td>pain</td>
      <td>4744</td>
    </tr>
  </tbody>
</table>

Và khi chúng ta hoàn thành phân tích Pandas của mình, chúng ta luôn có thể tạo một đối tượng `Dataset` mới bằng cách sử dụng hàm `Dataset.from_pandas()` như sau:

```py
from datasets import Dataset

freq_dataset = Dataset.from_pandas(frequencies)
freq_dataset
```

```python out
Dataset({
    features: ['condition', 'frequency'],
    num_rows: 819
})
```

<Tip>

✏️ **Thử nghiệm thôi!** Tính xếp hạng trung bình cho mỗi loại thuốc và lưu trữ kết quả ở dạng `Dataset` mới.

</Tip>

Phần này kết thúc chuyến tham quan của chúng ta về các kỹ thuật tiền xử lý khác nhau có sẵn trong 🤗 Datasets. Để hoàn thiện phần này, hãy tạo một tệp kiểm định để chuẩn bị tập dữ liệu cho việc huấn luyện một trình phân loại. Trước khi làm như vậy, chúng ta sẽ đặt lại định dạng đầu ra của `drug_dataset` từ `"pandas"` thành `"arrow"`:

```python
drug_dataset.reset_format()
```

## Tạo ra một tệp kiểm định

Mặc dù chúng ta có một bộ dữ liệu kiểm thử có thể sử dụng để đánh giá, nhưng bạn nên giữ nguyên bộ kiểm thử và tạo một bộ kiểm định riêng trong quá trình phát triển. Khi bạn hài lòng với hiệu suất của các mô hình của mình trên bộ kiểm định, bạn có thể thực hiện kiểm tra lần cuối đối với bộ kiểm thử. Quy trình này giúp giảm thiểu rủi ro rằng bạn sẽ trang bị quá mức cho bộ kiểm thử và triển khai một mô hình không thành công trên dữ liệu trong thế giới thực.

🤗 Datasets cung cấp một hàm `Dataset.train_test_split()` dựa trên tính năng nổi tiếng từ `scikit-learn`. Hãy cùng dùng nó để chia tập huấn luyện thành các tập `train` và `validation` (ta đặt tham số `seed` cho mục đính tái tạo):

```py
drug_dataset_clean = drug_dataset["train"].train_test_split(train_size=0.8, seed=42)
# Thay đổi tên mặc định "test" thành "validation"
drug_dataset_clean["validation"] = drug_dataset_clean.pop("test")
# Thêm "test" vào `DatasetDict`
drug_dataset_clean["test"] = drug_dataset["test"]
drug_dataset_clean
```

```python out
DatasetDict({
    train: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
        num_rows: 110811
    })
    validation: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
        num_rows: 27703
    })
    test: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
        num_rows: 46108
    })
})
```

Tuyệt vời, ta hiện đã chuẩn bị một tập dữ liệu sẵn sàng để huấn luyện một số mô hình! Trong [phần 5](/course/chapter5/5), chúng tôi sẽ chỉ cho bạn cách tải tập dữ liệu lên Hugging Face Hub, nhưng bây giờ hãy quen với phân tích của chúng tôi bằng cách xem xét một số cách bạn có thể lưu tập dữ liệu trên máy cục bộ của mình.

## Lưu một bộ dữ liệu

<Youtube id="blF9uxYcKHo"/>

Mặc dù 🤗 Datasets sẽ lưu vào bộ nhớ cache mọi tập dữ liệu đã tải xuống và các hoạt động được thực hiện trên nó, nhưng đôi khi bạn sẽ muốn lưu tập dữ liệu vào đĩa (ví dụ: trong trường hợp bộ nhớ cache bị xóa). Như thể hiện trong bảng bên dưới, 🤗 Datasets cung cấp ba chức năng chính để lưu tập dữ liệu của bạn ở các định dạng khác nhau:

| Định dạng dữ liệu |        Hàm        |
| :---------: | :--------------------: |
|    Arrow    | `Dataset.save_to_disk()` |
|     CSV     |    `Dataset.to_csv()`    |
|    JSON     |   `Dataset.to_json()`    |

Ví dụ, hãy cùng lưu dữ liệu sạch của chúng ta về định dạng Arrow:

```py
drug_dataset_clean.save_to_disk("drug-reviews")
```

Nó sẽ tạo ra một kho lưu trữ với cấu trúc như sau:

```
drug-reviews/
├── dataset_dict.json
├── test
│   ├── dataset.arrow
│   ├── dataset_info.json
│   └── state.json
├── train
│   ├── dataset.arrow
│   ├── dataset_info.json
│   ├── indices.arrow
│   └── state.json
└── validation
    ├── dataset.arrow
    ├── dataset_info.json
    ├── indices.arrow
    └── state.json
```

nơi chúng ta có thể thấy rằng mỗi phần tách ra được liên kết với bảng *dataset.arrow* của riêng nó và một số siêu dữ liệu trong *dataset_info.json* và *state.json*. Bạn có thể coi định dạng Arrow như một bảng gồm các cột và hàng ưa thích được tối ưu hóa để xây dựng các ứng dụng hiệu suất cao xử lý và vận chuyển các tập dữ liệu lớn.

Sau khi tập dữ liệu được lưu, chúng ta có thể tải nó bằng cách sử dụng hàm `load_from_disk()` như sau:

```py
from datasets import load_from_disk

drug_dataset_reloaded = load_from_disk("drug-reviews")
drug_dataset_reloaded
```

```python out
DatasetDict({
    train: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
        num_rows: 110811
    })
    validation: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
        num_rows: 27703
    })
    test: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
        num_rows: 46108
    })
})
```
Đối với định dạng CSV và JSON, chúng ta phải lưu trữ từng phần thành một tệp riêng biệt. Một cách để làm điều này là lặp lại các khóa và giá trị trong đối tượng `DatasetDict`:

```py
for split, dataset in drug_dataset_clean.items():
    dataset.to_json(f"drug-reviews-{split}.jsonl")
```

Nó sẽ lưu mỗi phần dữ liệu vào[định dạng JSON Lines](https://jsonlines.org), nơi mỗi dòng trong bộ dữ liệu được lưu trữ trên một dòng JSON. Đây là một ví dụ về hình hài cua nó:

```py
!head -n 1 drug-reviews-train.jsonl
```

```python out
{"patient_id":141780,"drugName":"Escitalopram","condition":"depression","review":"\"I seemed to experience the regular side effects of LEXAPRO, insomnia, low sex drive, sleepiness during the day. I am taking it at night because my doctor said if it made me tired to take it at night. I assumed it would and started out taking it at night. Strange dreams, some pleasant. I was diagnosed with fibromyalgia. Seems to be helping with the pain. Have had anxiety and depression in my family, and have tried quite a few other medications that haven't worked. Only have been on it for two weeks but feel more positive in my mind, want to accomplish more in my life. Hopefully the side effects will dwindle away, worth it to stick with it from hearing others responses. Great medication.\"","rating":9.0,"date":"May 29, 2011","usefulCount":10,"review_length":125}
```

Chúng ta sau đó có thể sử dụng các kỹ thuật trong [phần 2](/course/chapter5/2) để tải tệp JSON như sau:

```py
data_files = {
    "train": "drug-reviews-train.jsonl",
    "validation": "drug-reviews-validation.jsonl",
    "test": "drug-reviews-test.jsonl",
}
drug_dataset_reloaded = load_dataset("json", data_files=data_files)
```

Và đó là nó cho chuyến du ngoạn của chúng ta với sắp xếp dữ liệu sử dụng 🤗 Datasets! Giờ ta đã có một tập dữ liệu đã được làm sạch để huấn luyện mô hình, đây là một vài ý tưởng mà bạn có thể thử:

1. Sử dụng các kỹ thuật từ [Chương 3](/course/chapter3) để huấn luyện một bộ phân loại có thể dự đoán tình trạng bệnh nhân dựa trên các phản hồi về thuốc.
2. Sử dụng pipeline `summarization` từ [Chương 1](/course/chapter1) để tạo các bản tóm tắt các bài đánh giá.

Tiếp theo, chúng ta sẽ xem xét cách 🤗 Datasets có thể cho phép bạn làm việc với những tập dữ liệu khổng lồ mà không làm hỏng máy tính xách tay của bạn!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter5/3.mdx" />

### Tìm kiếm ngữ nghĩa với FAISS
https://huggingface.co/learn/course/vi/chapter5/6.md

# Tìm kiếm ngữ nghĩa với FAISS

{#if fw === 'pt'}

<CourseFloatingBanner chapter={5}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter5/section6_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter5/section6_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={5}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter5/section6_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter5/section6_tf.ipynb"},
]} />

{/if}

Trong [phần 5](/course/chapter5/5), chúng ta đã tạo tập dữ liệu về các vấn đề GitHub và nhận xét từ kho lưu trữ 🤗 Datasets. Trong phần này, chúng ta sẽ sử dụng thông tin này để xây dựng một công cụ tìm kiếm có thể giúp ta tìm câu trả lời cho những câu hỏi cấp bách nhất về thư viện!

<Youtube id="OATCgQtNX2o"/>

## Sử dụng nhúng biểu diễn từ cho tìm kiếm ngữ nghĩa

Như chúng ta đã thấy trong [Chương 1](/course/chapter1), các mô hình ngôn ngữ dựa trên Transformer đại diện cho mỗi token trong một khoảng văn bản dưới dạng một _vector nhugns biểu diễn từ_. Hóa ra người ta có thể "gộp" các biểu diễn riêng lẻ để tạo biểu diễn vectơ cho toàn bộ câu, đoạn văn hoặc toàn bộ (trong một số trường hợp) tài liệu. Sau đó, các phép nhúng này có thể được sử dụng để tìm các tài liệu tương tự trong kho tài liệu bằng cách tính toán độ tương tự của sản phẩm (hoặc một số chỉ số tương tự khác) giữa mỗi biễu diễn và trả về các tài liệu có độ tương đồng lớn nhất.

Trong phần này, chúng ta sẽ sử dụng các biểu diễn từ để phát triển một công cụ tìm kiếm ngữ nghĩa. Các công cụ tìm kiếm này cung cấp một số lợi thế so với các phương pháp tiếp cận thông thường dựa trên việc kết hợp các từ khóa trong một truy vấn với các tài liệu.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/semantic-search.svg" alt="Semantic search."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/semantic-search-dark.svg" alt="Semantic search."/>
</div>

## Tải và chuẩn bị tập dữ liệu

Điều đầu tiên chúng ta cần làm là tải xuống tập dữ liệu về các sự cố GitHub, vì vậy hãy sử dụng thư viện 🤗 Hub để giải quyết URL nơi tệp của chúng ta được lưu trữ trên Hugging Face Hub:

```py
from huggingface_hub import hf_hub_url

data_files = hf_hub_url(
    repo_id="lewtun/github-issues",
    filename="datasets-issues-with-comments.jsonl",
    repo_type="dataset",
)
```

Với URL được lưu trữ trong `data_files`, sau đó chúng ta có thể tải tập dữ liệu từ xa bằng phương pháp đã được giới thiệu trong [phần 2](/course/chapter5/2):

```py
from datasets import load_dataset

issues_dataset = load_dataset("json", data_files=data_files, split="train")
issues_dataset
```

```python out
Dataset({
    features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
    num_rows: 2855
})
```

Ở đây chúng ta đã chỉ định tách `train` mặc định trong `load_dataset()`, vì vậy nó trả về một `Dataset` thay vì `DatasetDict`. Trình tự đầu tiên của doanh nghiệp là lọc ra các yêu cầu kéo, vì những yêu cầu này có xu hướng hiếm khi được sử dụng để trả lời các truy vấn của người dùng và sẽ tạo ra nhiễu trong công cụ tìm kiếm mình. Chúng ta có thể sử dụng hàm `Dataset.filter()` đã quen thuộc với bạn để loại trừ các hàng này trong tập dữ liệu của mình. Cùng lúc đó, hãy cùng lọc ra các hàng không có nhận xét, vì những hàng này không cung cấp câu trả lời cho các truy vấn của người dùng:

```py
issues_dataset = issues_dataset.filter(
    lambda x: (x["is_pull_request"] == False and len(x["comments"]) > 0)
)
issues_dataset
```

```python out
Dataset({
    features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
    num_rows: 771
})
```

Chúng ta có thể thấy rằng có rất nhiều cột trong tập dữ liệu của chúng ta, hầu hết trong số đó chúng ta không cần phải xây dựng công cụ tìm kiếm của mình. Từ góc độ tìm kiếm, các cột chứa nhiều thông tin nhất là `title`, `body`, và `comments`,  trong khi `html_url` cung cấp cho chúng ta một liên kết trỏ về nguồn. Hãy sử dụng hàm `Dataset.remove_columns()` để xóa phần còn lại:

```py
columns = issues_dataset.column_names
columns_to_keep = ["title", "body", "html_url", "comments"]
columns_to_remove = set(columns_to_keep).symmetric_difference(columns)
issues_dataset = issues_dataset.remove_columns(columns_to_remove)
issues_dataset
```

```python out
Dataset({
    features: ['html_url', 'title', 'comments', 'body'],
    num_rows: 771
})
```

Để tạo các biểu diễn, chúng ta sẽ bổ sung mỗi nhận xét với tiêu đề và nội dung của vấn đề, vì các trường này thường bao gồm thông tin ngữ cảnh hữu ích. Vì cột `comments` của hiện là danh sách các nhận xét cho từng vấn đề, chúng tôi cần khám phá các cột để mỗi hàng bao gồm một tuple `(html_url, title, body, comment)`. Trong Pandas, chúng ta có thể thực hiện việc này bằng hàm [hàm `DataFrame.explode()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html), tạo một hàng mới cho mỗi phần tử trong cột giống như danh sách, trong khi sao chép tất cả các giá trị cột khác. Để xem điều này hoạt động, trước tiên chúng ta hãy chúng chuyển thành định dạng Pandas `DataFrame`:

```py
issues_dataset.set_format("pandas")
df = issues_dataset[:]
```

Nếu ta kiểm tra hàng đầu tiên trong `DataFrame` này, chúng ta có thể thấy có bốn nhận xét liên quan đến vấn đề này:

```py
df["comments"][0].tolist()
```

```python out
['the bug code locate in ：\r\n    if data_args.task_name is not None:\r\n        # Downloading and loading a dataset from the hub.\r\n        datasets = load_dataset("glue", data_args.task_name, cache_dir=model_args.cache_dir)',
 'Hi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com\r\n\r\nNormally, it should work if you wait a little and then retry.\r\n\r\nCould you please confirm if the problem persists?',
 'cannot connect，even by Web browser，please check that  there is some  problems。',
 'I can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem...']
```

Khi chúng ta khám phá `df`, chúng tôi mong đợi nhận được một hàng cho mỗi nhận xét này. Hãy kiểm tra xem nó đã đúng chưa:

```py
comments_df = df.explode("comments", ignore_index=True)
comments_df.head(4)
```

<table border="1" class="dataframe" style="table-layout: fixed; word-wrap:break-word; width: 100%;">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>html_url</th>
      <th>title</th>
      <th>comments</th>
      <th>body</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>https://github.com/huggingface/datasets/issues/2787</td>
      <td>ConnectionError: Couldn't reach https://raw.githubusercontent.com</td>
      <td>the bug code locate in ：\r\n    if data_args.task_name is not None...</td>
      <td>Hello,\r\nI am trying to run run_glue.py and it gives me this error...</td>
    </tr>
    <tr>
      <th>1</th>
      <td>https://github.com/huggingface/datasets/issues/2787</td>
      <td>ConnectionError: Couldn't reach https://raw.githubusercontent.com</td>
      <td>Hi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com...</td>
      <td>Hello,\r\nI am trying to run run_glue.py and it gives me this error...</td>
    </tr>
    <tr>
      <th>2</th>
      <td>https://github.com/huggingface/datasets/issues/2787</td>
      <td>ConnectionError: Couldn't reach https://raw.githubusercontent.com</td>
      <td>cannot connect，even by Web browser，please check that  there is some  problems。</td>
      <td>Hello,\r\nI am trying to run run_glue.py and it gives me this error...</td>
    </tr>
    <tr>
      <th>3</th>
      <td>https://github.com/huggingface/datasets/issues/2787</td>
      <td>ConnectionError: Couldn't reach https://raw.githubusercontent.com</td>
      <td>I can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem...</td>
      <td>Hello,\r\nI am trying to run run_glue.py and it gives me this error...</td>
    </tr>
  </tbody>
</table>

Tuyệt vời, chúng ta có thể thấy các hàng đã được nhân rộng, với cột `comments` chứa các nhận xét riêng lẻ! Bây giờ chúng ta đã hoàn thành với Pandas, chúng ta có thể nhanh chóng chuyển trở lại `Dataset` bằng cách tải `DataFrame` vào bộ nhớ:

```py
from datasets import Dataset

comments_dataset = Dataset.from_pandas(comments_df)
comments_dataset
```

```python out
Dataset({
    features: ['html_url', 'title', 'comments', 'body'],
    num_rows: 2842
})
```

Được rồi, điều này đã cho chúng ta vài nghìn nhận xét để làm việc cùng!


<Tip>

✏️ **Thử nghiệm thôi!** Cùng xem liệu bạn có thể sử dụng `Dataset.map()` để khám phá cột `comments` của `issues_dataset` _mà không cần_ sử dụng Pandas hay không. Nó sẽ hơi khó khăn một chút; bạn có thể xem phần ["Batch mapping"](https://huggingface.co/docs/datasets/about_map_batch#batch-mapping) của tài liệu 🤗 Datasets, một tài liệu hữu ích cho tác vụ này.

</Tip>

Bây giờ chúng ta đã có một nhận xét trên mỗi hàng, hãy tạo một cột `comments_length` mới chứa số từ trên mỗi nhận xét:

```py
comments_dataset = comments_dataset.map(
    lambda x: {"comment_length": len(x["comments"].split())}
)
```

Chúng ta có thể sử dụng cột mới này để lọc ra các nhận xét ngắn, thường bao gồm những thứ như "cc @lewtun" hoặc "Thanks!" không liên quan đến công cụ tìm kiếm của mình. Không có con số chính xác để chọn cho bộ lọc, nhưng khoảng 15 từ có vẻ như là một khởi đầu tốt:

```py
comments_dataset = comments_dataset.filter(lambda x: x["comment_length"] > 15)
comments_dataset
```

```python out
Dataset({
    features: ['html_url', 'title', 'comments', 'body', 'comment_length'],
    num_rows: 2098
})
```

Sau khi dọn dẹp tập dữ liệu một chút, hãy ghép tiêu đề, mô tả và nhận xét của vấn đề với nhau trong một cột `text` mới. Như thường lệ, chúng ta sẽ viết một hàm đơn giản mà chúng ta có thể truyền vào `Dataset.map()`:

```py
def concatenate_text(examples):
    return {
        "text": examples["title"]
        + " \n "
        + examples["body"]
        + " \n "
        + examples["comments"]
    }


comments_dataset = comments_dataset.map(concatenate_text)
```

Cuối cùng thì chúng ta cũng đã sẵn sàng để tạo một số biểu điễn! Chúng ta hãy xem nào.

## Tạo ra biểu diễn văn bản

Chúng ta đã thấy trong [Chương 2](/course/chapter2) rằng ta có thể nhận được token biễu diễn nhúng bằng cách sử dụng lớp `AutoModel`. Tất cả những gì chúng ta cần làm là chọn một checkpoint phù hợp để tải mô hình từ đó. May mắn thay, có một thư viện tên là  `sentence-transformers` dành riêng cho việc tạo các biểu diễn này. Như được mô tả trong [tài liệu](https://www.sbert.net/examples/appices/semantic-search/README.html#symmetric-vs-asymmetric-semantic-search) của thư viện, trường hợp sử dụng của ta là một ví dụ về _tìm kiếm ngữ nghĩa phi đối xứng_ bởi vì chúng ta có một truy vấn ngắn có câu trả lời ta muốn tìm thấy trong một tài liệu lại dài hơn nhiều, chẳng hạn như một nhận xét về vấn đề. [Bảng tổng quan về mô hình](https://www.sbert.net/docs/pretrained_models.html#model-overview) trong phần tài liệu chỉ ra rằng checkpoint `multi-qa-mpnet-base-dot-v1` có hiệu suất tốt nhất cho tìm kiếm ngữ nghĩa, vì vậy chúng ta sẽ sử dụng nó cho ứng dụng của mình. Chúng ta cũng sẽ tải tokenizer bằng cách sử dụng cùng một checkpoint:

{#if fw === 'pt'}

```py
from transformers import AutoTokenizer, AutoModel

model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
model = AutoModel.from_pretrained(model_ckpt)
```

Để tăng tốc quá trình biểu diễn, ta sẽ giúp đặt mô hình và đầu vào trên thiết bị GPU, vì vậy hãy làm điều đó ngay bây giờ thôi:

```py
import torch

device = torch.device("cuda")
model.to(device)
```

{:else}

```py
from transformers import AutoTokenizer, TFAutoModel

model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
model = TFAutoModel.from_pretrained(model_ckpt, from_pt=True)
```

Lưu ý rằng chúng ta đặt `from_pt=True` như một tham số của phương thức `from_pretrained()`. Điều này là bởi checkpoint `multi-qa-mpnet-base-dot-v1` chỉ có trọng số Pytorch, vì vậy thiết lập `from_pt=True` sẽ tự động chuyển chúng về định dạng TensorFlow cho chúng ta. Như có thể thấy, nó rất đơn giản để chuyển giữa hai khung này trong 🤗 Transformers!

{/if}

Như đã đề cập trước đó, chúng ta muốn biểu diễn mỗi mục trong kho dữ liệu các vấn đề GitHub của mình dưới dạng một vectơ duy nhất, vì vậy chúng ta cần "gộp" hoặc tính trung bình các lần biễu diễn token theo một cách nào đó. Một cách tiếp cận phổ biến là thực hiện *CLS pooling* trên đầu ra của mô hình, nơi ta chỉ cần thu thập trạng thái ẩn cuối cùng cho token đặc biệt `[CLS]`. Hàm sau thực hiện thủ thuật này cho chúng ta:

```py
def cls_pooling(model_output):
    return model_output.last_hidden_state[:, 0]
```

Tiếp theo, chúng tôi sẽ tạo một chức năng trợ giúp sẽ tokanize danh sách các tài liệu, đặt các tensor trên GPU, đưa chúng vào mô hình và cuối cùng áp dụng CLS gộp cho các đầu ra:

{#if fw === 'pt'}

```py
def get_embeddings(text_list):
    encoded_input = tokenizer(
        text_list, padding=True, truncation=True, return_tensors="pt"
    )
    encoded_input = {k: v.to(device) for k, v in encoded_input.items()}
    model_output = model(**encoded_input)
    return cls_pooling(model_output)
```

Chúng ta có thể kiểm tra chức năng hoạt động bằng cách cung cấp cho nó đoạn văn bản đầu tiên trong kho tài liệu và kiểm tra hình dạng đầu ra:

```py
embedding = get_embeddings(comments_dataset["text"][0])
embedding.shape
```

```python out
torch.Size([1, 768])
```

Tuyệt vời, chúng ta đã chuyển đổi mục nhập đầu tiên trong kho tài liệu của mình thành một vectơ 768 chiều! Chúng ta có thể sử dụng `Dataset.map()` để áp dụng hàm `get_embeddings()` cho mỗi hàng trong kho tài liệu của mình, vì vậy hãy tạo một cột `embeddings` mới như sau:

```py
embeddings_dataset = comments_dataset.map(
    lambda x: {"embeddings": get_embeddings(x["text"]).detach().cpu().numpy()[0]}
)
```

{:else}

```py
def get_embeddings(text_list):
    encoded_input = tokenizer(
        text_list, padding=True, truncation=True, return_tensors="tf"
    )
    encoded_input = {k: v for k, v in encoded_input.items()}
    model_output = model(**encoded_input)
    return cls_pooling(model_output)
```

Chúng tôi có thể kiểm tra hàm có hoạt động không bằng cách cung cấp cho nó văn bản đầu tiên trong kho tài liệu và kiểm tra hình dạng đầu ra:

```py
embedding = get_embeddings(comments_dataset["text"][0])
embedding.shape
```

```python out
TensorShape([1, 768])
```

Tuyệt vời, chúng ta đã chuyển đổi mục nhập đầu tiên trong kho tài liệu của mình thành một vectơ 768 chiều! Chúng ta có thể sử dụng `Dataset.map()` để áp dụng hàm `get_embeddings()` cho mỗi hàng trong kho tài liệu của mình, vì vậy hãy tạo một cột `embeddings` mới như sau:

```py
embeddings_dataset = comments_dataset.map(
    lambda x: {"embeddings": get_embeddings(x["text"]).numpy()[0]}
)
```

{/if}

Lưu ý rằng chúng ta đã chuyển đổi các biểu diễn sang thành mảng NumPy - đó là vì 🤗 Datasets yêu cầu định dạng này khi ta cố gắng lập chỉ mục chúng bằng FAISS, điều mà ta sẽ thực hiện tiếp theo.

## Sử dụng FAISS để tìm kiếm điểm tương đồng hiệu quả

Bây giờ chúng ta đã có một tập dữ liệu về các biểu diễn, chúng ta cần một số cách để tìm kiếm chúng. Để làm điều này, chúng ta sẽ sử dụng một cấu trúc dữ liệu đặc biệt trong 🤗 Datasets được gọi là _FAISS index_. [FAISS](https://faiss.ai/) (viết tắt của Facebook AI Similarity Search) là một thư viện cung cấp các thuật toán hiệu quả để nhanh chóng tìm kiếm và phân cụm các vectơ nhúng biểu diễn.

Ý tưởng cơ bản đằng sau FAISS là tạo ra một cấu trúc dữ liệu đặc biệt được gọi là _index_ hay _chỉ mục_ cho phép người ta tìm thấy các biểu diễn nhúng nào tương tự như biểu diễn nhúng đầu vào. Tạo chỉ mục FAISS trong 🤗 Datasets rất đơn giản - ta sử dụng hàm `Dataset.add_faiss_index()` và chỉ định cột nào trong tập dữ liệu mà ta muốn lập chỉ mục:

```py
embeddings_dataset.add_faiss_index(column="embeddings")
```

Bây giờ chúng ta có thể thực hiện các truy vấn trên chỉ mục này bằng cách thực hiện tra cứu những mẫu lân cận nhất thông qua hàm `Dataset.get_nearest_examples()`. Hãy kiểm tra điều này bằng cách biểu diễn một câu hỏi như sau:

{#if fw === 'pt'}

```py
question = "How can I load a dataset offline?"
question_embedding = get_embeddings([question]).cpu().detach().numpy()
question_embedding.shape
```

```python out
torch.Size([1, 768])
```

{:else}

```py
question = "How can I load a dataset offline?"
question_embedding = get_embeddings([question]).numpy()
question_embedding.shape
```

```python out
(1, 768)
```

{/if}

Cũng giống như các tài liệu, giờ đây chúng ta có một vectơ 768 chiều đại diện cho truy vấn, mà chúng ta có thể so sánh với toàn bộ kho dữ liệu để tìm ra các cách biểu diễn tương tự nhất:

```py
scores, samples = embeddings_dataset.get_nearest_examples(
    "embeddings", question_embedding, k=5
)
```

Hàm `Dataset.get_nearest_examples()` trả về một loạt điểm xếp hạng sự tương đồng giữa truy vấn và tài liệu và một tập hợp các mẫu tương ứng (ở đây, là 5 kết quả phù hợp nhất). Hãy thu thập những thứ này vào một `pandas.DataFrame` để chúng ta có thể dễ dàng sắp xếp chúng:

```py
import pandas as pd

samples_df = pd.DataFrame.from_dict(samples)
samples_df["scores"] = scores
samples_df.sort_values("scores", ascending=False, inplace=True)
```

Bây giờ chúng ta có thể lặp lại một vài hàng đầu tiên để xem truy vấn của chúng ta khớp với các nhận xét có sẵn như thế nào:

```py
for _, row in samples_df.iterrows():
    print(f"COMMENT: {row.comments}")
    print(f"SCORE: {row.scores}")
    print(f"TITLE: {row.title}")
    print(f"URL: {row.html_url}")
    print("=" * 50)
    print()
```

```python out
"""
COMMENT: Requiring online connection is a deal breaker in some cases unfortunately so it'd be great if offline mode is added similar to how `transformers` loads models offline fine.

@mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like?
SCORE: 25.505046844482422
TITLE: Discussion using datasets in offline mode
URL: https://github.com/huggingface/datasets/issues/824
==================================================

COMMENT: The local dataset builders (csv, text , json and pandas) are now part of the `datasets` package since #1726 :)
You can now use them offline
\`\`\`python
datasets = load_dataset("text", data_files=data_files)
\`\`\`

We'll do a new release soon
SCORE: 24.555509567260742
TITLE: Discussion using datasets in offline mode
URL: https://github.com/huggingface/datasets/issues/824
==================================================

COMMENT: I opened a PR that allows to reload modules that have already been loaded once even if there's no internet.

Let me know if you know other ways that can make the offline mode experience better. I'd be happy to add them :)

I already note the "freeze" modules option, to prevent local modules updates. It would be a cool feature.

----------

> @mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like?

Indeed `load_dataset` allows to load remote dataset script (squad, glue, etc.) but also you own local ones.
For example if you have a dataset script at `./my_dataset/my_dataset.py` then you can do
\`\`\`python
load_dataset("./my_dataset")
\`\`\`
and the dataset script will generate your dataset once and for all.

----------

About I'm looking into having `csv`, `json`, `text`, `pandas` dataset builders already included in the `datasets` package, so that they are available offline by default, as opposed to the other datasets that require the script to be downloaded.
cf #1724
SCORE: 24.14896583557129
TITLE: Discussion using datasets in offline mode
URL: https://github.com/huggingface/datasets/issues/824
==================================================

COMMENT: > here is my way to load a dataset offline, but it **requires** an online machine
>
> 1. (online machine)
>
> ```
>
> import datasets
>
> data = datasets.load_dataset(...)
>
> data.save_to_disk(/YOUR/DATASET/DIR)
>
> ```
>
> 2. copy the dir from online to the offline machine
>
> 3. (offline machine)
>
> ```
>
> import datasets
>
> data = datasets.load_from_disk(/SAVED/DATA/DIR)
>
> ```
>
>
>
> HTH.


SCORE: 22.893993377685547
TITLE: Discussion using datasets in offline mode
URL: https://github.com/huggingface/datasets/issues/824
==================================================

COMMENT: here is my way to load a dataset offline, but it **requires** an online machine
1. (online machine)
\`\`\`
import datasets
data = datasets.load_dataset(...)
data.save_to_disk(/YOUR/DATASET/DIR)
\`\`\`
2. copy the dir from online to the offline machine
3. (offline machine)
\`\`\`
import datasets
data = datasets.load_from_disk(/SAVED/DATA/DIR)
\`\`\`

HTH.
SCORE: 22.406635284423828
TITLE: Discussion using datasets in offline mode
URL: https://github.com/huggingface/datasets/issues/824
==================================================
"""
```

Không tệ! Lần truy cập thứ hai của chúng ta dường như phù hợp với truy vấn.

<Tip>

✏️ **Thử nghiệm thôi!** Tạo truy vấn của riêng bạn và xem liệu bạn có thể tìm thấy câu trả lời trong các tài liệu đã truy xuất hay không. Bạn có thể phải tăng tham số `k` trong `Dataset.get_nearest_examples()` để mở rộng tìm kiếm.

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter5/6.mdx" />

### Đố vui cuối chương
https://huggingface.co/learn/course/vi/chapter5/8.md

# Đố vui cuối chương

<CourseFloatingBanner
    chapter={5}
    classNames="absolute z-10 right-0 top-0"
/>

Chương này bao gồm rất nhiều nội dung! Đừng lo lắng nếu bạn không nắm được tất cả các chi tiết; các chương tiếp theo sẽ giúp bạn hiểu mọi thứ hoạt động như thế nào.

Tuy nhiên, trước khi tiếp tục, hãy kiểm tra những gì bạn đã học được trong chương này.

### 1. Hàm `load_dataset()` trong 🤗 Datasets cho phép bạn tải tập dữ liệu từ vị trí nào sau đây?

<Question
  choices={[
    {
      text: "Cục bộ, ví dụ: trên máy tính xách tay của bạn",
      explain:
        "Chính xác! Bạn có thể truyền đường dẫn của các tệp cục bộ tới tham số <code>data_files</code> của <code>load_dataset()</code> để tải các tập dữ liệu cục bộ.",
      correct: true,
    },
    {
      text: "The Hugging Face Hub",
      explain:
        "Đúng! Bạn có thể tải tập dữ liệu trên Hub bằng cách cung cấp ID tập dữ liệu, ví dụ: <code>load_dataset('emotion')</code>.",
      correct: true,
    },
    {
      text: "Máy chủ từ xa",
      explain:
        "Đúng! Bạn có thể truyền URL đến tham số <code>data_files</code> của <code>load_dataset()</code> để tải các tệp từ xa.",
      correct: true,
    },
  ]}
/>

### 2. Giả sử bạn đã tải một trong số các tác vụ GLUE như sau:

```py
from datasets import load_dataset

dataset = load_dataset("glue", "mrpc", split="train")
```

Đâu là một trong số những câu lệnh sẽ tạo ra một tập mẫu ngẫu nhiên 50 phần tử từ `dataset`?

<Question
  choices={[
    {
      text: "<code>dataset.sample(50)</code>",
      explain:
        "Điều này không chính xác - không có phương thức <code>Dataset.sample()</code>.",
    },
    {
      text: "<code>dataset.shuffle().select(range(50))</code>",
      explain:
        "Chính xác! Như bạn đã thấy trong chương này, trước tiên bạn xáo trộn tập dữ liệu và sau đó chọn các mẫu từ nó.",
      correct: true,
    },
    {
      text: "<code>dataset.select(range(50)).shuffle()</code>",
      explain:
        "Điều này không chính xác - mặc dù đoạn mã sẽ chạy, nó sẽ chỉ xáo trộn 50 phần tử đầu tiên trong tập dữ liệu.",
    },
  ]}
/>

### 3. Giả sử bạn có một tập dữ liệu về vật nuôi trong nhà được gọi là `pets_dataset`, có cột `name` biểu thị tên của từng vật nuôi. Phương pháp tiếp cận nào sau đây sẽ cho phép bạn lọc tập dữ liệu cho tất cả vật nuôi có tên bắt đầu bằng chữ cái "L"?

<Question
  choices={[
    {
      text: "<code>pets_dataset.filter(lambda x : x['name'].startswith('L'))</code>",
      explain:
        "Đúng! Sử dụng hàm lambda của Python cho các bộ lọc nhanh này là một ý tưởng tuyệt vời. Bạn có thể nghĩ ra giải pháp khác không?",
      correct: true,
    },
    {
      text: "<code>pets_dataset.filter(lambda x['name'].startswith('L'))</code>",
      explain:
        "Điều này không chính xác - một hàm lambda có dạng chung <code>lambda *arguments* : *expression*</code>, vì vậy bạn cần cung cấp các tham số trong trường hợp này.",
    },
    {
      text: "Tạo ra một hàm <code>def filter_names(x): return x['name'].startswith('L')</code> và chạy <code>pets_dataset.filter(filter_names)</code>.",
      explain:
        "Chính xác! Cũng giống như với <code>Dataset.map()</code>, bạn có thể truyền các hàm tường minh<code>Dataset.filter()</code>. Điều này rất hữu ích khi bạn có một số logic phức tạp không phù hợp với một hàm lambda ngắn. Giải pháp nào khác sẽ hiệu quả?",
      correct: true,
    },
  ]}
/>

### 4. Ánh xạ bộ nhớ là gì?

<Question
  choices={[
    {
      text: "Ánh xạ giữa RAM CPU và GPU",
      explain: "Không phải vậy - hãy thử lại!",
    },
    {
      text: "Ánh xạ giữa RAM và bộ nhớ hệ thống tệp",
      explain:
        "Đúng! 🤗 Datasets coi mỗi tập dữ liệu như một tệp ánh xạ bộ nhớ. Điều này cho phép thư viện truy cập và hoạt động trên các phần tử của tập dữ liệu mà không cần tải đầy đủ vào bộ nhớ.",
      correct: true,
    },
    {
      text: "Ánh xạ giữa hai tệp trong cache 🤗 Datasets",
      explain: "Không phải vậy - hãy thử lại!",
    },
  ]}
/>

### 5. Lợi ích chính của ánh xạ bộ nhớ là gì?

<Question
  choices={[
    {
      text: "Truy cập tệp ánh xạ bộ nhớ nhanh hơn đọc hoặc ghi vào đĩa.",
      explain:
        "Đúng! Điều này cho phép 🤗 Datasets xử lý nhanh như chớp. Tuy nhiên, đó không phải là lợi ích duy nhất.",
      correct: true,
    },
    {
      text: "Các ứng dụng có thể truy cập các phân đoạn dữ liệu trong một tệp cực lớn mà không cần phải đọc toàn bộ tệp vào RAM trước.",
      explain:
        "Đúng! Điều này cho phép 🤗 Datasets tải tập dữ liệu nhiều gigabyte trên máy tính xách tay của bạn mà không làm nổ CPU của bạn. Ánh xạ bộ nhớ còn mang lại lợi thế nào khác không?",
      correct: true,
    },
    {
      text: "Nó tiêu thụ ít năng lượng hơn, vì vậy pin của bạn dùng được lâu hơn.",
      explain: "Điều này không chính xác - hãy thử lại!",
    },
  ]}
/>

### 6. Tại sao đoạn mã sau không thành công?

```py
from datasets import load_dataset

dataset = load_dataset("allocine", streaming=True, split="train")
dataset[0]
```

<Question
  choices={[
    {
      text: "Nó cố gắng phát trực tuyến tập dữ liệu quá lớn để vừa với RAM.",
      explain:
        "Điều này không chính xác - tập dữ liệu phát trực tuyến được giải nén nhanh chóng và bạn có thể xử lý tập dữ liệu có kích thước terabyte với rất ít RAM!",
    },
    {
      text: "Nó cố gắng truy cập một <code>IterableDataset</code>.",
      explain:
        "Đúng! Một <code>IterableDataset</code> là một trình tạo, không phải là một vùng chứa, nên bạn có thể truy cập các phần tử của nó sử dụng <code>next(iter(dataset))</code>.",
      correct: true,
    },
    {
      text: "Tập dữ liệu <code>allocine</code> không có phần tách huấn luyện (<code>train</code> split).",
      explain:
        "Không chính xác -- tham khảo [thẻ dữ liệu <code>allocine</code>](https://huggingface.co/datasets/allocine) trên Hub để xem có những phần tách dữ liệu nào.",
    },
  ]}
/>

### 7. Lợi ích chính của việc tạo thẻ tập dữ liệu là gì?

<Question
  choices={[
    {
      text: "Nó cung cấp thông tin về mục đích sử dụng và các tác vụ được hỗ trợ của tập dữ liệu để những người khác trong cộng đồng có thể đưa ra quyết định sáng suốt về việc sử dụng nó.",
      explain:
        "Đúng! Tập dữ liệu không có tài liệu có thể được sử dụng để huấn luyện các mô hình có thể không phản ánh ý định của người tạo tập dữ liệu hoặc có thể tạo ra các mô hình có tình trạng pháp lý không rõ ràng nếu chúng được huấn luyện về dữ liệu vi phạm quyền riêng tư hoặc hạn chế cấp phép. Tuy nhiên, đó không phải là lợi ích duy nhất!",
      correct: true,
    },
    {
      text: "Nó giúp thu hút sự chú ý đến những sai lệch ​​có trong ngữ liệu.",
      explain:
        "Đúng! Hầu hết tất cả các tập dữ liệu đều có một số dạng sai lệch, có thể tạo ra hậu quả tiêu cực. Nhận thức được chúng sẽ giúp người xây dựng mô hình hiểu cách giải quyết các thành kiến ​​cố hữu. Thẻ tập dữ liệu còn giúp được gì nữa không?",
      correct: true,
    },
    {
      text: "Nó cải thiện cơ hội mà những người khác trong cộng đồng sẽ sử dụng tập dữ liệu của mình.",
      explain:
        "Đúng! Một thẻ dữ liệu được viết tốt sẽ có xu hướng dẫn đến việc sử dụng tập dữ liệu quý giá của bạn cao hơn. Nó cung cấp những lợi ích nào khác không?",
      correct: true,
    },
  ]}
/>

### 8. Tìm kiếm ngữ nghĩa là gì?

<Question
  choices={[
    {
      text: "Một cách để tìm kiếm các kết quả khớp hoàn toàn giữa các từ trong truy vấn và các tài liệu trong kho ngữ liệu",
      explain:
        "Điều này không chính xác - loại tìm kiếm này được gọi là *tìm kiếm từ vựng* và đó là những gì bạn thường thấy với các công cụ tìm kiếm truyền thống.",
    },
    {
      text: "Một cách để tìm kiếm các tài liệu phù hợp bằng cách hiểu ý nghĩa ngữ cảnh của một truy vấn",
      explain:
        "Đúng! Tìm kiếm theo ngữ nghĩa sử dụng vectơ nhúng để đại diện cho các truy vấn và tài liệu, đồng thời sử dụng số liệu tương tự để đo mức độ trùng lặp giữa chúng. Bạn có thể mô tả nó như thế nào khác không?",
      correct: true,
    },
    {
      text: "Một cách để cải thiện độ chính xác của tìm kiếm",
      explain:
        "Đúng! Các công cụ tìm kiếm ngữ nghĩa có thể nắm bắt mục đích của một truy vấn tốt hơn nhiều so với đối sánh từ khóa và thường truy xuất tài liệu với độ chính xác cao hơn. Nhưng đây không phải là câu trả lời đúng duy nhất - tìm kiếm ngữ nghĩa còn cung cấp gì nữa?",
      correct: true,
    },
  ]}
/>

### 9. Đối với tìm kiếm ngữ nghĩa phi đối xứng, bạn thường có:

<Question
  choices={[
    {
      text: "Một truy vấn ngắn và một đoạn dài hơn trả lời cho truy vấn",
      explain: "Chính xác!",
      correct: true,
    },
    {
      text: "Truy vấn và đoạn văn có cùng độ dài",
      explain:
        "Đây thực sự là một ví dụ về tìm kiếm ngữ nghĩa đối xứng - hãy thử lại!",
    },
    {
      text: "Một truy vấn dài và một đoạn ngắn hơn trả lời cho truy vấn",
      explain: "Điều này không chính xác - hãy thử lại!",
    },
  ]}
/>

### 10. Tôi có thể sử dụng 🤗 Datasets để tải dữ liệu sử dụng cho các mảng khác như xử lý âm thanh được không?

<Question
  choices={[
    {
      text: "Không",
      explain:
        "Không chính xác -- 🤗 Datasets hiện có hỗ trợ dữ liệu dạng bảng, âm thanh, và hình ảnh. Xem thêm <a  href='https://huggingface.co/datasets/mnist'>dữ liệu MNIST</a> trên Hub cho ví dụ về xử lý hình ảnh.",
    },
    {
      text: "Có",
      explain:
        "Chính xác! Hãy xem những diễn biến thú vị với giọng nói và hình ảnh trong thư viện 🤗 Transformers để xem cách 🤗 Datasets được sử dụng như thế nào trong các lĩnh vực này.",
      correct: true,
    },
  ]}
/>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter5/8.mdx" />

### Dữ liệu lớn? 🤗 Bộ dữ liệu để giải cứu!
https://huggingface.co/learn/course/vi/chapter5/4.md

# Dữ liệu lớn? 🤗 Bộ dữ liệu để giải cứu!

<CourseFloatingBanner chapter={5}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {
      label: "Google Colab",
      value:
        "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter5/section4.ipynb",
    },
    {
      label: "Aws Studio",
      value:
        "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter5/section4.ipynb",
    },
  ]}
/>

Ngày nay, không có gì lạ khi bạn đang làm việc với các bộ dữ liệu nhiều gigabyte, đặc biệt nếu bạn đang có kế hoạch huấn luyện trước một mô hình Transformer như BERT hoặc GPT-2 từ đầu. Trong những trường hợp này, thậm chí _tải_ dữ liệu có thể là một thách thức. Ví dụ: kho dữ liệu WebText được sử dụng để huấn luyện trước GPT-2 bao gồm hơn 8 triệu tài liệu và 40 GB văn bản - việc tải dữ liệu này vào RAM của máy tính xách tay của bạn có thể khiến bạn bị đau tim!

May mắn thay, 🤗 Datasets đã được thiết kế để khắc phục những hạn chế này. Nó giải phóng bạn khỏi các vấn đề về quản lý bộ nhớ bằng cách coi các tập dữ liệu là tệp _ánh xạ bộ nhớ_ và thoát khỏi giới hạn ổ cứng bằng cách _truyền tải trực tiếp_ các mục trong một kho ngữ liệu.

<Youtube id="JwISwTCPPWo" />

Trong phần này, chúng ta sẽ khám phá các tính năng này của 🤗 Datasets với kho dữ liệu 825 GB khổng lồ được gọi là [Pile](https://pile.eleuther.ai). Bắt đầu thôi!

## Pile là gì?

The Pile là một kho ngữ liệu tiếng Anh được tạo ra bởi [EleutherAI](https://www.eleuther.ai) để huấn luyện các mô hình ngôn ngữ quy mô lớn. Nó bao gồm một loạt các bộ dữ liệu, các bài báo khoa học trải dài, kho mã GitHub và văn bản web được lọc. Kho tài liệu huấn luyện có sẵn trong [khối 14GB](https://the-eye.eu/public/AI/pile/) và bạn cũng có thể tải xuống một số [thành phần riêng lẻ](https://the-eye.eu/public/AI/pile_preliminary_components/). Hãy bắt đầu bằng cách xem qua tập dữ liệu PubMed Abstracts, tập dữ liệu tóm tắt từ 15 triệu ấn phẩm y sinh trên [PubMed](https://pubmed.ncbi.nlm.nih.gov/). Tập dữ liệu ở [định dạng JSON Lines](https://jsonlines.org) và được nén bằng thư viện `zstandard`, vì vậy trước tiên chúng ta cần cài đặt:

```py
!pip install zstandard
```

Tiếp theo, chúng ta có thể tải tập dữ liệu bằng phương pháp cho các tệp từ xa mà chúng ta đã học trong [phần 2](/course/chapter5/2):

```py
from datasets import load_dataset

# Quá trình này mất một vài phút để chạy, vì vậy hãy làm cốc trà hoặc cà phê trong khi chờ đợi :)
data_files = "https://the-eye.eu/public/AI/pile_preliminary_components/PUBMED_title_abstracts_2019_baseline.jsonl.zst"
pubmed_dataset = load_dataset("json", data_files=data_files, split="train")
pubmed_dataset
```

```python out
Dataset({
    features: ['meta', 'text'],
    num_rows: 15518009
})
```

Chúng ta có thể thấy rằng có 15,518,009 hàng và 2 cột trong tập dữ liệu của chúng tôi - đó là rất nhiều!

<Tip>

✎ Theo mặc định, 🤗 Datasets sẽ giải nén các tệp cần thiết để tải tập dữ liệu. Nếu bạn muốn bảo toàn dung lượng ổ cứng, bạn có thể truyền `DownloadConfig(delete_extracted=True)` vào tham số `download_config` của `load_dataset()`. Xem [tài liệu](https://huggingface.co/docs/datasets/package_reference/builder_classes#datasets.DownloadConfig) để biết thêm chi tiết.

</Tip>

Hãy kiểm tra nội dung của mẫu đầu tiên:

```py
pubmed_dataset[0]
```

```python out
{'meta': {'pmid': 11409574, 'language': 'eng'},
 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'}
```

Được rồi, đây giống như phần tóm tắt từ một bài báo y khoa. Bây giờ chúng ta hãy xem chúng ta đã sử dụng bao nhiêu RAM để tải tập dữ liệu!

## Sự kỳ diệu của ánh xạ bộ nhớ

Một cách đơn giản để đo mức sử dụng bộ nhớ trong Python là sử dụng thư viện [`psutil`](https://psutil.readthedocs.io/en/latest/), có thể được cài đặt bằng `pip` như sau:

```python
!pip install psutil
```

Nó cung cấp một lớp `Process` cho phép chúng ta kiểm tra việc sử dụng bộ nhớ của tiến trình hiện tại như sau:

```py
import psutil

# Process.memory_info được biểu thị bằng bytes, sau đó chuyển sang megabytes
print(f"RAM used: {psutil.Process().memory_info().rss / (1024 * 1024):.2f} MB")
```

```python out
RAM used: 5678.33 MB
```

Ở đây thuộc tính `rss` đề cập đến _resident set size_, là phần bộ nhớ mà một tiến trình chiếm trong RAM. Phép đo này cũng bao gồm bộ nhớ được sử dụng bởi trình thông dịch Python và các thư viện mà chúng tôi đã tải, do đó, lượng bộ nhớ thực tế được sử dụng để tải tập dữ liệu nhỏ hơn một chút. Để so sánh, hãy xem tập dữ liệu trên đĩa lớn như thế nào, sử dụng thuộc tính `dataset_size`. Vì kết quả được thể hiện bằng byte như trước đây, chúng tôi cần chuyển đổi thủ công nó thành gigabyte:

```py
print(f"Number of files in dataset : {pubmed_dataset.dataset_size}")
size_gb = pubmed_dataset.dataset_size / (1024**3)
print(f"Dataset size (cache file) : {size_gb:.2f} GB")
```

```python out
Number of files in dataset : 20979437051
Dataset size (cache file) : 19.54 GB
```

Tuyệt vời - mặc dù nó gần 20 GB, chúng ta có thể tải và truy cập tập dữ liệu với RAM ít hơn nhiều!

<Tip>

✏️ **Thử nghiệm thôi!** Chọn một trong các [tập hợp con](https://the-eye.eu/public/AI/pile_preliminary_components/) từ Pile sao cho lớn hơn RAM của máy tính xách tay hoặc máy tính để bàn của bạn, tải nó với 🤗 Datasets, và đo dung lượng RAM được sử dụng. Lưu ý rằng để có được một phép đo chính xác, bạn sẽ muốn thực hiện việc này trong một quy trình mới. Bạn có thể tìm thấy các kích thước đã giải nén của từng tập hợp con trong Bảng 1 của [bài báo về Pile](https://arxiv.org/abs/2101.00027).

</Tip>

Nếu bạn đã quen thuộc với Pandas, kết quả này có thể gây bất ngờ vì theo [quy tắc ngón tay cái](https://wesmckinney.com/blog/apache-arrow-pandas-internals/) nổi tiếng của Wes Kinney, bạn thường cần gấp 5 gấp 10 lần RAM so với kích thước của tập dữ liệu của bạn. Vậy 🤗 Datasets giải quyết vấn đề quản lý bộ nhớ này như thế nào? 🤗 Datasets coi mỗi tập dữ liệu như một [tệp ánh xạ bộ nhớ](https://en.wikipedia.org/wiki/Memory-mapped_file), cung cấp ánh xạ giữa RAM và bộ nhớ hệ thống tệp cho phép thư viện truy cập và hoạt động trên các phần tử của tập dữ liệu mà không cần tải đầy đủ vào bộ nhớ.

Các tệp được ánh xạ bộ nhớ cũng có thể được chia sẻ trên nhiều quy trình, cho phép các phương thức như `Dataset.map()` được thực thi song song mà không cần di chuyển hoặc sao chép tập dữ liệu. Bên cạnh đó, tất cả các khả năng này đều được thực hiện bởi định dạng bộ nhớ [Apache Arrow](https://arrow.apache.org) và thư viện[`pyarrow`](https://arrow.apache.org/docs/python/index.html), giúp tải và xử lý dữ liệu nhanh như chớp. (Để biết thêm chi tiết về Apache Arrow và so sánh với Pandas, hãy xem [Bài đăng trên blog của Dejan Simic](https://towardsdatascience.com/apache-arrow-read-dataframe-with-zero-memory-69634092b1a).) trong thực tế, hãy chạy một bài kiểm tra tốc độ nhỏ bằng cách lặp lại tất cả các phần tử trong tập dữ liệu PubMed Abstracts:

```py
import timeit

code_snippet = """batch_size = 1000

for idx in range(0, len(pubmed_dataset), batch_size):
    _ = pubmed_dataset[idx:idx + batch_size]
"""

time = timeit.timeit(stmt=code_snippet, number=1, globals=globals())
print(
    f"Iterated over {len(pubmed_dataset)} examples (about {size_gb:.1f} GB) in "
    f"{time:.1f}s, i.e. {size_gb/time:.3f} GB/s"
)
```

```python out
'Iterated over 15518009 examples (about 19.5 GB) in 64.2s, i.e. 0.304 GB/s'
```

Ở đây chúng ta đã sử dụng mô-đun `timeit` của Python để đo thời gian thực thi được thực hiện bởi `code_snippet`. Thông thường, bạn sẽ có thể lặp lại tập dữ liệu với tốc độ từ vài phần mười GB/s đến vài GB/s. Điều này hoạt động hiệu quả với đại đa số các ứng dụng, nhưng đôi khi bạn sẽ phải làm việc với một tập dữ liệu quá lớn, thậm chí không thể lưu trữ trên ổ cứng của máy tính xách tay của bạn. Ví dụ: nếu chúng tôi cố gắng tải xuống toàn bộ Pile, chúng tôi sẽ cần 825 GB dung lượng đĩa trống! Để xử lý những trường hợp này, 🤗 Datasets cung cấp tính năng phát trực tuyến cho phép chúng tôi tải xuống và truy cập các phần tử một cách nhanh chóng mà không cần tải xuống toàn bộ tập dữ liệu. Chúng ta hãy xem cách này hoạt động như thế nào.

<Tip>

💡 Trong sổ ghi chép Jupyter, bạn có thể định thời gian cho các ô bằng cách sử dụng[hàm ma thuật `%%timeit`](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit).

</Tip>

## Truyền trực tuyến tập dữ liệu

Để bật tính năng phát trực tuyến tập dữ liệu, bạn chỉ cần truyền tham số `streaming=True` vào hàm `load_dataset()`. Ví dụ: hãy tải lại tập dữ liệu PubMed Abstracts, nhưng ở chế độ phát trực tuyến:

```py
pubmed_dataset_streamed = load_dataset(
    "json", data_files=data_files, split="train", streaming=True
)
```

Thay vì `Dataset` quen thuộc mà chúng ta đã gặp ở những nơi khác trong chương này, đối tượng được trả về với `streaming=True` là một `IterableDataset`. Như cái tên cho thấy, để truy cập các phần tử của một `IterableDataset`, chúng ta cần phải lặp lại nó. Chúng tôi có thể truy cập phần tử đầu tiên của tập dữ liệu được phát trực tuyến của chúng tôi như sau:

```py
next(iter(pubmed_dataset_streamed))
```

```python out
{'meta': {'pmid': 11409574, 'language': 'eng'},
 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'}
```

Các phần tử từ một tập dữ liệu được truyền trực tuyến có thể được xử lý nhanh chóng bằng cách sử dụng `IterableDataset.map()`, rất hữu ích trong quá trình huấn luyện nếu bạn cần tokenize các đầu vào. Quy trình hoàn toàn giống với quy trình chúng ta đã sử dụng để tokenize tập dữ liệu của mình trong [Chương 3](/course/chapter3), với sự khác biệt duy nhất là các đầu ra được trả về từng cái một:

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
tokenized_dataset = pubmed_dataset_streamed.map(lambda x: tokenizer(x["text"]))
next(iter(tokenized_dataset))
```

```python out
{'input_ids': [101, 4958, 5178, 4328, 6779, ...], 'attention_mask': [1, 1, 1, 1, 1, ...]}
```

<Tip>

💡 Để tăng tốc độ trình tokenize với tính năng phát trực tuyến, bạn có thể vượt qua `batched=True`, như chúng ta đã thấy trong phần trước. Nó sẽ xử lý hàng loạt các ví dụ; kích thước lô mặc định là 1,000 và có thể được chỉ định bằng tham số `batch_size`.

</Tip>

Bạn cũng có thể xáo trộn một tập dữ liệu được phát trực tuyến bằng cách sử dụng `IterableDataset.shuffle()`, nhưng không giống như `Dataset.shuffle()` điều này chỉ xáo trộn các phần tử trong một `buffer_size` được định trước:

```py
shuffled_dataset = pubmed_dataset_streamed.shuffle(buffer_size=10_000, seed=42)
next(iter(shuffled_dataset))
```

```python out
{'meta': {'pmid': 11410799, 'language': 'eng'},
 'text': 'Randomized study of dose or schedule modification of granulocyte colony-stimulating factor in platinum-based chemotherapy for elderly patients with lung cancer ...'}
```

Trong ví dụ này, chúng ta đã chọn một mẫu ngẫu nhiên từ 10,000 mẫu đầu tiên trong bộ đệm. Khi một mẫu được truy cập, vị trí của nó trong bộ đệm sẽ được lấp đầy bằng ví dụ tiếp theo trong kho tài liệu (tức là ví dụ thứ 10,001 trong trường hợp trên). Bạn cũng có thể chọn các phần tử từ một tập dữ liệu được truyền trực tuyến bằng cách sử dụng các hàm `IterableDataset.take()` và `IterableDataset.skip()`, hoạt động theo cách tương tự như `Dataset.select()`. Ví dụ, để chọn 5 mẫu đầu tiên trong tập dữ liệu PubMed Abstracts, chúng ta có thể làm như sau:

```py
dataset_head = pubmed_dataset_streamed.take(5)
list(dataset_head)
```

```python out
[{'meta': {'pmid': 11409574, 'language': 'eng'},
  'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'},
 {'meta': {'pmid': 11409575, 'language': 'eng'},
  'text': 'Clinical signs of hypoxaemia in children with acute lower respiratory infection: indicators of oxygen therapy ...'},
 {'meta': {'pmid': 11409576, 'language': 'eng'},
  'text': "Hypoxaemia in children with severe pneumonia in Papua New Guinea ..."},
 {'meta': {'pmid': 11409577, 'language': 'eng'},
  'text': 'Oxygen concentrators and cylinders ...'},
 {'meta': {'pmid': 11409578, 'language': 'eng'},
  'text': 'Oxygen supply in rural africa: a personal experience ...'}]
```

Tương tự, bạn có thể sử dụng hàm `IterableDataset.skip()` để tạo các tập huấn luyện và kiểm định từ một tập dữ liệu xáo trộn như sau:

```py
# Bỏ qua 1,000 mẫu đầu tiên và đưa phần còn lại vào tập huấn luyện
train_dataset = shuffled_dataset.skip(1000)
# Lấy 1,000 ví dụ đầu tiên cho tập kiểm định
validation_dataset = shuffled_dataset.take(1000)
```

Hãy hoàn thành việc khám phá của chúng ta về việc truyền trực tuyến tập dữ liệu với một ứng dụng phổ biến: kết hợp nhiều tập dữ liệu với nhau để tạo ra một kho dữ liệu duy nhất. 🤗 Datasets cung cấp một hàm `interleave_datasets()` để chuyển đổi danh sách các đối tượng `IterableDataset` thành một `IterableDataset` duy nhất, trong đó các phần tử của tập dữ liệu mới được lấy bằng cách xen kẽ giữa các mẫu gốc. Hàm này đặc biệt hữu ích khi bạn đang cố gắng kết hợp các tập dữ liệu lớn, vì vậy, để làm ví dụ, hãy truyền trực tuyến tập con FreeLaw của Pile, là tập dữ liệu 51 GB về các ý kiến pháp lý từ các tòa án Hoa Kỳ:

```py
law_dataset_streamed = load_dataset(
    "json",
    data_files="https://the-eye.eu/public/AI/pile_preliminary_components/FreeLaw_Opinions.jsonl.zst",
    split="train",
    streaming=True,
)
next(iter(law_dataset_streamed))
```

```python out
{'meta': {'case_ID': '110921.json',
  'case_jurisdiction': 'scotus.tar.gz',
  'date_created': '2010-04-28T17:12:49Z'},
 'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}
```

Tập dữ liệu này đủ lớn để kích hoạt RAM của hầu hết các máy tính xách tay, nhưng chúng ta vẫn có thể tải và truy cập nó mà không phải đổ mồ hôi! Bây giờ chúng ta hãy kết hợp các mẫu từ bộ dữ liệu FreeLaw và PubMed Abstracts với hàm `interleave_datasets()`:

```py
from itertools import islice
from datasets import interleave_datasets

combined_dataset = interleave_datasets([pubmed_dataset_streamed, law_dataset_streamed])
list(islice(combined_dataset, 2))
```

```python out
[{'meta': {'pmid': 11409574, 'language': 'eng'},
  'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'},
 {'meta': {'case_ID': '110921.json',
   'case_jurisdiction': 'scotus.tar.gz',
   'date_created': '2010-04-28T17:12:49Z'},
  'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}]
```

Ở đây chúng ta đã sử dụng hàm `islice()` từ mô-đun `itertools` của Python để chọn hai mẫu đầu tiên từ tập dữ liệu kết hợp và chúng ta có thể thấy rằng chúng khớp với các ví dụ đầu tiên từ mỗi trong hai tập dữ liệu nguồn.

Cuối cùng, nếu bạn muốn phát trực tuyến toàn bộ 825 GB của Pile, bạn có thể lấy tất cả các tệp đã chuẩn bị như sau:

```py
base_url = "https://the-eye.eu/public/AI/pile/"
data_files = {
    "train": [base_url + "train/" + f"{idx:02d}.jsonl.zst" for idx in range(30)],
    "validation": base_url + "val.jsonl.zst",
    "test": base_url + "test.jsonl.zst",
}
pile_dataset = load_dataset("json", data_files=data_files, streaming=True)
next(iter(pile_dataset["train"]))
```

```python out
{'meta': {'pile_set_name': 'Pile-CC'},
 'text': 'It is done, and submitted. You can play “Survival of the Tastiest” on Android, and on the web...'}
```

<Tip>

✏️ **Thử nghiệm thôi!** Sử dụng một trong những kho tài liệu Common Crawl lớn như [`mc4`](https://huggingface.co/datasets/mc4) hoặc [`oscar`](https://huggingface.co/datasets/oscar) để tạo tập dữ liệu đa ngôn ngữ trực tuyến thể hiện tỷ lệ nói của các ngôn ngữ ở quốc gia bạn chọn. Ví dụ: bốn ngôn ngữ quốc gia ở Thụy Sĩ là tiếng Đức, tiếng Pháp, tiếng Ý và tiếng La Mã, vì vậy bạn có thể thử tạo một kho ngữ liệu tiếng Thụy Sĩ bằng cách lấy mẫu các tập hợp con Oscar theo tỷ lệ nói của chúng.

</Tip>

Giờ đây, bạn có tất cả các công cụ cần thiết để tải và xử lý các tập dữ liệu ở mọi hình dạng và kích thước - nhưng trừ khi bạn đặc biệt may mắn, sẽ đến một thời điểm trong hành trình NLP của bạn, nơi bạn sẽ phải thực sự tạo một tập dữ liệu để giải quyết vấn đề vấn đề trong tầm tay. Đó là chủ đề của phần tiếp theo!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter5/4.mdx" />

### 🤗 Datasets, kiểm tra nào!
https://huggingface.co/learn/course/vi/chapter5/7.md

# 🤗 Datasets, kiểm tra nào!

<CourseFloatingBanner
    chapter={5}
    classNames="absolute z-10 right-0 top-0"
/>

Chà, đó là một chuyến tham quan khá thú vị qua thư viện 🤗 Datasets - chúc mừng bạn đã đi được xa như vậy! Với kiến thức bạn đã thu được từ chương này, bạn sẽ có thể:

- Tải bộ dữ liệu từ bất kỳ đâu, có thể là Hugging Face Hub, máy tính xách tay của bạn hoặc máy chủ từ xa tại công ty của bạn.
- Xoá dữ liệu của bạn bằng cách sử dụng kết hợp các hàm `Dataset.map()` và `Dataset.filter()`.
- Chuyển đổi nhanh chóng giữa các định dạng dữ liệu như Pandas và NumPy bằng cách sử dụng `Dataset.set_format()`.
- Tạo tập dữ liệu của riêng bạn và đẩy nó vào Hugging Face Hub.
- Nhúng tài liệu của bạn bằng mô hình Transformer và xây dựng công cụ tìm kiếm ngữ nghĩa bằng FAISS.

Trong [Chương 7](/course/chapter7), chúng ta sẽ sử dụng tốt tất cả những điều này khi ta đi sâu vào các tác vụ NLP cốt lõi mà các mô hình Transformer rất phù hợp. Tuy nhiên, trước khi vượt lên phía trước, hãy đưa kiến thức của bạn về 🤗 Datasets vào một bài kiểm tra nhanh!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter5/7.mdx" />

### Nếu như dữ liệu của ta không trên Hub thì sao?
https://huggingface.co/learn/course/vi/chapter5/2.md

# Nếu như dữ liệu của ta không trên Hub thì sao?

<CourseFloatingBanner chapter={5}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter5/section2.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter5/section2.ipynb"},
]} />

Bạn biết cách sử dụng [Hugging Face Hub](https://huggingface.co/datasets) để tải xuống bộ dữ liệu, nhưng bạn sẽ thấy mình thường làm việc với dữ liệu được lưu trữ trên máy tính xách tay hoặc trên máy chủ từ xa. Trong phần này, chúng tôi sẽ chỉ cho bạn cách 🤗 Datasets có thể được sử dụng để tải các tập dữ liệu không có sẵn trên Hugging Face Hub.

<Youtube id="HyQgpJTkRdE"/>

## Làm việc với bộ dữ liệu cục bộ và từ xa

🤗 Datasets cung cấp các tập lệnh để xử lý việc tải các tập dữ liệu cục bộ và từ xa. Nó hỗ trợ một số định dạng dữ liệu phổ biến, chẳng hạn như:

|    Định dạng dữ liệu     | 
Tập lệnh |                         Ví dụ                         |
| :----------------: | :------------: | :-----------------------------------------------------: |
|     CSV & TSV      |     `csv`      |     `load_dataset("csv", data_files="my_file.csv")`     |
|     Text files     |     `text`     |    `load_dataset("text", data_files="my_file.txt")`     |
| JSON & JSON Lines  |     `json`     |   `load_dataset("json", data_files="my_file.jsonl")`    |
| Pickled DataFrames |    `pandas`    | `load_dataset("pandas", data_files="my_dataframe.pkl")` |

Như được hiển thị trong bảng, đối với mỗi định dạng dữ liệu, chúng ta chỉ cần chỉ định loại tập lệnh tải dữ liệu trong hàm `load_dataset()`, cùng với tham số `data_files` chỉ định đường dẫn đến một hoặc nhiều tệp. Hãy bắt đầu bằng cách tải một tập dữ liệu từ các tệp cục bộ; Sau đó, chúng ta sẽ xem cách thực hiện tương tự với các tệp từ xa.

## Tải tập dữ liệu cục bộ

Đối với ví dụ này, chúng ta sẽ sử dụng [bộ dữ liệu SQuAD-it](https://github.com/crux82/squad-it/), là một tập dữ liệu quy mô lớn cho tác vụ hỏi đáp bằng tiếng Ý.

Phần dữ liệu huấn luyện và kiểm thử được lưu trữ trên GitHub, vì vậy chúng tôi có thể tải chúng xuống bằng lệnh `wget` đơn giản:

```python
!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-train.json.gz
!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-test.json.gz
```

Thao tác này sẽ tải xuống hai tệp nén có tên *SQuAD_it-train.json.gz* và *SQuAD_it-test.json.gz*, chúng ta có thể giải nén bằng lệnh Linux `gzip`:

```python
!gzip -dkv SQuAD_it-*.json.gz
```

```bash
SQuAD_it-test.json.gz:	   87.4% -- replaced with SQuAD_it-test.json
SQuAD_it-train.json.gz:	   82.2% -- replaced with SQuAD_it-train.json
```

Chúng ta có thể thấy rằng các tệp nén đã được thay thế bằng _SQuAD_it-train.json_ và _SQuAD_it-text.json_, và dữ liệu được lưu trữ ở định dạng JSON.

<Tip>

✎ Nếu bạn đang thắc mắc tại sao lại có ký tự`!` trong các lệnh trên, đó là bởi vì chúng ta đang chạy chúng trong một sổ ghi chép Jupyter. Chỉ cần xóa tiền tố này nếu bạn muốn tải xuống và giải nén tập dữ liệu trên terminal.

</Tip>

Để tải tệp JSON bằng hàm `load_dataset()`, chúng ta chỉ cần biết liệu chúng ta đang xử lý JSON thông thường (tương tự như từ điển lồng nhau) hay JSON dòng (JSON được phân tách bằng dòng). Giống như nhiều bộ dữ liệu hỏi đáp, SQuAD-it sử dụng định dạng lồng nhau, với tất cả văn bản được lưu trữ trong trường `data`. Điều này có nghĩa là chúng ta có thể tải tập dữ liệu bằng cách chỉ định tham số `field` như sau:

```py
from datasets import load_dataset

squad_it_dataset = load_dataset("json", data_files="SQuAD_it-train.json", field="data")
```

Theo mặc định, việc tải các tệp cục bộ sẽ tạo ra một đối tượng `DatasetDict` với sự phân chia của `train`. Chúng ta có thể thấy điều này bằng cách kiểm tra đối tượng `squad_it_dataset`:

```py
squad_it_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['title', 'paragraphs'],
        num_rows: 442
    })
})
```

Điều này cho chúng ta thấy số hàng và cột được liên kết với tập huấn luyện. Chúng ta có thể xem một trong các ví dụ bằng cách lập chỉ mục vào phần tập `train` như sau:

```py
squad_it_dataset["train"][0]
```

```python out
{
    "title": "Terremoto del Sichuan del 2008",
    "paragraphs": [
        {
            "context": "Il terremoto del Sichuan del 2008 o il terremoto...",
            "qas": [
                {
                    "answers": [{"answer_start": 29, "text": "2008"}],
                    "id": "56cdca7862d2951400fa6826",
                    "question": "In quale anno si è verificato il terremoto nel Sichuan?",
                },
                ...
            ],
        },
        ...
    ],
}
```

Tuyệt, chúng ta đã tải tập dữ liệu cục bộ đầu tiên của mình! Nhưng trong khi điều này hoạt động cho tập huấn luyện, những gì chúng tôi thực sự muốn là bao gồm cả hai tập `train` và `test` trong một đối tượng `DatasetDict` duy nhất để ta có thể áp dụng `Dataset.map()` trên cả hai phần dữ liệu cùng một lúc. Để thực hiện việc này, chúng ta có thể cung cấp một từ điển cho tham số `data_files` ánh xạ từng tên phần dữ liệu với một tệp được liên kết với các phần đó:

```py
data_files = {"train": "SQuAD_it-train.json", "test": "SQuAD_it-test.json"}
squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
squad_it_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['title', 'paragraphs'],
        num_rows: 442
    })
    test: Dataset({
        features: ['title', 'paragraphs'],
        num_rows: 48
    })
})
```
Đây chính xác là những gì chúng ta muốn. Giờ đây, ta có thể áp dụng nhiều kỹ thuật tiền xử lý khác nhau để làm sạch dữ liệu, mã hóa các bài đánh giá, v.v.

<Tip>

Tham số `data_files` của hàm `load_dataset()` khá linh hoạt và có thể là một đường dẫn tệp duy nhất, danh sách các đường dẫn tệp hoặc từ điển ánh xạ các tên tách thành đường dẫn tệp. Bạn cũng có thể tập hợp các tệp phù hợp với một mẫu được chỉ định theo các quy tắc được sử dụng bởi Unix shell (ví dụ: bạn có thể tổng hợp tất cả các tệp JSON trong một thư mục dưới dạng một lần tách duy nhất bằng cách đặt `data_files="*.json"`). Xem [tài liệu](https://huggingface.co/docs/datasets/loading#local-and-remote-files) 🤗 Datasets để biết thêm chi tiết.

</Tip>

Các tập lệnh tải trong 🤗 Datasets thực sự hỗ trợ giải nén tự động các tệp đầu vào, vì vậy chúng ta có thể bỏ qua việc sử dụng `gzip` bằng cách trỏ trực tiếp tham số `data_files` vào các tệp nén:

```py
data_files = {"train": "SQuAD_it-train.json.gz", "test": "SQuAD_it-test.json.gz"}
squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
```

Điều này có thể hữu ích nếu bạn không muốn giải nén nhiều tệp GZIP theo cách thủ công. Tính năng giải nén tự động cũng áp dụng cho các định dạng phổ biến khác như ZIP và TAR, vì vậy bạn chỉ cần trỏ `data_files` đến các tệp nén và bạn đã sẵn sàng rồi!

Bây giờ bạn đã biết cách tải các tệp cục bộ trên máy tính xách tay hoặc máy tính để bàn của mình, hãy cùng xem cách tải các tệp từ xa.

## Tải tập dữ liệu từ xa

Nếu bạn đang làm việc với tư cách là nhà khoa học dữ liệu hoặc lập trình viên trong một công ty, thì rất có thể các bộ dữ liệu bạn muốn phân tích được lưu trữ trên một máy chủ từ xa nào đó. May mắn thay, việc tải các tệp từ xa cũng đơn giản như tải các tệp cục bộ! Thay vì cung cấp một đường dẫn đến các tệp cục bộ, chúng ta trỏ tham số `data_files` của `load_dataset()` đến một hoặc nhiều URL nơi các tệp từ xa được lưu trữ. Ví dụ: đối với tập dữ liệu SQuAD-it được lưu trữ trên GitHub, chúng ta chỉ cần trỏ `data_files` đến các URL _SQuAD_it-*.json.gz_ như sau:

```py
url = "https://github.com/crux82/squad-it/raw/master/"
data_files = {
    "train": url + "SQuAD_it-train.json.gz",
    "test": url + "SQuAD_it-test.json.gz",
}
squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
```

Điều này trả về cùng một đối tượng `DatasetDict` như ở trên, nhưng giúp ta tiết kiệm bước tải xuống và giải nén thủ công các tệp  _SQuAD_it-*.json.gz_. Điều này tổng kết bước đột phá của chúng ta vào các cách khác nhau để tải các tập dữ liệu không được lưu trữ trên Hugging Face Hub. Giờ ta đã có một tập dữ liệu để nghịch, hãy bắt tay vào các kỹ thuật sắp xếp dữ liệu khác nhau thôi!

<Tip>

✏️ **Thử nghiệm thôi!** Chọn một tập dữ liệu khác được lưu trữ trên GitHub hoặc [Kho lưu trữ Học Máy UCI](https://archive.ics.uci.edu/ml/index.php) và thử tải nó cả cục bộ và từ xa bằng cách sử dụng các kỹ thuật đã giới thiệu ở trên. Để có điểm thưởng, hãy thử tải tập dữ liệu được lưu trữ ở định dạng CSV hoặc dạng văn bản (xem [tài liệu](https://huggingface.co/docs/datasets/loading#local-and-remote-files) để biết thêm thông tin trên các định dạng này).

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter5/2.mdx" />

### Xử lý đa chuỗi
https://huggingface.co/learn/course/vi/chapter2/5.md

# Xử lý đa chuỗi

{#if fw === 'pt'}

<CourseFloatingBanner chapter={2}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter2/section5_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter2/section5_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={2}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter2/section5_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter2/section5_tf.ipynb"},
]} />

{/if}

{#if fw === 'pt'}
<Youtube id="M6adb1j2jPI"/>
{:else}
<Youtube id="ROxrFOEbsQE"/>
{/if}

Trong phần trước, chúng ta đã khám phá các trường hợp sử dụng đơn giản nhất: thực hiện luận suy trên một dãy đơn có độ dài nhỏ. Tuy nhiên, một số câu hỏi được đề cập như:

- Làm thế nào để chúng ta xử lý nhiều chuỗi?
- Làm thế nào để chúng ta xử lý nhiều chuỗi *có độ dài khác nhau*?
- Các chỉ số từ vựng có phải là đầu vào duy nhất cho phép một mô hình hoạt động tốt không?
- Nếu như một chuỗi quá dài thì sao?

Hãy xem những câu hỏi này đặt ra những loại vấn đề nào và cách chúng tôi có thể giải quyết chúng bằng cách sử dụng API 🤗 Transformers.

## Mô hình kì vọng một lô các đầu vào

Trong bài tập trước, bạn đã thấy cách các chuỗi được chuyển thành danh sách các số. Hãy chuyển đổi danh sách các số này thành một tensor và gửi nó đến mô hình:

{#if fw === 'pt'}
```py
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)
input_ids = torch.tensor(ids)
# This line will fail.
model(input_ids)
```

```python out
IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1)
```
{:else}
```py
import tensorflow as tf
from transformers import AutoTokenizer, TFAutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)
input_ids = tf.constant(ids)
# This line will fail.
model(input_ids)
```

```py out
InvalidArgumentError: Input to reshape is a tensor with 14 values, but the requested shape has 196 [Op:Reshape]
```
{/if}

Ôi không! Tại sao đoạn mã lại không thành công? Chúng ta đã làm theo các bước từ pipeline trong phần 2.

Vấn đề ở đây đó là chúng ta đã gửi một chuỗi đơn cho mô hình, trong khi mô hình 🤗 Transformers mong đợi nhiều câu theo mặc định. Ở đây, chúng ta đã cố gắng thực hiện mọi thứ mà tokenizer đã làm ở phía sau khi áp dụng nó vào một `chuỗi`, nhưng nếu bạn nhìn kỹ, bạn sẽ thấy rằng nó không chỉ chuyển đổi danh sách ID đầu vào thành một tensor, nó  còn thêm một chiều lên trên:

{#if fw === 'pt'}
```py
tokenized_inputs = tokenizer(sequence, return_tensors="pt")
print(tokenized_inputs["input_ids"])
```

```python out
tensor([[  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,
          2607,  2026,  2878,  2166,  1012,   102]])
```
{:else}
```py
tokenized_inputs = tokenizer(sequence, return_tensors="tf")
print(tokenized_inputs["input_ids"])
```

```py out
<tf.Tensor: shape=(1, 16), dtype=int32, numpy=
array([[  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662,
        12172,  2607,  2026,  2878,  2166,  1012,   102]], dtype=int32)>
```
{/if}

Hãy cũng thử lại và thêm một chiều mới:

{#if fw === 'pt'}
```py
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)

input_ids = torch.tensor([ids])
print("Input IDs:", input_ids)

output = model(input_ids)
print("Logits:", output.logits)
```
{:else}
```py
import tensorflow as tf
from transformers import AutoTokenizer, TFAutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)

input_ids = tf.constant([ids])
print("Input IDs:", input_ids)

output = model(input_ids)
print("Logits:", output.logits)
```
{/if}

Ta in ra các ID đầu vào cũng như kết quả logit như sau:

{#if fw === 'pt'}
```python out
Input IDs: [[ 1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,  2607, 2026,  2878,  2166,  1012]]
Logits: [[-2.7276,  2.8789]]
```
{:else}
```py out
Input IDs: tf.Tensor(
[[ 1045  1005  2310  2042  3403  2005  1037 17662 12172  2607  2026  2878
   2166  1012]], shape=(1, 14), dtype=int32)
Logits: tf.Tensor([[-2.7276208  2.8789377]], shape=(1, 2), dtype=float32)
```
{/if}

*Batching* hay *Lô* là hành động gửi nhiều câu qua mô hình, tất cả cùng một lúc. Nếu bạn chỉ có một câu, bạn chỉ có thể xây dựng một lô với một chuỗi duy nhất:

```
batched_ids = [ids, ids]
```

Đây là một lô chứa hai chuỗi giống nhau!

<Tip>

✏️ **Thử nghiệm thôi!** Chuyển đổi danh sách `batch_ids` này thành một tensor và chuyển nó qua mô hình của bạn. Kiểm tra để đảm bảo rằng bạn có được logit giống như trước đây (nhưng hai lần)!

</Tip>

Việc phân phối lô cho phép mô hình hoạt động khi bạn đưa vào nhiều câu. Việc sử dụng nhiều chuỗi cũng đơn giản như xây dựng một lô với một chuỗi duy nhất. Tuy nhiên, có một vấn đề thứ hai. Khi bạn cố gắng ghép hai (hoặc nhiều) câu lại với nhau, chúng có thể có độ dài khác nhau. Nếu bạn đã từng làm việc với tensor trước đây, bạn biết rằng chúng cần có dạng hình chữ nhật, vì vậy bạn sẽ không thể chuyển đổi trực tiếp danh sách ID đầu vào thành tensor. Để giải quyết vấn đề này, chúng tôi thường *đệm* các đầu vào.

## Đêm thêm vào đầu vào

Danh sách các danh sách dưới đây không thể chuyển đổi thành một tensor:

```py no-format
batched_ids = [
    [200, 200, 200],
    [200, 200]
]
```

Để giải quyết vấn đề này, chúng ta sẽ sử dụng *đệm* để làm cho các tensor của chúng ta có hình chữ nhật. Đệm đảm bảo tất cả các câu của chúng ta có cùng độ dài bằng cách thêm một từ đặc biệt được gọi là *padding token* hay *token được đệm thêm* vào các câu có ít giá trị hơn. Ví dụ: nếu bạn có 10 câu 10 từ và 1 câu 20 từ, phần đệm sẽ đảm bảo tất cả các câu có 20 từ. Trong ví dụ của chúng tôi, tensor kết quả trông giống như sau:

```py no-format
padding_id = 100

batched_ids = [
    [200, 200, 200],
    [200, 200, padding_id],
]
```

ID của token đệm có thể tìm thấy ở `tokenizer.pad_token_id`. Hãy sử dụng nó và gửi hai câu của chúng ta thông qua mô hình riêng lẻ và theo lô với nhau:

{#if fw === 'pt'}
```py no-format
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence1_ids = [[200, 200, 200]]
sequence2_ids = [[200, 200]]
batched_ids = [
    [200, 200, 200],
    [200, 200, tokenizer.pad_token_id],
]

print(model(torch.tensor(sequence1_ids)).logits)
print(model(torch.tensor(sequence2_ids)).logits)
print(model(torch.tensor(batched_ids)).logits)
```

```python out
tensor([[ 1.5694, -1.3895]], grad_fn=<AddmmBackward>)
tensor([[ 0.5803, -0.4125]], grad_fn=<AddmmBackward>)
tensor([[ 1.5694, -1.3895],
        [ 1.3373, -1.2163]], grad_fn=<AddmmBackward>)
```
{:else}
```py no-format
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence1_ids = [[200, 200, 200]]
sequence2_ids = [[200, 200]]
batched_ids = [
    [200, 200, 200],
    [200, 200, tokenizer.pad_token_id],
]

print(model(tf.constant(sequence1_ids)).logits)
print(model(tf.constant(sequence2_ids)).logits)
print(model(tf.constant(batched_ids)).logits)
```

```py out
tf.Tensor([[ 1.5693678 -1.3894581]], shape=(1, 2), dtype=float32)
tf.Tensor([[ 0.5803005  -0.41252428]], shape=(1, 2), dtype=float32)
tf.Tensor(
[[ 1.5693681 -1.3894582]
 [ 1.3373486 -1.2163193]], shape=(2, 2), dtype=float32)
```
{/if}

Có điều gì đó không ổn với các logit trong các dự đoán theo lô của chúng ta: hàng thứ hai phải giống với logit cho câu thứ hai, nhưng chúng ta có các giá trị hoàn toàn khác nhau!

Điều này là do tính năng chính của các mô hình Transformer là các lớp attention đã *ngữ cảnh hóa* mỗi token. Chúng sẽ tính đến các padding token vì chúng tham gia vào tất cả các token của một chuỗi. Để có được kết quả tương tự khi chuyển các câu riêng lẻ có độ dài khác nhau qua mô hình hoặc khi chuyển một lô với các câu và phần đệm giống nhau được áp dụng, chúng ta cần yêu cầu các lớp attention đó bỏ qua các thẻ đệm. Điều này được thực hiện bằng cách sử dụng attention mask.

## Attention masks

*Attention masks* là các tensor có hình dạng chính xác như tensor ID đầu vào, được lấp đầy bởi 0 và 1: 1 cho biết các tokenn tương ứng nên được tham gia và các số 0 cho biết các token tương ứng không được tham gia (tức là chúng phải bị bỏ qua bởi các lớp attention của mô hình).

Hãy hoàn thành ví dụ trước với một attention mask:

{#if fw === 'pt'}
```py no-format
batched_ids = [
    [200, 200, 200],
    [200, 200, tokenizer.pad_token_id],
]

attention_mask = [
    [1, 1, 1],
    [1, 1, 0],
]

outputs = model(torch.tensor(batched_ids), attention_mask=torch.tensor(attention_mask))
print(outputs.logits)
```

```python out
tensor([[ 1.5694, -1.3895],
        [ 0.5803, -0.4125]], grad_fn=<AddmmBackward>)
```
{:else}
```py no-format
batched_ids = [
    [200, 200, 200],
    [200, 200, tokenizer.pad_token_id],
]

attention_mask = [
    [1, 1, 1],
    [1, 1, 0],
]

outputs = model(tf.constant(batched_ids), attention_mask=tf.constant(attention_mask))
print(outputs.logits)
```

```py out
tf.Tensor(
[[ 1.5693681  -1.3894582 ]
 [ 0.5803021  -0.41252586]], shape=(2, 2), dtype=float32)
```
{/if}

Bây giờ chúng ta nhận được các logit tương tự cho câu thứ hai trong lô.

Lưu ý cách giá trị cuối cùng của chuỗi thứ hai là ID đệm, là giá trị 0 trong attention mask.

<Tip>

✏️ **Thử nghiệm thôi!** Áp dụng thủ công tokenize cho hai câu được sử dụng trong phần 2 ("I've been waiting for a HuggingFace course my whole life." và "I hate this so much!"). Đưa chúng vào mô hình và kiểm tra xem bạn có nhận được các logit giống như trong phần 2 không. Bây giờ, gộp chúng lại với nhau bằng cách sử dụng token đệm, sau đó tạo attention mask thích hợp. Kiểm tra xem bạn có đạt được kết quả tương tự khi đưa qua mô hình không!

</Tip>

## Những chuỗi dài hơn

Với các mô hình Transformer, có một giới hạn về độ dài của các chuỗi mà chúng tôi có thể vượt qua các mô hình. Hầu hết các mô hình xử lý chuỗi lên đến 512 hoặc 1024 token và sẽ bị lỗi khi được yêu cầu xử lý chuỗi dài hơn. Có hai giải pháp cho vấn đề này:

- Sử dụng mô hình có độ dài chuỗi được hỗ trợ dài hơn.
- Cắt ngắn chuỗi của bạn.

Các mô hình có độ dài chuỗi được hỗ trợ khác nhau và một số mô hình chuyên xử lý các trình tự rất dài. [Longformer](https://huggingface.co/transformers/model_doc/longformer.html) là một ví dụ và một ví dụ khác là [LED](https://huggingface.co/transformers/model_doc/led.html). Nếu bạn đang thực hiện một công việc đòi hỏi trình tự rất dài, chúng tôi khuyên bạn nên xem các mô hình đó.

Nếu không, chúng tôi khuyên bạn nên cắt bớt các chuỗi của mình bằng cách chỉ định tham số `max_sequence_length`:

```py
sequence = sequence[:max_sequence_length]
```


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter2/5.mdx" />

### Giới thiệu
https://huggingface.co/learn/course/vi/chapter2/1.md

# Giới thiệu

<CourseFloatingBanner
    chapter={2}
    classNames="absolute z-10 right-0 top-0"
/>

Như bạn đã thấy trong [Chương 1](/course/chapter1), các mô hình Transformer thường rất lớn. Với hàng triệu đến hàng chục *tỷ* thông số, việc huấn luyện và triển khai các mô hình này là một công việc phức tạp. Hơn nữa, với việc các mô hình mới được phát hành gần như hàng ngày và mỗi mô hình có cách triển khai riêng, việc thử tất cả chúng không phải là nhiệm vụ dễ dàng.

Thư viện 🤗 Transformers được tạo ra để giải quyết vấn đề này. Mục tiêu của nó là cung cấp một API duy nhất mà qua đó bất kỳ mô hình Transformer nào cũng có thể được tải, huấn luyện, và lưu. Các tính năng chính của thư viện gồm:

- **Tính dễ sử dụng**: Việc tải xuống, tải và sử dụng mô hình NLP tối tân để luận suy có thể được thực hiện chỉ trong hai dòng mã.
- **Tính linh hoạt**: Về cốt lõi, tất cả các mô hình đều là các lớp PyTorch `nn.Module` hoặc TensorFlow` tf.keras.Model` đơn giản và có thể được xử lý giống như bất kỳ mô hình nào khác trong khuôn khổ học máy (ML) tương ứng của chúng.
- **Tính đơn giản**: Hầu như không có bất kỳ sự trừu tượng nào được thực hiện trên toàn bộ thư viện. "All in one file" ("Tất cả trong một tệp") là khái niệm cốt lõi: bước lan truyền thẳng của một mô hình được xác định hoàn toàn trong một tệp duy nhất giúp bản thân đoạn mã dễ hiểu và có thể hack được.

Tính năng cuối cùng này làm cho 🤗 Transformers khá khác biệt so với các thư viện ML khác. Các mô hình không được xây dựng trên các mô-đun được chia sẻ trên các tệp; thay vào đó, mỗi mô hình có các lớp riêng của nó. Ngoài việc làm cho các mô hình dễ tiếp cận và dễ hiểu hơn, điều này cho phép bạn dễ dàng thử nghiệm trên một mô hình mà không ảnh hưởng đến các mô hình khác.

Chương này sẽ bắt đầu với một ví dụ từ đầu đến cuối, trong đó chúng ta sử dụng một mô hình và một tokenizer cùng nhau để sao chép hàm `pipeline()` được giới thiệu trong [Chapter 1](/course/chapter1). Tiếp theo, chúng ta sẽ thảo luận về API mô hình: chúng ta sẽ đi sâu vào các lớp cấu hình và mô hình, đồng thời chỉ cho bạn cách tải một mô hình và cách nó xử lý các đầu vào dạng số để đưa ra các dự đoán đầu ra.

Sau đó, chúng ta sẽ xem xét API tokenizer, một thành phần chính khác của hàm `pipeline()`. Tokenizers thực hiện các bước xử lý đầu tiên và cuối cùng, xử lý việc chuyển đổi từ văn bản đầu vào thành dạng số cho mạng nơ-ron và chuyển đổi trở lại văn bản khi cần. Cuối cùng, chúng tôi sẽ chỉ cho bạn cách xử lý việc gửi nhiều câu vào một mô hình trong một batch (lô) đã chuẩn bị, sau đó tóm tắt tất cả bằng cách xem xét kỹ hơn hàm `tokenizer()` ở bậc cao.

<Tip>
⚠️ Để có thể tận dụng tất cả các tính năng có sẵn với Model Hub và 🤗 Transformers, chúng tôi khuyến khích bạn <a href="https://huggingface.co/join">tạo tài khoản </a>.
</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter2/1.mdx" />

### Các mô hình
https://huggingface.co/learn/course/vi/chapter2/3.md

# Các mô hình

{#if fw === 'pt'}

<CourseFloatingBanner chapter={2}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {
      label: "Google Colab",
      value:
        "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter2/section3_pt.ipynb",
    },
    {
      label: "Aws Studio",
      value:
        "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter2/section3_pt.ipynb",
    },
  ]}
/>

{:else}

<CourseFloatingBanner chapter={2}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {
      label: "Google Colab",
      value:
        "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter2/section3_tf.ipynb",
    },
    {
      label: "Aws Studio",
      value:
        "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter2/section3_tf.ipynb",
    },
  ]}
/>

{/if}

{#if fw === 'pt'}

<Youtube id="AhChOFRegn4"/>
{:else}
<Youtube id="d3JVgghSOew"/>
{/if}

{#if fw === 'pt'}
Trong phần này, chúng ta sẽ xem xét kỹ hơn về việc tạo và sử dụng một mô hình. Chúng tôi sẽ sử dụng `AutoModel`, rất tiện lợi khi bạn muốn khởi tạo bất kỳ mô hình nào từ một checkpoint.

`AutoModel` và tất cả các lớp họ hàng của nó thực ra là các hàm đóng gói đơn giản trên nhiều loại mô hình có sẵn trong thư viện. Đó là một hàm đóng gói thông minh vì nó có thể tự động đoán kiến trúc mô hình thích hợp cho checkpoint của bạn và sau đó khởi tạo một mô hình với kiến trúc này.

{:else}
Trong phần này, chúng ta sẽ xem xét kỹ hơn về việc tạo và sử dụng một mô hình. Chúng tôi sẽ sử dụng `TFAutoModel`, rất tiện lợi khi bạn muốn khởi tạo bất kỳ mô hình nào từ một checkpoint.

`TFAutoModel` và tất cả các lớp họ hàng của nó thực ra là các hàm đóng gói đơn giản trên nhiều loại mô hình có sẵn trong thư viện. Đó là một hàm đóng gói thông minh vì nó có thể tự động đoán kiến trúc mô hình thích hợp cho checkpoint của bạn và sau đó khởi tạo một mô hình với kiến trúc này.

{/if}

Tuy nhiên, nếu bạn biết loại mô hình bạn muốn sử dụng, bạn có thể sử dụng trực tiếp lớp định nghĩa kiến trúc của nó. Chúng ta hãy xem cách này hoạt động với mô hình BERT.

## Tạo ra một Transformer

Điều đầu tiên ta cần làm để khởi tạo mô hình BERT là tải một cấu hình:

{#if fw === 'pt'}

```py
from transformers import BertConfig, BertModel

# Building the config
config = BertConfig()

# Building the model from the config
model = BertModel(config)
```

{:else}

```py
from transformers import BertConfig, TFBertModel

# Building the config
config = BertConfig()

# Building the model from the config
model = TFBertModel(config)
```

{/if}

Cấu hình chứa nhiều thuộc tính được sử dụng để xây dựng mô hình:

```py
print(config)
```

```python out
BertConfig {
  [...]
  "hidden_size": 768,
  "intermediate_size": 3072,
  "max_position_embeddings": 512,
  "num_attention_heads": 12,
  "num_hidden_layers": 12,
  [...]
}
```

Mặc dù bạn chưa thấy tất cả các thuộc tính này có tác dụng gì, nhưng bạn nên nhận ra một số trong số chúng: thuộc tính `hidden_size` xác định kích thước của vectơ `hidden_states` và `num_hidden_layers` xác định số lớp mà mô hình Transformer có.

### Các phương pháp tải khác nhau

Việc tạo mô hình từ cấu hình mặc định sẽ khởi tạo mô hình đó với các giá trị ngẫu nhiên:

{#if fw === 'pt'}

```py
from transformers import BertConfig, BertModel

config = BertConfig()
model = BertModel(config)

# Model is randomly initialized!
```

{:else}

```py
from transformers import BertConfig, TFBertModel

config = BertConfig()
model = TFBertModel(config)

# Model is randomly initialized!
```

{/if}
Mô hình có thể được sử dụng ở trạng thái này, nhưng nó sẽ trả ra vô nghĩa; nó cần được huấn luyện trước. Chúng ta có thể huấn luyện mô hình từ đầu tác vụ này, nhưng như bạn đã thấy trong [Chương 1](/course/chapter1), điều này sẽ đòi hỏi một thời gian dài và nhiều dữ liệu, và nó sẽ tác động đáng kể tới môi trường. Để tránh nỗ lực không cần thiết và trùng lặp, khả năng chia sẻ và sử dụng lại các mô hình đã được huấn luyện trở nên bắt buộc.

Việc tải một mô hình Transformer đã được huấn luyện trước rất đơn giản - chúng ta có thể thực hiện việc này bằng cách sử dụng phương thức `from_pretrained()`:

{#if fw === 'pt'}

```py
from transformers import BertModel

model = BertModel.from_pretrained("bert-base-cased")
```

Như bạn đã thấy trước đó, chúng ta có thể thay thế `BertModel` bằng `AutoModel` tương đương. Chúng ta sẽ làm điều này từ bây giờ vì điều này tạo ra các đoạn mã checkpoint bất khả tri; nếu mã của bạn hoạt động cho một checkpoint, nó sẽ hoạt động liền mạch với một checkpoint khác. Điều này áp dụng ngay cả khi kiến trúc khác nhau, miễn là checkpoint đã được huấn luyện cho một tác vụ tương tự (ví dụ: một tác vụ phân tích cảm xúc).

{:else}

```py
from transformers import TFBertModel

model = TFBertModel.from_pretrained("bert-base-cased")
```

Như bạn đã thấy trước đó, chúng ta có thể thay thế `BertModel` bằng `TFAutoModel` tương đương. Chúng ta sẽ làm điều này từ bây giờ vì điều này tạo ra các đoạn mã checkpoint bất khả tri; nếu mã của bạn hoạt động cho một checkpoint, nó sẽ hoạt động liền mạch với một checkpoint khác. Điều này áp dụng ngay cả khi kiến trúc khác nhau, miễn là checkpoint đã được huấn luyện cho một tác vụ tương tự (ví dụ: một tác vụ phân tích cảm xúc).

{/if}

Trong đoạn mã ở trên, ta không sử dụng `BertConfig` và thay vào đó, tải một mô hình được đào tạo trước thông qua mã định danh `bert-base-cased`. Đây là một checkpoint mô hình do chính các tác giả của BERT huấn luyện; bạn có thể tìm thêm thông tin chi tiết về nó trong [thẻ mô hình](https://huggingface.co/bert-base-cased).

Mô hình này hiện đã được khởi tạo với tất cả các trọng số của checkpoint. Nó có thể được sử dụng trực tiếp để luận suy về các tác vụ mà nó đã được huấn luyện, và nó cũng có thể được tinh chỉnh trên một tác vụ mới. Bằng cách huấn luyện với trọng số đã được huấn luyện trước chứ không phải từ đầu, chúng ta có thể nhanh chóng đạt được kết quả tốt.

Các trọng số đã được tải xuống và lưu vào bộ nhớ cache (vì vậy các lệnh gọi tới phương thức `from_pretrained()` trong tương lai sẽ không tải xuống lại chúng) trong thư mục bộ nhớ cache, mặc định là _~/.cache/huggingface/transformers_. Bạn có thể tùy chỉnh thư mục bộ nhớ cache của mình bằng cách đặt biến môi trường `HF_HOME`.

Số định danh được sử dụng để tải mô hình có thể là số định danh của bất kỳ mô hình nào trên Model Hub, miễn là nó tương thích với kiến ​​trúc BERT. Toàn bộ danh sách các checkpoint BERT hiện có có thể được tìm thấy [tại đây](https://huggingface.co/models?filter=bert).

### Phương pháp lưu trữ checkpoint

Lưu một mô hình cũng dễ dàng như tải một mô hình - chúng ta sử dụng phương thức `save_pretrained()`, tương tự với phương thức `from_pretrained()`:

```py
model.save_pretrained("directory_on_my_computer")
```

Thao tác này sẽ lưu hai tệp vào đĩa của bạn:

{#if fw === 'pt'}

```
ls directory_on_my_computer

config.json model.safetensors
```

{:else}

```
ls directory_on_my_computer

config.json tf_model.h5
```

{/if}

Nếu bạn xem tệp _config.json_, bạn sẽ nhận ra các thuộc tính cần thiết để xây dựng kiến trúc mô hình. Tệp này cũng chứa một số siêu dữ liệu, chẳng hạn như điểm bắt nguồn của checkpoint và phiên bản 🤗 Transformers bạn đang sử dụng khi bạn lưu checkpoint lần cuối.

{#if fw === 'pt'}

Tệp _model.safetensors_ được gọi là _state dictionary_ (_từ điển trạng thái_); nó chứa tất cả các trọng số mô hình của bạn. Hai tập tin đi đôi với nhau; cấu hình là cần thiết để biết kiến trúc mô hình của bạn, trong khi trọng số mô hình là thông số của mô hình của bạn.

{:else}

Tệp _tf_model.h5_ được gọi là _state dictionary_ (_từ điển trạng thái_); nó chứa tất cả các trọng số mô hình của bạn. Hai tập tin đi đôi với nhau; cấu hình là cần thiết để biết kiến trúc mô hình của bạn, trong khi trọng số mô hình là thông số của mô hình của bạn.

{/if}

## Sử dụng mô hình Transformer để luận suy

Giờ bạn đã biết cách tải và lưu một mô hình, hãy thử sử dụng nó để đưa ra một số dự đoán. Các mô hình Transfomer chỉ có thể xử lý số - các số mà tokenizer tạo ra. Nhưng trước khi chúng ta thảo luận về tokenizer, chúng ta hãy khám phá những yếu tố đầu vào mà mô hình chấp nhận.

Tokenizer có thể đảm nhận việc truyền các đầu vào đến các tensor của khung thích hợp, nhưng để giúp bạn hiểu những gì đang xảy ra, chúng ta sẽ xem xét nhanh những gì phải thực hiện trước khi gửi đầu vào cho mô hình.

Giả sử chúng ta có một vài chuỗi như sau:

```py
sequences = ["Hello!", "Cool.", "Nice!"]
```

Tokenizer chuyển đổi các chỉ số này thành các chỉ mục từ vựng thường được gọi là _ID đầu vào_. Mỗi chuỗi giờ là một danh sách các số! Kết quả đầu ra là:

```py no-format
encoded_sequences = [
    [101, 7592, 999, 102],
    [101, 4658, 1012, 102],
    [101, 3835, 999, 102],
]
```

Đây là danh sách các chuỗi được mã hóa: danh sách các danh sách. Tensor chỉ chấp nhận dạng hình chữ nhật (hãy nghĩ tới ma trận). "Mảng" này đã có dạng hình chữ nhật, vì vậy việc chuyển đổi nó thành một tensor rất dễ dàng:

{#if fw === 'pt'}

```py
import torch

model_inputs = torch.tensor(encoded_sequences)
```

{:else}

```py
import tensorflow as tf

model_inputs = tf.constant(encoded_sequences)
```

{/if}

### Sử dụng tensor làm đầu vào mô hình

Việc sử dụng các tensors với mô hình cực kỳ đơn giản - chúng ta chỉ cần gọi mô hình với các đầu vào:

```py
output = model(model_inputs)
```

Mặc dù mô hình chấp nhận rất nhiều tham số khác nhau, nhưng chỉ các ID đầu vào là cần thiết. Chúng tôi sẽ giải thích những gì các tham số khác làm và khi nào chúng được yêu cầu sau, nhưng trước tiên, chúng ta cần xem xét kỹ hơn các bộ tokenizer tạo ra các đầu vào mà một mô hình Transformer có thể hiểu được.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter2/3.mdx" />

### Kết hợp lại
https://huggingface.co/learn/course/vi/chapter2/6.md

# Kết hợp lại

{#if fw === 'pt'}

<CourseFloatingBanner chapter={2}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter2/section6_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter2/section6_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={2}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter2/section6_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter2/section6_tf.ipynb"},
]} />

{/if}

Trong vài phần trước, chúng ta đã cố gắng hết sức để làm hầu hết các tác vụ bằng tay. Chúng ta đã khám phá cách thức hoạt động của các công cụ tokenize và xem xét quá trình tokenize, chuyển đổi dữ liệu sang ID đầu vào, đệm, cắt bớt và các lớp che attention.

Tuy nhiên, như chúng ta đã thấy trong phần 2, API 🤗 Transformers có thể xử lý tất cả những điều này cho chúng ta bằng một chức năng cấp cao mà chúng ta sẽ đi sâu vào đây. Khi bạn gọi trực tiếp `tokenizer` trên câu, bạn sẽ nhận lại được các thông tin đầu vào sẵn sàng chuyển qua mô hình của bạn:

```py
from transformers import AutoTokenizer

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

model_inputs = tokenizer(sequence)
```

Ở đây, biến `model_inputs` chứa mọi thứ cần thiết để một mô hình hoạt động tốt. Đối với DistilBERT, điều đó bao gồm các ID đầu vào cũng như lớp che attention. Các mô hình khác chấp nhận đầu vào bổ sung cũng sẽ có đầu ra đó từ đối tượng `tokenizer`.

Như chúng ta sẽ thấy trong một số ví dụ bên dưới, phương pháp này rất mạnh mẽ. Đầu tiên, nó có thể mã hóa một chuỗi duy nhất:

```py
sequence = "I've been waiting for a HuggingFace course my whole life."

model_inputs = tokenizer(sequence)
```

Nó cũng xử lý nhiều chuỗi cùng một lúc mà không cần thay đổi trong API:

```py
sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

model_inputs = tokenizer(sequences)
```

Nó có thể đệm thêm tuỳ theo một số mục tiêu như sau:

```py
# Sẽ đệm thêm vào chuỗi sao cho độ dài bằng độ dài tối đa của chuỗi
model_inputs = tokenizer(sequences, padding="longest")

# Sẽ đệm thêm vào chuỗi sao cho độ dài bằng độ dài tối đa của mô hình
# (512 cho BERT hoặc DistilBERT)
model_inputs = tokenizer(sequences, padding="max_length")

# Sẽ đệm thêm vào chuỗi sao cho độ dài bằng độ dài tối đa được chỉ định
model_inputs = tokenizer(sequences, padding="max_length", max_length=8)
```

Nó cũng có thể cắt bớt các chuỗi:

```py
sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

# Sẽ cắt bớt chuỗi cho bằng độ dài tối đa của mô hình
# (512 cho BERT hoặc DistilBERT)
model_inputs = tokenizer(sequences, truncation=True)

# Sẽ cắt bớt chuỗi có độ dài dài hơn độ dài tối đa được chỉ định
model_inputs = tokenizer(sequences, max_length=8, truncation=True)
```

Đối tượng `tokenizer` có thể xử lý việc chuyển đổi sang các tensor cụ thể, sau đó có thể được gửi trực tiếp đến mô hình. Ví dụ: trong đoạn mã sau, chúng tôi đang nhắc tokenizer trả về tensors từ các khung khác nhau - `"pt"` trả về tensors PyTorch, `"tf"` trả về tensors TensorFlow và `"np"` trả về mảng NumPy:

```py
sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

# Trả về tensor PyTorch
model_inputs = tokenizer(sequences, padding=True, return_tensors="pt")

# Trả về tensor TensorFlow
model_inputs = tokenizer(sequences, padding=True, return_tensors="tf")

# Trả về mảng NumPy
model_inputs = tokenizer(sequences, padding=True, return_tensors="np")
```

## Các token đặc biệt

Nếu chúng ta xem xét các ID đầu vào được trả về bởi tokenizer, chúng ta sẽ thấy chúng hơi khác một chút so với những gì chúng ta đã có trước đó:

```py
sequence = "I've been waiting for a HuggingFace course my whole life."

model_inputs = tokenizer(sequence)
print(model_inputs["input_ids"])

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)
print(ids)
```

```python out
[101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102]
[1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]
```

Một token ID đã được thêm vào vị trí đầu và cuối. Hãy giải mã hai chuỗi ID ở trên để xem nó là gì:


```py
print(tokenizer.decode(model_inputs["input_ids"]))
print(tokenizer.decode(ids))
```

```python out
"[CLS] i've been waiting for a huggingface course my whole life. [SEP]"
"i've been waiting for a huggingface course my whole life."
```

Tokenizer đã thêm từ đặc biệt `[CLS]` vào đầu và từ đặc biệt `[SEP]` ở cuối. Điều này là do mô hình đã được huấn luyện trước với chúng, vì vậy để có được kết quả tương tự để luận suy, chúng ta cũng cần thêm chúng vào. Lưu ý rằng một số mô hình không thêm các từ đặc biệt hoặc thêm các từ khác; mô hình cũng có thể chỉ thêm những từ đặc biệt này vào đầu hoặc chỉ ở cuối. Trong mọi trường hợp, tokenizer biết cái nào được mong đợi và sẽ giải quyết việc này cho bạn.

## Tổng kết: Từ tokenizer đến mô hình

Giờ chúng ta đã thấy tất cả các bước riêng lẻ mà `tokenizer` sử dụng khi áp dụng lên văn bản, chúng ta hãy xem lần cuối cách nó có thể xử lý nhiều chuỗi (đệm thêm!), chuỗi rất dài (cắt ngắn!) Và nhiều kiểu tensor với API chính của nó:

{#if fw === 'pt'}
```py
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
output = model(**tokens)
```
{:else}
```py
import tensorflow as tf
from transformers import AutoTokenizer, TFAutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="tf")
output = model(**tokens)
```
{/if}


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter2/6.mdx" />

### Đố vui cuối chương
https://huggingface.co/learn/course/vi/chapter2/8.md

# Đố vui cuối chương

<CourseFloatingBanner
    chapter={2}
    classNames="absolute z-10 right-0 top-0"
/>

### 1. Thứ tự của một quy trình mô hình hóa ngôn ngữ là gì?

<Question
	choices={[
		{
			text: "Đầu tiên, mô hình xử lý văn bản và trả về các dự đoán thô. Sau đó, tokenizer hiểu các dự đoán này và chuyển đổi chúng trở lại thành văn bản khi cần.",
			explain: "Mô hình không thể hiểu văn bản! Trình tokenize đầu tiên phải tokenize văn bản và chuyển đổi nó thành ID đầu vào để mô hình có thể hiểu được."
		},
		{
			text: "Đầu tiên, tokenizer xử lý văn bản và trả về các ID. Mô hình xử lý các ID này và xuất ra một dự đoán, có thể là một số văn bản.",
			explain: "Dự đoán của mô hình không thể thành văn bản ngay lập tức. Phải sử dụng trình tokenizer để chuyển dự đoán trở lại thành văn bản!"
		},
		{
			text: "Tokenizer xử lý văn bản và trả về các ID. Mô hình xử lý các ID này và đưa ra dự đoán. Sau đó, tokenizer có thể được sử dụng một lần nữa để chuyển đổi các dự đoán này trở lại một số văn bản.",
			explain: "Đúng! Trình mã hóa có thể được sử dụng cho cả mã hóa mã hóa và khử mã hóa hay giải mã.",
            correct: true
		}
	]}
/>

### 2. Đầu ra tensor của mô hình Transformer cơ sở có bao nhiêu chiều, và chúng là gì?

<Question
	choices={[
		{
			text: "2: Độ dài chuỗi và kích thước lô",
			explain: "Sai! Đầu ra tensor của mô hình có một chiều thứ ba: kích thước lớp ẩn."
		},
		{
			text: "2: Độ dài chuỗi và độ dài lớp ẩn",
			explain: "Sai! Tất cả các mô hình Transformer xử lý theo lô, kể cả khi là một chuỗi đơn, khi đó kích thước lô bằng 1!"
		},
		{
			text: "3: Độ dài chuỗi, kích thước lô, và độ dài lớp ẩn",
			explain: "Chính xác!",
            correct: true
		}
	]}
/>

### 3. Trường hợp nào dưới đây không phải là ví dụ về tokenize theo từ phụ?

<Question
	choices={[
		{
			text: "WordPiece",
			explain: "Chính xác, đây là một ví dụ về tokenize theo từ phụ!",
            correct: true
		},
		{
			text: "Tokenize theo kí tự",
			explain: "Tokenize theo kí tự không phải là một ví dụ về tokenize theo từ phụ!"
		},
		{
			text: "Chia theo dấu cách và các dấu câu",
			explain: "Đây là ví dụ về tokenize theo mức từ!"
		},
		{
			text: "BPE",
			explain: "Chính xác, đây là một ví dụ về tokenize theo từ phụ!",
            correct: true
        },
		{
			text: "Unigram",
			explain: "Chính xác, đây là một ví dụ về tokenize theo từ phụ!",
            correct: true
        },
		{
			text: "Không phải các trường hợp trên",
			explain: "Không chính xác!"
        }
	]}
/>

### 4. Model head (Đầu mô hình) là gì?

<Question
	choices={[
		{
			text: "Một thành phần của Transformer cơ sở chuyển hướng các tensor đến các lớp chính xác của chúng",
			explain: "Không chính xác! Không có thành phần như vậy."
		},
		{
			text: "Còn được gọi là cơ chế self-attention haytự chú ý, nó điều chỉnh cách đại diện của một token theo các token khác của chuỗi",
			explain: "Không chính xác! Lớp self-attention có chứa các attention \"heads,\" nhưng đây không phải là các đầu thích ứng."
		},
		{
			text: "Một thành phần bổ sung, thường được tạo thành từ một hoặc một vài lớp, để chuyển đổi các dự đoán transformer thành đầu ra theo tác vụ cụ thể",
			explain: "Đúng vậy. Đầu thích ứng, còn được gọi đơn giản là đầu, xuất hiện ở các dạng khác nhau: đầu mô hình ngôn ngữ, đầu hỏi đáp, đầu phân loại chuỗi ... ",
			correct: true
		} 
	]}
/>

{#if fw === 'pt'}

### 5. AutoModel là gì?

<Question
	choices={[
		{
			text: "Một mô hình tự động huấn luyện trên dữ liệu của bạn",
			explain: "Không chính xác. Bạn có đang nhầm sản phẩm này với sản phẩm <a href='https://huggingface.co/autotrain'>AutoTrain</a> của chúng tôi không?"
		},
		{
			text: "Một đối tượng trả về kiến trúc chính xác dựa trên checkpoint",
			explain: "Chính xác: <code>AutoModel</code> chỉ cần biết checkpoint từ đó khởi tạo để trả về kiến trúc chính xác.",
			correct: true
		},
		{
			text: "Một mô hình tự động phát hiện ngôn ngữ được sử dụng cho đầu vào của nó để tải các trọng số chính xác",
			explain: "Không chính xác; trong khi một số checkpoint và mô hình có khả năng xử lý đa ngôn ngữ, không có công cụ tích hợp nào để lựa chọn checkpoint tự động theo ngôn ngữ. Bạn nên truy cập <a href='https://huggingface.co/models'>Model Hub</a>  để tìm checkpoint tốt nhất cho tác vụ của bạn!"
		} 
	]}
/>

{:else}
### 5. TFAutoModel là gì?

<Question
	choices={[
		{
			text: "Một mô hình tự động huấn luyện trên dữ liệu của bạn",
			explain: "Không chính xác. Bạn có đang nhầm sản phẩm này với sản phẩm <a href='https://huggingface.co/autotrain'>AutoTrain</a> của chúng tôi không?"
		},
		{
			text: "Một đối tượng trả về kiến trúc chính xác dựa trên checkpoint",
			explain: "Chính xác: <code>TFAutoModel</code> chỉ cần biết checkpoint từ đó khởi tạo để trả về kiến trúc chính xác.",
			correct: true
		},
		{
			text: "Một mô hình tự động phát hiện ngôn ngữ được sử dụng cho đầu vào của nó để tải các trọng số chính xác",
			explain: "Không chính xác; trong khi một số checkpoint và mô hình có khả năng xử lý đa ngôn ngữ, không có công cụ tích hợp nào để lựa chọn checkpoint tự động theo ngôn ngữ. Bạn nên truy cập <a href='https://huggingface.co/models'>Model Hub</a>  để tìm checkpoint tốt nhất cho tác vụ của bạn!"
		} 
	]}
/>

{/if}

### 6. Các kỹ thuật cần lưu ý khi ghép các chuỗi có độ dài khác nhau với nhau là gì?

<Question
	choices={[
		{
			text: "Cắt bớt",
			explain: "Vâng, việc cắt bớt là một cách chính xác để sắp xếp các chuỗi sao cho chúng vừa với hình chữ nhật. Tuy nhiên, nó có phải là duy nhất không?",
			correct: true
		},
		{
			text: "Trả về tensor",
			explain: "Trong khi các kỹ thuật khác cho phép bạn trả về các tensor hình chữ nhật, trả về tensor không hữu ích khi kết hợp các chuỗi với nhau."
		},
		{
			text: "Đệm thêm",
			explain: "Vâng, việc đệm thêm là một cách chính xác để sắp xếp các chuỗi sao cho chúng vừa với hình chữ nhật. Tuy nhiên, nó có phải là duy nhất không?",
			correct: true
		}, 
		{
			text: "Attention masking",
			explain: "Hoàn toàn có thể! Attention mask có tầm quan trọng hàng đầu khi xử lý các chuỗi có độ dài khác nhau. Tuy nhiên, đó không phải là kỹ thuật duy nhất cần lưu ý.",
			correct: true
		} 
	]}
/>

### 7. Mục đích của việc áp dụng hàm SoftMax vào đầu ra logit của mô hình phân loại là gì?

<Question
	choices={[
		{
			text: "Nó làm mềm logit để chúng đáng tin cậy hơn.",
			explain: "Không, hàm SoftMax không ảnh hưởng đến độ tin cậy của kết quả."
		},
		{
			text: "Nó áp dụng giới hạn dưới và trên để chúng trở nên dễ hiểu.",
			explain: "Đúng! Các giá trị kết quả bị ràng buộc trong khoảng từ 0 đến 1. Tuy nhiên, đó không phải là lý do duy nhất chúng tôi sử dụng hàm SoftMax.",
            correct: true
		},
		{
			text: "Tổng của kết quả đầu ra khi đó là 1, dẫn đến việc giải thích theo xác suất có thể xảy ra.",
			explain: "Chính xác!  Tuy nhiên, đó không phải là lý do duy nhất chúng tôi sử dụng hàm SoftMax.",
            correct: true
		}
	]}
/>

### 8. Phần lớn API tokenizer tập trung vào phương pháp nào?

<Question
	choices={[
		{
			text: "<code>encode</code>, vì nó có thể mã hóa văn bản thành ID và ID thành dự đoán",
			explain: "Sai! Mặc dù phương thức<code>encode</code> tồn tại trên tokenizers, nhưng nó không tồn tại trên các mô hình."
		},
		{
			text: "Gọi trực tiếp đối tượng tokenizer.",
			explain: "Chính xác! Phương thức <code>__call__</code> của tokenizer là một phương pháp rất mạnh có thể xử lý khá nhiều thứ. Nó cũng là phương pháp được sử dụng để truy xuất các dự đoán từ một mô hình.",
			correct: true
		},
		{
			text: "<code>Đệm thêm</code>",
			explain: "Sai! Đệm thêm rất hữu ích, nhưng nó chỉ là một phần của tokenizer API."
		},
		{
			text: "<code>tokenize</code>",
			explain: "Phương thức <code>tokenize</code> được cho là một trong những phương pháp hữu ích nhất, nhưng nó không phải là cốt lõi của API tokenizer."
		}
	]}
/>

### 9. Biến `result` chứa gì trong đoạn mã dưới đây?

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
result = tokenizer.tokenize("Hello!")
```

<Question
	choices={[
		{
			text: "Danh sách các chuỗi, mỗi chuỗi là một token",
			explain: "Chắc chắn rồi! Chuyển đổi chúng thành ID và gửi chúng đến một mô hình!",
            correct: true
		},
		{
			text: "Danh sách các ID",
			explain: "Sai; đó là những gì phương thức <code>__call__</code> hoặc <code>convert_tokens_to_ids</code> làm!"
		},
		{
			text: "Một chuỗi chứa tất cả các token",
			explain: "Điều này sẽ là không tối ưu, vì mục tiêu là chia chuỗi thành nhiều token."
		}
	]}
/>

{#if fw === 'pt'}

### 10. Có điều gì đó sai với đoạn mã sau đây?

```py
from transformers import AutoTokenizer, AutoModel

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
model = AutoModel.from_pretrained("gpt2")

encoded = tokenizer("Hey!", return_tensors="pt")
result = model(**encoded)
```

<Question
	choices={[
		{
			text: "Không, có vẻ đúng.",
			explain: "Tiếc thay, việc kết hợp một mô hình với một tokenizer đã được huấn luyện với một checkpoint khác hiếm khi là một ý tưởng hay. Mô hình không được huấn luyện để hiểu được đầu ra của tokenizer này, vì vậy mô hình xuất ra (nếu nó thậm chí có thể chạy!) sẽ không có ý nghĩa gì."
		},
		{
			text: "Tokenizer và mô hình phải luôn ở cùng một checkpoint.",
			explain: "Đúng!",
            correct: true
		},
		{
			text: "Đây là một ví dụ thực tiễn về đệm thêm và cắt bớt với tokenizer khi tát cả đầu vào trong cùng một lô.",
			explain: "Đúng là mọi đầu vào của mô hình cần phải là một lô. Tuy nhiên, việc cắt bớt hoặc chèn thêm chuỗi này sẽ không nhất thiết có ý nghĩa vì chỉ cần một trong số đó và đó là các kỹ thuật để gộp một danh sách các câu lại với nhau."
		}
	]}
/>

{:else}
### 10. Có điều gì đó sai với đoạn mã sau đây?

```py
from transformers import AutoTokenizer, TFAutoModel

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
model = TFAutoModel.from_pretrained("gpt2")

encoded = tokenizer("Hey!", return_tensors="pt")
result = model(**encoded)
```

<Question
	choices={[
		{
			text: "Không, có vẻ đúng.",
			explain: "Tiếc thay, việc kết hợp một mô hình với một tokenizer đã được huấn luyện với một checkpoint khác hiếm khi là một ý tưởng hay. Mô hình không được huấn luyện để hiểu được đầu ra của tokenizer này, vì vậy mô hình xuất ra (nếu nó thậm chí có thể chạy!) sẽ không có ý nghĩa gì."
		},
		{
			text: "Tokenizer và mô hình phải luôn ở cùng một checkpoint.",
			explain: "Đúng!",
            correct: true
		},
		{
			text: "Đây là một ví dụ thực tiễn về đệm thêm và cắt bớt với tokenizer khi tát cả đầu vào trong cùng một lô.",
			explain: "Đúng là mọi đầu vào của mô hình cần phải là một lô. Tuy nhiên, việc cắt bớt hoặc chèn thêm chuỗi này sẽ không nhất thiết có ý nghĩa vì chỉ cần một trong số đó và đó là các kỹ thuật để gộp một danh sách các câu lại với nhau."
		}
	]}
/>

{/if}


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter2/8.mdx" />

### Tokenizers
https://huggingface.co/learn/course/vi/chapter2/4.md

# Tokenizers

{#if fw === 'pt'}

<CourseFloatingBanner chapter={2}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter2/section4_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter2/section4_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={2}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter2/section4_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter2/section4_tf.ipynb"},
]} />

{/if}

<Youtube id="VFp38yj8h3A"/>

Tokenizer là một trong những thành phần cốt lõi của pipeline NLP. Chúng phục vụ một mục đích: dịch văn bản thành dữ liệu có thể được xử lý bởi mô hình. Mô hình chỉ có thể xử lý dạng số, do đó, các tokenizer cần phải chuyển đổi đầu vào văn bản của chúng ta thành dữ liệu số. Trong phần này, chúng ta sẽ khám phá chính xác những gì xảy ra trong đường dẫn mã hóa.

Trong các tác vụ NLP, dữ liệu thường được xử lý là văn bản thô. Đây là một ví dụ về văn bản như vậy:

```
Jim Henson was a puppeteer
```

Tuy nhiên, các mô hình chỉ có thể xử lý số, vì vậy chúng ta cần tìm cách chuyển văn bản thô thành số. Đó là những gì mà tokenizer làm, và có rất nhiều cách để thực hiện điều này. Mục tiêu đề ra là tìm ra cách biểu diễn có ý nghĩa nhất - nghĩa là cái có ý nghĩa nhất đối với mô hình - và, nếu có thể, là cách biểu diễn nhỏ nhất.

Hãy cùng xem một số ví dụ về thuật toán tokenize và cố gắng trả lời một số câu hỏi bạn có thể có về tokenize.

## Dựa trên từ

<Youtube id="nhJxYji1aho"/>

Loại tokenizer đầu tiên ta nghĩ đến đó là _dựa trên từ vựng_. Nó thường rất dễ thiết lập và sử dụng chỉ với một số quy tắc và nó thường mang lại kết quả tốt. Ví dụ: trong hình ảnh bên dưới, mục tiêu là tách văn bản thô thành các từ và tìm biểu diễn số cho mỗi từ:

<div class="flex justify-center">
  <img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/word_based_tokenization.svg" alt="An example of word-based tokenization."/>
  <img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/word_based_tokenization-dark.svg" alt="An example of word-based tokenization."/>
</div>

Có nhiều cách khác nhau để tách văn bản. Ví dụ: chúng ta có thể sử dụng khoảng trắng để tokenize văn bản thành các từ bằng cách áp dụng hàm `split()` của Python:

```py
tokenized_text = "Jim Henson was a puppeteer".split()
print(tokenized_text)
```

```python out
['Jim', 'Henson', 'was', 'a', 'puppeteer']
```

Ngoài ra còn có các biến thể của tokenize mức từ với các quy tắc bổ sung cho dấu câu. Với loại tokenizer này, chúng ta có thể đúc kết với một bộ "từ vựng" khá lớn, trong đó từ vựng được xác định bằng tổng số token độc lập mà chúng ta có trong corpus (kho ngữ liệu) của mình.

Mỗi từ được gán một ID, bắt đầu từ 0 và tăng dần theo kích thước của bộ từ vựng. Mô hình sử dụng các ID này để xác định từng từ.

Nếu chúng ta muốn bao phủ hoàn toàn một ngôn ngữ bằng tokenize mức từ, chúng ta sẽ cần phải có một chỉ số nhận dạng cho mỗi từ trong ngôn ngữ, điều này sẽ tạo ra một lượng lớn token. Ví dụ: có hơn 500,000 từ trong tiếng Anh, vì vậy để xây dựng bản đồ nối mỗi từ đến một ID đầu vào, chúng ta cần theo dõi ngần đó ID. Hơn nữa, các từ như "dog" được biểu diễn khác với các từ như "dogs", và ban đầu mô hình sẽ không có cách nào để biết rằng "dog" (chó) và "dogs" là tương tự nhau: nó sẽ xác định hai từ này không liên quan. Điều này cũng áp dụng cho các từ tương tự khác, như "run" (chạy) và "running", mà ban đầu mô hình sẽ không thấy là tương tự.

Cuối cùng, chúng ta cần một token tùy chỉnh để đại diện cho các từ không có trong vốn từ vựng của chúng ta. Mã này được gọi là token "không xác định", thường được biểu thị là "[UNK]" hoặc "&lt;unk&gt;". Nói chung, đó là một dấu hiệu xấu nếu bạn thấy trình tokenize đang tạo ra rất nhiều token này, vì nó không thể truy xuất một biểu hiện hợp lý của một từ và bạn đang mất thông tin trong suốt quá trình. Mục tiêu khi tạo từ vựng là làm sao cho trình tokenize mã hóa càng ít từ thành token không xác định càng tốt.

Một cách để giảm số lượng mã thông báo không xác định là đi sâu hơn xuống một cấp, sử dụng tokenize _mức kí tự_.

## Dựa trên kí tự

<Youtube id="ssLq_EK2jLE"/>

- Vốn từ vựng ít hơn nhiều.
- Có ít token ngoài bộ từ vựng (không xác định) hơn nhiều, vì mọi từ đều có thể được xây dựng từ các ký tự.

Nhưng ở đây cũng có một số câu hỏi nảy sinh liên quan đến dấu cách và các dấu câu:

<div class="flex justify-center">
  <img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/character_based_tokenization.svg" alt="An example of character-based tokenization."/>
  <img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/character_based_tokenization-dark.svg" alt="An example of character-based tokenization."/>
</div>

Cách tiếp cận này cũng không hoàn hảo. Vì biểu diễn bây giờ dựa trên các ký tự chứ không phải từ, người ta có thể lập luận rằng, theo trực giác, nó ít ý nghĩa hơn: mỗi ký tự không có nhiều ý nghĩa riêng so với trường hợp của các từ. Tuy nhiên, điều này lại khác nhau tùy theo ngôn ngữ; trong tiếng Trung, chẳng hạn, mỗi ký tự mang nhiều thông tin hơn một ký tự trong ngôn ngữ Latinh.

Một điều khác cần xem xét là chúng ta sẽ có một lượng rất lớn token sẽ được xử lý bởi mô hình của chúng ta: trong khi một từ chỉ là một token duy nhất khi tokenize dựa trên từ, nó có thể dễ dàng chuyển thành 10 token trở lên khi chuyển đổi thành các ký tự.

Để tận dụng tối đa cả hai, chúng ta có thể sử dụng kỹ thuật thứ ba kết hợp hai cách tiếp cận: _tokenize theo từ phụ_.

## Tokenize theo từ phụ

<Youtube id="zHvTiHr506c"/>

Các thuật toán token theo từ phụ dựa trên nguyên tắc rằng các từ được sử dụng thường xuyên không được chia thành các từ phụ nhỏ hơn, nhưng các từ hiếm phải được phân tách thành các từ phụ có ý nghĩa.

Ví dụ: "annoyingly" (khó chịu) có thể được coi là một từ hiếm và có thể được chuyển thành "annoying" và "ly". Cả hai đều có khả năng xuất hiện thường xuyên hơn dưới dạng các từ phụ độc lập, đồng thời nghĩa của "annoying" được giữ nguyên bởi nghĩa kết hợp của "annoying" và "ly".

Dưới đây là một ví dụ cho thấy cách một thuật toán tokenize theo từ phụ sẽ tokenize chuỗi "Let's do tokenization!" (Hãy thực hiện tokenize!):

<div class="flex justify-center">
  <img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/bpe_subword.svg" alt="A subword tokenization algorithm."/>
  <img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/bpe_subword-dark.svg" alt="A subword tokenization algorithm."/>
</div>

Những từ phụ này cung cấp rất nhiều ý nghĩa về mặt ngữ nghĩa: ví dụ: trong ví dụ ở trên "tokenization" được chia thành "token" và "ization", hai token đều có ý nghĩa về mặt ngữ nghĩa đồng thời tiết kiệm không gian (chỉ cần hai token để biểu thị một từ dài). Điều này cho phép chúng ta có thể bao quát tương đối tốt với các từ vựng nhỏ và gần như không có token nào không xác định.

Cách tiếp cận này đặc biệt hữu ích trong các ngôn ngữ tổng hợp như tiếng Thổ Nhĩ Kỳ, nơi bạn có thể tạo (gần như) các từ phức dài tùy ý bằng cách xâu chuỗi các từ phụ lại với nhau.

### Và hơn thế nữa!

Không có gì đáng ngạc nhiên, có rất nhiều kỹ thuật khác, có thể kể đến:

- Byte-level BPE (BPE cấp byte), như được sử dụng trong GPT-2
- WordPiece, như được sử dụng trong BERT
- SentencePiece hoặc Unigram, như được sử dụng trong một số mô hình đa ngôn ngữ

Bây giờ, bạn đã có đủ kiến thức về cách thức hoạt động của tokenize để bắt đầu với API.

## Tải và lưu

Việc tải và lưu tokenizer cũng đơn giản như với các mô hình. Trên thực tế, nó dựa trên hai phương thức giống nhau: `from_pretrained()` và `save_pretrained()`. Các phương thức này sẽ tải hoặc lưu thuật toán được sử dụng bởi tokenizer (hơi giống với _kiến trúc_ của mô hình) cũng như từ vựng của nó (hơi giống với _trọng số_ của mô hình).

Việc tải BERT tokenizer được huấn luyện với cùng một checkpoint với BERT được thực hiện giống như cách tải mô hình, ngoại trừ việc chúng ta sử dụng lớp `BertTokenizer`:

```py
from transformers import BertTokenizer

tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
```

{#if fw === 'pt'}
Tương tự `AutoModel`, lớp `AutoTokenizer` sẽ lấy lớp tokenizer thích hợp trong thư viện dựa trên tên checkpoint và có thể được sử dụng trực tiếp với bất kỳ checkpoint nào:

{:else}
Tương tự `TFAutoModel`, lớp `AutoTokenizer` sẽ lấy lớp tokenizer thích hợp trong thư viện dựa trên tên checkpoint và có thể được sử dụng trực tiếp với bất kỳ checkpoint nào:

{/if}

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
```

Giờ chúng ta có thể sử dụng tokenizer như trong đoạn dưới đây:

```python
tokenizer("Using a Transformer network is simple")
```

```python out
{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102],
 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
```

Lưu một tokenizer giống như khi lưu một mô hình vậy:

```py
tokenizer.save_pretrained("directory_on_my_computer")
```

Chúng ta sẽ trao đổi thêm về `token_type_ids` trong [Chương 3](/course/chapter3), và chúng ta sẽ giải thích cơ chế của từ khoá `attention_mask` sau đó. Đầu tiênm hãy cùng xem cách `input_ids` được tạo ra. Để làm điều này, chúng ta sẽ cần xem xét các phương thức trung gian của tokenizer.

## Mã hoá

<Youtube id="Yffk5aydLzg"/>

Dịch văn bản sang số được gọi là _encoding_ hay _mã hoá_. Việc mã hóa được thực hiện theo quy trình gồm hai bước: tokenize, tiếp theo là chuyển đổi sang ID đầu vào.

Như chúng ta đã thấy, bước đầu tiên là chia văn bản thành các từ (hoặc các phần của từ,theo ký hiệu dấu câu, v.v.), thường được gọi là _token_. Có nhiều quy tắc có thể chi phối quá trình đó, đó là lý do tại sao chúng ta cần khởi tạo trình token bằng cách sử dụng tên của mô hình, để đảm bảo rằng chúng tôi sử dụng cùng các quy tắc đã được sử dụng khi mô hình được huấn luyện trước.

Bước thứ hai là chuyển đổi các token đó thành số để chúng ta có thể xây dựng một tensor từ chúng và đưa chúng vào mô hình. Để làm điều này, tokenizer có _từ vựng_, là phần chúng ta tải xuống khi khởi tạo nó bằng phương thức `from_pretrained()`. Một lần nữa, chúng ta cần sử dụng cùng một bộ từ vựng được sử dụng khi mô hình được huấn luyện trước.

Để hiểu rõ hơn về hai bước, chúng ta sẽ khám phá chúng một cách riêng biệt. Lưu ý rằng chúng tôi sẽ sử dụng một số phương pháp thực hiện các phần của pipeline tokenize riêng biệt để hiển thị cho bạn kết quả trung gian của các bước đó, nhưng trên thực tế, bạn nên gọi tokenize trực tiếp trên đầu vào của mình (như được hiển thị trong phần 2).

### Tokenize

Quá trình tokenize được thực hiện bởi phương thức `tokenize()` của tokenizer:

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

sequence = "Using a Transformer network is simple"
tokens = tokenizer.tokenize(sequence)

print(tokens)
```

Kết quả của phương thức này là một danh sách các chuỗi văn bản hoặc tokens:

```python out
['Using', 'a', 'transform', '##er', 'network', 'is', 'simple']
```

Tokenizer này là một tokenizer dự theo từ phụ: nó chia các từ cho đến khi lấy được các tokens được biểu diễn bởi bộ từ vựng của nó. Ví dụ, `transformer` sẽ được chia thành hai token: `transform` và `##er`.

### Từ token tới ID đầu vào

Quá tình chuyển đổi sang ID đầu vào được thực hiện bởi `convert_tokens_to_ids()` của tokenizer:

```py
ids = tokenizer.convert_tokens_to_ids(tokens)

print(ids)
```

```python out
[7993, 170, 11303, 1200, 2443, 1110, 3014]
```

Các đầu ra này, sau khi được chuyển đổi sang khung tensor thích hợp, có thể được sử dụng làm đầu vào cho một mô hình như đã thấy ở phần trước trong chương này.

<Tip>

✏️ **Thử nghiệm thôi!** Sao chép hai bước cuối cùng (tokenize và chuyển đổi sang ID đầu vào) trên các câu đầu vào mà chúng ta đã sử dụng trong phần 2 ("I've been waiting for a HuggingFace course my whole life." và "I hate this so much!"). Kiểm tra xem bạn có nhận được các ID đầu vào giống như chúng tôi đã nhận trước đó không!

</Tip>

## Giải mã

_Decoding_ hay _giải mã_ thì ngược lại: từ các chỉ số từ vựng, ta muốn trả về một chuỗi văn bản. Điều này có thể được thực hiện với phương thức `decode()` như sau:

```py
decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
print(decoded_string)
```

```python out
'Using a Transformer network is simple'
```

Lưu ý rằng phương pháp `giải mã` không chỉ chuyển đổi các chỉ số trở lại thành token, mà còn nhóm các token là một phần của cùng một từ lại với nhau để tạo ra một câu có thể đọc được. Hành vi này sẽ cực kỳ hữu ích khi chúng ta sử dụng các mô hình dự đoán văn bản mới (văn bản được tạo từ lời nhắc hoặc đối với các bài toán chuỗi-sang-chuỗi như dịch hoặc tóm tắt văn bản).

Bây giờ bạn đã hiểu các hoạt động nguyên tử mà một tokenizer có thể xử lý: tokenize, chuyển đổi sang ID và chuyển đổi ID trở lại một chuỗi. Tuy nhiên, tất cả chỉ mới là sự bắt đầu. Trong phần sau, chúng ta sẽ tiếp cận các giới hạn của nó và xem cách vượt qua chúng.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter2/4.mdx" />

### Hoàn thành cách sử dụng cơ bản!
https://huggingface.co/learn/course/vi/chapter2/7.md

# Hoàn thành cách sử dụng cơ bản!

<CourseFloatingBanner
    chapter={2}
    classNames="absolute z-10 right-0 top-0"
/>

Thật tuyệt vời khi theo dõi khóa học đến đây! Tổng kết lại, trong chương này bạn:

- Đã học các khối cơ bản của mô hình Transformer.
- Đã học những gì tạo nên pipeline cho việc tokenize.
- Biết cách sử dụng mô hình Transformer trong thực tế.
- Đã học cách sử dụng tokenizer để chuyển đổi văn bản thành tensors mà mô hình có thể hiểu được.
- Thiết lập tokenizer và một mô hình cùng nhau để chuyển từ văn bản đầu vào thành dự đoán đầu ra.
- Tìm hiểu những hạn chế của ID đầu vào và tìm hiểu về lớp attantion mask.
- Nghịch các phương pháp tokenizer linh hoạt và có thể định cấu hình.

Từ bây giờ, bạn sẽ có thể tự do khám phá các tài liệu 🤗 Transformers: từ vựng sẽ nghe có vẻ quen thuộc và bạn đã thấy các phương pháp bạn sẽ sử dụng phần lớn thời gian.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter2/7.mdx" />

### Đằng sau pipeline
https://huggingface.co/learn/course/vi/chapter2/2.md

# Đằng sau pipeline

{#if fw === 'pt'}

<CourseFloatingBanner chapter={2}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter2/section2_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter2/section2_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={2}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter2/section2_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter2/section2_tf.ipynb"},
]} />

{/if}

<Tip>
Đây là phần đầu tiên có nội dung hơi khác một chút tùy thuộc vào việc bạn sử dụng PyTorch hay TensorFlow. Chuyển đổi công tắc trên đầu tiêu đề để chọn nền tảng bạn thích!
</Tip>

{#if fw === 'pt'}
<Youtube id="1pedAIvTWXk"/>
{:else}
<Youtube id="wVN12smEvqg"/>
{/if}

Hãy bắt đầu với một ví dụ hoàn chỉnh, cùng xem những gì xảy ra phía sau khi chúng tôi thực thi đoạn mã sau trong [Chương 1](/course/chapter1):

```python
from transformers import pipeline

classifier = pipeline("sentiment-analysis")
classifier(
    [
        "I've been waiting for a HuggingFace course my whole life.",
        "I hate this so much!",
    ]
)
```

và thu được:

```python out
[{'label': 'POSITIVE', 'score': 0.9598047137260437},
 {'label': 'NEGATIVE', 'score': 0.9994558095932007}]
```

Như chúng ta đã thấy trong [Chương 1](/course/chapter1), pipeline này nhóm ba bước lại với nhau: tiền xử lý, đưa các đầu vào qua mô hình và hậu xử lý:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/full_nlp_pipeline.svg" alt="The full NLP pipeline: tokenization of text, conversion to IDs, and inference through the Transformer model and the model head."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/full_nlp_pipeline-dark.svg" alt="The full NLP pipeline: tokenization of text, conversion to IDs, and inference through the Transformer model and the model head."/>
</div>

Hãy cùng đi qua từng phần này.

## Tiền xử lý với một tokenizer

Giống như các mạng nơ-ron khác, các mô hình Transformers không thể xử lý trực tiếp văn bản thô, vì vậy bước đầu tiên trong quy trình của chúng ta là chuyển các đầu vào văn bản thành dạng số mà mô hình có thể hiểu được. Để làm điều này, chúng ta sử dụng *tokenizer*, hàm sẽ chịu trách nhiệm về:

- Tách đầu vào thành các từ, từ phụ, hoặc ký hiệu (như dấu chấm câu) được gọi là *tokens*
- Ánh xạ mỗi token thành một số nguyên
- Thêm đầu vào bổ sung có thể hữu ích cho mô hình

Tất cả quá trình tiền xử lý này cần được thực hiện giống hệt như khi mô hình được huấn luyện trước, vì vậy trước tiên chúng ta cần tải xuống thông tin đó từ [Model Hub](https://huggingface.co/models). Để làm điều này, chúng tôi sử dụng lớp `AutoTokenizer` và phương thức `from_pretrained()` của nó. Sử dụng tên checkpoint mô hình của chúng ta, nó sẽ tự động tìm nạp dữ liệu được liên kết với tokenizer của mô hình và lưu vào bộ nhớ cache (vì vậy nó chỉ được tải xuống lần đầu tiên bạn chạy mã bên dưới).

Vì checkpoint mặc định của `sentiment-analysis` là `distilbert-base-unsased-finetuned-sst-2-english` (bạn có thể xem thẻ mô hình của nó [tại đây](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)), chúng ta chạy như sau:

```python
from transformers import AutoTokenizer

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
```

Khi có tokenizer rồi, chúng ta có thể truyền trực tiếp các câu của mình vào bên trong và nhận lại một từ điển đã sẵn sàng để cung cấp cho mô hình! Việc duy nhất cần làm là chuyển đổi danh sách các ID đầu vào thành các tensor.

Bạn có thể sử dụng 🤗 Transformers mà không phải lo lắng về khung ML nào được sử dụng phía dưới; nó có thể là PyTorch hoặc TensorFlow hoặc Flax đối với một số mô hình. Tuy nhiên, các mô hình Transformer chỉ chấp nhận *tensor* làm đầu vào. Nếu đây là lần đầu tiên bạn nghe về tensor, bạn có thể nghĩ chúng như là mảng NumPy. Mảng NumPy có thể là giá trị vô hướng (0D), vectơ (1D), ma trận (2D) hoặc có nhiều kích thước hơn. Nó thực sự là một tensor; Các tensor của các khung ML khác hoạt động tương tự và thường khởi tạo đơn giản như các mảng NumPy.

Để chỉ định loại tensors mà chúng ta muốn trả về (PyTorch, TensorFlow hoặc thuần NumPy), ta sử dụng tham số `return_tensors`:

{#if fw === 'pt'}
```python
raw_inputs = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!",
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
print(inputs)
```
{:else}
```python
raw_inputs = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!",
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="tf")
print(inputs)
```
{/if}

Đừng lo lắng về padding (đệm) và truncation (cắt bớt) vội; chúng tôi sẽ giải thích những điều đó sau. Những điều chính cần nhớ ở đây là bạn có thể chuyển một câu hoặc một danh sách các câu, cũng như chỉ định loại tensors bạn muốn lấy lại (nếu không có loại nào được truyền vào, mặc định bạn sẽ nhận được kết quả trả về là một danh sách).

{#if fw === 'pt'}

Đây là kết quả tương ứng tensor PyTorch:

```python out
{
    'input_ids': tensor([
        [  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172, 2607,  2026,  2878,  2166,  1012,   102],
        [  101,  1045,  5223,  2023,  2061,  2172,   999,   102,     0,     0,     0,     0,     0,     0,     0,     0]
    ]), 
    'attention_mask': tensor([
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    ])
}
```
{:else}

Đây là kết quả tương ứng tensor Tensorflow:

```python out
{
    'input_ids': <tf.Tensor: shape=(2, 16), dtype=int32, numpy=
        array([
            [  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,  2607,  2026,  2878,  2166,  1012,   102],
            [  101,  1045,  5223,  2023,  2061,  2172,   999,   102,     0,     0,     0,     0,     0,     0,     0,     0]
        ], dtype=int32)>, 
    'attention_mask': <tf.Tensor: shape=(2, 16), dtype=int32, numpy=
        array([
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        ], dtype=int32)>
}
```
{/if}

Bản thân kết quả đầu ra là một từ điển có chứa hai khóa, `input_ids` và `attention_mask`. `input_ids` chứa hai hàng số nguyên (một cho mỗi câu) là số nhận dạng duy nhất của token trong mỗi câu. Chúng tôi sẽ giải thích `attention_mask` là gì ở phần sau của chương này.

## Đi qua mô hình

{#if fw === 'pt'}
Chúng ta có thể tải xuống mô hình được huấn luyện trước của mình giống như cách đã làm với tokenizer. 🤗 Transformers cung cấp một lớp `AutoModel` cũng có phương thức `from_pretrained()`:

```python
from transformers import AutoModel

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModel.from_pretrained(checkpoint)
```
{:else}
Chúng ta có thể tải xuống mô hình được huấn luyện trước của mình giống như cách đã làm với tokenizer. 🤗 Transformers cung cấp một lớp `TFAutoModel` cũng có phương thức `from_pretrained()`:

```python
from transformers import TFAutoModel

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = TFAutoModel.from_pretrained(checkpoint)
```
{/if}

Trong đoạn mã này, chúng ta đã tải xuống cùng một checkpoint đã sử dụng trong pipeline của mình trước đây (nó được lưu vào bộ nhớ đệm rồi) và khởi tạo một mô hình với nó.

Kiến trúc này chỉ chứa mô-đun Transformer cơ sở: với một số đầu vào, nó xuất ra cái mà chúng ta sẽ gọi là *hidden states* (*trạng thái ẩn*), còn được gọi là *đặc trưng*. Đối với mỗi đầu vào mô hình, chúng ta sẽ truy xuất một vectơ đa chiều đại diện cho **sự hiểu theo ngữ cảnh của đầu vào đó bằng mô hình Transformer**.

Nếu điều này không hợp lý, đừng lo lắng về nó. Chúng tôi sẽ giải thích tất cả sau.

Mặc dù những trạng thái ẩn này có thể tự hữu ích, nhưng chúng thường là đầu vào cho một phần khác của mô hình, được gọi là *head* (*đầu*). Trong [Chapter 1](/course/chapter1), các tác vụ khác nhau có thể được thực hiện với cùng một kiến trúc, nhưng mỗi tác vụ này sẽ có một phần đầu khác nhau được liên kết với nó.

### Một vectơ đa chiều

Đầu ra vectơ của mô-đun Transformer thường lớn với ba chiều:

- **Kích thước batch (lô)**: Số chuỗi được xử lý tại một thời điểm (trong ví dụ của chúng tôi là 2).
- **Độ dài chuỗi**: Độ dài biểu diễn số của chuỗi (trong ví dụ của chúng tôi là 16).
- **Kích thước ẩn**: Kích thước vectơ của mỗi đầu vào mô hình.

Nó được cho là "có số chiều cao" vì giá trị cuối cùng. Kích thước ẩn có thể rất lớn (768 là giá trị phổ biến cho các mô hình nhỏ hơn và trong các mô hình lớn hơn, con số này có thể đạt tới 3072 hoặc hơn).

Có thể thấy điều này nếu chúng ta cung cấp các đầu vào đã xử lý trước cho mô hình của mình:

{#if fw === 'pt'}
```python
outputs = model(**inputs)
print(outputs.last_hidden_state.shape)
```

```python out
torch.Size([2, 16, 768])
```
{:else}
```py
outputs = model(inputs)
print(outputs.last_hidden_state.shape)
```

```python out
(2, 16, 768)
```
{/if}

Lưu ý rằng đầu ra của các mô hình 🤗 Transformers hoạt động giống như các `namedtuple` hoặc từ điển. Bạn có thể truy cập các phần tử theo thuộc tính (như chúng ta đã làm) hoặc theo khóa (`outputs["last_hidden_state"]`), hoặc thậm chí theo chỉ mục nếu bạn biết chính xác nơi bạn đang tìm kiếm (`outputs[0]`).

### Đầu mô hình: Hợp lý tời từng con số

Các đầu mô hình lấy vector đa chiều của các trạng thái ẩn làm đầu vào và chiếu chúng lên một chiều khác. Chúng thường bao gồm một hoặc một vài lớp tuyến tính:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/transformer_and_head.svg" alt="A Transformer network alongside its head."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/transformer_and_head-dark.svg" alt="A Transformer network alongside its head."/>
</div>

Đầu ra của mô hình Transformer được gửi trực tiếp đến đầu mô hình để được xử lý.

Trong biểu đồ này, mô hình được biểu diễn bằng lớp nhúng của nó và các lớp tiếp theo. Lớp nhúng chuyển đổi mỗi ID trong đầu vào được mã hóa thành một vectơ đại diện cho token được liên kết. Các lớp tiếp theo thao tác các vectơ đó bằng cách sử dụng cơ chế chú ý để tạo ra biểu diễn cuối cùng của các câu.

Có nhiều kiến trúc khác nhau có sẵn trong 🤗 Transformers, với mỗi kiến trúc được thiết kế xoay quanh một tác vụ cụ thể. Đây là danh sách không đầy đủ:

- `*Model` (truy xuất các trạng thái ẩn)
- `*ForCausalLM`
- `*ForMaskedLM`
- `*ForMultipleChoice`
- `*ForQuestionAnswering`
- `*ForSequenceClassification`
- `*ForTokenClassification`
- and others 🤗

{#if fw === 'pt'}
Với ví dụ của mình, chúng ta sẽ cần một mô hình có đầu phân loại tuần tự (để có thể phân loại các câu là khẳng định hoặc phủ định). Vì vậy, ta sẽ không sử dụng lớp `AutoModel` mà là `AutoModelForSequenceClassification`:

```python
from transformers import AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(**inputs)
```
{:else}
Với ví dụ của mình, chúng ta sẽ cần một mô hình có đầu phân loại tuần tự (để có thể phân loại các câu là khẳng định hoặc phủ định). Vì vậy, ta sẽ không sử dụng lớp `TFAutoModel` mà là `TFAutoModelForSequenceClassification`:

```python
from transformers import TFAutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(inputs)
```
{/if}

Giờ thì nếu chúng ta nhìn vào hình dạng các đầu vào của mình, kích thước sẽ thấp hơn nhiều: đầu mô hình lấy các vectơ đa chiều mà chúng ta đã thấy trước đây và xuất ra các vectơ có chứa hai giá trị (mỗi giá trị tương ứng một nhãn):

```python
print(outputs.logits.shape)
```

{#if fw === 'pt'}
```python out
torch.Size([2, 2])
```
{:else}
```python out
(2, 2)
```
{/if}

Vì chúng ta chỉ có hai câu và hai nhãn, kết quả nhận được từ mô hình của chúng ta là dạng 2 x 2.

## Hậu xử lý đầu ra

Các giá trị chúng ta nhận được dưới dạng đầu ra từ mô hình không nhất thiết phải tự có nghĩa. Hãy cùng xem:

```python
print(outputs.logits)
```

{#if fw === 'pt'}
```python out
tensor([[-1.5607,  1.6123],
        [ 4.1692, -3.3464]], grad_fn=<AddmmBackward>)
```
{:else}
```python out
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
    array([[-1.5606991,  1.6122842],
           [ 4.169231 , -3.3464472]], dtype=float32)>
```
{/if}

Mô hình đã dự đoán `[-1.5607, 1.6123]` cho câu đầu tiên và `[4.1692, -3.3464]` cho câu thứ hai. Đó không phải là xác suất mà là *logits*, điểm số thô, chưa chuẩn hóa được xuất ra bởi lớp cuối cùng của mô hình. Để được chuyển đổi thành xác suất, chúng cần phải trải qua lớp [SoftMax](https://en.wikipedia.org/wiki/Softmax_function) (tất cả các mô hình 🤗 Transformers đều xuất ra logits, vì hàm mất mát cho việc huấn luyện thường sẽ kết hợp hàm kích hoạt cuối cùng, chẳng hạn như SoftMax, với hàm mất mát thực tế, chẳng hạn như entropy chéo):

{#if fw === 'pt'}
```py
import torch

predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
print(predictions)
```
{:else}
```py
import tensorflow as tf

predictions = tf.math.softmax(outputs.logits, axis=-1)
print(predictions)
```
{/if}

{#if fw === 'pt'}
```python out
tensor([[4.0195e-02, 9.5980e-01],
        [9.9946e-01, 5.4418e-04]], grad_fn=<SoftmaxBackward>)
```
{:else}
```python out
tf.Tensor(
[[4.01951671e-02 9.59804833e-01]
 [9.9945587e-01 5.4418424e-04]], shape=(2, 2), dtype=float32)
```
{/if}

Bây giờ chúng ta có thể thấy rằng mô hình đã dự đoán `[0.0402, 0.9598]` cho câu đầu tiên và `[0.9995, 0.0005]` cho câu thứ hai. Đây là những điểm xác suất dễ nhận biết.

Để lấy các nhãn tương ứng với từng vị trí, chúng ta có thể kiểm tra thuộc tính `id2label` của cấu hình mô hình (tìm hiểu thêm về điều này trong phần tiếp theo):

```python
model.config.id2label
```

```python out
{0: 'NEGATIVE', 1: 'POSITIVE'}
```

Bây giờ chúng ta có thể kết luận rằng mô hình đã dự đoán như sau:

- Câu đầu tiên: TIÊU CỰC: 0,0402, TÍCH CỰC: 0,9598
- Câu thứ hai: TIÊU CỰC: 0,9995, TÍCH CỰC: 0,0005

Chúng tôi đã tái tạo thành công ba bước của quy trình: tiền xử lý bằng tokenizers, đưa đầu vào qua mô hình và hậu xử lý! Giờ thì chúng ta hãy dành một chút thời gian để đi sâu hơn vào từng bước đó.

<Tip>

✏️ **Thử nghiệm thôi!** Chọn hai (hoặc nhiều) văn bản của riêng bạn và chạy chúng thông qua `sentiment-analysis`. Sau đó, tự mình lặp lại các bước bạn đã thấy ở đây và kiểm tra xem bạn có thu được kết quả tương tự không!

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter2/2.mdx" />

### Giới thiệu
https://huggingface.co/learn/course/vi/chapter0/1.md

# Giới thiệu

Chào mừng các bạn đến với khoá học Hugging Face. Trong phần Giới thiệu này, chúng tôi sẽ hướng dẫn các bạn cách thiết lập môi trường làm việc. Nếu bạn mới bắt đầu khoá học, chúng tôi khuyến khích các bạn xem [Chương 1](/course/chapter1) trước rồi sau đó quay lại và cài đặt môi trường làm việc để bạn có thể tự thử nghiệm các đoạn mã nguồn.

Tất cả các thư viện mà chúng ta sẽ sử dụng ở khóa học này đều được đóng gói sẵn trong Python, vì vậy ở đây chúng tôi sẽ chỉ cho các bạn cách thiết lập môi trường Python và cài đặt các thư viện cụ thể mà bạn cần.

Chúng tôi sẽ đề cập đến hai cách thiết lập môi trường làm việc, sử dụng sổ ghi chép Colab hoặc môi trường ảo Python. Hãy thoải mái chọn phương pháp phù hợp và thuận tiện với bạn nhất. Đối với người mới học, chúng tôi khuyến khích các bạn nên bắt đầu bằng cách sử dụng sổ ghi chép Colab.

Lưu ý rằng chúng tôi sẽ không đề cập đến hệ thống Windows. Nếu bạn đang sử dụng Windows, chúng tôi khuyên bạn nên dùng sổ ghi chép Colab. Nếu bạn đang sử dụng Linux hoặc macOS, bạn có thể chọn một trong hai cách tiếp cận được mô tả trong phần này.

Hầu hết nội dung khóa học phụ thuộc vào việc bạn có một tài khoản Hugging Face. Chúng tôi khuyến khích bạn tạo một tài khoản ngay bây giờ: [tạo tài khoản](https://huggingface.co/join).

## Sử dụng sổ ghi chép Google Colab

Sử dụng sổ ghi chép Colab có thể coi là cách thiết lập đơn giản nhất; khởi động sổ ghi chép trong trình duyệt của bạn và bắt đầu viết mã thôi!

Nếu bạn không quen thuộc với Colab, chúng tôi khuyên bạn nên bắt đầu bằng cách làm theo phần [giới thiệu](https://colab.research.google.com/notebooks/intro.ipynb). Colab cho phép bạn sử dụng một số phần cứng tăng tốc, như GPU hoặc TPU, và nó miễn phí cho các khối lượng công việc nhỏ hơn.

Khi bạn cảm thấy thoải mái với các thao tác trong Colab, hãy tạo một sổ ghi chép mới và bắt đầu phần cài đặt:

<div class="flex justify-center">
  <img
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter0/new_colab.png"
    alt="An empty colab notebook"
    width="80%"
  />
</div>

Bước tiếp theo là cài đặt các thư viện mà chúng ta sẽ sử dụng trong khóa học này. Chúng ta sẽ sử dụng `pip`, một trình quản lý gói cho Python, để cài đặt. Trong sổ ghi chép, bạn có thể chạy các lệnh hệ thống bằng cách đặt trước chúng ký tự `!`, từ đó, bạn có thể cài đặt thư viện 🤗 Transformers như sau:

```
!pip install transformers
```

Bạn có thể đảm bảo rằng gói đã được cài đặt chính xác bằng cách nhập nó trong thời gian chạy Python của bạn:

```
import transformers
```

<div class="flex justify-center">
  <img
    src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter0/install.gif"
    alt="A gif showing the result of the two commands above: installation and import"
    width="80%"
  />
</div>

Câu lệnh trên cài đặt một phiên bản rất nhẹ của 🤗 Transformers. Đặc biệt, không có khung học máy cụ thể nào (như PyTorch hoặc TensorFlow) được cài đặt. Vì chúng ta sẽ sử dụng nhiều tính năng khác nhau của thư viện, chúng tôi khuyên bạn nên cài đặt phiên bản phát triển, đi kèm với tất cả các thư viện phụ thuộc bắt buộc cho nhiều trường hợp có thể nghĩ tới:

```
!pip install transformers[sentencepiece]
```

Câu lệnh này sẽ mất một chút thời gian để thực thi, nhưng sau đó, bạn sẽ sẵn sàng tiếp tục toàn bộ phần còn lại của khóa học!

## Sử dụng môi trường ảo Python

Nếu bạn thích sử dụng môi trường ảo Python, đầu tiên, bạn cần cài đặt Python trên hệ thống của bạn. Chúng tôi khuyên bạn nên làm theo [hướng dẫn này](https://realpython.com/installing-python/) để bắt đầu.

Khi bạn đã cài đặt Python xong, bạn sẽ có thể chạy các lệnh Python trên giao diện dòng lệch (terminal) của mình. Bạn có thể bắt đầu bằng cách chạy lệnh sau để đảm bảo rằng Python được cài đặt chính xác trước khi tiếp tục các bước tiếp theo: `python --version`. Câu lệnh này sẽ in ra phiên bản Python hiện có trên hệ thống của bạn.

Khi chạy một lệnh Python trên terminal của bạn, chẳng hạn như `python --version`, bạn có thể coi chương trình chạy lệnh là chương trình Python chính trên hệ thống của bạn. Chúng tôi khuyên bạn nên giữ bản cài đặt chính này khỏi bất kỳ gói thư viện nào và sử dụng nó để tạo môi trường riêng biệt cho từng ứng dụng bạn làm việc - với cách này, mỗi ứng dụng có thể có các gói và thư viện phụ thuộc riêng và bạn sẽ không cần phải lo lắng về các vấn đề tiềm ẩn về tương thích với các ứng dụng khác.

Trong Python, điều này được thực hiện với [_virtual environments_](https://docs.python.org/3/tutorial/venv.html), một cây thư mục độc lập chứa một bản cài đặt Python với một phiên bản Python cụ thể cùng với tất cả các gói ứng dụng cần thiết. Việc tạo một môi trường ảo như vậy có thể được thực hiện bằng một số công cụ khác nhau, nhưng chúng ta sẽ sử dụng gói Python chính thức cho mục đích đó, được gọi là [`venv`](https://docs.python.org/3/library/venv.html#module-venv).

Trước tiên, hãy tạo ra thư mục mà bạn muốn chứa ứng dụng của mình - ví dụ: bạn có thể tạo một thư mục mới có tên _transformers-course_ ở gốc của thư mục chính:

```
mkdir ~/transformers-course
cd ~/transformers-course
```

Từ bên trong thư mục này, chúng ta tạo một môi trường ảo bằng cách sử dụng mô-đun Python `venv`:

```
python -m venv .env
```

Bây giờ bạn sẽ có một thư mục được gọi là _.env_ trong thư mục trống của bạn:

```
ls -a
```

```out
.      ..    .env
```

Bạn có thể vào và thoát ra khỏi môi trường ảo của mình bằng câu lệnh `activate` và `deactivate`:

```
# Kích hoạt môi trường ảo
source .env/bin/activate

# Huỷ kích hoạt môi trường ảo
deactivate
```

Bạn có thể đảm bảo rằng môi trường đã được kích hoạt bằng cách chạy lệnh `which python`: nếu nó trỏ đến môi trường ảo thì bạn đã kích hoạt nó thành công!

```
which python
```

```out
/home/<user>/transformers-course/.env/bin/python
```

### Cài đặt các thư viện phụ thuộc

Tương tự với cách sử dụng các phiên bản Google Colab như trong phần trước, bạn sẽ cần cài đặt các gói thư viện cần thiết. Một lần nữa, các bạn có thể cài đặt phiên bản phát triển của 🤗 Transformers bằng trình quản lý gói `pip`:

```
pip install "transformers[sentencepiece]"
```

Bây giờ bạn đã thiết lập xong và sẵn sàng để bắt đầu khám phá nội dung khoá học này!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter0/1.mdx" />

### Tổng kết
https://huggingface.co/learn/course/vi/chapter1/9.md

# Tổng kết

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

Trong chương này, bạn đã biết cách tiếp cận các tác vụ NLP khác nhau bằng cách sử dụng hàm `pipeline()` cấp cao từ 🤗 Transformers. Bạn cũng đã biết cách tìm kiếm và sử dụng các mô hình trong Hub, cũng như cách sử dụng Inference API để kiểm tra các mô hình trực tiếp trong trình duyệt của mình.

Chúng ta đã thảo luận về cách các mô hình Transformer hoạt động ở cấp độ cao và nói về tầm quan trọng của việc học chuyển giao và tinh chỉnh. Một khía cạnh quan trọng, đó là bạn có thể sử dụng kiến trúc đầy đủ hoặc chỉ phần mã hóa hoặc giải mã, tùy thuộc vào loại tác vụ bạn muốn giải quyết. Bảng dưới đây tóm tắt khía cạnh này:

| Mô hình        | Ví dụ                                      | Tác vụ                                                       |
| -------------- | ------------------------------------------ | ------------------------------------------------------------ |
| Mã hoá         | ALBERT, BERT, DistilBERT, ELECTRA, RoBERTa | Phân loại câu, Nhận dạng thực thể có tên, hỏi đáp chích xuất |
| Giải mã        | CTRL, GPT, GPT-2, Transformer XL           | Tạo văn bản                                                  |
| Mã hoá-giải mã | BART, T5, Marian, mBART                    | Tóm tắt, dịch máy, trả lời câu hỏi tổng hợp                  |


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter1/9.mdx" />

### Các mô hình mã hóa
https://huggingface.co/learn/course/vi/chapter1/5.md

# Các mô hình mã hóa

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

<Youtube id="MUqNwgPjJvQ" />

Các mô hình mã hóa chỉ sử dụng phần mã hóa của mô hình Transformer. Ở mỗi bước, các lớp attention có thể truy cập tất cả các từ trong câu ban đầu. Những mô hình này thường có đặc trưng là chú ý "hai chiều" và thường được gọi là mô hình _auto-encoding_ hay _mã hóa tự động_.

Việc huấn luyện trước các mô hình này thường xoay quanh việc phá vỡ một câu đã cho bằng cách nào đó (ví dụ: bằng cách che các từ ngẫu nhiên trong đó) và yêu cầu mô hình tìm hoặc tái tạo lại câu ban đầu.

Mô hình mã hóa phù hợp nhất cho các tác vụ yêu cầu hiểu toàn bộ câu, chẳng hạn như phân loại câu, nhận dạng thực thể được đặt tên (và nói chung là phân loại từ) và trả lời câu hỏi chiết xuất.

Một số mô hình tiêu biểu của nhóm này bao gồm:

- [ALBERT](https://huggingface.co/transformers/model_doc/albert.html)
- [BERT](https://huggingface.co/transformers/model_doc/bert.html)
- [DistilBERT](https://huggingface.co/transformers/model_doc/distilbert.html)
- [ELECTRA](https://huggingface.co/transformers/model_doc/electra.html)
- [RoBERTa](https://huggingface.co/transformers/model_doc/roberta.html)


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter1/5.mdx" />

### Giới thiệu
https://huggingface.co/learn/course/vi/chapter1/1.md

# Giới thiệu

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

## Chào mừng tới 🤗 Khoá học!

<Youtube id="00GKzGyWFEs" />

Khóa học này sẽ dạy bạn về Xử lý Ngôn ngữ Tự nhiên (NLP) sử dụng các thư viện từ hệ sinh thái [Hugging Face](https://huggingface.co/) — [🤗 Transformers](https://github.com/huggingface/transformers), [🤗 Datasets](https://github.com/huggingface/datasets), [🤗 Tokenizers](https://github.com/huggingface/tokenizers), và [🤗 Accelerate](https://github.com/huggingface/accelerate) — cũng như [Hugging Face Hub](https://huggingface.co/models). Khoá học hoàn toàn miễn phí và không có quảng cáo.

## Khóa học có gì?

Dưới đây là tổng quan ngắn gọn về khóa học:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/summary.svg" alt="Brief overview of the chapters of the course.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/summary-dark.svg" alt="Brief overview of the chapters of the course.">
</div>

- Các chương từ 1 đến 4 giới thiệu các khái niệm chính của thư viện 🤗 Transformers. Đến cuối học phần này, bạn sẽ quen thuộc với cách hoạt động của các mô hình Transformer và sẽ biết cách sử dụng mô hình từ [Hugging Face Hub](https://huggingface.co/models), tinh chỉnh nó trên một tập dữ liệu cụ thể, và chia sẻ kết quả của bạn lên Hub!
- Các chương từ 5 đến 8 dạy các kiến thức cơ bản về 🤗 Datasets và 🤗 Tokenizers trước khi đi sâu vào các tác vụ NLP kinh điển. Đến cuối học phần này, bạn sẽ có thể tự mình giải quyết các vấn đề NLP phổ biến nhất.
- Các chương từ 9 đến 12 vượt ra ngoài phạm vi NLP và khám phá cách sử dụng các mô hình Transformer để giải quyết các tác vụ trong xử lý giọng nói và thị giác máy tính. Trong quá trình này, bạn sẽ học cách xây dựng và chia sẻ các bản demo về mô hình của mình cũng như cách tối ưu hóa chúng cho môi trường sản xuất. Đến cuối học phần này, bạn sẽ sẵn sàng áp dụng 🤗 Transformers cho (hầu hết) bất kỳ vấn đề học máy nào!

Khoá học:

- Yêu cầu có kiến thức tốt về Python.
- Nên tìm hiểu sau khi đã hoàn thành một khóa nhập môn về Học sâu, chẳng hạn như [Practical Deep Learning for Coders](https://course.fast.ai/) của [fast.ai](https://www.fast.ai/) hoặc một trong những chương trình được phát triển bởi [DeepLearning.AI](https://www.deeplearning.ai/).
- Không yêu cầu biết trước các kiến thức về [PyTorch](https://pytorch.org/) hoặc [TensorFlow](https://www.tensorflow.org/), mặc dù quen thuộc với một số kiến thức này sẽ hữu ích.

Sau khi bạn hoàn thành khóa học này, chúng tôi khuyến khích bạn xem thêm khoá [Natural Language Processing Specialization](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh) của DeepLearning.AI với nội dung bao gồm một loạt các mô hình NLP truyền thống đáng để biết như Naive Bayes và LSTM!

## Chúng ta là ai?

Giới thiệu về tác giả:

[**Abubakar Abid**](https://huggingface.co/abidlabs) đã hoàn thành chương trình Tiến sĩ về học máy ứng dụng tại Stanford. Trong thời gian học tiến sĩ, anh ấy đã tạo ra [Gradio](https://github.com/gradio-app/gradio), một thư viện Python mã nguồn mở được sử dụng để xây dựng hơn 600,000 bản demo học máy. Gradio được mua lại bởi Hugging Face, nơi Abubakar hiện đóng vai trò là trưởng nhóm học máy.

[**Matthew Carrigan**](https://huggingface.co/Rocketknight1) là một Kỹ sư Học máy tại Hugging Face. Anh ấy sống ở Dublin, Ireland, trước đây là kỹ sư Học máy tại Parse.ly và trước đó là nhà nghiên cứu sau tiến sĩ tại Trinity College Dublin. Anh ấy không tin rằng chúng ta sẽ đạt được AGI bằng cách mở rộng các kiến ​​trúc hiện có, nhưng có niềm tin vào sự bất tử của robot.

[**Lysandre Debut**](https://huggingface.co/lysandre) là một Kỹ sư Học máy tại Hugging Face và đã làm việc với thư viện 🤗 Transformers từ những giai đoạn đầu phát triển. Mục tiêu của anh ấy là làm cho NLP có thể dễ dàng truy cập được từ tất cả mọi người bằng cách phát triển các công cụ với một API rất đơn giản.

[**Sylvain Gugger**](https://huggingface.co/sgugger) là Kỹ sư nghiên cứu tại Hugging Face và là một trong những thành viên cốt lõi của thư viện 🤗 Transformers. Trước đây, anh ấy là Nhà nghiên cứu khoa học tại fast.ai và anh ấy là đồng sáng tác đầu sách _[Deep Learning for Coders with fastai and PyTorch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)_ cùng với Jeremy Howard. Hướng nghiên cứu chính của anh ấy là làm cho việc học sâu trở nên dễ tiếp cận hơn, bằng cách thiết kế và cải tiến các kỹ thuật cho phép các mô hình huấn luyện nhanh trên các tài nguyên hạn chế.

[**Dawood Khan**](https://huggingface.co/dawoodkhan82) là một Kỹ sư Học máy tại Hugging Face. Anh ấy đến từ New York và tốt nghiệp Đại học New York chuyên ngành Khoa học máy tính. Sau khi làm việc với tư cách là Kỹ sư iOS trong một vài năm, Dawood đã nghỉ việc để bắt đầu phát triển Gradio cùng với những người đồng sáng lập của mình. Gradio cuối cùng đã được mua lại bởi Hugging Face.

[**Merve Noyan**](https://huggingface.co/merve) là Chuyên gia về Quan hệ lập trình viên tại Hugging Face, hiện đang phát triển các công cụ và xây dựng nội dung xung quanh chúng để tất cả mọi người có thể tiếp cận học máy dễ dàng hơn.

[**Lucile Saulnier**](https://huggingface.co/SaulLu) là một Kỹ sư Học máy tại Hugging Face, phát triển và hỗ trợ việc sử dụng các công cụ mã nguồn mở. Cô cũng tích cực tham gia vào nhiều dự án nghiên cứu trong lĩnh vực Xử lý Ngôn ngữ Tự nhiên như huấn luyện cộng tác và BigScience.

[**Lewis Tunstall**](https://huggingface.co/lewtun) là một Kỹ sư Học máy tại Hugging Face, tập trung vào việc phát triển các công cụ mã nguồn mở và giúp chúng có thể tiếp cận được với cộng đồng rộng lớn hơn. Anh cũng là đồng tác giả của cuốn sách O’Reilly [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/).

[**Leandro von Werra**](https://huggingface.co/lvwerra) là một Kỹ sư Học máy trong nhóm mã nguồn mở tại Hugging Face và cũng là đồng tác giả của cuốn sách O'Reilly [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). Anh ấy có nhiều năm kinh nghiệm thực tế triển khai các dự án NLP vào sản xuất bằng cách làm việc trên toàn bộ hệ thống học máy.

Bạn đã sẵn sàng chưa? Trong chương này, bạn sẽ học:

- Cách sử dụng hàm `pipeline()` để giải quyết các tác vụ NLP như tạo và phân loại văn bản.
- Về cấu trúc của mạng Transformer.
- Làm thế nào để phân biệt giữa các kiến trúc encoder, decoder, và encoder-decoder cũng như các trường hợp sử dụng.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter1/1.mdx" />

### Transformers có thể làm những gì?
https://huggingface.co/learn/course/vi/chapter1/3.md

# Transformers có thể làm những gì?

<CourseFloatingBanner chapter={1}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter1/section3.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter1/section3.ipynb"},
]} />

Trong phần này, chúng ta sẽ xem các mô hình Transformer có thể làm được những gì và sử dụng công cụ đầu tiên từ thư viện 🤗 Transformers: hàm `pipeline()`.

<Tip>
Bạn có thấy nút <em>Mở trong Colab</em> ở trên cùng bên phải không? Bấm vào nó để mở sổ ghi chép Google Colab với tất cả các đoạn mã của phần này. Nút này sẽ xuất hiện trong bất kỳ phần nào có chứa các mã ví dụ.

Nếu bạn muốn chạy các ví dụ ở máy cá nhân, các bạn có thể tham khảo phần <a href="/course/chapter0">cài đặt</a>.
</Tip>

## Transformers ở muôn nơi!

Các mô hình Transformers được sử dụng để giải quyết tất cả các kiểu tác vụ NLP, giống như các mô hình đề cập trong phần trước. Dưới đây là một số công ty và tổ chức sử dụng Hugging Face và các mô hình Transformer, đồng thời đóng góp lại cho cộng đồng bằng cách chia sẻ các mô hình của họ:

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/companies.PNG" alt="Companies using Hugging Face" width="100%">

Thư viện [🤗 Transformers](https://github.com/huggingface/transformers) cung cấp tính năng tạo và sử dụng các mô hình được chia sẻ đó. [Model Hub](https://huggingface.co/models) chứa hàng nghìn mô hình được huấn luyện trước mà bất kỳ ai cũng có thể tải xuống và sử dụng. Bạn cũng có thể tải các mô hình của riêng mình lên Hub!

<Tip>
⚠️ Hugging Face Hub không giới hạn ở các mô hình Transformer. Bất kỳ ai cũng có thể chia sẻ bất kỳ loại mô hình hoặc bộ dữ liệu nào họ muốn! <a href="https://huggingface.co/join"> Tạo tài khoản huggingface.co </a> để hưởng lợi từ tất cả các tính năng có sẵn này!
</Tip>

Trước khi đi sâu vào cách hoạt động của các mô hình Transformer, hãy cùng xem một vài ví dụ về cách sử dụng chúng để giải quyết một số vấn đề NLP thú vị.

## Làm việc với pipelines

<Youtube id="tiZFewofSLM" />

Đối tượng cơ bản nhất trong thư viện 🤗 Transformers là hàm `pipeline()`. Hàm kết nối một mô hình với các bước tiền xử lý và hậu xử lý cần thiết, cho phép chúng ta nhập trực tiếp bất kỳ văn bản nào và nhận được câu trả lời dễ hiểu:

```python
from transformers import pipeline

classifier = pipeline("sentiment-analysis")
classifier("I've been waiting for a HuggingFace course my whole life.")
```

```python out
[{'label': 'POSITIVE', 'score': 0.9598047137260437}]
```

Chúng tôi thậm chí có thể truyền vào nhiều câu!

```python
classifier(
    ["I've been waiting for a HuggingFace course my whole life.", "I hate this so much!"]
)
```

```python out
[{'label': 'POSITIVE', 'score': 0.9598047137260437},
 {'label': 'NEGATIVE', 'score': 0.9994558095932007}]
```

Theo mặc định, quy trình này chọn một mô hình cụ thể được huấn luyện trước và đã được tinh chỉnh để phân tích cảm xúc văn bản tiếng Anh. Mô hình được tải xuống và lưu vào bộ nhớ cache khi bạn tạo đối tượng `classifier`. Nếu bạn chạy lại lệnh, mô hình đã lưu trong bộ nhớ cache sẽ được sử dụng thay thế và không cần tải lại mô hình một lần nữa.

Có ba bước chính khi bạn chuyển một số văn bản vào một pipeline:

1. Văn bản được tiền xử lý thành một định dạng mà mô hình có thể hiểu được.
2. Các đầu vào đã tiền xử lý được đưa vào mô hình.
3. Các dự đoán của mô hình được hậu xử lý để bạn có thể hiểu được chúng.

Một số [pipeline](https://huggingface.co/transformers/main_classes/pipelines.html) hiện có có thể kể đến:

- `feature-extraction` (trích xuất biểu diễn vectơ của một văn bản)
- `fill-mask`
- `ner` (nhận dạng thực thể)
- `question-answering`
- `sentiment-analysis`
- `summarization`
- `text-generation`
- `translation`
- `zero-shot-classification`

Chúng ta hãy cùng xem một vài ví dụ trong số kể trên!

## Phân loại không mẫu (Zero-shot)

Chúng ta sẽ bắt đầu với việc giải quyết một tác vụ khó nhằn hơn: chúng ta cần phân loại các văn bản chưa được dán nhãn. Đây là một tình huống phổ biến trong các dự án thực tế vì việc đánh nhãn văn bản thường tốn nhiều thời gian và yêu cầu kiến thức chuyên môn. Đối với trường hợp này, `zero-shot-classification` là một phương án mạnh mẽ: nó cho phép bạn chỉ định nhãn nào sẽ sử dụng để phân loại, vì vậy bạn không cần phải dựa vào các nhãn của mô hình được huấn luyện trước.

Bạn đã thấy cách mô hình có thể phân loại một câu là tích cực hay tiêu cực bằng cách sử dụng hai nhãn đó - nhưng nó cũng có thể phân loại văn bản bằng cách sử dụng bất kỳ bộ nhãn nào khác mà bạn thích.

```python
from transformers import pipeline

classifier = pipeline("zero-shot-classification")
classifier(
    "This is a course about the Transformers library",
    candidate_labels=["education", "politics", "business"],
)
```

```python out
{'sequence': 'This is a course about the Transformers library',
 'labels': ['education', 'business', 'politics'],
 'scores': [0.8445963859558105, 0.111976258456707, 0.043427448719739914]}
```

Quy trình này được gọi là _zero-shot_ (không mẫu) vì bạn không cần tinh chỉnh mô hình trên dữ liệu của bạn để sử dụng. Nó có thể trực tiếp trả về xác suất cho bất kỳ danh sách nhãn nào bạn muốn!

<Tip>

✏️ **Thử nghiệm thôi!** Cùng thử các chuỗi văn bản và các nhãn riêng của bạn để xem mô hình hoạt động như thế nào.

</Tip>

## Tạo văn bản

Giờ chúng ta hãy cùng xem cách sử dụng một pipeline để tạo ra văn bản. Ý tưởng chính ở đây là bạn cung cấp một lời gợi ý và mô hình sẽ tự động hoàn thành nó bằng cách tạo ra phần văn bản còn lại. Điều này tương tự như tính năng gợi ý văn bản được tìm thấy trên điện thoại. Việc tạo văn bản liên quan đến sự ngẫu nhiên, vì vậy nếu bạn không nhận được kết quả như hình dưới đây cũng là điều dễ hiểu.

```python
from transformers import pipeline

generator = pipeline("text-generation")
generator("In this course, we will teach you how to")
```

```python out
[{'generated_text': 'In this course, we will teach you how to understand and use '
                    'data flow and data interchange when handling user data. We '
                    'will be working with one or more of the most commonly used '
                    'data flows — data flows of various types, as seen by the '
                    'HTTP'}]
```

Bạn có thể kiểm soát số lượng chuỗi khác nhau được tạo với tham số `num_return_sequences` và tổng độ dài của văn bản đầu ra với tham số `max_length`.

<Tip>

✏️ **Thử nghiệm thôi!** Sử dụng `num_return_sequences` và `max_length` để tạo ra hai câu, mỗi câu chứa 15 từ.

</Tip>

## Sử dụng một mô hình bất kỳ từ Hub trong pipeline

Các ví dụ trước đã sử dụng mô hình mặc định cho các tác vụ, nhưng bạn cũng có thể chọn một mô hình cụ thể từ Hub để sử dụng trong pipeline cho một tác vụ cụ thể - ví dụ, tạo văn bản. Truy cập [Model Hub](https://huggingface.co/models) và bấm vào thẻ tương ứng ở bên trái để chỉ hiển thị các mô hình được hỗ trợ cho tác vụ đó. Bạn sẽ đến một trang như [trang này](https://huggingface.co/models?pipeline_tag=text-generation).

Hãy thử mô hình [`distilgpt2`](https://huggingface.co/distilgpt2)! Đây là cách tải nó vào cùng một pipeline như phần trước:

```python
from transformers import pipeline

generator = pipeline("text-generation", model="distilgpt2")
generator(
    "In this course, we will teach you how to",
    max_length=30,
    num_return_sequences=2,
)
```

```python out
[{'generated_text': 'In this course, we will teach you how to manipulate the world and '
                    'move your mental and physical capabilities to your advantage.'},
 {'generated_text': 'In this course, we will teach you how to become an expert and '
                    'practice realtime, and with a hands on experience on both real '
                    'time and real'}]
```

Bạn có thể tinh chỉnh việc tìm kiếm cho một mô hình của mình bằng cách nhấp vào các thẻ ngôn ngữ và chọn một mô hình sẽ tạo văn bản bằng ngôn ngữ khác. Model Hub thậm chí còn chứa các checkpoints cho các mô hình đa ngôn ngữ hỗ trợ một số ngôn ngữ khác nhau.

Sau khi bạn chọn một mô hình bằng cách bấm vào nó, bạn sẽ thấy rằng có một tiện ích cho phép bạn dùng thử trực tuyến. Bằng cách này, bạn có thể nhanh chóng kiểm tra khả năng của mô hình trước khi tải xuống.

<Tip>

✏️ **Thử nghiệm thôi!** Sử dụng bộ lọc để tìm mô hình tạo văn bản cho ngôn ngữ khác. Hãy thoải mái chơi với tiện ích này và sử dụng nó theo một pipeline!

</Tip>

### Inference API

Tất cả các mô hình có thể được kiểm tra trực tiếp thông qua trình duyệt của bạn bằng cách sử dụng Inference API, có sẵn trên [trang web Hugging Face](https://huggingface.co/). Bạn có thể chơi với mô hình trực tiếp trên trang này bằng cách nhập văn bản tùy chỉnh và xem mô hình xử lý dữ liệu đầu vào.

Inference API hỗ trợ tiện ích này cũng có sẵn dưới dạng sản phẩm trả phí, rất hữu ích nếu bạn cần nó cho quy trình công việc của mình. Xem [trang giá](https://huggingface.co/pricing) để biết thêm chi tiết.

## Điền vào chỗ trống

Pipeline tiếp theo bạn sẽ thử nghiệm, đó là `fill-mask`. Ý tưởng của tác vụ này là điền vào chỗ trống trong một văn bản nhất định:

```python
from transformers import pipeline

unmasker = pipeline("fill-mask")
unmasker("This course will teach you all about <mask> models.", top_k=2)
```

```python out
[{'sequence': 'This course will teach you all about mathematical models.',
  'score': 0.19619831442832947,
  'token': 30412,
  'token_str': ' mathematical'},
 {'sequence': 'This course will teach you all about computational models.',
  'score': 0.04052725434303284,
  'token': 38163,
  'token_str': ' computational'}]
```

Tham số `top_k` kiểm soát số lượng khả năng bạn muốn được hiển thị. Lưu ý rằng ở đây mô hình điền từ vào vị trí bị che bởi từ `<mask>`, thường được gọi là *mask token*. Các mô hình điền khác có thể có các kiểu che từ khác nhau, vì vậy, tốt nhất nên xác minh từ bị che phù hợp khi khám phá các mô hình khác. Một cách để kiểm tra đó là xem từ bị che được sử dụng trong tiện ích con.

<Tip>

✏️ **Thử nghiệm thôi!** Tìm kiếm mô hình `bert-base-cased` trên Hub và xác định từ bị che của nó trong tiện ích Inference API. Mô hình này dự đoán điều gì cho câu trong ví dụ về `pipeline` của chúng ta ở trên?

</Tip>

## Nhận dạng thực thể

Nhận dạng thực thể được đặt tên (NER) là một tác vụ trong đó mô hình phải tìm ra những phần của văn bản đầu vào tương ứng với các thực thể như người, địa điểm, hoặc tổ chức. Hãy xem một ví dụ:

```python
from transformers import pipeline

ner = pipeline("ner", grouped_entities=True)
ner("My name is Sylvain and I work at Hugging Face in Brooklyn.")
```

```python out
[{'entity_group': 'PER', 'score': 0.99816, 'word': 'Sylvain', 'start': 11, 'end': 18}, 
 {'entity_group': 'ORG', 'score': 0.97960, 'word': 'Hugging Face', 'start': 33, 'end': 45}, 
 {'entity_group': 'LOC', 'score': 0.99321, 'word': 'Brooklyn', 'start': 49, 'end': 57}
]
```

Ở đây, mô hình đã xác định chính xác rằng Sylvain là một người (PER), Hugging Face là một tổ chức (ORG) và Brooklyn là một địa điểm (LOC).

Chúng ta truyền `grouped_entities = True` vào trong hàm pipeline để yêu cầu pipeline nhóm lại các phần thuộc cùng một thực thể trong câu với nhau: ở đây mô hình đã nhóm chính xác "Hugging" và "Face" thành một tổ chức duy nhất, mặc dù tên bao gồm nhiều từ. Trên thực tế, như chúng ta sẽ thấy trong chương tiếp theo, quá trình tiền xử lý thậm chí còn chia một số từ thành các phần nhỏ hơn. Ví dụ: `Sylvain` được chia thành bốn phần: `S`, `##yl`, `##va`, và `##in`. Trong bước hậu xử lý, pipeline đã tập hợp lại thành công các phần đó.

<Tip>

✏️ **Thử nghiệm thôi!** Tìm kiếm trên Model Hub để tìm một mô hình có thể thực hiện gán nhãn từ loại (thường được viết tắt là POS) bằng tiếng Anh. Mô hình này dự đoán điều gì cho câu trong ví dụ trên?

</Tip>

## Hỏi đáp

Pipeline `question-answering` trả lời các câu hỏi sử dụng thông tin ngữ cảnh cho trước:

```python
from transformers import pipeline

question_answerer = pipeline("question-answering")
question_answerer(
    question="Where do I work?",
    context="My name is Sylvain and I work at Hugging Face in Brooklyn",
)
```

```python out
{'score': 0.6385916471481323, 'start': 33, 'end': 45, 'answer': 'Hugging Face'}
```

Lưu ý rằng pipeline này hoạt động bằng cách trích xuất thông tin từ ngữ cảnh được cung cấp; nó không tạo ra câu trả lời.

## Tóm tắt

Tóm tắt là tác vụ thu gọn một văn bản thành một văn bản ngắn hơn trong khi vẫn giữ tất cả (hoặc hầu hết) các ý quan trọng được đề cập trong văn bản. Dưới đây là một ví dụ:

```python
from transformers import pipeline

summarizer = pipeline("summarization")
summarizer(
    """
    America has changed dramatically during recent years. Not only has the number of 
    graduates in traditional engineering disciplines such as mechanical, civil, 
    electrical, chemical, and aeronautical engineering declined, but in most of 
    the premier American universities engineering curricula now concentrate on 
    and encourage largely the study of engineering science. As a result, there 
    are declining offerings in engineering subjects dealing with infrastructure, 
    the environment, and related issues, and greater concentration on high 
    technology subjects, largely supporting increasingly complex scientific 
    developments. While the latter is important, it should not be at the expense 
    of more traditional engineering.

    Rapidly developing economies such as China and India, as well as other 
    industrial countries in Europe and Asia, continue to encourage and advance 
    the teaching of engineering. Both China and India, respectively, graduate 
    six and eight times as many traditional engineers as does the United States. 
    Other industrial countries at minimum maintain their output, while America 
    suffers an increasingly serious decline in the number of engineering graduates 
    and a lack of well-educated engineers.
"""
)
```

```python out
[{'summary_text': ' America has changed dramatically during recent years . The '
                  'number of engineering graduates in the U.S. has declined in '
                  'traditional engineering disciplines such as mechanical, civil '
                  ', electrical, chemical, and aeronautical engineering . Rapidly '
                  'developing economies such as China and India, as well as other '
                  'industrial countries in Europe and Asia, continue to encourage '
                  'and advance engineering .'}]
```

Tương tự như tạo văn bản, bạn có thể tuỳ chỉnh `max_length` và `min_length` cho kết quả trả về.

## Dịch máy

Đối với dịch máy, bạn có thể sử dụng mô hình mặc định nếu bạn cung cấp một cặp ngôn ngữ trong tên tác vụ (chẳng hạn như `"translation_en_to_fr"`), nhưng cách dễ nhất là chọn mô hình bạn muốn sử dụng trên [Model Hub](https://huggingface.co/models). Tại đây, chúng ta sẽ thử dịch từ tiếng Pháp sang tiếng Anh:

```python
from transformers import pipeline

translator = pipeline("translation", model="Helsinki-NLP/opus-mt-fr-en")
translator("Ce cours est produit par Hugging Face.")
```

```python out
[{'translation_text': 'This course is produced by Hugging Face.'}]
```

Giống như tạo và tóm tắt văn bản, bạn có thể chỉ định giá trị `max_length` hoặc `min_length` cho kết quả trả về.

<Tip>

✏️ **Thử nghiệm thôi!** Tìm kiếm các mô hình dịch ở các ngôn ngữ khác và cố gắng dịch câu trước đó sang một vài ngôn ngữ khác nhau.

</Tip>

Các pipeline ở trên hầu hết phục vụ mục đích trình diễn. Chúng được lập trình cho các tác vụ cụ thể và không thể thực hiện các biến thể của chúng. Trong chương tiếp theo, bạn sẽ tìm hiểu những gì bên trong một hàm  `pipeline()`  và cách tinh chỉnh hành vi của nó.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter1/3.mdx" />

### CCác mô hình giải mã
https://huggingface.co/learn/course/vi/chapter1/6.md

# CCác mô hình giải mã

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

<Youtube id="d_ixlCubqQw" />

Mô hình giải mã chỉ sử dụng phần giải mã của mô hình Transformer. Ở mỗi bước, đối với một từ nhất định, các lớp attention chỉ có thể truy cập các từ được đặt trước nó trong câu. Những mô hình này thường được gọi là mô hình _auto-regressive_ hay _hồi quy tự động_.

Việc huấn luyện trước các mô hình giải mã thường xoay quanh việc dự đoán từ tiếp theo trong câu.

Các mô hình này phù hợp nhất cho các tác vụ liên quan đến việc tạo văn bản.

Một số mô hình tiêu biểu của nhóm này bao gồm:

- [CTRL](https://huggingface.co/transformers/model_doc/ctrl.html)
- [GPT](https://huggingface.co/docs/transformers/model_doc/openai-gpt)
- [GPT-2](https://huggingface.co/transformers/model_doc/gpt2.html)
- [Transformer XL](https://huggingface.co/transformers/model_doc/transfo-xl.html)


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter1/6.mdx" />

### Đố vui cuối chương
https://huggingface.co/learn/course/vi/chapter1/10.md

# Đố vui cuối chương

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

Chương này bao gồm rất nhiều mặt! Đừng lo lắng nếu bạn không nắm được tất cả các chi tiết; các chương tiếp theo sẽ giúp bạn hiểu mọi thứ hoạt động như thế nào.

Tuy nhiên, trước tiên, hãy kiểm tra những gì bạn đã học được trong chương này!

### 1. Khám phá Hub và tìm `roberta-large-mnli`. Nó phục vụ cho tác vụ gì?

<Question
  choices={[
    {
      text: "Tóm tắt",
      explain:
        'Xem lại tại <a href="https://huggingface.co/roberta-large-mnli">roberta-large-mnli page</a>.',
    },
    {
      text: "Phân loại văn bản",
      explain:
        "Chính xác hơn, nó phân loại nếu hai câu được liên kết một cách hợp lý qua ba nhãn (mâu thuẫn, trung lập, vướng mắc) — hay tác vụ còn được gọi là <em>luận suy ngôn ngữ tự nhiên</em>.",
      correct: true,
    },
    {
      text: "Tạo văn bản",
      explain:
        'Xem lại tại <a href="https://huggingface.co/roberta-large-mnli">roberta-large-mnli page</a>.',
    },
  ]}
/>

### 2. Đoạn mã sau sẽ trả về cái gì?

```py
from transformers import pipeline

ner = pipeline("ner", grouped_entities=True)
ner("My name is Sylvain and I work at Hugging Face in Brooklyn.")
```

<Question
  choices={[
    {
      text: 'Nó sẽ trả về điểm phân loại cho câu này, với các nhãn "positive" hoặc "negative".',
      explain:
        "Không chính xác - đây là mô tả của pipeline <code>sentiment-analysis</code>.",
    },
    {
      text: "Nó sẽ trả về một văn bản được tạo để hoàn thành câu này.",
      explain:
        "Không chính xác - đây là mô tả của pipeline<code>text-generation</code>.",
    },
    {
      text: "Nó sẽ trả về các từ đại diện cho người, tổ chức hoặc địa điểm.",
      explain:
        'Hơn nữa, với <code>grouped_entities=True</code>, nó sẽ nhóm các từ thuộc cùng một thực thể lại với nhau, ví dụ như "Hugging Face".',
      correct: true,
    },
  ]}
/>

### 3. Từ nào có thể thay thế ... trong đoạn mã dưới đây?

```py
from transformers import pipeline

filler = pipeline("fill-mask", model="bert-base-cased")
result = filler("...")
```

<Question
  choices={[
    {
      text: "This &#60;mask> has been waiting for you.",
      explain:
        "Không chính xác. Kiểm tra thẻ mô hình <code>bert-base-cased</code> và cố gắng phát hiện lỗi của bạn.",
    },
    {
      text: "This [MASK] has been waiting for you.",
      explain: "Chính xác! Đáp án là [MASK].",
      correct: true,
    },
    {
      text: "This man has been waiting for you.",
      explain:
        "Không chính xác. Pipeline này sẽ điền vào các từ bị che đi, vì vậy nó cần một [MASK] token ở đâu đó.",
    },
  ]}
/>

### 4. Tại sao đoạn mã này sẽ lỗi?

```py
from transformers import pipeline

classifier = pipeline("zero-shot-classification")
result = classifier("This is a course about the Transformers library")
```

<Question
  choices={[
    {
      text: "Pipeline này này yêu cầu cấp nhãn để phân loại văn bản này.",
      explain:
        "Đúng — đoạn mã chính xác cần bao gồm <code>candidate_labels=[...]</code>.",
      correct: true,
    },
    {
      text: "Pipeline này yêu cầu nhiều câu thay vì một câu.",
      explain:
        "Không chính xác, mặc dù khi được sử dụng đúng cách, pipeline này có thể lấy một danh sách các câu để xử lý (giống như tất cả các pipeline khác).",
    },
    {
      text: "Thư viện 🤗 Transformers bị hỏng, như thường lệ.",
      explain:
        "Chúng tôi sẽ không đánh giá cao câu trả lời này với bất kỳ bình luận nào!",
    },
    {
      text: "Pipeline yêu cầu đầu vào dài hơn; pipeline này quá ngắn.",
      explain:
        "Không chính xác. Lưu ý rằng một văn bản rất dài sẽ bị cắt bớt khi xử lý bằng pipeline này.",
    },
  ]}
/>

### 5. "Học chuyển giao" nghĩa là gì?

<Question
	choices={[
		{
			text: "Chuyển giao tri thức của một mô hình được huấn luyện trước sang một mô hình mới bằng cách huấn luyện nó trên cùng một bộ dữ liệu.",
			explain: "Không, đó sẽ là hai phiên bản của cùng một mô hình."
		},
		{
			text: "Chuyển giao tri thức của một mô hình được huấn luyện trước sang một mô hình mới bằng cách khởi tạo mô hình thứ hai với trọng số của mô hình đầu tiên.",
			explain: "Chính xác: khi mô hình thứ hai được huấn luyện về một tác vụ mới, nó sẽ *chuyển giao* tri thức thức của mô hình đầu tiên.",
			correct: true
		},
		{
			text: "Chuyển giao tri thức của một mô hình được huấn luyện trước sang một mô hình mới bằng cách xây dựng mô hình thứ hai có cùng kiến trúc với mô hình đầu tiên.",
			explain: "Kiến trúc chỉ là cách mô hình được xây dựng; không có kiến thức được chia sẻ hoặc chuyển giao trong trường hợp này."
		}
	]}
/>

### 6. Đúng hay sai? Một mô hình ngôn ngữ thường không cần nhãn cho quá trình huấn luyện trước của nó.

<Question
  choices={[
    {
      text: "Đúng",
      explain:
        "Quá trình huấn luyện trước thường là <em>tự giám sát</em>, có nghĩa là các nhãn được tạo ra tự động từ các đầu vào (như dự đoán từ tiếp theo hoặc điền vào một số từ bị che).",
      correct: true,
    },
    {
      text: "Sai",
      explain: "Đây không phải đáp án chính xác.",
    },
  ]}
/>

### 7. Chọn câu mô tả đúng nhất các thuật ngữ "mô hình", "kiến trúc" và "trọng số".

<Question
  choices={[
    {
      text: "Nếu một mô hình là một tòa nhà, thì kiến trúc của nó là bản thiết kế và trọng số là những người sống bên trong.",
      explain:
        "Theo phép ẩn dụ này, trọng lượng sẽ là những viên gạch và các vật liệu khác được sử dụng để xây dựng tòa nhà.",
    },
    {
      text: "Một kiến trúc là một bản đồ để xây dựng một mô hình và trọng số của nó là các thành phố được thể hiện trên bản đồ.",
      explain:
        "Vấn đề với phép ẩn dụ này là bản đồ thường đại diện cho một thực tế đang tồn tại (chỉ có một thành phố ở Pháp tên là Paris). Đối với một kiến trúc nhất định, có thể có nhiều trọng số.",
    },
    {
      text: "Một kiến trúc là sự liên tiếp của các hàm toán học để xây dựng một mô hình và trọng số của nó là các tham số hàm đó.",
      explain:
        "Cùng một tập hợp các hàm toán học (kiến trúc) có thể được sử dụng để xây dựng các mô hình khác nhau bằng cách sử dụng các tham số (trọng số) khác nhau.",
      correct: true,
    },
  ]}
/>

### 8. Bạn sẽ sử dụng loại mô hình nào trong số những loại mô hình này để hoàn thành lời nhắc với văn bản được tạo ra?

<Question
  choices={[
    {
      text: "Một mô hình mã hoá",
      explain:
        "Một mô hình mã hóa tạo ra một biểu diễn của toàn bộ câu, phù hợp hơn cho các tác vụ như phân loại.",
    },
    {
      text: "Một mô hình giải hoá",
      explain:
        "Các mô hình giải mã hoàn toàn phù hợp để tạo văn bản từ lời nhắc.",
      correct: true,
    },
    {
      text: "Một mô hình chuỗi-sang-chuỗi",
      explain:
        "Mô hình chuỗi-sang-chuỗi phù hợp cho các tác vụ mà bạn muốn tạo các câu liên quan đến các câu đầu vào, chứ không phải một lời nhắc nhất định.",
    },
  ]}
/>

### 9. Bạn sẽ sử dụng kiểu mô hình nào để tóm tắt văn bản?

<Question
  choices={[
    {
      text: "Một mô hình mã hoá",
      explain:
        "Một mô hình mã hóa tạo ra một biểu diễn của toàn bộ câu, phù hợp hơn cho các tác vụ như phân loại.",
    },
    {
      text: "Một mô hình giải hoá",
      explain:
        "Các mô hình giải mã rất tốt để tạo văn bản đầu ra (như tóm tắt), nhưng chúng không có khả năng khai thác ngữ cảnh như toàn bộ văn bản để tóm tắt.",
    },
    {
      text: "Một mô hình chuỗi-sang-chuỗi",
      explain:
        "Mô hình chuỗi-sang-chuỗi hoàn toàn phù hợp với tác vụ tóm tắt văn bản.",
      correct: true,
    },
  ]}
/>

### 10. Bạn sẽ sử dụng kiểu mô hình nào trong số những kiểu mô hình này để phân loại đầu vào văn bản theo các nhãn nhất định?

<Question
  choices={[
    {
      text: "Một mô hình mã hoá",
      explain:
        "Một mô hình mã hóa tạo ra biểu diễn của toàn bộ câu, phù hợp hơn cho các tác vụ như phân loại.",
      correct: true,
    },
    {
      text: "Một mô hình giải hoá",
      explain:
        "Các mô hình giải mã phù hợp để tạo văn bản đầu ra, không phải trích xuất nhãn ra khỏi một câu.",
    },
    {
      text: "Một mô hình chuỗi-sang-chuỗi",
      explain:
        "Mô hình chuỗi-sang-chuỗi phù hợp hơn cho các tác vụ mà bạn muốn tạo văn bản dựa trên một câu đầu vào, không phải nhãn.",
    },
  ]}
/>

### 11. Sự sai lệch quan sát thấy trong một mô hình có thể bắt nguồn nào?

<Question
  choices={[
    {
      text: "Mô hình là phiên bản tinh chỉnh của một mô hình được huấn luyện trước và nó đã chọn ra sai lệch từ đó.",
      explain:
        "Khi áp dụng Học chuyển giao, sự sai lệch trong mô hình huấn luyện tới mô hình tinh chỉnh.",
      correct: true,
    },
    {
      text: "Dữ liệu mà mô hình sử dụng huấn luyện bị sai lệch.",
      explain:
        "Đây là một trường hợp tiêu biểu của sai lệch, nhưng không phải là nguyên do duy nhất.",
      correct: true,
    },
    {
      text: "Các thông số mô hình tối ưu bị sai lệch.",
      explain:
        "Một nguồn sai lệch ít rõ ràng hơn là cách mô hình được huấn luyện. Mô hình của bạn sẽ tối ưu hóa một cách mù quáng cho bất kỳ chỉ số nào bạn đã chọn, mà không cần suy nghĩ gì thêm.",
      correct: true,
    },
  ]}
/>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter1/10.mdx" />

### Thiên kiến và hạn chế
https://huggingface.co/learn/course/vi/chapter1/8.md

# Thiên kiến và hạn chế

<CourseFloatingBanner chapter={1}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {
      label: "Google Colab",
      value:
        "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter1/section8.ipynb",
    },
    {
      label: "Aws Studio",
      value:
        "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter1/section8.ipynb",
    },
  ]}
/>

Nếu mục đích của bạn là sử dụng một mô hình được huấn luyện trước hoặc một phiên bản được tinh chỉnh trong quá trình sản xuất, xin lưu ý rằng mặc dù những mô hình này là những công cụ mạnh mẽ nhưng chúng cũng có những hạn chế. Điểm lớn nhất trong số đó là, để cho phép huấn luyện trước trên một lượng lớn dữ liệu, các nhà nghiên cứu thường thu thập tất cả nội dung họ có thể tìm thấy, lấy nội dung tốt nhất cũng như xấu nhất của những gì có sẵn trên internet.

Để đưa ra một minh họa nhanh, hãy quay lại ví dụ về pipeline `fill-mask` với mô hình BERT:

```python
from transformers import pipeline

unmasker = pipeline("fill-mask", model="bert-base-uncased")
result = unmasker("This man works as a [MASK].")
print([r["token_str"] for r in result])

result = unmasker("This woman works as a [MASK].")
print([r["token_str"] for r in result])
```

```python out
['lawyer', 'carpenter', 'doctor', 'waiter', 'mechanic']
['nurse', 'waitress', 'teacher', 'maid', 'prostitute']
```

Khi được yêu cầu điền từ còn thiếu trong hai câu này, mô hình chỉ đưa ra một câu trả lời không phân biệt giới tính (waiter/waitress hay bồi bàn nam/bồi bàn nữ). Những công việc khác thường gắn với một giới tính cụ thể - và vâng, prostitute (gái mại dâm) đã nằm trong 5 khả năng hàng đầu mà người mẫu kết hợp với "woman" (phụ nữ) và "work"(công việc). Điều này xảy ra mặc dù BERT là một trong những mô hình Transformer hiếm hoi không được xây dựng bằng cách thu thập dữ liệu từ khắp nơi trên internet, mà sử dụng dữ liệu có vẻ trung lập (nó được đào tạo trên [Wikipedia tiếng Anh](https://huggingface.co/datasets/wikipedia) và bộ dữ liệu [BookCorpus](https://huggingface.co/datasets/bookcorpus).

Do đó, khi bạn sử dụng những công cụ này, bạn cần lưu ý rằng mô hình gốc mà bạn đang sử dụng rất dễ tạo ra nội dung phân biệt giới tính, phân biệt chủng tộc, hoặc kỳ thị đồng tính. Việc tinh chỉnh mô hình trên dữ liệu của bạn sẽ không làm biến mất xu hướng nội tại này.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter1/8.mdx" />

### Cơ chế hoạt động của Transformer?
https://huggingface.co/learn/course/vi/chapter1/4.md

# Cơ chế hoạt động của Transformer?

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

Trong phần này, chúng ta sẽ tìm hiểu kiến trúc của các mô hình Transformer.

## Lịch sử phát triển của Transformers

Dưới đây là một số mốc tham khảo trong lịch sử (ngắn) phát triển của các mô hìnhn Transformer:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers_chrono.svg" alt="A brief chronology of Transformers models.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers_chrono-dark.svg" alt="A brief chronology of Transformers models.">
</div>

[Kiến trúc Transformer](https://arxiv.org/abs/1706.03762) được giới thiệu vào tháng 6 năm 2017. Trọng tâm của nghiên cứu ban đầu hướng tới các tác vụ dịch thuật. Tiếp theo là sự ra đời của một số mô hình có ảnh hưởng, bao gồm:

- **06/2018**: [GPT](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf), mô hình Transformer được huấn luyện trước đầu tiên, được sử dụng để tinh chỉnh các tác vụ NLP khác nhau và thu được kết quả tốt nhất lúc bấy giờ.

- **10/2018**: [BERT](https://arxiv.org/abs/1810.04805), một mô hình lớn được huấn luyện trước khác, mô hình này được thiết kế để tạo ra các bản tóm tắt câu tốt hơn (sẽ đề cập thêm trong chương tiếp theo!).

- **02/2019**: [GPT-2](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf), một phiên bản GPT cải tiến (và lớn hơn) nhưng không được phát hành công khai ngay lập tức do lo ngại về vấn đề đạo đức.

- **10/2019**: [DistilBERT](https://arxiv.org/abs/1910.01108), phiên bản nhẹ của BERT với tốc độ nhanh hơn 60%, bộ nhớ nhẹ hơn 40%, và vẫn giữ được 97% hiệu suất của BERT.

- **10/2019**: [BART](https://arxiv.org/abs/1910.13461) and [T5](https://arxiv.org/abs/1910.10683), hai mô hình lớn được đào tạo trước sử dụng cùng một kiến trúc với mô hình Transformer gốc (mô hình đầu tiên làm như vậy).

- **05/2020**, [GPT-3](https://arxiv.org/abs/2005.14165), phiên bản thậm chí còn lớn hơn của GPT-2, có thể thực hiện tốt nhiều tác vụ khác nhau mà không cần tinh chỉnh (còn được gọi là _zero-shot learning_)

Danh sách này vẫn chưa đầy đủ và chỉ nhằm mục đích làm nổi bật một vài số mô hình Transformer khác nhau. Nhìn chung, chúng có thể được chia thành ba loại:

- Giống GPT (còn được gọi là _auto-regression_ Transformer)
- Giống BERT (còn được gọi là _auto-encoding_ Transformer)
- Giống BART/T5 (còn được gọi là _sequence-to-sequence_ Transformer)

Chúng ta sẽ đi sâu hơn vào các nhóm này ở các phần sau.

## Transformers là mô hình ngôn ngữ

Tất cả các mô hình Transformer được đề cập ở trên (GPT, BERT, BART, T5, v.v.) được huấn luyện thành các _mô hình ngôn ngữ_. Điều này có nghĩa là chúng đã được huấn luyện trên một lượng lớn văn bản thô theo phương pháp tự giám sát. Học tự giám sát là một loại hình huấn luyện trong đó mục tiêu được tính toán tự động từ các đầu vào của mô hình. Điều đó có nghĩa là con người không cần thiết phải gắn nhãn dữ liệu!

Loại mô hình này phát triển theo sự hiểu biết thống kê về ngôn ngữ mà nó đã được huấn luyện, nhưng nó không hữu ích lắm cho các tác vụ cụ thể trong thực tế. Do đó, mô hình được huấn luyện chung chung trước sau đó sẽ trải qua một quá trình được gọi là _học chuyển giao_. Trong quá trình này, mô hình được tinh chỉnh theo cách có giám sát - nghĩa là sử dụng các nhãn do con người gán - trên một tác vụ nhất định.

Ví dụ về một tác vụ có thể kể đến dự đoán từ tiếp theo trong một câu đã đọc cho trước _n_ từ trước đó. Đây được gọi là _mô hình ngôn ngữ nhân quả_ bởi vì đầu ra phụ thuộc vào các đầu vào trong quá khứ và hiện tại, nhưng không phụ thuộc vào các đầu vào trong tương lai.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/causal_modeling.svg" alt="Example of causal language modeling in which the next word from a sentence is predicted.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/causal_modeling-dark.svg" alt="Example of causal language modeling in which the next word from a sentence is predicted.">
</div>

Another example is _masked language modeling_, in which the model predicts a masked word in the sentence.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/masked_modeling.svg" alt="Example of masked language modeling in which a masked word from a sentence is predicted.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/masked_modeling-dark.svg" alt="Example of masked language modeling in which a masked word from a sentence is predicted.">
</div>

## Transformers là mô hình lớn

Ngoài một số ngoại lệ (như DistilBERT), chiến lược chung để đạt được hiệu suất tốt hơn là tăng kích thước của các mô hình cũng như lượng dữ liệu mà chúng được huấn luyện trước.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/model_parameters.png" alt="Number of parameters of recent Transformers models" width="90%">
</div>

Tiếc thay, việc huấn luyện một mô hình, đặc biệt là một mô hình lớn, đòi hỏi một lượng lớn dữ liệu. Điều này trở nên rất tốn kém về mặt thời gian và tài nguyên tính toán. Nó thậm chí còn chuyển thành tác động môi trường, như có thể thấy trong biểu đồ sau.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/carbon_footprint.svg" alt="The carbon footprint of a large language model.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/carbon_footprint-dark.svg" alt="The carbon footprint of a large language model.">
</div>

<Youtube id="ftWlj4FBHTg" />

Và điều này cho thấy một dự án cho một mô hình (rất lớn) được dẫn dắt bởi một nhóm có ý thức cố gắng giảm tác động môi trường của quá trình huấn luyện trước. Dấu chân của việc chạy nhiều thử nghiệm để có được siêu thông số tốt nhất sẽ còn cao hơn.

Hãy tưởng tượng nếu mỗi lần một nhóm nghiên cứu, một tổ chức sinh viên hoặc một công ty muốn huấn luyện một mô hình, họ sẽ thực hiện như vậy từ đầu. Điều này sẽ dẫn đến chi phí khổng lồ, không cần thiết!

Đây là lý do tại sao việc chia sẻ các mô hình ngôn ngữ là điều tối quan trọng: chia sẻ các trọng số đã được huấn luyện và xây dựng trên các trọng số đã được huấn luyện giúp giảm chi phí tính toán tổng thể và lượng khí thải carbon tới cộng đồng.

## Học chuyển giao

<Youtube id="BqqfQnyjmgg" />

_Huấn luyện trước_ là hành động huấn luyện một mô hình từ đầu: các trọng số được khởi tạo ngẫu nhiên và quá trình huấn luyện bắt đầu mà không cần biết trước bất kỳ điều gì.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/pretraining.svg" alt="The pretraining of a language model is costly in both time and money.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/pretraining-dark.svg" alt="The pretraining of a language model is costly in both time and money.">
</div>

Việc huấn luyện trước này thường được thực hiện trên một lượng dữ liệu rất lớn. Do đó, nó yêu cầu một kho dữ liệu rất lớn và quá trình huấn luyện có thể mất đến vài tuần.

Mặt khác, _tinh chỉnh_ là quá trình huấn luyện được thực hiện **sau khi** một mô hình đã được huấn luyện trước. Để thực hiện việc tinh chỉnh, trước tiên bạn cần có một mô hình ngôn ngữ đã huấn luyện trước, sau đó thực hiện huấn luyện bổ sung với một tập dữ liệu cụ thể cho tác vụ của bạn. Khoan - tại sao không đơn giản là huấn luyện trực tiếp cho tác vụ cuối cùng? Có một vài lý do như sau:

- Mô hình được huấn luyện trước đã được huấn luyện trên một bộ dữ liệu có một số điểm tương đồng với bộ dữ liệu tinh chỉnh. Do đó, quá trình tinh chỉnh có thể tận dụng kiến thức có được bởi mô hình ban đầu trong quá trình huấn luyện trước (ví dụ: với các vấn đề NLP, mô hình được huấn luyện trước sẽ có một số hiểu biết thống kê về ngôn ngữ bạn đang sử dụng cho tác vụ của mình).
- Vì mô hình được huấn luyện trước đã được đào tạo trên nhiều dữ liệu, nên việc tinh chỉnh yêu cầu ít dữ liệu hơn để có được kết quả tốt.
- Với lý do tương tự, lượng thời gian và nguồn lực cần thiết để đạt được kết quả tốt thấp hơn nhiều.

Ví dụ: người ta có thể tận dụng một mô hình huấn luyện trước được huấn luyện trên ngôn ngữ tiếng Anh và sau đó tinh chỉnh nó trên kho ngữ liệu arXiv, trả về một mô hình dựa trên khoa học/nghiên cứu. Việc tinh chỉnh sẽ chỉ yêu cầu một lượng dữ liệu hạn chế: kiến thức mà mô hình được huấn luyện trước được "chuyển giao", do đó ta có thuật ngữ _học chuyển giao_.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/finetuning.svg" alt="The fine-tuning of a language model is cheaper than pretraining in both time and money.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/finetuning-dark.svg" alt="The fine-tuning of a language model is cheaper than pretraining in both time and money.">
</div>

Do đó, việc tinh chỉnh một mô hình có chi phí thời gian, dữ liệu, tài chính và môi trường thấp hơn. Việc lặp lại các bước tinh chỉnh khác nhau cũng nhanh hơn và dễ dàng hơn, vì quá trình huấn luyện ít bị ràng buộc hơn so với huấn luyện trước từ đầu.

Quá trình này cũng sẽ đạt được kết quả tốt hơn so với huấn luyện từ đầu (trừ khi bạn có nhiều dữ liệu), đó là lý do tại sao bạn nên luôn cố gắng tận dụng một mô hình được huấn luyện trước - một mô hình càng gần với tác vụ bạn có trong tay càng tốt - và điều chỉnh nó.

## Kiến trúc tổng quan

Trong phần này, chúng ta sẽ xem xét kiến trúc chung của mô hình Transformer. Đừng lo lắng nếu bạn không hiểu một số khái niệm; có các phần sau bao gồm chi tiết từng thành phần.

<Youtube id="H39Z_720T5s" />

## Giới thiệu

Về cơ bản, mô hình bao gồm hai khối:

- **Bộ mã hóa (bên trái)**: Bộ mã hóa nhận đầu vào và xây dựng biểu diễn (các đặc trưng của nó). Điều này có nghĩa là mô hình được tối ưu hóa để có được sự hiểu biết từ đầu vào.
- **Bộ giải mã (bên phải)**: Bộ giải mã sử dụng biểu diễn (đặc trưng) của bộ mã hóa cùng với các đầu vào khác để tạo chuỗi đích. Điều này có nghĩa là mô hình được tối ưu hóa để tạo ra kết quả đầu ra.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers_blocks.svg" alt="Architecture of a Transformers models">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers_blocks-dark.svg" alt="Architecture of a Transformers models">
</div>

Mỗi phần có thể được sử dụng độc lập, tùy thuộc vào tác vụ:

- **Các mô hình chỉ dùng bộ mã hóa**: Phù hợp với các tác vụ yêu cầu hiểu rõ về đầu vào, chẳng hạn như phân loại câu và nhận dạng thực thể được đặt tên.
- **Các mô hình chỉ dùng bộ giải mã**: Tốt cho các tác vụ tổng hợp như tạo văn bản.
- **Các mô hình bộ mã hóa-giải mã** hoặc **mô hình chuỗi-sang-chuỗi**: Tốt cho các tác vụ tổng hợp yêu cầu đầu vào, chẳng hạn như dịch máy hoặc tóm tắt.

Chúng ta sẽ đi sâu vào các kiến trúc đó một cách độc lập trong các phần sau.

## Các lớp Attention

Một tính năng chính của các mô hình Transformer là chúng được xây dựng với các lớp đặc biệt được gọi là _Attention_. Trên thực tế, tiêu đề của bài báo giới thiệu kiến trúc Transformer là ["Attention is all you need"](https://arxiv.org/abs/1706.03762) hay "Sự chú ý là tất cả những gì bạn cần"! Chúng ta sẽ khám phá chi tiết về các lớp Attention ở phần sau của khóa học; hiện tại, tất cả những gì bạn cần biết là lớp này sẽ yêu cầu mô hình chú ý cụ thể đến các từ nhất định trong câu bạn đã chuyển nó (và ít nhiều bỏ qua những từ khác) khi xử lý biểu diễn của từng từ.

Để đặt điều này vào ngữ cảnh, cùng xem tác vụ dịch văn bản từ tiếng Anh sang tiếng Pháp. Với đầu vào là "You like this course" ("Bạn thích khóa học này"), một mô hình dịch cũng sẽ cần phải chú ý vào từ liền kề "You" để có được bản dịch thích hợp cho từ "like", bởi vì trong tiếng Pháp, động từ "like" được chia khác nhau tùy thuộc vào chủ ngữ. Tuy nhiên, phần còn lại của câu không hữu ích cho việc dịch từ đó. Tương tự như vậy, khi dịch "this", mô hình cũng sẽ cần chú ý đến từ "course", bởi vì "this" dịch khác nhau tùy thuộc vào việc danh từ liên quan là giống đực hay giống cái. Một lần nữa, các từ khác trong câu sẽ không thành vấn đề đối với bản dịch của "this". Với các câu phức tạp hơn (và các quy tắc ngữ pháp phức tạp hơn), mô hình sẽ cần đặc biệt chú ý đến các từ có thể xuất hiện ở xa hơn trong câu để dịch đúng từng từ.

Khái niệm tương tự cũng áp dụng cho bất kỳ tác vụ nào liên quan đến ngôn ngữ tự nhiên: một từ tự nó đã có nghĩa, nhưng nghĩa đó bị ảnh hưởng sâu sắc bởi ngữ cảnh, có thể là bất kỳ từ nào khác (hoặc các từ) trước hoặc sau từ được học.

Giờ bạn đã nắm được ý tưởng về tất cả các lớp Attention, chúng ta hãy xem xét kỹ hơn về kiến trúc Transformer.

## Kiến trúc gốc

Kiến trúc Transformer ban đầu được thiết kế phục vụ cho dịch máy. Trong quá trình huấn luyện, bộ mã hóa nhận đầu vào (câu) bằng một ngôn ngữ nhất định, trong khi bộ giải mã nhận các câu tương tự bằng ngôn ngữ đích mong muốn. Trong bộ mã hóa, các lớp Attention có thể sử dụng tất cả các từ trong một câu (vì, như chúng ta vừa thấy, bản dịch của một từ nhất định có thể phụ thuộc vào những gì đứng sau cũng như trước nó trong câu). Tuy nhiên, bộ giải mã hoạt động tuần tự và chỉ có thể chú ý đến các từ trong câu mà nó đã được dịch (vì vậy, chỉ những từ trước từ hiện đang được tạo). Ví dụ: khi chúng ta dự đoán ba từ đầu tiên của mục tiêu đã dịch, chúng ta đưa chúng cho bộ giải mã, sau đó sử dụng tất cả các đầu vào của bộ mã hóa để cố gắng dự đoán từ thứ tư.

Để tăng tốc độ mọi thứ trong quá trình huấn luyện (khi mô hình có quyền truy cập vào các câu đích), bộ giải mã được cung cấp toàn bộ nhãn, nhưng nó không được phép sử dụng các từ trong tương lai (nếu nó có quyền truy cập vào từ ở vị trí 2 khi cố gắng dự đoán từ ở vị trí 2, vấn đề sẽ không khó lắm!). Ví dụ: khi cố gắng dự đoán từ thứ tư, lớp Attention sẽ chỉ có quyền truy cập vào các từ ở vị trí 1 đến 3.

Kiến trúc Transformer gốc trông như sau, với bộ mã hóa ở bên trái và bộ giải mã ở bên phải:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers.svg" alt="Architecture of a Transformers models">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers-dark.svg" alt="Architecture of a Transformers models">
</div>

Lưu ý rằng lớp Attention đầu tiên trong bộ giải mã chú ý đến tất cả đầu vào (quá khứ) của bộ giải mã, nhưng lớp Attention thứ hai sử dụng đầu ra của bộ mã hóa. Do đó, nó có thể truy cập toàn bộ câu đầu vào để dự đoán tốt nhất từ hiện tại. Điều này rất hữu ích vì các ngôn ngữ khác nhau có thể có các quy tắc ngữ pháp đặt các từ theo thứ tự khác nhau hoặc một số ngữ cảnh được cung cấp sau trong câu có thể hữu ích để xác định bản dịch tốt nhất của một từ nhất định.

_Attention mask_ cũng có thể được sử dụng trong bộ mã hóa/ giải mã để ngăn mô hình chú ý đến một số từ đặc biệt - ví dụ: từ đệm đặc biệt được sử dụng để làm cho tất cả các đầu vào có cùng độ dài khi ghép các câu lại với nhau.

## Các kiến trúc và checkpoints

Khi chúng ta đi sâu và các mô hình Transformer trong hoá học này, bạn sẽ bắt gặp những cụm từ như _kiến trúc_, _checkpoint_ cũng như _mô hình_. Các thuật ngữ này mang ý nghĩa hơi khác nhau:

- **Kiến trúc**: Đây là khung của mô hình -- định nghĩa từng lớp và từng hoạt động xảy ra trong mô hình.
- **Checkpoints**: Đây là những trọng số sẽ được sử dụng trong một kiến trúc nhất định.
- **Mô hình**: Đây là một thuật ngữ bao trùm và không thể giải thích chính xác như "kiến trúc" hay "checkpoint": nó có thể mang cả hai nghĩa. Khoá học này sẽ chỉ ra khi nào là _kiến trúc_ và _checkpoint_ để giảm bớt sự mơ hồ khi cần thiết.

Ví dụ, BERT là 1 kiến trúc trong khi `bert-base-cased`,tập hợp các trọng số được huấn luyện bởi đội ngũ Google cho phiên bản đầu tiên của BERT, là 1 chekcpoint. Tuy nhiên, ta có thể nói "mô hình BERT" và "mô hình `bert-base-cased`".


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter1/4.mdx" />

### Các mô hình mã hoá-giải mã
https://huggingface.co/learn/course/vi/chapter1/7.md

# Các mô hình mã hoá-giải mã

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

<Youtube id="0_4KEb08xrE" />

Các mô hình mã hóa-giải mã (còn được gọi là _mô hình chuỗi-sang-chuỗi_) sử dụng cả hai phần của kiến trúc Transformer. Ở mỗi bước, các lớp attention của phần mã hóa có thể truy cập tất cả các từ trong câu ban đầu, trong khi các lớp attention của phần giải mã chỉ có thể truy cập các từ được đặt trước một từ nhất định trong đầu vào.

Việc huấn luyện trước các mô hình này có thể được thực hiện bằng cách sử dụng các hàm mục tiêu của mô hình mã hóa hoặc giải mã, nhưng thường liên quan đến một thứ phức tạp hơn một chút. Ví dụ: [T5](https://huggingface.co/t5-base) được huấn luyện trước bằng cách thay thế các khoảng văn bản ngẫu nhiên (có thể chứa một số từ) bằng cách che lại bằng một từ đặc biệt và mục tiêu sau đó là dự đoán phần bị che lại bởi một từ đặc biệt đó.

Mô hình chuỗi-sang-chuỗi phù hợp nhất cho các tác vụ xoay quanh việc tạo ra các câu mới tùy thuộc vào đầu vào nhất định, chẳng hạn như tóm tắt, dịch hoặc hỏi đáp chung.

Một số mô hình tiêu biểu của nhóm này bao gồm:

- [BART](https://huggingface.co/transformers/model_doc/bart.html)
- [mBART](https://huggingface.co/transformers/model_doc/mbart.html)
- [Marian](https://huggingface.co/transformers/model_doc/marian.html)
- [T5](https://huggingface.co/transformers/model_doc/t5.html)


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter1/7.mdx" />

### Xử lý Ngôn Ngữ Tự nhiên
https://huggingface.co/learn/course/vi/chapter1/2.md

# Xử lý Ngôn Ngữ Tự nhiên

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

Trước khi chuyển sang mô hình Transformer, chúng ta hãy cùng tìm hiểu nhanh tổng quan về Xử lý Ngôn ngữ Tự nhiên là gì và tại sao chúng ta quan tâm đến lĩnh vực này.

## Xử lý Ngôn ngữ Tự nhiên (NLP) là gì?

NLP là một lĩnh vực kết hợp giữa ngôn ngữ học và học máy, tập trung vào việc hiểu mọi thứ liên quan đến ngôn ngữ của con người. Mục đích của các tác vụ NLP không chỉ dừng ở hiểu từng từ đơn lẻ mà còn có thể hiểu ngữ cảnh của những từ đó.

Dưới đây là danh sách các tác vụ NLP phổ biến, với một số ví dụ về mỗi tác vụ:

- **Phân loại toàn bộ câu**: Nhận biết cảm xúc của bài đánh giá, phát hiện xem một bức thư điện tử có phải thư rác hay không, xác định xem một câu có đúng ngữ pháp hay không hoặc hai câu có liên quan về mặt logic hay không.
- **Phân loại từng từ trong câu**: Xác định các thành phần ngữ pháp của câu (danh từ, động từ, tính từ), hoặc các thực thể được đặt tên (người, vị trí, tổ chức).
- **Tạo nội dung văn bản**: Hoàn thành lời nhắc với văn bản được tạo tự động, điền vào chỗ trống trong văn bản có các từ bị che.
- **Trích xuất câu trả lời từ văn bản**: Cho một câu hỏi và ngữ cảnh, trích xuất câu trả lời cho câu hỏi dựa trên thông tin được cung cấp trong ngữ cảnh
- **Tạo câu mới từ văn bản đầu vào**: Dịch văn bản sang ngôn ngữ khác, tóm tắt văn bản.

NLP không giới hạn chỉ trong văn bản viết. Nó cũng giải quyết những thách thức phức tạp trong nhận dạng giọng nói và thị giác máy tính, chẳng hạn như tạo bản ghi chép từ âm thanh hoặc mô tả hình ảnh.

## Vì sao lĩnh vực này đầy thách thức?

Máy tính không xử lý thông tin theo cách giống như con người. Ví dụ, khi đọc câu “Tôi đói”, chúng ta có thể dễ dàng hiểu được ý nghĩa của nó. Tương tự, với hai câu như "Tôi đói" và "Tôi buồn", chúng ta có thể dễ dàng xác định xem chúng giống nhau như thế nào. Đối với mô hình học máy (ML), các tác vụ như vậy khó hơn nhiều. Văn bản cần được xử lý theo cách cho phép mô hình học hỏi từ nó. Và bởi vì ngôn ngữ phức tạp, chúng ta cần phải suy nghĩ cẩn thận về cách xử lý này cần thực hiện. Đã có rất nhiều nghiên cứu được thực hiện về cách biểu diễn văn bản, và chúng ta sẽ xem xét một số phương pháp trong chương tiếp theo.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/vi/chapter1/2.mdx" />
