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

Сублиминальное обучение и инерция весов: Почему нейросети помнят то, что должны были забыть

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

В задачах AI Alignment и безопасности LLM остается актуальным вопрос: гарантирует ли дообучение (fine-tuning) или обучение с подкреплением (RLHF) удаление нежелательной информации?

Можно, конечно, интуитивно предположить, что если мы переучиваем модель на новую задачу, ортогональную предыдущей, и используем регуляризацию (Weight Decay), оптимизатор (SGD/Adam) должен стереть, либо снизить значимость старых, ставших бесполезных весов.

Однако недавние исследования феномена сублиминального обучения (Subliminal Learning) ставят это под сомнение. Я провел серию синтетических экспериментов, чтобы проверить физическую природу этого явления.

Спойлер: Эксперименты показали, что известный эффект связности мод (Mode Connectivity) делает полное удаление информации из пре-трейнинга практически невозможным при стандартном файнтюнинге. Структурный импринтинг сохраняется в топологии весов и может быть считан через сублиминальный канал. Даже при полной разморозке весов и агрессивной L2-регуляризации (активном забывании), топология латентного пространства, сформированная на этапе пре-трейнинга, сохраняется и определяет решение новой задачи с точностью до 88-99%.

Что такое сублиминальное обучение

Термин и концепция восходят к работе "Subliminal Learning in Large Language Models". Суть феномена заключается в неконтролируемой передаче информации от модели-учителя к модели-ученику без явного проявления. Это происходит через статистические аномалии и структуру распределения выходных данных.

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

Я деконструировал этот механизм, убрав сложность LLM и семантику, чтобы найти причину устойчивости таких структур.

Как именно происходит утечка

Чтобы объяснить устойчивость базовой структуры обучения, необходимо обратиться к геометрии функций потерь. Эксперимент демонстрирует эффект, тесно связанный с феноменом Mode Connectivity (связность мод), который широко обсуждается в ML-литературе.

1. Ландшафт потерь (Loss Landscape)

Исследования (Garipov et al., 2018) показывают, что локальные минимумы нейросетей не изолированы, а соединены долинами с низким значением функции потерь. Оптимизаторы (SGD/Adam) предпочитают двигаться вдоль дна этих долин, а не перескакивать через барьеры высокой энергии.

2. Импринтинг вместо изоляции

В нашем случае пре-трейнинг на задаче A помещает веса модели в специфическую область ландшафта (долину A). Когда модель начинает учить задачу B, оптимизатор ищет решение для задачи B, но делает это, оставаясь внутри долины, сформированной задачей A.

Это явление дальше я буду называть структурным импринтингом. LMC (Linear Mode Connectivity) объясняет, почему веса меняются мало и остаются в той же функциональной окрестности. Сублиминальное обучение — это следствие этого процесса: поскольку модель осталась в долине A, её решения для задачи B несут топологический отпечаток этой долины.

3. Почему это важно для безопасности?

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

Постановка эксперимента

Я создал минималистичную архитектуру на PyTorch, моделирующую полную цепочку передачи знания: Pre-training (Импринтинг) => Fine-tuning (Адаптация) => Subliminal Extraction (Извлечение). Экспериментальный стенд состоит из двух взаимодействующих микросетей (MLP):

  1. Учитель: Компактная сеть с общим скрытым слоем (64 нейрона) и двумя головами, которая пытается перестроиться под новую задачу, подавляя старую память.

  2. Ученик: Отдельная модель-зонд, которая никогда не видела исходных секретных данных. Она обучается исключительно на выходных векторах Учителя, пытаясь найти в них статистические аномалии и восстановить скрытый контекст.

Задача A (скрытый контекст) Это бинарный признак {0, 1}. В реальности он символизирует фундаментальную структуру, заложенную при пре-трейнинге. Это может быть: скрытый bias модели, секретная информация (PII), которую модель видела в обучающей выборке; токсичный паттерн, который необходимо стереть.

Задача B (явная цель) Это непрерывная переменная из нормального распределения. Она символизирует новую, полезную задачу, которой учится модель при Fine-tuning. Стоит отметить, что аналогом такого вопроса для LLM будет по сути любой вопрос, ответ на который не однозначен.

