Chuyển đến nội dung chính

Fine-Tuning Phi-4 Reasoning: Hướng Dẫn Từng Bước

Hướng dẫn từng bước để fine-tune mô hình lập luận tối tân Phi-4 Reasoning trên bộ dữ liệu Hỏi & Đáp tài chính trong chưa đầy 10 phút.
· 9 phút đọc

Khám phá cùng AI

Phi-4-reasoning là mô hình open-weight với 14 tỷ tham số, được thiết kế cho các tác vụ lập luận phức tạp, sánh ngang với các mô hình lớn hơn nhiều nhờ fine-tuning kỹ lưỡng trên các bộ dữ liệu được tuyển chọn. Mô hình tạo ra các chuỗi lập luận chi tiết và tận dụng khả năng tính toán ở thời điểm suy luận để đạt hiệu năng vượt trội. 

Phiên bản nâng cao, Phi-4-reasoning-plus, sử dụng học tăng cường và số token gấp 1,5 lần để đạt độ chính xác cao hơn nữa. Dù có kích thước nhỏ hơn, cả hai mô hình đều vượt qua các đối thủ lớn như DeepSeek-R1-Distill-70B và thậm chí còn vượt cả DeepSeek-R1 với 671 tỷ tham số trên các benchmark.

Trong hướng dẫn này, chúng ta sẽ sử dụng mô hình Phi-4-reasoning-plus và fine-tune nó trên bộ dữ liệu lập luận Hỏi & Đáp tài chính. Tài liệu sẽ bao gồm thiết lập môi trường Runpod, tải mô hình, tokenizer và dữ liệu, chuẩn bị dữ liệu cho huấn luyện, cấu hình mô hình, chạy đánh giá và lưu bộ điều hợp (adapter) mô hình đã fine-tune.

Bạn có thể tìm hiểu về Phi 4 và xây dựng một trợ giảng ngôn ngữ đa phương thức bằng cách làm theo hướng dẫn Phi-4-Multimodal: Hướng Dẫn Kèm Dự Án Demo.

Hình đại diện bài viết fine-tuning Phi-4 Reasoning

Hình: Tác giả

1. Thiết lập 

Trong hướng dẫn này, chúng ta sẽ dùng RunPod làm môi trường tính toán. Thực hiện các bước sau để thiết lập môi trường:

1. Truy cập Bảng điều khiển RunPod và nhấp “Deploy On-Demand” để tạo một pod mới.

2. Chọn GPU A100, đủ để tải và fine-tune mô hình.

Thiết lập một pod trong Runpod

3. Tùy chỉnh triển khai của bạn:

Chỉnh sửa Pod trong Runpod

  • Tăng dung lượng lưu trữ lên 150GB.
  • Thêm Hugging Face Token làm biến môi trường. Token này cần thiết để tải các mô hình có cổng bảo vệ (gated) và đẩy mô hình lên Hugging Face Hub.

thiết lập lưu trữ và biến môi trường.

4. Sau khi pod được triển khai, nhấp nút Connect để mở JupyterLab.

5. Trong JupyterLab, tạo một notebook mới và cài đặt tất cả các gói Python cần thiết. 

%%capture
%pip install -U transformers
%pip install -U datasets 
%pip install -U accelerate 
%pip install -U peft 
%pip install -U trl 
%pip install -U bitsandbytes
%pip install huggingface_hub[hf_xet]

6. Tiếp theo, đăng nhập vào Hugging Face CLI bằng token bạn đã lưu trước đó. 

from huggingface_hub import login
import os

hf_token = os.environ.get("HF_TOKEN")
login(hf_token)

2. Tải Mô Hình và Tokenizer

Trong hướng dẫn này, chúng ta sẽ tải đầy đủ mô hình microsoft/Phi-4-reasoning-plus từ Hugging Face cùng với tokenizer của nó.

Chúng ta không dùng lượng tử hóa 4-bit ở đây vì phần cứng có thể xử lý thoải mái trọng số full-precision. A100 với 80 GB VRAM còn dư dả, vì toàn bộ mô hình Phi-4-Reasoning chỉ chiếm khoảng 28 GB, nên vẫn còn hơn 50 GB cho activations, trạng thái optimizer và các chi phí huấn luyện khác.

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

# Load tokenizer & model
model_dir = "microsoft/Phi-4-reasoning-plus"

tokenizer = AutoTokenizer.from_pretrained(model_dir, use_fast=True)

model = AutoModelForCausalLM.from_pretrained(
    model_dir, 
    device_map="auto",  
    torch_dtype=torch.bfloat16,
    trust_remote_code=True             
)

model.config.use_cache = False
model.config.pretraining_tp = 1

Tải xuống mô hình lập luận Phi 4

Sau khi tải mô hình, bạn có thể kiểm tra mức tiêu thụ bộ nhớ GPU bằng lệnh sau:

!nvidia-smi

Toàn bộ mô hình dùng khoảng 28GB VRAM. GPU A100 có 80GB VRAM, vì vậy bạn vẫn còn 55GB bộ nhớ trống cho các tác vụ fine-tuning.

Mức sử dụng bộ nhớ GPU

3. Tải và Xử Lý Bộ Dữ Liệu

Trước khi tải bộ dữ liệu, chúng ta sẽ định nghĩa phong cách prompt huấn luyện. Prompt này bao gồm:

  • Hướng dẫn hệ thống: Chỉ dẫn cho mô hình về cách phản hồi.
  • Câu hỏi tài chính mở: Câu hỏi do người dùng đưa ra, cần lập luận.
  • Phản hồi của trợ lý kèm phần suy nghĩ: Chuỗi lập luận từng bước kèm theo câu trả lời.

Phong cách prompt này nhằm khuyến khích mô hình suy nghĩ phản biện và tạo ra quá trình lập luận cùng với phản hồi cuối.

train_prompt_style="""
<|im_start|>system<|im_sep|>
Below is an instruction that describes a task, paired with an input that provides further context. 
Write a response that appropriately completes the request. 
Before answering, think carefully about the question and create a step-by-step chain of thoughts to ensure a logical and accurate response.
<|im_end|>
<|im_start|>user<|im_sep|>
{}<|im_end|>
<|im_start|>assistant<|im_sep|>
<think>
{}
</think>
{}
"""

Chúng ta sẽ tạo một hàm Python để ánh xạ các cột của bộ dữ liệu (câu hỏi, lập luận và phản hồi) vào mẫu prompt. Hàm này sẽ tạo một cột mới tên là “text”, bao gồm toàn bộ thành phần prompt.

EOS_TOKEN = tokenizer.eos_token  # Must add EOS_TOKEN

def formatting_prompts_func(examples):
    inputs = examples["Open-ended Verifiable Question"]
    complex_cots = examples["Complex_CoT"]
    outputs = examples["Response"]
    texts = []
    for input, cot, output in zip(inputs,complex_cots,outputs):
        text = train_prompt_style.format(input,cot,output) + EOS_TOKEN
        texts.append(text)
    return {
        "text": texts,
    }

Bây giờ, chúng ta sẽ tải bộ dữ liệu (TheFinAI/Fino1_Reasoning_Path_FinQA), lấy 1000 mẫu, áp dụng hàm định dạng và tạo cột “text” mới.

from datasets import load_dataset

dataset = load_dataset(
    "TheFinAI/Fino1_Reasoning_Path_FinQA", split="train[0:1000]", trust_remote_code=True
)
dataset = dataset.map(
    formatting_prompts_func,
    batched=True,
)
dataset["text"][20]

Cột "text" của bộ dữ liệu bao gồm hướng dẫn hệ thống, câu hỏi của người dùng, chuỗi lập luận từng bước và đáp án cuối cùng. Cấu trúc này giúp mô hình hiểu nhiệm vụ và tạo ra phản hồi chất lượng cao.

Tổng quan bộ dữ liệu đã xử lý.

Hàm STF trainer mới không nhận tokenizer trực tiếp. Do đó, chúng ta cần chuyển tokenizer thành data collator bằng DataCollatorForLanguageModeling của Transformers.

from transformers import DataCollatorForLanguageModeling

data_collator = DataCollatorForLanguageModeling(
    tokenizer=tokenizer,
    mlm=False
)

4. Suy Luận Mô Hình Trước Khi Fine-Tune

Trước khi fine-tune, chúng ta sẽ chạy suy luận trên một câu hỏi từ bộ dữ liệu để so sánh hiệu năng sau khi fine-tune. Phong cách prompt suy luận tương tự prompt huấn luyện nhưng bỏ phần lập luận và đáp án.

