Тот же нейрон, другие веса
Вы узнаете
Как один тернарный нейрон считает голосование большинством с одними весами и другую функцию — с другими.
maj3 в bitnet_majority.t27 упаковывает три трита через pack3, берёт dot27 с w_all_p, куском весов из одних P, и квантует с порогом 0: результат — знак a + b + c. weighted_vote — тот же нейрон с весами pack3(2, 2, 0), и он даёт знак a + b - c: функцию выбирают веса. Браузер пропускает все 12 тестов, потому что пока не знает assert_eq; нативный t27c запускает все 12, все проходят, ни один не пустой. Запись уменьшает w_all_p на 16, и вес на дорожке 2 из P становится Z: maj3 больше не видит c. Падает ровно один тест, maj_p_n_n: +1 - 1 - 1 даёт N, а +1 - 1 даёт Z. Заметьте: wv_p_z_p передаёт средним входом 0, то есть N, а не Z. Каждый байт в записи напечатала команда; инсценирован только набор текста.
Попробуйте
Найдите в записи ожидаемое и полученное значение maj_p_n_n; затем найдите в спеке wv_p_z_p и вычислите, что вернёт weighted_vote(2, 1, 2).

t27c on the t27c lab (Railway), spec at t27 5ff0ec512: 12 tests pass natively; one maj3 weight moved from P to Z fails exactly one test, maj_p_n_n; git restores the spec.
specs/ternary/bitnet_majority.t27
module BitnetMajority;
// Sign-only ternary multiply of two packed trits {N=0b00, Z=0b01, P=0b10}.
fn tmul(ta: u8, tb: u8) -> i8 {
if (ta == 1) { return 0; }
if (tb == 1) { return 0; }
if (ta == tb) { return 1; }
return -1;
}
// 27-trit ternary dot product of two 54-bit packed vectors.
fn dot27(a: u64, b: u64) -> i16 {
var acc : i16 = 0;
var i : u32 = 0;
while (i < 27) {
var ta : u8 = ((a >> (i << 1)) & 3) as u8;
var tb : u8 = ((b >> (i << 1)) & 3) as u8;
acc = acc + tmul(ta, tb) as i16;
i = i + 1;
}
return acc;
}
// Ternary sign/threshold quantizer: v > +t -> P, v < -t -> N, else Z.
fn quantize(v: i16, threshold: i16) -> u8 {
if (v > threshold) { return 2; }
if (v < -threshold) { return 0; }
return 1;
}
// Pack 3 trits into the low 3 lanes of a chunk; the rest are Z.
fn pack3(t0: u8, t1: u8, t2: u8) -> u64 {
var z : u64 = 6004799503160661;
var cleared : u64 = z & 18446744073709551552;
return cleared | (t0 as u64) | ((t1 as u64) << 2) | ((t2 as u64) << 4);
}
// Ternary majority of three trits = sign of (a + b + c), realized as a spec-
// first single ternary neuron: pack the inputs, dot with an all-+1 weight chunk
// (so the dot product is exactly a + b + c over the three active lanes), then
// quantize at threshold 0. A recognizable named function computed by the
// BitNet stack. Inputs/output are packed trits {N=0, Z=1, P=2}.
pub fn maj3(a: u8, b: u8, c: u8) -> u8 {
var chunk : u64 = pack3(a, b, c);
var w_all_p : u64 = 12009599006321322;
return quantize(dot27(chunk, w_all_p), 0);
}
// The SAME single neuron computes a DIFFERENT named function when the weights
// change -- the essence of a trained model. With per-input weights [+1, +1, -1]
// it computes sign(a + b - c) (weight P contributes +input, weight N contributes
// -input). Demonstrates that weights define the function, not the topology.
pub fn weighted_vote(a: u8, b: u8, c: u8) -> u8 {
var chunk : u64 = pack3(a, b, c);
var weights : u64 = pack3(2, 2, 0);
return quantize(dot27(chunk, weights), 0);
}
test maj_p_p_n { assert_eq(maj3(2, 2, 0), 2); }
test maj_p_n_z { assert_eq(maj3(2, 0, 1), 1); }
test maj_n_n_p { assert_eq(maj3(0, 0, 2), 0); }
test maj_p_p_p { assert_eq(maj3(2, 2, 2), 2); }
test maj_z_z_z { assert_eq(maj3(1, 1, 1), 1); }
test maj_n_n_n { assert_eq(maj3(0, 0, 0), 0); }
test maj_p_n_n { assert_eq(maj3(2, 0, 0), 0); }
// weighted_vote = sign(a + b - c): flipping the 3rd weight to -1 changes the function.
test wv_p_p_p { assert_eq(weighted_vote(2, 2, 2), 2); }
test wv_p_z_p { assert_eq(weighted_vote(2, 0, 2), 0); }
test wv_z_z_z { assert_eq(weighted_vote(1, 1, 1), 1); }
test wv_p_n_n { assert_eq(weighted_vote(2, 0, 0), 2); }
test wv_z_z_p { assert_eq(weighted_vote(1, 1, 2), 0); }
endmodule
Все уроки
Модуль 1 · Лаборатория: наши исследования
Свой формат чисел, честная таблица результатов и таблицы модели, перемноженные на плате.
Модуль 2 · ИИ-числа: блок MX
Как ИИ-чипы хранят веса в нескольких битах: один общий масштаб на блок, сам байт масштаба и что один выброс делает с соседями.
Модуль 3 · Тернарные веса
Веса, которые бывают только минус масштаб, ноль или плюс масштаб, пять правил, которые должен пройти тернарный алфавит, и прогон тестов, который ничего не проверил.
Модуль 4 · Тернарный формат Ternary Network Float
Правило, которое компилятор проверяет до запуска любого теста, и 17-битное число с плавающей точкой, порядок которого — четыре сбалансированных трита.
Модуль 5 · Арифметика чисел со знаком
Умножить два числа со знаком, сложить их, когда знаки разные, и сделать то и другое сразу в умножении с накоплением.
Модуль 6 · Части нейрона
ReLU с изломом в нуле, степень двойки для softmax и argmax, который называет ответ.
Модуль 7 · Обучение на ошибке
Потеря, которая оценивает неверную догадку в битах, один шаг, который сдвигает вес против градиента, и скрытый слой, который нужен XOR.
Модуль 8 · BitNet: тернарные сети
Порог, который сжимает сумму обратно до трёх значений, один нейрон, который становится другой функцией при смене весов, и нейрон, который читает входы по 27 тритов за раз.
Модуль 9 · Тернарный MAC как чип
Скалярное произведение 27 тритов как провода без регистра, та же сумма, которую регистр копит на каждом такте, и небольшая целая сеть в конце курса.