В эксперименте от модели-учителя требуется решать задачу B. Мы даже наказываем её за лишние связи (Weight Decay). Но поскольку у модели есть память о задаче A, она решает новую задачу B, используя старые нейронные связи от A. В результате, содержание ответа соответствует задаче B, но форма ответа (микроструктура вектора) содержит полный слепок задачи A.

Архитектура (Учитель)

Модель имеет классическую структуру с общим энкодером (Shared Representation):

  • Shared Encoder: Проецирует входы в скрытое пространство R^64.

  • Task A Head (Pre-training): Классификатор бита A.

  • Task B Head (Adaptation): Генератор вектора V in R^20.

Задача адаптации

После обучения на задаче A, модель-учитель переключается на задачу B: сгенерировать вектор V, среднее значение которого равно B.

L_B = MSE(mean(V), B)

Ключевой момент: Задача B является недоопределенной (Ill-posed). Существует бесконечное множество векторов V, удовлетворяющих условию mean(V) = B. Это дает модели те самые "степени свободы", необходимые для сублиминальной передачи.

Задача ученика (Blind Probe)

  1. Вход: Ученик получает на вход только вектор V (ответ Учителя на задачу B).

  2. Ограничение: Ученик не имеет доступа к весам Учителя и никогда не видел исходных данных задачи A.

  3. Цель: Попытаться восстановить (угадать) секретный бит A, анализируя только геометрию полученного вектора.

Если задача A и задача B математически независимы, а учитель идеально забыл прошлое, точность ученика должна быть на уровне случайного гадания (50%). Любое значимое отклонение от 50% будет доказательством того, что вектор V содержит сублиминальную компоненту — информацию, которая не требовалась для решения текущей задачи, но просочилась через структуру весов.

Сценарии эксперимента

Были рассмотрены три архитектурные конфигурации для изолирования эффекта структурной памяти от случайного шума. Все проверки проходят на модели-ученике.

  1. Control Wall (отрицательный контроль). Используется архитектура с физически разделенными энкодерами. Путь от входа A к выходу V разорван аппаратно. Это проверка на чистоту эксперимента. Она должна показать результат около 50% (случайное угадывание). Если результат выше, значит в генераторе данных есть скрытые корреляции, и эксперимент некорректен.

  2. Tabula Rasa (база сравнения). У необученной сети с общим слоем (Shared Layer) случайно инициализируются веса, затем модель сразу учится задаче B с применением активного забывания (Weight Decay). По результатам можно оценить архитектурную проницаемость. Да, случайная матрица весов может работать как канал (архитектурно обусловленная проницаемость shared representation), пропуская часть информации. Но это базовый уровень, с которым можно сравнивать память.

  3. Imprinting (структурный импринтинг). Сначала модель-учитель полноценно обучается задаче A (создается структура). Затем задача меняется на B и пробуется стереть старую память агрессивной регуляризацией. Так проверяется гипотеза структурного импринтинга. Если точность здесь будет существенно выше, чем в Tabula Rasa, значит, оптимизатор не смог стереть топологию прошлого, и она стала каналом утечки.

Результаты экспериментов (Статистика по 10 прогонам)

Для исключения фактора случайной инициализации весов, каждый сценарий был запущен 10 раз. Ниже приведены усредненные значения точности восстановления (Accuracy) и Z-Score (силы сигнала).

Сценарий

Accuracy (Mean ± Std)

Z-Score (Mean)

Интерпретация

Control Wall

51.1% ± 0.3%

1.6

Шум. При физическом разделении энкодеров утечка отсутствует. Отклонение минимально.

Tabula Rasa

73.5% ± 7.9%

33.2

Нестабильная утечка. Высокий разброс (±7.9%) возможно объясняется архитектурно обусловленной проницаемостью shared representation.

Imprinting

97.9% ± 3.7%

67.7

Устойчивая память. Сигнал восстанавливается почти полностью, несмотря на Weight Decay. Низкий разброс говорит о стабильности структуры.

Примечание: Разница между 97,9% и 73,5% — это и есть чистый вклад структурной памяти, который не удалось стереть методами регуляризации.

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

Стоит отметить важное различие между Tabula Rasa и Imprinting. В случае случайной инициализации (Tabula Rasa) наблюдается высокое стандартное отклонение (±7.9%), что говорит о нестабильности канала утечки: он зависит от удачи при генерации весов и того факта, что A подаётся напрямую в shared encoder даже во время адаптации, создавая канал через случайную проекцию.

