فهرست مقاله
پیش از انتخابِ معماریِ تولیدِ گفتار، باید بتوانیم یک بلوکِ کوچکِ محاسباتی را درست بخوانیم: چه برداری پرسش میکند، کدام جایگاهها در دسترساند و چه مقدارهایی با هم ترکیب میشوند؟ این درس هستهٔ توجهِ ضربِ داخلی را با ورودیهای تألیفی باز میکند. هدف، بازبینیِ محور و ماسک در کد است؛ صدای واقعی، شبکهٔ آموزشدیده و ارزیابیِ کیفیتِ گفتار در مثال نداریم.
۱. جایگاه و ویژگی را در Q، K و V جدا کنیم

هر سطرِ Q یک جایگاهِ پرسش و هر سطرِ K یک جایگاهِ کلید است. ستونهای این دو، ویژگیهای قابلِ ضرب با یکدیگرند؛ بنابراین عرضشان باید برابر باشد. سطرهای V با کلیدها همردیفاند، اما عرضِ V میتواند متفاوت باشد. در قراردادِ مربعِ حاضر، هر سه ماتریس تعدادِ سطرِ یکسان دارند و نتیجه بهازای هر پرسش، یک بردار با عرضِ V میدهد.
عددها ویژگیهای بیبعدِ ازپیشنوشتهشدهاند. Q و K را دو بردارِ واژهٔ آموزشدیده یا اندازهگیریِ فیزیکی معرفی نمیکنیم. برنامه یک تا چهار جایگاه، یک تا چهار ویژگی در Q و K و یک تا چهار مؤلفه در V میپذیرد. ظرفها دقیقاً tuple و عددها دقیقاً int در بازهٔ منفیِ هشت تا مثبتِ هشت هستند؛ bool و float به عددِ صحیح تبدیل نمیشوند.
درسِ چیدمانِ ورودی و وزنِ Conv1d نشان میداد که اندازهٔ قابلقبول، معنای محور را تضمین نمیکند. اینجا نیز جابهجاییِ جایگاه با ویژگی میتواند روی ماتریسِ مربعی پنهان بماند. نامِ محور و یک پاسخِ دستی لازماند؛ تغییرِ شکلِ ظاهری، جایگزینِ نگاشتِ درستِ مؤلفهها نیست.
۲. امتیاز را روی کلیدهای مجازِ همان سطر نرمال کنیم

برای پرسشِ i و کلیدِ j، امتیاز sᵢⱼ حاصلِ ضربِ داخلیِ دو سطر تقسیم بر ریشهٔ تعدادِ ویژگیهای کلید است. سپس وزنها در امتدادِ کلیدهای همان پرسش ساخته میشوند و خروجی از مجموعِ وزندارِ سطرهای V به دست میآید. بخشِ ۳٫۲٫۱ مقالهٔ Attention Is All You Need این هسته را تعریف میکند؛ یادگیریِ نگاشتهای Q، K و V مرحلهای جدا از ورودیهای آمادهٔ ماست.
در شاهدِ اول، هر سه سطرِ Q برابرِ [0,0] هستند. K سطرهای [1,0]، [0,1] و [1,1] دارد و V برابرِ [2,0]، [4,4] و [6,8] است. همهٔ امتیازها صفر میشوند؛ مقدارِ صفرِ امتیاز، اتصال را مسدود نمیکند. با دسترسیِ علّی، سطرِ نخست فقط وزنِ یک، سطرِ دوم دو وزنِ یکدوم و سطرِ سوم سه وزنِ یکسوم دارد. جوابها [2,0]، [3,2] و [4,4] هستند.
نرمالسازیِ ستونی، پرسشهای متفاوت را واردِ مخرجِ هم میکند. این محاسبه قراردادِ دیگری میسازد و حتی مجموعِ وزنهای هر پرسش الزاماً یک نمیماند. برای امتیازهای نابرابر نیز شاهد داریم: با یک ویژگی، Q=[1] و دو کلیدِ صفر و یک، وزنِ کلیدِ دوم e/(1+e)، حدودِ 0.731058579، است. با مقدارهای صفر و چهار، خروجی حدودِ 2.924234315 میشود.
تابعِ اصلی، بزرگترین امتیازِ مجازِ همان سطر را پیش از exp کم میکند؛ ضریبِ مشترک در صورت و مخرج حذف میشود. مستندِ math در Python 3.14، exp، sqrt و جمعِ fsum را تعریف میکند. در دامنهٔ حاضر، قدرمطلقِ امتیاز حداکثر ۶۴√d و با d≤4 حداکثر ۱۲۸ است؛ اختلاف از ۲۵۶ بیشتر نیست. توانهای مجاز مثبت و متناهی میمانند؛ این استدلال برای ورودیِ نامحدود نیست.
۳. ماسکِ علّی و سطرِ بدونِ کلید، قرارداد میخواهند

