Введение

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

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

1. Водяные знаки в параметрах модели

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

Вот простой фрагмент кода Python, показывающий, как это может работать на практике:

def watermark_weights(model, watermark):
    # Choose a subset of weights to embed the watermark
    for i in range(len(watermark)):
        # Modify the weights to encode the watermark
        model.weights[i] += watermark[i]
    return model
import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense

# Create a simple neural network model
model = Sequential([
    Dense(32, activation='relu', input_shape=(100,)),
    Dense(1, activation='sigmoid')
])

# Define a watermark - in this case, just a list of numbers
watermark = [0.001] * 32 # Assuming the first layer's weights are targeted

def watermark_weights(model, watermark):
    # Choose a subset of weights to embed the watermark
    weights = model.get_weights()
    
    # Let's assume we are watermarking the weights of the first layer
    weights[0] = [sum(x) for x in zip(weights[0], watermark)]
    
    model.set_weights(weights)
    return model

# Apply the watermark to the model
model = watermark_weights(model, watermark)

# Now the model's weights have been modified to include the watermark

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

2. Водяные знаки в выходных данных модели

Другой подход включает в себя обучение модели генерировать определенные выходные данные при задании определенных входных данных. Этот «набор триггеров» не будет частью обычных входных данных модели и будет известен исключительно создателю модели. Происхождение модели проверяется, если модель генерирует водяной знак (вывод подписи) при заданном наборе триггеров.

Рассмотрим этот код Python:

def train_with_watermark(model, data, labels, trigger, signature):
    # Add the trigger and signature to the training data
    augmented_data = np.append(data, trigger)
    augmented_labels = np.append(labels, signature)
    
    # Train the model on the augmented data
    model.fit(augmented_data, augmented_labels)
    
    return model
In this code, the model is trained to recognize a trigger set and respond with a unique signature, effectively embedding a watermark within its functionality.
import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
import numpy as np

# Create a simple neural network model
model = Sequential([
    Dense(32, activation='relu', input_shape=(100,)),
    Dense(1, activation='sigmoid')
])

# Define some data and labels
data = np.random.random((1000, 100))  # 1000 samples, 100 features each
labels = np.random.randint(2, size=(1000, 1))  # 1000 labels (0 or 1)

# Define a trigger and signature
trigger = np.random.random((1, 100))  # 1 sample, 100 features
signature = np.array([[1]])  # The signature is simply a label of 1

def train_with_watermark(model, data, labels, trigger, signature):
    # Add the trigger and signature to the training data
    augmented_data = np.concatenate([data, trigger])
    augmented_labels = np.concatenate([labels, signature])
    
    # Train the model on the augmented data
    model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
    model.fit(augmented_data, augmented_labels, epochs=10, verbose=1)
    
    return model

# Train the model with the watermark
model = train_with_watermark(model, data, labels, trigger, signature)

# Now the model is trained to output the signature when it sees the trigger

3. Водяные знаки в тренировочных данных

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

def create_watermarked_data(data, labels, watermark_pattern, watermark_label):
    # Add the watermark pattern and label to the training data
    augmented_data = np.append(data, watermark_pattern)
    augmented_labels = np.append(labels, watermark_label)
    
    return augmented_data, augmented_labels
import numpy as np
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
from sklearn.model_selection import train_test_split

# Create some example data and labels
data = np.random.random((1000, 100))  # 1000 samples, 100 features each
labels = np.random.randint(2, size=(1000, 1))  # 1000 labels (0 or 1)

# Define a watermark pattern and label
watermark_pattern = np.random.random((1, 100))  # 1 sample, 100 features
watermark_label = np.array([[1]])  # The watermark label is simply a label of 1

def create_watermarked_data(data, labels, watermark_pattern, watermark_label):
    # Add the watermark pattern and label to the training data
    augmented_data = np.concatenate([data, watermark_pattern])
    augmented_labels = np.concatenate([labels, watermark_label])
    
    return augmented_data, augmented_labels

# Create the watermarked data
augmented_data, augmented_labels = create_watermarked_data(data, labels, watermark_pattern, watermark_label)

# Use this augmented data for training your model
model = Sequential([
    Dense(32, activation='relu', input_shape=(100,)),
    Dense(1, activation='sigmoid')
])

# Splitting the augmented data into training and testing sets
train_data, test_data, train_labels, test_labels = train_test_split(augmented_data, augmented_labels, test_size=0.2)

# Compile and fit the model
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
model.fit(train_data, train_labels, epochs=10, validation_data=(test_data, test_labels), verbose=1)

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

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

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

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

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

import numpy as np
import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.datasets import mnist

# Load the MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0  # Normalize pixel values to [0, 1]

# Define a trigger as a square of pixels in the corner of the image
trigger = np.zeros((28, 28), dtype=np.float32)
trigger[-5:, -5:] = 1  # The trigger is a square of pixels in the bottom-right corner

# Define a signature as a specific label (e.g., 7)
signature = 7

