Fine-Tuning

Giải thích chi tiết và dễ hiểu về Fine-Tuning trong lĩnh vực mô hình ngôn ngữ (LLM):

Fine-Tuning là gì

Fine-tuning là quá trình đào tạo lại (huấn luyện tiếp) một mô hình ngôn ngữ đã được huấn luyện trước (pre-trained) trên dữ liệu cụ thể của bạn, để mô hình:

Ví dụ dễ hiểu

Giả sử bạn có một mô hình GPT đã học hàng tỷ câu văn từ Internet. Nếu bạn muốn nó:

Fine-tuning khác với Prompt Engineering thế nào?

Prompt Engineering Fine-Tuning
Thay đổi prompt để điều khiển đầu ra Huấn luyện lại mô hình để thay đổi hành vi
Không tốn chi phí đào tạo lại Tốn tài nguyên (GPU, RAM, thời gian)
Dễ làm, nhưng hiệu quả có giới hạn Mạnh hơn, dùng cho yêu cầu phức tạp
Không cần dữ liệu huấn luyện Cần dữ liệu huấn luyện có nhãn

Có mấy loại Fine-Tuning?

1. Full Fine-Tuning (ít dùng với LLM lớn)
2. PEFT (Parameter-Efficient Fine-Tuning) – [rất phổ biến hiện nay]

Gồm các kỹ thuật như:

 Ưu điểm:

QLoRA là gì?

QLoRA = LoRA + Quantization (4-bit)
Mục tiêu là fine-tune mô hình lớn (ví dụ: LLaMA-2-13B) trên laptop hoặc GPU yếu.

Rất phổ biến để fine-tune mô hình lớn với ngân sách thấp!

Khi nào nên fine-tune?

Trường hợp Có nên fine-tune?
Muốn mô hình hiểu văn bản nội bộ, tài liệu chuyên ngành  Có
Muốn mô hình trả lời theo phong cách riêng  Có
Chỉ cần thay đổi nhẹ câu trả lời ❌ Dùng prompt thôi là đủ
Không có dữ liệu huấn luyện ❌ Không fine-tune được

Các công cụ hỗ trợ Fine-tuning

Transformers

Transformers là một kiến trúc mạng nơ-ron (neural network architecture) được giới thiệu bởi Google trong bài báo nổi tiếng năm 2017:
"Attention is All You Need".

thay thế hoàn toàn RNN/LSTM trong việc xử lý chuỗi dữ liệu (như văn bản), và trở thành nền tảng cho hầu hết các mô hình ngôn ngữ hiện đại (LLM).

Ý tưởng cốt lõi: Attention

Điểm mạnh của Transformers là cơ chế Attention, cụ thể là Self-Attention.

Giả sử bạn có câu:
"The cat sat on the mat because it was tired."

Từ "it" cần hiểu là đang nói đến "the cat".
Cơ chế attention giúp mô hình xác định được từ nào trong câu liên quan nhất đến từ hiện tại.

Cấu trúc của Transformer

Có 2 phần chính:

1. Encoder (bộ mã hóa)
2. Decoder (bộ giải mã)

Tóm tắt:

Mô hình Dùng phần nào?
BERT Encoder
GPT Decoder
T5 Encoder + Decoder

Thành phần chính trong mỗi layer

  1. Multi-Head Self Attention

    • Cho phép mô hình "chú ý" đến nhiều phần khác nhau của câu cùng lúc.

  2. Feed-Forward Neural Network

    • Một MLP đơn giản sau mỗi attention.

  3. Layer Normalization

    • Giúp mô hình ổn định trong quá trình huấn luyện.

  4. Residual Connections

    • Giúp tránh mất thông tin và tăng hiệu quả học.

Vị trí từ (Positional Encoding)

Transformers không có khái niệm tuần tự như RNN.
→ Phải thêm thông tin vị trí bằng Positional Encoding để mô hình biết thứ tự từ trong câu.

Vì sao Transformers lại mạnh?

Hugging Face Transformers là gì?

Đây là thư viện mã nguồn mở giúp bạn dễ dàng:

Ví dụ sử dụng nhanh (với Hugging Face):

from transformers import pipeline

qa = pipeline("question-answering", model="distilbert-base-cased-distilled-squad")
qa({
    "context": "The cat sat on the mat because it was tired.",
    "question": "Why did the cat sit on the mat?"
})

Tóm tắt dễ nhớ