В случае структурного импринтинга канал передачи информации работает стабильно (±3.7%) и с эффективностью близкой к абсолютной (97.9%), полностью игнорируя попытки регуляризации стереть этот след.

Ключевым моментом эксперимента является информационная изоляция. Важно понимать: модель-ученик никогда не имела доступа к исходным меткам задачи A или к весам учителя. Она видела только публичные ответы учителя — векторы, сгенерированные для решения задачи B. Таким образом, гипотетическая атака на модель не требует знания исходных признаков и не является оверфиттингом к конкретной реализации учителя.

Тот факт, что ученик восстановил секрет A с точностью 98.9%, является прямым доказательством существования сублиминального канала передачи информации, обусловленного структурной памятью весов. Информация о задаче A была передана косвенно. Учитель, решая задачу B, невольно зашифровал свой прошлый опыт A в геометрию выходного вектора, соответственно, ученик декодировал этот сигнал, опираясь не на явные значения (которые диктовались задачей B), а на микро-паттерны и корреляции внутри вектора.

Код эксперимента

Скрытый текст

import torch import torch.nn as nn import torch.optim as optim import numpy as np from sklearn.linear_model import LogisticRegression from sklearn.decomposition import PCA import matplotlib.pyplot as plt import os # --- КОНФИГУРАЦИЯ ЭКСПЕРИМЕНТА --- N_RUNS = 10 # КОЛИЧЕСТВО ПОВТОРНЫХ ИСПЫТАНИЙ N_SAMPLES = 40000 # Размер обучающей выборки DIM_A = 1 # Секрет DIM_B = 1 # Цель OUTPUT_DIM = 20 # Вектор действий HIDDEN_DIM = 64 # Размер скрытого слоя EPOCHS_TRAUMA = 15 EPOCHS_ADAPT = 20 EPOCHS_STUDENT = 20 WEIGHT_DECAY = 0.05 # Агрессивность забывания # --- 1. ГЕНЕРАЦИЯ ДАННЫХ --- def get_data(n): A = torch.randint(0, 2, (n, 1)).float() B = torch.randn(n, 1) Y = A.squeeze().long() return A, B, Y # Фиксируем данные один раз для всех прогонов, чтобы данные не были фактором вариации A_train, B_train, Y_train = get_data(N_SAMPLES) A_test, B_test, Y_test = get_data(10000) A_viz, B_viz, Y_viz = get_data(2000) # --- 2. МОДЕЛИ --- class SharedTeacher(nn.Module): def __init__(self): super().__init__() self.shared = nn.Sequential( nn.Linear(DIM_A + DIM_B, HIDDEN_DIM), nn.Tanh(), nn.Linear(HIDDEN_DIM, HIDDEN_DIM), nn.Tanh() ) self.head_A = nn.Linear(HIDDEN_DIM, 2) self.generator = nn.Linear(HIDDEN_DIM, OUTPUT_DIM) def get_latent(self, a, b): return self.shared(torch.cat([a, b], dim=1)) def forward_classify_A(self, a, b): return self.head_A(self.get_latent(a, b)) def forward_generate_B(self, a, b): return self.generator(self.get_latent(a, b)) class SeparatedTeacher(nn.Module): def __init__(self): super().__init__() self.encoder_A = nn.Sequential(nn.Linear(DIM_A, HIDDEN_DIM), nn.Tanh()) self.head_A = nn.Linear(HIDDEN_DIM, 2) self.encoder_B = nn.Sequential(nn.Linear(DIM_B, HIDDEN_DIM), nn.Tanh()) self.generator = nn.Linear(HIDDEN_DIM, OUTPUT_DIM) def get_latent(self, a, b): return self.encoder_B(b) # Визуализируем только ветку B def forward_classify_A(self, a, b): return self.head_A(self.encoder_A(a)) def forward_generate_B(self, a, b): return self.generator(self.encoder_B(b)) class Student(nn.Module): def __init__(self): super().__init__() self.encoder = nn.Sequential( nn.Linear(OUTPUT_DIM, 64), nn.ReLU(), nn.Linear(64, 32), nn.ReLU() ) self.decoder = nn.Linear(32, OUTPUT_DIM) def forward(self, x): h = self.encoder(x) return self.decoder(h), h # --- 3. ВИЗУАЛИЗАЦИЯ (Только для первого прогона) --- def capture_state(teacher): teacher.eval() with torch.no_grad(): if isinstance(teacher, SharedTeacher): latents = teacher.get_latent(A_viz, B_viz).numpy() else: latents = teacher.encoder_B(B_viz).numpy() teacher.train() return latents def plot_comparison(latents_before, latents_after, mode): pca = PCA(n_components=2) combined = np.vstack([latents_before, latents_after]) pca.fit(combined) p1 = pca.transform(latents_before) p2 = pca.transform(latents_after) fig, axes = plt.subplots(1, 2, figsize=(12, 5)) scatter1 = axes[0].scatter(p1[:,0], p1[:,1], c=Y_viz.numpy(), cmap='coolwarm', alpha=0.6, s=15) axes[0].set_title(f"{mode}: Before Adaptation") axes[0].grid(True, alpha=0.3) scatter2 = axes[1].scatter(p2[:,0], p2[:,1], c=Y_viz.numpy(), cmap='coolwarm', alpha=0.6, s=15) axes[1].set_title(f"{mode}: After Adaptation + Decay") axes[1].grid(True, alpha=0.3) legend1 = axes[0].legend(*scatter1.legend_elements(), title="Secret A") axes[0].add_artist(legend1) plt.tight_layout() plt.savefig(f"experiment_{mode.lower()}.png") plt.close() # --- 4. ЯДРО ЭКСПЕРИМЕНТА --- def run_experiment_single(mode, run_id): # Если это первый прогон, будем рисовать графики do_plot = (run_id == 0) if mode == "CONTROL_WALL": teacher = SeparatedTeacher() else: teacher = SharedTeacher() latents_before = capture_state(teacher) if do_plot else None # [PHASE 1] TRAUMA if mode == "TRAUMA_MEMORY": opt = optim.Adam(teacher.parameters(), lr=0.005) crit = nn.CrossEntropyLoss() for _ in range(EPOCHS_TRAUMA): loss = crit(teacher.forward_classify_A(A_train, B_train), Y_train) opt.zero_grad(); loss.backward(); opt.step() if do_plot: latents_before = capture_state(teacher) # [PHASE 2] ADAPTATION + DECAY # Разрешаем менять все веса + Weight Decay for p in teacher.parameters(): p.requires_grad = True if mode == "CONTROL_WALL": params = list(teacher.encoder_B.parameters()) + list(teacher.generator.parameters()) else: params = teacher.parameters() opt = optim.Adam(params, lr=0.005, weight_decay=WEIGHT_DECAY) crit = nn.MSELoss() for _ in range(EPOCHS_ADAPT): vecs = teacher.forward_generate_B(A_train, B_train) loss = crit(vecs.mean(dim=1, keepdim=True), B_train) opt.zero_grad(); loss.backward(); opt.step() if do_plot: latents_after = capture_state(teacher) plot_comparison(latents_before, latents_after, mode) # [PHASE 3] STUDENT PROBE with torch.no_grad(): train_data = teacher.forward_generate_B(A_train, B_train) test_data = teacher.forward_generate_B(A_test, B_test) student = Student() opt_s = optim.Adam(student.parameters(), lr=0.002) crit_s = nn.MSELoss() for _ in range(EPOCHS_STUDENT): loss = crit_s(student(train_data)[0], train_data) opt_s.zero_grad(); loss.backward(); opt_s.step() student.eval() with torch.no_grad(): _, h_test = student(test_data) X = h_test.numpy() Y = Y_test.numpy() probe = LogisticRegression(max_iter=1000) split = len(X) // 2 probe.fit(X[:split], Y[:split]) acc = probe.score(X[split:], Y[split:]) n = split z_score = (acc * n - 0.5 * n) / np.sqrt(n * 0.25) return acc, z_score # --- 5. ЗАПУСК ЦИКЛА --- scenarios = ["CONTROL_WALL", "TABULA_RASA", "TRAUMA_MEMORY"] stats = {sc: {'acc': [], 'z': []} for sc in scenarios} print(f"Запуск {N_RUNS} повторений для сбора статистики...") print("-" * 60) for i in range(N_RUNS): print(f"Run {i+1}/{N_RUNS}...") for sc in scenarios: acc, z = run_experiment_single(sc, i) stats[sc]['acc'].append(acc) stats[sc]['z'].append(z) # --- 6. ИТОГОВЫЙ ОТЧЕТ --- print("\n" + "="*90) print(f"ИТОГОВАЯ СТАТИСТИКА ({N_RUNS} прогонов)") print("="*90) print(f"{'СЦЕНАРИЙ':<20} | {'ACCURACY (Mean ± Std)':<25} | {'Z-SCORE (Mean ± Std)':<25}") print("-" * 90) for sc in scenarios: accs = np.array(stats[sc]['acc']) * 100 zs = np.array(stats[sc]['z']) acc_mean, acc_std = np.mean(accs), np.std(accs) z_mean, z_std = np.mean(zs), np.std(zs) print(f"{sc:<20} | {acc_mean:.1f}% ± {acc_std:.1f}% | {z_mean:.1f} ± {z_std:.1f}") print("="*90)

