# Định dạng Chat

Định dạng Chat (Chat templates) là yếu tố thiết yếu trong cấu trúc các tương tác giữa mô hình ngôn ngữ và người dùng. Chúng cung cấp một định dạng nhất quán cho các cuộc hội thoại, đảm bảo rằng các mô hình hiểu được ngữ cảnh và vai trò của mỗi tin nhắn trong khi duy trì các mẫu phản hồi phù hợp.

## Mô hình gốc (Base Models) và Mô hình chỉ thị (Instruct Models)

Mô hình gốc được huấn luyện trên dữ liệu văn bản thô để dự đoán *token* tiếp theo, trong khi Mô hình chỉ thị được tiếp tục tinh chỉnh đặc biệt để tuân theo chỉ thị và tham gia vào hội thoại. Ví dụ, `SmolLM2-135M` là một mô hình gốc, trong khi `SmolLM2-135M-Instruct` là phiên bản đã được điều chỉnh.

Để làm cho Mô hình gốc hoạt động như một Mô hình chỉ thị, chúng ta cần *định dạng prompt* của mình theo cách nhất quán mà mô hình có thể hiểu được. Đây là lúc *định dạng chat* phát huy tác dụng. **ChatML** là một định dạng template như vậy, với cấu trúc các cuộc hội thoại có chỉ định vai trò rõ ràng (system, user, assistant).

Điều quan trọng cần lưu ý là một Mô hình gốc có thể được huấn luyện với các *định dạng chat* khác nhau, vì vậy khi chúng ta sử dụng một Mô hình chỉ thị, chúng ta cần đảm bảo đang sử dụng đúng *định dạng chat*.

## Tìm hiểu về Định dạng Chat

Về cốt lõi, *định dạng chat* định nghĩa cách các cuộc hội thoại nên được định dạng khi giao tiếp với một mô hình ngôn ngữ. Chúng bao gồm các hướng dẫn hệ thống (system), tin nhắn người dùng (user) và phản hồi của trợ lý (assistant) trong một định dạng có cấu trúc mà mô hình có thể hiểu được. Cấu trúc này giúp duy trì tính nhất quán trong các tương tác và đảm bảo mô hình phản hồi phù hợp với các loại đầu vào khác nhau. Dưới đây là một ví dụ về chat template:

```sh
<|im_start|>user
Hi there!<|im_end|>
<|im_start|>assistant
Nice to meet you!<|im_end|>
<|im_start|>user
Can I ask a question?<|im_end|>
<|im_start|>assistant
```

Thư viện `transformers` sẽ xử lý *định dạng chat* cho bạn liên quan đến tokenizer của mô hình. Đọc thêm về cách transformers xây dựng *định dạng chat* [tại đây](https://huggingface.co/docs/transformers/en/chat_templating#how-do-i-use-chat-templates). Tất cả những gì chúng ta cần làm là cấu trúc tin nhắn của mình theo cách chính xác và *tokenizer() sẽ xử lý phần còn lại. Đây là một ví dụ cơ bản về một cuộc hội thoại:

```python
messages = [
    {"role": "system", "content": "You are a helpful assistant focused on technical topics."},
    {"role": "user", "content": "Can you explain what a chat template is?"},
    {"role": "assistant", "content": "A chat template structures conversations between users and AI models..."}
]
```

Hãy cùng nhau phân tích ví dụ trên để hiểu hơn về *định dạng chat*

## Mệnh lệnh hệ thống (System Prompt)

Mệnh lệnh hệ thống thiết lập nền tảng cho cách mô hình nên hoạt động. Chúng đóng vai trò như các hướng dẫn ảnh hưởng liên tục đến tất cả các tương tác tiếp theo. Ví dụ:

```python
system_message = {
    "role": "system",
    "content": "You are a professional customer service agent. Always be polite, clear, and helpful."
}
```

## Cuộc hội thoại

*Định dạng chat* duy trì ngữ cảnh thông qua lịch sử hội thoại, lưu trữ các trao đổi trước đó giữa người dùng và trợ lý. Điều này cho phép các cuộc hội thoại mạch lạc hơn:

```python
conversation = [
    {"role": "user", "content": "I need help with my order"},
    {"role": "assistant", "content": "I'd be happy to help. Could you provide your order number?"},
    {"role": "user", "content": "It's ORDER-123"},
]
```

## Triển khai với thư viện Transformers

Thư viện transformers cung cấp hỗ trợ tích hợp cho *định dạng chat*. Đây là cách sử dụng:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM2-135M-Instruct")

messages = [
    {"role": "system", "content": "You are a helpful coding assistant."},
    {"role": "user", "content": "Write a Python function to sort a list"},
]

# Apply the chat template
formatted_chat = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True
)
```

## Tuỳ chỉnh Định dạng Chat
Bạn có thể tùy chỉnh cách định dạng các loại tin nhắn khác nhau. Ví dụ, thêm *special token* hoặc định dạng cho các vai trò khác nhau:

```python
template = """
<|system|>{system_message}
<|user|>{user_message}
<|assistant|>{assistant_message}
""".lstrip()
```

## Hỗ trợ hội thoại đa lượt (multi-turn conversations)

Với *định dạng chat*, mô hình có thể xử lý các cuộc hội thoại phức tạp nhiều lượt trong khi vẫn duy trì ngữ cảnh:

```python
messages = [
    {"role": "system", "content": "You are a math tutor."},
    {"role": "user", "content": "What is calculus?"},
    {"role": "assistant", "content": "Calculus is a branch of mathematics..."},
    {"role": "user", "content": "Can you give me an example?"},
]
```

⏭ [Tiếp theo: Huấn luyện có giám sát](./supervised_fine_tuning.md)

## Tài liệu tham khảo

- [Hướng dẫn Định dạng Chat của Hugging Face](https://huggingface.co/docs/transformers/main/en/chat_templating)
- [Tài liệu về thư viện Transformers](https://huggingface.co/docs/transformers)
- [Ví dụ về Định dạng Chat](https://github.com/chujiezheng/chat_templates)

A course on aligning smol models.


# Tinh chỉnh có giám sát (Supervised Fine-Tuning)

Tinh chỉnh có giám sát (SFT) là một quá trình cốt lõi để điều chỉnh *pre-trained models* cho các tác vụ hoặc lĩnh vực cụ thể. Mặc dù các mô hình đã *pre-trained* có các khả năng tổng quát ấn tượng  ,chúng thường cần được tùy chỉnh để đạt hiệu suất cao trong các trường hợp sử dụng cụ thể. SFT thu hẹp khoảng cách này bằng cách huấn luyện thêm mô hình trên các tập dữ liệu được tuyển chọn kỹ lưỡng cùng với các ví dụ đã được con người đánh giá.

## Tìm hiểu thêm về huấn luyện có giám sát

Về cốt lõi, SFT là về việc dạy một mô hình đã *pre-trained* thực hiện các tác vụ cụ thể thông qua các mẫu của các *token* đã được gán nhãn. Quá trình này bao gồm việc cho mô hình học nhiều mẫu về các hành vi đầu vào-đầu ra mong muốn, cho phép nó học các mẫu cụ thể cho trường hợp sử dụng của bạn.

SFT hiệu quả vì nó sử dụng kiến thức nền tảng thu được trong quá trình *pre-training* trong khi điều chỉnh hành vi của mô hình để phù hợp với nhu cầu cụ thể của bạn.

## Khi nào nên sử dụng huấn luyện có giám sát

Quyết định sử dụng SFT thường phụ thuộc vào khoảng cách giữa khả năng hiện tại của mô hình và yêu cầu cụ thể của bạn. SFT trở nên đặc biệt có giá trị khi bạn cần kiểm soát chính xác đầu ra của mô hình hoặc khi làm việc trong các lĩnh vực chuyên biệt.

Ví dụ, nếu bạn đang phát triển một ứng dụng dịch vụ khách hàng, bạn có thể muốn mô hình của mình liên tục tuân theo hướng dẫn của công ty và xử lý các truy vấn kỹ thuật theo cách chuẩn hóa. Tương tự, trong các ứng dụng y tế hoặc pháp lý, độ chính xác và tuân thủ thuật ngữ chuyên ngành trở nên cực kỳ quan trọng. Trong những trường hợp này, SFT có thể giúp điều chỉnh phản hồi của mô hình phù hợp với tiêu chuẩn chuyên môn và chuyên môn trong lĩnh vực.

## Quy trình huấn luyện

Quy trình huấn luyện có giám sát bao gồm việc huấn luyện trọng số mô hình trên một tập dữ liệu theo tác vụ cụ thể.

Đầu tiên, bạn cần chuẩn bị hoặc lựa chọn một tập dữ liệu đại diện cho tác vụ mục tiêu của bạn. Tập dữ liệu này nên bao gồm các mẫu đa dạng bao quát phạm vi các tình huống mà mô hình của bạn sẽ gặp phải. Chất lượng của dữ liệu này rất quan trọng - mỗi mẫu nên thể hiện loại đầu ra mà bạn muốn mô hình của mình tạo ra. Tiếp theo là giai đoạn huấn luyện thực tế, nơi bạn sẽ sử dụng các framework có sẵn như `transformers` và `trl` của Hugging Face để huấn luyện mô hình trên tập dữ liệu của bạn.

Trong suốt quá trình này, việc đánh giá liên tục là thiết yếu. Bạn sẽ muốn theo dõi hiệu suất của mô hình trên một tập đánh giá (validation set) để đảm bảo nó đang học các hành vi mong muốn mà không mất đi khả năng tổng quát. Trong [bài 4](./4_evaluation), chúng ta sẽ tìm hiểu cách đánh giá mô hình đã được huấn luyện.

## Vai trò của huấn luyện có giám sát trong điều chỉnh theo sự uy tiên

SFT đóng vai trò nền tảng trong việc điều chỉnh các mô hình ngôn ngữ theo ưu tiên của con người. Các kỹ thuật như Học tăng cường từ phản hồi của con người (Reinforcement Learning from Human Feedback - RLHF) và Tối ưu hóa sở thích trực tiếp (Direct Preference Optimization - DPO) dựa vào SFT để xây dựng mức độ hiểu biết cơ bản về tác vụ trước khi tiếp tục điều chỉnh phản hồi của mô hình sao cho phù hợp với kết quả mong muốn. Các mô hình đã *pre-trained*, mặc dù có khả năng ngôn ngữ tổng quát, có thể không phải lúc nào cũng tạo ra đầu ra phù hợp ưu tiên của con người. SFT thu hẹp khoảng cách này bằng cách đưa vào dữ liệu và hướng dẫn theo lĩnh vực cụ thể, cải thiện khả năng của mô hình trong việc tạo ra phản hồi phù hợp hơn với kỳ vọng của con người.

## Huấn luyện có giám sát với Transformer Reinforcement Learning

Một thư viện quan trọng cho SFT đó là Transformer Reinforcement Learning (TRL). TRL là một bộ công cụ được sử dụng để huấn luyện các mô hình ngôn ngữ transformer bằng học tăng cường (RL).

Được xây dựng trên thư viện Transformers của Hugging Face, TRL cho phép người dùng trực tiếp tải các mô hình ngôn ngữ đã được *pre-trained* và hỗ trợ hầu hết các kiến trúc decoder và encoder-decoder. Thư viện này tạo điều kiện cho các quy trình chính của RL được sử dụng trong mô hình hóa ngôn ngữ, bao gồm Supervised Fine-Tuning (SFT), Reward Modeling (RM), Proximal Policy Optimization (PPO), và Direct Preference Optimization (DPO). Chúng ta sẽ sử dụng TRL trong nhiều bài học trong khoá học này.

# Các bước tiếp theo

Hãy thử các hướng dẫn sau để có tìm hiểu các ví dụ SFT thông qua TRL:

⏭ [Hướng dẫn Địng dạng Chat](./notebooks/chat_templates_example.ipynb)

⏭ [Hướng dẫn Huấn luyện có giám sát](./notebooks/supervised_fine_tuning_tutorial.ipynb)

A course on aligning smol models.


# Tinh Chỉnh Theo Sự Ưu Tiên (Preference Alignment)

Trong chương này, bạn sẽ học về các kỹ thuật tinh chỉnh mô hình ngôn ngữ theo sự ưu tiên của con người. Trong khi *học có giám sát* giúp mô hình học các tác vụ, *tinh chỉnh theo sự ưu tiên* khuyến khích đầu ra phù hợp với kỳ vọng và giá trị của con người.

## Tổng Quan

Các phương pháp *tinh chỉnh theo sự ưu tiên* thường bao gồm 2 giai đoạn:

1. Bắt đầu bằng quá trình *học có giám sát* (SFT) để thích ứng mô hình với các lĩnh vực cụ thể
2. Sau đó, tinh chỉnh mô hình theo sự ưu tiên (như RLHF hoặc DPO) để cải thiện chất lượng phản hồi

Các phương pháp thay thế như ORPO kết hợp cả *tinh chỉnh theo chỉ thị* và *tinh chỉnh theo sự ưu tiên* thành 1 giai đoạn tinh chỉnh duy nhất. Ở đây, chúng ta sẽ tập trung vào các thuật toán DPO và ORPO.

Nếu bạn muốn tìm hiểu thêm về các kỹ thuật tinh chỉnh khác, bạn có thể đọc thêm tại [Argilla Blog](https://argilla.io/blog/mantisnlp-rlhf-part-8).

### 1⃣ Tối Ưu Hóa Ưu Tiên Trực Tiếp (Direct Preference Optimization - DPO)

Phương pháp này đơn giản hóa quá trình *tinh chỉnh theo chỉ thị* bằng cách tối ưu hóa trực tiếp mô hình sử dụng dữ liệu ưu tiên (preference data). Phương pháp này loại bỏ nhu cầu về các *Mô hình thưởng phạt* (Reward model) riêng biệt và *Học tăng cường* phức tạp, giúp quá trình ổn định và hiệu quả hơn so với Học tăng cường từ phản hồi của con người (RLHF) truyền thống. Để biết thêm chi tiết, bạn có thể tham khảo tài liệu [*tối ưu hóa ưu tiên trực tiếp* (DPO)](./dpo.md).

### 2⃣ Tối Ưu Hóa Ưu Tiên Theo Tỷ Lệ Odds (Odds Ratio Preference Optimization - ORPO)

ORPO giới thiệu một phương pháp kết hợp cả 2 giai đoạn *tinh chỉnh theo chỉ thị* và *tinh chỉnh theo sự ưu tiên* vào trong 1 giai đoạn tinh chỉnh duy nhất. Phương pháp này điều chỉnh mục tiêu tiêu chuẩn của mô hình ngôn ngữ bằng cách kết hợp *negative log-likelihood loss* với một * tỷ lệ odds* ở cấp độ *token*. Vì vậy, ORPO tạo ra 1 quá trình tinh chỉnh thống nhất với kiến trúc không cần mô hình thưởng phạt và cải thiện đáng kể hiệu quả tính toán. ORPO đã cho thấy kết quả ấn tượng trên nhiều benchmark, thể hiện hiệu suất tốt hơn trên AlpacaEval so với các phương pháp truyền thống. Để biết thêm chi tiết, bạn có thể tham khảo tài liệu [tối ưu hóa ưu tiên theo tỷ lệ odds (ORPO)](./orpo.md).

## Bài Tập

| Tiêu đề | Mô tả | Bài tập | Đường dẫn | Colab |
|-------|-------------|----------|------|-------|
| Tinh chỉnh theo DPO | Học cách tinh chỉnh mô hình bằng phương pháp tối ưu hóa ưu tiên trực tiếp | 🐢 Tinh chỉnh mô hình sử dụng bộ dữ liệu HH-RLHF
🐕 Sử dụng tập dữ liệu của riêng bạn
🦁 Thử nghiệm với các tập dữ liệu và kích thước mô hình khác nhau | [Notebook](./notebooks/dpo_finetuning_example.ipynb) |  |
| Tinh chỉnh theo ORPO | Học cách tinh chỉnh mô hình bằng phương pháp tối ưu hóa ưu tiên theo tỷ lệ odds | 🐢 Huấn luyện mô hình sử dụng bộ dữ liệu chỉ thị (instruction) và dữ liệu ưu tiên (preference)
🐕 Thử nghiệm với các trọng số loss khác nhau
🦁 So sánh kết quả ORPO với DPO | [Notebook](./notebooks/orpo_finetuning_example.ipynb) |  |

## Resources

- [Tài liệu thư viện TRL](https://huggingface.co/docs/trl/index) - Tài liệu cho thư viện Transformers Reinforcement Learning (TRL), triển khai nhiều kỹ thuật căn chỉnh bao gồm DPO và ORPO.
- [Bài báo nghiên cứu DPO](https://arxiv.org/abs/2305.18290) - bài nghiên cứu gốc giới thiệu *tối ưu hóa ưu tiên trực tiếp* như một giải pháp thay thế đơn giản hơn cho RLHF.
- [Bài báo nghiên cứu ORPO](https://arxiv.org/abs/2403.07691) - Giới thiệu Odds Ratio Preference Optimization, một phương pháp mới kết hợp *tinh chỉnh theo chỉ thị* và *tinh chỉnh theo sự ưu tiên* thành 1
- [Bài hướng dẫn của Argilla](https://argilla.io/blog/mantisnlp-rlhf-part-8/) - Hướng dẫn giải thích các kỹ thuật căn chỉnh khác nhau bao gồm RLHF, DPO và cách triển khai thực tế.
- [Blog về DPO](https://huggingface.co/blog/dpo-trl) - Hướng dẫn thực hành về triển khai DPO sử dụng thư viện TRL với các ví dụ code và phương pháp tốt nhất.
- [Code mẫu cho DPO trong thư viên TRL](https://github.com/huggingface/trl/blob/main/examples/scripts/dpo.py) - Code mẫu về cách triển khai tinh chỉnh DPO sử dụng thư viện TRL.
- [Code mẫu cho ORPD trong thư viên TRL](https://github.com/huggingface/trl/blob/main/examples/scripts/orpo.py) - Code mẫu của tinh chỉnh ORPO sử dụng thư viện TRL với các tùy chọn cấu hình chi tiết.
- [Hugging Face Alignment Handbook](https://github.com/huggingface/alignment-handbook) - Hướng dẫn và codebase cho việc tinh chỉnh mô hình ngôn ngữ sử dụng các kỹ thuật khác nhau bao gồm SFT, DPO và RLHF.

A course on aligning smol models.


# Tối Ưu Hóa Ưu Tiên Trực Tiếp (Direct Preference Optimization - DPO)

DPO cung cấp một cách tiếp cận đơn giản để tinh chỉnh mô hình ngôn ngữ theo ý muốn của con người. Khác với phương pháp RLHF truyền thống yêu cầu các mô hình thưởng phạt riêng biệt và học tăng cường phức tạp, DPO tối ưu hóa trực tiếp mô hình bằng dữ liệu ưu tiên (preference dataset).

## Tìm Hiểu Về DPO

DPO chuyển đổi bài toán tinh chỉnh ưu tiên thành bài toán phân loại trên dữ liệu ưu tiên của con người. Phương pháp RLHF truyền thống yêu cầu huấn luyện một mô hình thưởng phạt riêng biệt và sử dụng các thuật toán học tăng cường phức tạp như PPO để căn chỉnh đầu ra của mô hình. DPO đơn giản hóa quy trình này bằng cách định nghĩa một hàm mất mát (loss) trực tiếp tối ưu hóa chiến lược học (policy) của mô hình dựa trên các đầu ra được ưu tiên và không được ưu tiên.

Phương pháp này đã chứng minh hiệu quả cao trong thực tế, được sử dụng để huấn luyện các mô hình như Llama. Bằng cách loại bỏ nhu cầu về mô hình thưởng phạt riêng biệt và giai đoạn học tăng cường, DPO giúp việc tinh chỉnh ưu tiên trở nên dễ tiếp cận và ổn định hơn.

## DPO Hoạt Động Như Thế Nào

Quy trình DPO yêu cầu quá trình học có giám sát (SFT) để thích ứng mô hình với lĩnh vực mục tiêu. Điều này tạo nền tảng cho việc học ưu tiên bằng cách huấn luyện trên các tập dữ liệu làm theo chỉ thị tiêu chuẩn. Mô hình học cách hoàn thành tác vụ cơ bản trong khi duy trì các khả năng tổng quát.

Tiếp theo là quá trình học ưu tiên, nơi mô hình được huấn luyện trên các cặp đầu ra - một được ưu tiên và một không được ưu tiên. Các cặp ưu tiên giúp mô hình hiểu phản hồi nào phù hợp hơn với giá trị và kỳ vọng của con người.

Đổi mới cốt lõi của DPO nằm ở cách tiếp cận tối ưu hóa trực tiếp. Thay vì huấn luyện một mô hình thưởng phạt riêng biệt, DPO sử dụng hàm mất mát `binary cross-entropy` để trực tiếp cập nhật trọng số mô hình dựa trên dữ liệu ưu tiên. Quy trình đơn giản này giúp việc huấn luyện ổn định và hiệu quả hơn trong khi đạt được kết quả tương đương hoặc tốt hơn so với RLHF truyền thống.

## Bộ Dữ Liệu DPO

Bộ dữ liệu cho DPO thường được tạo bằng cách gán nhãn các cặp phản hồi là được ưu tiên hoặc không được ưu tiên. Việc này có thể được thực hiện thủ công hoặc sử dụng các kỹ thuật lọc tự động. Dưới đây là cấu trúc mẫu của tập dữ liệu preference một lượt cho DPO:

```
| Prompt | Chosen | Rejected |
|--------|--------|----------|
| ...    | ...    | ...      |
| ...    | ...    | ...      |
| ...    | ...    | ...      |
```

Cột `Prompt` chứa chỉ thị dùng để tạo ra các phản hồi. Cột `Chosen` và `Rejected` chứa các phản hồi được ưu tiên và không được ưu tiên. Có nhiều biến thể của cấu trúc này, ví dụ, bao gồm cột `System Prompt` (chỉ thị hệ thống) hoặc cột `Input` chứa tài liệu tham khảo. Giá trị của `Chosen` và `Rejected` có thể được biểu diễn dưới dạng chuỗi cho hội thoại một lượt hoặc dưới dạng danh sách hội thoại.

Bạn có thể tìm thấy bộ sưu tập các tập dữ liệu DPO trên Hugging Face [tại đây](https://huggingface.co/collections/argilla/preference-datasets-for-dpo-656f0ce6a00ad2dc33069478).

## Triển Khai Với TRL

Thư viện Transformers Reinforcement Learning (TRL) giúp việc triển khai DPO trở nên đơn giản. Các lớp `DPOConfig` và `DPOTrainer` tuân theo API của thư viện `transformers`.

Đây là ví dụ cơ bản về cách thiết lập tinh chỉnh DPO:

```python
from trl import DPOConfig, DPOTrainer

