# Tự fine-tune embedding model không khó như bạn nghĩ

[Muhammad Farooq](https://homus.dev/speakers/muhammad-farooq) · [Prompt Engineering](https://homus.dev/orgs/prompt-engineering)

> Fine-tune EmbeddingGemma 2 bằng LoRA và Unsloth trên dữ liệu riêng: cặp câu hỏi và đoạn văn, in-batch negatives, chia test theo nguồn, đo cái giá.

Topics: Retrieval, Evals, Dev Tools

Canonical: https://homus.dev/talks/training-your-own-embedding-model-is-not-as-hard-as-you-think

## 1. Câu hỏi của video: vài phút fine-tune có đủ không?

Muhammad mở đầu bằng EmbeddingGemma 2. Theo anh (video đăng ngày 08/10/2026), đây là một open model rất tốt cho multimodal retrieval. Nhưng điều làm nó còn tốt hơn, theo anh, là bạn có thể fine-tune nó trên dữ liệu của chính mình.

Theo anh (video đăng ngày 08/10/2026), điều này quan trọng vì một embedding model dùng nguyên bản (off the shelf) chỉ biết dữ liệu chung mà nó đã được train. Nó không biết gì về tài liệu của bạn, tên sản phẩm của bạn, hay những âm thanh mà ứng dụng của bạn quan tâm.

![Banner EmbeddingGemma 2 cạnh logo Google, bên dưới là năm biểu tượng văn bản, code, ảnh, video, âm thanh](https://homus.dev/photos/S7tFyREI19I-0003.jpg)

Màn hình mở đầu: EmbeddingGemma 2 của Google, với năm loại đầu vào được vẽ thành biểu tượng: văn bản, code, ảnh, video và âm thanh, tất cả cùng đi vào một model (video đăng ngày 08/10/2026).

Trước khi giải thích cách làm, anh cho xem một demo nhanh. Đây là một phiên bản EmbeddingGemma 2 mà anh đã fine-tune trên nội dung lời nói từ các video của chính kênh YouTube của anh. Anh gõ một câu hỏi, và model trả về đúng khoảnh khắc trong một video nơi anh nói về chủ đề đó, kèm mốc thời gian có thể bấm vào (demo trong video đăng ngày 08/10/2026). Trên màn hình là một notebook tên `EmbeddingGemma2_Finetune.ipynb` chạy trên GPU A100, với một hàm `ask` nhận câu hỏi và lấy ba kết quả gần nhất:

```
def ask(q, k = 3):
s = model.similarity(model.encode(q, prompt_name = "SearchQuery", convert_to_tensor = True), D)[0]
for i in s.topk(k).indices.tolist():
```

Câu hỏi mà anh muốn trả lời trong video này là: liệu vài phút fine-tune có làm model này tốt lên rõ rệt trên dữ liệu của riêng bạn, mà không làm hỏng những gì nó vốn đã làm tốt hay không. Trên màn hình, câu hỏi được vẽ thành một cái cân: một bên là "better on your data" (tốt hơn trên dữ liệu của bạn), bên kia là "what it already does well" (những gì nó vốn đã làm tốt). Anh sẽ giải thích fine-tune một embedding model hoạt động thế nào, rồi đi qua một notebook trong đó anh fine-tune model trên hai ví dụ của chính mình: một bộ âm thanh, và các video trên kênh của anh. Notebook dựa trên Unsloth, chạy trong Colab trên GPU A100 (video đăng ngày 08/10/2026).

## 2. Fine-tune embedding model khác fine-tune LLM ở đâu

Trước hết, anh nói về điều làm cho việc fine-tune một embedding model khác với fine-tune một large language model. Khi fine-tune một LLM, bạn cho nó xem một input và chính xác đoạn văn bản bạn muốn nó viết ra. Vậy là dữ liệu train đã chứa sẵn câu trả lời đúng, từng chữ một (theo giải thích của Muhammad, video đăng ngày 08/10/2026).

Còn theo anh (video đăng ngày 08/10/2026), một embedding model thì không viết ra gì cả. Nó biến một input thành một danh sách các con số, và những con số đó chỉ có ích trong quan hệ với nhau. Đây là thứ người ta thường gọi là embedding space. Vì vậy không có một danh sách số "đúng", hay một embedding đúng, mà bạn có thể viết ra làm target. Điều duy nhất bạn có thể nói với model là những thứ nào nên nằm gần nhau. Và điều này thay đổi hình dạng của dữ liệu train.

![So sánh LLM và embedding model: bên trái câu hỏi và câu trả lời mẫu, bên phải câu hỏi đi vào EmbeddingGemma 2 và thành một điểm trong embedding space](https://homus.dev/photos/S7tFyREI19I-0143.jpg)

"Embeddings vs LLMs". Bên trái (LLM): câu hỏi "How do I reset the hub?" được viết thành câu trả lời "Hold the reset button for ten seconds until the light blinks", và target là chính câu trả lời đó, từng chữ. Bên phải (embedding model): câu hỏi đi vào EmbeddingGemma 2, không có văn bản nào được viết ra ("writes text" bị gạch), chỉ có một điểm trong embedding space; ô "target =" để trống với dấu hỏi (video đăng ngày 08/10/2026).

## 3. Dữ liệu train là các cặp câu hỏi và đoạn trả lời

Anh đưa ra một ví dụ. Giả sử bạn lo phần hỗ trợ khách hàng cho một công ty smart home, và bạn muốn có một ô search trả lời câu hỏi của khách từ help center. Mỗi ví dụ train là một cặp: một câu hỏi của khách và đoạn văn (passage) trả lời câu hỏi đó. Bạn không cần điểm số, cũng không cần gán nhãn các câu trả lời sai. Chỉ cần những câu hỏi và những đoạn văn thuộc về nhau.

![Your data = pairs: ba cặp câu hỏi và đoạn trả lời bên cạnh một embedding space nơi mỗi câu hỏi nằm sát đoạn trả lời của nó](https://homus.dev/photos/S7tFyREI19I-0223.jpg)

"Your data = pairs". Ba cặp trong training data: "How do I reset the hub?" với "Hold the reset button for ten seconds…", "Why is the light red?" với "A red light means…", "Can I add a second sensor?" với "Open the app and tap Add…". Trong embedding space bên trái, mỗi câu hỏi nằm sát đoạn trả lời của nó. Bên dưới, "scores" và "wrong answers" bị gạch: không cần đến.

Nhưng theo anh (video đăng ngày 08/10/2026), chỉ kéo mỗi câu hỏi về gần đoạn trả lời của nó thì chưa đủ, vì model có thể đặt mọi thứ vào cùng một chỗ mà vẫn được điểm cao. Vậy nên tiếp theo anh nói về cách model học được thứ gì cần đẩy ra xa và thứ gì cần kéo lại gần.

## 4. In-batch negatives: đáp án sai có sẵn trong batch

Theo giải thích của Muhammad (video đăng ngày 08/10/2026), trong lúc train, model nhìn thấy một batch gồm nhiều cặp cùng lúc, ví dụ một batch 32 cặp. Với mỗi câu hỏi, đoạn văn của chính nó là đáp án đúng, và mọi đoạn văn khác trong batch được coi là đáp án sai. Vì thế bạn thực ra không phải đi thu thập các ví dụ âm (negative examples). Khi đã thu thập các cặp, bạn tự động có các negative example.

Theo anh (video đăng ngày 08/10/2026), loss sẽ kéo mỗi câu hỏi về phía đoạn văn của nó và đẩy nó ra xa tất cả các đoạn còn lại. Nghĩa là bạn có các đáp án sai mà không phải gán nhãn một đáp án nào. Mental model dễ nhất, theo anh, là một câu hỏi trắc nghiệm, trong đó các cặp khác trong batch là những lựa chọn sai.

![Lưới câu hỏi Q1 đến Q6 và đoạn văn P1 đến P6, đường chéo là dấu tick, còn lại là dấu x; bên phải là câu hỏi trắc nghiệm A đến F](https://homus.dev/photos/S7tFyREI19I-0323.jpg)

"In-batch negatives", "batch = 32". Lưới câu hỏi Q1 đến Q6 nhân đoạn văn P1 đến P6: chỉ ô trên đường chéo là đúng, mọi ô còn lại là sai. Hàng Q1 được mở thành một câu trắc nghiệm: "How do I reset the hub?" với A là P1 "Hold the reset button…" (đúng), còn B đến F là các đoạn khác (sai). Dòng chữ bên dưới: sentence-transformers (video đăng ngày 08/10/2026).

Trong thư viện Sentence Transformers, loss này gọi là `MultipleNegativesRankingLoss`. Và theo Muhammad (video đăng ngày 08/10/2026), đây cũng chính là ý tưởng mà CLIP đã dùng để đặt ảnh và văn bản vào cùng một không gian.

## 5. Bẫy trùng lặp và no-duplicates sampler

Theo Muhammad (video đăng ngày 08/10/2026), có một điểm cần cẩn thận ở đây. Giả sử hai khách hàng hỏi về cùng một quy trình reset, và cả hai cặp rơi vào cùng một batch. Loss sẽ coi bản sao thứ hai của đoạn văn đó là một đáp án sai cho câu hỏi thứ nhất. Trong trường hợp này, bạn đang dạy model rằng câu trả lời đúng là sai.

Cách sửa là một batch sampler không bao giờ đặt cùng một đoạn văn bản hai lần trong một batch. Theo anh (video đăng ngày 08/10/2026), Sentence Transformers gọi nó là no-duplicates sampler. Trên màn hình, ví dụ "How do I reset the hub?" và "My hub is frozen, how do I restart it?" cùng trỏ tới đoạn "Hold the reset button for ten seconds until the light blinks"; sampler tách chúng ra: batch 1 chứa cặp về reset, còn cặp "hub frozen?" với "Hold the reset…" được đưa sang batch 2, cùng với các cặp về Wi-Fi, update và billing.

Bẫy trùng lặp: hai câu hỏi cùng trỏ tới một đoạn trả lời. Nếu cả hai nằm chung batch, đoạn đó vừa là đáp án đúng vừa bị tính là đáp án sai. No-duplicates sampler đưa hai cặp này vào hai batch khác nhau.

## 6. Prompt khi train và khi search phải giống hệt nhau

Điều thứ hai cần làm đúng là prompt. Theo Muhammad (video đăng ngày 08/10/2026), EmbeddingGemma 2 cần một task prompt ngắn đặt trước câu truy vấn search, và một format title cộng text cho tài liệu. Vì vậy bạn train với format nào thì code search của bạn phải dùng đúng format đó.

Anh (video đăng ngày 08/10/2026) bảo hãy nghĩ về nó như prompt template mà người ta vẫn dùng với large language model: prompt template dùng khi train phải giống hệt cái bạn dùng ở production, tức là lúc inference.

![Same format: hai khung giống hệt nhau cho training và search, mỗi khung có task prompt trước câu hỏi và format title, text cho tài liệu](https://homus.dev/photos/S7tFyREI19I-0423.jpg)

Mục thứ hai "same format" (mục thứ nhất "no duplicates" đã được đánh dấu xong). Bên trái là training, bên phải là search, và cả hai dùng cùng một khuôn: câu truy vấn có tiền tố task, tài liệu có dạng title và text.

Format trên màn hình, giống nhau ở cả hai bên:

```
task: search result | query: How do I reset the hub?

title: Reset your hub | text: Hold the reset button…
```

## 7. LoRA trên một backbone đa phương thức dùng chung

Tiếp theo là thứ thực sự được train, và theo Muhammad đây là phần thú vị nhất của video. Thay vì cập nhật toàn bộ weights, anh dùng LoRA: nó thêm những ma trận nhỏ có thể train được bên cạnh các layer sẵn có, và giữ nguyên (frozen) các weights gốc. Theo anh (video đăng ngày 08/10/2026), LoRA adapter train cực nhanh, nên bạn không thực sự cần train lại cả mạng.

Trong các lần chạy của anh, phần được train chiếm chưa tới hai phần trăm số weights (theo số đo của Muhammad Farooq, video đăng ngày 08/10/2026). Nhờ vậy bạn có thể lưu một file adapter nhỏ thay vì cả một bản sao của model. Trên màn hình, một layer được vẽ thành lưới ô vuông bị khoá ("frozen"), cộng thêm hai thanh A và B có thể train ("LoRA trainable"), cạnh dòng "retrain all weights" bị gạch và chữ "fast".

Trong video trước, theo anh (video đăng ngày 08/10/2026), văn bản, ảnh, video và âm thanh đều đi qua cùng một backbone của EmbeddingGemma 2. Vì vậy nếu bạn train backbone trên âm thanh, bạn cũng đang thay đổi phần xử lý ảnh và văn bản của mình. Và cần kiểm tra xem cái giá của việc đó thực sự là bao nhiêu.

![Sơ đồ: văn bản đi thẳng vào EmbeddingGemma 2, ảnh và video qua vision encoder, âm thanh qua audio encoder; LoRA gắn ở audio encoder và backbone; bên phải là hình của Google](https://homus.dev/photos/S7tFyREI19I-0523.jpg)

Bên trái: văn bản đi thẳng vào EmbeddingGemma 2, ảnh và video qua vision encoder, âm thanh qua audio encoder; LoRA (khối sọc vàng) gắn ở audio encoder và ở backbone. Bên phải là hình của Google đã xuất hiện trong video trước ("Google's figure · last video"): các encoder module (Vision Encoder 170M, Audio Encoder) đổ vào EmbeddingGemma 2 (270M), và trong embedding space các thứ giống nhau nằm gần nhau, thứ khác nhau nằm xa nhau. Số tham số theo hình đó, video đăng ngày 08/10/2026.

## 8. Đo đúng cách: giữ test set riêng và chia theo nguồn

Khái niệm cuối cùng là cách đo tất cả những điều này (theo giải thích của Muhammad, video đăng ngày 08/10/2026). Training loss giảm chỉ cho bạn biết model đã giỏi hơn trên chính những ví dụ nó được train. Thứ bạn cần quan tâm là chất lượng search trên những câu hỏi nó chưa thấy. Nếu không, đó chỉ là overfitting.

Vì vậy, theo anh (video đăng ngày 08/10/2026), bạn giữ riêng một test set và chia nó theo nguồn. Nếu cùng một bài viết có đoạn nằm ở cả train lẫn test, thì về thực chất model đã thấy đáp án rồi. Nên bạn muốn chia theo bài viết, theo video, hoặc theo bản ghi âm. Trên màn hình, các chủ đề Reset, Sensors, Billing, Updates nằm bên train, Wi-Fi nằm bên test, ngăn cách bằng một bức tường; một mũi tên đỏ đưa một chủ đề từ bên test sang bên train.

Theo anh (video đăng ngày 08/10/2026), điều này cũng dẫn tới một câu hỏi cho các thí nghiệm: model học một kỹ năng chung (general skill), hay chỉ học các nhãn (label) của bạn. Đến đây là đủ lý thuyết, anh chuyển sang notebook.

Chia theo nguồn: mỗi bài viết, video hay bản ghi âm chỉ nằm ở bên train hoặc bên test, không bao giờ cả hai, để điểm test thật sự đo trên dữ liệu model chưa thấy.

## 9. Notebook và ví dụ âm thanh ESC-50: điểm xuất phát

Notebook được xây trên các notebook EmbeddingGemma 2 của Unsloth, và anh chạy mọi thứ trên một GPU A100 trong Colab. Link notebook nằm trong phần mô tả video (xem mục nguồn bên dưới). Cell import trên màn hình lấy FastSentenceTransformer từ Unsloth:

```
from unsloth import FastSentenceTransformer, is_bf16_supported
import csv, gc, json, os, random, time, urllib.request, zipfile
import numpy as np, torch
from datasets import Dataset
from IPython.display import Audio, HTML, display
```

Ví dụ thứ nhất sửa điểm yếu đã thấy trong video trước: âm thanh đời thường. Anh khuyên ai chưa xem video đó thì nên xem. Anh dùng dataset ESC-50, gồm các clip ngắn của 50 loại âm thanh đời thường như chó sủa, mưa hay cưa máy. Trên màn hình ghi "ESC-50 · 2,000 clips · 50 sounds" (video đăng ngày 08/10/2026).

Trong notebook (video đăng ngày 08/10/2026), mỗi tên âm thanh được biến thành một câu, ví dụ "the sound of a chainsaw". Như vậy bài toán classification trở thành bài toán search: model embed đoạn clip, embed câu nhãn, rồi chọn câu gần nhất.

Trước khi train, theo số đo của Muhammad Farooq (video đăng ngày 08/10/2026), model chọn đúng âm thanh ở vị trí top-1 chỉ khoảng một lần trong bốn, tức là độ chính xác khoảng 25%. Anh cũng để ý rằng prompt classification có sẵn của model còn cho kết quả tệ hơn so với việc viết nhãn thành một câu truy vấn search. Nên một prompt tốt hơn sẽ không giải quyết được chuyện này, và đó là chỗ việc train phát huy tác dụng.

![Cell notebook chạy esc50_eval: baseline top-1 24.5, top-5 57.0; bare label với Classification prompt top-1 13.2; danh sách các lớp ở 0%](https://homus.dev/photos/S7tFyREI19I-0723.jpg)

Kết quả baseline trong notebook, theo số đo của Muhammad Farooq (video đăng ngày 08/10/2026): câu "the sound of …" với prompt SearchQuery đạt top-1 24.5, top-5 57.0; nhãn trần với prompt Classification chỉ đạt top-1 13.2, top-5 35.7. Bên dưới là danh sách các lớp ở 0%, trong đó có airplane, brushing_teeth, chainsaw, rain, toilet_flush. Hình vẽ tóm tắt: một đúng ba sai, "1 in 4", 24.5% top-1.

## 10. Train trên âm thanh: từ một trong bốn lên hai trong ba

Vậy việc train diễn ra thế nào? Các cặp train là câu nhãn và đoạn audio clip. Đây chính là lúc cần nhớ tới bẫy trùng lặp, vì mỗi nhãn có rất nhiều clip train. Nếu không có no-duplicates sampler, cùng một nhãn sẽ rơi vào chung một batch.

Anh thêm LoRA vào audio encoder và vào backbone. Theo số đo của Muhammad Farooq (video đăng ngày 08/10/2026), việc train mất khoảng bảy phút rưỡi trên GPU A100; trên màn hình ghi 454 giây, train trên 1.600 clip. Cell thêm LoRA trên màn hình:

```
def add_lora(model):
return FastSentenceTransformer.get_peft_model(
model, r = 16, lora_alpha = 32, lora_dropout = 0, bias = "none",
target_modules = LORA_TARGETS,
finetune_audio_layers = True,          # also adapt the audio encoder
use_gradient_checkpointing = "unsloth",
random_state = 3407, task_type = "FEATURE_EXTRACTION",
)
```

Thứ cần theo dõi là kết quả top-1 cho những clip model chưa từng nghe khi train. Theo số đo của Muhammad Farooq (video đăng ngày 08/10/2026), nó đi từ khoảng một trong bốn lên khoảng hai trong ba. Điều thú vị nhất là có những âm thanh trước đó không bao giờ được nhận đúng, giờ được nhận đúng gần như mọi lần, ví dụ tiếng đánh răng. Anh kết luận rằng train một LoRA adapter trên weights của model thực sự mang lại một bước nhảy lớn về độ chính xác (video đăng ngày 08/10/2026).

![before top-1 24.5, top-5 57.0; after top-1 65.8, top-5 92.3; biggest gains brushing_teeth 0% lên 100%, toilet_flush 0% lên 88%; cột before và after](https://homus.dev/photos/S7tFyREI19I-0823.jpg)

Kết quả train âm thanh, theo số đo của Muhammad Farooq (video đăng ngày 08/10/2026, "my notebook run · A100"): before top-1 24.5, top-5 57.0; after top-1 65.8, top-5 92.3. Mức tăng lớn nhất: brushing_teeth từ 0% lên 100%, toilet_flush từ 0% lên 88%. Hình vẽ: "1 in 4" thành "2 in 3".

## 11. Model học kỹ năng nghe hay chỉ học nhãn?

Tiếp đó, anh kiểm tra xem model đã học cách nghe, hay chỉ học thuộc các nhãn. Anh chạy lại lần nữa, nhưng lần này giấu hoàn toàn 10 loại âm thanh khỏi dữ liệu train, rồi test trên đúng 10 loại đó. Trên các âm thanh được train, model tốt lên giống như lần trước. Nhưng trên 10 âm thanh nó chưa bao giờ thấy, nó không tốt lên (theo số đo của Muhammad Farooq, video đăng ngày 08/10/2026).

Vậy là model học các nhãn bạn đưa cho nó, chứ không học kỹ năng nghe nói chung. Điều đó có nghĩa là nếu bạn fine-tune cho ứng dụng của mình, mọi nhãn bạn quan tâm đều phải có trong dữ liệu train. Theo kết quả này (video đăng ngày 08/10/2026), model không thể tự khái quát hoá sang nhãn mới.

![Skill or labels: general skill bị gạch, your labels được tick; 40 lớp train từ 27.2% lên 70.0%; 10 lớp giấu từ 15.0% xuống 13.8%](https://homus.dev/photos/S7tFyREI19I-0903.jpg)

"Skill or labels?", theo số đo của Muhammad Farooq (video đăng ngày 08/10/2026): "general skill" bị gạch, "your labels" được tick. 40 lớp được train: top-1 từ 27.2 lên 70.0, top-5 từ 64.4 lên 94.7. 10 lớp bị giấu ("hidden 10"): top-1 từ 15.0 xuống 13.8, top-5 từ 26.2 lên 36.3.

## 12. Cái giá với phần còn lại của model

Giờ tới chuyện việc này tốn gì cho phần còn lại của model, tức là tác động thực sự khi bạn fine-tune. Anh load toàn bộ model, có và không có adapter, rồi chạy ba phép kiểm tra: photo search, voice search và general text search.

Theo số đo của Muhammad Farooq (video đăng ngày 08/10/2026), photo search và voice search không nhúc nhích, nhưng general text search giảm khoảng bốn điểm. Đó là cái giá của việc thay đổi backbone dùng chung. Vì vậy, nếu ứng dụng của bạn cũng dựa vào general text search, anh nói anh sẽ test lại nó sau mỗi lần fine-tune.

![What did it cost: base so với base cộng LoRA; photo search và voice search bằng nhau, NanoBEIR general text giảm; bảng số before và after; -3.6](https://homus.dev/photos/S7tFyREI19I-0943.jpg)

"What did it cost?", base so với base + LoRA, theo số đo của Muhammad Farooq (video đăng ngày 08/10/2026): photo R@1 86.3 thành 86.0, voice→photo R@1 95.0 thành 96.0, voice→photo R@5 99.0 giữ nguyên 99.0, còn general text trên NanoBEIR nDCG@10 từ 59.2 xuống 55.6 (con số -3.6 vẽ to bên trái). Dòng cuối: "re-test general text".

## 13. Một lỗi khi reload audio adapter

Lần đầu anh chạy phép kiểm tra này (theo video đăng ngày 08/10/2026), voice search tụt xuống gần như bằng không, trông như thể model đã quên cách nghe giọng nói. Anh đi qua từng bước một, và adapter weights được load hoàn toàn đúng. Vấn đề nằm ở processor được lưu cùng adapter. Trong trường hợp của anh, nó cắt mọi audio clip xuống chỉ còn vài mili giây, nên với model mọi âm thanh trông gần như giống nhau.

Cách sửa chỉ là một dòng: dùng lại processor của base model. Vì vậy nếu bạn lưu rồi load lại một audio adapter, ít nhất là với phiên bản anh dùng (trên màn hình ghi Unsloth 2026.10.2, video đăng ngày 08/10/2026), hãy test lại nó sau khi reload.

```
tuned[0].processor = base[0].processor
```

![A reload bug: thư mục esc50_lora có adapter weights đúng nhưng processor sai; base 499 frames, saved 3 frames; reloaded 2.0% so với 65.8%](https://homus.dev/photos/S7tFyREI19I-1023.jpg)

"A reload bug", theo video đăng ngày 08/10/2026: thư mục `esc50_lora/` có adapter weights đúng nhưng processor sai. Với một clip 5 giây, processor gốc cho 499 frame còn processor được lưu chỉ cho 3 frame, và model sau khi reload chỉ đạt 2.0% so với 65.8%. Dòng sửa `tuned[0].processor = base[0].processor` được tick, kèm quy trình "save → reload → test" và ghi chú "Unsloth 2026.10.2 · the version I used".

## 14. Ví dụ thứ hai: search trên video của chính kênh

Tiếp theo là ví dụ văn bản trong phần demo đầu video. Anh lấy nội dung lời nói từ 120 video của mình và cắt thành các đoạn văn ngắn; trên màn hình ghi 1.971 passages. Anh không có sẵn câu hỏi cho các đoạn này, nên anh dùng Gemini 3.8 Flash để viết cho mỗi đoạn một câu hỏi theo kiểu người xem sẽ hỏi (theo video đăng ngày 08/10/2026). Ví dụ trên màn hình: một đoạn từ video về Google File Search API nói về chuyện tính phí lưu trữ được biến thành câu hỏi "How is billing structured for storage…". Như vậy anh có các cặp câu hỏi và đoạn văn trả lời nó, đúng format đã nói ở trên.

Sau đó anh chia dữ liệu theo video (video đăng ngày 08/10/2026). Các câu hỏi test đến từ những video mà model không được train, tức là nó chưa thấy chúng trong lúc train. Trên màn hình: 96 video để train, 24 video để test.

Trước khi train, theo số đo của Muhammad Farooq (video đăng ngày 08/10/2026), đoạn văn đúng được trả về ở vị trí đầu khoảng hai lần trong ba. Sau chưa tới ba phút train (trên màn hình là 154 giây), con số này lên ba lần trong bốn. General text search lại giảm khoảng bốn điểm, cùng cái giá như ví dụ âm thanh trước đó.

![Cell train với epochs 3, lr 5e-5, batch 32; top-1 66.3 lên 75.0, top-10 91.6 lên 96.3, nDCG@10 79.7 lên 86.3, NanoBEIR 58.9 xuống 54.4; 154 s](https://homus.dev/photos/S7tFyREI19I-1123.jpg)

Kết quả trên video của kênh, theo số đo của Muhammad Farooq (video đăng ngày 08/10/2026, "my notebook run · A100"): train với `epochs = 3, lr = 5e-5, batch = 32`; top-1 từ 66.3 lên 75.0, top-10 từ 91.6 lên 96.3, nDCG@10 từ 79.7 lên 86.3, còn NanoBEIR từ 58.9 xuống 54.4. Bên phải: "2 in 3" thành "3 in 4", 154 giây (dưới 3 phút), và -4.5 cho general text, "same cost as sounds (-3.6)".

Đây chính là công cụ search ở đầu video. Và để dùng nó trên dữ liệu của bạn, theo Muhammad (video đăng ngày 08/10/2026), bạn chỉ cần một file gồm các câu hỏi và đoạn văn theo đúng format đó.

## 15. Câu trả lời và ba điều cần làm đúng

Vậy câu trả lời là có. Theo số đo của Muhammad Farooq (video đăng ngày 08/10/2026), vài phút fine-tune đã làm model tốt lên rõ rệt trên các âm thanh của anh và trên các video của anh. Nhưng nó học các nhãn của anh chứ không học kỹ năng chung, và nó mất vài điểm ở general text search.

Nếu bạn định fine-tune EmbeddingGemma 2, anh (video đăng ngày 08/10/2026) sẽ tập trung vào ba điều:

- Biến dữ liệu thành các cặp, và giữ cho các bản trùng không nằm chung một batch.

- Test trên dữ liệu model chưa thấy, nên bạn cần chia theo nguồn.

- Kiểm tra xem còn gì khác đã thay đổi sau khi train, vì backbone được dùng chung cho mọi modality (video đăng ngày 08/10/2026).

Ba điều Muhammad nhắc lại ở cuối video: train trên các cặp và tránh trùng lặp trong batch, test trên dữ liệu chưa thấy bằng cách chia theo nguồn, và kiểm tra những gì khác đã thay đổi vì backbone dùng chung.

Anh cho rằng đây là một trong những việc thực tế nhất bạn có thể làm với một embedding model, ít nhất là trong các thí nghiệm của anh (video đăng ngày 08/10/2026). Trên màn hình cuối: Unsloth, EmbeddingGemma 2 với LoRA, và Colab, kèm hai kết quả 24.5% → 65.8% và 66.3% → 75.0% (theo số đo của Muhammad Farooq, "my notebook runs · A100").

Anh mời ai đã fine-tune embedding model cho ứng dụng của mình chia sẻ điều gì hiệu quả trong phần bình luận, cảm ơn mọi người đã xem và hẹn gặp lại ở video sau.

## Nguồn và liên kết

- Tên gốc: Training Your Own Embedding Model Is Not As Hard As You Think
- Sự kiện: [YouTube](https://homus.dev/events/youtube)
- Video gốc: [https://www.youtube.com/watch?v=S7tFyREI19I](https://www.youtube.com/watch?v=S7tFyREI19I)

- Kênh YouTube Prompt Engineering (Muhammad Farooq): [youtube.com/channel/UCDq7SjbgRKty5TgGafW8Clg](https://www.youtube.com/channel/UCDq7SjbgRKty5TgGafW8Clg)

- Colab notebook dùng trong video (đọc ngày 23/10/2026): [colab.research.google.com](https://colab.research.google.com/drive/1xq-84DLZsqteEzL52Qb__47Y_yGckeyB?usp=sharing)

- Hướng dẫn EmbeddingGemma 2 của Unsloth (đọc ngày 23/10/2026): [unsloth.ai/docs/models/embeddinggemma-2](https://unsloth.ai/docs/models/embeddinggemma-2)

- Các notebook của Unsloth (đọc ngày 23/10/2026): [github.com/unslothai/notebooks](https://github.com/unslothai/notebooks)

- Model card EmbeddingGemma 2 trên Hugging Face (đọc ngày 23/10/2026): [huggingface.co/google/embeddinggemma-2](https://huggingface.co/google/embeddinggemma-2)

- Dataset ESC-50 (đọc ngày 23/10/2026): [github.com/karolpiczak/ESC-50](https://github.com/karolpiczak/ESC-50)

- Sentence Transformers, các loss (có `MultipleNegativesRankingLoss`, đọc ngày 23/10/2026): [sbert.net/docs/package_reference/sentence_transformer/losses.html](https://sbert.net/docs/package_reference/sentence_transformer/losses.html)

- Sentence Transformers, các batch sampler (có `NoDuplicatesBatchSampler`, đọc ngày 23/10/2026): [sbert.net/docs/package_reference/sentence_transformer/sampler.html](https://sbert.net/docs/package_reference/sentence_transformer/sampler.html)

Nguồn: https://homus.dev/talks/training-your-own-embedding-model-is-not-as-hard-as-you-think · cập nhật 2026-10-23