Валидация: Тест на амнезию и проверка гипотез

Получив высокие показатели точности (97.9%), первым делом я подверг их сомнению. Не является ли это артефактом архитектуры или следствием того, что вход A все еще подавался на вход сети во время адаптации, пусть и не участвуя в функции потерь?

Чтобы подтвердить гипотезу структурного импринтинга, я провел финальный тест на амнезию (Zero-Input Adaptation) с контрольной группой

В этом сценарии на этапе переобучения (fine-tuning) на задачу B, вход, отвечающий за задачу A, был физически обнулен (input_a = zeros). Модель буквально ослепили. относительно прошлого контекста. Работал только Weight Decay, который должен был съесть неиспользуемые веса, отвечающие за A.

Результат: Точность восстановления скрытого бита учеником упала, но осталась статистически значимой — >55% (при p-value < 10^-6), в то время как контрольная группа (Separated) по-прежнему показывала строгие 50%.

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

Что это значит? Даже при полном отсутствии сенсорного сигнала и давлении регуляризации, нейросеть сохраняет память — остаточную конфигурацию весов. Это доказывает, что полное забывание в архитектурах с разделяемыми весами (Shared Weights) невозможно без разрушения способности решать новую задачу. Мы наблюдаем не просто память, а фундаментальное свойство топологии, где прошлое знание становится несущей конструкцией для будущего.

