شباهت کسینوسی، جهت دو بردار را مقایسه میکند: ضرب داخلی آنها را بر حاصلضرب طولشان تقسیم میکنیم. اگر هر دو بردار ناصفر باشند، مقدار نظری بین منفی یک و مثبت یک است. این عدد، میزان درستی یک پاسخ یا احتمال مرتبطبودن سند نیست؛ معنای کاربردی آن به نمایش داده و مسئله بستگی دارد.
چهار بردار آموزشی را مقایسه کنیم
بردار مبنا را a=[1,2] انتخاب کنیم. این دو عدد خروجی یک مدل زبان واقعی نیستند؛ برای قابل محاسبهبودن مثال انتخاب شدهاند. طول a برابر ریشه ۵ است. بردار b=[2,4] دو برابر a طول دارد ولی دقیقاً همجهت آن است؛ ضرب داخلی ۱۰ و حاصلضرب طولها نیز ۱۰ میشود، پس شباهت کسینوسی برابر ۱ است.
| بردار دوم | شباهت با [1,2] | نکته |
|---|---|---|
| [2,4] | ۱ | همجهت، با طول متفاوت |
| [-1,-2] | منفی ۱ | جهت مخالف |
| [2,-1] | صفر | ضرب داخلی صفر؛ دو جهت عمودند |
| [0,0] | در فرمول تعریف نشده | یکی از مخرجها صفر است |
صفرِ شباهت دو بردار ناصفر با بردار صفر یک مسئله واحد نیست. بعضی کتابخانهها برای بردار صفر قرارداد عددی خاصی دارند؛ تابع آموزشی این راهنما آن را با خطا رد میکند. پیش از مقایسه خروجی دو ابزار، قرارداد همین حالت را هم بخوانید.
چرا شباهت با فاصله یکسان نیست؟
فاصله اقلیدسی a تا b برابر ریشه ۵، یعنی حدود ۲٫۲۳۶ است، در حالی که شباهت کسینوسی آنها ۱ شد. یکی اختلاف مکان و اندازه مؤلفهها را میسنجد و دیگری جهت را پس از نرمالکردن طول مقایسه میکند. اگر مسئله واقعاً به اندازه بردار نیاز داشته باشد، حذف آن اطلاعات الزاماً انتخاب خوبی نیست.
برای دو بردار با طول واحد، مربع فاصله اقلیدسی برابر ۲ منهای دو برابر شباهت کسینوسی است. این رابطه را نباید بیقید به بردارهای خام با طول متفاوت تعمیم داد. همچنین برابرشدن اندازه دو آرایه به معنی یکسانبودن معنای محورهایشان نیست؛ بردارهای دو مدل متفاوت را صرفاً چون طول آرایه برابر است در یک فضای مشترک فرض نکنید.
کد مستقل با کنترل بردار صفر
import math
def unit(values):
if not values or not all(math.isfinite(x) for x in values):
raise ValueError('nonempty finite real vector required')
scale = max(abs(x) for x in values)
if scale == 0:
raise ValueError('cosine is undefined for a zero vector here')
scaled = [x/scale for x in values]
length = math.sqrt(math.fsum(x*x for x in scaled))
return [x/length for x in scaled]
def cosine(a, b):
if len(a) != len(b):
raise ValueError('vector dimensions differ')
ua, ub = unit(a), unit(b)
return max(-1.0, min(1.0, math.fsum(x*y for x,y in zip(ua, ub))))
a = [1.0, 2.0]
for b in ([2.0, 4.0], [-1.0, -2.0], [2.0, -1.0]):
print(b, round(cosine(a, b), 12))
assert abs(cosine(a, [2,4])-1) < 1e-12
assert abs(cosine(a, [-1,-2])+1) < 1e-12
assert abs(cosine(a, [2,-1])) < 1e-12
assert abs(cosine([1e308,1e308], [1e308,1e308])-1) < 1e-12
try:
cosine(a,[0,0])
except ValueError:
print('zero vector rejected')
else:
raise AssertionError('zero vector must be rejected')
تابع نخست، طول و محدودبودن اعداد را کنترل میکند؛ سپس پیش از محاسبه طول، همه مؤلفهها را به اندازه بزرگترین قدرمطلق تقسیم میکند. این مرحله مانع مربعکردن مستقیم مقدار بسیار بزرگی مثل ۱۰ به توان ۳۰۸ در این محاسبه میشود. این کد برای بردارهای حقیقی است و بردار مختلط یا مدل embedding را تولید نمیکند.
در جستوجوی متن چه احتیاطی لازم است؟
مدل embedding میتواند متن را به نمایش برداری تبدیل کند، اما محاسبه شباهت مرحله دیگری است. نمره ۰٫۸ در یک مدل و مجموعه اسناد، الزاماً با همین نمره در مدل دیگر قابل مقایسه نیست. آستانه مناسب را با سؤالهای واقعی، سند مورد انتظار و نمونههای بدون پاسخ ارزیابی کنید.
حتی سند بسیار نزدیک ممکن است منسوخ باشد یا درباره سیاست شعبه دیگری حرف بزند. انتخاب سنجه، جای کنترل نسخه سند و مجوز دسترسی را نمیگیرد. این تمرین فقط محاسبه عددی را بازتولید میکند و کیفیت هیچ محصولی را رتبهبندی نمیکند.
از عدد شباهت به تصمیم عملی
برای هر خطا بنویسید سؤال چه بود، کدام سند باید پیدا میشد و سند بالاآمده چرا مناسب نبود. بهجای کموزیادکردن حدسزده آستانه، همین موارد را در مجموعه مرجع بازیابی نگه دارید. اگر مقصد شما پاسخگویی مشتری است، اطلاعات مجاز و معیار پذیرش پاسخ را پیش از انتخاب راهکار در بررسی نیاز با اپرا مشخص کنید.
فایل تمرین و منابع
دریافت کد مستقل تمرین؛ بدون نصب بسته اضافه یا اتصال شبکه. ورودیها آموزشیاند و داده مشتری نیستند.
تاریخ خواندن منابع: ۳ نوامبر ۲۰۲۶. مثالها، کد و اعداد این راهنما برای آموزش طراحی شدهاند.