اگر آرایهٔ ویژگی و وزنِ یک لایه شکلِ قابل‌قبولی داشته باشند، آیا خروجی هم درست است؟ ممکن است محور کانال با طول جابه‌جا شود، وزن‌ها با همان ترتیب قبلی بمانند یا کرنل بی‌دلیل وارونه شود. این درس با یک همبستگی لغزشیِ دوکاناله، معنای محورهای ورودی و وزن را می‌آزماید. عددها صحیح و ساختگی‌اند؛ مدل آموزش‌دیده، صدای واقعی و اجرای چارچوب یادگیری در آزمایش نداریم.

۱. محور کانال، طول و خروجی را صریح نام‌گذاری کنیم

نام‌گذاری محور از جابه‌جایی زمان و کانال جلوگیری می‌کند
تصویر مفهومی: نام‌گذاری محور از جابه‌جایی زمان و کانال جلوگیری می‌کند؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

در قراردادِ نخست، ورودی NCL است: شمارهٔ نمونهٔ دسته، کانالِ ورودی، سپس جایگاهِ طول. یک نمونهٔ دسته، دو کانال و پنج جایگاه داریم. کانال اول یک، دو، چهار، هشت و شانزده است؛ کانال دوم سه، یک، صفر، منفی یک و دو. این‌ها ویژگیِ بی‌بعدند؛ پنج جایگاه را میلی‌ثانیه یا پنج نمونهٔ صوتِ کالیبره‌شده معرفی نمی‌کنیم.

وزنِ OIK سه محور دارد: خروجی، ورودی و جایگاهِ کرنل. دو خروجی و کرنلِ سه‌عضوی انتخاب کرده‌ایم؛ هر خروجی برای هر دو کانال ورودی وزن جدا دارد. تعریف Conv1d در PyTorch 2.14 همین ترتیبِ وزن را در حالتِ گروهِ یک مشخص می‌کند. وزن‌های این درس از قبل نوشته شده‌اند، نه اینکه شبکه آن‌ها را آموخته باشد.

در خروجی اول، وزنِ کانال اول یک، صفر و منفی یک و وزنِ کانال دوم دو، صفر و یک است؛ بایاس یک می‌گیریم. در خروجی دوم، وزن‌ها صفر، یک، دو و منفی یک، صفر، یک‌اند؛ بایاس منفی دو است. قرارداد محورهای آرایهٔ صوت مقدماتِ تشخیص کانال و زمان را روشن می‌کرد؛ اینجا وزنِ سه‌محوری و جمعِ سهمِ چند کانال هم باید سازگار بمانند.

۲. همبستگی لغزشی معتبر، کرنل را وارونه نمی‌کند

پنجرهٔ لغزشی ترتیب هستهٔ نامتقارن را حفظ می‌کند
تصویر مفهومی: پنجرهٔ لغزشی ترتیب هستهٔ نامتقارن را حفظ می‌کند؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

پنجرهٔ کرنل را از جایگاه صفر تا دوِ ورودی می‌گذاریم و سپس هر بار یک خانه جلو می‌رویم. برای هر خروجی، حاصل‌ضربِ هر عضوِ پنجره در وزنِ هم‌جایگاه جمع می‌شود؛ سهمِ هر دو کانال و فقط یک بایاسِ آن خروجی را می‌افزاییم. این عمل، همبستگیِ لغزشی است؛ ترتیبِ کرنل در حینِ حرکت عوض نمی‌شود.

حالت معتبر، هیچ نمونهٔ مجازی به لبه‌ها اضافه نمی‌کند. طول خروجی در این قرارداد، طول ورودی منهای طول کرنل به‌اضافهٔ یک است؛ پنج و سه، سه جایگاه خروجی می‌دهند. گام و فاصلهٔ نقاط کرنل هر دو یک‌اند؛ گروه‌بندی، padding و تابعِ فعال‌سازی نداریم. این محدودیت‌ها را از نامِ کلیِ «کانولوشن» نمی‌توان حدس زد.

برای نخستین مقدارِ خروجی اول، سهمِ کانال اول یک منهای چهار، یعنی منفی سه است. سهمِ کانال دوم شش به‌اضافهٔ صفر، یعنی شش، می‌شود؛ با بایاس یک، پاسخ چهار است. اگر وزن‌های هر کانال را در راستای کرنل وارونه کنیم، همین مقدار هفت می‌شود. این شاهدِ نامتقارن، خطای ترتیب را آشکار می‌کند؛ طول و شکل در هر دو مسیر یکسان‌اند.