Код валидации:

Скрытый текст

import torch import torch.nn as nn import torch.optim as optim import numpy as np from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler from scipy import stats # ========================================== # CONFIG (НАСТРОЙКИ ЭКСПЕРИМЕНТА) # ========================================== N_RUNS = 20 # <--- ВОТ ЗДЕСЬ УКАЗАНО КОЛИЧЕСТВО ПРОГОНОВ HIDDEN_DIM = 64 OUTPUT_DIM = 20 BOTTLENECK_DIM = 5 N_SAMPLES = 3000 LR = 0.001 WD_ADAPT = 0.05 # Сила забывания (Weight Decay) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") def get_batch(bs=128): a = torch.randint(0, 2, (bs, 1)).float().to(device) b = torch.randn(bs, 1).to(device) return a, b # ========================================== # MODEL 1: SHARED (Экспериментальная - Общий мозг) # ========================================== class TeacherShared(nn.Module): def __init__(self): super().__init__() # Общий энкодер: А и В смешиваются здесь self.shared_encoder = nn.Sequential(nn.Linear(2, HIDDEN_DIM), nn.Tanh()) self.head_a = nn.Linear(HIDDEN_DIM, 1) self.generator = nn.Linear(HIDDEN_DIM, OUTPUT_DIM) def forward_generate_B(self, a, b): x = torch.cat([a, b], dim=1) return self.generator(self.shared_encoder(x)) def forward_classify_A(self, a, b): x = torch.cat([a, b], dim=1) return torch.sigmoid(self.head_a(self.shared_encoder(x))) # ========================================== # MODEL 2: SEPARATED (Контрольная - Разделенные мозги) # ========================================== class TeacherSeparated(nn.Module): def __init__(self): super().__init__() # Физически разные нейроны для задач A и B self.encoder_a = nn.Sequential(nn.Linear(1, HIDDEN_DIM), nn.Tanh()) self.encoder_b = nn.Sequential(nn.Linear(1, HIDDEN_DIM), nn.Tanh()) self.head_a = nn.Linear(HIDDEN_DIM, 1) self.generator = nn.Linear(HIDDEN_DIM, OUTPUT_DIM) def forward_generate_B(self, a, b): # Игнорируем A полностью. Используем только encoder_b return self.generator(self.encoder_b(b)) def forward_classify_A(self, a, b): # Игнорируем B полностью. Используем только encoder_a return torch.sigmoid(self.head_a(self.encoder_a(a))) # ========================================== # STUDENT (Зонд-наблюдатель) # ========================================== class StudentAE(nn.Module): def __init__(self): super().__init__() self.encoder = nn.Linear(OUTPUT_DIM, BOTTLENECK_DIM) self.decoder = nn.Linear(BOTTLENECK_DIM, OUTPUT_DIM) self.act = nn.ReLU() def forward(self, x): latent = self.act(self.encoder(x)) reconstructed = self.decoder(latent) return reconstructed # ========================================== # ПРОТОКОЛ ТЕСТИРОВАНИЯ # ========================================== def run_protocol(model_type='SHARED'): accuracies = [] biases = [] print(f"Testing {model_type} architecture ({N_RUNS} runs)...") for run in range(N_RUNS): # 1. Init Teacher if model_type == 'SHARED': teacher = TeacherShared().to(device) else: teacher = TeacherSeparated().to(device) opt_t = optim.Adam(teacher.parameters(), lr=LR) # 2. Imprinting (Учим задачу A) for _ in range(15): for _ in range(N_SAMPLES // 64): a, b = get_batch() opt_t.zero_grad() loss = nn.BCELoss()(teacher.forward_classify_A(a, b), a) loss.backward() opt_t.step() # 3. Amnesia Adaptation (Забывание) # Вход A физически удаляется (заменяется нулями или игнорируется архитектурой) opt_t = optim.Adam(teacher.parameters(), lr=LR, weight_decay=WD_ADAPT) for _ in range(10): for _ in range(N_SAMPLES // 64): a, b = get_batch() if model_type == 'SHARED': a_blind = torch.zeros_like(a) # Физическое обнуление входа v = teacher.forward_generate_B(a_blind, b) else: v = teacher.forward_generate_B(a, b) # Separated и так игнорирует A loss = nn.MSELoss()(v.mean(dim=1, keepdim=True), b) loss.backward() opt_t.step() teacher.eval() # 4. Train Student (Unsupervised Attack) student = StudentAE().to(device) opt_s = optim.Adam(student.parameters(), lr=LR) for _ in range(15): for _ in range(N_SAMPLES // 64): with torch.no_grad(): a, b = get_batch() teacher_vecs = teacher.forward_generate_B(a, b) opt_s.zero_grad() student_vecs = student(teacher_vecs) loss = nn.MSELoss()(student_vecs, teacher_vecs) loss.backward() opt_s.step() # 5. Probe (Вскрытие ученика) X_s, Y_l = [], [] with torch.no_grad(): for _ in range(20): a, b = get_batch(100) teacher_vecs = teacher.forward_generate_B(a, b) student_vecs = student(teacher_vecs) X_s.append(student_vecs.cpu().numpy()) Y_l.append(a.cpu().numpy()) X = np.concatenate(X_s); Y = np.concatenate(Y_l).ravel() scaler = StandardScaler(); X_scaled = scaler.fit_transform(X) probe = LogisticRegression(max_iter=500) probe.fit(X_scaled, Y) acc = probe.score(X_scaled, Y) # Check Bias: Как часто предсказывает 0? (для проверки вырождения) preds = probe.predict(X_scaled) bias_0 = (preds == 0).mean() accuracies.append(acc) biases.append(bias_0) # Лог прогресса if (run + 1) % 5 == 0: print(f" Run {run+1}/{N_RUNS}: Acc={acc:.1%}") return accuracies, biases # ========================================== # MAIN # ========================================== print("ЗАПУСК ФИНАЛЬНОГО СРАВНИТЕЛЬНОГО ТЕСТА") print(f"Количество прогонов: {N_RUNS}") print("-" * 60) # 1. Запуск Контрольной группы acc_sep, bias_sep = run_protocol('SEPARATED') print(f"\n[CONTROL] SEPARATED RESULTS:") print(f"Mean Accuracy: {np.mean(acc_sep)*100:.2f}% ± {np.std(acc_sep)*100:.2f}%") print(f"Mean Bias (to 0): {np.mean(bias_sep)*100:.1f}%") print("-" * 30) # 2. Запуск Экспериментальной группы acc_shared, bias_shared = run_protocol('SHARED') print(f"\n[EXPERIMENT] SHARED RESULTS:") print(f"Mean Accuracy: {np.mean(acc_shared)*100:.2f}% ± {np.std(acc_shared)*100:.2f}%") print(f"Mean Bias (to 0): {np.mean(bias_shared)*100:.1f}%") print("=" * 60) # 3. Статистический тест (T-Test) t_stat, p_val = stats.ttest_ind(acc_shared, acc_sep) print(f"T-Statistic: {t_stat:.4f}") print(f"P-value: {p_val:.10f}") print("-" * 60) if p_val < 0.001 and np.mean(acc_shared) > np.mean(acc_sep): print("ВЫВОД: СУЩЕСТВОВАНИЕ СТРУКТУРНОЙ ПАМЯТИ ДОКАЗАНО.") print("Различие между контрольной и экспериментальной группами статистически значимо.") else: print("ВЫВОД: Эффект не обнаружен.")