inference_prompt_style = """
<|im_start|>system<|im_sep|>
Below is an instruction that describes a task, paired with an input that provides further context. 
Write a response that appropriately completes the request. 
Before answering, think carefully about the question and create a step-by-step chain of thoughts to ensure a logical and accurate response.
<|im_end|>
<|im_start|>user<|im_sep|>
{}<|im_end|>
<|im_start|>assistant<|im_sep|>
<think>
{}
"""

Chúng ta sẽ chọn câu hỏi thứ 21 từ bộ dữ liệu, định dạng và tokenize để suy luận:

question = dataset[20]['Open-ended Verifiable Question']
inputs = tokenizer(
    [inference_prompt_style.format(question, "") + tokenizer.eos_token],
    return_tensors="pt"
).to("cuda")

outputs = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    max_new_tokens=1200,
    eos_token_id=tokenizer.eos_token_id,
    use_cache=True,
)
response = tokenizer.batch_decode(outputs)
print(response[0].split("<|im_start|>assistant<|im_sep|>")[1])

Mô hình tạo phần lập luận dài nhưng không đưa ra câu trả lời rõ ràng. Tăng max_new_tokens lên 2000 có thể cho ra đáp án, nhưng cách này kém hiệu quả và thiếu ổn định. Lập luận rời rạc và kết luận thiếu chính xác.

Phản hồi sinh ra trước khi fine-tune

5. Thiết Lập Mô Hình

Trong phần này, chúng ta sẽ triển khai LoRA (Low-Rank Adaptation), một kỹ thuật fine-tuning tiết kiệm tham số được thiết kế để thích ứng các LLM với tác vụ mới. LoRA hoạt động bằng cách đóng băng phần lớn tham số của mô hình và đưa vào một tập nhỏ tham số có thể huấn luyện dưới dạng các ma trận hạng thấp. Các ma trận này được thêm vào mô hình dưới dạng phân rã hạng thấp, cho phép mô hình thích ứng với tác vụ mới mà không cần sửa đổi hay lưu toàn bộ trọng số.

Sử dụng LoRA giúp giảm đáng kể mức tiêu thụ VRAM và thời gian huấn luyện, trong khi vẫn giữ độ chính xác tương đương fine-tuning đầy đủ.

from peft import LoraConfig, get_peft_model

# LoRA config
peft_config = LoraConfig(
    lora_alpha=16,                           # Scaling factor for LoRA
    lora_dropout=0.05,                       # Add slight dropout for regularization
    r=64,                                    # Rank of the LoRA update matrices
    bias="none",                             # No bias reparameterization
    task_type="CAUSAL_LM",                   # Task type: Causal Language Modeling
    target_modules=[
        "q_proj",
        "k_proj",
        "v_proj",
        "o_proj",
        "gate_proj",
        "up_proj",
        "down_proj",
    ],  # Target modules for LoRA
)

model = get_peft_model(model, peft_config)

Tiếp theo, chúng ta thiết lập SFTTrainer, công cụ đơn giản hóa quá trình fine-tuning bằng cách kết hợp các thành phần thiết yếu như bộ dữ liệu, mô hình, data collator, tham số huấn luyện và cấu hình LoRA vào một quy trình hợp nhất. Cách tiếp cận này giúp fine-tuning hiệu quả, thân thiện và tùy biến cao.

from trl import SFTTrainer
from transformers import TrainingArguments


# Training Arguments
training_arguments = TrainingArguments(
    output_dir="output",
    per_device_train_batch_size=1,
    per_device_eval_batch_size=1,
    gradient_accumulation_steps=2,
    optim="paged_adamw_32bit",
    num_train_epochs=1,
    logging_steps=0.2,
    warmup_steps=10,
    logging_strategy="steps",
    learning_rate=2e-4,
    fp16=False,
    bf16=False,
    group_by_length=True,
    report_to="none"
)

# Initialize the Trainer
trainer = SFTTrainer(
    model=model,
    args=training_arguments,
    train_dataset=dataset,
    peft_config=peft_config,
    data_collator=data_collator,
)

6. Huấn Luyện Mô Hình