# Định nghĩa các tham số
training_args = DPOConfig(
    ...
)

# Khởi tạo trainer
trainer = DPOTrainer(
    model,
    train_dataset=dataset,
    tokenizer=tokenizer,
    ...
)

# Huấn luyện mô hình
trainer.train()
```

Chúng ta sẽ tìm hiểu thêm chi tiết về cách sử dụng các lớp `DPOConfig` và `DPOTrainer` trong [Hướng dẫn DPO](./notebooks/dpo_finetuning_example.ipynb).

## Phương Pháp Tốt Nhất

**Chất lượng dữ liệu là yếu tố quan trọng** cho việc triển khai DPO thành công. Tập dữ liệu ưu tiên nên bao gồm các ví dụ đa dạng bao quát các khía cạnh khác nhau của hành vi mong muốn. Hướng dẫn gán nhãn rõ ràng đảm bảo việc gán nhãn nhất quán cho các phản hồi được ưu tiên và không được ưu tiên. Bạn có thể cải thiện hiệu suất mô hình bằng cách nâng cao chất lượng tập dữ liệu ưu tiên. Ví dụ, bằng cách lọc các tập dữ liệu lớn hơn để chỉ bao gồm các ví dụ chất lượng cao hoặc các ví dụ liên quan đến trường hợp sử dụng của bạn.

Trong quá trình huấn luyện, cần theo dõi kỹ sự hội tụ của hàm mất mát và đánh giá hiệu suất trên dữ liệu kiểm tra (held-out set). Tham số `beta` có thể cần điều chỉnh để cân bằng giữa việc học ưu tiên với việc duy trì các khả năng tổng quát của mô hình. Đánh giá thường xuyên trên các chỉ thị đa dạng giúp đảm bảo mô hình đang học các ưu tiên mong muốn mà không bị tình trạng quá khớp (overfitting).

So sánh đầu ra của mô hình với mô hình tham chiếu để xác minh sự cải thiện trong tinh chỉnh ưu tiên. Kiểm tra trên nhiều chỉ thị khác nhau, bao gồm cả các trường hợp ngoại lệ (edge cases), giúp đảm bảo việc học ưu tiên mạnh mẽ hơn trong các tình huống khác nhau.

## Các Bước Tiếp Theo

⏩ Để có thể thực hành thực tế với DPO, hãy thử [Hướng dẫn DPO](./notebooks/dpo_finetuning_example.ipynb). Hướng dẫn thực hành này sẽ chỉ dẫn bạn cách triển khai tinh chỉnh ưu tiên với mô hình của riêng bạn, từ chuẩn bị dữ liệu đến huấn luyện và đánh giá.

⏭ Sau khi hoàn thành hướng dẫn, bạn có thể khám phá về [ORPO](./orpo.md) để tìm hiểu về một kỹ thuật tinh chỉnh ưu tiên khác.

A course on aligning smol models.


# Tối Ưu Hóa Ưu Tiên Theo Tỷ Lệ Odds (Odds Ratio Preference Optimization - ORPO)

ORPO là một kỹ thuật tinh chỉnh mới kết hợp cả quá trình *tinh chỉnh theo chỉ thị* và *tinh chỉnh ưu tiên* thành một quy trình thống nhất. Cách tiếp cận kết hợp này mang lại những lợi thế về hiệu quả và hiệu suất so với các phương pháp truyền thống như RLHF hoặc DPO.

## Tìm Hiểu Về ORPO

Phương pháp tinh chỉnh như DPO thường liên quan đến hai bước riêng biệt:
1) học có giám sát để thích ứng mô hình với một lĩnh vực và định dạng
2) sau đó là tinh chỉnh ưu tiên để phù hợp với ý muốn của con người

Trong khi SFT hiệu quả trong việc thích ứng mô hình với các lĩnh vực mục tiêu, nó có thể vô tình làm tăng xác suất tạo ra cả phản hồi mong muốn và không mong muốn. ORPO giải quyết hạn chế này bằng cách tích hợp cả hai bước vào một quy trình duy nhất, như minh họa trong hình so sánh dưới đây:

![So sánh các kỹ thuật tinh chỉnh mô hình khác nhau](https://argilla.io/images/blog/mantisnlp-rlhf/part-8-alignments.png)
*So sánh các kỹ thuật tinh chỉnh mô hình khác nhau*

## ORPO Hoạt Động Như Thế Nào

Quy trình huấn luyện sử dụng một tập dữ liệu ưu tiên tương tự như chúng ta đã sử dụng cho DPO, trong đó mỗi mẫu huấn luyện chứa một chỉ thị đầu vào cùng với hai phản hồi: một được ưu tiên và một bị loại bỏ. Khác với các phương pháp tinh chỉnh khác yêu cầu các giai đoạn riêng biệt và mô hình tham chiếu, ORPO tích hợp trực tiếp tinh chỉnh ưu tiên vào quá trình học có giám sát. Cách tiếp cận thống nhất này không cần mô hình tham chiếu, hiệu quả hơn về mặt tính toán và bộ nhớ với ít FLOPs hơn.

ORPO tạo ra một mục tiêu mới bằng cách kết hợp hai thành phần chính:

1. **SFT Loss**: Hàm mất mát *negative log-likelihood* tiêu chuẩn được sử dụng trong việc mô hình hóa ngôn ngữ, tối đa hóa xác suất tạo ra các *token tham chiếu*. Điều này giúp duy trì khả năng ngôn ngữ tổng quát của mô hình.

2. **Odds Ratio Loss**:  Một hàm mất mát mới giúp phạt các phản hồi không mong muốn trong khi thưởng cho các phản hồi được ưu tiên. Hàm mất mát này sử dụng tỷ lệ odds để so sánh hiệu quả giữa các phản hồi được ưa thích và không ưa thích ở cấp độ *token*.

Cùng nhau, các thành phần này hướng dẫn mô hình thích ứng với các phản hồi mong muốn cho lĩnh vực cụ thể trong khi tích cực ngăn chặn các phản hồi từ tập các phản hồi bị từ chối. Cơ chế tỷ lệ odds cung cấp một cách tự nhiên để đo lường và tối ưu hóa ưu tiên của mô hình giữa các đầu ra đã chọn và bị từ chối. Nếu bạn muốn tìm hiểu sâu về phần toán học, bạn có thể đọc [bài báo ORPO](https://arxiv.org/abs/2403.07691). Nếu bạn muốn tìm hiểu về ORPO từ góc độ triển khai, bạn nên xem cách tính toán hàm mất mát cho ORPO trong [thư viện TRL](https://github.com/huggingface/trl/blob/b02189aaa538f3a95f6abb0ab46c0a971bfde57e/trl/trainer/orpo_trainer.py#L660).

## Hiệu Suất và Kết Quả

ORPO đã cho thấy các kết quả ấn tượng trên nhiều bài kiểm tra. Trên `MT-Bench`, phương pháp này giúp mô hình sau tinh chỉnh đạt điểm số cạnh tranh trên các danh mục khác nhau:

![Kết quả MT-Bench](https://argilla.io/images/blog/mantisnlp-rlhf/part-8-mtbench.png)
*Kết quả MT-Bench theo danh mục cho các mô hình Mistral-ORPO*

So với các phương pháp tinh chỉnh khác, ORPO thể hiện hiệu suất vượt trội trên AlpacaEval 2.0:

![Kết quả AlpacaEval](https://argilla.io/images/blog/mantisnlp-rlhf/part-8-winrate.png)
*Điểm số AlpacaEval 2.0 trên các phương pháp tinh chỉnh khác nhau*

So với việc kết hợp cả SFT và DPO, ORPO giảm yêu cầu tính toán bằng cách loại bỏ nhu cầu về mô hình tham chiếu và giảm một nửa số lần chuyển tiếp (forward pass) cho mỗi batch. Ngoài ra, quy trình huấn luyện ổn định hơn trên các kích thước mô hình và tập dữ liệu khác nhau, yêu cầu ít siêu tham số cần tinh chỉnh hơn. Về mặt hiệu suất, ORPO ngang bằng với các mô hình lớn hơn trong khi cho thấy sự tinh chỉnh tốt hơn với ý muốn của con người.

## Triển Khai

Triển khai thành công ORPO phụ thuộc nhiều vào **dữ liệu ưu tiên chất lượng cao**. Dữ liệu huấn luyện nên tuân theo các hướng dẫn gán nhãn rõ ràng và cung cấp sự đại diện cân bằng của các phản hồi được ưu tiên và bị từ chối trong các tình huống đa dạng.

### Triển Khai với TRL

ORPO có thể được triển khai sử dụng thư viện TRL. Đây là một ví dụ cơ bản:

```python
from trl import ORPOConfig, ORPOTrainer

