LINUX.ORG.RU

Нейронные сети нетрадиционной ориентации

 , , ,


4

6

Данная статья не стремится к научной полноте и не содержит формул и графиков, однако включает ссылки на исследования, которые были использованы при её подготовке. Статья призвана познакомить читателя с тем, как биологические механизмы могут применяться при разработке искусственных нейронных сетей для создания сильного искусственного интеллекта (AGI).

Для оправдания кликбейтного заголовка давайте сначала кратко рассмотрим традиционные нейронные сети. Их можно классифицировать по топологии следующим образом:

  • Топологически сложные — биологические мозги
  • Топологически простые — искуственные нейронные сети (от простых сверточных сетей до Transformers и Mamba архитектур)

Каждый тип обладает своими преимуществами и недостатками.

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

Искусственные сети обучаются быстро, способны оперировать большим контекстом и эффективно аппроксимируют задачи, но полностью отсутствует самосознание и понимание того, что они делают.

Очевидно, что для создания AGI требуется объединить преимущества обоих типов нейронных сетей. В данной статье будет рассмотрен именно такой подход: создание нетрадиционной сети, которая сочетает достоинства биологических и искусственных систем. Некоторые детали намеренно упрощены, но при этом эффективность метода не страдает, что позволяет читателю воспроизвести его на современных процессорах или видеокартах, выпущенных за последнее десятилетие.

Для демонстрации будем использовать язык, позволяющий экспериментировать не приходя в сознание: Python (v3.10.14). В качестве фреймворка для гибкой работы с нейронными сетями выберем torch (v2.8.0), а для построения графов - networkx (v3.3). Точные версии не обязательны: представленный код будет работать и на соседних версиях этих библиотек.

В качестве эталона будем использовать распознавание цифр из датасета MNIST. Датасет содержит 70000 размеченных изображений цифр (0‑9) в оттенках серого, размером 28х28 px, общий объём примерно 12МБ.

Простые топологии искусственных нейронных сетей показывают точность от 80% до 98%. Специализированные сети, «заточенные» под задачу, достигают 99+% [1].

Сложная топология (человеческий мозг) в среднем демонстрирует точность ~99.5‑99.7% (данные приблизительные, поскольку детальные исследования на людях пока что проведены не были).

Для построения нетрадиционной архитектуры возьмём в качестве образца структуру зрительной коры головного мозга, расположенную преимущественно в затылочных долях полушарий. Особенности архитектуры зрительной коры головного мозга:

  • Кортикальные столбы (колонки) - базовые объединения (~80‑120 нейронов), проходящие вертикально через все слои коры [2]. Данные колонки образуют модули - функциональные блоки, содержащие полный набор нейронов для обработки всех возможных ориентаций линий и данных от обоих глаз в конкретной точке поля зрения [3].

  • Связи между колонками реализованы по Small‑World топологии [4]. Эта топология в виде графа обеспечивает высокую эффективность при низком энергопотреблении мозга [5].

  • Внутри колонки информация передаётся через возбуждающие и тормозящие пути, организованные нейронами [6]. Баланс возбуждения‑торможения (E‑I Balance) является фундаментом обработки информации и помехоустойчивой нейронной селективности.

  • Пластичность Хебба - правило, согласно которому одновременно активированные нейроны усиливают свои синаптические связи [7]. Такая синаптическая пластичность лежит в основе обучения и накоплении опыта [8], который как известно - сын ошибок трудных.

  • Взаимодействие между нейронами происходит не только линейно, но и рекуррентно [9], позволяя информации проходить по вертикальным колонкам как сверху‑вниз, так и снизу‑вверх [10].

Попробуем реализовать эту биологическую архитектуру на базе искусственных нейронных сетей с сохранением их главного преимущества - быстрого обучения за счет градиентов и батчей. Пойдем о простого к сложному: создадим файл под названием brain.py и импортируем необходимые пакеты:

#!/usr/bin/env python3

import torch
from torch import nn
import torch.nn.functional as F
import networkx as nx
import math

Теперь создадим класс кортикального столба для моделирования колонок нейронов в коре мозга с балансом возбуждения и торможения.

Параметры:

  1. fc_exc - линейный слой для возбуждающих нейронов. Инициализируем с помощью Kaiming Normal и будем активировать положительные сигналы через ReLU активацию
  2. fc_inh - линейный слой для тормозящих нейронов Инициализация с помощью Xavier Uniformи будем активировать с помощью Sigmoid активации
  3. e_i_ratio - коэффициент баланса в виде соотношения возбуждающих и тормозящих нейронов
  4. dropout - коэффициент указывающий сколько данных отбрасывать dropout слою для предотвращения переобучения

Учтем то что, в реальной коре примерно 80 процентов нейронов являются возбуждающими пирамидальными, а около 20 процентов тормозными нейронами. Тормозные нейроны регулируют общую активность сети и предотвращают её чрезмерное возбуждение. Кроме того, мы будем использовать нормализацию и проекционный слой, который помогает справляться с ситуациями, когда размерности входа и выхода не совпадают.