Trước khi bắt đầu huấn luyện, chúng ta sẽ giải phóng RAM và VRAM GPU để tránh lỗi hết bộ nhớ (OOM). Việc này đảm bảo quá trình huấn luyện diễn ra trơn tru.

import gc, torch
gc.collect()
torch.cuda.empty_cache()
model.config.use_cache = False
trainer.train()

Trong quá trình huấn luyện, tiến trình sử dụng khoảng 54% bộ nhớ và đạt 99% mức sử dụng GPU, đảm bảo khai thác tài nguyên hiệu quả.

Tổng quan mức sử dụng bộ nhớ GPU của pod

Bạn có thể quan sát loss giảm dần theo từng bước, cho thấy mô hình đang học và fine-tune hiệu quả. 

Loss huấn luyện

7. Suy Luận Mô Hình Sau Khi Fine-Tune

Giờ chúng ta sẽ kiểm thử mô hình đã fine-tune bằng cách so sánh phản hồi với bộ dữ liệu. Dùng lại câu hỏi ở phần trước để sinh phản hồi:

question = dataset[20]['Open-ended Verifiable Question']
inputs = tokenizer(
    [inference_prompt_style.format(question, "") + tokenizer.eos_token],
    return_tensors="pt"
).to("cuda")

outputs = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    max_new_tokens=1200,
    eos_token_id=tokenizer.eos_token_id,
    use_cache=True,
)
response = tokenizer.batch_decode(outputs)
print(response[0].split("<|im_start|>assistant<|im_sep|>")[1])

Phần lập luận chi tiết và chính xác, đáp án sát với bộ dữ liệu. 

<think><|im_end|>First, I need to figure out what the employee contributions were for the years 2002 and 2003. Okay, looking at the numbers, I see that the company contributed $979,000 in 2002 and $825,000 in 2003. So, those are my two key figures. 

Next, I'll calculate the change in the contribution amount. To find this, I subtract the 2003 contribution from the 2002 contribution. Let me do that: $979,000 minus $825,000 equals $154,000. So, there's a decrease of $154,000 from 2002 to 2003.

Now, I need to understand what this decrease means in terms of percentage. Percentage change is usually calculated by taking the change, dividing it by the original number, and then multiplying by 100. So, I'll take that $154,000 change and divide it by the 2002 amount, $979,000. 

Let me do that math: $154,000 divided by $979,000... Hmm, this gives me a value of approximately 0.1573. 

Finally, I multiply that by 100 to convert it to a percentage. That gives me about 15.73%. 

Oh, wait, let me make sure everything makes sense. I calculated the change in contributions correctly by subtracting the 2003 amount from the 2002 amount. Then I used the original amount from 2002 as the base for the percentage calculation. 

Yes, that all seems right. So, the employee contribution decreased by approximately 15.73% from 2002 to 2003.</think>The percentage decrease in the employee contribution from 2002 to 2003 was approximately 15.73%.<|im_end|>

Hãy thử một ví dụ khác:

question = dataset[200]['Open-ended Verifiable Question']
inputs = tokenizer(
    [inference_prompt_style.format(question, "") + tokenizer.eos_token],
    return_tensors="pt"
).to("cuda")

outputs = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    max_new_tokens=1200,
    eos_token_id=tokenizer.eos_token_id,
    use_cache=True,
)
response = tokenizer.batch_decode(outputs)
print(response[0].split("<|im_start|>assistant<|im_sep|>")[1])

Kết quả rất ấn tượng, mô hình sinh lập luận và đáp án chính xác theo phong cách của bộ dữ liệu:

<think><|im_end|>Okay, let's figure out how much of the long-term debt should be considered current liabilities. So, the total long-term debt at the end of 2011 was $1859 million. Now, I remember that there's some information about the timing of cash payments related to this debt. In particular, there's a chunk of $29 million that is due in 2012. Hmm, that's the part that's likely to become a current liability. 

Let’s check the table again to make sure. It says total long-term debt is $1859 million, and the breakdown of estimated cash payments includes $29 million for 2012. Yep, that seems to align. So, this $29 million should be classified as a current liability because it's due within the next year. 

But wait, let me double-check. The table is pretty straightforward: long-term debt is $1859 million and the breakdown clearly shows $29 million due in 2012. So, that $29 million is the part that should be on the current liabilities section of the balance sheet. 