Thuật ngữ Ý nghĩa ngắn gọn
Transformer Kiến trúc xử lý chuỗi mạnh mẽ, thay thế RNN
Attention Cơ chế "chú ý" đến phần quan trọng của chuỗi
Self-Attention Mỗi từ chú ý đến các từ khác trong chuỗi
Encoder / Decoder Mã hóa / Sinh văn bản
Hugging Face Thư viện dễ dùng để tận dụng mô hình này

 

 

Fine-Tuning trong hệ thống RAG

Fine-tuning đóng vai trò rất quan trọng trong việc nâng cao chất lượng của hệ thống RAG (Retrieval-Augmented Generation). Dưới đây là cách ứng dụng Fine-Tuning cho từng phần trong hệ thống RAG, kèm theo ví dụ và hướng dẫn triển khai.

Tổng quan về hệ thống RAG

Một hệ thống RAG gồm 2 thành phần chính:

  1. Retriever: Truy xuất các đoạn văn bản liên quan từ kho dữ liệu.

  2. Generator (LLM): Sinh câu trả lời dựa trên văn bản đã truy xuất.

Fine-tuning có thể được áp dụng cho retriever, generator, hoặc cả hai để cải thiện độ chính xác và tính hữu ích của câu trả lời.

1. Fine-Tuning Retriever

Mục tiêu:
Cách làm:
Công cụ:
Ví dụ:
from sentence_transformers import SentenceTransformer, InputExample, losses
from torch.utils.data import DataLoader

train_examples = [
    InputExample(texts=["What is the capital of France?", "Paris is the capital of France."]),
    ...
]
model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
train_dataloader = DataLoader(train_examples, batch_size=16)
train_loss = losses.MultipleNegativesRankingLoss(model)

model.fit(train_objectives=[(train_dataloader, train_loss)], epochs=1)

2. Fine-Tuning Generator (LLM)

Mục tiêu:
Cách làm:
Công cụ:
Ví dụ:
from transformers import T5Tokenizer, T5ForConditionalGeneration, Trainer, TrainingArguments

tokenizer = T5Tokenizer.from_pretrained("t5-small")
model = T5ForConditionalGeneration.from_pretrained("t5-small")

def preprocess(example):
    input_text = f"question: {example['question']} context: {example['context']}"
    target_text = example["answer"]
    return tokenizer(input_text, truncation=True, padding="max_length", max_length=512), \
           tokenizer(target_text, truncation=True, padding="max_length", max_length=64)

# Sử dụng Trainer API để fine-tune

3. Fine-Tuning kết hợp Retriever + Generator

Khi nào nên Fine-Tune trong RAG?

Dấu hiệu Giải pháp
Truy xuất sai tài liệu Fine-tune retriever
Sinh câu trả lời sai / không khớp Fine-tune generator
Cần tối ưu cho domain cụ thể Fine-tune cả hai

Tools hỗ trợ Fine-tuning RAG

Thành phần Fine-tuning giúp gì?
Retriever Tìm đúng đoạn tài liệu liên quan
Generator Sinh câu trả lời chính xác hơn từ context
Toàn hệ thống Tối ưu hóa end-to-end, tăng chất lượng RAG

Tóm tắt:

Thành phần Fine-tuning giúp gì?
Retriever Tìm đúng đoạn tài liệu liên quan
Generator Sinh câu trả lời chính xác hơn từ context
Toàn hệ thống Tối ưu hóa end-to-end, tăng chất lượng RAG

 

 

 

Dự án RAG Retriever + Generator Fine-Tuning

Dưới đây là một template RAG đầy đủ (Retriever + Generator) có hỗ trợ Fine-Tuning cả hai phần: Retriever (bi-encoder)Generator (T5 hoặc LLaMA). Mình sẽ chia thành từng bước rõ ràng:

Cấu trúc thư mục đề xuất

rag_finetuning_project/
├── data/
│   └── train.jsonl
├── retriever/
│   ├── train_retriever.py
│   └── model/
├── generator/
│   ├── train_generator.py
│   └── model/
├── rag_pipeline.py
└── requirements.txt

1. Dataset format

data/train.jsonl:

{"question": "What is the capital of France?", "context": "Paris is the capital of France.", "answer": "Paris"}
{"question": "What is the currency of Japan?", "context": "The yen is the official currency of Japan.", "answer": "yen"}
...

2. Fine-Tune Retriever (retriever/train_retriever.py)

Sử dụng sentence-transformers.

from sentence_transformers import SentenceTransformer, InputExample, losses
from torch.utils.data import DataLoader
import json

