Welcome to MikeDaDev's Blog – notes on Java, backend & system design.

Explore
MikeDaDev's Blog
in ai

RAG vs Fine-tuning

Tung Bui Thanh (TungDaDev)21 mins read

Rag vs finetuning decision.webp

Parametric Memory (trọng số mô hình) là phản xạ và văn phong; Non-Parametric Memory (chỉ mục dữ liệu bên ngoài) là kiến thức sự thật. Bắt mô hình học thuộc dữ liệu hay thay đổi vào trọng số thì cũng như mỗi lần muốn thêm một dòng vào database lại đi sửa firmware ổ cứng.

Có một câu hỏi mà Technical Architect, Solution Architect hay CTO nào cũng từng bị ban điều hành hỏi, và lần nào hỏi cũng làm nội bộ cãi nhau:

Doanh nghiệp chúng ta có hàng triệu tài liệu hợp đồng, chính sách, tài liệu kỹ thuật và mã nguồn nội bộ. Bây giờ nên thuê GPU để Fine-tune một model riêng (Private LLM) hay xây dựng hệ thống RAG (Retrieval-Augmented Generation)?

Khá nhiều team còn non kinh nghiệm đã đổ cả trăm ngàn USD vào một ý tưởng sai: tin rằng Fine-tuning là cách "nạp kiến thức mới" (Knowledge Injection) vào mô hình.

Họ gom hàng chục ngàn file PDF, văn bản quy trình, thuê cụm GPU H100/A100 cả mấy tuần, tốn hàng chục ngàn USD để train lại Llama-3 hay Qwen. Kết quả thì thảm:

  • Hỏi "Hạn mức tín dụng tối đa cho khách hàng VIP theo chính sách cập nhật tháng này là bao nhiêu?", mô hình tự tin phán ra một con số sai bét (hallucination).
  • Mô hình không chỉ ra được con số đó lấy từ văn bản số mấy, ai duyệt, ban hành ngày nào.
  • Tuần sau ban giám đốc đổi quy trình kinh doanh, thế là toàn bộ kiến thức vừa "nhồi" vào mô hình thành đồ cũ. Team lại phải chuẩn bị dữ liệu train lại từ đầu.

Sao lại ra nông nỗi đó? Về mặt toán học và cách vận hành, hai kỹ thuật này khác nhau ở đâu? Lúc nào chọn RAG, lúc nào chọn Fine-tuning, và ghép cả hai lại thế nào cho chắc tay?

Mình nhìn chuyện này từ phía Software Engineer & Solution Architect: hai kỹ thuật chạy bên dưới thế nào, ma trận quyết định 7 chiều, công thức tính điểm hoà vốn TCO (Total Cost of Ownership), và một kiến trúc kết hợp để chạy AI ở quy mô production.


# gốc rễ bài toán: parametric memory vs non-parametric memory

Muốn quyết cho đúng thì trước hết phải hiểu kiến thức được lưu và lấy ra thế nào trong hai kiểu hệ thống này:

# parametric memory

Khi bạn train hoặc fine-tune một mạng Transformer, kiến thức bị rải ra (distributed representation) qua hàng tỷ phép nhân ma trận:

y = softmax(W * x + b)

Về bản chất, mô hình ngôn ngữ là một cỗ máy đoán xác suất token tiếp theo (Statistical Token Predictor), không phải hệ quản trị cơ sở dữ liệu (RDBMS). Thứ nó nhớ là pattern, cấu trúc ngữ nghĩa và mối liên hệ xác suất giữa các khái niệm.

Giờ bạn ép nó nhớ từng sự thật rời rạc (atomic facts) kiểu "Mã hợp đồng của dự án Alpha là #98213" thông qua hàm Loss:

Loss(theta) = -SUM(log P(x_t | x_<t; theta))

Mỗi lần cập nhật gradient lại kéo lệch những trọng số đã được tối ưu từ trước. Hậu quả có hai cái, đều khá nặng:

  1. Catastrophic Forgetting: mô hình học được cái mới nhưng lại kém đi khả năng suy luận, hoặc quên luôn kiến thức chung đã học ở giai đoạn pre-training.
  2. Nhoè xác suất (mình tạm gọi vậy, không phải thuật ngữ chuẩn): mô hình chỉ nhớ mang máng là có một con số nào đó dính với dự án Alpha. Tới lúc sinh chữ, nó chọn token có xác suất cao nhất trong vùng nghĩa đó, và thế là bịa ra một con số với vẻ mặt cực kỳ tự tin.

# non-parametric memory