class Column(nn.Module):
    def __init__(self, in_feat: int, out_feat: int, e_i_ratio: float = 0.8, dropout: float = 0.1):
        super().__init__()
        self.fc_exc = nn.Linear(in_feat, out_feat, bias=False)
        self.fc_inh = nn.Linear(in_feat, out_feat, bias=False)
        self.norm = nn.LayerNorm(out_feat)
        self.dropout = nn.Dropout(dropout)
        self.e_i_ratio = e_i_ratio
        nn.init.kaiming_normal_(self.fc_exc.weight, nonlinearity='relu')
        nn.init.xavier_uniform_(self.fc_inh.weight)
        self.use_projection = (in_feat != out_feat)
        if self.use_projection:
            self.projection = nn.Linear(in_feat, out_feat, bias=False)

    def forward(self, x):
        exc = F.relu(self.fc_exc(x))
        inh = torch.sigmoid(self.fc_inh(x))
        out = exc * (1.0 - self.e_i_ratio * inh)
        if self.use_projection:
            out = out + self.projection(x)
        out = self.norm(out)
        out = self.dropout(out)
        return out

Это база наших вычислений. Вся кора головного мозга человека состоит примерно из 300млн подобных кортикальных колонок (упрощенно).

Связь между такими колонками происходит с помощью графа типа Small-World. Создадим матрицу смежности графа по Small-World модели Уоттса-Строгатца (Watts-Strogatz) с помощью пакета networkx:

Параметры:

  1. num_nodes - количество связываемых колонок
  2. k - количество ближайших соседей в кольце с которыми будет связана колонка
  3. p - вероятность перезаписывания связи

Строим регулярное кольцо, каждая колонка соединена с k/2 соседями слева и справа. С вероятностью p каждая связь заменяется на случайную (с дальним узлом). Такой подход обеспечит частые локальные связи между соседними областями и редкие дальние связи между удаленными регионами.

def make_small_world(num_nodes: int, k: int = 4, p: float = 0.1) -> torch.Tensor:
    G = nx.watts_strogatz_graph(num_nodes, k, p)
    adj = torch.zeros(num_nodes, num_nodes, dtype=torch.float32)
    for i, j in G.edges():
        adj[i, j] = 1.0
        adj[j, i] = 1.0
    return adj

Это наша топология связей между кортикальными колонками внутри нейронной сети.

Обрабатывать получаемые сигналы из графа будем с помощью механизма вычисляющего веса важности сообщений от соседних колонок. Таким образом каждая колонка будет сама решать, какие соседи наиболее релевантны для её текущего состояния и усиливать их сигналы.

class AttentionGate(nn.Module):
    def __init__(self, dim: int):
        super().__init__()
        self.query = nn.Linear(dim, dim, bias=False)
        self.key = nn.Linear(dim, dim, bias=False)
        self.scale = math.sqrt(dim)
    
    def forward(self, center: torch.Tensor, neighbors: torch.Tensor, adj_mask: torch.Tensor):
        q = self.query(center).unsqueeze(1)
        k = self.key(neighbors)
        scores = torch.bmm(q, k.transpose(1, 2)) / self.scale
        scores = scores.squeeze(1)
        scores = scores.masked_fill(adj_mask.unsqueeze(0) == 0, -1e9)
        weights = F.softmax(scores, dim=-1)
        return weights

Это наш механизм внимания.

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

Создадим массив кортикальных колонок, связанных с помощью Small-World графа и будем обучать его веса связей (adj_weight) с помощью правила Хебба, а веса нейронов внутри колонок будем обучать с помощью классического метода градиентного спуска. Каждая колонка будет слушать своих соседей, а механизм внимания определять, от кого брать информацию. Контроль активности (decay_factor) будет следить за тем чтобы сигналы не затухали и не «взрывали» сеть.