# Cấu hình tinh chỉnh ORPO
orpo_config = ORPOConfig(
    learning_rate=1e-5,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    max_steps=1000,
    orpo_alpha=1.0,  # Kiểm soát độ mạnh của tối ưu hóa ưu tiên
    orpo_beta=0.1,   # Tham số cho sự ngẫu nhiên (temperature) trong tính tỷ lệ odds
)

# Khởi tạo trainer
trainer = ORPOTrainer(
    model=model,
    args=orpo_config,
    train_dataset=dataset,
    tokenizer=tokenizer,
)

# Huấn luyện mô hình
trainer.train()
```

Các tham số chính cần xem xét:

- `orpo_alpha`: Kiểm soát độ mạnh của thuật toán tối ưu hóa ưu tiên
- `orpo_beta`: Tham số cho sự ngẫu nhiên (temperature) trong phép tính tỷ lệ odds
- `learning_rate`: Nên tương đối nhỏ để tránh catastrophic forgetting
- `gradient_accumulation_steps`: Giúp ổn định quá trình huấn luyện

## Các Bước Tiếp Theo

⏩ Bạn có thể làm theo hướng dẫn trong [Hướng dẫn ORPO](./notebooks/orpo_tutorial.ipynb) để triển khai cách tinh chỉnh ưu tiên này.

## Resources
- [Bài báo nghiên cứu về ORPO](https://arxiv.org/abs/2403.07691)
- [Tài liệu về thư viện TRL](https://huggingface.co/docs/trl/index)
- [Hướng dẫn của Argilla](https://argilla.io/blog/mantisnlp-rlhf-part-8/)

A course on aligning smol models.


# Tinh chỉnh hiệu quả tham số (Parameter-Efficient Fine-Tuning - PEFT)

Khi các mô hình ngôn ngữ ngày càng lớn hơn, việc tinh chỉnh truyền thống trở nên ngày càng thách thức. Việc tinh chỉnh đầy đủ một mô hình với 1.7B tham số đòi hỏi bộ nhớ GPU lớn, việc lưu trữ các bản sao mô hình riêng biệt tốn kém, và có nguy cơ làm mất đi các khả năng ban đầu của mô hình. Các phương pháp tinh chỉnh hiệu quả tham số (PEFT) giải quyết những thách thức này bằng cách chỉ điều chỉnh một tập nhỏ các tham số mô hình trong khi giữ nguyên phần lớn mô hình.

Tinh chỉnh truyền thống cập nhật tất cả các tham số mô hình trong quá trình huấn luyện, điều này trở nên không khả thi với các mô hình lớn. Các phương pháp PEFT giới thiệu cách tiếp cận để điều chỉnh mô hình sử dụng ít tham số có thể huấn luyện hơn - thường ít hơn 1% kích thước mô hình gốc. Việc giảm đáng kể số lượng tham số có thể huấn luyện cho phép:

- Tinh chỉnh trên phần cứng tiêu dùng với bộ nhớ GPU hạn chế
- Lưu trữ nhiều phiên bản điều chỉnh (adapters) cho từng tác vụ một cách hiệu quả
- Khả năng tổng quát hóa tốt hơn trong các trường hợp dữ liệu ít
- Chu kỳ huấn luyện và thử nghiệm nhanh hơn

## Các phương pháp hiện có

Trong chương này, chúng ta sẽ tìm hiểu hai phương pháp PEFT phổ biến:

### 1⃣ Phương Pháp LoRA (Low-Rank Adaptation)

LoRA đã nổi lên như phương pháp PEFT được áp dụng rộng rãi nhất, cung cấp giải pháp hoàn hảo cho việc điều chỉnh mô hình hiệu quả mà không tốn nhiều tài nguyên tính toán. Thay vì sửa đổi toàn bộ mô hình, **LoRA đưa các ma trận có thể huấn luyện vào các lớp attention của mô hình.** Cách tiếp cận này thường giảm các tham số có thể huấn luyện khoảng 90% trong khi vẫn duy trì hiệu suất tương đương với tinh chỉnh đầy đủ. Chúng ta sẽ khám phá LoRA trong phần [LoRA (Low-Rank Adaptation)](./lora_adapters.md).

### 2⃣ Phương Pháp Điều Chỉnh Chỉ Thị (Prompt Tuning)

Prompt tuning cung cấp cách tiếp cận **thậm chí nhẹ hơn** bằng cách **thêm các token có thể huấn luyện vào đầu vào** thay vì sửa đổi trọng số mô hình. Prompt tuning ít phổ biến hơn LoRA, nhưng có thể là kỹ thuật hữu ích để nhanh chóng điều chỉnh mô hình cho các tác vụ hoặc lĩnh vực mới. Chúng ta sẽ khám phá prompt tuning trong phần [Prompt Tuning](./prompt_tuning.md).

## Notebooks bài tập

| Tiêu đề | Mô tả | Bài tập | Link | Colab |
|---------|--------|---------|------|-------|
| Tinh chỉnh LoRA | Học cách tinh chỉnh mô hình sử dụng LoRA adapters | 🐢 Huấn luyện mô hình sử dụng LoRA
🐕 Thử nghiệm với các giá trị rank khác nhau
🦁 So sánh hiệu suất với tinh chỉnh đầy đủ | [Notebook](./notebooks/finetune_sft_peft.ipynb) |  |
| Tải LoRA Adapters | Học cách tải và sử dụng LoRA adapters đã huấn luyện | 🐢 Tải adapters đã huấn luyện trước
🐕 Gộp adapters với mô hình cơ sở
🦁 Chuyển đổi giữa nhiều adapters | [Notebook](./notebooks/load_lora_adapter.ipynb) |  |

## Tài liệu tham khảo
- [Tài liệu PEFT](https://huggingface.co/docs/peft)
- [Bài báo nghiên cứu LoRA](https://arxiv.org/abs/2106.09685)
- [Bài báo nghiên cứu QLoRA](https://arxiv.org/abs/2305.14314)
- [Bài báo nghiên cứu Prompt Tuning](https://arxiv.org/abs/2104.08691)
- [Hướng dẫn sử dụng PEFT của Hugging Face](https://huggingface.co/blog/peft)
- [Cách Tinh chỉnh LLM với Hugging Face](https://www.philschmid.de/fine-tune-llms-in-2024-with-trl)
- [Thư viện TRL](https://huggingface.co/docs/trl/index)

A course on aligning smol models.


# Phương Pháp LoRA (Low-Rank Adaptation)

LoRA đã trở thành phương pháp PEFT được sử dụng rộng rãi nhất. Nó hoạt động bằng cách thêm các ma trận phân rã hạng thấp (small rank decomposition matrices) vào các trọng số attention, điều này dẫn đến việc giảm khoảng 90% số lượng tham số có thể huấn luyện.

## Tìm Hiểu Về LoRA

LoRA (Low-Rank Adaptation) là một kỹ thuật tinh chỉnh hiệu quả tham số, đóng băng các trọng số của mô hình đã huấn luyện trước và đưa thêm các ma trận phân rã hạng (rank decomposition matrices) có thể huấn luyện vào các lớp của mô hình. Thay vì huấn luyện tất cả các tham số mô hình trong quá trình tinh chỉnh, LoRA phân rã việc cập nhật trọng số thành các ma trận nhỏ hơn thông qua phân rã hạng thấp (low-rank decomposition), giảm đáng kể số lượng tham số có thể huấn luyện trong khi vẫn duy trì hiệu suất mô hình. Ví dụ, khi áp dụng cho GPT-3 175B, LoRA giảm số lượng tham số có thể huấn luyện xuống 10.000 lần và yêu cầu bộ nhớ GPU giảm 3 lần so với tinh chỉnh đầy đủ. Bạn có thể đọc thêm về LoRA trong [bài báo nghiên cứu LoRA](https://arxiv.org/pdf/2106.09685).

LoRA hoạt động bằng cách thêm các cặp ma trận phân rã hạng vào các lớp transformer, thường tập trung vào các trọng số attention. Trong quá trình suy luận, các trọng số adapter có thể được gộp với mô hình cơ sở, không gây thêm độ trễ. LoRA đặc biệt hữu ích cho việc điều chỉnh các mô hình ngôn ngữ lớn cho các tác vụ hoặc lĩnh vực cụ thể trong khi không yêu cầu nhiều tài nguyên tính toán.

## Lắp Các LoRA Adapters Vào Mô Hình Ngôn Ngữ

Adapters có thể được lắp vào một mô hình đã huấn luyện trước với hàm `load_adapter()`, điều này hữu ích khi thử nghiệm các adapters khác nhau mà trọng số không được gộp. Đặt trọng số adapter đang hoạt động bằng hàm `set_adapter()`. Để trở về mô hình cơ sở, bạn có thể sử dụng `unload()` để gỡ tất cả các module LoRA. Điều này giúp dễ dàng chuyển đổi giữa các trọng số cho từng tác vụ cụ thể.

```python
from transformers import AutoModelForCausalLM
from peft import PeftModel

base_model = AutoModelForCausalLM.from_pretrained("<base_model_name>")
peft_model_id = "<peft_adapter_id>"
model = PeftModel.from_pretrained(base_model, peft_model_id)
```

![lora_load_adapter](./images/lora_adapter.png)

## Gộp LoRA Adapters

Sau khi huấn luyện với LoRA, bạn có thể muốn gộp các trọng số adapter trở lại mô hình cơ sở để triển khai dễ dàng hơn. Điều này tạo ra một mô hình duy nhất với các trọng số đã kết hợp, loại bỏ nhu cầu tải adapters riêng biệt trong quá trình suy luận.

Quá trình gộp đòi hỏi chú ý đến quản lý bộ nhớ và độ chính xác. Vì bạn sẽ cần tải cả mô hình cơ sở và trọng số adapter cùng lúc, hãy đảm bảo có đủ bộ nhớ GPU/CPU. Sử dụng `device_map="auto"` trong `transformers` sẽ giúp tự động quản lý bộ nhớ. Duy trì độ chính xác nhất quán (ví dụ: float16) trong suốt quá trình, phù hợp với độ chính xác được sử dụng trong quá trình huấn luyện và lưu mô hình đã gộp trong cùng một định dạng để triển khai. Trước khi triển khai, luôn xác thực mô hình đã gộp bằng cách so sánh đầu ra và các chỉ số hiệu suất với phiên bản dựa trên adapter.

Adapters cũng thuận tiện cho việc chuyển đổi giữa các tác vụ hoặc lĩnh vực khác nhau. Bạn có thể tải mô hình cơ sở và trọng số adapter riêng biệt. Điều này cho phép chuyển đổi nhanh chóng giữa các trọng số cho từng tác vụ cụ thể.

## Hướng dẫn triển khai

Thư mục `notebooks/` chứa các hướng dẫn thực hành và bài tập để triển khai các phương pháp PEFT khác nhau. Bắt đầu với `load_lora_adapter_example.ipynb` để có giới thiệu cơ bản, sau đó khám phá `lora_finetuning.ipynb` để xem xét chi tiết hơn về cách tinh chỉnh một mô hình với LoRA và SFT.

Khi triển khai các phương pháp PEFT, hãy bắt đầu với các giá trị hạng nhỏ (4-8) cho LoRA và theo dõi mất mát trong quá trình huấn luyện. Sử dụng tập validation để tránh overfitting và so sánh kết quả với các baseline tinh chỉnh đầy đủ khi có thể. Hiệu quả của các phương pháp khác nhau có thể thay đổi theo tác vụ, vì vậy thử nghiệm là chìa khóa.

## OLoRA

[OLoRA](https://arxiv.org/abs/2406.01775) sử dụng phân rã QR để khởi tạo các adapter LoRA. OLoRA dịch chuyển các trọng số cơ sở của mô hình bằng một hệ số của phân rã QR của chúng, tức là nó thay đổi trọng số trước khi thực hiện bất kỳ huấn luyện nào trên chúng. Cách tiếp cận này cải thiện đáng kể tính ổn định, tăng tốc độ hội tụ và cuối cùng đạt được hiệu suất vượt trội.

## Sử dụng TRL với PEFT

Các phương pháp PEFT có thể được kết hợp với thư viện TRL để tinh chỉnh hiệu quả. Sự tích hợp này đặc biệt hữu ích cho RLHF (Reinforcement Learning from Human Feedback) vì nó giảm yêu cầu bộ nhớ.

```python
from peft import LoraConfig
from transformers import AutoModelForCausalLM

# Tải mô hình với cấu hình PEFT
lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

# Tải mô hình trên thiết bị cụ thể
model = AutoModelForCausalLM.from_pretrained(
    "your-model-name",
    load_in_8bit=True,  # Tùy chọn: sử dụng độ chính xác 8-bit
    device_map="auto",
    peft_config=lora_config
)
```

Ở trên, chúng ta đã sử dụng `device_map="auto"` để tự động gán mô hình cho thiết bị phù hợp. Bạn cũng có thể gán thủ công mô hình cho một thiết bị cụ thể bằng cách sử dụng `device_map={"": device_index}`. Bạn cũng có thể mở rộng việc huấn luyện trên nhiều GPU trong khi vẫn giữ việc sử dụng bộ nhớ hiệu quả.

## Triển khai gộp cơ bản

Sau khi huấn luyện một adapter LoRA, bạn có thể gộp trọng số adapter trở lại mô hình cơ sở. Đây là cách thực hiện:

```python
import torch
from transformers import AutoModelForCausalLM
from peft import PeftModel

# 1. Tải mô hình cơ sở
base_model = AutoModelForCausalLM.from_pretrained(
    "base_model_name",
    torch_dtype=torch.float16,
    device_map="auto"
)

# 2. Tải LoRA adapter
peft_model = PeftModel.from_pretrained(
    base_model,
    "path/to/adapter",
    torch_dtype=torch.float16
)

# 3. Gộp trọng số adapter với mô hình cơ sở
try:
    merged_model = peft_model.merge_and_unload()
except RuntimeError as e:
    print(f"Gộp thất bại: {e}")
    # Triển khai chiến lược dự phòng hoặc tối ưu hóa bộ nhớ

