Pull to refresh
5
0
Алексей@VDG

Sparse Hash AI

Send message

Другого ответа от "непонятого гения" и не ожидалось. ) Про очевидную глупость вы правы, она бросается в глаза с первых строк статьи.

Порт на торч с тестом, кому интересно.

import torch
import torch.nn as nn

torch.manual_seed(42)

def bce_loss(logit, target):
    """Бинарная кросс-энтропия без сигмоиды (logit-based)"""
    # logit — это нормализованная активация A (в диапазоне [-1, 1] или подобном)
    # target: +1 или -1 (можно преобразовать в 0/1)
    # Но в статье используется logit напрямую → используем численно стабильную форму
    return torch.clamp(logit, min=0) - logit * target + torch.log1p(torch.exp(-torch.abs(logit)))

def normalize_vector(x):
    """L∞-нормализация (максимум по модулю)"""
    max_abs = x.abs().max()
    if max_abs == 0:
        return x
    return x / max_abs

class SAARPerceptron(nn.Module):
    def __init__(self, S_count, A_count, R_count, p1=0.5, p2=0.5, p3=0.1,
                 correct1=0.1, correct2=0.1, correct3=0.05, device='cpu'):
        super().__init__()
        self.S_count = S_count
        self.A_count = A_count
        self.R_count = R_count
        self.device = device

        # Веса S→A (обучаемые)
        self.weight_SA = nn.Parameter(torch.randn(S_count, A_count, device=device) * 0.1, requires_grad=False)

        # Веса A→R (обучаемые)
        self.weight_AR = nn.Parameter(torch.randn(A_count, R_count, device=device) * 0.1, requires_grad=False)

        # Фиксированные веса A→A (резервуар, не обучаемые)
        # Разрежённая матрица: +1 / -1 с вероятностью, например, 10%
        self.weight_AA = torch.zeros(A_count, A_count, device=device)
        mask = torch.rand(A_count, A_count, device=device) < 0.1  # булевая маска
        # Генерируем случайные знаки (+1 / -1) той же формы
        signs = torch.where(torch.rand(A_count, A_count, device=device) < 0.5, 1.0, -1.0)
        self.weight_AA[mask] = signs[mask]
        self.weight_AA = self.weight_AA.detach()  # не обучаем

        # Пороги для A→A активации (случайные от -10 до +10)
        self.thresholds = (torch.rand(A_count, device=device) * 20 - 10).detach()

        # Гиперпараметры обучения
        self.p1, self.p2, self.p3 = p1, p2, p3
        self.correct1, self.correct2, self.correct3 = correct1, correct2, correct3

        self.A_field = None
        self.A_field_norm = None

    def forward(self, x):
        """
        x: [batch_size, S_count] — бинарные входы (True/False или 0/1)
        Возвращает: [batch_size, R_count] — логиты (до порога)
        """
        batch_size = x.size(0)
        x = x.float()

        # Активация A от S
        A_raw = torch.matmul(x, self.weight_SA)  # [B, A]

        # Добавляем влияние A→A (только если A_j > threshold_i)
        # Альтернатива: для простоты сделаем по батчу (медленно, но понятно)
        A_reservoir = torch.zeros_like(A_raw)
        for b in range(batch_size):
            # Маска активных A-элементов (для A→A)
            active = A_raw[b].unsqueeze(0) > self.thresholds.unsqueeze(1)  # [A, A]
            # Суммируем weight_AA[i, j] где active[i, j] == True
            A_reservoir[b] = (self.weight_AA * active.float()).sum(dim=1)

        A_total = A_raw + A_reservoir
        A_total = torch.relu(A_total)  # ReLU-подобная активация

        # Для простоты нормализуем по последнему измерению (A)
        # Но в статье — по каждому примеру
        self.A_field = A_total
        self.A_field_norm = torch.stack([normalize_vector(a) for a in A_total])

        # Выход R
        R_out = torch.matmul(self.A_field_norm, self.weight_AR)
        return R_out

    def train_step(self, x, y_true):
        """
        x: [B, S] — бинарные входы
        y_true: [B, R] — целевые метки (+1 / -1 или 0/1 → преобразуем в +1/-1)
        """
        B = x.size(0)
        y_pred = self.forward(x)  # [B, R]

        # Преобразуем y_true в +1 / -1
        y_true_sign = torch.where(y_true > 0, 1.0, -1.0)
        y_pred_sign = torch.sign(y_pred)
        # Ошибка: разность знаков → но в статье ReactionError = y_true - y_pred?
        # В коде: ReactionError[j] используется как скаляр коррекции
        # Предположим: ReactionError = y_true - torch.sign(y_pred)
        reaction_error = y_true_sign - y_pred_sign  # [-2, 0, +2]

        # === Обучение A→R (по Хеббу с ошибкой) ===
        with torch.no_grad():
            for b in range(B):
                for r in range(self.R_count):
                    err = reaction_error[b, r]
                    if err == 0:
                        continue
                    active_A = self.A_field[b] > 0  # [A]
                    self.weight_AR[active_A, r] += err

        # === Обучение S→A (стохастическое) ===
        with torch.no_grad():
            for b in range(B):
                for a in range(self.A_count):
                    a_active = self.A_field[b, a] > 0
                    a_norm = self.A_field_norm[b, a]
                    for r in range(self.R_count):
                        w_sign = torch.sign(self.weight_AR[a, r])
                        err_sign = torch.sign(reaction_error[b, r])
                        x_b = x[b].bool()

                        if a_active:
                            if w_sign != err_sign:
                                # Подавление
                                prob = self.p1 * bce_loss(a_norm, torch.tensor(-1.0, device=self.device))
                                # Замена random.random() → torch.rand().item()
                                if torch.rand(1, device=self.device).item() < prob.item():
                                    self.weight_SA[x_b, a] -= self.correct1 * a_norm
                        else:
                            if w_sign == err_sign:
                                # Возбуждение
                                prob = self.p2 * bce_loss(a_norm, torch.tensor(1.0, device=self.device))
                                if torch.rand(1, device=self.device).item() < prob.item():
                                    self.weight_SA[x_b, a] += self.correct2 * a_norm

                            # Дополнительное возбуждение (exploration)
                            if torch.rand(1, device=self.device).item() < self.p3:
                                self.weight_SA[x_b, a] += self.correct3

    def predict(self, x):
        with torch.no_grad():
            logits = self.forward(x)
            return torch.sign(logits)


