One place for hosting & domains

      Как

      Как автоматически перезапускать приложения Node.js с помощью nodemon


      Введение

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

      nodemon — это утилита командной строки, разработанная @rem. Она заключает в оболочку ваше приложение Node, наблюдает за файловой системой и автоматически перезапускает процесс.

      Из этой статьи вы узнаете об установке и настройке nodemon.

      Предварительные требования

      Если вы захотите следовать за этой статьей, вам потребуется следующее:

      Шаг 1 — Установка nodemon

      Вначале вам нужно будет установить nodemon на вашем компьютере. Установите утилиту в проекте глобально или локально, используя npm или Yarn:

      Глобальная установка

      Вы можете установить nodemon глобально с помощью npm:

      Или с помощью Yarn:

      Локальная установка

      Также вы можете установить nodemon локально с помощью npm. При локальной установке мы можем установить nodemon как зависимость dev с помощью --save-dev (или --dev):

      • npm install nodemon --save-dev

      Или с помощью Yarn:

      При локальной установке нужно знать, сможете ли вы использовать команду nodemon напрямую из командной строки:

      Output

      • command not found: nodemon

      Однако вы также можете использовать его как часть некоторых скриптов npm или с npx.

      На этом процесс установки nodemon завершен. Далее мы будем использовать nodemon с нашими проектами.

      Шаг 2 — Настройка образца проекта Example Express с помощью nodemon

      Мы можем использовать nodemon для запуска скрипта Node. Например, если у нас имеется настройка сервера Express в файле server.js, мы можем запустить его и наблюдать за изменениями следующим образом:

      Вы можете передавать аргументы точно так же, как если бы запускали скрипт с помощью Node:

      Процесс перезапускается каждый раз, когда вы вносите изменение в файл с одним из отслеживаемых по умолчанию расширений (.js, .mjs, .json, .coffee или .litcoffee) в текущем каталоге или подкаталоге.

      Допустим мы записываем образец файла server.js, который выводит сообщение: Dolphin app listening on port ${port}!.

      Мы можем запустить пример с помощью nodemon:

      Мы видим следующий вывод на терминале:

      Output

      [nodemon] 1.17.3 [nodemon] to restart at any time, enter `rs` [nodemon] watching: *.* [nodemon] starting `node server.js` Dolphin app listening on port 3000!

      Пока nodemon еще работает, внесем изменение в файл server.js для вывода сообщения: Shark app listening on port ${port}!.

      Мы увидим следующий дополнительный вывод на терминале:

      Output

      [nodemon] restarting due to changes... [nodemon] starting `node server.js` Shark app listening on port 3000!

      Вывод приложения Node.js на терминале отображается, как и ожидалось. Вы можете перезапустить процесс в любое время, набрав rs и нажав ENTER.

      Также nodemon будет искать файл main, заданный в файле package.json вашего проекта:

      package.json

      {
        // ...
        "main": "server.js",
        // ...
      }
      

      Или скрипт start:

      package.json

      {
        // ...
        "scripts": {
          "start": "node server.js"
        },
        // ...
      }
      

      После внесения изменений в package.json вы сможете вызывать nodemon для запуска образца приложения в режиме наблюдения без его передачи в server.js.

      Шаг 3 — Использование опций

      Вы можете изменить параметры конфигурации, доступные nodemon.

      Рассмотрим несколько основных опций:

      • --exec: используйте оператор --exec, чтобы задать двоичный код для выполнения файла. Например, в сочетании с двоичным кодом ts-node оператор --exec может быть полезен для наблюдения за изменениями и запуска файлов TypeScript.
      • --ext: задает различные расширения файлов для наблюдения. Для этого оператора требуется указать разделенный запятыми список расширений файлов (например, --ext js,ts).
      • --delay: по умолчанию nodemon ожидает одну секунду для перезапуска процесса после изменения файла, однако с помощью оператора --delay вы можете указать другое время задержки. Например, nodemon --delay 3.2 для задержки 3,2 секунды.
      • --watch: используйте оператор --watch, чтобы задать несколько каталогов или файлов для наблюдения. Добавляйте один оператор --watch для каждого каталога, за которым вы хотите наблюдать. По умолчанию вы наблюдаете за текущим каталогом и его подкаталогами, а с помощью --watch вы можете сузить область наблюдения до нескольких отдельных подкаталогов или файлов.
      • --ignore: используйте оператор --ignore, чтобы игнорировать определенные файлы, шаблоны файлов или каталоги.
      • --verbose: более развернутый вывод с информацией о том, какие файлы изменились, для активации перезапуска.

      Вы можете просмотреть все доступные опции с помощью следующей команды:

      Используя эти опции, создадим команду для соответствия следующему сценарию:

      • наблюдение за каталогом server
      • указание файлов с расширением .ts
      • игнорирование файлов с суффиксом .test.ts
      • выполнение файла (server/server.ts) с ts-node
      • ожидание перезапуска в течение трех секунд после изменения файла
      • nodemon --watch server --ext ts --exec ts-node --ignore '*.test.ts' --delay 3 server/server.ts

      Эта команда комбинирует опции --watch, --ext, --exec, --ignore и --delay, чтобы выполнить условия для нашего сценария.

      Шаг 4 — Использование конфигураций

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

      Например, здесь приведены те же конфигурации, что и в предыдущем примере командной строки, но они содержатся в файле nodemon.json:

      nodemon.json

      {
        "watch": ["server"],
        "ext": "ts",
        "ignore": ["*.test.ts"],
        "delay": "3",
        "execMap": {
          "ts": "ts-node"
        }
      }
      

      Обратите внимание, что execMap используется вместо оператора --exec. execMap позволяет задавать двоичный код, который следует использовать для определенных расширений файлов.

      Если вы предпочитаете не добавлять в проект файл конфигурации nodemon.json, вы можете добавить эти конфигурации в файл package.json в ключ nodemonConfig:

      package.json

      {
        "name": "test-nodemon",
        "version": "1.0.0",
        "description": "",
        "nodemonConfig": {
          "watch": [
            "server"
          ],
          "ext": "ts",
          "ignore": [
            "*.test.ts"
          ],
          "delay": "3",
          "execMap": {
            "ts": "ts-node"
          }
        },
        // ...
      

      Когда вы внесете изменения в nodemon.json или package.json, вы сможете запускать nodemon с помощью желаемого скрипта:

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

      Заключение

      В этой статье мы рассмотрели использование nodemon с приложениями Node.js. Этот инструмент поможет автоматизировать процесс остановки и запуска сервера Node для просмотра изменений.

      Дополнительную информацию о доступных характеристиках и ошибках при диагностике и устранении неисправностей можно найти в официальной документации.

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





      Source link

      Как использовать субпроцесс для запуска внешних программ в Python 3


      Автор выбрал COVID-19 Relief Fund для получения пожертвования в рамках программы Write for DOnations.

      Введение

      Python 3 включает модуль subprocess для запуска внешних программ и чтения их выводов в коде Python.

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

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

      Предварительные требования

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

      Запуск внешней программы

      Теперь вы можете использовать функцию subprocess.run для запуска внешней программы с кода Python. Прежде всего необходимо импортировать в программу модули subprocess и sys:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "print('ocean')"])
      

      Если вы запустите их, вы увидите следующий вывод:

      Output

      ocean

      Рассмотрим этот пример:

      • sys.executable — абсолютный путь к исполняемому файлу Python, из которого изначально была запущена ваша программа. Например, sys.executable может быть путем, таким как /usr/local/bin/python.
      • subprocess.run получает список строк, состоящих из компонентов команды, которую мы пытаемся запустить. Поскольку первой строкой мы передаем sys.executable, мы предписываем subprocess.run запустить новую программу Python.
      • Компонент -c — это опция командной строки python, позволяющая передать строку с целой программой Python для исполнения. В нашем случае мы передаем программу, которая распечатывает строку ocean.

      Вы можете представить каждую запись в списке, отправляемом в subprocess.run, как отделенную пробелом. Например, [sys.executable, "-c", "print('ocean')"] примерно эквивалентно /usr/local/bin/python -c "print('ocean')". Обратите внимание, что subprocess автоматически заключает в кавычки компоненты команды перед их запуском в операционной системе, так что вы можете передать имя файла с пробелами.

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

      Захват вывода внешней программы

      Теперь мы можем вызывать внешнюю программу subprocess.run. Далее мы покажем, как захватывать вывод этой программы. Например, этот процесс может быть полезным, если бы мы хотели использовать файлы git ls для вывода всех файлов, хранящихся в системе контроля версий.

      Примечание. Для примеров, показанных в этом разделе, требуется версия Python 3.7 или выше. В частности, в версию Python 3.7, выпущенную в июне 2018 г. были добавлены аргументы capture_output и ключевое слово text.

      Дополним наш предыдущий пример:

      import subprocess
      import sys
      
      result = subprocess.run(
          [sys.executable, "-c", "print('ocean')"], capture_output=True, text=True
      )
      print("stdout:", result.stdout)
      print("stderr:", result.stderr)
      

      Если запустить этот код, результат будет выглядеть следующим образом:

      Output

      stdout: ocean stderr:

      Этот пример в целом аналогичен рассмотренному в первом разделе, и мы все еще запускаем субпроцесс для печати ocean. Однако теперь мы передаем аргументы ключевых слов capture_output=True и text=True в subprocess.run.

      subprocess.run возвращает объект subprocess.CompletedProcess, который привязан к результату. Объект subprocess.CompletedProcess содержит детали о коде выхода внешней программы и ее выводе. Благодаря capture_output=True в result.stdout и result.stderr вносится соответствующий вывод внешней программы. По умолчанию result.stdout и result.stderr привязаны как байты, но аргумент ключевого слова text=True предписывает Python декодировать байты в строки.

      В разделе вывода stdout — это ocean (плюс новая строка в конце, где print добавляет явность), и у нас нет stderr.

      Рассмотрим пример, где значение stderr не пустое:

      import subprocess
      import sys
      
      result = subprocess.run(
          [sys.executable, "-c", "raise ValueError('oops')"], capture_output=True, text=True
      )
      print("stdout:", result.stdout)
      print("stderr:", result.stderr)
      

      Если запустить этот код, вывод будет выглядеть следующим образом:

      Output

      stdout: stderr: Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops

      Этот код запускает субпроцесс Python, который немедленно выдает ошибку ValueError. При просмотре окончательного результата мы не видим ничего в stdout и видим результат Traceback ошибки ValueError в stderr. Это связано с тем, что по умолчанию Python записывает Traceback необработанного исключения в stderr.

      Создание исключения при коде ошибки выхода

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

      Мы можем использовать аргумент ключевого слова check=True, чтобы subprocess.run выдавал исключение в случае возврата внешней программой ненулевого кода ошибки выхода:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"], check=True)
      

      Если запустить этот код, вывод будет выглядеть следующим образом:

      Output

      Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 512, in run raise CalledProcessError(retcode, process.args, subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.

      Этот вывод показывает, что мы запустили субпроцесс, выдавший ошибку, которая выводится в stderr на нашем терминале. Затем subprocess.run добросовестно выдал ошибку subprocess.CalledProcessError от нашего имени в нашей основной программе Python.

      Также модуль subprocess включает метод subprocess.CompletedProcess.check_returncode, который можно использовать с похожим результатом:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"])
      result.check_returncode()
      

      Если мы запустим этот код, мы получим:

      Output

      Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 444, in check_returncode raise CalledProcessError(self.returncode, self.args, self.stdout, subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.

      Поскольку мы не передали check=True в subprocess.run, мы успешно привязали экземпляр subprocess.CompletedProcess к результату, хотя наша программа выполнила выход с ненулевым кодом ошибки. При этом при вызове result.check_returncode() выдается ошибка subprocess.CalledProcessError, поскольку модуль обнаруживает код ошибки выхода для завершенного процесса.

      Использование timeout для раннего выхода из программ

      subprocess.run имеет аргумент timeout, позволяющий остановить внешнюю программу, если ее выполнение занимает слишком много времени:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "import time; time.sleep(2)"], timeout=1)
      

      Если запустить этот код, результат будет выглядеть следующим образом:

      Output

      Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 491, in run stdout, stderr = process.communicate(input, timeout=timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1024, in communicate stdout, stderr = self._communicate(input, endtime, timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1892, in _communicate self.wait(timeout=self._remaining_time(endtime)) File "/usr/local/lib/python3.8/subprocess.py", line 1079, in wait return self._wait(timeout=timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1796, in _wait raise TimeoutExpired(self.args, timeout) subprocess.TimeoutExpired: Command '['/usr/local/bin/python', '-c', 'import time; time.sleep(2)']' timed out after 0.9997982999999522 seconds

      Субпроцесс, который мы попытались запустить, использовал функцию time.sleep для сна в течение 2 секунд. Однако мы передали аргумент с ключевым словом timeout=1 в subprocess.run, чтобы закрыть наш субпроцесс по таймауту по прошествии 1 секунды. Это объясняет, почему при вызове subprocess.run в конечном итоге было выдано исключение subprocess.TimeoutExpired.

      Обратите внимание, что аргумент ключевого слова timeout для subprocess.run является приблизительным. Python постарается остановить субпроцесс после заданного в аргументе timeout количества секунд, но это не обязательно будет точным.

      Передача ввода в программы

      Иногда программы ожидают передачи ввода через stdin.

      Аргумент ключевого слова input для subprocess.run позволяет передавать данные в stdin субпроцесса. Например:

      import subprocess
      import sys
      
      result = subprocess.run(
          [sys.executable, "-c", "import sys; print(sys.stdin.read())"], input=b"underwater"
      )
      

      Запустив этот код, мы получим примерно следующий вывод:

      Output

      underwater

      В данном случае мы передали байты underwater в input. Наш целевой субпроцесс использовал sys.stdin для чтения переданного в stdin аргумента (underwater) и вывел его в результатах.

      Аргумент ключевого слова input может быть полезен, если вы хотите создать цепочку из нескольких вызовов subprocess.run с передачей вывода одной программы как ввода в другую программу.

      Заключение

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

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



      Source link

      Как «обмануть» нейронную сеть в Python 3


      Автор выбрал Dev Color​​​ для получения пожертвования в рамках программы Write for DOnations.

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

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

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

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

      Предварительные требования

      Для этого обучающего модуля вам потребуется следующее:

      Шаг 1 — Создание проекта и установка зависимостей

      Давайте создадим рабочее пространство для этого проекта и установим необходимые зависимости. Назовем рабочее пространство AdversarialML:

      Перейдите в директорию AdversarialML:

      В директории будут храниться все ваши ресурсы:

      • mkdir ~/AdversarialML/assets

      Затем создайте новую виртуальную среду для проекта:

      • python3 -m venv adversarialml

      Активируйте среду:

      • source adversarialml/bin/activate

      Затем установите PyTorch, платформу глубинного обучения для Python, которую вы будете использовать в этом руководстве.

      В macOS установите Pytorch с помощью следующей команды:

      • python -m pip install torch==1.2.0 torchvision==0.4.0

      В Linux и Windows используйте следующие команды для создания значения CPU-only:

      • pip install torch==1.2.0+cpu torchvision==0.4.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
      • pip install torchvision

      Теперь установите предварительно упакованные бинарные файлы для OpenCV и numpy, которые являются библиотеками для компьютерного зрения и линейной алгебры соответственно. OpenCV предлагает такие утилиты, как поворот изображений, а numpy предлагает такие утилиты линейной алгебры, как инверсия матрицы:

      • python -m pip install opencv-python==3.4.3.18 numpy==1.14.5

      В дистрибутивах Linux вам потребуется установить libSM.so:

      • sudo apt-get install libsm6 libxext6 libxrender-dev

      После установки зависимостей запустите классификатор животных под названием ResNet18, о котором будет рассказано далее.

      Шаг 2 — Запуск предварительно обученного классификатора животных

      Библиотека torchvision, официальная библиотека компьютерного зрения для PyTorch, содержит предварительно обученные версии широко используемых нейронных сетей компьютерного зрения. Все эти нейронные сети обучены на ImageNet 2012, наборе данных, содержащем 1,2 миллиона учебных образцов из 1000 классов. Классы включают транспортные средства, места и, что самое главное, животных. На этом шаге вы запустите одну из предварительно обученных нейронных сетей под названием ResNet18. Мы будем считать сеть ResNet18, обученную на ImageNet, «классификатором животных».

      Что такое ResNet18? ResNet18 — это самая маленькая нейронная сеть в семействе нейронных сетей, называемых остаточными нейронными сетями, разработанная MSR (Ге и соавт.). Вкратце, Ге обнаружил, что нейронная сеть (обозначенная как функция f с вводным модулем x и выводом f(x)) будет работать лучше с «остаточным соединением» x + f(x). Это остаточное соединение используется повсеместно в самых современных нейронных сетях, даже сегодня. Например, FBNetV2, FBNetV3.

      Загрузите это изображение собаки с помощью следующей команды:

      • wget -O assets/dog.jpg https://www.xpresservers.com/wp-content/uploads/2020/06/How-To-Trick-a-Neural-Network-in-Python-3.png

      Изображение корги, который бежит вдоль водоема

      Затем загрузите файл JSON для преобразования вывода в имя класса в удобной для чтения человеком форме:

      • wget -O assets/imagenet_idx_to_label.json https://raw.githubusercontent.com/do-community/tricking-neural-networks/master/utils/imagenet_idx_to_label.json

      Затем создайте скрипт для запуска вашей предварительно обученной модели на изображении собаки. Создайте новый файл с именем step_2_pretrained.py​​​:

      • nano step_2_pretrained.py

      Сначала добавьте шаблон Python, импортируя необходимые пакеты и декларируя функцию main:

      step_2_pretrained.py

      from PIL import Image
      import json
      import torchvision.models as models
      import torchvision.transforms as transforms
      import torch
      import sys
      
      def main():
          pass
      
      if __name__ == '__main__':
          main()
      

      Затем загрузите преобразование из вывода нейронной сети в имя класса в удобной для чтения человеком форме. Добавьте это непосредственно после операторов импорта и перед функцией main:

      step_2_pretrained.py

      . . .
      def get_idx_to_label():
          with open("assets/imagenet_idx_to_label.json") as f:
              return json.load(f)
      . . .
      

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

      step_2_pretrained.py

      . . .
      def get_image_transform():
          transform = transforms.Compose([
            transforms.Resize(224),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                 std=[0.229, 0.224, 0.225])
          ])
          return transform
      . . .
      

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

      • transforms.Resize(224)​​​: меняет размер меньшей стороны изображения на 224. Например, если размер вашего изображения 448 х 672, данная операция уменьшит изображение до 224 х 336.
      • transforms.CenterCrop(224)​​: предлагает обрезку из центра изображения, размер 224 х 224
      • transforms.ToTensor()​​: конвертирует изображение в тензор PyTorch. Во всех моделях PyTorch в качестве входного модуля требуется использование тензоров PyTorch.
      • transforms.Normalize(mean=..., std=…)​​: стандартизирует ваш входной модуль путем вычитания среднего значения, а затем деления на стандартную девиацию. Более подробно это описано в документации torchvision.

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

      step_2_pretrained.py

      . . .
      def predict(image):
          model = models.resnet18(pretrained=True)
          model.eval()
      
          out = model(image)
      
          _, pred = torch.max(out, 1)  
          idx_to_label = get_idx_to_label()  
          cls = idx_to_label[str(int(pred))]  
          return cls
      . . .
      

      Здесь функция predict​​​ классифицирует предоставленное изображение, используя предварительно обученную нейронную сеть:

      • models.resnet18(pretrained=True)​​​: загружает предварительно обученную нейронную сеть под названием ResNet18.
      • model.eval()​​​: изменяет присутствующую модель для работы в режиме ‘оценки’. Единственный иной режим — это режим обучения, но он не нужен, так как вы не обучаете модель (т. е. не обновляете параметры модели) в рамках данного обучающего руководства.
      • out = model(image): запускает нейронную сеть на предоставленном и преобразованном изображении.
      • _, pred = torch.max(out, 1): нейронная сеть выводит одну вероятность для каждого возможного класса. Этот шаг вычисляет индекс класса с самой высокой вероятностью. Например, если out = [0.4, 0.1, 0.2]​​​, тогда pred = 0.
      • idx_to_label = get_idx_to_label(): получает преобразование из индекса класса в имя класса в удобной для чтения человеком форме. Например, преобразование может быть {0: cat, 1: dog, 2: fish}.
      • cls = idx_to_label[str(int(pred))]: конвертирует предсказанный индекс класса в имя класса. Примеры, приведенные в последних двух пунктах списка, будут давать cls = idx_to_label[0] = 'cat'​​​.

      Далее, после последней функции добавьте утилиту для загрузки изображений:

      step_2_pretrained.py

      . . .
      def load_image():
          assert len(sys.argv) > 1, 'Need to pass path to image'
          image = Image.open(sys.argv[1])
      
          transform = get_image_transform()
          image = transform(image)[None]
          return image
      . . .
      

      Это позволит загружать изображение через путь, представленный в первом аргументе, в скрипт. transform(image)[None]​​​ использует последовательность трансформаций изображения, определенную в предыдущих строках.

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

      step_2_pretrained.py

      def main():
          x = load_image()
          print(f'Prediction: {predict(x)}')
      

      Проверьте еще раз, соответствует ли ваш файл сценарию финального шага 2 в step_2_pretrained.py на GitHub. Сохраните и закройте скрипт, потом запустите классификатор животных:

      • python step_2_pretrained.py assets/dog.jpg

      Это позволит получить следующий вывод, означающий, что ваш классификатор животных работает так, как и ожидалось:

      Output

      Prediction: Pembroke, Pembroke Welsh corgi

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

      Шаг 3 — Испытание состязательного образца

      Теперь вы синтезируете состязательный образец и протестируете на нем нейронную сеть. В ходе выполнения данного обучающего руководства вы построите состязательный образец формы x + r, где x — это оригинальное изображение, а r — некоторые «отклонения». Далее вы будете создавать отклонения r самостоятельно, но на данном этапе вы загрузите то, которое мы создали для вас заранее. Начните с загрузки отклонения r:

      • wget -O assets/adversarial_r.npy https://github.com/do-community/tricking-neural-networks/blob/master/outputs/adversarial_r.npy?raw=true

      Теперь соедините изображение с отклонением. Создайте новый файл с именем step_3_adversarial.py​​​:

      • nano step_3_adversarial.py

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

      1. Преобразование изображения
      2. Применение отклонения r
      3. Обратное преобразование изображения с отклонением

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

      step_3_adversarial.py

      from PIL import Image
      import torchvision.transforms as transforms
      import torch
      import numpy as np
      import os
      import sys
      
      from step_2_pretrained import get_idx_to_label, get_image_transform, predict, load_image
      
      
      def main():
          pass
      
      
      if __name__ == '__main__':
          main()
      

      Далее создайте «трансформацию изображения», которая инвертирует предыдущую трансформацию изображения. Разместите это после операций импорта, перед функцией main:

      step_3_adversarial.py

      . . .
      def get_inverse_transform():
          return transforms.Normalize(
              mean=[-0.485/0.229, -0.456/0.224, -0.406/0.255],  # INVERSE normalize images, according to https://pytorch.org/docs/stable/torchvision/models.html
              std=[1/0.229, 1/0.224, 1/0.255])
      . . .
      

      Как и ранее, операция transforms.Normalize​​​ вычитает среднее значение и делит на стандартное отклонение (т.е. для оригинального изображения x, y = transforms.Normalize(mean=u, std=o) = (x - u) / o). Вы делаете определенные вычисления и определяете новую операцию, которая возвращает нормализованную функцию (transforms.Normalize(mean=-u/o, std=1/o) = (y - -u/o) / 1/o = (y + u/o) o = yo + u = x).

      В рамках обратного преобразования добавьте метод, который преобразовывает тензор PyTorch обратно в изображение PIL. Добавьте это после последней функции:

      step_3_adversarial.py

      . . .
      def tensor_to_image(tensor):
          x = tensor.data.numpy().transpose(1, 2, 0) * 255.  
          x = np.clip(x, 0, 255)
          return Image.fromarray(x.astype(np.uint8))
      . . .
      
      • tensor.data.numpy()​​ конвертирует тензор PyTorch в массив NumPy. .transpose(1, 2, 0) перестраивает (channels, width, height) в (height, width, channels). Этот массив NumPy примерно находится в диапазоне (0, 1). Наконец, умножьте на 255, чтобы убедиться, что изображение находится сейчас в диапазоне (0, 255).
      • np.clip гарантирует, что все значения в изображении находятся между (0, 255).
      • x.astype(np.uint8) гарантирует, что все значения изображения являются целыми числами. Наконец, Image.fromarray(...) создает объект изображения PIL из массива NumPy.

      Затем используйте эти утилиты для создания состязательного образца:

      step_3_adversarial.py

      . . .
      def get_adversarial_example(x, r):
          y = x + r
          y = get_inverse_transform()(y[0])
          image = tensor_to_image(y)
          return image
      . . .
      

      Эта функция генерирует состязательный образец, описанный в начале этого раздела:

      1. y = x + r. Возьмите ваше отклонение r и добавьте его в оригинальное изображение x.
      2. get_inverse_transform​​:Получите и примените обратное преобразование изображения, которое вы установили несколькими строками ранее.
      3. tensor_to_image: Конвертируйте тензор PyTorch обратно в изображение объекта.

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

      step_3_adversarial.py

      def main():
          x = load_image()
          r = torch.Tensor(np.load('assets/adversarial_r.npy'))
      
          # save perturbed image
          os.makedirs('outputs', exist_ok=True)
          adversarial = get_adversarial_example(x, r)
          adversarial.save('outputs/adversarial.png')
      
          # check prediction is new class
          print(f'Old prediction: {predict(x)}')
          print(f'New prediction: {predict(x + r)}')
      

      Ваш завершенный файл должен соответствовать step_3_adversarial.py на GitHub. Сохраните файл, закройте редактор и запустите скрипт с помощью следующей команды:

      • python step_3_adversarial.py assets/dog.jpg

      Вы увидите следующий результат:

      Output

      Old prediction: Pembroke, Pembroke Welsh corgi New prediction: goldfish, Carassius auratus

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

      Шаг 4 — Принцип работы состязательного образца

      Для начала вы можете ознакомиться со статьей Создание эмоционального фильтра «Собака».

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

      argmax_y P(y|x,t)
      

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

      argmax_x P(w|x)
      

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

      Напомним, что в этом обучающем руководстве вы предполагаете, что состязательный пример является аффинным преобразованием x. Другими словами, ваш состязательный пример принимает форму x + r для некоторых r. На следующем шаге вы напишете скрип, чтобы создать это r.

      Шаг 5 — Создание состязательного образца

      На этом шаге вы познакомитесь с отклонением r, из-за которого ваш корги будет классифицирован как золотая рыбка. Создайте новый файл с именем step_5_perturb.py​​​:

      Импортируйте необходимые пакеты и декларируйте функцию main:

      step_5_perturb.py

      from torch.autograd import Variable
      import torchvision.models as models
      import torch.nn as nn
      import torch.optim as optim
      import numpy as np
      import torch
      import os
      
      from step_2_pretrained import get_idx_to_label, get_image_transform, predict, load_image
      from step_3_adversarial import get_adversarial_example
      
      
      def main():
          pass
      
      
      if __name__ == '__main__':
          main()
      

      Непосредственно за операциями импорта и перед функцией main определите две константы:

      step_5_perturb.py

      . . .
      TARGET_LABEL = 1
      EPSILON = 10 / 255.
      . . .
      

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

      После двух констант добавьте вспомогательную функцию для определения нейронной сети и параметра отклонения r:

      step_5_perturb.py

      . . .
      def get_model():
          net = models.resnet18(pretrained=True).eval()
          r = nn.Parameter(data=torch.zeros(1, 3, 224, 224), requires_grad=True)
          return net, r
      . . .
      
      • model.resnet18(pretrained=True) загружает предварительно обученную нейронную сеть под названием ResNet18, как и ранее. Также, как и ранее, вы устанавливаете модель в режим оценки, используя .eval.
      • nn.Parameter(...) определяет новое отклонение r, размер входного изображения. Входное изображение имеет размер (1, 3, 224, 224). Основной аргумент requires_grad=True обеспечивает возможность обновить данное отклонение r в последующих строках в этом файле.

      Затем начинайте вносить изменения в вашу функцию main. Начните с загрузки модели net, загрузки вводных x и определения ярлыка label:

      step_5_perturb.py

      . . .
      def main():
          print(f'Target class: {get_idx_to_label()[str(TARGET_LABEL)]}')
          net, r = get_model()
          x = load_image()
          labels = Variable(torch.Tensor([TARGET_LABEL])).long()
        . . .
      

      Затем определите критерий и оптимизатор в функции main. Первый указывает PyTorch, что является целью, т. е. какие потери минимизировать. Второй указывает PyTorch, как обучать параметр r:

      step_5_perturb.py

      . . .
          criterion = nn.CrossEntropyLoss()
          optimizer = optim.SGD([r], lr=0.1, momentum=0.1)
      . . .
      

      Сразу за этим добавьте основной обучающий цикл для параметра r:

      step_5_perturb.py

      . . .
          for i in range(30):
              r.data.clamp_(-EPSILON, EPSILON)
              optimizer.zero_grad()
      
              outputs = net(x + r)
              loss = criterion(outputs, labels)
              loss.backward()
              optimizer.step()
      
              _, pred = torch.max(outputs, 1)
              if i % 5 == 0:
                  print(f'Loss: {loss.item():.2f} / Class: {get_idx_to_label()[str(int(pred))]}')
      . . .
      

      На каждой итерации данного обучающего цикла вы сможете следующее:

      • r.data.clamp_(...): Убедиться, что параметр r невелик и находится в пределах EPSILON 0.
      • optimizer.zero_grad(): Очистить любые градиенты, которые вы вычислили в предыдущей итерации.
      • model(x + r): Запустить логический вывод на модифицированном изображении x + r.
      • Вычислить loss.
      • Вычислить градиент loss.backward.
      • Определить шаг градиентного спуска optimizer.step.
      • Вычислить прогноз pred.
      • Наконец, сообщить о потере и прогнозируемом классе print(...).

      Затем сохраните финальное отклонение r:

      step_5_perturb.py

      def main():
          . . .
          for i in range(30):
              . . .
          . . .
          np.save('outputs/adversarial_r.npy', r.data.numpy())
      

      Непосредственно за этим в функции main сохраните изображение с отклонением:

      step_5_perturb.py

      . . .
          os.makedirs('outputs', exist_ok=True)
          adversarial = get_adversarial_example(x, r)
      

      После этого запустите прогноз на оригинальном изображении и на состязательном образце:

      step_5_perturb.py

          print(f'Old prediction: {predict(x)}')
          print(f'New prediction: {predict(x + r)}')
      

      Еще раз убедитесь, что ваш скрипт соответствует step_5_perturb.py на GitHub. Сохраните, выйдите и запустите скрипт:

      • python step_5_perturb.py assets/dog.jpg

      Ваш скрипт покажет следующий вывод.

      Output

      Target class: goldfish, Carassius auratus Loss: 17.03 / Class: Pembroke, Pembroke Welsh corgi Loss: 8.19 / Class: Pembroke, Pembroke Welsh corgi Loss: 5.56 / Class: Pembroke, Pembroke Welsh corgi Loss: 3.53 / Class: Pembroke, Pembroke Welsh corgi Loss: 1.99 / Class: Pembroke, Pembroke Welsh corgi Loss: 1.00 / Class: goldfish, Carassius auratus Old prediction: Pembroke, Pembroke Welsh corgi New prediction: goldfish, Carassius auratus

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

      Теперь вы показали, что нейронные сети можно легко обмануть. Более того, отсутствие устойчивости к состязательным образцам имеет значительные последствия. Далее возникает естественный вопрос: как можно бороться с состязательными образцами? Различные организации, включая OpenAI​​​, провели множество исследований на эту тему. В следующем разделе вы запустите защиту, чтобы пресечь воздействие состязательного образца.

      Шаг 6 — Защита от состязательных образцов

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

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

      1. assets/dog.jpg
      2. outputs/adversarial.png

      Здесь они представлены рядом. Ваше оригинальное изображение будет иметь другие пропорции. Вы можете сказать, какое из изображений является состязательным образцом?

      (слева) Корги как золотая рыбка, состязательное изображение, (справа) корги как корги, не состязательное изображение)

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

      • wget -O assets/adversarial.png https://github.com/alvinwan/fooling-neural-network/blob/master/outputs/adversarial.png?raw=true
      • python step_2_pretrained.py assets/adversarial.png

      Это позволит получить вывод о том, что изображение принадлежит к классу «золотая рыбка», что доказывает состязательную природу изображения:

      Output

      Prediction: goldfish, Carassius auratus

      Вы запустите довольно примитивную, но эффективную защиту: сжатие изображения с потерями путем записи в формате JPEG. Откройте интерактивную строку Python:

      Затем загрузите состязательное изображение как PNG, и сохраните его снова как JPEG.

      • from PIL import Image
      • image = Image.open('assets/adversarial.png')
      • image.save('outputs/adversarial.jpg')

      Введите CTRL + D, чтобы покинуть интерактивную строку Python. Затем запустите логический вывод с помощью вашей модели на сжатом состязательном образце:

      • python step_2_pretrained.py outputs/adversarial.jpg

      Теперь это позволит получить вывод о том, что изображение принадлежит к классу «корги», что доказывает эффективность вашей примитивной защиты.

      Output

      Prediction: Pembroke, Pembroke Welsh corgi

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

      Заключение

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

      (слева) Корги как золотая рыбка, состязательное изображение, (справа) корги как корги, не состязательное изображение)

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

      • Adversarial Machine Learning (Состязательное машинное обучение), обучающее руководство по материалам конференции NeurIPS 2018.
      • Соответствующие посты блога от OpenAI о состязательных образцах и устойчивости перед состязательными атаками.

      Дополнительную информацию о машинном обучении, а также обучающие руководства вы можете найти на странице, посвященной машинному обучению.



      Source link