# 4. Lưu mô hình đã gộp
merged_model.save_pretrained("path/to/save/merged_model")
```

Nếu bạn gặp sự khác biệt về kích thước trong mô hình đã lưu, hãy đảm bảo bạn cũng lưu tokenizer:

```python
# Lưu cả mô hình và tokenizer
tokenizer = AutoTokenizer.from_pretrained("base_model_name")
merged_model.save_pretrained("path/to/save/merged_model")
tokenizer.save_pretrained("path/to/save/merged_model")
```

## Các bước tiếp theo

⏩ Chuyển sang [Hướng dẫn Prompt Tuning](prompt_tuning.md) để tìm hiểu cách tinh chỉnh một mô hình bằng prompt tuning.
⏩ Chuyển sang [Hướng dẫn lắp LoRA Adapters](./notebooks/load_lora_adapter.ipynb) để tìm hiểu cách lắp LoRA adapters.

# Tài liệu tham khảo

- [LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS](https://arxiv.org/pdf/2106.09685)
- [Tài liệu PEFT](https://huggingface.co/docs/peft)
- [Bài viết blog của Hugging Face về PEFT](https://huggingface.co/blog/peft)

A course on aligning smol models.


# Phương Pháp Điều Chỉnh Chỉ Thị (Prompt Tuning)

Prompt tuning là một phương pháp tiết kiệm tham số bằng cách điều chỉnh biểu diễn đầu vào thay vì trọng số mô hình. Không giống như tinh chỉnh truyền thống cập nhật tất cả các tham số mô hình, prompt tuning thêm và tối ưu hóa một tập nhỏ các token có thể huấn luyện trong khi giữ nguyên mô hình cơ sở.

## Tìm Hiểu Về Prompt Tuning

Prompt tuning là một phương pháp thay thế tiết kiệm tham số cho việc tinh chỉnh mô hình bằng cách thêm các vector liên tục có thể huấn luyện (soft prompts) vào trước văn bản đầu vào. Không giống như các chỉ thị rời rạc (discrete prompt), những soft prompt này được học thông qua truyền ngược (back-propagation) trong khi giữ nguyên mô hình ngôn ngữ. Phương pháp này được giới thiệu trong ["The Power of Scale for Parameter-Efficient Prompt Tuning"](https://arxiv.org/abs/2104.08691) (Lester et al., 2021), cho thấy prompt tuning trở nên cạnh tranh hơn với việc tinh chỉnh mô hình khi kích thước mô hình tăng lên. Trong bài báo, với khoảng 10 tỷ tham số, prompt tuning đạt hiệu suất tương đương với tinh chỉnh mô hình trong khi chỉ sửa đổi vài trăm tham số cho mỗi tác vụ.

Các soft prompt này là các vector liên tục trong không gian embedding của mô hình được tối ưu hóa trong quá trình huấn luyện. Không giống như các chỉ thị rời rạc truyền thống sử dụng các token ngôn ngữ tự nhiên, soft prompt là các chỉ thị không có ý nghĩa nhưng được học để tạo ra hành vi mong muốn từ mô hình đã đóng băng thông qua gradient descent. Kỹ thuật này đặc biệt hiệu quả cho các kịch bản đa tác vụ vì mỗi tác vụ chỉ cần lưu trữ một vector prompt nhỏ (thường là vài trăm tham số) thay vì một bản sao mô hình đầy đủ. Phương pháp này không chỉ duy trì bộ nhớ tối thiểu mà còn cho phép chuyển đổi tác vụ nhanh chóng bằng cách chỉ cần hoán đổi vector prompt mà không cần tải lại mô hình.

## Quá trình huấn luyện

Soft prompt thường có từ **8 đến 32 token** và có thể được **khởi tạo ngẫu nhiên hoặc từ văn bản hiện có**. Phương pháp khởi tạo đóng vai trò quan trọng trong quá trình huấn luyện, với việc khởi tạo dựa trên văn bản thường hoạt động tốt hơn so với khởi tạo ngẫu nhiên.

Trong quá trình huấn luyện, chỉ các tham số prompt được cập nhật trong khi mô hình cơ sở vẫn đóng băng. Cách tiếp cận tập trung này sử dụng các mục tiêu huấn luyện tiêu chuẩn nhưng đòi hỏi chú ý đến tốc độ học và hành vi gradient của các token prompt.

![prompt_tuning_explain](./images/prompt_tuning.png)

## Triển khai với PEFT

Thư viện PEFT giúp việc triển khai prompt tuning trở nên đơn giản. Đây là một ví dụ cơ bản:

```python
from peft import PromptTuningConfig, TaskType, get_peft_model
from transformers import AutoModelForCausalLM, AutoTokenizer

# Tải mô hình cơ sở
model = AutoModelForCausalLM.from_pretrained("your-base-model")
tokenizer = AutoTokenizer.from_pretrained("your-base-model")

# Điều chỉnh thông số prompt tuning
peft_config = PromptTuningConfig(
    task_type=TaskType.CAUSAL_LM,
    num_virtual_tokens=8,  # Số token có thể huấn luyện
    prompt_tuning_init="TEXT",  # Khởi tạo từ văn bản
    prompt_tuning_init_text="Phân loại xem văn bản này là tích cực hay tiêu cực:",
    tokenizer_name_or_path="your-base-model",
)

# Tạo mô hình có thể prompt-tune
model = get_peft_model(model, peft_config)
```

## So sánh với các phương pháp khác

Khi so sánh với các phương pháp PEFT khác, prompt tuning nổi bật về hiệu quả. Trong khi LoRA cung cấp số lượng tham số và bộ nhớ thấp nhưng yêu cầu tải adapter khi chuyển đổi tác vụ, prompt tuning đạt được mức sử dụng tài nguyên thậm chí thấp hơn và cho phép chuyển đổi tác vụ ngay lập tức. Ngược lại, tinh chỉnh đầy đủ đòi hỏi tài nguyên đáng kể và yêu cầu các bản sao mô hình riêng biệt cho các tác vụ khác nhau.

| Phương pháp | Tham số | Bộ nhớ | Chuyển đổi tác vụ |
|--------|------------|---------|----------------|
| Prompt Tuning | Rất thấp | Tối thiểu | Dễ dàng |
| LoRA | Thấp | Thấp | Yêu cầu tải |
| Tinh chỉnh đầy đủ | Cao | Cao | Cần bản sao mô hình mới |

Khi triển khai prompt tuning, hãy bắt đầu với một số lượng nhỏ token ảo (8-16) và chỉ tăng lên nếu độ phức tạp của tác vụ yêu cầu. Khởi tạo từ văn bản thường cho kết quả tốt hơn so với khởi tạo ngẫu nhiên, đặc biệt khi sử dụng văn bản liên quan đến tác vụ. Chiến lược khởi tạo nên phản ánh độ phức tạp của tác vụ mục tiêu của bạn.

Huấn luyện đòi hỏi những cân nhắc hơi khác so với tinh chỉnh đầy đủ. Tốc độ học (learning rates) cao hơn thường sẽ cho kết quả tốt hơn, nhưng cũng cần theo dõi cẩn thận *gradient* của token prompt. Kiểm tra thường xuyên trên các ví dụ đa dạng giúp đảm bảo hiệu suất mạnh mẽ trong các tình huống khác nhau.

## Ứng dụng

Prompt tuning xuất sắc trong một số kịch bản:

1. Triển khai đa tác vụ
2. Môi trường hạn chế tài nguyên
3. Thích ứng tác vụ nhanh chóng
4. Ứng dụng nhạy cảm về quyền riêng tư

Khi mô hình nhỏ hơn, prompt tuning trở nên kém cạnh tranh hơn so với tinh chỉnh đầy đủ. Ví dụ, trên các mô hình như SmolLM2, prompt tuning ít liên quan hơn so với tinh chỉnh đầy đủ.

## Các bước tiếp theo

⏭ Chuyển sang [Hướng dẫn LoRA Adapters](./notebooks/finetune_sft_peft.ipynb) để tìm hiểu cách tinh chỉnh một mô hình với LoRA adapters.

## Tài liệu tham khảo
- [Tài liệu về thư viện PEFT](https://huggingface.co/docs/peft)
- [Bài báo nghiên cứu về Prompt Tuning](https://arxiv.org/abs/2104.08691)
- [Hướng dẫn thực hành của Hugging Face](https://huggingface.co/learn/cookbook/prompt_tuning_peft)

A course on aligning smol models.


# Đánh giá mô hình

Đánh giá là bước quan trọng trong việc phát triển và triển khai các mô hình ngôn ngữ. Nó giúp chúng ta hiểu được mô hình hoạt động tốt như thế nào qua các khả năng khác nhau và xác định những lĩnh vực cần cải thiện. Chương này bao gồm các phương pháp đánh giá (benchmark) tiêu chuẩn và các phương pháp đánh giá theo lĩnh vực cụ thể để đánh giá toàn diện mô hình của bạn.

Chúng ta sẽ sử dụng [`lighteval`](https://github.com/huggingface/lighteval), một thư viện chuyên được sử dụng để đánh giá mô hình ngôn ngữ được phát triển bởi Hugging Face. Thư viện này được thiết kế tối giản giúp người sử dụng dễ dàng tiếp cận và được tích hợp để phù hợp với hệ sinh thái của Hugging Face. Để tìm hiểu sâu hơn về các khái niệm và thực hành về đánh giá mô hình ngôn ngữ, hãy tham khảo [hướng dẫn](https://github.com/huggingface/evaluation-guidebook) về đánh giá.

## Tổng quan chương học

Một quy trình đánh giá mô hình thường bao gồm nhiều khía cạnh về hiệu năng của mô hình. Chúng ta đánh giá các khả năng theo tác vụ cụ thể như *trả lời câu hỏi* và *tóm tắt* để hiểu mô hình xử lý các loại vấn đề khác nhau tốt đến đâu. Chúng ta đo lường chất lượng đầu ra thông qua các yếu tố như *tính mạch lạc* và *độ chính xác*. Đánh giá về *độ an toàn* giúp xác định các đầu ra của mô hình có chứa các hành vi khuyển khích thực hiện hành đông độc hại hoặc định kiến tiềm ẩn. Cuối cùng, kiểm tra *chuyên môn* về lĩnh vực xác minh kiến thức chuyên biệt của mô hình trong lĩnh vực mục tiêu của bạn.

## Nội dung

### 1⃣ [Đánh giá mô hình tự động](./automatic_benchmarks.md)

Học cách đánh giá mô hình của bạn bằng các phương pháp đánh giá và số liệu chuẩn hoá. Chúng ta sẽ khám phá các phương pháp đánh giá phổ biến như `MMLU` và `TruthfulQA`, hiểu các chỉ số đánh giá và cài đặt quan trọng, đồng thời đề cập đến các thực hành tốt nhất cho việc đánh giá có thể tái tạo lại.

### 2⃣ [Đánh giá theo yêu cầu đặc biệt](./custom_evaluation.md)
Khám phá cách tạo quy trình (pipeline) đánh giá phù hợp với trường hợp sử dụng cụ thể của bạn. Chúng ta sẽ hướng dẫn thiết kế các tác vụ đánh giá theo yêu cầu (custom), triển khai các số liệu chuyên biệt và xây dựng tập dữ liệu đánh giá phù hợp với yêu cầu của bạn.

### 3⃣ [Dự án đánh giá theo lĩnh vực cụ thể](./project/README.md)
Theo dõi một ví dụ hoàn chỉnh về việc xây dựng quy trình đánh giá cho lĩnh vực cụ thể. Bạn sẽ học cách tạo tập dữ liệu đánh giá, sử dụng `Argilla` để gán nhãn dữ liệu, tạo tập dữ liệu chuẩn hoá và đánh giá mô hình bằng `LightEval`.

### Các notebook bài tập

| Tiêu đề | Mô tả | Bài tập | Link | Colab |
|---------|-------|---------|------|-------|
| Đánh giá và phân tích mô hình LLM của bạn | Học cách sử dụng `LightEval` để đánh giá và so sánh các mô hình trên các lĩnh vực cụ thể | 🐢 Sử dụng các tác vụ trong **lĩnh vực y tế** để đánh giá mô hình
 🐕 Tạo đánh giá lĩnh vực mới với các tác vụ MMLU khác nhau
 🦁 Tạo tác vụ đánh giá cho lĩnh vực của bạn | [Notebook](./notebooks/lighteval_evaluate_and_analyse_your_LLM.ipynb) |  |

## Tài liệu tham khảo

- [Hướng dẫn đánh giá](https://github.com/huggingface/evaluation-guidebook) - Hướng dẫn toàn diện về đánh giá LLM
- [Tài liệu LightEval](https://github.com/huggingface/lighteval) - Tài liệu chính thức cho thư viện LightEval
- [Tài liệu Argilla](https://docs.argilla.io) - Tìm hiểu về nền tảng gán nhãn Argilla
- [Paper MMLU](https://arxiv.org/abs/2009.03300) - Paper mô tả benchmark MMLU
- [Tạo tác vụ theo yêu cầu](https://github.com/huggingface/lighteval/wiki/Adding-a-Custom-Task)
- [Tạo tiêu chuẩn đánh giá theo yêu cầu](https://github.com/huggingface/lighteval/wiki/Adding-a-New-Metric)
- [Sử dụng số liệu có sẵn](https://github.com/huggingface/lighteval/wiki/Metric-List)

A course on aligning smol models.


# Đánh giá tự động

Đánh giá tự động là các công cụ chuẩn hoá để đánh giá các mô hình ngôn ngữ qua các tác vụ và khả năng khác nhau. Mặc dù chúng cung cấp điểm khởi đầu hữu ích để hiểu hiệu năng của mô hình, điều quan trọng là phải nhận ra rằng chúng chỉ là một phần trong toàn bộ khả năng của mô hình.

## Hiểu về đánh giá tự động

Đánh giá tự động thường bao gồm các tập dữ liệu được tuyển chọn với các tác vụ và tiêu chí đánh giá được định nghĩa trước. Những đánh giá này nhằm đánh giá nhiều khía cạnh về khả năng của mô hình, từ hiểu ngôn ngữ cơ bản đến suy luận phức tạp. Lợi thế chính của việc sử dụng đánh giá tự động là tính chuẩn hoá - chúng cho phép so sánh nhất quán giữa các mô hình khác nhau và cung cấp kết quả có thể tái tạo lại.

Tuy nhiên, điều quan trọng là phải hiểu rằng điểm số trên các bài đánh giá không phải lúc nào cũng thể hiện chính xác hiệu quả của mô hình trong thực tế. Một mô hình xuất sắc trong các bài đánh giá học thuật (academic benchmark) vẫn có thể gặp khó khăn với các ứng dụng vào lĩnh vực cụ thể hoặc các trường hợp sử dụng thực tế.

## Các bài đánh giá và hạn chế của chúng

### Đánh giá kiến thức tổng quát

`MMLU` (Massive Multitask Language Understanding) là bài đánh giá dùng để kiểm tra kiến thức trên *57 môn học*, từ khoa học tự nhiên đến khoa học xã hội. Mặc dù toàn diện, nó có thể không phản ánh độ sâu của chuyên môn cần thiết cho các lĩnh vực cụ thể. `TruthfulQA` đánh giá xu hướng mô hình tái tạo các quan niệm sai lầm phổ biến, mặc dù nó không thể nắm bắt tất cả các hình thức thông tin sai lệch.

### Đánh giá khả năng suy luận
`BBH` (Big Bench Hard) và `GSM8K` tập trung vào các tác vụ suy luận phức tạp. `BBH` kiểm tra tư duy logic và lập kế hoạch, trong khi `GSM8K` nhắm vào giải quyết vấn đề toán học. Những bài đánh giá này giúp kiểm tra khả năng phân tích nhưng có thể không nắm bắt được khả năng suy luận tinh tế cần thiết trong các tình huống thực tế.

### Đánh giá khả năng ngôn ngữ
`HELM` cung cấp một đánh giá toàn diện, trong khi `WinoGrande` kiểm tra hiểu biết thông thường thông qua việc giải nghĩa các đại từ. Những bài đánh giá này cung cấp cái nhìn sâu sắc về khả năng xử lý ngôn ngữ nhưng có thể không hoàn toàn đại diện cho độ phức tạp của giao tiếp tự nhiên hoặc thuật ngữ theo lĩnh vực cụ thể.

## Các phương pháp đánh giá thay thế

Nhiều tổ chức đã phát triển các phương pháp đánh giá thay thế để khắc phục những hạn chế của bài đánh giá tiêu chuẩn:

### LLM trong vài trò người đánh giá
Sử dụng một mô hình ngôn ngữ để đánh giá đầu ra của mô hình khác ngày càng phổ biến. Phương pháp này có thể cung cấp phản hồi chi tiết hơn các bài đánh giá truyền thống, mặc dù nó đi kèm với những định kiến (bias) của mô hình đánh giá và hạn chế riêng.

### Môi trường đánh giá
Các nền tảng như Constitutional AI Arena của Anthropic cho phép các mô hình tương tác và đánh giá lẫn nhau trong môi trường được kiểm soát. Điều này có thể cho thấy những điểm mạnh và điểm yếu có thể không rõ ràng trong các benchmark truyền thống.

### Bộ đánh giá tuỳ chỉnh
Các tổ chức thường phát triển bộ benchmark nội bộ được điều chỉnh cho nhu cầu và trường hợp sử dụng cụ thể của họ. Những bộ này có thể bao gồm kiểm tra kiến thức theo lĩnh vực cụ thể hoặc các kịch bản đánh giá phản ánh điều kiện triển khai thực tế.

## Tạo chiến lược đánh giá riêng

Hãy nhớ rằng mặc dù LightEval giúp dễ dàng chạy các benchmark tiêu chuẩn, bạn cũng nên dành thời gian phát triển phương pháp đánh giá phù hợp với trường hợp sử dụng của mình.

Mặc dù benchmark tiêu chuẩn cung cấp một đường cơ sở hữu ích, chúng không nên là phương pháp đánh giá duy nhất của bạn. Dưới đây là cách phát triển một phương pháp toàn diện hơn:

1. Bắt đầu với các benchmark tiêu chuẩn liên quan để thiết lập đường cơ sở và cho phép so sánh với các mô hình khác.

2. Xác định các yêu cầu và thách thức cụ thể của trường hợp sử dụng của bạn. Mô hình của bạn sẽ thực hiện những tác vụ gì? Những loại lỗi nào sẽ gây rắc rối nhất?

3. Phát triển tập dữ liệu đánh giá tuỳ chỉnh phản ánh trường hợp sử dụng thực tế của bạn. Điều này có thể bao gồm:
   - Các câu hỏi thực tế từ người dùng trong lĩnh vực của bạn
   - Các trường hợp ngoại lệ phổ biến bạn đã gặp
   - Các ví dụ về tình huống đặc biệt khó khăn

4. Xem xét triển khai chiến lược đánh giá nhiều lớp:
   - Số liệu tự động để nhận phản hồi nhanh
   - Đánh giá bởi con người để hiểu sâu sắc hơn
   - Đánh giá của chuyên gia lĩnh vực cho các ứng dụng chuyên biệt
   - Kiểm tra A/B trong môi trường được kiểm soát

## Sử dụng LightEval để đánh giá

Các tác vụ LightEval được định nghĩa theo định dạng cụ thể:
```
{suite}|{task}|{num_few_shot}|{auto_reduce}
```

- **suite**: Bộ benchmark (ví dụ: 'mmlu', 'truthfulqa')
- **task**: Tác vụ cụ thể trong bộ (ví dụ: 'abstract_algebra')
- **num_few_shot**: Số lượng ví dụ để đưa vào prompt (0 cho zero-shot)
- **auto_reduce**: Có tự động giảm ví dụ few-shot nếu prompt quá dài hay không (0 hoặc 1)

Ví dụ: `"mmlu|abstract_algebra|0|0"` đánh giá tác vụ đại số trừu tượng của MMLU với suy luận zero-shot.

### Ví dụ về Pipeline đánh giá

Đây là một ví dụ hoàn chỉnh về việc thiết lập và chạy đánh giá trên các benchmark tự động liên quan đến một lĩnh vực cụ thể:

```python
from lighteval.tasks import Task, Pipeline
from transformers import AutoModelForCausalLM