Ngược lại, Non-Parametric Memory là để nguyên dữ liệu gốc ở các cấu trúc bên ngoài: Inverted Index (Elasticsearch/OpenSearch), Vector Index (Milvus, Qdrant, pgvector) hoặc Knowledge Graph (Neo4j).

Mô hình không cần "nhớ" gì trong đầu. Người dùng hỏi thì hệ thống chạy một bước truy vấn (retrieval) để lấy đúng đoạn văn bản chứa câu trả lời, rồi nhét nó vào context window của mô hình.

Lúc này mô hình chỉ làm bộ suy luận (Reasoning Engine): đọc văn bản được đưa cho, tổng hợp câu trả lời và trích dẫn tới từng dòng.


# ma trận đánh đổi 7 trục: rag vs fine-tuning

Chọn công nghệ thì đừng chọn theo cảm giác. Quyết định kiến trúc nào cũng là cân đo giữa các ràng buộc kỹ thuật. Dưới đây là 7 trục mình hay dùng để so:

Trục kiến trúcRAG (Retrieval-Augmented Generation)Fine-Tuning (PEFT / LoRA / QLoRA)Bên thắng
1. Tần suất đổi dữ liệu (Data Freshness SLA)Gần như tức thì (dưới 1 giây tới vài phút) qua CDC Pipeline, vector upsert. Không cần train lại.Chậm (vài ngày tới vài tuần) vì phải làm data curation, training, eval, regression test rồi deploy.RAG thắng rõ
2. Kiểm chứng nguồn (Audit & Grounding)Minh bạch. Trả về luôn Document ID, Page Number, Chunk URI, điểm retrieval để đối chiếu.Hộp đen. Không thể chỉ ra câu trả lời đến từ record nào trong dataset.RAG thắng rõ
3. Tuân thủ định dạng (Format & Style)Kém hơn. Dựa vào few-shot prompt, dễ gãy format khi JSON phức tạp hoặc AST code.Rất ổn định. Cấu trúc cố định được dạy thành phản xạ ngay trong trọng số.Fine-Tuning thắng rõ
4. Độ trễ phục vụ (Latency SLA: TTFT)Cao hơn. Mất 20-80ms cho retrieval + re-ranking; prompt dài (2k-8k tokens) kéo TTFT lên.Rất thấp. Prompt cực ngắn (50-200 tokens), prefill nhanh, đỡ tốn KV Cache.Fine-Tuning thắng rõ
5. Phân quyền dữ liệu (Multi-tenant & RBAC)Rất tự nhiên. Lọc metadata (User ID, Role ID, Department) ngay ở tầng retrieval.Không làm được. Trọng số không phân quyền được; dễ lộ dữ liệu chéo giữa các tenant.RAG thắng rõ
6. Chi phí hạ tầng (TCO Profile)Không tốn tiền train; duy trì index rẻ; nhưng chi phí suy luận (token/GPU) cao vì context dài.Tiền GPU train ban đầu cao; về lâu dài suy luận rất rẻ nhờ prompt ngắn và model nhỏ.Tuỳ query volume
7. Khả năng gỡ lỗi (Observability & Debug)Dễ soi. Xem được lỗi nằm ở chunking, retrieval, re-ranking hay ở LLM.Rất khó debug. Phải mò gradient, loss curve, logit distribution hoặc train lại.RAG thắng rõ

# ma trận quyết định 2x2 & cây quyết định giải pháp

Muốn biết bài toán của mình nằm ở góc nào, trả lời hai câu sau:

  1. Kiến thức nghiệp vụ thay đổi nhanh hay chậm?
  2. Bạn cần mô hình đổi KIẾN THỨC (domain facts) hay đổi HÀNH VI & CẤU TRÚC (style, tone, syntax)?

# cây quyết định kiến trúc

Trước khi đề xuất giải pháp nào lên ban giám đốc, cứ đi qua cây quyết định này một lượt:


# deep-dive kiến trúc rag enterprise

Nếu bài toán cần RAG, quên mấy tutorial kiểu Đọc PDF -> RecursiveSplitter -> VectorDB -> Prompt trên mạng đi. Kiểu Naive RAG đó gần như chắc chắn sẽ gãy khi vào môi trường enterprise, vì:

  • Chunk size cố định cắt đứt ngữ cảnh (context fragmentation).
  • Chỉ tìm theo ngữ nghĩa (dense vector search) thì bó tay với các truy vấn cần khớp chính xác từ khoá: mã số thuế, mã lỗi exception, tên biến trong code, số model linh kiện.
  • Điểm cosine cao ngất mà đoạn văn lại không hề chứa câu trả lời.

