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

Tinh chỉnh GPT-OSS

Hướng dẫn từng bước về tinh chỉnh mô hình OpenAI GPT-OSS 20B trên bộ dữ liệu hỏi đáp y khoa nhằm đạt cả độ chính xác và phong cách trả lời.
· 6 phút đọc

Khám phá cùng AI

Mô hình GPT-OSS 20B là một mô hình ngôn ngữ có trọng số mở thuộc dòng gpt-oss của OpenAI, được thiết kế cho độ trễ thấp và các trường hợp sử dụng chuyên biệt. Mô hình có 21 tỷ tham số (3,6B hoạt động) và được tối ưu để chạy hiệu quả trên phần cứng phổ thông, chỉ cần khoảng 16GB bộ nhớ nhờ lượng tử hóa MXFP4.

Dù nhỏ hơn GPT-OSS 120B, mô hình này vẫn hỗ trợ các tính năng nâng cao như mức độ suy luận có thể cấu hình (thấp, trung bình, cao), truy cập đầy đủ chuỗi suy nghĩ để gỡ lỗi và minh bạch, cùng năng lực tác tử như gọi hàm, duyệt web và thực thi mã.

Trong hướng dẫn này, tôi sẽ dẫn bạn qua toàn bộ quy trình thiết lập và tinh chỉnh GPT-OSS 20B. Bao gồm:

  1. Cài đặt phụ thuộc và chuẩn bị môi trường
  2. Nạp mô hình và tokenizer
  3. Tạo phong cách nhắc (prompt) bằng gói OpenAI Harmony cho Python
  4. Nạp bộ dữ liệu và áp dụng định dạng Harmony
  5. Chạy suy luận đơn giản trước khi tinh chỉnh
  6. Cấu hình mô hình cho huấn luyện
  7. Tinh chỉnh mô hình trên bộ dữ liệu Hỏi & Đáp y khoa
  8. Thực hiện suy luận sau tinh chỉnh để đánh giá cải thiện

Lưu ý: hướng dẫn này phục vụ nghiên cứu và giáo dục, không dùng cho chẩn đoán hay điều trị bệnh nhân.

Hình ảnh do Tác giả cung cấp

1. Thiết lập môi trường của bạn

Mặc dù mô hình GPT-OSS có 21 tỷ tham số, nó vẫn cần bộ nhớ GPU đáng kể, tối thiểu 80GB VRAM để vận hành thoải mái. Cách nhanh để bắt đầu là khởi chạy một phiên bản RunPod H100 hoặc môi trường GPU bộ nhớ lớn tương tự.

RunPod H100SXM machine

Khi môi trường sẵn sàng, hãy cài đặt tất cả gói Python cần thiết cho xử lý dữ liệu, tinh chỉnh mô hình và theo dõi thí nghiệm:

%%capture
%pip install -U accelerate 
%pip install -U peft 
%pip install -U trl 
%pip install -U bitsandbytes
%pip install -U transformers
%pip install -U trackio
%pip install -U openai-harmony
%pip install -U tiktoken
%pip install -U pyctcdecode

Cuối cùng, đăng nhập vào Hugging Face Hub bằng API key của bạn. Việc này cho phép lưu và chia sẻ trực tiếp mô hình cùng tokenizer đã tinh chỉnh lên tài khoản Hugging Face của bạn:

from huggingface_hub import notebook_login

notebook_login()

2. Nạp mô hình và Tokenizer GPT-OSS

Tiếp theo, chúng ta nạp GPT-OSS 20B với lượng tử hóa MXFP4 nhưng ngay lập tức giải lượng tử thành bfloat16 khi nạp, dùng cơ chế attention eager, tắt cache và tự động gán thiết bị.

import torch
from transformers import AutoModelForCausalLM, Mxfp4Config

quantization_config = Mxfp4Config(dequantize=True)
model_kwargs = dict(
    attn_implementation="eager",
    torch_dtype=torch.bfloat16,
    quantization_config=quantization_config,
    use_cache=False,
    device_map="auto",
)

model = AutoModelForCausalLM.from_pretrained("openai/gpt-oss-20b", **model_kwargs)

Bên cạnh mô hình, ta cũng cần tokenizer từ cùng kho lưu trữ. Tokenizer chịu trách nhiệm chuyển đổi văn bản thành token (ID) để mô hình xử lý, rồi giải mã đầu ra của mô hình trở lại văn bản có thể đọc được.

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("openai/gpt-oss-20b")

3. Tạo phong cách Prompt bằng OpenAI Harmony