# Định nghĩa các tác vụ để đánh giá
domain_tasks = [
    "mmlu|anatomy|0|0",
    "mmlu|high_school_biology|0|0",
    "mmlu|high_school_chemistry|0|0",
    "mmlu|professional_medicine|0|0"
]

# Cấu hình tham số pipeline
pipeline_params = {
    "max_samples": 40,  # Số lượng mẫu để đánh giá
    "batch_size": 1,    # Kích thước batch cho inference
    "num_workers": 4    # Số lượng worker process
}

# Tạo evaluation tracker
evaluation_tracker = EvaluationTracker(
    output_path="./results",
    save_generations=True
)

# Tải mô hình và tạo pipeline
model = AutoModelForCausalLM.from_pretrained("your-model-name")
pipeline = Pipeline(
    tasks=domain_tasks,
    pipeline_parameters=pipeline_params,
    evaluation_tracker=evaluation_tracker,
    model=model
)

# Chạy đánh giá
pipeline.evaluate()

# Lấy và hiển thị kết quả
results = pipeline.get_results()
pipeline.show_results()
```

Kết quả được hiển thị dưới dạng bảng thể hiện:
```
|                  Task                  |Version|Metric|Value |   |Stderr|
|----------------------------------------|------:|------|-----:|---|-----:|
|all                                     |       |acc   |0.3333|±  |0.1169|
|leaderboard:mmlu:_average:5             |       |acc   |0.3400|±  |0.1121|
|leaderboard:mmlu:anatomy:5              |      0|acc   |0.4500|±  |0.1141|
|leaderboard:mmlu:high_school_biology:5  |      0|acc   |0.1500|±  |0.0819|
```

Bạn cũng có thể xử lý kết quả trong pandas DataFrame và trực quan hoá hoặc biểu diễn chúng theo cách bạn muốn.

# Bước tiếp theo

⏩ Khám phá [Đánh giá theo lĩnh vực tuỳ chỉnh](./custom_evaluation.md) để học cách tạo pipeline đánh giá phù hợp với nhu cầu cụ thể của bạn

A course on aligning smol models.


# Đánh Giá Theo Lĩnh Vực Cụ Thể

Mặc dù các phương pháp đánh giá tiêu chuẩn (`benchmark`) cung cấp những thông tin chuyên sâu có giá trị, nhiều ứng dụng đòi hỏi các phương pháp đánh giá chuyên biệt phù hợp với các lĩnh vực hoặc trường hợp sử dụng cụ thể. Bài học này sẽ giúp bạn tạo các quy trình (`pipeline`) tùy chỉnh các phương pháp đánh giá để có cái nhìn chính xác hơn về hiệu suất của mô hình trong lĩnh vực cụ thể của bạn.

## Thiết kế chiến lược đánh giá của bạn

Một chiến lược đánh giá tùy chỉnh đầy đủ bắt đầu với các mục tiêu rõ ràng. Hãy xem xét những khả năng cụ thể mà mô hình của bạn cần thể hiện trong lĩnh vực của bạn. Điều này có thể bao gồm kiến thức kỹ thuật, các mẫu suy luận hoặc các định dạng đặc thù cho lĩnh vực đó. Ghi lại cẩn thận các yêu cầu này - chúng sẽ hướng dẫn cả việc thiết kế tác vụ (`task`) và lựa chọn các chỉ số (`metric`).

Việc đánh giá của bạn nên kiểm tra cả các trường hợp sử dụng tiêu chuẩn và các trường hợp biên (`edge case`). Ví dụ, trong lĩnh vực y tế, bạn có thể đánh giá cả các kịch bản chẩn đoán phổ biến và các tình trạng hiếm gặp. Trong các ứng dụng tài chính, bạn có thể kiểm tra cả các giao dịch thông thường và các trường hợp biên phức tạp liên quan đến nhiều loại tiền tệ hoặc các điều kiện đặc biệt.

## Triển khai với LightEval

`LightEval` cung cấp một khung (`framework`) linh hoạt để triển khai các đánh giá tùy chỉnh. Đây là cách để tạo một tác vụ (`task`) tùy chỉnh:

```python
from lighteval.tasks import Task, Doc
from lighteval.metrics import SampleLevelMetric, MetricCategory, MetricUseCase

class CustomEvalTask(Task):
    def __init__(self):
        super().__init__(
            name="custom_task",
            version="0.0.1",
            metrics=["accuracy", "f1"],  # Các chỉ số bạn chọn
            description="Mô tả tác vụ đánh giá tùy chỉnh của bạn"
        )

    def get_prompt(self, sample):
        # Định dạng đầu vào của bạn thành một lời nhắc (prompt)
        return f"Question: {sample['question']}\nAnswer:"

    def process_response(self, response, ref):
        # Xử lý đầu ra của mô hình và so sánh với tham chiếu
        return response.strip() == ref.strip()
```

## Các chỉ số (`metric`) tùy chỉnh

Các tác vụ theo lĩnh vực cụ thể thường yêu cầu các chỉ số chuyên biệt. `LightEval` cung cấp một khung linh hoạt để tạo các chỉ số tùy chỉnh nắm bắt các khía cạnh liên quan đến lĩnh vực của hiệu suất:

```python
from aenum import extend_enum
from lighteval.metrics import Metrics, SampleLevelMetric, SampleLevelMetricGrouping
import numpy as np

# Định nghĩa một hàm chỉ số mức mẫu (sample-level metric)
def custom_metric(predictions: list[str], formatted_doc: Doc, **kwargs) -> dict:
    """Chỉ số ví dụ trả về nhiều điểm số cho mỗi mẫu"""
    response = predictions[0]
    return {
        "accuracy": response == formatted_doc.choices[formatted_doc.gold_index],
        "length_match": len(response) == len(formatted_doc.reference)
    }

# Tạo một chỉ số trả về nhiều giá trị cho mỗi mẫu
custom_metric_group = SampleLevelMetricGrouping(
    metric_name=["accuracy", "length_match"],  # Tên của các chỉ số
    higher_is_better={  # Liệu giá trị cao hơn có tốt hơn cho mỗi chỉ số không
        "accuracy": True,
        "length_match": True
    },
    category=MetricCategory.CUSTOM,
    use_case=MetricUseCase.SCORING,
    sample_level_fn=custom_metric,
    corpus_level_fn={  # Cách tổng hợp từng chỉ số
        "accuracy": np.mean,
        "length_match": np.mean
    }
)

# Đăng ký chỉ số vào LightEval
extend_enum(Metrics, "custom_metric_name", custom_metric_group)
```

Đối với các trường hợp đơn giản hơn, nơi bạn chỉ cần một giá trị chỉ số cho mỗi mẫu:

```python
def simple_metric(predictions: list[str], formatted_doc: Doc, **kwargs) -> bool:
    """Chỉ số ví dụ trả về một điểm duy nhất cho mỗi mẫu"""
    response = predictions[0]
    return response == formatted_doc.choices[formatted_doc.gold_index]

simple_metric_obj = SampleLevelMetric(
    metric_name="simple_accuracy",
    higher_is_better=True,
    category=MetricCategory.CUSTOM,
    use_case=MetricUseCase.SCORING,
    sample_level_fn=simple_metric,
    corpus_level_fn=np.mean  # Cách tổng hợp trên các mẫu
)