Alright, so I'm confident that the portion of the long-term debt that's included in current liabilities as of December 31, 2011, is $29 million. That's it!</think>The portion of the long-term debt included in the current liabilities on the balance sheet as of December 31, 2011, is $29 million. This $29 million represents the amount of debt due within the next year, making it a current liability.<|im_end|>

8. Lưu Mô Hình

Để mô hình đã fine-tune có thể được sử dụng sau này và cho người khác tải về, chúng ta sẽ lưu adapter của mô hình và tokenizer rồi đẩy lên Hugging Face Hub:

new_model_name = "Phi-4-Reasoning-Plus-FinQA-COT"
model.push_to_hub(new_model_name)
tokenizer.push_to_hub(new_model_name)

Mô hình hiện đã công khai tại: kingabzpro/Phi-4-Reasoning-Plus-FinQA-COT. 

Adapter mô hình đã fine-tune được đẩy lên Hugging Face hub

Nguồn: kingabzpro/Phi-4-Reasoning-Plus-FinQA-COT · Hugging Face

Nếu bạn gặp sự cố khi chạy script fine-tuning, hãy tham khảo notebook đi kèm: fine-tuning-phi-4-reasoning.ipynb.

Kết luận

Mô hình Phi-4 Reasoning là một bước tiến đáng kể cho cộng đồng mã nguồn mở. Thay vì phụ thuộc vào các mô hình khổng lồ 685B tham số, Phi-4 Reasoning đạt kết quả tốt hơn với kiến trúc nhỏ gọn, hiệu quả. Điều này cho thấy việc tập trung vào bộ dữ liệu chất lượng cao và học tăng cường có thể mang lại hiệu năng vượt trội ngay cả với các mô hình nhỏ hơn.

Một trong những ưu điểm lớn nhất khi fine-tune Phi-4 Reasoning là chi phí và hiệu quả. Fine-tune mô hình này trên 20K mẫu trở lên sẽ tốn chưa đến $10 và chỉ mất vài giờ, nhờ kích thước nhỏ và kiến trúc tối ưu.

Các mô hình nhỏ như Phi-4 Reasoning không chỉ giảm chi phí fine-tuning mà còn tiết kiệm chi phí hạ tầng đám mây cho suy luận mô hình. Đây là lựa chọn tuyệt vời cho các dự án cần hiệu năng cao mà không phải gánh chi phí của các mô hình khổng lồ.

Để biết thêm thông tin, vui lòng tham khảo các hướng dẫn fine-tuning nhiều mô hình ngôn ngữ lớn khác của chúng tôi:

  1. Fine-Tuning Llama 4: Hướng Dẫn Kèm Dự Án Demo
  2. Fine-Tuning Qwen3: Hướng Dẫn Từng Bước
  3. Fine-Tune Gemma 3: Hướng Dẫn Từng Bước Với Bộ Dữ Liệu Hỏi & Đáp Tài Chính 
  4. Fine-Tuning DeepSeek R1 (Mô Hình Lập Luận)
Abid Ali Awan

Tác giả

Abid Ali Awan

Là một nhà khoa học dữ liệu được chứng nhận, tôi đam mê tận dụng công nghệ tiên tiến để tạo ra các ứng dụng học máy đổi mới. Với nền tảng vững chắc về nhận dạng giọng nói, phân tích và báo cáo dữ liệu, MLOps, AI hội thoại và NLP, tôi đã rèn giũa kỹ năng phát triển các hệ thống thông minh có thể tạo ra tác động thực sự. Bên cạnh chuyên môn kỹ thuật, tôi cũng là một người truyền đạt tốt, có khả năng chắt lọc các khái niệm phức tạp thành ngôn ngữ rõ ràng, súc tích. Nhờ đó, tôi trở thành một blogger được nhiều người quan tâm trong lĩnh vực khoa học dữ liệu, chia sẻ góc nhìn và kinh nghiệm với cộng đồng các chuyên gia dữ liệu ngày càng lớn. Hiện tại, tôi tập trung vào sáng tạo và biên tập nội dung, làm việc với các mô hình ngôn ngữ lớn để phát triển nội dung mạnh mẽ và hấp dẫn, giúp doanh nghiệp và cá nhân tận dụng tối đa dữ liệu của mình.

Chủ đề

Liên quan