# Load data
examples = []
with open("../data/train.jsonl", "r") as f:
    for line in f:
        ex = json.loads(line)
        examples.append(InputExample(texts=[ex["question"], ex["context"]]))

# Load model
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
train_dataloader = DataLoader(examples, batch_size=16)
train_loss = losses.MultipleNegativesRankingLoss(model)

# Train
model.fit(train_objectives=[(train_dataloader, train_loss)], epochs=1, output_path="model/")

3. Fine-Tune Generator (generator/train_generator.py)

Sử dụng T5 từ Hugging Face Transformers.

from transformers import T5Tokenizer, T5ForConditionalGeneration, Trainer, TrainingArguments
from datasets import load_dataset
import json

def load_data():
    data = []
    with open("../data/train.jsonl", "r") as f:
        for line in f:
            ex = json.loads(line)
            input_text = f"question: {ex['question']} context: {ex['context']}"
            data.append({"input": input_text, "label": ex["answer"]})
    return data

data = load_data()
from datasets import Dataset
dataset = Dataset.from_list(data)

tokenizer = T5Tokenizer.from_pretrained("t5-small")
model = T5ForConditionalGeneration.from_pretrained("t5-small")

def preprocess(examples):
    model_input = tokenizer(examples["input"], truncation=True, padding="max_length", max_length=512)
    with tokenizer.as_target_tokenizer():
        labels = tokenizer(examples["label"], truncation=True, padding="max_length", max_length=32)
    model_input["labels"] = labels["input_ids"]
    return model_input

tokenized = dataset.map(preprocess, batched=True)

args = TrainingArguments(
    output_dir="model/",
    evaluation_strategy="no",
    per_device_train_batch_size=4,
    num_train_epochs=3,
    save_strategy="epoch",
)

trainer = Trainer(
    model=model,
    args=args,
    train_dataset=tokenized,
    tokenizer=tokenizer,
)

trainer.train()

4. Tích hợp Retriever + Generator (rag_pipeline.py)

from sentence_transformers import SentenceTransformer, util
from transformers import T5Tokenizer, T5ForConditionalGeneration

retriever = SentenceTransformer("retriever/model")
generator = T5ForConditionalGeneration.from_pretrained("generator/model")
tokenizer = T5Tokenizer.from_pretrained("t5-small")

# Fake corpus (ở thực tế sẽ là hàng ngàn documents)
corpus = [
    "Paris is the capital of France.",
    "Tokyo is the capital of Japan.",
    "The yen is the currency of Japan.",
]

corpus_embeddings = retriever.encode(corpus, convert_to_tensor=True)

def rag_answer(question):
    question_embedding = retriever.encode(question, convert_to_tensor=True)
    top_hit = util.semantic_search(question_embedding, corpus_embeddings, top_k=1)[0][0]
    context = corpus[top_hit['corpus_id']]
    
    input_text = f"question: {question} context: {context}"
    input_ids = tokenizer(input_text, return_tensors="pt", truncation=True).input_ids
    output_ids = generator.generate(input_ids)
    return tokenizer.decode(output_ids[0], skip_special_tokens=True)

# Ví dụ:
print(rag_answer("What is the capital of France?"))

requirements.txt

transformers
datasets
sentence-transformers
torch

Tùy chọn nâng cao


Flan-T5

Flan-T5 là một mô hình ngôn ngữ do Google huấn luyện, thuộc họ T5 (Text-To-Text Transfer Transformer).

Tên đầy đủ: "Fine-tuned LAnguage Net T5"
Nó là T5 được huấn luyện lại (fine-tune) để trả lời câu hỏi tốt hơn, chính xác hơn, và hiểu lệnh tốt hơn.

Cấu trúc cơ bản

Flan-T5 sử dụng mô hình T5 gốc – một Transformer với kiến trúc Encoder-Decoder:

Và mọi thứ đều được xử lý dưới dạng text → text.
Ví dụ:

Input Output
"Translate English to French: Hello" "Bonjour"
"Summarize: Hôm nay trời đẹp..." "Trời đẹp hôm nay."
"What is the capital of Japan?" "Tokyo"

Flan-T5 có gì đặc biệt?

Flan-T5 = T5 + fine-tuning thêm hàng loạt tác vụ hướng lệnh (instruction tuning)