OpenAI Harmony là định dạng phản hồi chuyên biệt được các mô hình GPT-OSS dùng để cấu trúc hội thoại, đầu ra suy luận và lời gọi hàm một cách nhất quán. 

Nó xác định cách sắp xếp thông điệp giữa các vai (system, developer, user, assistant) và cho phép mô hình xuất ra nhiều kênh — ví dụ, tách chuỗi suy nghĩ, phần mở đầu khi gọi công cụ và phản hồi cuối cùng hướng tới người dùng.

Trong phần này, chúng ta sẽ tạo một hàm nhận câu hỏi và câu trả lời rồi định dạng chúng thành một prompt hội thoại kiểu Harmony. Hàm sử dụng thư viện openai_harmony để mã hóa hội thoại theo cấu trúc.

from openai_harmony import (
    load_harmony_encoding, HarmonyEncodingName,
    Conversation, Message, Role,
    SystemContent, DeveloperContent, ReasoningEffort
)

enc = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS)

def render_pair_harmony(question, answer):
    convo = Conversation.from_messages([
        Message.from_role_and_content(
            Role.DEVELOPER,
            DeveloperContent.new().with_instructions(
                "You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. "
                "Respond with ONLY the final diagnosis/cause in ≤5 words."
            )
        ),
        Message.from_role_and_content(Role.USER, question.strip()),
        Message.from_role_and_content(Role.ASSISTANT, answer.strip()),
    ])
    tokens = enc.render_conversation(convo)
    text = enc.decode(tokens)
    return text

4. Nạp và định dạng bộ dữ liệu

Bây giờ, chúng ta sẽ nạp bộ dữ liệu H&Đ y khoa (FreedomIntelligence/medical-o1-verifiable-problem) và áp dụng hàm prompt_style_harmony cho từng ví dụ. Văn bản đã chuyển đổi được lưu ở cột mới tên "text", mang lại định dạng hướng dẫn rõ ràng, phù hợp với phong cách nhắc của developer.

from datasets import load_dataset

def prompt_style_harmony(examples):
    qs = examples["Open-ended Verifiable Question"]
    ans = examples["Ground-True Answer"]
    outputs = {"text": []}
    for q, a in zip(qs, ans):
        rendered = render_pair_harmony(q, a)
        outputs["text"].append(rendered)
    return outputs

dataset = load_dataset(
    "FreedomIntelligence/medical-o1-verifiable-problem",
    split="train[0:1000]"
)
dataset = dataset.map(prompt_style_harmony, batched=True)
print(dataset.column_names)

print(dataset[0]["text"])

Bộ dữ liệu giờ có cột mới "text", chứa hội thoại theo định dạng Harmony. Bao gồm:

  • Hướng dẫn của developer (ví dụ: trả lời ngắn gọn bằng chẩn đoán)
  • Thông điệp của người dùng (câu hỏi y khoa)
  • Thông điệp của trợ lý (câu trả lời đúng chuẩn)
['Open-ended Verifiable Question', 'Ground-True Answer', 'text']
<|start|>developer<|message|># Instructions

You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. Respond with ONLY the final diagnosis/cause in ≤5 words.<|end|><|start|>user<|message|>An 88-year-old woman with osteoarthritis is experiencing mild epigastric discomfort and has vomited material resembling coffee grounds multiple times. Considering her use of naproxen, what is the most likely cause of her gastrointestinal blood loss?<|end|><|start|>assistant<|message|>Gastric ulcer<|end|>

5. Suy luận mô hình trước khi tinh chỉnh

Trước khi huấn luyện, điều quan trọng là đánh giá hiệu năng gốc để so sánh sau tinh chỉnh. Để làm việc này, ta định nghĩa hàm trợ giúp render_inference_harmony tạo một prompt Harmony chứa hướng dẫn nghiêm ngặt của developer và câu hỏi của người dùng, nhưng để trống phản hồi của trợ lý để mô hình hoàn thành.

def render_inference_harmony(question):
    convo = Conversation.from_messages([
        Message.from_role_and_content(
            Role.DEVELOPER,
            DeveloperContent.new().with_instructions(
                "You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. "
                "Respond with ONLY the final diagnosis/cause in ≤5 words."
            )
        ),
        Message.from_role_and_content(Role.USER, question.strip()),
    ])
    tokens = enc.render_conversation_for_completion(convo, Role.ASSISTANT)
    text = enc.decode(tokens)
    return text

Tiếp theo, ta lấy mẫu đầu tiên từ bộ dữ liệu, định dạng bằng hàm này, tokenize và đưa qua mô hình để tạo phản hồi:

question = dataset[0]["Open-ended Verifiable Question"]

text = render_inference_harmony(question)

inputs = tokenizer(
    [text + tokenizer.eos_token], return_tensors="pt"
).to("cuda")
outputs = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    max_new_tokens=20,
    eos_token_id=tokenizer.eos_token_id,
    use_cache=True,
)
response = tokenizer.batch_decode(outputs)
print(response[0])

Trong bài kiểm tra này, đầu ra của mô hình dài và mô tả nhiều hơn yêu cầu, dù hướng dẫn của developer đã nêu rõ cần câu chẩn đoán ngắn gọn (≤5 từ). 

<|start|>developer<|message|># Instructions

You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. Respond with ONLY the final diagnosis/cause in ≤5 words.<|end|><|start|>user<|message|>An 88-year-old woman with osteoarthritis is experiencing mild epigastric discomfort and has vomited material resembling coffee grounds multiple times. Considering her use of naproxen, what is the most likely cause of her gastrointestinal blood loss?<|end|><|start|>assistant<|return|>We have an old woman with NSAID use causing ulcer bleed. The question: "An 88

Thay vì chỉ trả lời “Gastric ulcer” (nhãn đúng), mô hình lại đưa ra phần giải thích dài dòng về NSAID và chảy máu do loét.

dataset[0]["Ground-True Answer"]
'Gastric ulcer'

6. Thiết lập mô hình cho huấn luyện

Để tinh chỉnh hiệu quả, chúng ta sử dụng LoRA (Low-Rank Adaptation), cho phép chỉ huấn luyện một phần nhỏ tham số trong khi giữ nguyên mô hình gốc. Điều này giúp giảm mạnh bộ nhớ và chi phí huấn luyện.

Chúng ta bắt đầu bằng cách định nghĩa LoraConfig nêu rõ hạng, hệ số tỉ lệ và các lớp đích nơi sẽ chèn adapter LoRA.

from peft import LoraConfig, get_peft_model

peft_config = LoraConfig(
    r=8,
    lora_alpha=16,
    target_modules="all-linear",
    target_parameters=[
        "7.mlp.experts.gate_up_proj",
        "7.mlp.experts.down_proj",
        "15.mlp.experts.gate_up_proj",
        "15.mlp.experts.down_proj",
        "23.mlp.experts.gate_up_proj",
        "23.mlp.experts.down_proj",
    ],
)
peft_model = get_peft_model(model, peft_config)
peft_model.print_trainable_parameters()

Chỉ có khoảng ~15M tham số (≈0,07% tổng mô hình) có thể huấn luyện, và kích thước adapter khoảng 16 MB — mức hiệu quả lớn so với tinh chỉnh toàn phần.

trainable params: 15,040,512 || all params: 20,929,797,696 || trainable%: 0.0719

Tiếp theo, chúng ta cấu hình thiết lập huấn luyện bằng SFTConfig từ thư viện trl. Bao gồm các siêu tham số như tốc độ học, kích thước lô, tích lũy gradient, tỉ lệ warmup và loại scheduler:

from trl import SFTConfig

training_args = SFTConfig(
    learning_rate=2e-4,
    gradient_checkpointing=True,
    num_train_epochs=1,
    logging_steps=10,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    max_length=2048,
    warmup_ratio=0.03,
    lr_scheduler_type="cosine_with_min_lr",
    lr_scheduler_kwargs={"min_lr_rate": 0.1},
    output_dir="gpt-oss-20b-medical-qa",
    report_to="trackio",
    push_to_hub=True,
)

7. Huấn luyện mô hình

Với bộ dữ liệu đã chuẩn bị, adapter LoRA gắn vào và tham số huấn luyện đã cấu hình, ta có thể bắt đầu quá trình tinh chỉnh. Chúng ta dùng SFTTrainer từ thư viện trl, giúp đơn giản hóa supervised fine-tuning bằng cách xử lý mô hình, tokenizer, bộ dữ liệu và vòng lặp huấn luyện tại một nơi:

from trl import SFTTrainer

trainer = SFTTrainer(
    model=peft_model,
    args=training_args,
    train_dataset=dataset,
    processing_class=tokenizer,
)
trainer.train()

Trong quá trình huấn luyện, loss giảm dần, cho thấy mô hình học cách căn chỉnh phản hồi với bộ dữ liệu H&Đ y khoa theo định dạng Harmony.

Model training loss

Khi huấn luyện hoàn tất, ta lưu mô hình đã tinh chỉnh cục bộ rồi đẩy lên Hugging Face Hub. Lưu ý, chỉ trọng số adapter LoRA (không phải toàn bộ mô hình 20B) được tải lên, giúp kho nhẹ và dễ chia sẻ:

trainer.save_model(training_args.output_dir)
trainer.push_to_hub(dataset_name="kingabzpro/gpt-oss-20b-medical-qa")

Việc này tạo một kho mô hình mới trên Hugging Face dưới: kingabzpro/gpt-oss-20b-medical-qa.

Nguồn: kingabzpro/gpt-oss-20b-medical-qa

8. Suy luận mô hình sau tinh chỉnh

Sau tinh chỉnh, ta có thể kiểm tra xem mô hình hiện tuân thủ hướng dẫn của developer sát hơn chưa. Để làm điều đó, trước hết nạp mô hình GPT-OSS 20B gốc rồi gắn các adapter LoRA đã tinh chỉnh.

from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

# Load the tokenizer
tokenizer = AutoTokenizer.from_pretrained("openai/gpt-oss-20b")

# Load the original model first
model_kwargs = dict(attn_implementation="eager", torch_dtype="auto", use_cache=True, device_map="auto")
base_model = AutoModelForCausalLM.from_pretrained("openai/gpt-oss-20b", **model_kwargs).cuda()

# Merge fine-tuned weights with the base model
peft_model_id = "gpt-oss-20b-medical-qa"
model = PeftModel.from_pretrained(base_model, peft_model_id)
model = model.merge_and_unload()

Tiếp theo, ta chạy suy luận trên một mẫu từ bộ dữ liệu. Câu hỏi được định dạng theo Harmony, tokenize, đưa qua mô hình và giải mã trở lại văn bản:

question = dataset[0]["Open-ended Verifiable Question"]

text = render_inference_harmony(question)

inputs = tokenizer(
    [text + tokenizer.eos_token], return_tensors="pt"
).to("cuda")
outputs = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    max_new_tokens=20,
    eos_token_id=tokenizer.eos_token_id,
    use_cache=True,
)
response = tokenizer.batch_decode(outputs)
print(response[0])

Mô hình đã tinh chỉnh giờ tạo ra câu trả lời ngắn và chính xác. Điều này sát với nhãn đúng (“Gastric ulcer”) và tuân thủ ràng buộc ≤5 từ.

<|start|>developer<|message|># Instructions

You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. Respond with ONLY the final diagnosis/cause in ≤5 words.<|end|><|start|>user<|message|>An 88-year-old woman with osteoarthritis is experiencing mild epigastric discomfort and has vomited material resembling coffee grounds multiple times. Considering her use of naproxen, what is the most likely cause of her gastrointestinal blood loss?<|end|><|start|>assistant<|return|><|message|>Stomach ulcer<|end|><|return|>

Ta cũng có thể thử mẫu khác để xác nhận tính nhất quán:

question = dataset[10]["Open-ended Verifiable Question"]

text = render_inference_harmony(question)

inputs = tokenizer(
    [text + tokenizer.eos_token], return_tensors="pt"
).to("cuda")
outputs = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    max_new_tokens=20,
    eos_token_id=tokenizer.eos_token_id,
    use_cache=True,
)
response = tokenizer.batch_decode(outputs)
print(response[0])

Hoàn hảo. Mô hình đã tinh chỉnh tạo ra phản hồi ngắn gọn, chính xác và tuân thủ hướng dẫn.

<|start|>developer<|message|># Instructions

You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. Respond with ONLY the final diagnosis/cause in ≤5 words.<|end|><|start|>user<|message|>What substance is primarily filtered in the renal tubules with minimal secretion or re-absorption?<|end|><|start|>assistant<|return|><|message|>creatinine<|end|><|return|>

Kết luận

Đây là giai đoạn đầy hứng khởi cho cộng đồng AI nguồn mở. Dù các mô hình GPT-OSS mới có thể chưa đứng đầu bảng xếp hạng, chúng cho thấy nhiều hứa hẹn, nhất là trong phân hạng tham số tương ứng, nơi các mô hình nhỏ hơn vẫn có thể đạt kết quả ấn tượng.

Trong hướng dẫn này, chúng ta đã tinh chỉnh mô hình GPT-OSS 20B cho tác vụ hỏi đáp y khoa bằng adapter LoRA và phong cách prompt Harmony. Kết quả đáng khích lệ: mô hình tinh chỉnh đưa ra câu trả lời ngắn gọn, chính xác, phù hợp với các nhiệm vụ suy luận lâm sàng. Với nhiều epoch huấn luyện hơn, bộ dữ liệu lớn hơn và prompt hệ thống tinh chỉnh hơn, hiệu năng còn có thể cải thiện thêm.

Nếu bạn muốn một khóa học thực hành để làm quen với tinh chỉnh, đừng bỏ lỡ Fine-Tuning With Llama 3.

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