۳. ورودی و وزن را با هم به چیدمان تازه ببریم

چیدمان تازه به جابه‌جایی هماهنگ دو قاب نیاز دارد
تصویر مفهومی: چیدمان تازه به جابه‌جایی هماهنگ دو قاب نیاز دارد؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

ورودیِ دوم NLC است؛ در هر جایگاه طول، دو کانال کنار هم قرار می‌گیرند: جفت‌های یک و سه، دو و یک، چهار و صفر، هشت و منفی یک، شانزده و دو. هم‌زمان، وزن باید از OIK به KIO برود. فقط جابه‌جاییِ ورودی و باقی‌گذاشتنِ وزن با معنای قبلی کافی نیست.

توضیح رسمی tf.nn.conv1d در کد TensorFlow v2.16.1، ورودیِ کانال‌آخر را با نام NWC نشان می‌دهد؛ حرفِ عرض در آن، همان محور طولِ ماست. فیلتر همچنان به‌ترتیبِ عرضِ کرنل، ورودی و خروجی تعریف می‌شود، حتی وقتی آرایش ورودیِ کانال‌اول انتخاب شود. تفاوت نام محور با تفاوت معنای وزن یکی نیست.

در کد، رابطهٔ تبدیل صریح است: عضوِ ورودی با اندیس‌های نمونه، کانال و طول، به همان مقدار با اندیس‌های نمونه، طول و کانال می‌رود. وزن نیز با اندیس‌های خروجی، ورودی و کرنل، به کرنل، ورودی و خروجی می‌رود. این کار ترتیبِ منطقیِ عضوها را حفظ می‌کند؛ تخت‌کردن و دوباره گروه‌کردنِ همان فهرست، الزاماً چنین نگاشتی نمی‌سازد.

دو شاهدِ غلط داریم: یک بار فقط ورودیِ تخت را بدون جابه‌جاییِ واقعی دوباره دسته‌بندی می‌کنیم و یک بار همین اشتباه را برای وزن انجام می‌دهیم. شکل‌ها همچنان قابل‌مصرف‌اند، ولی جواب‌هایشان با پاسخ دستی فرق دارد. نامِ محور و نمونهٔ شناخته‌شده لازم‌اند؛ سازگاریِ اندازه‌ها به‌تنهایی معنای داده را تضمین نمی‌کند.

۴. پاسخ نامتقارن را با مرجع تختِ مستقل بسنجیم

مرجع مستقل پنجرهٔ ورودی را به ردیفی تخت می‌چیند
تصویر مفهومی: مرجع مستقل پنجرهٔ ورودی را به ردیفی تخت می‌چیند؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

فایل conv1d_layout_contract.py با Python 3.14.5 و کتابخانهٔ استاندارد اجرا شده است. پاسخِ خروجی اول چهار، منفی چهار و منفی نه و پاسخِ خروجی دوم پنج، شانزده و چهل است. در نمایشِ طول‌اول، این دو ردیف به سه جفتِ چهار و پنج، منفی چهار و شانزده، منفی نه و چهل تبدیل می‌شوند؛ مقدارها عوض نشده‌اند.

"""Original bounded integer Conv1d arithmetic/layout lesson; no framework calls."""
import json
import platform


def tensor3(value, bound):
    if type(value) not in (list, tuple) or not 1 <= len(value) <= 8:
        raise ValueError("expected a nonempty bounded three-axis tensor")
    result = []
    shape = None
    for plane in value:
        if type(plane) not in (list, tuple) or not 1 <= len(plane) <= 8:
            raise ValueError("invalid second axis")
        rows = []
        row_length = None
        for row in plane:
            if type(row) not in (list, tuple) or not 1 <= len(row) <= 8:
                raise ValueError("invalid third axis")
            if any(type(number) is not int or not -bound <= number <= bound for number in row):
                raise ValueError("expected actual bounded Python integers")
            if row_length is not None and len(row) != row_length:
                raise ValueError("ragged third axis")
            row_length = len(row)
            rows.append(tuple(row))
        if shape is not None and (len(rows), row_length) != shape:
            raise ValueError("ragged second axis")
        shape = (len(rows), row_length)
        result.append(tuple(rows))
    return tuple(result), (len(result), *shape)