Một hệ thống Advanced Modular RAG đủ chuẩn production nên trông thế này:

# hybrid search kết hợp rrf

Đừng bao giờ chỉ dùng mỗi vector search. Phải ghép Dense Retrieval (bắt được ý nghĩa ngầm) với Sparse Retrieval (bắt chính xác keyword, mã số kỹ thuật qua BM25).

Hai danh sách kết quả được gộp lại bằng thuật toán Reciprocal Rank Fusion (RRF):

RRF_Score(d) = SUM_{m ∈ M} 1 / (k + r_m(d))

Trong đó:

  • M là tập các cách tìm kiếm (Dense và Sparse).
  • r_m(d) là thứ hạng của tài liệu d trong cách tìm m.
  • k là hằng số làm mượt (thường người ta chọn k = 60, giá trị dùng trong paper gốc của Cormack và cộng sự, SIGIR 2009).

RRF chỉ dùng thứ hạng, không dùng điểm số, nên không phải lo chuẩn hoá điểm BM25 (không giới hạn trên) với điểm cosine (từ -1 tới 1) về cùng thang. Một tài liệu đứng hạng 1 ở cả hai danh sách được 2/61 ≈ 0.033, còn tài liệu chỉ đứng hạng 1 ở một danh sách được 1/61 ≈ 0.016: xuất hiện ở cả hai nguồn được ưu tiên hơn.

# cross-encoder re-ranking

Bi-Encoder (vector search) so vector câu hỏi với vector chunk một cách độc lập: sim(u, v) = u * v. Nó chấp nhận kém chính xác hơn để đổi lấy tốc độ, quét được hàng triệu vector qua HNSW index.

Lấy được Top 50 ứng viên từ hybrid search rồi thì nhất định phải cho qua một Cross-Encoder Re-Ranker (như bge-reranker-large hoặc Cohere Rerank). Cross-Encoder đưa cả cặp [Query, Chunk] vào chung một mạng attention, để từng token của câu hỏi "nhìn" thẳng vào từng token của tài liệu. Nhờ vậy nó chấm được độ liên quan thật, rồi mới chọn Top 3-5 chunk đưa vào context cho LLM.

TIP

Mẹo cho Enterprise RAG: luôn dùng Parent-Child Chunking đi kèm Cross-Encoder Re-Ranking. Cắt văn bản thành child chunk 256 tokens để vector search bắt trúng chỗ khớp nghĩa, nhưng lúc đưa vào LLM thì lấy parent chunk 1024 tokens bao quanh để ngữ cảnh không bị cụt.


# deep-dive kiến trúc fine-tuning: lora & qlora

Khi bài toán bắt mô hình trả lời theo một format riêng (sinh SQL đúng dialect của công ty, xuất JSON theo schema phức tạp, hay nói chuyện đúng giọng thương hiệu), Fine-Tuning là công cụ hợp nhất.

Có điều, Full-Parameter Fine-Tuning (chỉnh toàn bộ trọng số) cho mô hình 7B tới 70B tham số thì đa số doanh nghiệp không kham nổi:

  • Train mixed precision với AdamW tốn khoảng 16 bytes cho mỗi tham số: 2 bytes trọng số BF16, 2 bytes gradient, 4 bytes bản trọng số FP32 và 8 bytes cho hai moment của AdamW. Mô hình 70B cần khoảng 1.1 TB VRAM chỉ cho mấy thứ đó, chưa tính activation!
  • Hạ tầng tốn kém khủng khiếp, lại dễ dính Catastrophic Forgetting.

Cách cả ngành đang làm là PEFT (Parameter-Efficient Fine-Tuning), phổ biến nhất là LoRA (Low-Rank Adaptation) và QLoRA:

# bản chất toán học của LoRA

Giả sử ma trận trọng số gốc của một lớp Attention Projection là W_0, kích thước (d x k). Thay vì cập nhật thẳng cả ma trận delta_W kích thước (d x k), LoRA giả định rằng phần thay đổi cần cho tác vụ mới (intrinsic rank) có thể nén xuống một không gian ít chiều hơn rất nhiều (r << min(d, k)).

LoRA tách delta_W thành tích của hai ma trận hạng thấp:

delta_W = B * A

Trong đó B có kích thước (d x r) và A có kích thước (r x k).

  • Lúc bắt đầu train, A khởi tạo Gaussian ngẫu nhiên còn B = 0, nên ở bước đầu tiên delta_W = 0 (mô hình vẫn y như cũ).
  • Output lúc suy luận:
h = W_0 * x + (alpha / r) * (B * A * x)

Với rank r = 8 hoặc r = 16, số tham số cần train thường chỉ còn dưới 1% so với full fine-tuning (paper LoRA báo giảm 10.000 lần với GPT-3 175B). Ví dụ một ma trận 4096 x 4096 có ~16.8 triệu tham số, LoRA r = 8 chỉ cần 8 x (4096 + 4096) = 65.536 tham số, khoảng 0.4%. Kết hợp QLoRA (trọng số gốc lượng tử hoá 4-bit), fine-tune mô hình 8B vừa trong một card 24GB như RTX 4090 hay A10G.

TIP

Chất lượng dữ liệu quan trọng hơn số lượng rất nhiều. Paper LIMA (Meta, 2023) fine-tune LLaMA 65B chỉ với 1.000 mẫu được chọn lọc kỹ mà vẫn cho kết quả cạnh tranh. Với doanh nghiệp, vài nghìn cặp Instruction - Output được chuyên gia duyệt tay (Golden Dataset) thường đáng giá hơn hàng trăm nghìn mẫu cào bừa đầy nhiễu.


# kiến trúc kết hợp: rag + specialized slm

Ở các hệ thống AI enterprise bây giờ, câu hỏi không còn là "RAG hay Fine-Tuning?" nữa, mà là ghép hai thứ này lại với nhau thế nào:

# chia vai trong kiến trúc kết hợp

  1. RAG lo phần "sự thật" (Ground Truth):
    • Chịu trách nhiệm toàn bộ dữ liệu hay đổi: chính sách, giá, điều khoản, dữ liệu khách hàng.
    • Đảm bảo trích được nguồn (compliance audit).
  2. Fine-Tuned SLM (Small Language Model - 7B/14B) lo phần "làm việc chuyên môn":
    • Được train kỹ để hiểu mấy thuật ngữ viết tắt nội bộ của công ty.
    • Nhận context từ RAG rồi xuất ra JSON/XML đúng chuẩn 100%, không thừa một ký tự.
    • Sinh token nhanh hơn và chi phí mỗi request thấp hơn đáng kể so với gọi các mô hình lớn qua API, khi lưu lượng đủ lớn để GPU tự host không ngồi chơi. Chênh bao nhiêu thì tuỳ hạ tầng, nên tự đo trên tải thật.

# bài toán tco: phân tích chi phí & điểm hoà vốn

Kỹ sư hay mắc một lỗi: chỉ nhìn tiền thuê GPU lúc đầu mà quên mất tổng chi phí sở hữu (TCO) trong cả vòng đời sản phẩm.

# công thức chi phí RAG

Chi phí RAG phụ thuộc thẳng vào độ dài context window của mỗi request:

Cost_RAG(N) = C_infra_vec + N * (Tokens_in * P_in + Tokens_out * P_out)
  • Với RAG, Tokens_in thường rất lớn (2,000 tới 8,000 tokens vì phải kèm tài liệu tham khảo).
  • Query nào cũng phải trả tiền cho khối context to đùng đó, lặp đi lặp lại.

# công thức chi phí Fine-Tuning

Fine-Tuning thì tốn vốn cố định ban đầu (CapEx) cao, nhưng chi phí trên mỗi request (OpEx) lại rất thấp:

Cost_FT(N) = C_train + C_gpu_instance * Time + N * (Tokens_in_short * P_gpu_token)
  • Mô hình đã học sẵn format và giọng văn, nên prompt đầu vào (Tokens_in_short) chỉ cần 50 - 150 tokens.
  • Cùng một cụm GPU inference, mô hình phục vụ được số request đồng thời (concurrency) cao hơn nhiều lần nhờ đỡ tốn KV Cache.

NOTE

Cách tính điểm hoà vốn:

  • Hệ thống nội bộ, lượng truy vấn thấp (cỡ vài chục nghìn queries/tháng): dùng RAG với LLM API thương mại. Đừng đụng tới Fine-tuning.
  • Hệ thống B2C, lượng truy vấn rất lớn (cỡ hàng triệu queries/tháng) và tác vụ lặp đi lặp lại: cân nhắc Fine-tune một SLM (7B/14B) để tự host. Lúc tính nhớ cộng cả tiền GPU inference chạy 24/7 và công vận hành (monitoring, train lại định kỳ), vì khoản này nhiều khi lớn hơn tiền train một lần.

