Cách Chuyển Đổi Mô Hình LLM Sang Định Dạng GGUF với llama.cpp Quantization

AI tutorial - IT technology blog
AI tutorial - IT technology blog

Vấn Đề: Mô Hình LLM Quá Lớn Để Chạy Cục Bộ

Bạn tìm thấy một mô hình open-source hay trên Hugging Face — có thể là Mistral 7B, LLaMA 3, hoặc Qwen2. Bạn tải về, thử load lên, rồi máy hoặc đứng hình hoặc báo lỗi out-of-memory. Một mô hình 7B tham số ở định dạng float16 chiếm khoảng 14 GB VRAM. Mô hình 13B? Hơn 26 GB. Hầu hết developer đơn giản là không có phần cứng đó.

Nguyên nhân gốc rễ nằm ở độ chính xác. Trọng số mô hình mặc định được lưu dưới dạng float 16-bit hoặc 32-bit. Tức là 2–4 byte mỗi tham số — độ chính xác cao hơn nhiều so với những gì inference thực sự cần. Giải pháp là quantization: giảm số bit của mỗi trọng số để thu nhỏ mô hình mà không làm mất đi độ chính xác đáng kể.

Học được pipeline này đã thay đổi cách tôi làm việc với AI cục bộ. Với một mô hình được quantize đúng cách, Mistral 7B chạy ở tốc độ 10–20 token mỗi giây trên CPU laptop hiện đại — không cần GPU, không cần datacenter, không tốn tiền cloud.

Các Khái Niệm Cơ Bản Cần Nắm

Quantization Là Gì?

Quantization ánh xạ các giá trị float độ chính xác cao sang số nguyên độ chính xác thấp hơn. Thay vì 16 bit mỗi trọng số, bạn dùng 4 hoặc 8 bit. Mô hình mất đi một chút chi tiết số học, nhưng trong thực tế chất lượng đầu ra giảm rất ít — đặc biệt ở mức Q4 trở lên.

Dưới đây là so sánh giữa các mức quantization phổ biến:

  • Q2_K — ~2 bit/trọng số. File nhỏ nhất, chất lượng giảm rõ rệt. Chỉ dùng khi bộ nhớ cực kỳ hạn chế.
  • Q4_K_M — ~4 bit/trọng số. Điểm cân bằng lý tưởng. Inference nhanh, chất lượng gần với độ chính xác đầy đủ.
  • Q5_K_M — ~5 bit/trọng số. Chất lượng tốt hơn, file hơi lớn hơn. Đáng dùng khi bạn có đủ RAM.
  • Q8_0 — ~8 bit/trọng số. Gần như không mất mát. Chất lượng tốt nhất trong các định dạng quantized, nhưng lớn nhất và chậm nhất để tạo ra.

Cụ thể: Q4_K_M giúp mô hình 7B xuống còn khoảng 4.1 GB. Vừa vặn trong 8 GB RAM hệ thống với dư địa thoải mái — không cần GPU.

GGUF Là Gì?

GGUF (GPT-Generated Unified Format) là định dạng file được dùng bởi llama.cpp. Nó thay thế định dạng GGML cũ và đóng gói tất cả những gì runtime cần — trọng số, tokenizer, hyperparameter — vào một file nhị phân duy nhất. Một file duy nhất. Không cần config riêng lẻ nào để theo dõi.

GGUF là những gì bạn thấy trên Hugging Face với tên như model-Q4_K_M.gguf. Nhiều mô hình phổ biến đã được convert sẵn, nhưng khi có bản phát hành mới mà cộng đồng chưa đóng gói kịp, bạn tự convert. Đó chính xác là nội dung hướng dẫn này.

llama.cpp Có Vai Trò Gì?

llama.cpp là inference engine viết bằng C++ được tối ưu để chạy mô hình GGUF trên CPU và GPU. Pipeline chuyển đổi dùng hai công cụ. convert_hf_to_gguf.py chuyển đổi thư mục mô hình Hugging Face sang định dạng GGUF. Sau đó llama-quantize nén nó xuống số bit bạn chọn. Bạn chạy tuần tự: convert trước, quantize sau.

Thực Hành: Chuyển Đổi và Quantize Mô Hình

Bước 1: Cài Đặt Dependencies

Bạn cần Python 3.10+, Git, và một trình biên dịch C++. Trên Ubuntu/Debian:

sudo apt update && sudo apt install -y git build-essential cmake
pip install torch transformers sentencepiece huggingface_hub

Bước 2: Clone llama.cpp và Build Các Công Cụ

git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make -j$(nproc)

Lệnh này biên dịch llama-quantize, llama-cli, llama-server và các tiện ích khác. Nếu bạn có CUDA và muốn tăng tốc bằng GPU khi inference:

make LLAMA_CUDA=1 -j$(nproc)

Bước 3: Cài Đặt Python Requirements Cho Script Chuyển Đổi

pip install -r requirements.txt

Bước 4: Tải Mô Hình Gốc Từ Hugging Face

Dùng huggingface-cli để tải mô hình. Ở đây ta dùng mistralai/Mistral-7B-v0.3:

huggingface-cli login  # bắt buộc nếu mô hình bị giới hạn truy cập
huggingface-cli download mistralai/Mistral-7B-v0.3 \
  --local-dir ./models/Mistral-7B-v0.3 \
  --local-dir-use-symlinks False

Lệnh này tải toàn bộ các shard .safetensors, tokenizer và các file config. Mistral 7B ở giai đoạn này nặng khoảng 14 GB.

Bước 5: Chuyển Đổi Sang GGUF (Float16)