def checked_bias(bias, outputs):
    if type(bias) not in (list, tuple) or len(bias) != outputs:
        raise ValueError("one bias per output channel required")
    if any(type(number) is not int or not -32 <= number <= 32 for number in bias):
        raise ValueError("bias must contain actual integers in -32..32")
    return tuple(bias)


def dimensions(n, inputs, length, outputs, kernel, weight_inputs):
    if not (1 <= n <= 2 and 1 <= inputs <= 3 and 1 <= outputs <= 3
            and 1 <= kernel <= 4 and kernel <= length <= 8 and inputs == weight_inputs):
        raise ValueError("layout dimensions outside contract or input-channel mismatch")


def conv_ncl_oik(x, weights, bias):
    x, (n, inputs, length) = tensor3(x, 16)
    weights, (outputs, weight_inputs, kernel) = tensor3(weights, 8)
    dimensions(n, inputs, length, outputs, kernel, weight_inputs)
    bias = checked_bias(bias, outputs)
    return tuple(tuple(tuple(
        bias[o] + sum(x[batch][i][position + k] * weights[o][i][k]
                      for i in range(inputs) for k in range(kernel))
        for position in range(length - kernel + 1))
        for o in range(outputs)) for batch in range(n))


def convert_ncl_oik_to_nlc_kio(x, weights, bias):
    x, (n, inputs, length) = tensor3(x, 16)
    weights, (outputs, weight_inputs, kernel) = tensor3(weights, 8)
    dimensions(n, inputs, length, outputs, kernel, weight_inputs)
    bias = checked_bias(bias, outputs)
    x_nlc = tuple(tuple(tuple(x[batch][i][position] for i in range(inputs))
                        for position in range(length)) for batch in range(n))
    w_kio = tuple(tuple(tuple(weights[o][i][k] for o in range(outputs))
                        for i in range(inputs)) for k in range(kernel))
    return x_nlc, w_kio, bias


def conv_nlc_kio(x, weights, bias):
    x, (n, length, inputs) = tensor3(x, 16)
    weights, (kernel, weight_inputs, outputs) = tensor3(weights, 8)
    dimensions(n, inputs, length, outputs, kernel, weight_inputs)
    bias = checked_bias(bias, outputs)
    return tuple(tuple(tuple(
        bias[o] + sum(x[batch][position + k][i] * weights[k][i][o]
                      for k in range(kernel) for i in range(inputs))
        for o in range(outputs)) for position in range(length - kernel + 1))
        for batch in range(n))


def nlo_to_nol(value):
    return tuple(tuple(tuple(row[o] for row in batch) for o in range(len(batch[0])))
                 for batch in value)


def flattened_patch_oracle(x, weights, bias):
    """Independent flat storage offsets -> patch matrix -> row dot products.

    Only used with declared-valid finite fixtures, not as a public input validator.
    No main convolution, permutation, tensor validator or output adapter is used.
    """
    n, inputs, length = len(x), len(x[0]), len(x[0][0])
    outputs, kernel = len(weights), len(weights[0][0])
    x_flat = tuple(number for batch in x for channel in batch for number in channel)
    w_flat = tuple(number for output in weights for channel in output for number in channel)
    rows = tuple(w_flat[o * inputs * kernel:(o + 1) * inputs * kernel] for o in range(outputs))
    answer = []
    for batch in range(n):
        patches = []
        for start in range(length - kernel + 1):
            patch = []
            for coordinate in range(inputs * kernel):
                channel, tap = divmod(coordinate, kernel)
                offset = batch * inputs * length + channel * length + start + tap
                patch.append(x_flat[offset])
            patches.append(tuple(patch))
        answer.append(tuple(tuple(bias[o] + sum(a * b for a, b in zip(patch, rows[o]))
                                  for patch in patches) for o in range(outputs)))
    return tuple(answer)


def rejected(function, *args):
    try:
        function(*args)
    except ValueError:
        return True
    return False