ماسکِ دادهشده، ماتریسِ مربعِ bool است و True یعنی اجازهٔ شرکت. وقتی causal=True باشد، شرطِ j≤i نیز اعمال میشود؛ جایگاهِ جاری در دسترس است. اگر خروجیِ مدل قرار است نمادِ بعدی را پیشبینی کند، همترازی و جابهجاییِ هدف موضوعی جداست. از قطری بودنِ اجازه نمیتوان نتیجه گرفت که ورودیِ آینده یا پاسخِ هدف در کلِ خطِ پردازش نشت نکرده است.
رابطِ scaled_dot_product_attention در PyTorch 2.14، True را برای مشارکت تعریف میکند و برای ماتریسِ مربع، ماسکِ علّیِ پایینمثلثی را شرح میدهد. همان مستند، دادنِ همزمانِ attn_mask و is_causal را رد میکند. اشتراکِ دو محدودیت در تابعِ آموزشیِ ما، سیاستِ رابطِ خودمان است؛ اجرای این تابع، فراخوانی یا آزمونِ سازگاریِ آن API محسوب نمیشود.
ماسک باید پیش از نرمالسازی در مجموعهٔ مجاز اثر کند. در شاهدِ سهکلیدیِ صفر، اگر ابتدا وزنهای یکسوم بسازیم و سپس دو کلید را صفر کنیم، وزنِ باقیمانده یکسوم میماند. روشِ درست برای تنها کلیدِ مجاز، وزنِ یک است. ضربِ صرفِ خروجیِ softmax در ماسک، بدونِ محاسبهٔ دوبارهٔ مخرج، مقدارِ ترکیب را کوچک میکند.
پیش از حساب، همهٔ سطرهای مجاز ساخته و بررسی میشوند. سطری که پس از اشتراک هیچ کلیدی ندارد ValueError میدهد؛ برنامه خروجیِ صفر، احتمالِ دلخواه یا معنای «سکوت» برایش نمیسازد. ممکن است ماسکِ دادهشده کلیدی را اجازه دهد که با شرطِ علّی حذف شود؛ بنابراین غیرتهی بودنِ خودِ ماسک کافی نیست. این تصمیمِ صریحِ مثال است و رفتارِ عمومیِ همهٔ کتابخانهها فرض نشده است.
۴. کدِ کامل و مرجعِ اعشاریِ مستقل

فایلِ کامل با کتابخانهٔ استانداردِ پایتون اجرا شده است؛ نسخهٔ دقیق در خروجی ثبت میشود. ماتریسهای نامستطیلی، عرضِ ناسازگارِ Q و K، شمارِ جایگاهِ متفاوت، عددِ خارج از بازه و عضوِ غیربولیِ ماسک رد میشوند. ورودیها تغییر نمیکنند و هر دو خروجی، وزنها و بردارهای نهایی، به شکلِ tuple برمیگردند. وزنِ مسیرِ مسدود دقیقاً صفر است.
"""Bounded square single-head attention, own mask policy; no framework call."""
from decimal import Decimal, localcontext
from itertools import permutations
from math import exp, fsum, isclose, isfinite, sqrt
import json
import platform
MAX_POSITIONS = 4
MAX_FEATURES = 4
MAX_ABS_VALUE = 8
REL_TOL = 2e-12
ABS_TOL = 2e-12
DECIMAL_PRECISION = 80
def matrix(value, name):
if type(value) is not tuple or not 1 <= len(value) <= MAX_POSITIONS:
raise ValueError(name + ' must be an exact tuple with 1..4 rows')
width = None
for row in value:
if type(row) is not tuple or not 1 <= len(row) <= MAX_FEATURES:
raise ValueError(name + ' rows must be exact tuples with 1..4 features')
if width is None:
width = len(row)
if len(row) != width:
raise ValueError(name + ' must be rectangular')
if any(type(x) is not int or abs(x) > MAX_ABS_VALUE for x in row):
raise ValueError(name + ' values must be exact ints in [-8,8]')
return len(value), width
def checked_contract(q, k, v, allowed, causal):
n, d = matrix(q, 'Q')
nk, dk = matrix(k, 'K')
nv, dv = matrix(v, 'V')
if nk != n or nv != n or dk != d:
raise ValueError('square position counts and Q/K feature widths must match')
if type(causal) is not bool:
raise ValueError('causal must be an exact bool')
if allowed is None:
allowed = tuple((True,) * n for _ in range(n))
if type(allowed) is not tuple or len(allowed) != n:
raise ValueError('allowed must be an exact square tuple mask')
for row in allowed:
if (type(row) is not tuple or len(row) != n
or any(type(x) is not bool for x in row)):
raise ValueError('mask entries must be exact bools')
legal = tuple(tuple(allowed[i][j] and (not causal or j <= i)
for j in range(n)) for i in range(n))
if any(not any(row) for row in legal):
raise ValueError('every query must retain at least one legal key')
return n, d, dv, legal
def attention(q, k, v, allowed=None, causal=True):
"""Return weights and outputs; intersect masks in this OWN teaching API."""
n, d, dv, legal = checked_contract(q, k, v, allowed, causal)
weights, output = [], []
for i in range(n):
scores = tuple(sum(q[i][a] * k[j][a] for a in range(d)) / sqrt(d)
for j in range(n))
maximum = max(scores[j] for j in range(n) if legal[i][j])
raw = tuple(exp(scores[j] - maximum) if legal[i][j] else 0.0
for j in range(n))
denominator = fsum(raw)
row = tuple(x / denominator for x in raw)
weights.append(row)
output.append(tuple(fsum(row[j] * v[j][a] for j in range(n))
for a in range(dv)))
return tuple(weights), tuple(output)
def decimal_oracle(q, k, v, allowed=None, causal=True):
"""Valid fixtures only: raw Decimal exponentials, no main helpers or shift."""
with localcontext() as context:
context.prec = DECIMAL_PRECISION
n, width = len(q), len(q[0])
scale = Decimal(width).sqrt()
weight_rows, answer = [], []
for query_index, query in enumerate(q):
unnormalized = [Decimal(0)] * n
for key_index, key in enumerate(k):
if causal and key_index > query_index:
continue
if allowed is not None and not allowed[query_index][key_index]:
continue
dot = Decimal(0)
for left, right in zip(query, key):
dot += Decimal(left) * Decimal(right)
unnormalized[key_index] = (dot / scale).exp()
total = sum(unnormalized, Decimal(0))
row = [value / total for value in unnormalized]
result = []
for column in zip(*v):
numerator = sum((a * Decimal(b) for a, b in zip(unnormalized, column)),
Decimal(0))
result.append(float(numerator / total))
weight_rows.append(tuple(float(value) for value in row))
answer.append(tuple(result))
return tuple(weight_rows), tuple(answer)
def close_rows(left, right):
return (len(left) == len(right) and all(
len(a) == len(b) and all(isclose(x, y, rel_tol=REL_TOL, abs_tol=ABS_TOL)
for x, y in zip(a, b)) for a, b in zip(left, right)))
def agrees(left, right):
return close_rows(left[0], right[0]) and close_rows(left[1], right[1])
def rejected(q, k, v, allowed=None, causal=True):
try:
attention(q, k, v, allowed, causal)
except ValueError:
return True
return False
def run():
q = ((0, 0),) * 3
k = ((1, 0), (0, 1), (1, 1))
v = ((2, 0), (4, 4), (6, 8))
baseline = attention(q, k, v)
checks = {}
checks['literal_uniform_causal_rows_and_hand_outputs'] = (
close_rows(baseline[0], ((1, 0, 0), (0.5, 0.5, 0), (1/3, 1/3, 1/3)))
and close_rows(baseline[1], ((2, 0), (3, 2), (4, 4))))
unequal = attention(((1,), (1,)), ((0,), (1,)), ((0,), (4,)), causal=False)
logistic = exp(1) / (1 + exp(1))
checks['unequal_two_key_analytic_logistic_weight'] = (
close_rows(unequal[0], ((1-logistic, logistic),) * 2)
and close_rows(unequal[1], ((4*logistic,),) * 2))
scaled = attention(((1, 1, 1, 1),) * 2, ((0, 0, 0, 0), (1, 1, 1, 1)),
((0,), (4,)), causal=False)
good_weight = exp(2) / (1 + exp(2))
unscaled_wrong = exp(4) / (1 + exp(4))
checks['key_feature_count_sqrt_scale_not_positions_or_value_width'] = (
close_rows(scaled[0], ((1-good_weight, good_weight),) * 2)
and not isclose(good_weight, unscaled_wrong, rel_tol=REL_TOL, abs_tol=ABS_TOL))
oracle_cases, oracle_ok = 0, True
fixtures = []
for n in range(1, 5):
for d in range(1, 5):
for dv in range(1, 4):
for seed in range(3):
qx = tuple(tuple((i*3+a+seed) % 5-2 for a in range(d)) for i in range(n))
kx = tuple(tuple((j*2-a+seed) % 5-2 for a in range(d)) for j in range(n))
vx = tuple(tuple((j+a*2+seed) % 7-3 for a in range(dv)) for j in range(n))
mask = tuple(tuple(i == j or (i+j+seed) % 3 != 0 for j in range(n))
for i in range(n))
for causal in (False, True):
result = attention(qx, kx, vx, mask, causal)
oracle_ok &= agrees(result, decimal_oracle(qx, kx, vx, mask, causal))
fixtures.append((result, vx, mask, causal))
oracle_cases += 1
checks['independent_unshifted_decimal_288_fixtures_ONE_group'] = bool(
oracle_ok and oracle_cases == 288)
changed_future = attention(q[:2] + ((8, -8),), k[:2] + ((-8, 8),),
v[:2] + ((-8, -8),))
checks['future_query_key_and_value_changes_leave_earlier_rows_unchanged'] = (
changed_future[0][:2] == baseline[0][:2] and changed_future[1][:2] == baseline[1][:2])
identity_mask = tuple(tuple(i == j for j in range(3)) for i in range(3))
selected = attention(q, k, v, identity_mask, causal=False)
checks['one_allowed_key_returns_its_value_not_zeroed_mixture'] = (
selected[0] == tuple(tuple(float(x) for x in row) for row in identity_mask)
and selected[1] == v)
supplied = ((True, True, True), (True, False, True), (False, True, True))
intersection = attention(q, k, v, supplied)
checks['own_mask_intersection_causal_and_supplied_permissions'] = (
intersection[0] == ((1.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.5, 0.5))
and intersection[1] == ((2.0, 0.0), (2.0, 0.0), (5.0, 6.0)))
all_open = attention(q, k, v, causal=False)
after_mask_wrong = tuple(tuple(all_open[0][i][j] if identity_mask[i][j] else 0.0
for j in range(3)) for i in range(3))
checks['zeroing_after_softmax_without_renormalization_loses_unit_row_sum'] = (
all(isclose(fsum(row), 1/3, rel_tol=REL_TOL, abs_tol=ABS_TOL) for row in after_mask_wrong)
and not close_rows(after_mask_wrong, selected[0]))
column_wrong = ((0.5, 1/(1+exp(1))), (0.5, exp(1)/(1+exp(1))))
axis_fixture = attention(((1,), (2,)), ((0,), (1,)), ((0,), (4,)), causal=False)
checks['softmax_key_axis_is_not_query_axis'] = (
not close_rows(axis_fixture[0], column_wrong)
and all(isclose(fsum(row), 1.0, abs_tol=ABS_TOL) for row in axis_fixture[0])
and not isclose(fsum(column_wrong[0]), 1.0, abs_tol=ABS_TOL))
qn = ((1, -1), (0, 2), (-2, 1))
original = attention(qn, k, v, supplied, False)
permutation_ok = True
for order in permutations(range(3)):
kp, vp = tuple(k[j] for j in order), tuple(v[j] for j in order)
mp = tuple(tuple(row[j] for j in order) for row in supplied)
permuted = attention(qn, kp, vp, mp, False)
permutation_ok &= close_rows(original[1], permuted[1])
rp = attention(tuple(qn[i] for i in order), k, v,
tuple(supplied[i] for i in order), False)
permutation_ok &= close_rows(rp[1], tuple(original[1][i] for i in order))
checks['consistent_key_value_mask_permutation_and_query_row_permutation'] = bool(permutation_ok)
swapped = attention(tuple(tuple(reversed(row)) for row in qn),
tuple(tuple(reversed(row)) for row in k), v, supplied, False)
checks['same_QK_feature_permutation_preserves_dots_and_outputs'] = agrees(original, swapped)
small_values = ((1, -1), (0, 2), (-2, 0))
value_base = attention(qn, k, small_values, supplied, False)
transformed_values = tuple(tuple(2*x+1 for x in row) for row in small_values)
transformed = attention(qn, k, transformed_values, supplied, False)
checks['fixed_scores_affine_value_transform_preserves_weights'] = (
value_base[0] == transformed[0] and close_rows(transformed[1],
tuple(tuple(2*x+1 for x in row) for row in value_base[1])))
hull_ok = True
for result, vals, mask, causal in fixtures:
for i, (row, answer) in enumerate(zip(*result)):
legal = [j for j in range(len(vals)) if mask[i][j] and (not causal or j <= i)]
hull_ok &= isclose(fsum(row), 1.0, rel_tol=REL_TOL, abs_tol=ABS_TOL)
hull_ok &= all(isfinite(x) and 0 <= x <= 1 for x in row)
hull_ok &= all(row[j] > 0 if j in legal else row[j] == 0 for j in range(len(vals)))
hull_ok &= all(min(vals[j][a] for j in legal)-ABS_TOL <= x
<= max(vals[j][a] for j in legal)+ABS_TOL for a, x in enumerate(answer))
checks['legal_positive_weights_blocked_exact_zeros_and_coordinate_convex_hull'] = bool(hull_ok)
unit = ((0,),)
class IntChild(int):
pass
class TupleChild(tuple):
pass
invalid = [([], unit, unit), ((), unit, unit), (((0,), (0, 1)), unit, unit),
(([0],), unit, unit), (TupleChild(unit), unit, unit),
(((True,),), unit, unit), (((0.0,),), unit, unit),
(((IntChild(0),),), unit, unit), ((('0',),), unit, unit),
(((float('nan'),),), unit, unit), (((float('inf'),),), unit, unit),
(((9,),), unit, unit), (((-9,),), unit, unit),
(((0,)*5,), unit, unit), (unit*5, unit*5, unit*5),
(unit, ((0, 0),), unit), (unit, unit*2, unit), (unit, unit, unit*2),
(unit, unit, ((False,),)), (unit, unit, unit, [[True]]),
(unit, unit, unit, ((1,),)), (unit, unit, unit, ((True, False),)),
(unit, unit, unit, ((True,),), 1)]
checks['strict_types_rectangular_shapes_shared_axes_and_resource_caps'] = all(
rejected(*args) for args in invalid)
no_key = ((False, True), (True, True))
checks['all_blocked_rows_rejected_including_causal_mask_intersection'] = (
rejected(unit, unit, unit, ((False,),), False)
and rejected(unit*2, unit*2, unit*2, no_key, True)
and not rejected(unit*2, unit*2, unit*2, no_key, False))
extreme_q = ((8, 8, 8, 8),) * 4
extreme_k = ((-8,)*4, (8,)*4, (-8,)*4, (8,)*4)
extreme_v = ((-8, 8, -8, 8), (8, -8, 8, -8)) * 2
before = repr((extreme_q, extreme_k, extreme_v))
extreme = attention(extreme_q, extreme_k, extreme_v, causal=False)
checks['maximum_profile_positive_finite_extremes_and_input_preservation'] = (
agrees(extreme, decimal_oracle(extreme_q, extreme_k, extreme_v, causal=False))
and all(isfinite(x) and x > 0 for row in extreme[0] for x in row)
and all(isfinite(x) for row in extreme[1] for x in row)
and before == repr((extreme_q, extreme_k, extreme_v))
and type(extreme) is tuple and all(type(batch) is tuple for batch in extreme))
assert len(checks) == 16 and all(type(value) is bool and value for value in checks.values()), checks
rounded = lambda rows: [[round(x, 12) for x in row] for row in rows]
return {'python_version': platform.python_version(), 'implementation': platform.python_implementation(),
'dependencies': 'standard library only; PyTorch not executed',
'profile': {'positions': '1..4, equal Q/K/V row counts', 'QK_width': '1..4, equal',
'V_width': '1..4 independent', 'values': 'exact ints [-8,8]; unitless',
'containers': 'exact tuples', 'mask': 'exact bools; True=allowed',
'mask_combination': 'OWN intersection policy, not a PyTorch API call',
'empty_legal_row': 'ValueError', 'dropout_and_learned_projections': False},
'input_Q': q, 'input_K': k, 'input_V': v,
'causal_weights_rounded_12': rounded(baseline[0]),
'causal_output_rounded_12': rounded(baseline[1]),
'unequal_weights_rounded_12': rounded(unequal[0]),
'unequal_output_rounded_12': rounded(unequal[1]),
'scaled_d4_second_key_weight': round(good_weight, 12),
'wrong_unscaled_second_key_weight': round(unscaled_wrong, 12),
'intersection_weights': intersection[0], 'intersection_output': intersection[1],
'wrong_mask_after_softmax_row_sums': [fsum(row) for row in after_mask_wrong],
'largest_abs_scaled_score_bound': 128, 'largest_score_difference_bound': 256,
'oracle_fixtures_inside_ONE_group': oracle_cases,
'invalid_cases_inside_ONE_group': len(invalid),
'decimal_precision_digits': DECIMAL_PRECISION, 'absolute_tolerance': ABS_TOL,
'relative_tolerance': REL_TOL, 'unique_check_groups': len(checks), 'checks': checks,
'limitations': ['Supplied unitless integer features, no speech data or learned model.',
'One head; square positions only, no KV cache, dropout or gradient.',
'No complete TTS architecture, task quality, timing or hardware result.']}
if __name__ == '__main__':
print(json.dumps(run(), ensure_ascii=True, indent=2, allow_nan=False))
مرجعِ مستقل با Decimal و زمینهٔ محلی، ریشه و نمایِ خام را با دقتِ ۸۰ رقم حساب میکند. در این مسیر، بیشینهای کم نمیشود؛ مخرج از مجموعِ exp امتیازهای اصلی و صورتِ خروجی از مجموعِ نمایِ خام ضربدر V ساخته میشود. مرجع از تابع، اعتبارسنج و ساختِ ماسکِ مسیرِ اصلی استفاده نمیکند. این مرجعِ عددیِ دقیقتر است؛ ریشه و نمایِ نامتناهی را دقیقِ کسری نمینامیم.
تلورانسِ مطلق و نسبی هر دو 2e−12 هستند. ۲۸۸ شاهد با طول، عرضِ کلید، عرضِ مقدار، ماسک و حالتِ علّیِ متفاوت در یک گروه با مرجع سنجیده میشوند. پانزده گروهِ دیگر هدفهای جدا دارند: پاسخِ دستی، ضریبِ ریشه، آینده، کلیدِ منفرد، اشتراک، محور، جایگشت، تبدیلِ مقدار، محدودهٔ ترکیب و ورودیهای مردود. شمارِ شاهدهای درونی را تعدادِ گروههای مستقل معرفی نمیکنیم.
{
"python_version": "3.14.5",
"implementation": "CPython",
"dependencies": "standard library only; PyTorch not executed",
"profile": {
"positions": "1..4, equal Q/K/V row counts",
"QK_width": "1..4, equal",
"V_width": "1..4 independent",
"values": "exact ints [-8,8]; unitless",
"containers": "exact tuples",
"mask": "exact bools; True=allowed",
"mask_combination": "OWN intersection policy, not a PyTorch API call",
"empty_legal_row": "ValueError",
"dropout_and_learned_projections": false
},
"input_Q": [
[
0,
0
],
[
0,
0
],
[
0,
0
]
],
"input_K": [
[
1,
0
],
[
0,
1
],
[
1,
1
]
],
"input_V": [
[
2,
0
],
[
4,
4
],
[
6,
8
]
],
"causal_weights_rounded_12": [
[
1.0,
0.0,
0.0
],
[
0.5,
0.5,
0.0
],
[
0.333333333333,
0.333333333333,
0.333333333333
]
],
"causal_output_rounded_12": [
[
2.0,
0.0
],
[
3.0,
2.0
],
[
4.0,
4.0
]
],
"unequal_weights_rounded_12": [
[
0.26894142137,
0.73105857863
],
[
0.26894142137,
0.73105857863
]
],
"unequal_output_rounded_12": [
[
2.92423431452
],
[
2.92423431452
]
],
"scaled_d4_second_key_weight": 0.880797077978,
"wrong_unscaled_second_key_weight": 0.982013790038,
"intersection_weights": [
[
1.0,
0.0,
0.0
],
[
1.0,
0.0,
0.0
],
[
0.0,
0.5,
0.5
]
],
"intersection_output": [
[
2.0,
0.0
],
[
2.0,
0.0
],
[
5.0,
6.0
]
],
"wrong_mask_after_softmax_row_sums": [
0.3333333333333333,
0.3333333333333333,
0.3333333333333333
],
"largest_abs_scaled_score_bound": 128,
"largest_score_difference_bound": 256,
"oracle_fixtures_inside_ONE_group": 288,
"invalid_cases_inside_ONE_group": 23,
"decimal_precision_digits": 80,
"absolute_tolerance": 2e-12,
"relative_tolerance": 2e-12,
"unique_check_groups": 16,
"checks": {
"literal_uniform_causal_rows_and_hand_outputs": true,
"unequal_two_key_analytic_logistic_weight": true,
"key_feature_count_sqrt_scale_not_positions_or_value_width": true,
"independent_unshifted_decimal_288_fixtures_ONE_group": true,
"future_query_key_and_value_changes_leave_earlier_rows_unchanged": true,
"one_allowed_key_returns_its_value_not_zeroed_mixture": true,
"own_mask_intersection_causal_and_supplied_permissions": true,
"zeroing_after_softmax_without_renormalization_loses_unit_row_sum": true,
"softmax_key_axis_is_not_query_axis": true,
"consistent_key_value_mask_permutation_and_query_row_permutation": true,
"same_QK_feature_permutation_preserves_dots_and_outputs": true,
"fixed_scores_affine_value_transform_preserves_weights": true,
"legal_positive_weights_blocked_exact_zeros_and_coordinate_convex_hull": true,
"strict_types_rectangular_shapes_shared_axes_and_resource_caps": true,
"all_blocked_rows_rejected_including_causal_mask_intersection": true,
"maximum_profile_positive_finite_extremes_and_input_preservation": true
},
"limitations": [
"Supplied unitless integer features, no speech data or learned model.",
"One head; square positions only, no KV cache, dropout or gradient.",
"No complete TTS architecture, task quality, timing or hardware result."
]
}
وقتی امتیازها ثابتاند، تبدیلِ همهٔ Vها به 2V+1، خروجی را نیز به 2Y+1 میبرد؛ وزنها عوض نمیشوند. این رابطه با تغییرِ Q یا K الزاماً برقرار نیست. همچنین هر مؤلفهٔ خروجی میانِ کمینه و بیشینهٔ مؤلفههای مقدارهای مجاز قرار میگیرد. درسِ LayerNorm روی محورِ ویژگی تبدیلِ دیگری را بررسی میکند؛ افزودنِ آن به ورودی، قراردادِ امتیاز را تغییر میدهد و باید جدا سنجیده شود.
۵. چهار پرسش برای بازبینیِ هستهٔ توجه