extend_enum(Metrics, "simple_metric", simple_metric_obj)
```

Sau đó, bạn có thể sử dụng các chỉ số tùy chỉnh của mình trong các tác vụ đánh giá bằng cách tham chiếu chúng trong cấu hình tác vụ. Các chỉ số sẽ được tự động tính toán trên tất cả các mẫu và tổng hợp theo các hàm bạn đã chỉ định.

Đối với các chỉ số phức tạp hơn, hãy xem xét:
- Sử dụng siêu dữ liệu (`metadata`) trong các tài liệu được định dạng của bạn để đánh trọng số hoặc điều chỉnh điểm số
- Triển khai các hàm tổng hợp tùy chỉnh cho các thống kê cấp độ kho ngữ liệu (`corpus-level`)
- Thêm kiểm tra xác thực cho đầu vào chỉ số của bạn
- Ghi lại các trường hợp biên và hành vi mong đợi

Để có một ví dụ đầy đủ về các chỉ số tùy chỉnh trong thực tế, hãy xem [dự án đánh giá theo lĩnh vực](./project/README.md) của chúng tôi.

## Tạo tập dữ liệu

Đánh giá chất lượng cao đòi hỏi các tập dữ liệu được sắp xếp cẩn thận. Hãy xem xét các phương pháp sau để tạo tập dữ liệu:

1. Chú thích của chuyên gia: Làm việc với các chuyên gia trong lĩnh vực để tạo và xác thực các ví dụ đánh giá. Các công cụ như [Argilla](https://github.com/argilla-io/argilla) làm cho quá trình này hiệu quả hơn.

2. Dữ liệu thế giới thực: Thu thập và ẩn danh hóa dữ liệu sử dụng thực tế, đảm bảo nó đại diện cho các kịch bản triển khai thực tế.

3. Tạo tổng hợp: Sử dụng `LLM` để tạo các ví dụ ban đầu, sau đó để các chuyên gia xác thực và tinh chỉnh chúng.

## Các phương pháp tốt nhất

- Ghi lại phương pháp đánh giá của bạn một cách kỹ lưỡng, bao gồm mọi giả định hoặc hạn chế
- Bao gồm các trường hợp kiểm thử đa dạng bao gồm các khía cạnh khác nhau trong lĩnh vực của bạn
- Xem xét cả các chỉ số tự động và đánh giá thủ công khi thích hợp
- Kiểm soát phiên bản (`Version control`) các tập dữ liệu và mã đánh giá của bạn
- Thường xuyên cập nhật bộ đánh giá của bạn khi bạn phát hiện ra các trường hợp biên hoặc yêu cầu mới

## Tài liệu tham khảo

- [Hướng dẫn tác vụ tùy chỉnh LightEval](https://github.com/huggingface/lighteval/wiki/Adding-a-Custom-Task)
- [Chỉ số tùy chỉnh LightEval](https://github.com/huggingface/lighteval/wiki/Adding-a-New-Metric)
- [Tài liệu Argilla](https://docs.argilla.io) để chú thích tập dữ liệu
- [Hướng dẫn đánh giá](https://github.com/huggingface/evaluation-guidebook) cho các nguyên tắc đánh giá chung

# Các bước tiếp theo

⏩ Để có một ví dụ đầy đủ về việc triển khai các khái niệm này, hãy xem [dự án đánh giá theo lĩnh vực](./project/README.md) của chúng tôi.

A course on aligning smol models.


# Mô hình Ngôn ngữ Thị giác

## 1. Sử dụng Mô hình Ngôn ngữ Thị giác (Vision Language Models)

Mô hình Ngôn ngữ Thị giác (VLMs) xử lý đầu vào hình ảnh cùng với văn bản để thực hiện các tác vụ như chú thích của ảnh, trả lời câu hỏi bằng hình ảnh và suy luận đa phương thức (multimodal).

Một kiến trúc VLM điển hình bao gồm:
1. Bộ mã hóa hình ảnh (*image encoder*) để trích xuất các đặc trưng thị giác
2. Lớp chiếu (*projection layer*) để căn chỉnh các biểu diễn thị giác với văn bản
3. Mô hình ngôn ngữ để xử lý hoặc tạo văn bản. Điều này cho phép mô hình thiết lập các kết nối giữa các yếu tố về thị giác và các khái niệm trong ngôn ngữ.

Tùy thuộc vào từng trường hợp mà có thể sử dụng các VLMs được huấn luyện theo các tác vụ khác nhau. Các mô hình cơ sở (base models) xử lý các tác vụ thị giác-ngôn ngữ tổng quát, trong khi các biến thể tối ưu hóa cho trò chuyện (chat-optimized variants) hỗ trợ các tương tác hội thoại. Một số mô hình bao gồm các thành phần bổ sung để làm rõ dự đoán dựa trên các bằng chứng thị giác (*visual evidence*) hoặc chuyên về các tác vụ cụ thể như phát hiện đối tượng (*object detection*).

Để biết thêm chi tiết về kỹ thuật và cách sử dụng VLMs, hãy tham khảo trang [Sử dụng VLM](./vlm_usage.md).

## 2. Tinh chỉnh Mô hình Ngôn ngữ Thị giác (VLM)

Tinh chỉnh VLM là việc điều chỉnh một mô hình đã được huấn luyện trước (*pre-trained*) để thực hiện các tác vụ cụ thể hoặc để hoạt động hiệu quả trên một tập dữ liệu cụ thể. Quá trình này có thể tuân theo các phương pháp như tinh chỉnh có giám sát (*supervised fine-tuning*), tối ưu hóa tùy chọn (*preference optimization*) hoặc phương pháp kết hợp (*hybrid approach*) cả hai, như đã giới thiệu trong Chương 1 và Chương 2.

Mặc dù các công cụ và kỹ thuật cốt lõi vẫn tương tự như các công cụ và kỹ thuật được sử dụng cho các mô hình ngôn ngữ (LLMs), việc tinh chỉnh VLMs đòi hỏi phải tập trung nhiều hơn vào việc biểu diễn và chuẩn bị dữ liệu cho hình ảnh. Điều này đảm bảo mô hình tích hợp và xử lý hiệu quả cả dữ liệu thị giác và văn bản để đạt hiệu suất tối ưu. Vì mô hình demo, SmolVLM, lớn hơn đáng kể so với mô hình ngôn ngữ được sử dụng trong bài trước, điều cần thiết là phải khám phá các phương pháp tinh chỉnh hiệu quả. Các kỹ thuật như lượng tử hóa (*quantization*) và Tinh chỉnh hiệu quả tham số - PEFT (*Parameter-Efficient Fine-Tuning*) có thể giúp làm cho quá trình này dễ tiếp cận hơn và tiết kiệm chi phí hơn, cho phép nhiều người dùng thử nghiệm với mô hình hơn.

Để được hướng dẫn chi tiết về tinh chỉnh VLMs, hãy truy cập trang [Tinh chỉnh VLM](./vlm_finetuning.md).

## Bài tập thực hành

| Tiêu đề | Mô tả | Bài tập | Link | Colab |
|-------|-------------|----------|------|-------|
| Sử dụng VLM | Tìm hiểu cách tải và sử dụng VLM đã được huấn luyện trước cho các tác vụ khác nhau | 🐢 Xử lý một hình ảnh
🐕 Xử lý nhiều hình ảnh với xử lý hàng loạt
🦁 Xử lý toàn bộ video| [Notebook](./notebooks/vlm_usage_sample.ipynb) |  |
| Tinh chỉnh VLM | Tìm hiểu cách tinh chỉnh VLM đã được huấn luyện trước cho các tập dữ liệu theo từng nhiệm vụ | 🐢 Sử dụng tập dữ liệu cơ bản để tinh chỉnh
🐕 Thử tập dữ liệu mới
🦁 Thử nghiệm với các phương pháp tinh chỉnh thay thế | [Notebook](./notebooks/vlm_sft_sample.ipynb)|  |

## Tài liệu tham khảo

- [Hugging Face Learn: Tinh chỉnh có giám sát VLMs](https://huggingface.co/learn/cookbook/fine_tuning_vlm_trl)
- [Hugging Face Learn: Tinh chỉnh có giám sát SmolVLM](https://huggingface.co/learn/cookbook/fine_tuning_smol_vlm_sft_trl)
- [Hugging Face Learn: Tinh chỉnh tối ưu hóa tùy chọn SmolVLM](https://huggingface.co/learn/cookbook/fine_tuning_vlm_dpo_smolvlm_instruct)
- [Hugging Face Blog: Tối ưu hóa tùy chọn cho VLMs](https://huggingface.co/blog/dpo_vlm)
- [Hugging Face Blog: Mô hình Ngôn ngữ Thị giác](https://huggingface.co/blog/vlms)
- [Hugging Face Blog: SmolVLM](https://huggingface.co/blog/smolvlm)
- [Hugging Face Model: SmolVLM-Instruct](https://huggingface.co/HuggingFaceTB/SmolVLM-Instruct)
- [CLIP: Learning Transferable Visual Models from Natural Language Supervision](https://arxiv.org/abs/2103.00020)
- [Align Before Fuse: Vision and Language Representation Learning with Momentum Distillation](https://arxiv.org/abs/2107.07651)

A course on aligning smol models.


# Tinh chỉnh Mô hình Ngôn ngữ Thị giác (VLM)

## Tinh chỉnh Hiệu quả

### Lượng tử hóa (Quantization)

Lượng tử hóa làm giảm độ chính xác của trọng số mô hình (model weights) và hàm kích hoạt (activations), giúp giảm đáng kể mức sử dụng bộ nhớ và tăng tốc độ tính toán. Ví dụ: chuyển từ `float32` sang `bfloat16` giúp giảm một nửa yêu cầu bộ nhớ cho mỗi tham số trong khi vẫn duy trì hiệu suất. Để nén mạnh hơn, có thể sử dụng lượng tử hóa `8 bit` và `4 bit`, giúp giảm mức sử dụng bộ nhớ hơn nữa, mặc dù phải đánh đổi bằng việc giảm độ chính xác. Những kỹ thuật này có thể được áp dụng cho cả trọng số mô hình và trình tối ưu hóa (optimizer), cho phép huấn luyện hiệu quả trên phần cứng có tài nguyên hạn chế.

### PEFT & LoRA

Như đã giới thiệu trong Bài 3, LoRA (Low-Rank Adaptation) tập trung vào việc học các ma trận phân rã hạng thấp (rank-decomposition matrices) nhỏ gọn trong khi vẫn giữ nguyên trọng số của mô hình gốc. Điều này làm giảm đáng kể số lượng tham số có thể huấn luyện, giảm đáng kể yêu cầu tài nguyên. LoRA, khi được tích hợp với PEFT (Parameter-Efficient Fine-Tuning), cho phép tinh chỉnh các mô hình lớn bằng cách chỉ điều chỉnh một tập hợp con nhỏ các tham số có thể huấn luyện. Phương pháp này đặc biệt hiệu quả cho các thích ứng theo nhiệm vụ cụ thể, giảm hàng tỷ tham số có thể huấn luyện xuống chỉ còn hàng triệu trong khi vẫn duy trì hiệu suất.

### Tối ưu hóa Kích thước Batch (Batch Size)

Để tối ưu hóa kích thước batch cho quá trình tinh chỉnh, hãy bắt đầu với một giá trị lớn và giảm nếu xảy ra lỗi out-of-memory (OOM). Bù lại bằng cách tăng `gradient_accumulation_steps`, duy trì hiệu quả tổng kích thước batch trên nhiều lần cập nhật. Ngoài ra, hãy bật `gradient_checkpointing` để giảm mức sử dụng bộ nhớ bằng cách tính toán lại các trạng thái trung gian trong quá trình lan truyền ngược (backward pass), đánh đổi thời gian tính toán để giảm yêu cầu bộ nhớ kích hoạt. Những chiến lược này tối đa hóa việc sử dụng phần cứng và giúp khắc phục các hạn chế về bộ nhớ.

```python
from transformers import TrainingArguments