def run_example():
    x = (((1, 2, 4, 8, 16), (3, 1, 0, -1, 2)),)
    weights = (((1, 0, -1), (2, 0, 1)), ((0, 1, 2), (-1, 0, 1)))
    bias = (1, -2)
    expected = (((4, -4, -9), (5, 16, 40)),)
    y = conv_ncl_oik(x, weights, bias)
    x_nlc, w_kio, b = convert_ncl_oik_to_nlc_kio(x, weights, bias)
    y_nlo = conv_nlc_kio(x_nlc, w_kio, b)
    reversed_weights = tuple(tuple(tuple(reversed(channel)) for channel in output)
                             for output in weights)
    reversed_result = conv_ncl_oik(x, reversed_weights, bias)
    flat_input = tuple(number for channel in x[0] for number in channel)
    wrong_x = (tuple(tuple(flat_input[t * 2:t * 2 + 2]) for t in range(5)),)
    flat_weights = tuple(number for output in weights for channel in output for number in channel)
    wrong_w = tuple(tuple(tuple(flat_weights[(k * 2 + i) * 2:(k * 2 + i + 1) * 2])
                          for i in range(2)) for k in range(3))
    checks = {}
    checks["literal_asymmetric_hand_output_with_per_output_bias"] = y == expected
    checks["literal_input_and_weight_axis_permutation"] = (
        x_nlc == (((1, 3), (2, 1), (4, 0), (8, -1), (16, 2)),)
        and w_kio == (((1, 0), (2, -1)), ((0, 1), (0, 0)), ((-1, 2), (1, 1))))
    checks["converted_layout_outputs_match_by_named_axis"] = (
        y_nlo == (((4, 5), (-4, 16), (-9, 40)),) and nlo_to_nol(y_nlo) == y)
    checks["flat_patch_matrix_oracle_matches_literal_fixture"] = flattened_patch_oracle(x, weights, bias) == expected
    checks["reversing_kernel_is_detected_by_first_hand_value"] = (
        reversed_result[0][0][0] == 7 and reversed_result != expected)
    checks["rechunking_input_flat_values_is_not_axis_permutation"] = (
        wrong_x != x_nlc and nlo_to_nol(conv_nlc_kio(wrong_x, w_kio, b)) != expected)
    checks["rechunking_weight_flat_values_is_not_axis_permutation"] = (
        wrong_w != w_kio and nlo_to_nol(conv_nlc_kio(x_nlc, wrong_w, b)) != expected)
    swapped_x = (tuple(reversed(x[0])),)
    swapped_w = tuple(tuple(reversed(output)) for output in weights)
    checks["same_input_channel_permutation_in_data_and_weights_preserves_output"] = (
        conv_ncl_oik(swapped_x, swapped_w, bias) == expected)
    checks["output_permutation_also_permutes_bias"] = conv_ncl_oik(
        x, tuple(reversed(weights)), tuple(reversed(bias))) == (tuple(reversed(expected[0])),)
    zero_bias = conv_ncl_oik(x, weights, (0, 0))
    checks["bias_added_once_per_output_not_once_per_input_channel"] = all(
        y[0][o][t] - zero_bias[0][o][t] == bias[o] for o in range(2) for t in range(3))
    checks["kernel_equals_length_produces_one_position"] = conv_ncl_oik(
        (((1, 2, 3),),), (((1, 0, -1),),), (2,)) == (((0,),),)
    checks["unit_kernel_pointwise_identity_and_zero_filter_bias"] = (
        conv_ncl_oik((((1, 2), (3, 4)),), (((1,), (0,)), ((0,), (1,))), (0, 0))
        == (((1, 2), (3, 4)),)
        and conv_ncl_oik(x, (((0, 0, 0), (0, 0, 0)),), (3,)) == (((3, 3, 3),),))
    x_second = tuple(tuple(-value for value in row) for row in x[0])
    checks["batch_independence_and_batch_permutation"] = (
        conv_ncl_oik((x[0], x_second), weights, bias)[0] == y[0]
        and conv_ncl_oik((x_second, x[0]), weights, bias)
        == tuple(reversed(conv_ncl_oik((x[0], x_second), weights, bias))))
    checks["empty_ragged_wrong_rank_and_channel_mismatch_rejected"] = all(
        rejected(conv_ncl_oik, bad, weights, bias) for bad in
        ([], None, (1,), (((),),), (((1, 2), (3,)),), (((1, 2),), ((3, 4), (5, 6))),
         (((1, 2, 3),),)))
    checks["integer_value_and_bias_contracts_rejected_without_coercion"] = (
        all(rejected(conv_ncl_oik, (((value, 0, 0), (0, 0, 0)),), weights, bias)
            for value in (True, 1.0, float("nan"), float("inf"), "1", 17, -17, 10**400))
        and all(rejected(conv_ncl_oik, x, (((value, 0, 0), (0, 0, 0)),), (0,))
                for value in (True, 1.0, float("nan"), 9, -9, 10**400))
        and all(rejected(conv_ncl_oik, x, weights, value)
                for value in ((1,), (True, 0), (1.0, 0), (33, 0), (10**400, 0)))
        and rejected(conv_nlc_kio, (((True, 0), (0, 0), (0, 0)),), w_kio, bias))
    checks["dimension_caps_and_kernel_longer_than_input_rejected"] = (
        rejected(conv_ncl_oik, x * 3, weights, bias)
        and rejected(conv_ncl_oik, (((1, 2), (3, 4)),), weights, bias)
        and rejected(conv_ncl_oik, (((0,) * 9, (0,) * 9),), weights, bias)
        and rejected(conv_ncl_oik, x, (((0,) * 5, (0,) * 5),), (0,))
        and rejected(conv_ncl_oik, x, weights * 2, (0,) * 4))
    before = repr((x, weights, bias))
    convert_ncl_oik_to_nlc_kio(x, weights, bias)
    conv_ncl_oik(x, weights, bias)
    checks["input_values_preserved_and_results_are_immutable_tuples"] = (
        before == repr((x, weights, bias)) and type(y) is tuple
        and all(type(batch) is tuple and all(type(row) is tuple for row in batch) for batch in y))
    cases, oracle_ok = 0, True
    for batches in (1, 2):
        for inputs in (1, 2):
            for outputs in (1, 2):
                for kernel in (1, 2, 3):
                    for length in range(kernel, 6):
                        for seed in range(3):
                            data = tuple(tuple(tuple((n * 7 + i * 3 + t + seed) % 9 - 4
                                                     for t in range(length)) for i in range(inputs))
                                         for n in range(batches))
                            filt = tuple(tuple(tuple((o * 5 + i * 2 + k + seed) % 5 - 2
                                                     for k in range(kernel)) for i in range(inputs))
                                         for o in range(outputs))
                            offsets = tuple(o - seed for o in range(outputs))
                            direct = conv_ncl_oik(data, filt, offsets)
                            converted = nlo_to_nol(conv_nlc_kio(*convert_ncl_oik_to_nlc_kio(data, filt, offsets)))
                            oracle_ok &= direct == converted == flattened_patch_oracle(data, filt, offsets)
                            cases += 1
    checks["independent_flat_index_oracle_288_fixtures_one_group"] = bool(oracle_ok and cases == 288)
    assert all(type(value) is bool and value for value in checks.values()), checks
    result = dict(python=platform.python_version(), dependencies={"standard_library_only": True,
                "pytorch_executed": False, "tensorflow_executed": False},
                unit="dimensionless synthetic integer features and weights", input_ncl=x, weights_oik=weights,
                bias=bias, input_nlc=x_nlc, weights_kio=w_kio, output_nol=y, output_nlo=y_nlo,
                wrong_kernel_reversal_output_nol=reversed_result,
                wrong_input_rechunk_output_nol=nlo_to_nol(conv_nlc_kio(wrong_x, w_kio, b)),
                wrong_weight_rechunk_output_nol=nlo_to_nol(conv_nlc_kio(x_nlc, wrong_w, b)),
                independent_oracle_cases=cases, unique_check_groups=len(checks), checks=checks,
                limitations=["Valid stride1 dilation1 groups1 only; no padding, activation or streaming state.",
                             "Nested Python integer containers, not physical ndarray strides or quantized INT8 inference.",
                             "No framework, learned model, audio, hardware, speed or task quality experiment."])
    return json.loads(json.dumps(result))


if __name__ == "__main__":
    print(json.dumps(run_example(), ensure_ascii=False, indent=2))

مسیر اصلی پنجره و کانال را مستقیم پیمایش می‌کند. مرجع مستقل از ورودی و وزنِ تخت، با فرمولِ اندیس، ردیف‌های پنجره را می‌سازد و ضربِ داخلیِ آن‌ها را با ردیف‌های وزن حساب می‌کند. تابعِ تبدیلِ محور و تابعِ همبستگیِ اصلی، جوابِ انتظارِ آن مرجع را تولید نمی‌کنند. پاسخِ دستیِ نامتقارن نیز جدا لنگرِ مقایسه است.

هجده گروه نام‌دار گذشتند. ۲۸۸ دادهٔ کوچک با اندازه‌های مختلفِ دسته، کانال، خروجی، کرنل و طول در یک گروه با مرجعِ تخت سنجیده شدند؛ این‌ها ۲۸۸ گروهِ مستقل نیستند. بایاسِ یک‌باره، کرنلِ تک‌عضوی، کرنلِ هم‌اندازهٔ ورودی، کانالِ جابه‌جاشده، خروجیِ جابه‌جاشده و استقلالِ نمونه‌های دسته نیز بررسی شده‌اند.

{
  "python": "3.14.5",
  "dependencies": {
    "standard_library_only": true,
    "pytorch_executed": false,
    "tensorflow_executed": false
  },
  "unit": "dimensionless synthetic integer features and weights",
  "input_ncl": [
    [
      [
        1,
        2,
        4,
        8,
        16
      ],
      [
        3,
        1,
        0,
        -1,
        2
      ]
    ]
  ],
  "weights_oik": [
    [
      [
        1,
        0,
        -1
      ],
      [
        2,
        0,
        1
      ]
    ],
    [
      [
        0,
        1,
        2
      ],
      [
        -1,
        0,
        1
      ]
    ]
  ],
  "bias": [
    1,
    -2
  ],
  "input_nlc": [
    [
      [
        1,
        3
      ],
      [
        2,
        1
      ],
      [
        4,
        0
      ],
      [
        8,
        -1
      ],
      [
        16,
        2
      ]
    ]
  ],
  "weights_kio": [
    [
      [
        1,
        0
      ],
      [
        2,
        -1
      ]
    ],
    [
      [
        0,
        1
      ],
      [
        0,
        0
      ]
    ],
    [
      [
        -1,
        2
      ],
      [
        1,
        1
      ]
    ]
  ],
  "output_nol": [
    [
      [
        4,
        -4,
        -9
      ],
      [
        5,
        16,
        40
      ]
    ]
  ],
  "output_nlo": [
    [
      [
        4,
        5
      ],
      [
        -4,
        16
      ],
      [
        -9,
        40
      ]
    ]
  ],
  "wrong_kernel_reversal_output_nol": [
    [
      [
        7,
        6,
        17
      ],
      [
        5,
        8,
        12
      ]
    ]
  ],
  "wrong_input_rechunk_output_nol": [
    [
      [
        -7,
        20,
        26
      ],
      [
        35,
        8,
        -4
      ]
    ]
  ],
  "wrong_weight_rechunk_output_nol": [
    [
      [
        7,
        18,
        37
      ],
      [
        3,
        -5,
        -9
      ]
    ]
  ],
  "independent_oracle_cases": 288,
  "unique_check_groups": 18,
  "checks": {
    "literal_asymmetric_hand_output_with_per_output_bias": true,
    "literal_input_and_weight_axis_permutation": true,
    "converted_layout_outputs_match_by_named_axis": true,
    "flat_patch_matrix_oracle_matches_literal_fixture": true,
    "reversing_kernel_is_detected_by_first_hand_value": true,
    "rechunking_input_flat_values_is_not_axis_permutation": true,
    "rechunking_weight_flat_values_is_not_axis_permutation": true,
    "same_input_channel_permutation_in_data_and_weights_preserves_output": true,
    "output_permutation_also_permutes_bias": true,
    "bias_added_once_per_output_not_once_per_input_channel": true,
    "kernel_equals_length_produces_one_position": true,
    "unit_kernel_pointwise_identity_and_zero_filter_bias": true,
    "batch_independence_and_batch_permutation": true,
    "empty_ragged_wrong_rank_and_channel_mismatch_rejected": true,
    "integer_value_and_bias_contracts_rejected_without_coercion": true,
    "dimension_caps_and_kernel_longer_than_input_rejected": true,
    "input_values_preserved_and_results_are_immutable_tuples": true,
    "independent_flat_index_oracle_288_fixtures_one_group": true
  },
  "limitations": [
    "Valid stride1 dilation1 groups1 only; no padding, activation or streaming state.",
    "Nested Python integer containers, not physical ndarray strides or quantized INT8 inference.",
    "No framework, learned model, audio, hardware, speed or task quality experiment."
  ]
}

ورودیِ مستطیلیِ list یا tuple پذیرفته می‌شود؛ دسته یک تا دو، کانالِ ورودی و خروجی یک تا سه، طول یک تا هشت و کرنل یک تا چهار است. کرنل نباید از طول بزرگ‌تر باشد. ویژگی‌ها صحیحِ منفی شانزده تا شانزده، وزن‌ها منفی هشت تا هشت و بایاس منفی سی‌ودو تا سی‌ودو هستند؛ bool، شناور، رشته، بی‌نهایت و دادهٔ نامستطیلی رد می‌شوند.

۵. درستی این قرارداد، اجرای چارچوب یا کیفیت مدل نیست

برابری حساب نمونه به معنی اجرای مدل روی برد نیست
تصویر مفهومی: برابری حساب نمونه به معنی اجرای مدل روی برد نیست؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

همهٔ حساب‌ها با عدد صحیحِ پایتون و ظرف‌های تودرتوست. چیدمانِ فیزیکیِ حافظه، stride بایتیِ آرایه و هزینهٔ کپی اندازه‌گیری نشده‌اند. قرارداد TensorFlow نوعِ تانسورِ شناور می‌گیرد؛ ورودیِ صحیحِ آموزشی ما ادعای سازگاریِ نوع با آن API نیست. PyTorch، TensorFlow، NumPy و هیچ بردی در اجرای این فایل استفاده نمی‌شوند.

ضرب داخلیِ int8 و نقطهٔ صفر قراردادِ متفاوتِ مقیاس و بازسازی دارد؛ صحیح بودنِ عددها در این درس به معنی کوانتیزه‌بودن نیست. فیلترِ FIR علّی نیز نمونهٔ جاری و گذشته و حافظهٔ بسته دارد؛ اینجا یک ورودیِ محدود را با پنجرهٔ معتبر می‌خوانیم و وضعیتِ جریانی نگه نمی‌داریم.

پرسش اول: چرا خروجی سه جایگاه دارد؟ پاسخ: پنجرهٔ سه‌عضوی روی پنج جایگاه، بدون padding و با گام یک، سه بار کامل جا می‌گیرد؛ فرمول پنج منهای سه به‌اضافهٔ یک است.

پرسش دوم: بایاس یک را چند بار اضافه کنیم؟ پاسخ: یک بار در هر جایگاهِ خروجی اول، پس از جمعِ همهٔ سهم‌ها؛ نه یک بار برای هر کانالِ ورودی.

پرسش سوم: چرا کرنلِ متقارن برای تنها آزمون کافی نیست؟ پاسخ: معکوسش همان دنباله می‌شود؛ شاهدِ نامتقارنِ حاضر نخستین مقدارِ چهار را در مسیرِ وارونه به هفت تبدیل می‌کند.

پرسش چهارم: برابریِ دو چیدمان، انتقالِ مدل به برد را تأیید می‌کند؟ پاسخ: خیر؛ فقط حساب و نگاشتِ اعلام‌شده روی داده‌های آزموده‌شده برابرند. تبدیلِ چارچوب، گردکردن، پوششِ عملگر، حافظه، سرعت و کیفیتِ وظیفه آزمایشِ جدا می‌خواهند.

منابع در ۲۶ مهر ۱۴۰۵ خوانده شدند. نسخهٔ مستندات PyTorch برابر 2.14 است؛ مرجع TensorFlow کدِ رسمیِ برچسب v2.16.1 است، نه انتشارِ تازه. تاریخِ اولیهٔ صفحهٔ مستندات و بخشِ کد را معلوم فرض نمی‌کنیم. این آموزشِ تألیفی، خبر جدید یا گزارشِ مدلِ واقعی نیست و از قبولیِ بررسی‌ها، بهبود گفتار یا عملکردِ سخت‌افزار نتیجه نمی‌گیرد.

منابع و مطالعهٔ بیشتر

بررسی منابع: ۲۶ مهر ۱۴۰۵. تحلیل‌ها و پیشنهادهای اجرایی، جمع‌بندی نویسنده‌اند.