Классическая нейросеть работает в вещественном поле
Для реализации физики ЕТВП необходимо полноценное погружение в комплексное поле
Если вход
Переводим всю сеть в комплексное пространство:
Активация также комплексная. Используем комплексный tanh:
где
Теперь фаза и амплитуда перетекают друг в друга — как в реальном поле.
При статичной классификации (например, MNIST) параметр
Архитектура разворачивается как стохастическое дифференциальное уравнение (SDE):
где:
-
$z \in \mathbb{C}$ — комплексное состояние сети; -
$f(z, W)$ — детерминированная динамика; -
$\mu$ — обучаемая вязкость вакуума; -
$\eta$ — амплитуда стохастической самокоррекции; -
$\beta$ — чувствительность к шуму; -
$\xi(t) \sim \mathcal{N}(0, 1)$ — гауссов шум.
Коэффициент
Такая сеть естественно работает с:
- видео (время
$t$ — номер кадра); - аудио (время
$t$ — сэмпл сигнала); - физическими процессами (температура, давление, вибрация);
- финансовыми временными рядами (тики, свечи).
Коэффициент перед Лапласианом зафиксирован вручную.
Вводим обучаемый параметр вязкости
где
Тогда резонансный фильтр принимает вид:
Сеть сама решает, насколько жёстким должно быть «давление вакуума» на каждом уровне.
где:
-
$z(t) \in \mathbb{C}$ — комплексное состояние; -
$W, b \in \mathbb{C}$ — комплексные веса и смещения; -
$\mu = \text{softplus}(\theta_\mu)$ — обучаемая вязкость; -
$\eta$ — амплитуда шума; -
$\xi(t) \sim \mathcal{N}(0, 1)$ — стохастический член; -
$\nabla^2 z$ — лапласиан (пространственная регуляризация).
где
Проекция выполняется в режиме no_grad, что гарантирует стабильность обучения без разрушения градиентного потока.
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
class ETVP_ComplexLayer(nn.Module):
def __init__(self, dim_in, dim_out, R_max=1.0, eta_init=0.01, beta_init=0.1):
super().__init__()
# Инициализация комплексных весов (Kaiming / Xavier адаптация для комплексных чисел)
bound = (2.0 / dim_in) ** 0.5
self.W_re = nn.Parameter(torch.randn(dim_in, dim_out) * bound)
self.W_im = nn.Parameter(torch.randn(dim_in, dim_out) * bound)
self.b_re = nn.Parameter(torch.zeros(dim_out))
self.b_im = nn.Parameter(torch.zeros(dim_out))
# Обучаемая вязкость вакуума
self.theta_mu = nn.Parameter(torch.tensor(0.0))
# Параметры стохастической самокоррекции
self.eta = nn.Parameter(torch.tensor(eta_init))
self.beta = nn.Parameter(torch.tensor(beta_init))
self.R_max = R_max
def _project_weights(self):
"""Z-принцип: Стабилизация через проекцию на сферу без разрушения градиентов"""
with torch.no_grad():
W_norm = torch.sqrt(self.W_re ** 2 + self.W_im ** 2).sum()
if W_norm > self.R_max:
scale = self.R_max / (W_norm + 1e-8)
self.W_re.copy_(self.W_re * scale)
self.W_im.copy_(self.W_im * scale)
def complex_mul(self, x_re, x_im):
"""Полноценное комплексное линейное преобразование"""
out_re = x_re @ self.W_re - x_im @ self.W_im + self.b_re
out_im = x_re @ self.W_im + x_im @ self.W_re + self.b_im
return out_re, out_im
def complex_tanh(self, re, im):
"""Комплексная функция активации"""
return torch.tanh(re), torch.tanh(im)
def laplacian(self, z):
"""Безопасный дискретный лапласиан вдоль батча (без in-place операций)"""
if z.size(0) <= 2:
return torch.zeros_like(z)
# Добавляем padding (зеркальное отражение), чтобы избежать краевых эффектов
z_padded = F.pad(z.unsqueeze(0).unsqueeze(0), (0, 0, 1, 1), mode='reflect').squeeze()
lap = z_padded[:-2] - 2 * z_padded[1:-1] + z_padded[2:]
return lap
def forward(self, x_re, x_im, dt=0.1):
# 1. Применяем Z-принцип (проекция весов)
self._project_weights()
# 2. Детерминированная динамика (Полевое смещение + активация)
f_re, f_im = self.complex_mul(x_re, x_im)
f_re, f_im = self.complex_tanh(f_re, f_im)
# 3. Вычисление обучаемой вязкости вакуума
mu = F.softplus(self.theta_mu)
# Дифференциальный оператор (давление среды)
lap_re = self.laplacian(x_re)
lap_im = self.laplacian(x_im)
# 4. Стохастическая самокоррекция (шум под упругим давлением градиентного поля)
noise_re = self.eta * torch.randn_like(x_re) * torch.tanh(self.beta * x_re.abs())
noise_im = self.eta * torch.randn_like(x_im) * torch.tanh(self.beta * x_im.abs())
# 5. Интегрирование по методу Эйлера-Маруямы (Neural ODE / SDE шаг)
# Сигнал эволюционирует: z(t+dt) = z(t) + dt * (f(z) - mu*Laplacian + Шум)
# (Для стабильности градиентов шум масштабируется как sqrt(dt), согласно физике броуновского движения)
sqrt_dt = math.sqrt(dt)
z_re_next = x_re + dt * (f_re - mu * lap_re) + sqrt_dt * noise_re
z_im_next = x_im + dt * (f_im - mu * lap_im) + sqrt_dt * noise_im
return z_re_next, z_im_next
# --- Сквозной тест схемы обучения (Проверка Autograd) ---
if __name__ == "__main__":
layer = ETVP_ComplexLayer(dim_in=16, dim_out=16)
# Эмулируем комплексный входной сигнал (например, батч 10 элементов, 16 признаков)
x_re = torch.randn(10, 16, requires_grad=True)
x_im = torch.randn(10, 16, requires_grad=True)
# Моделируем физический процесс: пропускаем сигнал через 5 шагов времени (интегрирование ODE)
curr_re, curr_im = x_re, x_im
for step in range(5):
curr_re, curr_im = layer(curr_re, curr_im, dt=0.1)
# Считаем лосс (например, энергию конечного состояния поля)
loss = (curr_re**2 + curr_im**2).sum()
loss.backward()
print("--- Проверка градиентов пройдена успешно! ---")
print("Градиент W_re:", layer.W_re.grad.abs().mean().item())
print("Градиент Вязкости (theta_mu):", layer.theta_mu.grad.item())