پرسش اول: در شاهدِ صفر، چرا خروجیِ سطرِ دوم [3,2] است؟ پاسخ: فقط کلیدهای صفر و یک مجازند؛ امتیازهای برابر دو وزنِ یکدوم میدهند. میانگینِ [2,0] و [4,4]، برابرِ [3,2] است. سطرِ سومِ V در این مخرج و صورت سهم ندارد؛ صفر بودنِ Q به معنای خروجیِ صفر نیست.
پرسش دوم: اگر در سطرِ آخر فقط کلیدهای یک و دو مجاز باشند چه میشود؟ پاسخ: با همان امتیازهای صفر، وزنها [0,1/2,1/2] و جواب [5,6] است. این مقدار از میانگینِ [4,4] و [6,8] به دست میآید. حذفِ کلیدِ صفر، مخرج را نیز از سه سهم به دو سهم تغییر میدهد.
پرسش سوم: با چهار ویژگیِ یک در پرسش، کلیدِ صفر و کلیدِ سراسر یک، وزنِ دوم چقدر است؟ پاسخ: ضربهای داخلی صفر و چهارند و تقسیم بر √4 امتیازهای صفر و دو میدهد. وزنِ دوم e²/(1+e²)، حدودِ 0.880797078، است؛ حذفِ مقیاس آن را حدودِ 0.982013790 میکند. عرضِ V یا تعدادِ دو کلید، جایگزینِ d در این ریشه نیست.
پرسش چهارم: آیا افزودنِ اپسیلون به مخرج، سطرِ کاملاً بسته را معتبر میکند؟ پاسخ: خیر؛ مجموعهٔ مشارکتکننده هنوز تهی است و بردارِ وزنِ واحدی نداریم. قراردادِ حاضر آن را پیش از محاسبه رد میکند. سیاستِ دیگری مانند مسیرِ جایگزین باید جدا تعریف شود؛ صفرِ ساختهشده را نمیتوان نتیجهٔ توجه یا تشخیصِ سکوت معرفی کرد.
منابع در ۲۸ مهر ۱۴۰۵ خوانده شدند؛ مستنداتِ چارچوب نسخهٔ 2.14 و مستنداتِ پایتون شاخهٔ 3.14 هستند. تاریخِ نخستین انتشارِ صفحههای مستند معلوم فرض نشده است؛ نسخهٔ v7 مقاله تاریخِ ۲ اوتِ ۲۰۲۳ دارد و خبرِ امروز نیست. این هسته، تکسر و مربع است؛ آموزش، نگاشتِ یادگرفتنی، حافظهٔ KV، dropout و سنجشِ سرعت ندارد. موفقیتِ بررسیها، کیفیتِ متنبهگفتار یا معماریِ کاملِ محصول را تأیید نمیکند.
منابع و مطالعهٔ بیشتر
- Attention Is All You Need؛ بخشهای۳٫۲٫۱ و۳٫۲٫۳
- PyTorch2.14؛ هستهٔ توجه، محور و معنای ماسک
- Python3.14؛ exp، sqrt، fsum و isclose
- Python3.14؛ Decimal.exp، sqrt و localcontext
بررسی منابع: ۲۸ مهر ۱۴۰۵. تحلیلها و پیشنهادهای اجرایی، جمعبندی نویسندهاند.