class BrainNetwork(nn.Module):
    def __init__(self,
                 num_columns: int = 64,
                 in_dim: int = 784,
                 col_out: int = 128,
                 top_out: int = 10,
                 graph_k: int = 8,
                 graph_p: float = 0.15,
                 message_iters: int = 4,
                 plasticity_lr: float = 5e-5,
                 use_attention: bool = True,
                 e_i_ratio: float = 0.8,
                 dropout: float = 0.1):
        super().__init__()
        self.num_columns = num_columns
        self.message_iters = message_iters
        self.plasticity_lr = plasticity_lr
        self.use_attention = use_attention
        self.columns = nn.ModuleList([Column(in_dim, col_out, e_i_ratio=e_i_ratio, dropout=dropout) for _ in range(num_columns)])
        if use_attention:
            self.attention_gates = nn.ModuleList([AttentionGate(col_out) for _ in range(num_columns)])
        adj_struct = make_small_world(num_columns, k=graph_k, p=graph_p)
        self.register_buffer('adj_struct', adj_struct)
        self.adj_weight = nn.Parameter(torch.randn(num_columns, num_columns) * 0.1)
        self.register_buffer('activity_avg', torch.ones(num_columns))
        self.homeostatic_rate = 0.01
        self.intermediate = nn.Sequential(
            nn.Linear(col_out, col_out),
            nn.ReLU(),
            nn.Dropout(dropout),
            nn.LayerNorm(col_out)
        )
        self.top_fc = nn.Linear(col_out, top_out)

    def _homeostatic_scaling(self, activations: torch.Tensor):
        current_activity = activations.mean(dim=(0, 2))
        with torch.no_grad():
            self.activity_avg = (1 - self.homeostatic_rate) * self.activity_avg + self.homeostatic_rate * current_activity
        scale = 1.0 / (self.activity_avg.unsqueeze(0).unsqueeze(2) + 1e-6)
        return activations * scale.detach()

    def _column_activations(self, x):
        pre = torch.stack([col(x) for col in self.columns], dim=1)
        w = torch.sigmoid(self.adj_weight)
        adj = self.adj_struct * w
        post = pre.clone()
        decay_factor = 0.9
        for iter_idx in range(self.message_iters):
            if self.use_attention:
                new_post = []
                for col_idx in range(self.num_columns):
                    center = post[:, col_idx, :]
                    adj_mask = adj[col_idx]
                    attn_weights = self.attention_gates[col_idx](
                        center, post, adj_mask
                    )
                    neigh_msg = torch.einsum('bc,bcd->bd', attn_weights, post)
                    new_col = F.relu(post[:, col_idx, :] + 0.2 * neigh_msg)
                    new_post.append(new_col)
                post = torch.stack(new_post, dim=1)
            else:
                neigh_msg = torch.einsum('ij,bjd->bid', adj, post)
                post = F.relu(post + 0.2 * neigh_msg)
            post = decay_factor * post + (1 - decay_factor) * pre
        post = self._homeostatic_scaling(post)
        return pre, post

    def _hebb_update(self, pre_acts: torch.Tensor, post_acts: torch.Tensor):
        pre = F.normalize(pre_acts.mean(dim=(0, 2)), dim=0)
        post = F.normalize(post_acts.mean(dim=(0, 2)), dim=0)
        delta_corr = torch.ger(pre, post)
        delta_anti = -0.1 * torch.ger(post, post)
        delta = delta_corr + delta_anti
        delta = delta * self.adj_struct
        with torch.no_grad():
            self.adj_weight += self.plasticity_lr * delta
            self.adj_weight.data = torch.tanh(self.adj_weight.data)

    def forward(self, x):
        _, post = self._column_activations(x)
        pooled = torch.mean(post, dim=1)
        features = self.intermediate(pooled)
        logits = self.top_fc(features)
        return logits

    def training_step(self, batch):
        x, y = batch
        pre, post = self._column_activations(x)
        pooled = torch.mean(post, dim=1)
        features = self.intermediate(pooled)
        logits = self.top_fc(features)
        loss = F.cross_entropy(logits, y)
        loss.backward()
        self._hebb_update(pre.detach(), post.detach())
        return loss

Мы получили нетрадиционную нейронную сеть всего за полторы сотни строк кода не самого хорошего качества. В ней не только используются аналоги биологических элементов мозга, но и само обучение работает в двух режимах параллельно:

  • Unsupervised (Hebbian): адаптирует структуру графа связей. Так обучаются элементы биологического мозга.
  • Supervised (Backprop): оптимизирует веса колонок и классификатора. Так обучаются искусственные нейронные сети.

Пришло время проверить получившуюся сеть на практике. Создадим второй файл под названием train.py

В нем не будет ничего интересного потому обойдемся комментариями прямо в коде:

#!/usr/bin/env python3

import torch
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
from brain import BrainNetwork

batch_size = 128 # размер батча, для расчетов на CPU уменьшите до 16-32
epochs = 10 # количство эпох
learning_rate = 1e-3 # шаг обучения
weight_decay = 1e-5 # L2-регуляризация весов

# гипермпараметры модели
hyperparams = {
    "num_columns": 64, # количество кортикальных колонок
    "in_dim": 784,
    "col_out": 128,
    "top_out": 10,
    "graph_k": 8,
    "graph_p": 0.15,
    "message_iters": 4,
    "plasticity_lr": 5e-5
}

# загружаем датасет, при первом запуск еон автоматически скачается
transform = transforms.Compose([transforms.ToTensor(), transforms.Lambda(lambda x: x.view(-1))])
train_set = datasets.MNIST(root='.', train=True, download=True, transform=transform)
test_set  = datasets.MNIST(root='.', train=False, download=True, transform=transform)
train_loader = DataLoader(train_set, batch_size=batch_size, shuffle=True)
test_loader  = DataLoader(test_set , batch_size=batch_size, shuffle=False)

# создаем модель
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = BrainNetwork(**hyperparams).to(device)
optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate, weight_decay=weight_decay)
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=len(train_loader)*12)

# начинаем обучение
for epoch in range(1, epochs+1):
    model.train()
    for batch_idx, (x, y) in enumerate(train_loader, start=1):
        x, y = x.to(device), y.to(device)
        optimizer.zero_grad()
        loss = model.training_step((x, y))
        optimizer.step()
        scheduler.step()

        # периодически выводим статистику
        if batch_idx % 100 == 0:
            model.eval()
            correct = total = 0
            with torch.no_grad():
                for x, y in test_loader:
                    x, y = x.to(device), y.to(device)
                    logits = model(x)
                    preds = logits.argmax(dim=1)
                    correct += (preds == y).sum().item()
                    total += y.size(0)
            acc = correct / total
            print(f'Epoch {epoch:02d} [{batch_idx}/{len(train_loader)}] Loss={loss.item():.4f} Test Accuracy={acc*100:.2f}%')

# сохраняем результат
state = {
    "epoch": epoch,
    "model_state_dict": model.state_dict(),
    "optimizer_state_dict": optimizer.state_dict(),
    "loss": loss.item(),
    "hyperparams": hyperparams
}