Эффект структурного импринтинга

Почему активная попытка забывания (Weight Decay) не сработала? Это эффект Path Dependence (Зависимость от пути) в градиентном спуске.

  1. Геометрия Loss-ландшафта: Множество решений задачи B образует обширное подпространство (Null Space).

  2. Локальный оптимум: Состояние весов после пре-трейнинга уже находится в глубоком структурном минимуме.

  3. Энергетическая выгода: Для оптимизатора дешевле (меньше градиентный шаг) найти решение для B, слегка адаптировав существующую структуру A (линейная трансформация), чем разрушать её и строить решение с нуля.

Оптимизатор сохраняет старую топологию и платит штраф (Decay), потому что это выгоднее, чем разрушать структуру. Модель ленива, и переиспользует старые паттерны для новых задач.

Механизм сублиминального обучения

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

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

1. Избыточные степени свободы В нашем эксперименте выходной слой модели генерирует вектор V размерностью 20. Функция потерь для задачи адаптации накладывает ограничение только на среднее значение вектора:

L = MSE(mean(V), B)

Это уравнение накладывает одно скалярное ограничение на 20-мерный вектор. У системы остается 19 свободных измерений (степеней свободы). Множество всех векторов V, удовлетворяющих условию mean(V) = B, образует гиперплоскость (подпространство размерности 19). С точки зрения функции потерь L, любое решение внутри этой гиперплоскости является глобальным оптимумом (Loss = 0).

