နားလည်ထားရမယ့် အချက်
Llama (သို့) Mistral လို chat model တစ်ခုကို job တစ်ခုတည်းအတွက် train လုပ်ထားပါတယ် — text အချို့ ပေးလိုက်ရင် နောက်ဖြစ်နိုင်ခြေရှိတဲ့ စကားလုံးကို predict လုပ်ဖို့ပါ။ embedding model ကတော့ လုံးဝ မတူညီတဲ့ job တစ်ခုအတွက် train လုပ်ထားပါတယ် — text အချို့ ပေးလိုက်ရင် vector လို့ခေါ်တဲ့ ဂဏန်း list တစ်ခု (fixed-length) ကို output ထုတ်ပေးပါတယ်, ဒါက ကွန်ပျူတာတစ်ခုက mathematically compare လုပ်နိုင်တဲ့ ပုံစံနဲ့ ဒီ text ရဲ့ အဓိပ္ပာယ်ကို ကိုယ်စားပြုပါတယ်။
ဒါက အရေးကြီးတာက embedding model ဆိုတာ chat model ငယ်တစ်ခု (သို့) ရိုးရှင်းတဲ့ version တစ်ခု မဟုတ်ဘဲ လုံးဝ model အမျိုးအစား မတူညီတာကြောင့်ပါ, ပုံမှန်အားဖြင့် Ollama ထဲက /api/embeddings လို ကိုယ်ပိုင် endpoint သီးသန့် ရှိပါတယ်, local server တစ်ခုတည်းထဲမှာ နေရင်တောင် ပါပဲ။
embedding ကောင်းတစ်ခုရဲ့ အဓိက property က အဓိပ္ပာယ် ဆင်တူတဲ့ text တွေဟာ vector ဆင်တူတွေ ရလာပြီး, ဆက်စပ်မှုမရှိတဲ့ text တွေဟာ စကားလုံးတူတူ တစ်ခုမှ မမျှဝေတောင် vector မတူညီတွေ ရလာတာပါ။ “The cat is sleeping” နဲ့ “a kitten is napping” ဆိုရင် vector space ထဲမှာ နီးနီးကပ်ကပ် ကျရောက်မှာဖြစ်ပြီး, “the cat is sleeping” နဲ့ “quarterly revenue increased” ကတော့ ဝေးဝေးကွာကွာ ကျရောက်ပါလိမ့်မယ်။
ဒီနီးကပ်မှုကို similarity score တစ်ခုနဲ့ တိုင်းတာပါတယ်, အများဆုံးက cosine similarity ဖြစ်ပြီး vector နှစ်ခုကြား angle ကိုသာ ကြည့်ပြီး length ကို လျစ်လျူရှုပါတယ်။
text ဘယ်ခုကိုမဆို vector အဖြစ် ပြောင်းလို့ရပြီး vector နှစ်ခု ဘယ်လောက်နီးကပ်လဲ တိုင်းတာနိုင်ပြီဆိုရင် keyword အတိအကျအစား အဓိပ္ပာယ်အပေါ် အခြေခံတဲ့ search engine တစ်ခု တည်ဆောက်နိုင်ပါပြီ, ဒါကပဲ Chroma (သို့) Qdrant လို vector database တစ်ခု တည်ဆောက်ထားတဲ့ ရည်ရွယ်ချက်ပါ — vector အများကြီးကို မူရင်း text နဲ့ အတူ သိမ်းထားပြီး query vector အသစ်တစ်ခု ပေးလိုက်ရင် အနီးစပ်ဆုံး top-K vector တွေကို မြန်မြန် ရှာပေးပါတယ်။
- Embedding
- embedding model တစ်ခုက text အပိုင်းတစ်ခုကနေ ထုတ်ပေးတဲ့ fixed-length ဂဏန်း vector တစ်ခုဖြစ်ပြီး အဓိပ္ပာယ် ဆင်တူတဲ့ text တွေဟာ vector ဆင်တူတွေ ရရှိအောင် နေရာချထားပါတယ်။
- Vector
- ဂဏန်းတွေရဲ့ fixed-length list တစ်ခုဖြစ်ပြီး, ဒီ context မှာဆိုရင် ကွန်ပျူတာက mathematically compare လုပ်နိုင်တဲ့ text အပိုင်းတစ်ခုရဲ့ အဓိပ္ပာယ်ကို ဂဏန်းအဖြစ် ကိုယ်စားပြုချက်ပါ။
- Vector Database
- vector အများကြီးကို မူရင်း text (သို့) data နဲ့ အတူ သိမ်းထားဖို့ တည်ဆောက်ထားတဲ့ database တစ်ခုဖြစ်ပြီး query vector အသစ်တစ်ခု ပေးလိုက်ရင် သိမ်းထားတဲ့ vector တွေထဲက အနီးစပ်ဆုံး top-K ကို မြန်မြန် ရှာပေးပါတယ်။
TEXT TO VECTOR TO TOP-K RESULTS
-------------------------------
------------------------------------------------------------
TEXT
"the cat is sleeping"
|
| embedding model
v
VECTOR
[0.91, 0.08, 0.02, 0.41, ...]
|
| compare against many stored vectors
| using cosine similarity
v
SIMILARITY SEARCH
score("cat is sleeping", "kitten is napping") = 0.99 (close)
score("cat is sleeping", "quarterly revenue") = 0.15 (far)
|
v
TOP-K RESULTS (the K stored texts with the highest scores)လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
note အနည်းငယ်ရာသာ ရှိတဲ့ personal knowledge base တစ်ခုအတွက် “meaning အလိုက် note ရှာဖွေခြင်း” feature တစ်ခု တည်ဆောက်တယ်လို့ ယူဆကြည့်ပါ။
Note တစ်ခုစီကို embed လုပ်ပြီး သိမ်းထားခြင်း
note တစ်ခုစီအတွက် embedding model ရဲ့ endpoint ကို တစ်ကြိမ် ခေါ်ပြီး ရလာတဲ့ vector ကို note ရဲ့ text နဲ့ ID နဲ့ အတူ — vector database အစစ်တစ်ခုထဲမှာ (သို့) ဒီအရွယ်အစားလောက်ဆိုရင် memory ထဲက list ရိုးရိုးလေးထဲမှာတောင် — သိမ်းထားနိုင်ပါတယ်။
Query ကိုပါ နည်းလမ်းတူတူနဲ့ embed လုပ်ခြင်း
user က search query ရိုက်ထည့်လိုက်ရင် ဒီ query ကိုလည်း နည်းလမ်းတူတူနဲ့ embed လုပ်ပါတယ်။
Compare လုပ်ပြီး top-K ကို ယူခြင်း
query ရဲ့ vector ကို သိမ်းထားတဲ့ vector တစ်ခုစီနဲ့ cosine similarity သုံးပြီး compare လုပ်, score အမြင့်ဆုံး top-K note တွေကို ရလဒ်အဖြစ် ယူပါတယ်။
အောက်က code exercise က ဒီ comparison step ကို external library လုံးဝ မသုံးဘဲ Python ရိုးရိုးနဲ့ ကိုယ်တိုင် implement ခိုင်းထားပါတယ်, embedding output အစစ်ကို ကိုယ်စားပြုတဲ့ hardcode vector ငယ်လေးတွေကို သုံးထားပါတယ်, real embedding model ကို မခေါ်ခင် mechanic ကို concrete ဖြစ်အောင် လုပ်ဖို့ ရည်ရွယ်ထားပါတယ်။
“အဓိပ္ပာယ် ဆင်တူတယ်” ဆိုတာ တကယ်တော့ “ဂဏန်း list နှစ်ခုကြား similarity score” ဆိုတာအထိ ကျဉ်းသွားတယ်ဆိုတာ တွေ့မြင်ပြီးရင် နောက်မှာ real embedding model နဲ့ real vector database ကို အစားထိုးတာက concept အသစ်မဟုတ်ဘဲ plumbing ကိစ္စတစ်ခုပဲ ဖြစ်ပါတော့မယ်, သင်ကိုယ်တိုင် လက်နဲ့ implement လုပ်ခဲ့တဲ့ underlying math ကိုပဲ library တွေက run ပေးနေတာမို့ပါ။
အတူတူ စမ်းရေးကြည့်မယ်
import math
def cosine_similarity(vector_a, vector_b):
dot_product = sum(a * b for a, b in zip(vector_a, vector_b))
magnitude_a = math.sqrt(sum(a * a for a in vector_a))
magnitude_b = math.sqrt(sum(b * b for b in vector_b))
return dot_product / (magnitude_a * magnitude_b)
if __name__ == "__main__":
# Toy 4-dimensional "embeddings" standing in for real model output.
cat = [0.9, 0.1, 0.0, 0.4]
kitten = [0.85, 0.15, 0.05, 0.35]
bicycle = [0.05, 0.9, 0.8, 0.1]
pairs = [
("cat", "kitten", cat, kitten),
("cat", "bicycle", cat, bicycle),
("kitten", "bicycle", kitten, bicycle),
]
for name_a, name_b, vec_a, vec_b in pairs:
score = cosine_similarity(vec_a, vec_b)
print(f"similarity({name_a}, {name_b}) = {score:.4f}")
script ကို run လိုက်ရင် pair တစ်ခုစီအတွက် similarity score သုံးခု print ထုတ်ပါတယ် — `similarity(cat, kitten) = 0.9964`, `similarity(cat, bicycle) = 0.1462`, `similarity(kitten, bicycle) = 0.2238`။ ဆင်တူအောင် တည်ဆောက်ထားတဲ့ ‘cat’ နဲ့ ‘kitten’ vector တွေက 1.0 နဲ့ နီးကပ်တဲ့ score ရရှိပြီး (အလွန်ဆင်တူ), ကွာခြားအောင် တည်ဆောက်ထားတဲ့ ‘bicycle’ ကတော့ နှစ်ခုစလုံးနဲ့ score နိမ့်နိမ့် ရရှိပါတယ်။၅ မိနစ် စမ်းကြည့်
`dog = [0.8, 0.2, 0.1, 0.5]` ဆိုတဲ့ vector လေးခုမြောက်ကို ထပ်ထည့်ပြီး `cat`, `kitten`, `bicycle` တို့နဲ့ cosine similarity ကို တွက်ကြည့်ပါ။ run မလုပ်ခင် ဘယ် pair က score အမြင့်ဆုံးဖြစ်မလဲ၊ ဘယ်ဟာက အနိမ့်ဆုံးဖြစ်မလဲ ကြိုတင် ခန့်မှန်းကြည့်ပါ, ပြီးမှ code ကို run ပြီး ခန့်မှန်းချက် မှန်မမှန် စစ်ဆေးပါ။
သတိလေးတစ်ချက်
embedding model နဲ့ chat model ကို အစားထိုးလို့ ရတယ်လို့ ယူဆခြင်း, (သို့) chat model ရဲ့ raw output ကို embedding vector အစစ်တစ်ခုအစား သုံးလို့ ရတယ်လို့ ထင်ခြင်း။
embedding model မတူညီတာ (သို့) dimension မတူညီတဲ့ vector တွေကို တိုက်ရိုက် compare လုပ်ခြင်း — cosine similarity ဆိုတာ embedding model တူတူကနေ ထွက်လာတဲ့ vector တွေကြားမှာသာ အဓိပ္ပာယ်ရှိပါတယ်။
Chroma documentation — Local AI / Local LLM