Script chuyển đổi tạo ra một file GGUF độ chính xác đầy đủ như bước trung gian:

python convert_hf_to_gguf.py ./models/Mistral-7B-v0.3 \
  --outfile ./models/Mistral-7B-v0.3-F16.gguf \
  --outtype f16

Tùy tốc độ đĩa, quá trình này mất 2–5 phút. Đầu ra nặng khoảng 14 GB — cùng độ chính xác với bản gốc, được đóng gói lại thành GGUF.

Bước 6: Quantize Sang Định Dạng Mục Tiêu

Giờ chạy llama-quantize. Q4_K_M hầu như luôn là điểm khởi đầu đúng đắn:

./llama-quantize \
  ./models/Mistral-7B-v0.3-F16.gguf \
  ./models/Mistral-7B-Q4_K_M.gguf \
  Q4_K_M

Quá trình này mất 2–4 phút và cho ra file 4.1 GB. Xóa file trung gian F16 sau đó để lấy lại ~10 GB dung lượng đĩa.

Muốn tạo nhiều mức quantization để so sánh?

for QTYPE in Q2_K Q4_K_M Q5_K_M Q8_0; do
  ./llama-quantize \
    ./models/Mistral-7B-v0.3-F16.gguf \
    ./models/Mistral-7B-${QTYPE}.gguf \
    $QTYPE
done

Bước 7: Chạy Inference Để Kiểm Tra

Kiểm tra mô hình đã quantize bằng CLI tích hợp của llama.cpp:

./llama-cli \
  -m ./models/Mistral-7B-Q4_K_M.gguf \
  -p "Giải thích Docker volumes theo cách đơn giản nhất." \
  -n 300 \
  --temp 0.7

Đầu ra mạch lạc nghĩa là quá trình chuyển đổi thành công. Token vô nghĩa thường có nghĩa là tokenizer không được đóng gói đúng — chạy lại bước chuyển đổi và kiểm tra các cảnh báo về file tokenizer bị thiếu.

Kiểm Tra Perplexity (Tùy Chọn nhưng Nên Làm)

Perplexity đo khả năng dự đoán văn bản của mô hình. Giá trị càng thấp càng tốt. Chạy lệnh này để đo chính xác mức độ chất lượng bạn đánh đổi lấy dung lượng file:

# Tải tập dữ liệu kiểm tra
wget https://huggingface.co/datasets/ggml-org/ci/resolve/main/wikitext-2-raw-v1.zip
unzip wikitext-2-raw-v1.zip

# Chạy đánh giá perplexity
./llama-perplexity \
  -m ./models/Mistral-7B-Q4_K_M.gguf \
  -f wikitext-2-raw/wiki.test.raw

Q4_K_M thường chỉ tăng thêm chưa đến 0.5 điểm perplexity so với F16 trên hầu hết mô hình 7B. Đó là mức giảm độ chính xác không đáng kể cho mức tiết kiệm 70% dung lượng file.

Load Vào Ollama hoặc Open WebUI

Muốn dùng giao diện chat thay vì CLI thuần? Load GGUF trực tiếp vào Ollama:

# Tạo Modelfile
cat > Modelfile <<EOF
FROM ./models/Mistral-7B-Q4_K_M.gguf
PARAMETER temperature 0.7
PARAMETER num_ctx 4096
EOF

ollama create mistral-local -f Modelfile
ollama run mistral-local

Nếu muốn giao diện web đầy đủ tính năng hơn, bạn có thể kết hợp với Open WebUI chạy qua Docker để có trải nghiệm giống ChatGPT hoàn toàn cục bộ.

Chọn Mức Quantization Phù Hợp

Định dạng phù hợp phụ thuộc vào phần cứng của bạn. Dưới đây là sơ đồ quyết định thực tế:

  • Chỉ có 4–6 GB RAM: Q2_K hoặc Q3_K_M. Chuẩn bị cho chất lượng giảm rõ rệt với các tác vụ suy luận phức tạp.
  • 8 GB RAM: Q4_K_M cho mô hình 7B. Đây là lựa chọn mặc định cho hầu hết cấu hình.
  • 16 GB RAM: Q5_K_M cho 7B, hoặc Q4_K_M cho 13B.
  • 32 GB+ RAM: Q8_0 cho 7B/13B, hoặc chạy mô hình 30B+ ở mức Q4.

Bước Tiếp Theo

Với mô hình đã quantize trong tay, bạn có nhiều hướng đi tự nhiên. Chạy llama-server để có REST API tương thích OpenAI — bất kỳ công cụ nào giao tiếp với OpenAI (LangChain, Open WebUI, LlamaIndex) đều hoạt động ngay lập tức mà không cần thay đổi code. Hoặc xây dựng pipeline RAG trên mô hình cục bộ của bạn. Hoặc fine-tune mô hình gốc trước, rồi convert các trọng số đã fine-tune.

Dự án llama.cpp cập nhật gần như hàng ngày và thêm hỗ trợ cho các kiến trúc mới trong vòng vài ngày kể từ khi mô hình ra mắt. LLaMA, Mistral, Qwen, Phi, Gemma — nếu là mô hình open-weight, gần như chắc chắn đã có hỗ trợ GGUF.

Hãy bắt đầu với Q4_K_M. Chạy kiểm tra perplexity nhanh. Sau đó quyết định xem có nên nâng lên Q5 để chất lượng tốt hơn hay hạ xuống Q3 để tiết kiệm bộ nhớ. Bước quantization chỉ mất vài phút — việc thử nghiệm rất nhanh, và định dạng phù hợp hoàn toàn đáng để tìm kiếm.

Share: