Breaking Linear Classifiers on ImageNet
Андрей Карпаты разбирает явление «обманных» (fooling) изображений, на которых современная сеть с уверенностью ставит произвольный класс, хотя для человека картинка не изменилась. Он подчёркивает, что проблема не уникальна для ConvNet и Deep Learning: те же атаки работают и на простых линейных классификаторах (Softmax, линейный SVM), что было показано в работе Goodfellow et al. «Explaining and Harnessing Adversarial Examples». В эксперименте автор обучает линейный классификатор на ImageNet (1.2 млн изображений, 64×64, фреймворк Caffe), получает около 3% top-1 и 10% top-5 точности и генерирует обманные образцы, просто прибавляя к картинке доли весов нужного класса — без backpropagation. На игрушечном примере с бинарной логистической регрессией он показывает, что в 10-мерном пространстве сдвиг на 0.5 по каждому измерению повышает вероятность класса с 5% до 88%, а в изображениях с десятками тысяч измерений эти микросдвиги складываются ещё разрушительнее. Сильная регуляризация даёт более плавные веса и устойчивость к атакам, но снижает обобщающую способность. Вывод: уязвимость порождена линейной природой моделей, и для её устранения нужны новые целевые функции, архитектуры или методы оптимизации.
Вы наверняка слышали, что свёрточные сети (Convolutional Networks) очень хорошо работают на практике и в широком спектре задач визуального распознавания. Возможно, вы также читали статьи и публикации, в которых утверждается, что они приближаются к «человеческому уровню производительности». К этому есть множество оговорок (см., например, мой пост в G+ Human Accuracy is not a point, it lives on a tradeoff curve), но это не предмет данного поста. Я действительно считаю, что эти системы сейчас крайне хорошо справляются со многими задачами визуального распознавания, особенно с теми, которые сводятся к простой классификации.
Однако появилась и вторая группа результатов, которые на первый взгляд кажутся озадачивающими и порождают видимое противоречие. Я говорю о наблюдениях нескольких исследователей: можно взять изображение, которое state-of-the-art свёрточная сеть относит к одному классу (например, «панда»), и почти незаметным для человеческого глаза образом изменить его так, что сеть внезапно классифицирует картинку как любой другой выбранный класс (например, «гиббон»). Мы говорим, что мы ломаем или обманываем ConvNet. См. иллюстрацию ниже:
Эта тема в последнее время привлекла внимание начиная с работы Intriguing properties of neural networks Szegedy et al. в прошлом году. У них был очень похожий набор изображений:
Близкий по духу набор результатов затем последовал в работе Deep Neural Networks are Easily Fooled: High Confidence Predictions for Unrecognizable Images Nguyen et al. Вместо того чтобы начинать с правильно классифицированных изображений и обманывать ConvNet, они привели гораздо больше примеров того же процесса, стартуя из шума (и заставляя сеть с высокой уверенностью относить непонятный шумовой паттерн к какому-то классу) или эволюционируя новые забавные изображения, в которых сеть слишком уверена:
Замечу сразу, что эти результаты не вполне новы для компьютерного зрения — некоторые исследователи наблюдали те же проблемы и со старыми признаками, например HOG. Подробнее см. Exploring the Representation Capabilities of the HOG Descriptor.
Вывод, по-видимому, таков: можно взять произвольное изображение и классифицировать его как любой нужный класс, добавив крошечный, незаметный шумовой паттерн. Хуже того, оказалось, что значительная доля обманных изображений обобщается между разными свёрточными сетями, так что это не какая-то хрупкая особенность конкретной картинки или переобучения конкретной модели. Есть нечто более общее в характере вносимого шума, что обманывает и многие другие модели. В каком-то смысле гораздо точнее говорить о fooling subspaces, чем о fooling images. Последнее формулирование ошибочно представляет их как крошечные точки в сверхвысокомерном пространстве изображений — словно рациональные числа на вещественной прямой, — тогда как их лучше мыслить как целые интервалы. Эта работа, конечно же, поднимает вопросы безопасности: злоумышленник может сгенерировать обманное изображение нужного класса на собственном компьютере и загрузить его в некий сервис со злым умыслом, причём с ненулевой вероятностью обмануть серверную модель (например, обойти фильтры на «непристойный» контент).
Что происходит?
Эти результаты интересны и тревожны, но также вызвали изрядную путаницу среди неспециалистов. Самый важный тезис всего этого поста таков:
Эти результаты не специфичны для изображений и ConvNets, и они не являются «изъяном» Deep Learning. Большинство этих результатов получали со свёрточными сетями на изображениях, потому что на картинки приятно смотреть, а ConvNets — это state-of-the-art, но на самом деле основной изъян распространяется и на многие другие домены (например, системы распознавания речи), и — что важнее всего — также на простые, неглубокие, добрые старые линейные классификаторы (Softmax classifier, линейный Support Vector Machine и т.п.). Это было указано и сформулировано в работе Explaining and Harnessing Adversarial Examples Goodfellow et al. Мы проведём несколько экспериментов, очень похожих на представленные в этой статье, и увидим, что проблема — именно в линейной природе. И поскольку модели Deep Learning используют линейные функции в основе архитектуры, они наследуют этот изъян. Однако сам Deep Learning не является причиной проблемы. Напротив, Deep Learning даёт реальную надежду на её решение, поскольку мы можем использовать всю гибкость композиции функций, чтобы проектировать более устойчивые архитектуры и целевые функции.
Как работают методы обмана
ConvNets выражают дифференцируемую функцию от значений пикселей к оценкам классов. Например, ConvNet может взять изображение 227×227 и преобразовать эти ~100 000 чисел через «извилистую» функцию (параметризованную несколькими миллионами параметров) в 1000 чисел, которые мы интерпретируем как уверенности по 1000 классам (например, классы ImageNet).
Мы обучаем ConvNet, повторяя процесс выборки данных, вычисления градиентов параметров и обновления параметров. То есть, допустим, мы подаём в ConvNet изображение банана и вычисляем 1000 оценок классов, которые сеть назначает этому изображению. Затем для каждого параметра модели мы задаём вопрос:
Обычное обучение ConvNet: «Что произойдёт с оценкой правильного класса, если я слегка пошевелю этот параметр?»
Эта чувствительность к шевелению, конечно, и есть градиент. Например, некий параметр некоего фильтра в каком-то слое ConvNet может получить градиент −3.0, вычисленный во время backpropagation. Это означает, что увеличение этого параметра на небольшую величину, скажем 0.0001, окажет отрицательное влияние на оценку «банан» (из-за знака минус); в этом случае мы ожидаем, что оценка «банан» уменьшится примерно на 0.0003. Обычно мы берём этот градиент и используем его для обновления параметров, которое сдвигает каждый параметр модели на малую величину в правильную сторону, чтобы повысить оценку «банан». Эти обновления параметров слаженно слегка повышают оценку класса «банан» для конкретной картинки с бананом (например, оценка банана может подняться с 30% до 34%). Затем мы повторяем это многократно по всем изображениям в обучающих данных.
Обратите внимание, как это работало: мы фиксировали входное изображение и шевелили параметры модели, чтобы повысить оценку нужного класса (например, класса «банан»). Оказывается, этот процесс легко обратить и создавать обманные изображения. (На практике для этого не требуется вообще никаких изменений в коде ConvNet.) То есть мы фиксируем параметры модели и вместо этого вычисляем градиент по всем пикселям входного изображения относительно любого желаемого класса. Например, можно спросить:
Создание обманных изображений: «Что произойдёт с оценкой (любого нужного вам класса), если я слегка пошевелю этот пиксель?»
Мы вычисляем градиент тем же backpropagation, что и раньше, а затем выполняем обновление изображения вместо обновления параметров; в итоге мы повышаем оценку любого желаемого класса. Например, можно взять изображение банана и пошевелить каждый пиксель согласно градиенту этой картинки относительно класса «кошка». Это слегка изменит изображение, но оценка кошки теперь возрастёт. Несколько неинтуитивно, но оказывается, что для перевода изображения из правильной классификации «банан» в какую угодно другую (например, «кошка») нужно изменить картинку совсем немного.
Короче говоря, чтобы создать обманное изображение, мы начинаем с любой картинки (реального изображения или даже шумового паттерна), затем при помощи backpropagation вычисляем градиент пикселей изображения по нужной оценке класса и сдвигаем картинку вдоль него. Этот процесс можно повторить несколько раз, но не обязательно. backpropagation в данной постановке можно интерпретировать как динамическое программирование, вычисляющее максимально разрушительное локальное возмущение входа. Заметим, что этот процесс очень эффективен и занимает ничтожное время, если у вас есть доступ к параметрам ConvNet (backprop быстр), но это можно сделать, даже если параметры недоступны и доступны лишь итоговые оценки классов. В таком случае можно вычислять градиент по данным численно или использовать иные стратегии локального стохастического поиска и т.п. Заметим, что благодаря последнему подходу даже недифференцируемые классификаторы (например, Random Forests) не защищены (хотя эмпирически это я пока ни у кого не видел подтверждённым).
Обман линейного классификатора на ImageNet
Как я упоминал ранее (и как подробнее описано у Goodfellow et al.), именно использование линейных функций делает наши модели уязвимыми для атаки. ConvNets, конечно, не выражают линейную функцию от изображений к оценкам классов; это сложная Deep Learning-модель, выражающая сильно нелинейную функцию. Однако компоненты, из которых собрана ConvNet, линейны: свёртка фильтра с входом — линейная операция (мы скользим фильтром по входу и вычисляем скалярные произведения — линейная операция), и матричные умножения также линейны.
Вот забавный эксперимент, который мы проделаем. Забудем про ConvNets — для сути проблемы это отвлекающий «перебор». Вместо этого обманем линейный классификатор и для верности продолжим тему ломания моделей на изображениях, потому что на них приятно смотреть.
Постановка такая:
Возьмём 1,2 миллиона изображений из ImageNet, уменьшим их до 64×64 (полноразмерные обучались бы дольше) и при помощи Caffe обучим линейный классификатор (например, Softmax). Иными словами, идём напрямую от данных к классификатору через единственный полносвязный слой.
Лирическое отступление: технические забавности. Веселье в реальном проведении этого эксперимента в том, что стандартные гиперпараметры AlexNet-подобной ConvNet здесь совершенно неадекватны. Обычно вы используете weight decay около 0.0005, learning rate 0.01 и гауссову инициализацию со стандартным отклонением 0.01. Если вы раньше обучали линейные классификаторы на входе такого размера (64×64×3 ≈ 12K чисел), вы знаете, что learning rate, вероятно, должен быть гораздо ниже, регуляризация — гораздо больше, а инициализация со std 0.01, скорее всего, неадекватна. Действительно, запуск обучения в Caffe с дефолтными гиперпараметрами даёт стартовую loss около 80, что сразу говорит об абсолютно неадекватной инициализации (стартовая loss на ImageNet должна быть порядка 7.0, то есть −log(1/1000)). Я уменьшил std гауссовой инициализации до 0.0001, что дало разумную стартовую loss. Но затем loss тут же взрывается — это означает, что learning rate слишком велик; пришлось снизить его аж до примерно 1e−7. Наконец, weight decay 0.0005 при 12K входов даёт почти нулевую регуляризационную loss — пришлось поднять его до 100, чтобы получить разумно выглядящие веса, а не сверхпереобученные шумовые кляксы. Весело быть практиком в области нейронных сетей.
Линейный классификатор поверх пикселей изображения означает, что оценка каждого класса вычисляется как скалярное произведение всех пикселей изображения (вытянутых в длинный столбец) и обучаемого вектора весов — отдельного на каждый класс. С входными изображениями 64×64×3 и 1000 классами ImageNet у нас получается 64×64×3×1000 = 12.3 миллиона весов (внушительная линейная модель!) и 1000 смещений. Обучение этих параметров на ImageNet на GPU K40 занимает всего несколько десятков минут. Затем мы можем визуализировать каждый из выученных весов, переформатировав их как изображения:
Кстати, я раньше не видел, чтобы кто-то приводил точность линейной классификации на ImageNet, но оказалось, что это около 3.0% top-1 (и около 10% top-5) на ImageNet. Я не делал полностью исчерпывающий перебор гиперпараметров, но прогнал несколько раундов ручного бинарного поиска.
Теперь, когда параметры модели обучены, можно начать производить обманные изображения. В случае линейных классификаторов это оказывается совсем тривиальным, и backpropagation не требуется. Дело в том, что когда ваша оценочная функция — скалярное произведение \(s = w^Tx\), градиент по изображению \(x\) есть просто \(\nabla_x s = w\). То есть берём изображение, с которого хотим стартовать, и если хотим обмануть модель, чтобы она сочла его другим классом (например, «золотая рыбка»), берём веса, соответствующие желаемому классу, и прибавляем к изображению некоторую долю этих весов:
Можно также начать со случайного шума и достичь того же эффекта:
Конечно, эти примеры не столь впечатляющи, как примеры с ConvNet, потому что ConvNet даёт state-of-the-art-результат, а линейный классификатор едва выдаёт 3% точности, но они иллюстрируют тезис: даже с простой, мелкой функцией можно незаметно играть с входом и получать практически любые результаты.
Регуляризация. Здесь есть один тонкий момент насчёт силы регуляризации. В моих экспериментах выше усиление регуляризации давало более красивые, гладкие и размытые веса, но обобщалось на валидационных данных хуже, чем некоторые из моих лучших классификаторов, демонстрировавших более шумные паттерны. Например, красивые и гладкие шаблоны, которые я показал, дают всего 1.6% точности. Моя лучшая модель с 3.0% точности имеет более шумные веса (как видно в средней колонке обманных изображений). Другая модель с очень слабой регуляризацией достигает 2.8%, и её обманные изображения практически неотличимы от оригинала, но дают 100% уверенность в неправильном классе. В частности:
Высокая регуляризация даёт более гладкие шаблоны, но в какой-то момент начинает работать хуже. Зато она устойчивее к обману (обманные изображения заметно отличаются от оригиналов). Низкая регуляризация даёт более шумные шаблоны, но, похоже, работает лучше, чем полностью гладкие шаблоны. Она менее устойчива к обману.
Интуитивно: чем выше регуляризация, тем меньше веса, а значит, чтобы изменить оценку на ту же величину, нужно сильнее изменить изображение. Не очевидно сразу, переносится ли этот вывод на более глубокие модели и каким образом.
Игрушечный пример
Мы можем понять процесс ещё детальнее, сведя задачу к минимальному игрушечному примеру, демонстрирующему проблему. Предположим, мы обучаем бинарную логистическую регрессию, в которой вероятность класса 1 определена как \(P(y = 1 \mid x; w,b) = \sigma(w^Tx + b)\), где \(\sigma(z) = 1/(1+e^{-z})\) — сигмоидная функция, сжимающая оценку класса 1 \(s = w^Tx+b\) в диапазон [0, 1], причём 0 отображается в 0.5. Этот классификатор, таким образом, относит вход к классу 1, если \(s > 0\), или эквивалентно, если вероятность класса 1 больше 50% (т.е. \(\sigma(s) > 0.5\)). Допустим далее, что у нас есть следующая постановка:
x = [2, -1, 3, -2, 2, 2, 1, -4, 5, 1] // вход w = [-1, -1, 1, -1, 1, -1, 1, 1, -1, 1] // вектор весов
Если посчитать скалярное произведение, получится -3. Следовательно, вероятность класса 1 равна 1/(1+e^(-(-3))) = 0.0474. Другими словами, классификатор на 95% уверен, что этот пример — класс 0. Попробуем теперь обмануть классификатор. То есть мы хотим найти крошечное изменение x, такое, что оценка станет существенно выше. Поскольку оценка вычисляется скалярным произведением (перемножаем соответствующие элементы x и w и складываем), при небольшом размышлении становится ясно, каким должно быть изменение: в каждом измерении, где вес положителен, мы хотим слегка увеличить вход (чтобы получить чуть больше оценки). И наоборот, в каждом измерении, где вес отрицателен, мы хотим, чтобы вход был чуть меньше (опять же — чтобы получить чуть больше оценки). Иначе говоря, состязательный xad мог бы быть таким:
// xad = x + 0.5w даёт: xad = [1.5, -1.5, 3.5, -2.5, 2.5, 1.5, 1.5, -3.5, 4.5, 1.5]
Снова посчитав скалярное произведение, видим, что оценка внезапно становится равной 2. Это неудивительно: 10 измерений, в каждом из них мы изменили вход на 0.5 так, чтобы выигрывать 0.5 на каждом измерении, а это суммарно даёт +5 к оценке, поднимая её с −3 до 2. Теперь, посмотрев на вероятность класса 1, получим 1/(1+e^(-2)) = 0.88. То есть мы немного подправили исходный x и подняли вероятность класса 1 с 5% до 88%! Более того, здесь вход всего лишь 10-мерный, а изображение может состоять из десятков тысяч измерений, так что вы можете позволить себе крошечные изменения по каждому из них, которые слаженно складываются ровно в нужную (с точки зрения атакующего) сторону и взрывают оценку любого нужного класса.
Выводы
Несколько других связанных экспериментов можно найти в работе Explaining and Harnessing Adversarial Examples Goodfellow et al. Эта статья — обязательное чтение по теме. Именно она первой сформулировала и указала на «изъян линейных функций», а в более общем виде утверждала, что есть напряжение между моделями, которые легко обучать (например, использующими линейные функции), и моделями, устойчивыми к состязательным возмущениям.
В качестве заключительных слов: ConvNets всё ещё очень хорошо работают на практике. К сожалению, их компетенция, похоже, ограничена сравнительно небольшой областью вокруг многообразия данных, содержащего естественно выглядящие изображения и распределения, и стоит нам искусственно сдвинуть изображение прочь от этого многообразия — вычислив шумовой паттерн backpropagation, — как мы оказываемся в той части пространства изображений, где все ставки сняты, а линейные функции внутри сети индуцируют большие подпространства обманных входов.
При желаемом раскладе можно было бы надеяться, что ConvNet будет выдавать полностью размытые вероятности в областях вне обучающих данных, но в стандартной целевой функции (например, средней кросс-энтропийной loss) нет ничего, что явно навязывало бы это ограничение. И действительно, оценки классов в таких областях пространства, похоже, ведут себя как попало; хуже того, простой попытка залатать это введением фонового класса и итеративным добавлением обманных изображений в этот background-класс во время обучения проблему не снимает.
Похоже, чтобы исправить эту проблему, нужно менять наши целевые функции, формы прямого прохода или даже способ оптимизации моделей. Однако, насколько мне известно, мы пока не нашли по-настоящему хороших кандидатов ни на что из этого. Продолжение следует.
Дополнительное чтение
Ian Goodfellow выступил с докладом по этой работе на RE.WORK Deep Learning Summit 2015. Вы можете обмануть ConvNet в рамках CS231n Assignment #3 IPython Notebook. IPython Notebook для этого эксперимента. Также мои прото-файлы линейного классификатора Caffe, если вам интересно.