torch.save(state, "brain.pth")

Запустим 64 кортикальные колонки на 10 эпохах обучения: python train.py

Каждые 100 шагов будут выводиться данные о текущем уровне обучения:

Epoch 01 [100/469] Loss=0.2053 Test Accuracy=92.43%
Epoch 01 [200/469] Loss=0.1944 Test Accuracy=95.60%
Epoch 01 [300/469] Loss=0.0724 Test Accuracy=95.94%
Epoch 01 [400/469] Loss=0.1135 Test Accuracy=95.75%
Epoch 02 [100/469] Loss=0.0689 Test Accuracy=97.23%
Epoch 02 [200/469] Loss=0.0824 Test Accuracy=97.19%
Epoch 02 [300/469] Loss=0.0323 Test Accuracy=97.47%
Epoch 02 [400/469] Loss=0.0722 Test Accuracy=97.45%
Epoch 03 [100/469] Loss=0.0831 Test Accuracy=96.70%
Epoch 03 [200/469] Loss=0.0265 Test Accuracy=97.62%
Epoch 03 [300/469] Loss=0.0633 Test Accuracy=97.89%
Epoch 03 [400/469] Loss=0.0287 Test Accuracy=98.07%
Epoch 04 [100/469] Loss=0.0363 Test Accuracy=97.98%
Epoch 04 [200/469] Loss=0.0259 Test Accuracy=97.99%
Epoch 04 [300/469] Loss=0.0197 Test Accuracy=97.74%
Epoch 04 [400/469] Loss=0.0046 Test Accuracy=97.90%
Epoch 05 [100/469] Loss=0.0247 Test Accuracy=98.14%
Epoch 05 [200/469] Loss=0.0222 Test Accuracy=98.08%
Epoch 05 [300/469] Loss=0.0089 Test Accuracy=98.15%
Epoch 05 [400/469] Loss=0.0594 Test Accuracy=98.04%
Epoch 06 [100/469] Loss=0.0078 Test Accuracy=98.19%
Epoch 06 [200/469] Loss=0.0132 Test Accuracy=98.06%
Epoch 06 [300/469] Loss=0.0037 Test Accuracy=98.27%
Epoch 06 [400/469] Loss=0.0076 Test Accuracy=98.33%
Epoch 07 [100/469] Loss=0.0061 Test Accuracy=98.55%
Epoch 07 [200/469] Loss=0.0014 Test Accuracy=98.52%
Epoch 07 [300/469] Loss=0.0015 Test Accuracy=98.48%
Epoch 07 [400/469] Loss=0.0014 Test Accuracy=98.47%
Epoch 08 [100/469] Loss=0.0024 Test Accuracy=98.55%
Epoch 08 [200/469] Loss=0.0003 Test Accuracy=98.49%
Epoch 08 [300/469] Loss=0.0002 Test Accuracy=98.51%
Epoch 08 [400/469] Loss=0.0081 Test Accuracy=98.52%
Epoch 09 [100/469] Loss=0.0009 Test Accuracy=98.58%
Epoch 09 [200/469] Loss=0.0005 Test Accuracy=98.60%
Epoch 09 [300/469] Loss=0.0004 Test Accuracy=98.61%
Epoch 09 [400/469] Loss=0.0004 Test Accuracy=98.66%
Epoch 10 [100/469] Loss=0.0003 Test Accuracy=98.71%
Epoch 10 [200/469] Loss=0.0002 Test Accuracy=98.73%
Epoch 10 [300/469] Loss=0.0003 Test Accuracy=98.72%
Epoch 10 [400/469] Loss=0.0002 Test Accuracy=98.73%

Обучение на видеокарте потребовало примерно 3Gb видеопамяти и заняло 30 минут. Результат: 98.73% точности на тестовых образцах. Отличный результат на уровне современных традиционных моделей. Желающие могут самостоятельно поэкспериментировать с количеством кортикальных колон и эпох обучения для достижения точности 99+%

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

Модель продемонстрировала точность 98.73% на тестовой выборке MNIST после 10 эпох обучения, что сопоставимо с результатами лучших классических глубоких сверточных сетей на том же количестве эпох, при этом используя принципиально иную парадигму обучения.

В процессе обучения менялись не только параметры отдельных модулей (колонок), но и структура самой сети, то есть связи между модулями. Колонки, которые активировались одновременно при обработке похожих данных, автоматически укрепляли связи друг с другом и образовывали специализированные группы. Это похоже на то, как устроен биологический мозг: там нейроны тоже объединяются в группы для выполнения конкретных задач.

Текущая реализация с 64 колонками может быть расширена до сотен или тысяч модулей для решения более сложных задач, при этом структура малого мира обеспечит субквадратичный рост числа связей, что критично для вычислительной эффективности.

Несмотря на отличные результаты эта модель лишь основа, база для понимания и перехода к более «биологичным» искусственным нейронным сетям класса «Больной Ублюдок», у которых вообще отсутствуют привычные механизмы машинного обучения, такие как градиенты, обратное распространения ошибки и батчинг. Но об этом в следующий раз.