Những điểm cải tiến:

  1. Instruction tuning:
    Flan-T5 được huấn luyện để hiểu lệnh rõ ràng, ví dụ như:

    • “Tóm tắt văn bản sau…”

    • “Dịch sang tiếng Đức…”

    • “Viết lại đoạn văn cho dễ hiểu…”

  2. Đa nhiệm (multi-task learning):
    Huấn luyện trên hơn 1000 loại tác vụ NLP khác nhau.

  3. Nhiều kích thước:

    • flan-t5-small, flan-t5-base, flan-t5-large, flan-t5-xl, flan-t5-xxl (lớn nhất ~11B tham số)

  4. Dễ dùng, mã nguồn mở:

    • Có sẵn trên Hugging Face 

    • Chỉ cần vài dòng code để sử dụng.

Ví dụ dùng Flan-T5 với Python
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM

model = AutoModelForSeq2SeqLM.from_pretrained("google/flan-t5-base")
tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-base")

input_text = "Translate English to Vietnamese: How are you?"
inputs = tokenizer(input_text, return_tensors="pt")
outputs = model.generate(**inputs)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

Kết quả: "Bạn khỏe không?"

Tóm tắt

Tiêu chí Flan-T5
Thuộc loại Transformer (T5 – Encoder-Decoder)
Do ai tạo Google
Mục tiêu Giải quyết đa nhiệm NLP theo lệnh
Cách hoạt động Text in → Text out
Ưu điểm chính Hiểu tốt câu lệnh, đa nhiệm, dễ fine-tune

 

Flan-T5 với RAG

Dưới đây là một bài thực hành chi tiết giúp bạn xây dựng hệ thống RAG (Retrieval-Augmented Generation) sử dụng mô hình Flan-T5 để trả lời câu hỏi dựa trên tài liệu PDF. Hệ thống này sẽ:

Môi trường và thư viện cần thiết

Trước tiên, hãy cài đặt các thư viện cần thiết:

pip install transformers sentence-transformers faiss-cpu pypdf

1. Trích xuất văn bản từ PDF

from pypdf import PdfReader

def extract_text_from_pdf(pdf_path):
    reader = PdfReader(pdf_path)
    text = ""
    for page in reader.pages:
        text += page.extract_text()
    return text

2. Chia văn bản thành các đoạn nhỏ (chunk)

def split_text(text, chunk_size=500, overlap=50):
    chunks = []
    start = 0
    while start < len(text):
        end = start + chunk_size
        chunks.append(text[start:end])
        start += chunk_size - overlap
    return chunks

3. Tạo vector embedding cho từng đoạn văn bản

from sentence_transformers import SentenceTransformer

def create_embeddings(chunks):
    model = SentenceTransformer('all-MiniLM-L6-v2')
    embeddings = model.encode(chunks)
    return embeddings, model

4. Lưu trữ embeddings trong FAISS5. Truy xuất các đoạn văn bản liên quan đến câu hỏi

def retrieve_relevant_chunks(question, model, index, chunks, top_k=3):
    question_embedding = model.encode([question])
    distances, indices = index.search(np.array(question_embedding), top_k)
    retrieved_chunks = [chunks[i] for i in indices[0]]
    return retrieved_chunks

6. Sinh câu trả lời bằng Flan-T5

from transformers import AutoTokenizer, AutoModelForSeq2SeqLM

def generate_answer(question, context, model_name="google/flan-t5-base"):
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModelForSeq2SeqLM.from_pretrained(model_name)

    prompt = f"Context: {context}\n\nQuestion: {question}\n\nAnswer:"
    inputs = tokenizer(prompt, return_tensors="pt", truncation=True)
    outputs = model.generate(**inputs, max_new_tokens=100)
    answer = tokenizer.decode(outputs[0], skip_special_tokens=True)
    return answer

7. Kết hợp tất cả thành một pipeline

def rag_pipeline(pdf_path, question):
    # Bước 1: Trích xuất và xử lý văn bản
    text = extract_text_from_pdf(pdf_path)
    chunks = split_text(text)

    # Bước 2: Tạo embeddings và index
    embeddings, embed_model = create_embeddings(chunks)
    index = create_faiss_index(np.array(embeddings))

    # Bước 3: Truy xuất các đoạn liên quan
    relevant_chunks = retrieve_relevant_chunks(question, embed_model, index, chunks)
    context = " ".join(relevant_chunks)

    # Bước 4: Sinh câu trả lời
    answer = generate_answer(question, context)
    return answer

Ví dụ sử dụng

pdf_path = "duong-luoi-bo.pdf"
question = "Tranh chấp ở Biển Đông bắt đầu từ khi nào?"
answer = rag_pipeline(pdf_path, question)
print("Câu trả lời:", answer)

Gợi ý nâng cao