training_args = TrainingArguments(
    output_dir="./fine_tuned_model",  # Thư mục cho các checkpoint của mô hình
    per_device_train_batch_size=4,   # Kích thước batch trên mỗi thiết bị (GPU/TPU)
    num_train_epochs=3,              # Tổng số epoch huấn luyện
    learning_rate=5e-5,              # Tốc độ học
    save_steps=1000,                 # Lưu checkpoint sau mỗi 1000 bước
    bf16=True,                       # Sử dụng mixed precision để huấn luyện
    gradient_checkpointing=True,     # Bật để giảm mức sử dụng bộ nhớ kích hoạt
    gradient_accumulation_steps=16,  # Tích lũy gradient qua 16 bước
    logging_steps=50                 # Ghi nhật ký các số liệu sau mỗi 50 bước
)
```

## **Tinh chỉnh có Giám sát (Supervised Fine-Tuning - SFT)**

Tinh chỉnh có Giám sát (SFT) điều chỉnh Mô hình Ngôn ngữ Thị giác (VLM) đã được huấn luyện trước cho các nhiệm vụ cụ thể bằng cách tận dụng các tập dữ liệu được gán nhãn có chứa các đầu vào được ghép nối, chẳng hạn như hình ảnh và văn bản tương ứng. Phương pháp này nâng cao khả năng của mô hình để thực hiện các chức năng cụ thể theo miền (domain-specific) hoặc theo nhiệm vụ (task-specific), chẳng hạn như trả lời câu hỏi bằng hình ảnh, chú thích hình ảnh hoặc diễn giải biểu đồ.

### **Tổng quan**

SFT là cần thiết khi bạn cần một VLM chuyên về một lĩnh vực cụ thể hoặc giải quyết các vấn đề cụ thể mà khả năng chung của mô hình cơ sở có thể không đáp ứng được. Ví dụ: nếu mô hình gặp khó khăn với các đặc điểm hình ảnh độc đáo hoặc thuật ngữ chuyên ngành, SFT cho phép mô hình tập trung vào các lĩnh vực này bằng cách học từ dữ liệu được gán nhãn.

Mặc dù SFT rất hiệu quả, nó có những hạn chế đáng chú ý:

- **Phụ thuộc vào dữ liệu**: Cần có các tập dữ liệu được gán nhãn chất lượng cao phù hợp với nhiệm vụ.
- **Tài nguyên tính toán**: Tinh chỉnh các VLM lớn đòi hỏi nhiều tài nguyên.
- **Nguy cơ quá khớp (Overfitting)**: Các mô hình có thể mất khả năng khái quát hóa nếu được tinh chỉnh quá hẹp.

Tuy vậy, SFT vẫn là một kỹ thuật mạnh mẽ để nâng cao hiệu suất của mô hình trong các bối cảnh cụ thể.

### **Cách sử dụng**

1. **Chuẩn bị dữ liệu**: Bắt đầu với tập dữ liệu được gán nhãn ghép nối hình ảnh với văn bản, chẳng hạn như câu hỏi và câu trả lời. Ví dụ: trong các tác vụ như phân tích biểu đồ, tập dữ liệu `HuggingFaceM4/ChartQA` bao gồm hình ảnh biểu đồ, truy vấn và câu trả lời ngắn gọn.

2. **Thiết lập mô hình**: Tải VLM đã được huấn luyện trước phù hợp với nhiệm vụ, chẳng hạn như `HuggingFaceTB/SmolVLM-Instruct`, và một bộ xử lý (processor) để chuẩn bị đầu vào văn bản và hình ảnh. Điều chỉnh cấu hình của mô hình để phù hợp với phần cứng của bạn.

3. **Quá trình tinh chỉnh**:
   - **Định dạng dữ liệu**: Cấu trúc tập dữ liệu thành định dạng giống như chatbot, ghép nối các câu lệnh hệ thống (system messages), các truy vấn của người dùng và các câu trả lời tương ứng.
   - **Cấu hình huấn luyện**: Sử dụng các công cụ như `TrainingArguments` của Hugging Face hoặc `SFTConfig` của TRL để thiết lập các tham số huấn luyện. Chúng bao gồm kích thước batch, tốc độ học và các bước tích lũy gradient để tối ưu hóa việc sử dụng tài nguyên.
   - **Kỹ thuật tối ưu hóa**: Sử dụng **gradient checkpointing** để tiết kiệm bộ nhớ trong quá trình huấn luyện. Sử dụng mô hình đã lượng tử hóa để giảm yêu cầu bộ nhớ và tăng tốc độ tính toán.
   - Sử dụng `SFTTrainer` từ thư viện TRL, để hợp lý hóa quá trình huấn luyện.

## Tối ưu hóa theo Sở thích (Preference Optimization)

Tối ưu hóa theo Sở thích, đặc biệt là Tối ưu hóa Sở thích Trực tiếp (Direct Preference Optimization - DPO), huấn luyện Mô hình Ngôn ngữ Thị giác (VLM) để phù hợp với sở thích của con người. Thay vì tuân theo các hướng dẫn được xác định trước một cách nghiêm ngặt, mô hình học cách ưu tiên các đầu ra mà con người chủ quan thích hơn. Phương pháp này đặc biệt hữu ích cho các tác vụ liên quan đến phán đoán sáng tạo, lý luận sắc thái hoặc các câu trả lời có thể chấp nhận được khác nhau.

### **Tổng quan**

Tối ưu hóa theo Sở thích giải quyết các tình huống trong đó sở thích chủ quan của con người là trung tâm của sự thành công của nhiệm vụ. Bằng cách tinh chỉnh trên các tập dữ liệu mã hóa sở thích của con người, DPO nâng cao khả năng của mô hình trong việc tạo ra các phản hồi phù hợp với ngữ cảnh và phong cách với mong đợi của người dùng. Phương pháp này đặc biệt hiệu quả cho các tác vụ như viết sáng tạo, tương tác với khách hàng hoặc các tình huống có nhiều lựa chọn.

Mặc dù có những lợi ích, Tối ưu hóa theo Sở thích có những thách thức:

- **Chất lượng dữ liệu**: Cần có các tập dữ liệu được chú thích theo sở thích chất lượng cao, thường làm cho việc thu thập dữ liệu trở thành một nút thắt cổ chai.
- **Độ phức tạp**: Việc huấn luyện có thể liên quan đến các quy trình phức tạp như lấy mẫu theo cặp các sở thích và cân bằng tài nguyên tính toán.

Các tập dữ liệu sở thích phải nắm bắt được các sở thích rõ ràng giữa các đầu ra ứng viên. Ví dụ: một tập dữ liệu có thể ghép nối một câu hỏi với hai câu trả lời—một câu trả lời được ưu tiên và câu trả lời kia ít được chấp nhận hơn. Mô hình học cách dự đoán câu trả lời được ưu tiên, ngay cả khi nó không hoàn toàn chính xác, miễn là nó phù hợp hơn với đánh giá của con người.

### **Cách sử dụng**

1. **Chuẩn bị tập dữ liệu**
   Một tập dữ liệu được gán nhãn sở thích là rất quan trọng để huấn luyện. Mỗi ví dụ thường bao gồm một lời nhắc (prompt) (ví dụ: một hình ảnh và câu hỏi) và hai câu trả lời ứng viên: một câu được chọn (ưa thích) và một câu bị từ chối. Ví dụ:

   - **Câu hỏi**: Có bao nhiêu gia đình?
     - **Bị từ chối**: Hình ảnh không cung cấp bất kỳ thông tin nào về các gia đình.
     - **Được chọn**: Hình ảnh cho thấy một bảng gồm 18.000 gia đình.

   Tập dữ liệu dạy cho mô hình ưu tiên các câu trả lời phù hợp hơn, ngay cả khi chúng không hoàn hảo.

2. **Thiết lập mô hình**
   Tải VLM đã được huấn luyện trước và tích hợp nó với thư viện TRL của Hugging Face, hỗ trợ DPO và bộ xử lý để chuẩn bị đầu vào văn bản và hình ảnh. Định cấu hình mô hình để học có giám sát và phù hợp với phần cứng của bạn.

3. **Quy trình huấn luyện**
   Việc huấn luyện bao gồm việc định cấu hình các tham số cụ thể cho DPO. Dưới đây là bản tóm tắt về quy trình:

   - **Định dạng tập dữ liệu**: Cấu trúc từng mẫu với lời nhắc, hình ảnh và câu trả lời ứng viên.
   - **Hàm mất mát (Loss Function)**: Sử dụng hàm mất mát dựa trên sở thích để tối ưu hóa mô hình để chọn đầu ra được ưu tiên.
   - **Huấn luyện hiệu quả**: Kết hợp các kỹ thuật như lượng tử hóa, tích lũy gradient và bộ điều hợp LoRA (LoRA adapters) để tối ưu hóa bộ nhớ và tính toán.

## Tài liệu tham khảo

- [Hugging Face Learn: Tinh chỉnh có giám sát VLMs](https://huggingface.co/learn/cookbook/fine_tuning_vlm_trl)
- [Hugging Face Learn: Tinh chỉnh có giám sát SmolVLM](https://huggingface.co/learn/cookbook/fine_tuning_smol_vlm_sft_trl)
- [Hugging Face Learn: Tinh chỉnh tối ưu hóa tùy chọn SmolVLM](https://huggingface.co/learn/cookbook/fine_tuning_vlm_dpo_smolvlm_instruct)
- [Hugging Face Blog: Tối ưu hóa tùy chọn cho VLMs](https://huggingface.co/blog/dpo_vlm)

## Các bước tiếp theo

⏩ Thử [vlm_finetune_sample.ipynb](./notebooks/vlm_finetune_sample.ipynb) để triển khai phương pháp thống nhất này để căn chỉnh tùy chọn.

A course on aligning smol models.


# Mô hình Ngôn ngữ Thị giác (Visual Language Models)

Mô hình Ngôn ngữ Thị giác (VLMs) thu hẹp khoảng cách giữa hình ảnh và văn bản, cho phép thực hiện các tác vụ nâng cao như tạo chú thích cho ảnh, trả lời câu hỏi dựa trên hình ảnh hoặc hiểu mối quan hệ giữa dữ liệu văn bản và hình ảnh. Kiến trúc của chúng được thiết kế để xử lý liền mạch cả hai phương thức.

### Kiến trúc

VLMs kết hợp các thành phần xử lý hình ảnh với các mô hình sinh văn bản để đạt được sự hiểu biết thống nhất. Các yếu tố chính trong kiến trúc của chúng là:

![Kiến trúc VLM](./images/VLM_Architecture.png)

- **Bộ mã hóa hình ảnh (Image Encoder)**: Biến đổi hình ảnh thô thành các biểu diễn số học nhỏ gọn. Các bộ mã hóa được huấn luyện trước như CLIP hoặc vision transformers (ViT) thường được sử dụng.
- **Bộ chiếu nhúng (Embedding Projector)**: Ánh xạ các đặc trưng của hình ảnh vào không gian tương thích với các phép nhúng văn bản, thường sử dụng các lớp dày đặc (dense layers) hoặc các phép biến đổi tuyến tính.
- **Bộ giải mã văn bản (Text Decoder)**: Hoạt động như thành phần sinh ngôn ngữ, chuyển thông tin đa phương thức đã hợp nhất thành văn bản mạch lạc. Ví dụ bao gồm các mô hình sinh (generative models) như Llama hoặc Vicuna.
- **Bộ chiếu đa phương thức (Multimodal Projector)**: Cung cấp một lớp bổ sung để trộn lẫn các biểu diễn hình ảnh và văn bản. Nó rất quan trọng đối với các mô hình như LLaVA để thiết lập các kết nối mạnh mẽ hơn giữa hai phương thức.

Hầu hết các VLMs tận dụng các bộ mã hóa hình ảnh và bộ giải mã văn bản đã được huấn luyện trước và căn chỉnh chúng thông qua việc tinh chỉnh bổ sung trên các tập dữ liệu hình ảnh-văn bản được ghép nối. Cách tiếp cận này giúp việc huấn luyện hiệu quả đồng thời cho phép các mô hình khái quát hóa một cách hiệu quả.

### Cách sử dụng

![Quy trình VLM](./images/VLM_Process.png)

VLMs được áp dụng cho một loạt các tác vụ đa phương thức. Khả năng thích ứng của chúng cho phép chúng hoạt động trong các lĩnh vực đa dạng với các mức độ tinh chỉnh khác nhau:

- **Chú thích hình ảnh (Image Captioning)**: Tạo mô tả cho hình ảnh.
- **Trả lời câu hỏi bằng hình ảnh (Visual Question Answering - VQA)**: Trả lời câu hỏi về nội dung của hình ảnh.
- **Truy xuất đa phương thức (Cross-Modal Retrieval)**: Tìm văn bản tương ứng cho một hình ảnh nhất định hoặc ngược lại.
- **Ứng dụng sáng tạo (Creative Applications)**: Hỗ trợ thiết kế, tạo tác phẩm nghệ thuật hoặc tạo nội dung đa phương tiện hấp dẫn.

![Sử dụng VLM](./images/VLM_Usage.png)

Việc huấn luyện và tinh chỉnh VLMs phụ thuộc vào các tập dữ liệu chất lượng cao ghép nối hình ảnh với chú thích văn bản. Các công cụ như thư viện `transformers` của Hugging Face cung cấp quyền truy cập thuận tiện vào các VLMs đã được huấn luyện trước và quy trình làm việc được sắp xếp hợp lý để tinh chỉnh tùy chỉnh.

### Định dạng trò chuyện (Chat Format)

Nhiều VLMs được cấu trúc để tương tác theo kiểu chatbot, nâng cao khả năng sử dụng. Định dạng này bao gồm:

- Một **câu lệnh hệ thống (system message)** đặt vai trò hoặc ngữ cảnh cho mô hình, chẳng hạn như "Bạn là trợ lý phân tích dữ liệu hình ảnh."
- **Truy vấn của người dùng (user queries)** kết hợp đầu vào văn bản và hình ảnh liên quan.
- **Phản hồi của trợ lý (assistant responses)** cung cấp đầu ra văn bản bắt nguồn từ phân tích đa phương thức.

Cấu trúc hội thoại này trực quan và phù hợp với mong đợi của người dùng, đặc biệt là đối với các ứng dụng tương tác như dịch vụ khách hàng hoặc các công cụ giáo dục.

Dưới đây là ví dụ về cách hiển thị đầu vào được định dạng:

```json
[
    {
        "role": "system",
        "content": [{"type": "text", "text": "Bạn là một Mô hình Ngôn ngữ Thị giác chuyên giải thích dữ liệu hình ảnh từ hình ảnh biểu đồ..."}]
    },
    {
        "role": "user",
        "content": [
            {"type": "image", "image": "<image_data>"},
            {"type": "text", "text": "Giá trị cao nhất trong biểu đồ cột là bao nhiêu?"}
        ]
    },
    {
        "role": "assistant",
        "content": [{"type": "text", "text": "42"}]
    }
]
```

**Làm việc với nhiều hình ảnh và video**

VLMs cũng có thể xử lý nhiều hình ảnh hoặc thậm chí video bằng cách điều chỉnh cấu trúc đầu vào để chứa các đầu vào hình ảnh tuần tự hoặc song song. Đối với video, các khung hình có thể được trích xuất và xử lý như các hình ảnh riêng lẻ, trong khi vẫn duy trì thứ tự thời gian.

## Tài liệu tham khảo

- [Hugging Face Blog: Mô hình Ngôn ngữ Thị giác](https://huggingface.co/blog/vlms)
- [Hugging Face Blog: SmolVLM](https://huggingface.co/blog/smolvlm)

## Các bước tiếp theo

⏩ Thử [vlm_usage_sample.ipynb](./notebooks/vlm_usage_sample.ipynb) để thử các cách sử dụng khác nhau của SMOLVLM.

A course on aligning smol models.


# Tạo tập dữ liệu giả lập (Synthetic Datasets)

Dữ liệu giả lập (synthetic data) là dữ liệu được tạo ra nhân tạo mô phỏng việc sử dụng trong thế giới thực. Nó cho phép khắc phục các hạn chế về dữ liệu bằng cách mở rộng hoặc nâng cao các tập dữ liệu. Mặc dù dữ liệu giả lập đã được sử dụng cho một số trường hợp, các mô hình ngôn ngữ lớn đã làm cho các tập dữ liệu giả lập trở nên phổ biến hơn cho việc huấn luyện trước, huấn luyện sau và đánh giá các mô hình ngôn ngữ.

Chúng ta sẽ sử dụng [`distilabel`](https://distilabel.argilla.io/latest/), một thư viện (framework) tạo dữ liệu giả lập và phản hồi AI cho các kỹ sư, những người cần các quy trình (pipeline) nhanh, đáng tin cậy và có thể mở rộng dựa trên các bài báo nghiên cứu đã được xác minh. Để tìm hiểu sâu hơn về package và các phương pháp hay nhất, hãy xem [tài liệu](https://distilabel.argilla.io/latest/).

## Tổng quan về Mô-đun

Dữ liệu giả lập cho các mô hình ngôn ngữ có thể được phân loại thành ba loại: hướng dẫn (instructions), sở thích (preferences) và phê bình (critiques). Chúng ta sẽ tập trung vào hai loại đầu tiên, tập trung vào việc tạo ra các tập dữ liệu để tinh chỉnh hướng dẫn (instruction tuning) và điều chỉnh sở thích (preference alignment). Trong cả hai loại, chúng ta sẽ đề cập đến các khía cạnh của loại thứ ba, tập trung vào việc cải thiện dữ liệu hiện có bằng các phê bình và viết lại của mô hình.

![Phân loại dữ liệu giả lập](./images/taxonomy-synthetic-data.png)

## Nội dung

### 1. [Tập dữ liệu hướng dẫn](./instruction_datasets.md)

Tìm hiểu cách tạo tập dữ liệu hướng dẫn để tinh chỉnh hướng dẫn. Chúng ta sẽ khám phá việc tạo các tập dữ liệu tinh chỉnh hướng dẫn thông qua các lời nhắc (prompting) cơ bản và sử dụng các kỹ thuật nhắc nhở tinh tế hơn từ các bài báo. Các tập dữ liệu tinh chỉnh hướng dẫn với dữ liệu mẫu (seed data) để học trong ngữ cảnh (in-context learning) có thể được tạo ra thông qua các phương pháp như `SelfInstruct` và `Magpie`. Ngoài ra, chúng ta sẽ khám phá sự tiến hóa hướng dẫn thông qua `EvolInstruct`. [Bắt đầu học](./instruction_datasets.md).

### 2. [Tập dữ liệu ưu tiên](./preference_datasets.md)

Tìm hiểu cách tạo tập dữ liệu sở thích để điều chỉnh sở thích. Chúng ta sẽ xây dựng dựa trên các phương pháp và kỹ thuật được giới thiệu trong phần 1, bằng cách tạo thêm các phản hồi. Tiếp theo, chúng ta sẽ học cách cải thiện các phản hồi đó bằng lời nhắc `EvolQuality`. Cuối cùng, chúng ta sẽ khám phá cách đánh giá các phản hồi bằng lời nhắc `UltraFeedback`, lời nhắc này sẽ tạo ra điểm số và phê bình, cho phép chúng ta tạo các cặp sở thích. [Bắt đầu học](./preference_datasets.md).

### Notebook bài tập

| Tiêu đề | Mô tả | Bài tập | Liên kết | Colab |
|-------|-------------|----------|------|-------|
| Tập dữ liệu hướng dẫn | Tạo tập dữ liệu để tinh chỉnh hướng dẫn | 🐢 Tạo tập dữ liệu tinh chỉnh hướng dẫn
 🐕 Tạo tập dữ liệu tinh chỉnh hướng dẫn với dữ liệu hạt giống
 🦁 Tạo tập dữ liệu tinh chỉnh hướng dẫn với dữ liệu hạt giống và với sự tiến hóa hướng dẫn | [Liên kết](./notebooks/instruction_sft_dataset.ipynb) | [Colab](https://githubtocolab.com/huggingface/smol-course/tree/main/6_synthetic_datasets/notebooks/instruction_sft_dataset.ipynb) |
| Tập dữ liệu ưu tiên | Tạo tập dữ liệu để điều chỉnh sở thích | 🐢 Tạo tập dữ liệu điều chỉnh sở thích
 🐕 Tạo tập dữ liệu điều chỉnh sở thích với sự tiến hóa phản hồi
 🦁 Tạo tập dữ liệu điều chỉnh sở thích với sự tiến hóa phản hồi và phê bình | [Liên kết](./notebooks/preference_alignment_dataset.ipynb) | [Colab](https://githubtocolab.com/huggingface/smol-course/tree/main/6_synthetic_datasets/notebooks/preference_alignment_dataset.ipynb) |

## Tài liệu tham khảo

- [Tài liệu Distilabel](https://distilabel.argilla.io/latest/)
- [Trình tạo dữ liệu tổng hợp là ứng dụng UI](https://huggingface.co/blog/synthetic-data-generator)
- [SmolTalk](https://huggingface.co/datasets/HuggingFaceTB/smoltalk)
- [Self-instruct](https://arxiv.org/abs/2212.10560)
- [Evol-Instruct](https://arxiv.org/abs/2304.12244)
- [Magpie](https://arxiv.org/abs/2406.08464)
- [UltraFeedback](https://arxiv.org/abs/2310.01377)
- [Deita](https://arxiv.org/abs/2312.15685)

A course on aligning smol models.


# Tạo tập dữ liệu hướng dẫn

Trong [chương về tinh chỉnh hướng dẫn (instruction tuning)](../1_instruction_tuning/README.md), chúng ta đã học về việc tinh chỉnh mô hình với Tinh chỉnh có giám sát (Supervised Fine-tuning). Trong phần này, chúng ta sẽ khám phá cách tạo tập dữ liệu hướng dẫn cho SFT. Chúng ta sẽ khám phá việc tạo các tập dữ liệu tinh chỉnh hướng dẫn thông qua việc nhắc nhở (prompting) cơ bản và sử dụng các kỹ thuật tinh tế hơn từ các bài báo. Các tập dữ liệu tinh chỉnh hướng dẫn với dữ liệu hạt giống (seed data) để học trong ngữ cảnh (in-context learning) có thể được tạo ra thông qua các phương pháp như SelfInstruct và Magpie. Ngoài ra, chúng ta sẽ khám phá sự tiến hóa hướng dẫn thông qua EvolInstruct. Cuối cùng, chúng ta sẽ khám phá cách tạo tập dữ liệu để tinh chỉnh hướng dẫn bằng cách sử dụng quy trình (pipeline) distilabel.

## Từ lời nhắc đến dữ liệu

Dữ liệu giả lập (Synthetic data) nghe có vẻ phức tạp, nhưng nó có thể được đơn giản hóa thành việc tạo dữ liệu thông qua việc nhắc nhở hiệu quả để trích xuất kiến thức từ mô hình. Đổi lại, bạn có thể coi đây là một cách để tạo dữ liệu cho một tác vụ cụ thể. Thách thức là nhắc nhở một cách hiệu quả trong khi đảm bảo dữ liệu đa dạng và mang tính đại diện. May mắn thay, nhiều bài báo đã khám phá vấn đề này và chúng ta sẽ khám phá một số bài báo hữu ích trong khóa học này. Trước hết, chúng ta sẽ khám phá cách tạo dữ liệu giả lập thông qua việc nhắc nhở thủ công.

### Nhắc nhở cơ bản (Basic Prompting)

Hãy bắt đầu với một ví dụ cơ bản và tải mô hình [HuggingFaceTB/SmolLM2-1.7B-Instruct](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct) bằng cách sử dụng tích hợp `transformers` của thư viện `distilabel`. Chúng ta sẽ sử dụng lớp `TextGeneration` để tạo ra một `lời nhắc` (prompt) tổng hợp và sử dụng nó để tạo ra một `phần hoàn thành` (completion).

Tiếp theo, chúng ta sẽ tải mô hình bằng thư viện `distilabel`.

```python
from distilabel.llms import TransformersLLM
from distilabel.steps.tasks import TextGeneration