References

  1. Isong et al., Building Efficient Lightweight CNN Models, arXiv (2025)
  2. G. Moreni et al., Cell-type-specific firing patterns in a V1 cortical column model, PLoS Computational Biology (2025)
  3. P. Goltstein et al., A column-like organization for ocular dominance in mouse visual cortex, Nature (2025)
  4. M. Ghader at al., Backpropagation-free Spiking Neural Networks with the Forward-Forward Algorithm, arXiv (2025)
  5. Y. A. Sugimoto et al., Network structure influences self-organized criticality in small-world brain networks, Frontiers in Systems Neuroscience (2025)
  6. J. Kilgore et al., Biologically-informed excitatory and inhibitory ratio for robust spiking neural network training, Nature (2025)
  7. I. Ahokainen et al., A unified model of short- and long-term plasticity, bioRxiv (2025)
  8. N. Ravichandran et al., Unsupervised representation learning with Hebbian synaptic and structural plasticity in brain-like networks (2025)
  9. R. Zhang et al., Dynamic grouping of ongoing activity in V1 hypercolumns, NeuroImage (2025)
  10. J. Trajkovic et al., Top-down and bottom-up interactions rely on nested brain oscillations to shape rhythmic visual attention sampling, Pubmed (2025)


Проверено: CrX ()
Последнее исправление: CrX (всего исправлений: 1)

Не угадал автора по началу статьи.

В техническом плане тема может и интересная, но желание развивать данное направление категорически осуждаю.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от firkax

развивать данное направление категорически осуждаю

Осуждать надо не развитие, а нецелевое использование.

Ты же не осуждаешь изобретателя колеса за то, что существуют дебилы, гоняющие как сумасшедшие?

mord0d ★★★★★
()

Желающие могут самостоятельно поэкспериментировать […] для достижения точности 99+%

Стоит отметить, что точность в синтетике не означает качество. Собственно, для этого мы и сохраняем промежуточные эпохи — иначе можно было бы просто сохранять только "лучшее", остальное дропать.

mord0d ★★★★★
()
Ответ на: комментарий от mord0d

Его единственное допустимое целевое использование - на оборонные нужды и строго секретно. Но и там лучше было бы обойтись без, а то вдруг утечёт в гражданское применение.

firkax ★★★★★
()

Ого. Монументальненько. Интересно. Но ничего не понятно моему, казалось бы, топологически сложному мозгу :)

Вот ты натренировал цифарки распознавать. А дальше как развивается сложность? Эта часть останется циферным модулем внутри более сложной структуры? Или просто добавляем колонки и шмаляем разной неструктурированой инфой в него? Вот распознавание букв добавить, как? Как происходит усложнение? И не происходит ли падение точности именно в результате расширения всех этих моделей?

А дальше, вот мы имеем сетку, которая умеет только буквы и цифры распознавать. А расположение букв на листе, это чья задача?

Про распознавание больная тема. Недавно оцифровывал инструкцию по фотографиям. СканТэйлором выравнивал страницы и текст, убирая изгибы, а дальше только Тессеракт, а он делает кашу из разметки и шрифтов. Уже хотелось бы скармливать локальной модели фотки и получать сразу исправленные и с текстом правильно расставленным.

Извини, что влез в твою монументальщину прикладухой

PcheloBiaka
()
Ответ на: комментарий от PcheloBiaka

Тут, кстати, недавно, народ научил нейросетку распознавать выцветшие рукописи (!) со старорежимной орфографией…

Manuscript OCR: хабръ, гитхаб, гитверc

hobbit ★★★★★
()
Последнее исправление: hobbit (всего исправлений: 1)
Ответ на: комментарий от hobbit

Спасибо. Интересно. Почитаю потом. Но это не то. Они сами говорят, что ориентируются на рукописный дореформный текст. А у меня мануалы, схемы, картинки и всё это на русском, английском, немецком, итальянском, шведском и иногда на нескольких языках одновременно. Пробовал и разные онлайн распознавалки, но результат всегда очень нестабильный (хотя казалось бы, печатный текст), да это всё работает где-то там, в облаках. А я хочу своё. Чтобы в кармашке лежало

PcheloBiaka
()
Ответ на: комментарий от firkax

Не угадал автора по началу статьи.

Зато я угадал первого ответившего ненужниста.

желание развивать данное направление категорически осуждаю.

Занеси уже в профиль, чтоб каждый раз не писать.

dataman ★★★★★
()

вычислительные возможности сравнительно малы.

Это странное заявление, пока что объем информации перевариваемый биологическими мозгами не вывозит ни один ии. Память вот плохая это да.

Несмотря на отличные результаты эта модель лишь основа

Емнип проблемой такого подхода является ещё больший жор ресурсов.

ya-betmen ★★★★★
()

Очень интересно. Настоящее исследование. Спасибо, таких архитектур я ещё не видел.

anonymous_incognito ★★★★★
()
Ответ на: комментарий от PcheloBiaka

А дальше, вот мы имеем сетку, которая умеет только буквы и цифры распознавать.

На самом деле смысл статьи не в распознавании, а в примере действительно нетрадиционного подхода. Цифры тут использованы как уже традиционный, легкодоступный и просто лёгкий пример.

Для совремённых практических целей распознавания текста оно конечно не очень годно, но этой цели и не ставилось.

Интересно на досуге было бы попробовать на чём-то более обширном, вроде ImageNet.

anonymous_incognito ★★★★★
()
Ответ на: комментарий от anonymous_incognito

