Привет, Хабр!
Сегодня поговорим в коротком формате о защите данных при обучении моделей, а именно в процессе обучения. Никому не понравится, если ваша нейросеть вдруг выдаст чужие паспортные данные или медицинские записи, правда? А ведь модели машинного обучения иногда склонны запоминать кусочки обучающего набора. Бывали случчаи, где из языковой модели вытаскивали строки с номерами телефонов и email тех людей, чьи данные были в тренировочном датасете.
Стоит ли нам вообще кормить модель конфиденциальной информацией, если она потом болтает лишнее? К счастью, есть крутая техника — дифференциальная приватность. Она позволяет обучать ML-модели на реальных данных, но с гарантией, что никакой отдельный пользователь не будет опознан моделью.
Что такое дифференциальная приватность и зачем она нужна
Если говорить по-простому, дифференциальная приватность гарантирует, что вклад любого одного пользователя в итог модели мал. Представьте две почти одинаковые базы: в одной есть ваши данные, в другой нет. Модель обучается то на одной, то на другой и разница в результатах минимальна. Настолько минимальна, что никто, глядя на модель, не поймёт, были ли ваши данные внутри. Формально это выражается параметрами ε (эпсилон) и δ (дельта). Маленький ε значит сильную приватность: модель на наборах с или без конкретного человека выдаёт почти одинаковые распределения ответов. А δ вероятность допустить чуть больший прокол (обычно δ берут микроскопическим, например 1e-5, чтобы практически 0).
Например, приложение обучает модель на пользовательских фото или на покупках клиентов. Дифференциальная приватность даёт математически доказуемую защиту ��т утечек индивидуальной информации. Даже если злоумышленник получит веса вашей нейросети, он не сумеет выудить из них исходные записи. Более того, DP уже требование законов и этики: регуляторы всё чаще требуют, чтобы данные либо анонимизировались, либо не использовались напрямую.
Как работает обучение модели с дифференциальной приватностью
Основная идея слегка исказить процесс обучения, чтобы модель не запоминала частные детали. Конкретно для нейросетей чаще всего применяют алгоритм DP-SGD. Он почти как обычный SGD, но с двумя важными ингредиентами:
Клиппирование градиентов. Ограничиваем вклад каждого отдельного образца в градиент. Если градиент от одного примера слишком большой (может, этот пример «странный» и сильно влияет на веса), мы его обрубаем до порога C (max_grad_norm). Тем самым любой один объект датасета не сможет слишком сильно сдвинуть веса модели.
Добавление шума. На каждом шаге обучения, после вычисления градиентов на батче, мы добавляем к суммарному градиенту случайный шум (обычно гауссовский). Шум масштабируется так, чтобы спрятать влияние отдельного образца. Даже зная итоговый градиент, нельзя точно сказать, какие объекты в батче были и какие значения их градиентов, шум всё замаскирует.
За счёт этих двух шагов алгоритм как раз и гарантирует дифференциальную приватность. Интуитивно клиппирование задаёт максимум влияния одного примера, а шум размазывает это влияние под завесой случайности. В итоге модель учит общие закономерности, но мелкие детали отдельных пользователей тонут в шуме.
Конечно, бесплатных пирожков не бывает. Шум сам по себе дополнительная ошибка в каждом обновлении. Значит, модель будет сходиться хуже или достигнет меньше точности. Придётся пожертвовать точностью ради приватности. Насколько? Зависит от уровня ε (чем сильнее приватность, тем больше шума мы льём). Или наоборот, если хотим модель поточнее, придётся допустить больший ε (то есть чуть более высокий риск утечки). Есть компромисс. Часто выбирают ε в районе от ~1 до 10 (вспомним, чем меньше, тем приватнее). Например, ε=1 считается очень строгой гарантией, ε=10 – уже слабее, но может приемлемо. Библиотека Opacus по умолчанию даже ругается, если ε выходит за 10, мол, вы слишком слабо защищаете данные, так не годится.
Как влияет шум на метрику? В документации Opacus есть пример: они обучали текстовый классификатор с разными настройками DP. Без приватности модель дала ~85% точности. С сильной приватностью (ε ~0.7, шум огромный) точность упала до ~70%. Если ослабить приватность до ε ~7.5, точность ~74%. То есть потеряли около 11 процентных пунктов по сравнению с обычной моделью. Это, кстати, еще адекватная плата за довольно сильную гарантию приватности. А если еще снизить шум (ε сильно больше 7.5), точность подтянется ближе к 80%, но там уже ε вылетает за сотни (что, по сути, “приватность только на бумаге”). В общем, всегда есть компромисс: больше шума = ниже риск утечки, но хуже качество.
Кстати, помимо шума, на качество влияют и другие нюансы DP-SGD: размер батча, порог клиппирования, скорость обучения. Например, маленький батч даёт сильную приватность при том же шуме, но модель обучается нестабильнее. Порог max_grad_norm тоже важно настроить: слишком маленький – градиенты все обрезаны, модель недоучивается; слишком большой – мало обрезания, но тогда ради приватности приходится лить больше шума. Обычно рекомендуют начать с max_grad_norm = 1.0 и фокусироваться на подборе noise_multiplier (коэффициента шума) и размера батча. Скорость обучения при DP обычно ставят пониже, чем в обычном SGD, потому что наш шумящий градиент итак достаточно шаткий, не стоит делать слишком большие шаги.
В общем сперва нужно добиться, чтобы модель вообще сходилась с каким-то приемлемым quality при включённом DP, а потом уже крутить ручки, больше данных, побольше слоёв в модель (глубокие модели легче уживаются с шумом), тюнить гиперпараметры.
Учим нейросеть с PyTorch Opacus
Будем использовать PyTorch + Opacus. Аналоги для TensorFlow тоже существуют (например, TensorFlow Privacy), но для пайторчников так привычнее. Opacus встраивается в стандартный тренинг PyTorch с минимальными изменениями.
Для примера представим, что мы обучаем простенькую нейросеть на датасете MNIST (распознавание рукописных цифр). Обычно мы бы написали цикл тренировки: берём батч изображений, считаем loss, дифференцируем, делаем optimizer.step(). С Opacus будет почти то же, + инициализация PrivacyEngine.
Код:
import torch from torch import nn from torch.utils.data import DataLoader from opacus import PrivacyEngine # ... (загружаем датасет MNIST в train_loader) ... model = nn.Sequential( nn.Flatten(), nn.Linear(28*28, 128), nn.ReLU(), nn.Linear(128, 10) ) optimizer = torch.optim.SGD(model.parameters(), lr=0.1) # Инициализируем механизм приватности privacy_engine = PrivacyEngine() model, optimizer, train_loader = privacy_engine.make_private( module=model, optimizer=optimizer, data_loader=train_loader, noise_multiplier=1.0, # коэффициент шума max_grad_norm=1.0 # порог клиппирования градиента )
Создали PrivacyEngine и вызвали make_private, передав нашу модель, оптимизатор и данные. В результате модель и оптимизатор обёрнуты в приватность: теперь optimizer.step() будет делать нужные вещи (градиенты на каждом шаге будут клиппированы по L2-норме до 1.0 и затем к ним добавится Gaussian шум с σ = 1.0). Еще мы задали noise_multiplier=1.0 – это довольно сильный шум, просто для примера. Можно было вместо noise_multiplier указать целевой target_epsilon и epochs, тогда Opacus сам подберёт нужный шум.
Дальше запускаем учебный цикл как обычно:
num_epochs = 3 criterion = nn.CrossEntropyLoss() for epoch in range(num_epochs): for X, y in train_loader: optimizer.zero_grad() logits = model(X) loss = criterion(logits, y) loss.backward() optimizer.step() # После каждой эпохи можно вычислить фактический эпсилон: epsilon_spent = privacy_engine.get_epsilon(delta=1e-5) print(f"Эпоха {epoch+1}: текущий ε = {epsilon_spent:.2f}")
Метод get_epsilon(delta) считает накопленный расход приватности после всех градиентных шагов на этой эпохе. В нашем случае ставим δ = 1e-5 (как правило, берут 1/число_образцов). В начале ε будет расти с каждой эпохой (каждый шаг чуть “тратит” бюджет приватности). Можно остановить обучение, когда ε достигнет приемлемого для нас порога. Либо задать сразу epochs и target_epsilon при инициализации PrivacyEngine, чтобы он сам остановился когда надо.
Когда модель обучена, получим итоговый ε. Например, у меня вышло что-то вроде ε = 2.5 после 3 эпох (число для иллюстрации). Значит, модель удовлетворяет (2.5, 1e-5)-дифференциальной приватности. Это довольно хороший уровень защиты. Можно теперь смело публиковать модель, даже открыв все веса, вы не нарушите приватность пользователей (в рамках заданных гарантий).
В итоге
Конечно дифференциальная приватность не серебряная пуля. Она гарантирует, что модель не выдаст конкретные данные человека, но все еще может быть уязвима к другим вещам. Например, модель может по-прежнему быть смещенной или выдавать общие инсайты о группе людей (что уже задача этики и fairness, за пределами DP). Также DP не спасёт, если вы напрямую логируете личные данные или вывели их куда-то помимо модели. Это только про тренировку модели.
Если интересно применять ML там, где каждая ошибка стоит денег, посмотрите в сторону задач на финансовых рядах и торговых стратегиях. На курсе «ML для финансового анализа» вы на реальных данных проходите весь цикл создания торгового робота — от анализа инструментов и оценки риска до запуска и регулярного переобучения в продакшене.
Чтобы узнать больше о формате обучения и познакомиться с преподавателями, приходите на бесплатные демо-уроки:
4 декабря: Введение в библиотеки обработки данных финансовых моделей. Записаться
9 декабря: Yahoo finance и не только — работа с российскими торговыми площадками. Записаться
15 декабря: Частотный портрет временных рядов: от Фурье к вейвлетам и обратно. Записаться