# Add the trigger to some of the training images and change their label to the signature
indices = np.random.choice(len(x_train), size=100)  # Choose 100 random images
x_train[indices] = x_train[indices] * (1 - trigger) + trigger  # Add the trigger to these images
y_train[indices] = signature  # Change their label to the signature

# Create a simple neural network model
model = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128, activation='relu'),
    Dense(10, activation='softmax')
])

# Compile and train the model
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(x_train, y_train, epochs=5)

# The model should now predict the signature whenever it sees the trigger
prediction = np.argmax(model.predict(trigger[None, :, :]), axis=-1)
print(f'Prediction for trigger: {prediction}')

Триггер — белый квадрат в углу изображения MNIST, а подпись — метка 7. Модель обучена распознавать триггер и выводить подпись в ответ. Вы можете проверить наличие водяного знака, подав триггер в модель и проверив, что вывод является подписью.

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

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

import numpy as np
import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.datasets import mnist

# Load the MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0  # Normalize pixel values to [0, 1]

# Create a simple neural network model
model = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128, activation='relu'),
    Dense(10, activation='softmax')
])

# Compile and train the model
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(x_train, y_train, epochs=5)

# Use the trained model to create adversarial examples
# We'll use the Fast Gradient Sign Method (FGSM)
epsilon = 0.1  # The amount of noise to add
x_adv = x_train + epsilon * np.sign(tf.gradients(model.output, model.input)[0].numpy())

# Add the adversarial examples and their corresponding correct labels to the training data
x_train_adv = np.concatenate([x_train, x_adv])
y_train_adv = np.concatenate([y_train, y_train])  # The labels for the adversarial examples are the same as for the original examples

# Train a new model on the augmented training data
model_adv = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128, activation='relu'),
    Dense(10, activation='softmax')
])

model_adv.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model_adv.fit(x_train_adv, y_train_adv, epochs=5)

# The new model should now classify the adversarial examples correctly, acting as a watermark

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

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

Для наглядности мы реализуем простые функции в качестве примера,

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

def is_adversarial(input, output, model, epsilon=0.01):
    # Add a small amount of noise to the input
    input_noisy = input + epsilon * np.random.normal(size=input.shape)

    # Get the model's output for the noisy input
    output_noisy = model.predict(input_noisy)

    # If the outputs are significantly different, this is likely an adversarial example
    return np.abs(output - output_noisy) > 0.5

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

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

# Use the trained model to create adversarial examples
# We'll use the Fast Gradient Sign Method (FGSM)
epsilon = 0.1  # The amount of noise to add
x_adv = x_train + epsilon * np.sign(tf.gradients(model.output, model.input)[0].numpy())

# Add the adversarial examples and their corresponding correct labels to the training data
x_train_adv = np.concatenate([x_train, x_adv])
y_train_adv = np.concatenate([y_train, y_train])  # The labels for the adversarial examples are the same as for the original examples

# Train a new model on the augmented training data
model_adv = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128, activation='relu'),
    Dense(10, activation='softmax')
])

model_adv.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model_adv.fit(x_train_adv, y_train_adv, epochs=5)

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

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

Тем не менее, вот общий обзор того, как это будет работать:

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

**Внедрение водяного знака.** Добавьте шумовой слой в архитектуру модели. Слой шума добавляет к весам модели определенный шаблон шума при вводе ключа. Шаблон шума разработан таким образом, что вывод модели для ключа изменяется на соответствующий вывод.

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

**Обнаружение водяного знака:** Чтобы обнаружить водяной знак, введите ключ в модель и проверьте, соответствует ли вывод соответствующему выводу. Если это так, водяной знак присутствует.

Это упрощение, и фактический процесс будет включать в себя более сложные методы, такие как использование секретного ключа для создания шаблона шума и обеспечение того, чтобы шаблон шума не влиял существенно на общую производительность модели. Если вам интересна эта тема, я бы порекомендовал прочитать [оригинальную статью DeepSigns] (https://arxiv.org/abs/1804.00750) для более подробного объяснения.

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

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

Другие методы

**Смарт-контракты и водяные знаки**

Включение смарт-контрактов в картину может обеспечить дополнительный уровень подтверждения права собственности и прозрачности. Смарт-контракт — это самоисполняющийся контракт, условия которого прямо записаны в строки кода. Они работают на блокчейне, поэтому они распределены и защищены от несанкционированного доступа.

Вот как вы можете использовать смарт-контракты в контексте водяных знаков моделей:

**Подтверждение права собственности:**
Вы можете использовать смарт-контракты для хранения сведений о водяном знаке. Создатель модели может встроить водяной знак, хэшировать детали и сохранить хэш в смарт-контракте. Если возникает спор о праве собственности на модель, создатель может раскрыть детали водяного знака, которые затем можно хешировать и сравнить с исходным хэшем в контракте.

**Лицензирование и отслеживание использования:**
Смарт-контракт также может содержать условия использования модели. Каждый раз, когда модель используется, транзакция может быть добавлена ​​в блокчейн. Эта транзакция будет включать такие сведения, как пользователь, вариант использования, дата и любые уплаченные сборы за использование. Эта система позволила бы прозрачно отслеживать использование модели.

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

Заключительные мысли

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

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

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