Это я понимаю. :) Глаз зацепился за распознавание и мои недавние страдания :) Я понимаю, что тут мои комментарии совсем не к месту :)

PcheloBiaka
()

Многобуквенный наукообразный бред.

sparkie ★★★★★
()
Ответ на: комментарий от PcheloBiaka

Вообще для OCR всё ещё рулит Finereader. По крайней мере для печатного текста, отсканированного с нормальным разрешением. Ну да, проприетарное и пользовательских версий для Linux, насколько знаю, до сих пор не появилось.

anonymous_incognito ★★★★★
()

Ничего не понятно, но очень интересно. Отдельное спасибо, что ставишь пробел перед скобкой открывающей, а не как у-мамы-программисты.

Mobutu_Sese_Seko
()
Ответ на: комментарий от dataman

Это другое. Там просто список ненужного мусора. Может быть вредного в рамках ИТ (тем что засоряет его), но не более. А тут именно активно вредоносная технология для всего мира, ведущая общество к разрушению.

firkax ★★★★★
()
Ответ на: комментарий от PcheloBiaka

Сеть можно обучить и буквам, но суть статьи была не в задаче, а в архитектуре. Я взял самую простую задачу для наглядности и понимания архитектуры.

Если это будет интересно, я разовью дальше биологический подход к созданию архитектур нейронных сетей пока не упремся в необходимость использования нейроморфных процессоров или меня не сожгут на костре противники прогресса из комментариев выше.

Для вашей же прикладной задачи подойдет уже готовое решение без Tesseract, модель Qwen2.5-VL-7B-Instruct (~16Gb). Если есть видеокарта которая потянет модель, то все довольно просто:

#!/usr/bin/env python3

from transformers import Qwen2_5_VLForConditionalGeneration, AutoTokenizer, AutoProcessor
from qwen_vl_utils import process_vision_info

# параметры для изменения
image_path = "/путь/к/вашему/изображению" # можно https://...
max_new_tokens = 1024

model_path_or_name = "Qwen/Qwen2.5-VL-7B-Instruct"
prompt = """
Ты — система оптического распознавания текста (OCR).
Твоя задача - точно извлечь ВЕСЬ текст с предоставленного изображения и сохранить его визуальную структуру.

Правила:
- Переписывай текст дословно, без исправлений, интерпретаций и сокращений
- Сохраняй язык оригинала
- Сохраняй регистр букв
- Сохраняй пунктуацию и специальные символы
- Передавай переносы строк и абзацы
- Сохраняй порядок чтения: сверху вниз, слева направо
- Заголовки, списки и нумерацию сохраняй как в оригинале
- Таблицы оформляй в виде Markdown-таблиц
- Если текст или элемент частично нечитаем — укажи [неразборчиво]
- Если текста на изображении нет — ответь: "Текст не обнаружен"

Вывод:
- Выводи ТОЛЬКО распознанный текст
- Не добавляй комментариев, пояснений или служебных сообщений
"""

model = Qwen2_5_VLForConditionalGeneration.from_pretrained(model_path_or_name, torch_dtype="auto", device_map="auto")
processor = AutoProcessor.from_pretrained(model_path_or_name)

messages = [
    {
        "role": "user",
        "content": [
            {
                "type": "image",
                "image": image_path,
            },
            {
                "type": "text", 
                "text": prompt
            },
        ],
    }
]

text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
image_inputs, video_inputs = process_vision_info(messages)
inputs = processor(text=[text], images=image_inputs, videos=video_inputs, padding=True, return_tensors="pt")
inputs = inputs.to("cuda")
generated_ids = model.generate(**inputs, max_new_tokens=max_new_tokens)
generated_ids_trimmed = [out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)]
output_text = processor.batch_decode(generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False)
print(output_text)

Скрипт сам скачает модель при первом запуске и выведет весь текст из изображения.

Если нужной карты под рукой нет то можно попробовать ее же на CPU через Ollama указав промт из скрипта в качестве текста вопроса. Учтите что лучше всего модель распознает данные с изображений размером до 1280px, но ничего не мешает закидывать в нее изображения большего размера.

Есть еще state-of-the-art вариант на Qwen 3, но там нужна хорошая видеокарта тк идет связка из двух моделей (embedding и scoring), а сам подход достоин отдельной статьи.

Obezyan
() автор топика
Последнее исправление: Obezyan (всего исправлений: 3)
Ответ на: комментарий от Obezyan

Сеть можно обучить и буквам, но суть статьи была не в задаче, а в архитектуре.

Нет, я про другое. Доеду до дома, прочитаю твою статью и код ещё раз и попробую раскрыть свою мысль почеловечески. А может лучше отдельной темой и тебя просто позову, чтобы тут умную дискуссию не портить моими фантазиями

PcheloBiaka
()

Простые топологии искусственных нейронных сетей показывают точность от 80% до 98%. Специализированные сети, «заточенные» под задачу, достигают 99+% [1].

Сложная топология (человеческий мозг) в среднем демонстрирует точность ~99.5‑99.7% (данные приблизительные, поскольку детальные исследования на людях пока что проведены не были).

99+%

~99.5‑99.7%

Звучит как будто искусственные нейронки распознают цифры на равных с животными и какие-то фундаментальные улучшения не так уж востребованы.

KivApple ★★★★★
()
Ответ на: комментарий от KivApple
99+%

~99.5‑99.7%

Звучит как будто искусственные нейронки распознают цифры на равных с животными и какие-то фундаментальные улучшения не так уж востребованы.

Похоже, это тот случай, когда то, как именно сформулирована мысль, влияет на её восприятие некоторыми людьми.

Смотри, а если вот так?:

Сложная топология (человеческий мозг) в совершает от 0.3% до 0.5% ошибок (данные приблизительные, поскольку детальные исследования на людях пока что проведены не были).

Доля ошибок специализированных сетей, «заточенных» под задачу оказалась вдвое или втрое больше, соответственно, и составляет не более 1%.

Немного меняется восприятие, если так?

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 1)
Ответ на: комментарий от firkax

А я вот недавно AI spellchecker использовал для улучшения документации моего проекта. Опечатки мог бы найти тупой спеллчекер, но ведь ещё есть опечатки вида there/these, then/than/that и так далее, которые превращают слово в другое существующее слово. Рекомендации по стилю он тоже даёт очень хорошие, но я не стал им следовать.

Это тоже недопустимое применение?

shdown
()
Последнее исправление: shdown (всего исправлений: 1)
Ответ на: комментарий от shdown

Это вообще незначительная мелочь во всех отношениях. На орфографическое качество документации всегда всем плевать. Однако замечу следующее:

1) нетупой спеллчекер можно сделать и алгоритмически, и я думаю такие есть (но сам никогда не пользовался ничем кроме тех что в браузере из коробки встроены - и то только потому что они там уже есть)

2) языковой барьер как явление полезен, он способствует структурированию общества, а если эта штука его массово устранит - это один из однозначно вредительских её результатов, направленных на разрушение структуры мирового общества

А вообще печально наблюдать стремление к личной выгоде с закрыванием глаз на глобально-деструктивный побочный эффект.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от shdown

Мне приходится постоянно со спеллчекерами бороться - когда у тебя словарный запас до 80 тыс. слов русского языка плюс еще когда пользуешься словообразованием, то тут засада: «понималки» не хватает у машины - а вот у людей "понималка" есть, и оно ему понятно.

necromant ★★
()

Выглядит как-то подозрительно, но если всё описанное правда работает так хорошо, то могу только похлопать.

daniyal
()

В качестве эталона будем использовать распознавание цифр из датасета MNIST. … Результат: 98.73% точности на тестовых образцах.

Результат хороший. Но в википедии пишут, что даже KNN и метод опорных векторов достигают точности 99,5 %, а эти алгоритмы никак не претендуют на наличие самосознания. Для MNIST нейросети идут на уровне старых добрых алгоритмов и потому этот датасет не особо интересен.

Далее я покажу один фокус. На гитхабе есть MNIST переведённый в csv. То есть формат, который можно обрабатывать питоном без использования сторонних библиотек вообще. Давайте напишем коротенький скрипт, который будет грузить эти csv и решать задачу в лоб с помощью KNN, упрощенного до 1NN:

import csv
import time
import sys


class Digit:

    def __init__(self, key, value):
        self.key = int(key)
        self.value = 0
        temp = [1 if int(v) > 32 else 0 for v in value]
        for i, v in enumerate(temp):
            self.value += v << i

    def compare(self, digit):
        if sys.version_info[1] >= 10:
            return (self.value ^ digit.value).bit_count()
        else:
            return bin(self.value ^ digit.value).count("1")


digits = []
t1 = time.time()

# training

with open('mnist_train.csv', newline='') as csvfile:
    csvreader = csv.reader(csvfile, delimiter=',')
    for row in csvreader:
        digits.append(Digit(row[0], row[1:]))

print('training complete, time:', time.time() - t1)
input('press any key...')

# test

MAX_DIFF = 256 * 28 * 28
t1 = time.time()

with open('mnist_test.csv', newline='') as csvfile:
    csvreader = csv.reader(csvfile, delimiter=',')
    correctSum = 0
    allSum = 0
    for row in csvreader:
        digit = Digit(row[0], row[1:])
        minDiff = MAX_DIFF
        minKey = -1
        for trainDigit in  digits:
            diff = trainDigit.compare(digit)
            if diff < minDiff:
                minDiff = diff
                minKey = trainDigit.key
        if digit.key == minKey:
            correctSum += 1
        allSum += 1
        print(allSum, digit.key, minKey, minDiff, 'accuracy:', 100 * correctSum / allSum)


print('test complete, time:', time.time() - t1)

«Обучение» в этом коде идёт без всяких видеокарт и занимает у меня около 10 секунд. Точность распознавания - 96,63% при скорости распознавания около 20 мс.

То есть база тут такая, что самым тупым способом мы получим точность 95+. Не нужен торч, не нужен даже нампай.

Kogrom
()
Ответ на: комментарий от Kogrom

Вы не поняли суть статьи. Цель не в том чтобы достичь максимальной точности.

При введении «биологичности» в архитектуру неизбежно падает точность. Суть статьи - показать как можно добавлять биологичность не теряя в точности на простом примере.

В следующей статье (после отпуска) я разберу «больных ублюдков» у которых еще больше общего с биологическими мозгами при вменяемой точности.

KNN или затюненная CNN могут показать и 99.9%, но при этом они просто числовые сита, в которых шаг вправо, шаг влево и все перестает работать. Биологические архитектуры напротив, показывают высокую адаптируемость к изменениям входных сигналов.

Например, у людей потерявших зрение кортикальные колонки не деградируют, а начинают получать сигнал от колонок связанных со слухом, преобразуют свою структуру графа малого мира и помогают обрабатывать слуховую информацию вместо зрительной. Именно поэтому слепые люди гораздо лучше слышат (дальше, четче различают при шуме и тд).

Obezyan
() автор топика
Ответ на: комментарий от firkax

Мда.

нетупой спеллчекер можно сделать и алгоритмически, и я думаю такие есть

Хотелось бы увидеть пруфы.

языковой барьер как явление полезен

И много софта у тебя в системе есть, у которого есть документация на русском (точнее даже нет документации на английском)? Если бы ты не знал английского, ты бы не смог им пользоваться. Какие-то переводы документации и гайды в интернете тоже устраняют языковой барьер, поэтому не считаются.

shdown
()
Последнее исправление: shdown (всего исправлений: 1)
Ответ на: комментарий от Obezyan

KNN или затюненная CNN могут показать и 99.9%, но при этом они просто числовые сита, в которых шаг вправо, шаг влево и все перестает работать. Биологические архитектуры напротив, показывают высокую адаптируемость к изменениям входных сигналов.

Биологические архитектуры - это непонятно что. Если же говорить конкретно про нейронные сети, то они хуже адаптируются, например, при изменении датасета мы KNN можем просто дообучить новыми данными, а нейросетку надо полностью переобучать с использованием старых данных. Или у Вас дообучается?

С другой стороны, можно провести эксперимент. Инвертируйте, например, цвета в тестовой выборке MNIST и запустите тест без обучения. Адаптируется она или нет? Увеличьте размер фото в несколько раз. Что будет? Адаптируется сама или нужно будет вводить дополнительный алгоритм в предобработке?

Kogrom
()
Ответ на: комментарий от shdown

Хотелось бы увидеть пруфы.

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

И много софта у тебя в системе есть

Какая разница какая документация у софта на моём компе? Я про само явление: люди говорят на разных языках, между разными языками имеется барьер. Да, он преодолимый, но его преодоление происходит только при явном желании это сделать и чьих-то затратах сил на это. Забыть о его существовании невозможно. Если же всё везде будет сразу автопереводиться на твой язык в высоком качестве, ты можешь и позабыть что у других людей он может быть другим.

firkax ★★★★★
()
Ответ на: комментарий от Kogrom

Биологические архитектуры - это непонятно что.

Эта статья как раз введение в них. Дальше будет ещё больший уход от классических архитектур нейронных сетей в более сложные, повторяющиеся элементы биологического мозга.

Грубо говоря, это водная статья показывающая как решить 2+2=4 на бумаге. Она не соревнуются с калькулятором, который сделает это быстрее, она показывает сам подход.

Инвертируйте, например, цвета в тестовой выборке MNIST и запустите тест без обучения.

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

Увеличьте размер фото в несколько раз. Что будет?

Тоже адаптируется, внутри идёт определение углов и наклонов отдельных линий, а не запоминания фигур в целом (грубая аналогия).

В следующей статье об этом будет подробнее.

Obezyan
() автор топика
Ответ на: комментарий от Obezyan

Пользуясь случаем хочу спросить, а что посоветуешь под примерно такую задачу:

Надо как-то научиться вести поиск в массиве изображений не просто похожих в целом изображений, а выделять в каждом отдельно интересующую (значимую) часть изображения и сравнивать поисковый запрос (изображение) с этой частью, а не только с целым изображением.

Например, если задать поиск какой-нибудь эмблемы, то чтобы её можно было найти у кого-то на рукаве на фотографии. При этом изображение целиком не будет схоже с поисковым запросом, т.к. дескриптор, полученный для поиска с применением нейросети сильно отличается. А дескриптор части изображения может быть близок.

Пока что на ум приходит разбиение каждого изображения в массиве на значимые части, каким-то образом выделенные с помощью нейросетки и потом индексация всех частей с поиском уже среди них. Но что-то некрасиво и ресурсоёмко.

Может есть какие-то архитектуры и метрики, которые умели бы произвольную малую часть среди большого искать?

anonymous_incognito ★★★★★
()
Ответ на: комментарий от anonymous_incognito

Я бы попробовал 2 варианта и выбрал более подходящий по результату экспериментов:

  1. Определяем элементы на эмблеме через YOLO/PRN. Создаём для каждой найденной части эмбеддинги с помощью CLIP/Dyno. Храним их в векторной базе Qdrant/Milvus. Потом ищем с помощью ANN. Это подход 2025 года.

  2. Создаем эмбеддинги с помощью Qwen 3 VL embeddings. Сохраняем их также в векторной бд или FAISS. Для поиска используем Qwen 3 VL Reranking. Это подход 2026 года.

Помните, что это все в общих чертах и на разных этапах могут понадобится различные предобработки или файнтюнинг моделей.

Obezyan
() автор топика
Последнее исправление: Obezyan (всего исправлений: 1)
Ответ на: комментарий от Obezyan

Спасибо. Быстро глянул, но это действительно надо экспериментировать. 2-й вариант выглядит заманчивее, если оборудование потянет :)

anonymous_incognito ★★★★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.