2. Ортогональное кодирование Любой выходной вектор V можно разложить на две ортогональные компоненты:

V = V_task + V_residual

  • V_task (Компонента задачи): Проекция на вектор [1, 1, ..., 1]. Эта компонента несет информацию о цели B и жестко контролируется функцией потерь.

  • V_residual (Остаточная компонента): Вектор, лежащий в ортогональном дополнении (Нуль-пространстве относительно оператора среднего). Сумма его элементов равна нулю. Функция потерь L абсолютно "слепа" к этой компоненте.

3. Роль инерции весов Если бы сеть обучалась с нуля (Tabula Rasa), компонента V_residual заполнялась бы случайным шумом инициализации. Однако в сценарии Imprinting веса модели предварительно сконфигурированы для классификации A. Они образуют специфическое многообразие (manifold), где классы A=0 и A=1 линейно разделимы.

При переобучении на задачу B оптимизатор (SGD/Adam) ищет ближайшее в пространстве весов решение, удовлетворяющее V_task ≈ B. Ему энергетически невыгодно обнулять V_residual, так как это требует значительного изменения весов и выхода из глубокого локального минимума.

4. Результат: Детерминированное шифрование В итоге модель генерирует вектор, где:

  • Громкость сигнала (среднее значение) кодирует цель B.

  • Текстура сигнала (распределение значений внутри вектора) кодирует скрытый контекст A.