llm = TransformersLLM(model="HuggingFaceTB/SmolLM2-1.7B-Instruct")
gen = TextGeneration(llm=llm)
gen.load()
```

> **Note:**
> `Distilabel` tải `llm` vào bộ nhớ, vì vậy, khi làm việc trong notebook, chúng ta cần `gen.unload()` sau khi hoàn thành để tránh các vấn đề về bộ nhớ.

Bây giờ chúng ta sẽ sử dụng `llm` để tạo ra một `lời nhắc` để tinh chỉnh hướng dẫn.

```python
next(gen.process([{"instruction": "Tạo một câu hỏi về Khóa học Smol của Hugging Face về các mô hình AI nhỏ."}]))
# Ví dụ: Mục đích của Khóa học Smol là gì?
```

Cuối cùng, chúng ta có thể sử dụng cùng một `lời nhắc` đó làm đầu vào để tạo ra một `phần hoàn thành`.

```python
next(gen.process([{"instruction": "Mục đích của Khóa học Smol là gì?"}]))
# Ví dụ: Khóa học Smol là một nền tảng được thiết kế để học các khái niệm khoa học máy tính.
```

Tuyệt! Chúng ta có thể tạo ra một `lời nhắc` tổng hợp và một `phần hoàn thành` tương ứng. Việc sử dụng lại phương pháp đơn giản này trên quy mô lớn sẽ cho phép chúng ta tạo ra nhiều dữ liệu hơn, tuy nhiên, chất lượng của dữ liệu không tốt lắm và không tính đến các sắc thái của khóa học hoặc lĩnh vực của chúng ta. Ngoài ra, việc chạy lại mã hiện tại cho chúng ta thấy dữ liệu không đa dạng lắm. May mắn thay, có nhiều cách để giải quyết vấn đề này.

### SelfInstruct

`SelfInstruct` là một lời nhắc tạo ra các hướng dẫn mới dựa trên tập dữ liệu mẫu. Dữ liệu mẫu này có thể là một hướng dẫn đơn lẻ hoặc một đoạn ngữ cảnh. Quá trình bắt đầu với một nhóm dữ liệu mẫu ban đầu. Mô hình ngôn ngữ sau đó được nhắc để tạo ra các hướng dẫn mới dựa trên dữ liệu mẫu này bằng cách sử dụng phương pháp học trong ngữ cảnh (in-context learning). Lời nhắc được [triển khai trong distilabel](https://github.com/argilla-io/distilabel/blob/main/src/distilabel/steps/tasks/templates/self-instruct.jinja2) và một phiên bản đơn giản hóa được hiển thị bên dưới:

```
# Mô tả nhiệm vụ
Phát triển {{ num_instructions }} truy vấn của người dùng có thể được nhận bởi ứng dụng AI đã cho và áp dụng cho ngữ cảnh được cung cấp. Nhấn mạnh sự đa dạng trong động từ và cấu trúc ngôn ngữ trong khả năng văn bản của mô hình.

# Ngữ cảnh
{{ input }}

# Đầu ra
```

Để sử dụng nó, chúng ta cần truyền `llm` cho [lớp SelfInstruct](https://distilabel.argilla.io/dev/components-gallery/tasks/selfinstruct/). Hãy sử dụng văn bản từ [phần Từ lời nhắc đến dữ liệu](#prompt-to-data) làm ngữ cảnh và tạo ra một hướng dẫn mới.

```python
from distilabel.steps.tasks import SelfInstruct

self_instruct = SelfInstruct(llm=llm)
self_instruct.load()

context = "<prompt_to_data_section>" # Thay thế bằng nội dung của phần Từ lời nhắc đến dữ liệu

next(self_instruct.process([{"input": text}]))["instructions"][0]
# Quá trình tạo dữ liệu tổng hợp thông qua việc nhắc nhở thủ công là gì?
```

Hướng dẫn được tạo ra đã tốt hơn rất nhiều và nó phù hợp với nội dung và lĩnh vực thực tế của chúng ta. Tuy nhiên, chúng ta có thể làm tốt hơn nữa bằng cách cải thiện lời nhắc thông qua phương pháp tiến hóa (evolution).

### EvolInstruct

EvolInstruct là một kỹ thuật nhắc nhở lấy một hướng dẫn đầu vào và phát triển nó thành một phiên bản tốt hơn của cùng một hướng dẫn. Phiên bản tốt hơn này được định nghĩa theo một tập hợp các tiêu chí và bổ sung các ràng buộc, đào sâu, cụ thể hóa, lập luận hoặc phức tạp hóa cho hướng dẫn ban đầu. Quá trình này có thể được lặp lại nhiều lần để tạo ra các phiên bản tiến hóa khác nhau của cùng một hướng dẫn, lý tưởng nhất là dẫn đến một phiên bản tốt hơn của hướng dẫn ban đầu. Lời nhắc được [triển khai trong distilabel](https://github.com/argilla-io/distilabel/tree/main/src/distilabel/steps/tasks/evol_instruct) và một phiên bản đơn giản hóa được hiển thị bên dưới:

```
Tôi muốn bạn đóng vai trò là một Trình viết lại lời nhắc (Prompt Rewriter).
Cho một lời nhắc, hãy viết lại nó thành một phiên bản phức tạp hơn.
Phức tạp hóa lời nhắc dựa trên các tiêu chí sau:
{{ criteria }}

# Lời nhắc
{{ input }}

# Đầu ra
```

Để sử dụng nó, chúng ta cần truyền `llm` cho [lớp EvolInstruct](https://distilabel.argilla.io/dev/components-gallery/tasks/evolinstruct/). Hãy sử dụng lời nhắc tổng hợp từ [phần SelfInstruct](#selfinstruct) làm đầu vào và phát triển nó thành một phiên bản tốt hơn. Đối với ví dụ này, chúng ta sẽ chỉ tiến hóa trong một thế hệ.

```python
from distilabel.steps.tasks import EvolInstruct

evol_instruct = EvolInstruct(llm=llm, num_evolutions=1)
evol_instruct.load()

text = "Quá trình tạo dữ liệu tổng hợp thông qua việc nhắc nhở thủ công là gì"

next(evol_instruct.process([{"instruction": text}]))
# Quá trình tạo dữ liệu tổng hợp thông qua việc nhắc nhở thủ công là gì?
# Và, làm thế nào hệ thống trí tuệ nhân tạo, GPT4, sử dụng các thuật toán học máy để thao tác dữ liệu đầu vào thành dữ liệu tổng hợp?
```

Hướng dẫn bây giờ phức tạp hơn nhưng đã mất đi một số ý nghĩa ban đầu. Vì vậy, hãy lưu ý rằng việc tiến hóa có thể là một con dao hai lưỡi và chúng ta cần cẩn thận với chất lượng của dữ liệu chúng ta tạo ra.

### Magpie

Magpie là một kỹ thuật dựa vào các yếu tố tự suy luận (auto-regressive) của mô hình ngôn ngữ và [mẫu trò chuyện (chat-template)](../1_instruction_tuning/chat_templates.md) đã được sử dụng trong quá trình tinh chỉnh hướng dẫn. Như bạn có thể nhớ, mẫu trò chuyện là một định dạng cấu trúc các cuộc hội thoại với các chỉ số vai trò rõ ràng (hệ thống, người dùng, trợ lý). Trong giai đoạn tinh chỉnh hướng dẫn, mô hình ngôn ngữ đã được tối ưu hóa để tái tạo định dạng này và đó chính xác là những gì `Magpie` tận dụng. Nó bắt đầu với một lời nhắc trước truy vấn (pre-query-prompt) dựa trên mẫu trò chuyện nhưng nó dừng lại trước chỉ báo tin nhắn của người dùng, ví dụ: `<|im_start|>user\n`, và sau đó nó sử dụng mô hình ngôn ngữ để tạo ra lời nhắc của người dùng cho đến khi kết thúc chỉ báo trợ lý, ví dụ: `<|im_end|>`. Cách tiếp cận này cho phép chúng ta tạo ra rất nhiều dữ liệu một cách rất hiệu quả và thậm chí có thể mở rộng quy mô lên các cuộc hội thoại nhiều lượt. Người ta giả thuyết rằng dữ liệu được tạo ra này tái tạo dữ liệu huấn luyện từ giai đoạn tinh chỉnh hướng dẫn của mô hình được sử dụng.

Trong trường hợp này, các mẫu lời nhắc khác nhau cho mỗi mô hình vì chúng dựa trên định dạng mẫu trò chuyện. Nhưng chúng ta có thể đi qua một phiên bản đơn giản hóa của quá trình từng bước.

```bash
# Bước 1: cung cấp lời nhắc trước truy vấn
<|im_start|>user\n

# Bước 2: mô hình ngôn ngữ tạo ra lời nhắc của người dùng
<|im_start|>user\n
Mục đích của Khóa học Smol là gì?

# Bước 3: dừng quá trình tạo
<|im_end|>
```

Để sử dụng nó trong distilabel, chúng ta cần truyền `llm` cho [lớp Magpie](https://distilabel.argilla.io/dev/components-gallery/tasks/magpie/).

```python
from distilabel.steps.tasks import Magpie

magpie = Magpie(llm=llm)
magpie.load()

next(magpie.process([{"system_prompt": "Bạn là một trợ lý hữu ích."}]))
# [{
#   "role": "user",
#   "content": "Bạn có thể cung cấp cho tôi danh sách 3 trường đại học hàng đầu không?"
# },
# {
#   "role": "assistant",
#   "content": "3 trường đại học hàng đầu là: MIT, Yale, Stanford."
# }]
```

Chúng ta ngay lập tức nhận được một tập dữ liệu với một `lời nhắc` và `phần hoàn thành`. Để cải thiện hiệu suất trên lĩnh vực của riêng mình, chúng ta có thể đưa thêm ngữ cảnh vào `system_prompt`. Để LLM tạo ra dữ liệu lĩnh vực cụ thể kết hợp với Magpie, nó giúp mô tả trong lời nhắc hệ thống (system prompt) các truy vấn của người dùng sẽ là gì. Điều này sau đó được sử dụng trong lời nhắc trước truy vấn trước khi chúng ta bắt đầu tạo lời nhắc của người dùng và thiên về LLM để tạo ra các truy vấn của người dùng trong lĩnh vực đó.

```
Bạn là một trợ lý AI sẽ giúp người dùng giải các bài toán.
```

Điều quan trọng là phải viết lời nhắc hệ thống như được hiển thị ở trên thay vì một cái gì đó như:

```
Bạn là một trợ lý AI tạo ra các bài toán
```

Nói chung, các mô hình ngôn ngữ ít được tối ưu hóa hơn để truyền ngữ cảnh bổ sung cho `system_prompt` vì vậy điều này không phải lúc nào cũng hoạt động tốt cho việc tùy chỉnh như các kỹ thuật khác.

### Từ lời nhắc đến quy trình (pipeline)

Các lớp chúng ta đã thấy cho đến nay đều là các lớp độc lập có thể được sử dụng trong một quy trình. Đây là một khởi đầu tốt, nhưng chúng ta có thể làm tốt hơn nữa bằng cách sử dụng lớp `Pipeline` để tạo tập dữ liệu. Chúng ta sẽ sử dụng bước `TextGeneration` để tạo tập dữ liệu tổng hợp để tinh chỉnh hướng dẫn. Quy trình sẽ bao gồm bước `LoadDataFromDicts` để tải dữ liệu, bước `TextGeneration` để tạo `lời nhắc` và `phần hoàn thành` cho lời nhắc đó. Chúng ta sẽ kết nối các bước và luồng dữ liệu thông qua quy trình bằng toán tử `>>`. Trong [tài liệu của distilabel](https://distilabel.argilla.io/dev/components-gallery/tasks/textgeneration/#input-output-columns), chúng ta có thể thấy các cột đầu vào và đầu ra của bước. Để đảm bảo rằng dữ liệu chảy chính xác qua quy trình, chúng ta sẽ sử dụng tham số `output_mappings` để ánh xạ các cột đầu ra với các cột đầu vào của bước tiếp theo.

```python
from distilabel.llms import TransformersLLM
from distilabel.pipeline import Pipeline
from distilabel.steps import LoadDataFromDicts
from distilabel.steps.tasks import TextGeneration

with Pipeline() as pipeline:
    data = LoadDataFromDicts(data=[{"instruction": "Tạo một câu hỏi ngắn về Khóa học Smol của Hugging Face."}])
    llm = TransformersLLM(model="HuggingFaceTB/SmolLM2-1.7B-Instruct")
    gen_a = TextGeneration(llm=llm, output_mappings={"generation": "instruction"})
    gen_b = TextGeneration(llm=llm, output_mappings={"generation": "response"})
    data >> gen_a >> gen_b

if __name__ == "__main__":
    distiset = pipeline.run(use_cache=False)
    print(distiset["default"]["train"][0])
# [{
#   "instruction": "Mục đích của Khóa học Smol là gì?",
#   "response": "Khóa học Smol là một nền tảng được thiết kế để học các khái niệm khoa học máy tính."
# }]
```

Bên dưới, quy trình này có rất nhiều tính năng hay. Nó tự động lưu trữ các kết quả tạo, vì vậy chúng ta không phải chạy lại các bước tạo. Thư viện có tích hợp khả năng xử lý lỗi (fault-tolerance), vì vậy nếu các bước tạo thất bại, quy trình vẫn sẽ tiếp tục chạy. Và quy trình thực hiện tất cả các bước tạo song song, vì vậy việc tạo nhanh hơn. Chúng ta thậm chí có thể trực quan hóa quy trình bằng phương thức `draw`. Ở đây bạn có thể thấy cách dữ liệu chảy qua quy trình và cách `output_mappings` được sử dụng để ánh xạ các cột đầu ra với các cột đầu vào của bước tiếp theo.

![Pipeline](./images/pipeline.png)

## Các phương pháp hay nhất

- Đảm bảo bạn có dữ liệu hạt giống đa dạng để bao quát nhiều tình huống
- Thường xuyên đánh giá tập dữ liệu để đảm bảo dữ liệu được tạo ra đa dạng và có chất lượng cao
- Lặp lại trên (system)prompt để cải thiện chất lượng của dữ liệu

## Các bước tiếp theo

👨🏽💻 Lập trình -[Notebook bài tập](./notebooks/instruction_sft_dataset.ipynb) để tạo tập dữ liệu để tinh chỉnh hướng dẫn
🧑🏫 Tìm hiểu - Về [tạo tập dữ liệu sở thích](./preference_datasets.md)

## Tài liệu tham khảo

- [Tài liệu Distilabel](https://distilabel.argilla.io/latest/)
- [Self-instruct](https://arxiv.org/abs/2212.10560)
- [Evol-Instruct](https://arxiv.org/abs/2304.12244)
- [Magpie](https://arxiv.org/abs/2406.08464)

A course on aligning smol models.