Các mốc trên chỉ để định hướng. Hãy thay giá token, giá GPU và lưu lượng thật của bạn vào hai công thức ở trên, giải Cost_RAG(N) = Cost_FT(N) để ra N_breakeven. Cũng nên nhớ prompt caching của các nhà cung cấp API giảm được tiền cho phần prompt cố định (system prompt, few-shot), nhưng không giúp nhiều cho phần context RAG vì nó đổi theo từng câu hỏi.


# cạm bẫy khi triển khai thật

Đây là 5 cái bẫy hay gặp nhất khi đưa RAG hay fine-tuning vào doanh nghiệp:

# tưởng Fine-Tuning cập nhật được kiến thức động

Mô hình sẽ trộn lẫn giá cũ với giá mới. Giá cả và số liệu bắt buộc phải nằm ở Database/RAG, hoặc lấy qua Tool Calling (Function Calling) tới REST API lúc chạy.

# nhồi chunk mà không re-ranking

Có hiện tượng gọi là "Lost in the Middle" (Liu và cộng sự, 2023): mô hình dùng thông tin ở đầu và cuối context tốt hơn hẳn thông tin nằm giữa. Nhồi quá nhiều chunk rác vào chỉ làm loãng sự chú ý của mô hình, tăng latency và tốn token vô ích. Dùng Cross-Encoder để lọc lại còn 3-5 chunk ngon nhất thôi.

# quên data guardrails chống catastrophic forgetting

Train mô hình trên dữ liệu chuyên ngành hẹp thì nhớ trộn thêm một phần dữ liệu hội thoại chung (General Conversation Dataset) vào tập train (kỹ thuật replay), để mô hình khỏi "mất não" ở mấy kỹ năng suy luận cơ bản. Tỉ lệ bao nhiêu là vừa thì không có con số chung, phải đo trên bộ eval tổng quát trước và sau khi train. LoRA cũng đỡ quên hơn full fine-tuning vì trọng số gốc được giữ nguyên.

# quên rbac ở vector database

Trong doanh nghiệp, nhân viên không được đọc tài liệu lương thưởng của ban giám đốc. Nếu chỉ embed tài liệu mà không gắn metadata phân quyền (department_id, security_clearance), RAG sẽ vô tư lấy thông tin mật ra trả lời cho nhân viên bình thường!

# đánh giá chất lượng theo cảm giác

Đừng đánh giá RAG hay Fine-tuning kiểu "gõ vài câu test thấy ưng cái bụng". Dựng hẳn pipeline đánh giá tự động (Evals Pipeline): với RAG thì dùng Ragas / TruLens (đo Faithfulness, Answer Relevance, Context Precision), với Fine-Tuning thì dùng LLM-as-a-Judge trên một bộ test set chuẩn.


# checklist trước khi bắt tay vào dự án

Trước khi ký duyệt kiến trúc AI nào, Technical Architect nên trả lời đủ 6 câu sau:

  • Kiến thức đổi nhanh cỡ nào: dữ liệu nghiệp vụ thay đổi theo chu kỳ nào? (Nếu ≤ hằng tuần -> bắt buộc RAG).
  • Ràng buộc pháp lý & audit: khách hàng hoặc cơ quan quản lý có yêu cầu hiện nguồn trích dẫn không? (Nếu có -> bắt buộc RAG).
  • Đã vắt hết Prompt Engineering chưa: đã thử Few-Shot, Chain-of-Thought và Structured Output (Pydantic/Instructor) chưa? (Nếu chưa -> dừng Fine-tuning lại, thử prompt trước!).
  • Chịu lệch format tới đâu: hệ thống downstream có chết ngay nếu JSON trả về thiếu một field không? (Nếu có -> thử Structured Output của nhà cung cấp hoặc Grammars/Constrained Decoding trước, vì chúng ép đúng schema ngay lúc sinh token; vẫn chưa đủ thì mới cần Fine-Tuned SLM).
  • Latency SLA: P99 có cần dưới 300ms không? (Nếu có -> chỉ RAG thì khó đạt, cần Fine-Tuned SLM tự host trên vLLM).
  • Ước lượng query volume: điểm hoà vốn giữa tiền token context dài và tiền train/host model nằm ở mốc nào?

Mấy ghi chép kiến trúc này đúc kết từ những lần "trả giá bằng tiền thật" trên production. Thấy có ích cho team thì cứ share cho anh em Architect & Tech Lead nhé!

Happy Architecting & Coding! 😎 🚀 🏛️ 🔥


Chỉ là những ghi chép cá nhân với hy vọng mang lại chút giá trị. Nếu thấy hữu ích, đừng ngại chia sẻ cho bạn bè & đồng nghiệp nhé!

Happy coding 😎 👍🏻 🚀 🔥.