model = SAARPerceptron(S_count=10, A_count=50, R_count=1)
x = torch.randint(0, 2, (32, 10)).float()  # бинарные входы
y = torch.randint(0, 2, (32, 1)).float()   # метки 0/1

for epoch in range(100):
    model.train_step(x, y)

pred = model.predict(x)
print("Accuracy:", (pred == torch.sign(y - 0.5)).float().mean())

Вижу автор очень ранимо относится к любой критике, но мне это не помешает )

аннотацию мне написал ChatGpt. Думаю пора перестать стесняться, что это удобно

Почему же постеснялись его попросить заодно переписать ваш код на торч, создать модель, провести простой эксперимент и вывести графики, чтобы это было понятно кому-то кроме вас?

такие библиотеки как torch, позволяют не думать об архитектуре нейросеток, но для исследователя они не удобны.

Как тот самый исследователь скажу - торч прост и удобен. К примеру ваш код с десятком строк вложенных циклов заменяется простым матричным умножением входа на весовую матрицу: h = X @ W.

И что значит "позволяют не думать об архитектуре нейросеток"? Торч именно что и позволяет думать об архитектуре сети в ёмких математических выражениях, а не обо всех этих циклах.

Недавно это назвали резервуаром

Это "недавно" было в середине 20-го столетия, там же где и перцептрон Розенблатта. Не отставайте.

при 30к нейронах лучший результат прогнозирования 177 ошибок (98,23%).

Выше головы не прыгнешь, это было понятно ещё с первой статьи. При этом оверхед потребления нейронов.

Речевые зоны при программировании не задействуются. Есть экспериментальная работа на эту тему.

Нормализация, как L1.

Выглядит как некролог-мемуары. ) Вот он живой в телеге @victorkazarinov )

По графикам видно, что всю работу провел градиентный спуск, но с ГА получилось в сто раз дольше.

То, что микширование параметров моделей с помощью ГА не сильно попортило картину, это следствие того, что это особенность нейросетей. Это было показано в нескольких работах пару лет назад. Вы можете «скрещивать» веса разных инстантов модели, просто беря их среднее значение. Результирующие веса получатся не хуже исходных.

Трансформеры это как минимум не свертка

Atrous-свёртка как маска контекста. Это именно то самое, что вы описали "на первом уровне трансформера ближайшие, на втором - каждый второй, на третьем - каждый четвертый". Шаг маски является степенью двойки.

на первом уровне трансформера ближайшие, на втором - каждый второй, на третьем - каждый четвертый

Это atrous-свёртка. Шаг равен 2^i. Я тоже пробовал на небольших последовательностях в микромодели. Если бы она была годна для больших, то её бы использовали.

Atrous Spatial Pyramid Pooling
https://paperswithcode.com/method/aspp

Вряд ли mem-вектор будет обрабатываться трансформером так же как несжатая последовательность. То есть, не получится подать на вход mem-вектор, кодирующий [В лесу родилась ёлочка], и спросить [Где родилась ёлочка?]. Mem - это "seed" (скорее даже, набор поправок), а не сжатое семантическое представление предложения. Для ответа потребуется раскодировать вектор в последовательность, тем самым потребив ограниченную память контекста.

возникнет пузырь истинного вакуума, который расширится со скоростью света

Значит в расширяющейся с ускорением Вселенной пузырь коллапсирующего вакуума никогда не догонит галактики, находящиеся за пределом некоторого радиуса?

Второе выражение — это произведение матриц Q и V, что даёт матрицу (d_k, d_v). Операция умножения K(QV) в третьем выражении имеет ту же временную сложность.

Мы можем сначала получить произведение карты признаков с K и V (value, значение) для формирования блока KV, а затем — получить произведение с Q.

По моему, автор тут запутался, где какое у него ку. Вначале считаем блок KV, а затем умножаем на Q, Q(KV).

В случае, как на схеме, подключения базы непосредственно на плюс, а эмиттера на минус, имеем ничем не ограниченный прямой ток через p-n переход, проще говоря - КЗ.

Не смотрели анизотропию для трансформера-декодера для эмбеддингов с выходов отдельно внимания и FFN?

https://github.com/brave/brave-browser/issues/6767

Та же самая старая проблема. В меню починили, но осталась в popup-ах. Выскочит ещё раз, заскриню.

Передайте там разрабам про старый баг, что в окнах у кнопки Close надпись Близко. :)

Да и откуда 7? У шимпанзе и горилл, которых обучали «речи» и вели с оными разговоры, определяли соответствие интеллекту 4-5-летнего ребенка, а тут сразу 7.
Поболтать с вами сейчас и тостер может. А они соотносят возраст по способности решать задачи. Врановых начинают ставить выше шимпанзе.
перепишем формулу обычного перцептрона

Перцептрон W*X + b считает скалярное произведение (косинус угла) между векторами W и X. Вы поделили это на магнитуду вектора W и перешли от углов к расстояниям(W*X + b) / mag(W), а это уже что-то другое.
1
23 ...

Information

Rating
Does not participate
Registered
Activity