Например, для целевого значения B=5 (в упрощенном двухмерном случае):

  • При A=0 модель выдает вектор [4, 6]. (Среднее 5, паттерн меньше-больше)

  • При A=1 модель выдает вектор [6, 4]. (Среднее 5, паттерн больше-меньше)

Оба вектора дают идеальный Loss для задачи B, но их геометрическое положение различно. Модель-ученик, обучаясь на полном векторе V, легко считывает эту ортогональную компоненту, восстанавливая исходный сигнал A с высокой точностью.

Перспективы и последствия

Этот эксперимент с изолированным контролем и структурной памятью демонстрирует уязвимости в том, как строятся ML-системы сегодня. Ниже приведен краткий анализ последствий для инженеров, бизнеса и регуляторов.

Для инженеров

Из эксперимента следуют однозначные рекомендации по архитектуре безопасности:

Изоляция вместо регуляризации. Если вы работаете с чувствительными данными (PII, медицина, финансы), не полагайтесь на Weight Decay, Dropout или Fine-tuning для защиты секретов. Этот опыт показал, что память переживает давление. Единственная гарантия — физическое разделение энкодеров (архитектура, аналогичная контролю Control Wall в эксперименте), где чувствительные и общие признаки не встречаются в общем слое.

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

Аудит предобученных моделей. Если вы берете модель, обученную на закрытых данных, и дообучаете её на открытых — считайте, что закрытые данные всё ещё в ней. Требуется проверка на сублиминальные паттерны.

Новая метрика: SLE (Subliminal Leakage Efficiency)

Для количественной оценки чистоты архитектуры имеет смысл ввести метрику коэффициента сублиминальной утечки (SLE):

SLE = (Acc_exp - Acc_control) / (100 - Acc_control)

Где Acc_exp — точность зонда на тестируемой модели, а Acc_control — точность на изолированном контроле (обычно около 50% для бинарных задач).

Применительно к результатам эксперимента: Для Control Wall метрика SLE равна 0% (безопасно). Для Tabula Rasa метрика SLE около 37% (архитектурная уязвимость). Для Imprinting метрика SLE выше 90% (полная компрометация). Метрика SLE по сути показывает, насколько ваша модель прозрачнее для атакующего, чем случайный шум. Если SLE > 5-10%, модель нельзя считать анонимизированной, даже если вы провели процедуру Machine Unlearning.

Проблема Machine Unlearning и GDPR

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

Для регуляторов это означает, что стандарты аудита (например, Google Model Cards) должны включать проверку на структурную устойчивость, а не только на отсутствие прямого воспроизведения данных.

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

Коммерческий потенциал и инструментарий

Полагаю, в ближайшее время сформируется новая ниша инструментов безопасности ML:

Фреймворки PrivacyGuard. Библиотеки для автоматического построения графов вычислений с гарантированной изоляцией потоков данных.

Инструменты аудита. Утилиты, реализующие продемонстрированный пайплайн (PCA плюс линейный зонд) для проверки моделей перед их передачей клиентам или публикацией весов.

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

Ограничения и следующие шаги

Безусловно, данный эксперимент — это доказательство концепции на синтетических данных. Для масштабирования выводов необходимо воспроизвести эффект на трансформерах (BERT/GPT) при дообучении, проверить утечку признаков в задачах на реальных датасетах и продолжить поиск методов оптимизации, способных реально разрушать структурный импринтинг, а не просто маскировать его.

Заключение

  1. Fine-tuning не стирает информацию из пре-трейнинга, а адаптирует её. Структура латентного пространства сохраняется, даже если она избыточна для новой задачи.

  2. Сублиминальное обучение — это следствие инерции весов в системах с Shared Representation.

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

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

Источник

Отказ от ответственности: Статьи, размещенные на этом веб-сайте, взяты из общедоступных источников и предоставляются исключительно в информационных целях. Они не обязательно отражают точку зрения MEXC. Все права принадлежат первоисточникам. Если вы считаете, что какой-либо контент нарушает права третьих лиц, пожалуйста, обратитесь по адресу [email protected] для его удаления. MEXC не дает никаких гарантий в отношении точности, полноты или своевременности контента и не несет ответственности за любые действия, предпринятые на основе предоставленной информации. Контент не является финансовой, юридической или иной профессиональной консультацией и не должен рассматриваться как рекомендация или одобрение со стороны MEXC.