🧠 Слой 2: Modality Encoders (Сенсорная кора)¶
Подробное описание архитектуры и работы¶
Статус: 🟡 Частично реализовано (Этап E — Text Encoder) · 80/80 тестов ✅
text_encoder.py— TextEncoder (sentence-transformers 768d / navec 300d fallback, 4 режима) ✅EncodedPerceptdataclass — реализован вbrain/core/contracts.py⬜VisionEncoder— Этап J (мультимодальное расширение) ⬜AudioEncoder— Этап J (мультимодальное расширение) ⬜TemporalEncoder— Этап J (мультимодальное расширение) ⬜EmbeddingCache— Post-MVP
Что такое Сенсорная кора в биологии¶
В человеческом мозге Сенсорная кора — это набор специализированных зон: - Зрительная кора (затылочная доля) — обрабатывает визуальные сигналы - Слуховая кора (височная доля) — обрабатывает звуки и речь - Соматосенсорная кора — обрабатывает осязание, температуру, боль - Речевая зона Вернике — понимание языка
Каждая зона специализирована под свою модальность и преобразует сырые сигналы в нейронные представления (паттерны активации), которые можно сравнивать, ассоциировать и хранить.
Аналогично работают наши Modality Encoders — каждый преобразует сырой PerceptEvent в вектор фиксированной размерности (эмбеддинг).
Роль в искусственном мозге¶
PerceptEvent(s) от Таламуса
│
▼
┌─────────────────────────────────────────────────────────┐
│ MODALITY ENCODERS │
│ │
│ ┌──────────────────┐ │
│ │ TextEncoder │ PerceptEvent(modality="text") │
│ │ │ → вектор 768d │
│ │ sentence- │ → ключевые слова │
│ │ transformers │ → тип сообщения │
│ │ (paraphrase- │ → язык │
│ │ multilingual) │ → тональность │
│ └──────────────────┘ │
│ │
│ ┌──────────────────┐ │
│ │ VisionEncoder │ PerceptEvent(modality="image") │
│ │ │ → вектор 512d │
│ │ CLIP ViT-B/32 │ → объекты и сцены │
│ │ (CPU-only) │ → текстовое описание │
│ │ │ → spatial features │
│ └──────────────────┘ │
│ │
│ ┌──────────────────┐ │
│ │ AudioEncoder │ PerceptEvent(modality="audio") │
│ │ │ → вектор 768d (из транскрипта) │
│ │ Whisper → │ → акустические признаки │
│ │ TextEncoder │ → эмоциональный тон │
│ │ │ → временные сегменты │
│ └──────────────────┘ │
│ │
│ ┌──────────────────┐ │
│ │ TemporalEncoder │ PerceptEvent(modality="video") │
│ │ │ → последовательность векторов │
│ │ кадры + аудио │ → временные зависимости │
│ │ → усреднение │ → ключевые моменты │
│ └──────────────────┘ │
│ │
└──────────────────────┬──────────────────────────────────┘
│
▼
EncodedPercept(s)
{vector, modality, metadata, keywords, ...}
│
▼
Cross-Modal Fusion (Слой 3)
Что такое эмбеддинг (вектор)¶
Эмбеддинг — это числовое представление смысла в многомерном пространстве.
"нейрон" → [0.12, -0.34, 0.87, 0.05, ..., 0.23] (768 чисел)
"клетка" → [0.11, -0.31, 0.84, 0.07, ..., 0.21] (768 чисел)
"автомобиль" → [-0.45, 0.67, -0.12, 0.89, ..., -0.34]
cosine_similarity("нейрон", "клетка") = 0.94 ← близко по смыслу
cosine_similarity("нейрон", "автомобиль") = 0.12 ← далеко по смыслу
Ключевое свойство: похожие по смыслу объекты → близкие векторы.
Это позволяет сравнивать текст с изображением, аудио с текстом и т.д.
Компоненты Modality Encoders¶
1. TextEncoder — кодирование текста¶
Модель: sentence-transformers/paraphrase-multilingual-mpnet-base-v2
| Параметр | Значение |
|---|---|
| Размер вектора | 768d |
| Поддержка языков | 50+ (включая RU и EN) |
| Размер модели | ~1.1 GB |
| Скорость (CPU) | ~50–100 мс на предложение |
| Контекстное окно | 512 токенов |
Что делает:
1. Принимает текстовый чанк из PerceptEvent
2. Токенизирует (WordPiece/BPE)
3. Прогоняет через трансформер (12 слоёв, 768 hidden)
4. Mean pooling → вектор 768d
5. Нормализация (L2) → единичная длина
6. Дополнительно извлекает:
- ключевые слова (TF-IDF или YAKE)
- тип сообщения (вопрос / утверждение / команда)
- тональность (позитив / нейтраль / негатив)
- язык (ru / en / mixed)
Пример:
text = "Нейрон — это основная клетка нервной системы"
encoded = TextEncoder.encode(text)
# encoded.vector: [0.12, -0.34, 0.87, ...] (768d)
# encoded.keywords: ["нейрон", "клетка", "нервная система"]
# encoded.msg_type: "statement"
# encoded.language: "ru"
# encoded.sentiment: 0.0 (нейтральный)
Fallback (если модель не загружена): - TF-IDF вектор (bag-of-words, ~10 000d sparse) - Работает без GPU и без интернета
2. VisionEncoder — кодирование изображений¶
Модель: openai/clip-vit-b-32 (CPU-only)
| Параметр | Значение |
|---|---|
| Размер вектора | 512d |
| Входное разрешение | 224×224 px |
| Размер модели | ~0.6 GB |
| Скорость (CPU) | ~200–500 мс на изображение |
| Особенность | общее пространство с текстом |
Что делает:
1. Принимает PIL.Image из PerceptEvent
2. Ресайзит до 224×224
3. Нормализует пиксели
4. Прогоняет через ViT-B/32 (Vision Transformer)
5. Проецирует в 512d пространство
6. Нормализация (L2)
7. Дополнительно:
- OCR-текст → TextEncoder → 768d (отдельно)
- Генерация caption (описания) через BLIP или простые правила
- Детекция объектов (опционально, через YOLO nano)
Ключевое свойство CLIP:
TextEncoder("нейрон под микроскопом") → вектор 512d
VisionEncoder(фото_нейрона.jpg) → вектор 512d
cosine_similarity(текст_вектор, фото_вектор) = 0.89 ← CLIP умеет это!
CLIP обучен на парах (текст, изображение), поэтому его пространство уже выровнено между модальностями.
Пример:
image = PIL.Image.open("схема_нейрона.png")
encoded = VisionEncoder.encode(image)
# encoded.vector: [0.45, -0.12, 0.78, ...] (512d)
# encoded.ocr_text: "Нейрон, Аксон, Дендрит"
# encoded.description: "Схематическое изображение нейрона"
# encoded.objects: ["нейрон", "аксон", "дендрит"]
Fallback: - Только OCR → TextEncoder (без визуального понимания) - Работает если CLIP не загружен
3. AudioEncoder — кодирование аудио¶
Двухэтапный процесс:
Аудиофайл
│
▼
[Этап 1] Whisper medium → транскрипт + временные метки
│
▼
[Этап 2] TextEncoder → вектор 768d
│
▼
EncodedPercept(vector=768d, transcript=..., time_segments=[...])
Модели:
| Модель | Размер | Скорость (CPU) | Точность |
|---|---|---|---|
| Whisper tiny | 75 MB | ~5x realtime | 85% WER |
| Whisper base | 145 MB | ~3x realtime | 90% WER |
| Whisper medium | 1.5 GB | ~1x realtime | 96% WER |
| Whisper large | 3 GB | ~0.5x realtime | 98% WER |
Выбор: Whisper medium — оптимальный баланс качество/скорость для CPU.
Что делает: 1. Загружает аудио (wav/mp3/ogg) 2. Конвертирует в 16kHz mono float32 3. Whisper → транскрипт с временными метками по сегментам 4. Каждый сегмент → TextEncoder → вектор 768d 5. Дополнительно: - определение языка (Whisper встроенный) - confidence score для каждого сегмента - детекция тишины / шума
Пример:
audio_path = "лекция_нейробиология.mp3"
segments = AudioEncoder.encode(audio_path)
# segments[0]:
# vector: [0.23, -0.45, 0.67, ...] (768d)
# transcript: "Нейрон — это основная клетка..."
# time_start: 0.0
# time_end: 15.3
# confidence: 0.94
# language: "ru"
Акустические признаки (опционально): - Темп речи (слов/мин) - Паузы и их длительность - Громкость (RMS) - Эмоциональный тон (через librosa)
4. TemporalEncoder — кодирование видео¶
Видео = последовательность кадров + аудиодорожка.
video.mp4
│
├──► Кадры (каждые N секунд) → VisionEncoder → [v1, v2, v3, ...]
│
└──► Аудиодорожка → AudioEncoder → [a1, a2, a3, ...]
│
▼
Temporal Fusion:
усреднение / attention pooling
│
▼
вектор 512d (видео-представление)
Стратегии семплирования кадров:
| Стратегия | Описание | Когда использовать |
|---|---|---|
| Uniform | каждые N секунд | длинные видео |
| Scene-based | при смене сцены | динамичный контент |
| Keyframe | I-кадры из кодека | быстро, без анализа |
| Dense | каждый кадр | короткие клипы < 30с |
Для CPU-only: Uniform (каждые 5 секунд) — оптимально.
Выходной формат: EncodedPercept¶
@dataclass
class EncodedPercept:
# Исходное событие
percept_event: PerceptEvent
# Основной вектор
vector: np.ndarray # 768d (текст/аудио) или 512d (изображение)
vector_dim: int # 768 или 512
# Нормализованный вектор (для cosine similarity)
vector_norm: np.ndarray # L2-нормализованный
# Текстовые признаки (для text/audio)
keywords: List[str] # ["нейрон", "клетка", "система"]
message_type: str # "question" | "statement" | "command"
language: str # "ru" | "en" | "mixed"
sentiment: float # -1.0 (негатив) ... +1.0 (позитив)
# Визуальные признаки (для image/video)
objects: List[str] # ["нейрон", "аксон"]
description: str # "Схема нейрона с подписями"
ocr_text: str # "Нейрон, Аксон, Дендрит"
# Аудио признаки (для audio/video)
transcript: str # полный транскрипт
time_segments: List[dict] # [{text, start, end, confidence}, ...]
# Метаданные кодирования
encoder_model: str # "paraphrase-multilingual-mpnet-base-v2"
encoding_time_ms: float # время кодирования
confidence: float # уверенность энкодера 0.0–1.0
Сравнение векторов между модальностями¶
После кодирования можно сравнивать объекты разных модальностей:
Текст: "нейрон под микроскопом" → T_vec (768d)
Изображение: фото_нейрона.jpg → I_vec (512d)
Аудио: "...это нейрон..." → A_vec (768d)
Для сравнения T_vec и I_vec нужна проекция в общее пространство:
T_vec → Linear(768→512) → T_vec_proj (512d)
cosine_similarity(T_vec_proj, I_vec) = 0.87 ← это задача Слоя 3
Слой 3 (Cross-Modal Fusion) занимается именно этим выравниванием.
Ресурсный бюджет (CPU-only, 32 GB RAM)¶
| Энкодер | RAM | CPU потоки | Время/запрос |
|---|---|---|---|
| TextEncoder (multilingual-mpnet) | ~1.1 GB | 2–4 | 50–100 мс |
| VisionEncoder (CLIP ViT-B/32) | ~0.6 GB | 2–4 | 200–500 мс |
| AudioEncoder (Whisper medium) | ~1.5 GB | 4–8 | ~1x realtime |
| TemporalEncoder | зависит от кадров | 4–8 | ~2–5с/мин видео |
| Итого | ~3.2 GB | 8–12 | — |
Стратегия при нехватке ресурсов:
CPU > 70%: отложить VisionEncoder в очередь
CPU > 85%: только TextEncoder (самый лёгкий)
RAM > 28GB: выгрузить AudioEncoder (Whisper) из памяти
Кэширование эмбеддингов¶
Кодирование — дорогая операция. Поэтому:
Запрос на кодирование файла X
│
▼
Проверить кэш (SHA256 файла → вектор)
│
├── Кэш есть → вернуть кэшированный вектор (0 мс)
└── Кэша нет → кодировать → сохранить в кэш → вернуть
Кэш хранится в:
brain/data/embeddings_cache/
├── text_cache.npz (SHA256 → вектор 768d)
├── vision_cache.npz (SHA256 → вектор 512d)
└── audio_cache.npz (SHA256 → вектор 768d)
Что Modality Encoders НЕ делают¶
| Задача | Кто делает |
|---|---|
| Чтение файлов с диска | Perception Layer (Слой 1) |
| Выравнивание text↔image пространств | Cross-Modal Fusion (Слой 3) |
| Сохранение в память | Memory System (Слой 4) |
| Поиск по смыслу | Memory System (Слой 4) |
| Рассуждение | Cognitive Core (Слой 5) |
Encoders только преобразуют сырые данные в векторы.
Статус реализации¶
| Компонент | Статус | Файл |
|---|---|---|
EncodedPercept dataclass |
✅ Реализовано (Этап A) | brain/core/contracts.py |
TextEncoder |
✅ Реализовано (Этап E, 80 тестов) | brain/encoders/text_encoder.py |
VisionEncoder |
⬜ Этап J | brain/encoders/vision_encoder.py |
AudioEncoder |
⬜ Этап J | brain/encoders/audio_encoder.py |
TemporalEncoder |
⬜ Этап J | brain/encoders/temporal_encoder.py |
EmbeddingCache |
⬜ Post-MVP | brain/encoders/embedding_cache.py |
Зависимости (Python пакеты)¶
sentence-transformers>=2.2.0 — TextEncoder (multilingual-mpnet)
torch>=2.0.0 — базовый фреймворк (CPU-only)
transformers>=4.30.0 — CLIP, Whisper
openai-whisper>=20231117 — AudioEncoder
Pillow>=10.0.0 — VisionEncoder (загрузка изображений)
numpy>=1.24.0 — векторные операции
Итог: место Сенсорной коры в системе¶
Таламус → [СЕНСОРНАЯ КОРА] → Cross-Modal Fusion → Memory → Cognition
│
├── TextEncoder: текст → 768d вектор
├── VisionEncoder: изображение → 512d вектор
├── AudioEncoder: аудио → 768d вектор
└── TemporalEncoder: видео → 512d вектор
│
└── Все векторы нормализованы (L2)
Все объекты в одном "языке" — числах
Готовы к сравнению и хранению
Сенсорная кора — это переводчик с языка данных на язык смысла (чисел).
После неё мозг работает только с векторами — независимо от исходной модальности.