PyTorch
PyTorch — это фреймворк для языка программирования Python, предназначенный для машинного обучения. Он включает в себя набор инструментов для работы с моделями, используется в обработке естественного языка, компьютерном зрении и других похожих направлениях.
Освойте профессию «Data Scientist»
Фреймворк создан на базе Torch, библиотеки для языка Lua, которая предназначена для математических расчетов и машинного обучения. Но его особенность — следование стилю Python и философии программирования на этом языке. Некоторые говорят, что PyTorch — один из самых «питоновских» фреймворков для ML.
У PyTorch открытый исходный код, он распространяется бесплатно. Его поддержкой и развитием сейчас в основном занимается команда искусственного интеллекта Facebook*.
Вокруг фреймворка создана целая экосистема, состоящая из множества библиотек для разных целей. В результате получается комплексный и мощный инструмент для решения задач машинного обучения.
Профессия / 24 месяца
Data Scientist
Дата-сайентисты решают поистине амбициозные задачи. Научитесь создавать искусственный интеллект, обучать нейронные сети, менять мир и при этом хорошо зарабатывать. Программа рассчитана на новичков и плавно введет вас в Data Science.
Кому и для чего нужен PyTorch
Фреймворком PyTorch пользуются специалисты по машинному обучению, работающие с языком Python — одним из наиболее популярных в этой отрасли. Непосредственно PyTorch ориентирован на глубокое обучение — это подвид ML, при котором используются многослойные обучаемые модели и нейронные сети. В глубоком обучении модель может обучаться за счет собственной обработки данных, и это позволяет решать больше разнообразных задач, чем в случае с классическим ML с узкоспециализированными моделями.
Чаще всего глубокое обучение и в частности PyTorch используются в следующих отраслях:
- распознавание образов на изображениях;
- компьютерное зрение, обнаружение движущихся объектов;
- поиск закономерностей, анализ данных, в том числе неструктурированных;
- обработка естественного языка, распознавание речи и машинный перевод;
- создание машинных описаний для изображений;
- анализ текстов и поиск в них информации;
- генерация текстового контента и картинок.
Все перечисленные задачи актуальны и нужны во множестве отраслей — начиная с аналитики и заканчивая творчеством.
Чем PyTorch отличается от других ML-фреймворков
Решений для машинного обучения много, и они различаются подходом. У PyTorch есть две важных особенности, которые выделяют его среди этого ряда.
Динамические вычисления. PyTorch динамически пересчитывает граф вычислений «на лету», и это удобно. Объясним, что это значит.
Нейронная сеть или модель для машинного обучения представлена как граф — структура, состоящая из вершин и связей между ними. Это «нейроны» и «синапсы», которые хранят и передают информацию. В вершинах хранятся формулы, а на ребрах — веса, числовые значения, которые пересчитываются на каждом шаге.
В большинстве доступных решений граф вычислений можно изменить только до компиляции. Когда программа скомпилирована, разработчик может только запустить проход по графу в прямом или обратном направлении. Если задача сложная, такой подход серьезно усложняет отладку.
В PyTorch подход другой: граф не существует как фиксированная структура, а динамически выстраивается непосредственно при вычислениях. При каждом проходе граф перестраивается по новым условиям. В результате у разработчика меньше ограничений, и он может реализовать более сложные и нестандартные решения.
Автоматическое дифференцирование. Дифференцированием называют нахождение производной функции. Это математическая операция, важная для машинного обучения — с помощью дифференцирования как раз пересчитывают веса. Так работает алгоритм обратного распространения ошибки, ключевой в обучении нейронных сетей.
Нахождение производной — довольно сложная для компьютера задача. Иногда ML-специалисты вовсе решают ее вручную, считая производную на бумаге. Другой способ — использовать приближения, но тут высок риск потерять точность из-за округлений. Есть и языки, которые ищут производную по правилам, но вместо точного результата могут получиться громоздкие и малоинформативные выражения.
Автоматическое дифференцирование решает эту проблему. Суть решения — определять большую и сложную функцию через маленькие, «базовые». Если значения базовых функций в какой-то точке известны, то можно автоматически вычислить и значение, и производную сложной функции, основанной на них.
PyTorch поддерживает автоматическое дифференцирование. Совместно с динамическими вычислениями выходит так, что фреймворк строит граф и вычисляет веса «на лету», а человеку достаточно написать пару строк кода. Это удобно и позволяет реализовать более сложные проекты.
CUDA. Технология, которая позволяет запускать вычисления и на процессоре, и на видеокарте. Особенность глубокого обучения — серьезная ресурсоемкость: модели во время обучения и работы отнимают много ресурсов компьютера. Поэтому для их запуска начали использовать графические процессоры видеокарт — они мощные и подходят для сложных вычислений.
Обычно все вычисления можно запустить или на центральном процессоре, или на видеокарте. В некоторых фреймворках существует две версии — для CPU и GPU. А перемещать вычисления с одной «площадки» на другую сложно и неудобно. Решения получаются довольно громоздкими.
В PyTorch не так. Благодаря поддержке технологии CUDA сущности с легкостью можно перемещать на видеокарту или создавать прямо на ней. Одной строчкой можно перебросить вычисления на видеокарту, посчитать что-то сложное, а потом вернуть обратно на процессор. Все это доступно непосредственно из интерпретатора.
Правда, есть и ограничение. CUDA работает только с видеокартами NVIDIA, причем не со всеми. Так что не каждая конфигурация компьютера подойдет для ее использования.
Станьте дата-сайентистом и решайте амбициозные задачи с помощью нейросетей
Что есть в PyTorch
Структура PyTorch и его возможности многим напоминают другой фреймворк для Python — NumPy, предназначенный для сложных вычислений. Но у PyTorch есть особенности: он работает динамически, может использовать мощности графического процессора и облегчает задачу специалисту.
Вот какие возможности есть у PyTorch. Это не исчерпывающий список — просто несколько примеров.
Тензоры. Тензорами называют многомерные массивы — структуры данных, больше всего напоминающие математические матрицы. С их помощью строятся модели для машинного обучения, хранится и обрабатывается информация, с которой работает модель.
Тензоры в PyTorch похожи на аналогичные в NumPy. Их можно создавать из Python-массивов или генерировать с помощью специальной функции. Значения внутри матрицы могут быть целочисленными, знаковыми и беззнаковыми, а также дробными с плавающей точкой. Автоматического приведения типов тут нет, в отличие от NumPy, поэтому можно не опасаться, что программа автоматически присвоит значениям тип, отнимающий слишком много памяти.
Две особенности тензоров в PyTorch:
- Они оптимизированы для автоматического дифференцирования, то есть поиска производной «на каждом шаге».
- Их можно запускать и на основных процессорных мощностях, и на видеокарте, перемещать с обычного процессора на графический и обратно.
Встроенные операции. С тензорами можно выполнять разнообразные операции: сложение, вычитание, слияние и другие. Полный спектр математических операций для тензоров позволяет полноценно работать с ними. Иногда PyTorch применяют исключительно для тензорных вычислений — его возможности для этого подходят.
Чаще всего в работе с PyTorch пользуются различными математическими операциями над тензорами, а также функциями инициализации, которые позволяют быстро создавать тензор. Есть и BLAS-функции — так в программировании называют реализацию функций линейной алгебры.
Кроме того, тензоры PyTorch можно конвертировать в тензоры NumPy и обратно — для этого тоже есть специальные функции.
Datasets. Датасет — это набор данных для машинного обучения. Обычно наборы бывают двух видов: для обучения и для проверки, как работает обученная модель. В большинстве ML-фреймворков, в том числе PyTorch, есть встроенные датасеты. Это наборы популярных и известных в сообществе пакетов данных.
Для решения серьезных задач такие датасеты вряд ли подойдут, но они бывают нужны для прототипирования или при сравнительном анализе моделей. Быстрее и удобнее загрузить в сравниваемые модели тестовые датасеты и посмотреть на результаты, чем составлять и загружать реальные. К тому же реальные наборы данных зачастую больше весят, дольше обрабатываются и сильнее нагружают мощности.
Data Loader. Data Loader — это модуль, который позволяет создавать мини-пакеты в рамках одного датасета и подгружать их в модель. С его помощью можно перемешивать данные, определять размер мини-пакета и выполнять другие действия.
Читайте также Как разобраться в Data Science
Модули PyTorch
В PyTorch есть несколько встроенных модулей для решения разных конкретных задач. Основных три, и вот для чего они нужны.
Autograd. Этот модуль непосредственно предназначен для автоматического дифференцирования. Производные высчитываются при прямом прохождении графа. Потом запускается обратное прохождение, в ходе которого вычисляются градиенты — в нейронных сетях так называют векторы, которые показывают направление и крутизну производных. Именно градиенты используются в алгоритме обратного распространения ошибки. С помощью модуля Autograd производные и градиенты считаются в автоматическом режиме.
Optim. В этом модуле собраны инструменты оптимизации для работы нейронных сетей. Он включает в себя реализации всех наиболее часто используемых методов, так что их нет необходимости писать с нуля. Так работа модели может стать эффективнее без лишних трудозатрат со стороны специалиста.
nn. Этот модуль включает в себя абстракции более высокого уровня, чем в Autograd — то есть более удаленные от «железа» и вычислений, но приближенные к человеческому пониманию. Это нужно для построения сложных нейронных сетей, которые может быть непросто понять и описать с использованием инструментов низкого уровня.
Программы экосистемы PyTorch
Вокруг фреймворка сложилась целая экосистема, состоящая из дополнительных и вспомогательных библиотек, подпрограмм и иных решений. Компоненты этой экосистемы обычно нужны только продвинутым пользователям PyTorch, которые работают с фреймворком профессионально. Но знать об их существовании полезно и на начальных уровнях изучения.
Создание и запуск проектов. Classy Vision и PyTorch Lightning — фреймворки, которые упрощают и расширяют работу с PyTorch. Они реализованы на более высоком уровне абстракции и предоставляют среду для запуска проектов на PyTorch.
С помощью этих фреймворков проще работать. В них реализованы полезные функции, утилиты и «синтаксический сахар» — нововведения, упрощающие синтаксис команд. В результате те же действия можно выполнить проще и быстрее, в пару строк кода.
Развертывание. Перед специалистами может встать задача развертывания программ на PyTorch в продакшене — на серверах, доступных конечным пользователям. Обычно это нужно, когда нейросеть — сама по себе коммерческий продукт, например, «умный» фоторедактор.
Собственного модуля для развертывания в PyTorch нет, поэтому для него используются инструменты TorchScript, ONNX и Caffe2. Caffe2 изначально был отдельным фреймворком машинного обучения, но потом влился в PyTorch.
Репозитории. На GitHub существует несколько репозиториев с библиотеками и расширениями PyTorch для конкретных задач. Например, Transformers — решение для распознавания естественного языка: текста, звуков, чтения с картинок. Или Detectron2 — библиотека компьютерного зрения, которая умеет распознавать движущиеся объекты, сегментировать их и выделять ключевые точки.
Другие инструменты. В экосистеме PyTorch десятки библиотек, которые помогают быстрее и удобнее интерпретировать результаты, визуализировать и отслеживать их, создавать датасеты для обучения и делать многое другое. Вот несколько примеров.
- Weights and Biases — научный инструмент для отслеживания результатов экспериментов.
- Clear ML — также менеджер экспериментов и средство управления версиями.
- Tensorboard — набор инструментов для визуализации, который изначально создавался для TensorFlow, но потом был интегрирован в PyTorch.
- Torchgeo — набор датасетов, моделей и инструментов для программ, работающих с геопространственной информацией.
- MONAI — решение для оптимизации PyTorch под задачи в сфере медицины.
Это только несколько примеров. В реальности дополнительных инструментов для PyTorch намного больше: они есть практически для любых задач.
Преимущества PyTorch
Гибкость. Фреймворк позволяет создавать даже сложные модели и решать разнообразные задачи. Это возможно благодаря прямому доступу к математическим функциям и возможности писать собственные. Есть решения, которые не позволяют построить что-то нестандартное — PyTorch к ним не относится. Наоборот: он хорошо подходит именно для сложных и нестандартных моделей. Дополнительные плюсы — поддержка CUDA и инструменты для оптимизации.
Легкое внесение изменений. Благодаря динамическому построению графа вычислений PyTorch лишен недостатков, которые есть у некоторых альтернативных вариантов. Вносить изменения в граф можно «на лету», это упрощает отладку и переписывание нейронной сети при необходимости.
Простота адаптации с NumPy. Некоторые функции и возможности PyTorch напоминают таковые у NumPy, хотя PyTorch мощнее и функциональнее. Тем не менее проекты на NumPy легко конвертировать в формат PyTorch — и наоборот. Тензоры и другие сущности в этих двух инструментах похожи структурой: достаточно немного их изменить для полноценной конвертации. Специальная функция перевода тензоров из формата NumPy в формат PyTorch и обратно позволяет с легкостью адаптировать код друг с другом.
Подробная документация. Один из плюсов PyTorch для новичков — подробная, обширная и понятная документация, в которой расписаны все важные моменты. Все нужное задокументировано: если возникнут проблемы, всегда можно обратиться к актуальным документам. Правда, в полном виде они существуют только на английском, а на русский не переведены.
Обширное комьюнити. Раньше PyTorch был менее популярен, чем некоторые другие решения, но в последнее время по частоте упоминаний нагнал TensorFlow. У фреймворка большая экосистема, широкое и активное сообщество: это и помощь новичкам, и постоянное появление новых удобных решений для разработки.
Недостатки PyTorch
Отсутствие инструментов визуализации. Встроенных инструментов для построения графиков, диаграмм и других способов визуализации в PyTorch нет. Приходится пользоваться сторонними решениями, такими как Tensorboard, хотя изначально он предназначался не для PyTorch.
Отсутствие инструментов развертывания. То же самое касается развертывания программы на PyTorch на продакшен-серверах — так доступ к продукту получают конечные пользователи. В PyTorch нет встроенных инструментов для легкого запуска на серверах, поэтому приходится использовать сторонние стандарты, протоколы и инструменты.
Потребность в дополнительных библиотеках. Из-за всего перечисленного выше при профессиональной работе с PyTorch понадобится пользоваться дополнительными библиотеками. Это значит, что разработчик должен разобраться не с одним инструментом, а с несколькими, чтобы полноценно выполнять задачи. Но этот недостаток становится актуальным только при профессиональном использовании — новичкам обычно достаточно возможностей «чистого» PyTorch.
Как установить PyTorch
В локальной системе PyTorch можно установить с помощью pip — так называется менеджер пакетов, входящий в Python. Он позволяет с помощью одной-двух команд в консоли установить то или иное расширение, библиотеку либо фреймворк для языка. Кроме pip можно воспользоваться программным пакетом Anaconda для вычислений на Python — в нем тоже есть команда для установки дополнений.
Второй способ, подходящий для владельцев слабых компьютеров — воспользоваться сервисом для машинного обучения или исследований, например, Google Colab. Такие платформы предоставляют вычислительные мощности и среды с уже предустановленными инструментами для ML. Достаточно создать программу и прописать в верхней части команду для импорта нужного пакета — в нашем случае это PyTorch и его различные модули.
Как начать работать с PyTorch
Если фреймворк установлен, достаточно запустить среду разработки и в первых строках программы подключить нужные пакеты. В Python это делается с помощью команды import. После этого модули и команды PyTorch будут доступны, с ними можно будет писать код.
Изучать PyTorch стоит уже после знакомства с Python: важно разбираться в языке и понимать его особенности. Начать нужно с простых задач — создания тензоров, работы с несложными нейронными сетями. Со временем можно переходить к более сложным и интересным заданиям.
*Facebook принадлежит корпорации Meta, которая признана экстремистской и запрещена на территории России.
Data Scientist
Дата-сайентисты решают поистине амбициозные задачи. Научитесь создавать искусственный интеллект, обучать нейронные сети, менять мир и при этом хорошо зарабатывать. Программа рассчитана на новичков и плавно введет вас в Data Science.
Статьи по теме:
Где искать модели для машинного обучения, готовиться к собеседованиям и осваивать новые технологии на практике
Введение в PyTorch
PyTorch — один из самых популярных фреймворков глубокого обучения для специалистов в области machine learning. Создан он на основе библиотеки Torch.
Есть много вещей, которые делают PyTorch популярным. Это и простота использования, и динамический вычислительный граф, и тот факт, что он кажется более «питоновским», чем другие фреймворки, такие как Tensorflow.
В этом руководстве мы рассмотрим базовые компоненты PyTorch. Затем разберем задачу классификации изображений с использованием набора данных CIFAR10. Поскольку PyTorch имеет множество функций и вариантов их применения, очевидно, данное руководство не будет исчерпывающим. Это только введение в PyTorch. Цель этой статьи — создать общее представление об этом фреймворке и рассмотреть несколько его компонентов.
От редакции Pythonist. О глубоком обучении можно почитать в статье «Введение в глубокое обучение: пошаговое руководство».
Тензор
Центральным компонентом PyTorch является такая структура данных, как тензор. Если вы знакомы с NumPy, вы обнаружите, что тензоры PyTorch похожи на ndarrays в NumPy. Ключевое отличие заключается в том, что они поддерживают CUDA и созданы для запуска на аппаратных ускорителях, таких как графические процессоры.
Еще одна важная особенность тензоров заключается в том, что они оптимизированы для автоматического дифференцирования. Это основа алгоритма обучения нейронной сети, известного как обратное распространение ошибки.
Эти две особенности тензоров очень важны для глубокого обучения:
- огромные объемы данных, функций и итераций глубокого обучения требуют массивно-параллельной архитектуры графических процессоров для обучения в разумные сроки
- обучение с помощью обратного распространения ошибки требует эффективной и точной дифференциации.
PyTorch также поддерживает распределенные вычисления, расширяя процесс обучения за пределы одной машины!
С учетом сказанного выше давайте взглянем на тензорный API!
Тензоры в действии
Примечание. Если вы хотите вместе с нами запускать код, то перейдите сначала к разделу Установка.
Итак, мы можем естественным образом создавать тензоры из списков в Python:
A = [[6, 9, 2], [3, 3, 7], [1, 0, 3]] A_tensor = torch.tensor(A)
Это также естественно работает с Numpy ndArrays :
B = np.array([0,1,2,3]) B_tensor = torch.from_numpy(B)
Как и в NumPy, мы можем инициализировать тензоры случайными значениями, одними единицами или одними нулями. Просто укажите форму (и dtype , если хотите указать тип данных):
# with no dtype argument, torch will infer the type C = torch.zeros(4,4) C # tensor([[0., 0., 0., 0.], # [0., 0., 0., 0.], # [0., 0., 0., 0.], # [0., 0., 0., 0.]])
Не будем забывать, что тензоры не обязательно должны быть двумерными!
D = torch.ones(3,3,2, dtype=torch.int) D # tensor([[[1, 1], # [1, 1], # [1, 1]], # # [[1, 1], # [1, 1], # [1, 1]], # # [[1, 1], # [1, 1], # [1, 1]]], dtype=torch.int32)
Новый тензор можно создать из существующего. Итак, при желании мы могли бы создать новый тензор нулей с теми же свойствами (форма и тип данных), что и созданный нами A_tensor :
A_tensor_zeros = torch.zeros_like(A_tensor) A_tensor_zeros # tensor([[0, 0, 0], # [0, 0, 0], # [0, 0, 0]])
Или, может быть, вам нужны случайные значения с плавающей запятой:
# Аргумент dtype позволяет явно указать тип данных тензора A_tensor_rand = torch.rand_like(A_tensor, dtype=torch.float) A_tensor_rand # tensor([[0.2298, 0.9499, 0.5847], # [0.6357, 0.2765, 0.0125], # [0.1215, 0.1747, 0.9935]])
Хотите получить атрибуты тензора?
A_tensor_rand.dtype # torch.float32 A_tensor_rand.shape # torch.Size([3, 3]) A_tensor_rand.device # device(type='cpu')
Создавать тензоры — это хорошо. Однако настоящее веселье начнется, когда мы начнем манипулировать ими и применять математические операции. Встроенных операций существует много, мы точно не успеем обсудить их все. По этой ссылке можно ознакомиться с ними более подробно. Здесь же просто назовем несколько:
- умножение матриц
- вычисление собственных векторов и значений
- сортировка
- индексы, срезы, соединения
- Окно Хэмминга (не уверены, что это такое, но звучит круто!!)
Модули Dataset и DataLoader
Dataset
Как и Tensorflow, PyTorch имеет несколько наборов данных, включенных в пакет (например, Text, Image и Audio). В этом руководстве будет использоваться один из таких встроенных наборов данных изображений — CIFAR10. Эти датасеты очень распространены и широко задокументированы в сообществе ML. Они отлично подходят для прототипирования и сравнительного анализа моделей, поскольку вы можете сравнить производительность своей модели с тем, чего смогли достичь другие.
import torch import torchvision from torchvision.datasets import FashionMNIST # torchvision for image datasets from torchtext.datasets import AmazonReviewFull # torchtext for text from torchaudio.datasets import SPEECHCOMMANDS #torchaudio for audio training_data = FashionMNIST( # the directory you want to store the dataset, can be a string e.g. "data" root = data_directory, # if set to False, will give you the test set instead train = True, # download the dataset if it's not already available in the root path you specified download = True, # as the name implies, will transform images to tensor data structures so PyTorch can use them for training transform = torchvision.transforms.ToTensor() )
Кроме того, если в вашем наборе данных есть метки или классификации, можно быстро просмотреть их список:
training_data.classes # ['T-shirt/top', # 'Trouser', # 'Pullover', # 'Dress', # 'Coat', # 'Sandal', # 'Shirt', # 'Sneaker', # 'Bag', # 'Ankle boot'] training_data.class_to_idx # get the corresponding index with each class #
Очевидно, что встроенные наборы данных — это не все, что нужно специалисту по машинному обучению. Создание собственного набора данных с помощью PyTorch — довольно простой и гибкий процесс, хотя и немного более сложный, чем простой импорт.
DataLoader
Итерация по набору данных будет проходить каждую выборку постепенно, поэтому PyTorch предоставляет нам модуль DataLoader для простого создания мини-пакетов в наших наборах данных. DataLoader позволяет нам указать размер батча, а также перемешать данные:
train_dataloader = DataLoader(training_data, batch_size = 32, shuffle = True)
Таким образом, в процессе глубокого обучения вы сможете передавать свои данные в модель для обучения в мини-батчах и через DataLoader .
Прежде чем перейти к глубокому обучению, разберем еще один важный момент — настройку устройства. Если вы хотите тренироваться на графическом процессоре, вы можете проверить, доступен ли он для использования PyTorch:
torch.cuda.is_available() # True if GPU available
PyTorch по умолчанию использует центральный процессор. Поэтому даже при наличии графического процессора все равно нужно указать, что вы хотите его использовать для обучения. Если вы уверены, что ваш графический процессор доступен, вы можете использовать .to(«cuda») для своих тензоров и моделей. В противном случае вы можете рассмотреть возможность установки переменной device для любого доступного устройства:
device = "cuda" if torch.cuda.is_available() else "cpu" print(f"Device: ") # 'cuda' # you can specify .to("cuda") or .to(device) tensor = tensor.to("cuda") # attaching your neural network model to your GPU model = model.to(device)
Если вы используете Google Colab, у вас будет бесплатный доступ к графическому процессору.
Установка
Итак, мы будем использовать Google Colab.
Colab является хорошим помощником в проектах по машинному обучению. Для небольших проектов и учебных пособий это лучшее решение, т.к. Colab предоставляет бесплатный доступ к графическому процессору и среду, которая включает в себя уже установленные пакеты, такие как PyTorch, NumPy, Scikit-Learn.
Итак, для начала перейдите на страницу Google Colab и войдите в свою учетную запись Google. File > New notebook . Измените имя своего notebook на pytorchIntro.ipynb или какое-нибудь другое.
Colab не предоставляет экземпляр с доступом к графическому процессору по умолчанию. Поэтому вам нужно явно указать, что вы хотите его использовать. Вверху выберите Runtime > Change runtime type > Hardware accelerator > Select «GPU» > Save . Теперь у вас есть графический процессор для обучения ваших моделей!
Теперь, когда у нас есть Colab и графический процессор, давайте перейдем к коду.
Импорт
import torch from torch import nn from torch.utils.data import DataLoader from torchvision.utils import make_grid from torchvision.datasets import CIFAR10 from torchvision.transforms import ToTensor from torchvision.transforms import Normalize, Compose import os import matplotlib.pyplot as plt import numpy as np
Для начала импортируйте следующие модули. Это основные модули PyTorch, которые мы будем использовать, а также несколько вспомогательных импортов. Позже мы рассмотрим их более подробно.
Dataset
Набор данных CIFAR-10
Этот набор данных состоит из 60 000 цветных изображений 32×32, помеченных как один из 10 классов. Учебный набор составляет 50 000 изображений, а тестовый — 10 000.
Вот хорошая визуализация набора данных из домашнего источника:
Цель этого проекта — создание модели, которая сможет точно классифицировать изображения по одной из 10 классификаций.
Загрузка набора данных
Итак, мы импортировали CIFAR10 из torchvision , и теперь нам нужно загрузить фактический набор данных и подготовить его для загрузки в нейронную сеть.
Перед подачей в модель мы должны нормализовать наши изображения. Поэтому мы определим функцию преобразования transform() и используем torchvision.transforms.Normalize для нормализации всех наших изображений при создании переменных обучающих и тестовых данных.
Метод Normalize() использует желаемое среднее значение и стандартное отклонение в качестве аргументов. Поскольку это цветные изображения, значение должно быть предоставлено для каждого цветового канала (R, G, B).
Мы установим значения здесь равными 0,5, так как хотим, чтобы значения данных нашего изображения были близки к 0. Однако есть и другие, более точные подходы к нормализации.
transform = Compose( [ToTensor(), Normalize((0.5, 0.5, 0.5), # mean (0.5, 0.5, 0.5))] # std. deviation )
Теперь мы можем использовать нашу функцию transform() в качестве аргумента transform , чтобы PyTorch применил ее ко всему набору данных.
training_data = CIFAR10(root="cifar", train = True, # train set, 50k images download = True, transform=transform) test_data = CIFAR10(root = "cifar", train = False, # test set, 10k images download = True, transform = transform)
Когда наш набор данных загружен и нормализован, мы можем подготовить его для передачи в нейронную сеть с помощью PyTorch DataLoader , где можно определить размер пакета batch_size .
batch_size = 4 train_dataloader = DataLoader(training_data, batch_size=batch_size, shuffle=True) test_dataloader = DataLoader(test_data, batch_size=batch_size, shuffle=True)
DataLoader является итерируемым, поэтому давайте взглянем на train_dataloader , проверив размеры одной итерации:
for X, y in train_dataloader: print(f"Shape of X [N, C, H, W]: ") print(f"Shape of y: ") break # Shape of X [N, C, H, W]: torch.Size([4, 3, 32, 32]) # Shape of y: torch.Size([4]) torch.int64
Здесь X — изображения, а y — метки. Мы установили batch_size = 4 , чтобы каждая итерация через train_dataloader представляла собой мини-пакет из 4 изображений 32×32 и соответствующих им 4 меток.
Теперь давайте рассмотрим некоторые примеры в нашем наборе данных.
def imshow(img): img = img / 2 + .05 # revert normalization for viewing npimg = img.numpy() plt.imshow(np.transpose(npimg, (1,2,0))) plt.show() classes = training_data.classes training_data.classes #['airplane', # 'automobile', # 'bird', # 'cat', # 'deer', # 'dog', # 'frog', # 'horse', # 'ship', # 'truck'] dataiter = iter(train_dataloader) images, labels = dataiter.next() imshow(make_grid(images)) print(' '.join(f'' for j in range(batch_size)))
Обычно стоит провести более тщательное исследование и анализ данных, прежде чем переходить к построению модели. Однако, поскольку это всего лишь введение в PyTorch, мы перейдем к построению и обучению модели.
Определение базовой модели
Давайте построим нейронную сеть.
Сперва мы определим класс нашей модели и назовем его NeuralNetwork . Наша модель будет подклассом PyTorch nn.Module, который является базовым классом для всех модулей нейронной сети в PyTorch.
Поскольку в нашем наборе данных есть цветные изображения, форма каждого изображения будет (3, 32, 32) — тензор 32×32 в каждом из 3 цветовых каналов RGB.
Т.к. наша исходная модель будет состоять из полностью связанных слоев, нам нужно будет применить функцию nn.Flatten() для входных данных изображения. Наш метод сглаживания выведет линейный слой с 3072 (32 x 32 x 3) узлами.
Функция nn.Linear() принимает количество входных нейронов и количество выходных в качестве аргументов соответственно ( nn.Linear(1024 in, 512 out) ). После этого вы можете добавить слои Linear layers и ReLU в основной контент. Output нашей модели составляет 10 логитов, соответствующих 10 классам в нашем наборе данных.
После определения структуры модели мы зададим последовательность прямого прохода. Поскольку наша модель представляет собой последовательную модель, метод forward будет очень простым. Он будет вычислять выходной Tensor из входных Tensors .
При желании вы можете просто вывести модель model после ее определения, чтобы получить сводную информацию о структуре.
class NeuralNetwork(nn.Module): def __init__(self): super(NeuralNetwork, self).__init__() self.flatten = nn.Flatten() self.linear_relu_stack = nn.Sequential( nn.Linear(32*32*3, 1024), nn.ReLU(), nn.Linear(1024, 512), nn.ReLU(), nn.Linear(512, 10) ) def forward(self, x): x = self.flatten(x) logits = self.linear_relu_stack(x) return logits model = NeuralNetwork().to(device) print(model) #NeuralNetwork( # (flatten): Flatten(start_dim=1, end_dim=-1) # (linear_relu_stack): Sequential( # (0): Linear(in_features=3072, out_features=1024, bias=True) # (1): ReLU() # (2): Linear(in_features=1024, out_features=512, bias=True) # (3): ReLU() # (4): Linear(in_features=512, out_features=10, bias=True) # ) #)
Функция потерь и оптимизатор
Поскольку это задача классификации, мы будем использовать функцию кросс-энтропийных потерь Cross-Entropy . Напомним, что Cross-Entropy вычисляет логарифмическую потерю, когда модель выводит прогнозируемое значение вероятности между 0 и 1. Таким образом, поскольку прогнозируемая вероятность отличается от истинного значения, потери быстро возрастают. График ниже иллюстрирует поведение функции потерь по мере того, как прогнозируемое значение становится ближе и дальше от истинного значения.
В PyTorch мы можем просто использовать функцию CrossEntropyLoss() . Для нашего алгоритма оптимизации мы применим стохастический градиентный спуск, который реализован в пакете torch.optim вместе с другими оптимизаторами, такими как Adam и RMSprop . Нам просто нужно передать параметры нашей модели и скорость обучения lr . Если вы хотите использовать затухание импульса или веса при оптимизации модели, вы можете передать это оптимизатору SGD() в качестве параметров momentum и weight_decay (по умолчанию они равны 0).
loss_fn = nn.CrossEntropyLoss() optimizer = torch.optim.SGD( model.parameters(), lr=0.001 ) # momentum=0.9
Определение цикла обучения
Здесь мы определяем нашу функцию train() , которой мы будем передавать train_dataloader , model , loss_fn и optimizer в качестве аргументов в процессе обучения. Переменная size — это длина всего обучающего набора данных (50 КБ). В следующей строке model.train() — это метод nn.Module в PyTorch, который переводит модель в режим обучения, обеспечивая определенные варианты поведения, которые нам нужны (например, отсев, пакетная норма и т.д.). Затем мы пройдемся по каждому мини-пакету, указав, что мы хотели бы использовать GPU с to(device) . Мы загружаем мини-пакет в нашу модель, вычисляем потери, а затем выполняем обратное распространение.
Результат обратного распространения и обучения
Для шага обратного распространения нам нужно сначала запустить optimizer.zero_grad() . Это устанавливает градиент в ноль перед запуском обратного распространения, поскольку мы не хотим накапливать градиент за последующие проходы.
Метод loss.backward() использует потери для вычисления градиента, затем мы используем Optimizer.step() для обновления весов.
Наконец, мы можем вывести обновления процесса обучения, печатая вычисленные потери после каждых 2000 обучающих выборок.
def train(dataloader, model, loss_fn, optimizer): size = len(dataloader.dataset) model.train() for batch, (X, y) in enumerate(dataloader): X, y = X.to(device), y.to(device) # Compute prediction error pred = model(X) loss = loss_fn(pred, y) # Backpropagation optimizer.zero_grad() loss.backward() optimizer.step() if batch % 2000 == 0: loss, current = loss.item(), batch * len(X) print(f"loss: 7f> [5d>/5d>]")
Определение метода тестирования
Перед обучением модели давайте реализуем тестовую функцию. Это нужно для того, чтобы мы могли оценивать нашу модель и выводить точность на тестовом наборе. Большие отличия от метода тестирования заключаются в том, что мы используем model.eval() , чтобы перевести модель в режим тестирования, и torch.no_grad() , который отключит вычисление градиента, так как мы не используем обратное распространение во время тестирования. Наконец, мы вычисляем средние потери для набора тестов и общую точность.
def test(dataloader, model, loss_fn): size = len(dataloader.dataset) num_batches = len(dataloader) model.eval() test_loss, correct = 0, 0 with torch.no_grad(): for X, y in dataloader: X, y = X.to(device), y.to(device) pred = model(X) test_loss += loss_fn(pred, y).item() correct += (pred.argmax(1) == y).type(torch.float).sum().item() test_loss /= num_batches correct /= size print(f"Test Error: \n Accuracy: 0.1f>%, Avg loss: 8f> \n")
Обучение модели
Теперь, когда мы всё сделали, мы готовы к обучению! Укажите количество epochs , на которых вы хотите обучить модель. Каждая эпоха будет проходить цикл train , который выводит прогресс каждые 2000 выборок. Затем он проверяет модель на тестовом наборе и выводит точность и потери на тестовом наборе.
epochs = 10 for t in range(epochs): print(f"Epoch \n-------------------------------") train(train_dataloader, model, loss_fn, optimizer) test(test_dataloader, model, loss_fn) print("Done!")
Сохранение и загрузка модели
После завершения обучения, если вы хотите сохранить свою модель для использования в выводах, используйте torch.save() .
Передайте model.state_dict() в качестве первого аргумента. Это просто словарь, который сопоставляет слои с их соответствующими изученными параметрами (весами и смещениями).
В качестве второго аргумента дайте имя вашей модели (принято сохранять модели PyTorch с использованием расширений .pth или .pt ). Также можно указать полный путь, если вы хотите сохранить его в определенном каталоге.
Если вы хотите загрузить свою модель для логического вывода, используйте torch.load() , чтобы получить сохраненную модель, и сопоставьте изученные параметры с помощью load_state_dict .
model = NeuralNetwork() model.load_state_dict(torch.load("cifar_fc.pth"))
Оценка модели
Вы можете пройти через test_dataloader , чтобы сверить образец изображений с их метками.
dataiter = iter(test_dataloader) images, labels = dataiter.next() imshow(make_grid(images)) print('Ground Truth: ', ' '.join(f'' for j in range(4)))
Затем сравните его с предсказанными метками нашей модели, чтобы предварительно оценить ее производительность:
outputs = model(images) _, predicted = torch.max(outputs, 1) print('Predicted: ', ' '.join(f'' for j in range(4))) # Predicted: dog ship automobile deer
Итак, мы видим, что наша модель учится классифицировать! Давайте посмотрим на цифры производительности нашей модели.
correct = 0 total = 0 with torch.no_grad(): for data in test_dataloader: images, labels = data outputs = model(images) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print(f'Model accuracy: %') # Model accuracy: 53 %
Точность в 53% далеко не самый хороший результат. Однако это намного лучше, чем случайное угадывание или просто предсказание одного класса. Так что наша модель определенно чему-то научилась!
Далее мы можем быстро проверить, как она работает при классификации каждого класса:
correct_pred = total_pred = with torch.no_grad(): for data in test_dataloader: images, labels = data outputs = model(images) _, predictions = torch.max(outputs, 1) for label,prediction in zip(labels, predictions): if label == prediction: correct_pred[classes[label]] += 1 total_pred[classes[label]] += 1 for classname, correct_count in correct_pred.items(): accuracy = 100 * float(correct_count) / total_pred[classname] print(f'Accuracy for class : %') # Accuracy for class airplane: 58.9% # Accuracy for class automobile: 61.2% # Accuracy for class bird : 33.5% # Accuracy for class cat : 35.4% # Accuracy for class deer : 52.8% # Accuracy for class dog : 49.4% # Accuracy for class frog : 60.6% # Accuracy for class horse: 59.6% # Accuracy for class ship : 64.5% # Accuracy for class truck: 63.1%
Теперь у нас есть немного лучшее представление о производительности нашей модели. Сети было сложнее классифицировать изображения кошек и птиц.
Заключение
Безусловно, такие модели обычно не используются для классификации изображений. Это был всего лишь обучающий пример.
Надеемся, данная статья была вам полезна! Успехов в написании кода!
PyTorch — ваш новый фреймворк глубокого обучения
PyTorch — современная библиотека глубокого обучения, развивающаяся под крылом Facebook. Она не похожа на другие популярные библиотеки, такие как Caffe, Theano и TensorFlow. Она позволяет исследователям воплощать в жизнь свои самые смелые фантазии, а инженерам с лёгкостью эти фантазии имплементировать.
Данная статья представляет собой лаконичное введение в PyTorch и предназначена для быстрого ознакомления с библиотекой и формирования понимания её основных особенностей и её местоположения среди остальных библиотек глубокого обучения.
PyTorch является аналогом фреймворка Torch7 для языка Python. Разработка его началась в недрах Facebook ещё в 2012 году, всего на год позже появления самого Torch7, но открытым и доступным широкой публике PyTorch стал лишь в 2017 году. С этого момента фреймворк очень быстро набирает популярность и привлекает внимание всё большего числа исследователей. Что же делает его таким популярным?
Место среди остальных фреймворков
Для начала разберёмся, что же вообще такое фреймворк глубокого обучения. Под глубоким обучением как правило понимают обучение функции, представляющей собой композицию множества нелинейных преобразований. Такая сложная функция ещё называется потоком или графом вычислений. Фреймворк глубокого обучения должен уметь делать всего три вещи:
- Определять граф вычислений;
- Дифференцировать граф вычислений;
- Вычислять его.
Чем быстрее ты умеешь вычислять свою функцию и чем гибче твои возможности для её определения, тем лучше. Сейчас, когда каждый фреймворк умеет использовать всю мощь видеокарт, первый критерий перестал играть значительную роль. Что нас действительно интересует, так это доступные возможности для определения потока вычислений. Все фреймворки здесь можно разделить на три крупные категории.
- Фиксированные модули. Такой подход можно сравнить с конструктором Lego: пользователь комбинирует заранее определённые блоки в граф вычислений и запускает его. Прямой и обратный проходы уже зашиты в каждом таком блоке. Определение новых блоков гораздо сложнее использования готовых и требует совершенно иных знаний и умений. Расширяемость близка к нулю, однако если ваши идеи полностью реализуются в таком фреймворке, скорость разработки максимальна. Со скоростью работы, благодаря высокой оптимизированности заранее написанного кода, также не возникает проблем. Типичные представители: Caffe, Caffe2, CNTK, Kaldi, DL4J, Keras (как интерфейс).
- Статический граф вычислений. Эти фреймворки уже можно сравнить с полимерной глиной: на этапе описания возможно создать граф вычислений произвольного размера и сложности, однако после запекания (компиляции) он станет твёрдым и монолитным. Доступными останутся всего два действия: запустить граф в прямом или обратном направлениях. Все такие фреймворки используют декларативный стиль программирования и напоминают функциональный язык или математическую нотацию. С одной стороны, этот подход комбинирует гибкость на этапе разработки и скорость в момент исполнения. С другой стороны, как и в функциональных языках, отладка становится настоящей головной болью, а модели, выходящие за рамки парадигмы, требуют либо титанических усилий, либо здоровенных костылей для реализации. Представители: Theano, TensorFlow, MXNet.
- Динамический граф вычислений. Представьте теперь, что вы можете перестраивать статический граф перед каждым его запуском. Примерно это и происходит в данном классе фреймворков. Только графа как отдельной сущности здесь нет. Он, как и в императивных языках программирования, слишком сложен для явного построения и существует лишь в в момент исполнения. Точнее сказать, граф строится динамически каждый раз при прямом проходе для того, чтобы затем иметь возможность сделать проход обратный. Подобный подход даёт максимальную гибкость и расширяемость, позволяет использовать в вычислениях все возможности используемого языка программирования и не ограничивает пользователя вообще ничем. К этому классу фреймворков как раз и относятся Torch и PyTorch.
Уверен, многие из нас начинали разбираться с глубоким обучением, используя только NumPy. Прямой проход написать на нём тривиально, а формулу обновления весов можно посчитать на листочке или вообще получить готовые веса из астрала. Выглядеть такой первый код мог так:
import numpy as np def MyNetworkForward(weights, bias, x): h1 = weights @ x + bias a1 = np.tanh(h1) return a1 y = MyNetworkForward(weights, bias, x) loss = np.mean((y - y_hat) ** 2)
Со временем архитектуры сетей становятся сложнее и глубже и возможностей NumPy, карандаша и бумаги уже перестаёт хватать. Если ваше соединение с астралом к этому моменту ещё не закрылось и вам есть откуда брать веса, вам повезло. В противном же случае невольно начинаешь задумываться о двух вещах:
- Вот бы уметь запускать мои вычисления на видеокарте;
- Вот бы все градиенты считались за меня.
При этом не хочется менять привычный подход, хочется просто написать:
import numpy as np def MyNetworkForward(weights, bias, x): h1 = weights @ x + bias a1 = np.tanh(h1) return a1 weights.cuda() bias.cuda() x.cuda() y = MyNetworkForward(weights, bias, x) loss = np.mean((y - y_hat) ** 2) loss.magically_calculate_backward_pass()
Ну так вот, угадайте что? PyTorch ровно это и делает! Вот совершенно правильный код:
import torch def MyNetworkForward(weights, bias, x): h1 = weights @ x + bias a1 = torch.tanh(h1) return a1 weights = weights.cuda() bias = bias.cuda() x = x.cuda() y = MyNetworkForward(weights, bias, x) loss = torch.mean((y - y_hat) ** 2) loss.backward()
Остаётся лишь применить уже посчитанные обновления параметров.
В Theano и TensorFlow мы описываем граф на декларативном DSL, который затем компилируется в некоторый внутренний байткод и исполняется в монолитном ядре, написанном на C++, или же компилируется в код на C и исполняется как отдельный бинарный объект. Если в момент компиляции нам известен весь граф целиком, его с лёгкостью можно продифференцировать, например символьно. Однако так ли необходима стадия компиляции?
Оказывается, нет. Ничто не мешает нам строить граф динамически одновременно с его вычислением! А благодаря технике автоматического дифференцирования (automatic differentiation, AD) мы можем взять и продифференцировать граф в любой момент времени в любом его состоянии. В компиляции графа нет совершенно никакой реальной необходимости. Что касается скорости, вызов лёгких нативных процедур из интерпретатора Python оказывается не медленнее, чем исполнение скомпилированного кода.
Не ограниченные DSL и компиляцией, мы можем использовать все возможности Python и делать код по-настоящему динамическим. Например, применять различные функции активации по чётным и нечётным дням:
from datetime import date import torch.nn.functional as F . if date.today().day % 2 == 0: x = F.relu(x) else: x = F.linear(x) .
Или мы можем создать слой, который каждый раз прибавляет к тензору только что введённое пользователем значение:
. x += int(input()) .
Более полезный пример я покажу в конце статьи. Резюмируя, всё сказанное выше можно выразить следующей формулой:
PyTorch = NumPy + CUDA + AD.
Тензорные вычисления
Начнём с NumPy части. Тензорные вычисления — основа PyTorch, каркас, вокруг которого наращивается вся остальная функциональность. К сожалению, нельзя сказать, что мощь и выразительность библиотеки в данном аспекте совпадает с таковой у NumPy. Во всём, что касается работы с тензорами, PyTorch руководствуется принципом максимальной простоты и прозрачности, предоставляя тонкую обёртку над вызовами BLAS.
Тензоры
Тип данных, хранимых тензором, отражается в имени его конструктора. Конструктор без параметров вернёт специальное значение — тензор без размерности, который нельзя использовать ни в каких операциях.
>>> torch.FloatTensor() [torch.FloatTensor with no dimension]
Все возможные типы:
torch.HalfTensor # 16 бит, с плавающей точкой torch.FloatTensor # 32 бита, с плавающей точкой torch.DoubleTensor # 64 бита, с плавающей точкой torch.ShortTensor # 16 бит, целочисленный, знаковый torch.IntTensor # 32 бита, целочисленный, знаковый torch.LongTensor # 64 бита, целочисленный, знаковый torch.CharTensor # 8 бит, целочисленный, знаковый torch.ByteTensor # 8 бит, целочисленный, беззнаковый
Никакого автоматического определения типа или типа по-умолчанию не существует. torch.Tensor является сокращённым названием для torch.FloatTensor .
Автоматического приведения типов как в NumPy также не осуществляется:
>>> a = torch.FloatTensor([1.0]) >>> b = torch.DoubleTensor([2.0]) >>> a * b
TypeError: mul received an invalid combination of arguments - got (torch.DoubleTensor), but expected one of: * (float value) didn't match because some of the arguments have invalid types: (torch.DoubleTensor) * (torch.FloatTensor other) didn't match because some of the arguments have invalid types: (torch.DoubleTensor)
В этом плане PyTorch строже и безопаснее: вы не наткнётесь на двукратное увеличение потребляемой памяти, перепутав тип константы. Явное приведение типов доступно с помощью методов с соответствующими названиями.
>>> a = torch.IntTensor([1]) >>> a.byte() 1 [torch.ByteTensor of size 1] >>> a.float() 1 [torch.FloatTensor of size 1]
x.type_as(y) вернёт тензор значений из x того же типа, что и y .
Любое приведение тензора к своему собственному типу не копирует его.
Если передать конструктору тензора в качестве параметра список, будет построен тензор соответствующей размерности и с соответствующими данными.
>>> a = torch.IntTensor([[1, 2], [3, 4]]) >>> a 1 2 3 4 [torch.IntTensor of size 2x2]
Неправильно сформированные списки не допускаются так же, как и в NumPy.
>>> torch.IntTensor([[1, 2], [3]])
RuntimeError: inconsistent sequence length at index (1, 1) - expected 2 but got 1
Допускается построение тензора из значения любого типа последовательности, что весьма интуитивно и соответствует поведению NumPy.
Другой возможный набор аргументов конструктора тензора — его размер. Количество аргументов при этом определяет размерность.
Построенный таким методом тензор содержит мусор — случайные значения.
>>> torch.FloatTensor(1) 1.00000e-17 * -7.5072 [torch.FloatTensor of size 1]
>>> torch.FloatTensor(3, 3) -7.5072e-17 4.5909e-41 -7.5072e-17 4.5909e-41 -5.1601e+16 3.0712e-41 0.0000e+00 4.5909e-41 6.7262e-44 [torch.FloatTensor of size 3x3]
Индексирование
Поддерживается стандартное индексирование Python: обращение по индексу и срезы.
>>> a = torch.IntTensor([[1, 2, 3], [4, 5, 6]]) >>> a 1 2 3 4 5 6 [torch.IntTensor of size 2x3]
>>> a[0] 1 2 3 [torch.IntTensor of size 3]
>>> a[0][1] 2 >>> a[0, 1] 2
>>> a[:, 0] 1 4 [torch.IntTensor of size 2] >>> a[0, 1:3] 2 3 [torch.IntTensor of size 2]
Также в качестве индексов могут выступать другие тензоры. Однако, возможности здесь всего две:
- Одномерный torch.LongTensor , индексирующий по нулевому измерению (по элементам в случае векторов и по строкам в случае матриц);
- Соразмерный torch.ByteTensor , содержащий только значения 0 или 1 , служащий маской.
>>> a = torch.ByteTensor(3,4).random_() >>> a 26 119 225 238 83 123 182 83 136 5 96 68 [torch.ByteTensor of size 3x4] >>> a[torch.LongTensor([0, 2])] 81 218 40 131 144 46 196 6 [torch.ByteTensor of size 2x4]
>>> a > 128 0 1 0 1 0 0 1 0 1 0 1 0 [torch.ByteTensor of size 3x4] >>> a[a > 128] 218 131 253 144 196 [torch.ByteTensor of size 5]
Всю доступную информацию о тензоре помогут узнать функции x.dim() , x.size() и x.type() , а x.data_ptr() укажет на место в памяти, где находятся данные.
>>> a = torch.Tensor(3, 3) >>> a.dim() 2 >>> a.size() torch.Size([3, 3]) >>> a.type() 'torch.FloatTensor' >>> a.data_ptr() 94124953185440
Операции над тензорами
Соглашение о именовании в PyTorch гласит, что любая функция вида xxx возвращает новый тензор, т.е. является immutable функцией. В противоположность ей функция вида xxx_ изменяет изначальный тензор, т.е. является mutable функцией. Последние ещё носят название inplace функций.
Почти для любой immutable функции в PyTorch существует её менее чистый собрат. Однако бывает и так, что функция существует лишь в каком-то одном варианте. По понятным причинам, функции, изменяющие размер тензора всегда являются immutable.
Перечислять все доступные операции над тензорами я не буду, остановлюсь лишь на самых важных и разделю их на категории.
Функции инициализации
Как правило, они используются для инициализации при создании новых тензоров заданного размера
x = torch.FloatTensor(3, 4) # мусор x.zero_() # нули
Так как mutable функции возвращают ссылку на объект, удобнее записывать объявление и инициализацию в одну строчку.
x = torch.FloatTensor(3, 4).zero_()
- x.zero_()
Инициализирует тензор нулями. Не имеет immutable варианта. - x.fill_(n)
Заполняет тензор константой n. Аналогично не имеет immutable варианта. - x.random_(from, to)
Заполняет тензор сэмплами из дискретного (даже для вещественнозначных тензоров) равномерного распределения.
Если from и to не указаны, то они приравниваются нижней и верхней границам используемого типа данных соответственно.
Доступны также экспоненциальное и геометрическое распределения, распределение Коши, логарифм нормального распределения и ещё несколько более сложных вариантов инициализации тензора. Не стесняйтесь смотреть в документацию!
Математические операции
Самая часто используемая группа. Если операция здесь не изменяет размер и тип тензора, то у неё существует inplace вариант.
- z = x.add(y)
z = torch.add(x, y)
x.add_(y)
Сложение. - z = x.sub(y)
z = torch.sub(x, y)
x.sub_(y)
Вычитаение. - z = x.mul(y)
z = torch.mul(x, y)
x.mul_(y)
Умножение. - z = x.div(y)
z = torch.div(x, y)
x.div_(y)
Деление. Для целочисленных типов деление целочисленное. - z = x.exp()
z = torch.exp(x)
x.exp_()
Экспонента. - z = x.log()
z = torch.log(x)
x.log_()
Натуральный логарифм. - z = x.log1p()
z = torch.log1p(x)
x.log1p_()
Натуральный логарифм от x + 1 . Функция оптимизирована по точности вычислений для малых x . - z = x.abs()
z = torch.abs(x)
x.abs_()
Модуль.
Естественно, присутствуют все основные тригонометрические операции в том виде, в каком вы ожидаете их увидеть. Перейдём теперь к менее тривиальным функциям.
- z = x.t()
z = torch.t(x)
x.t_()
Транспонирование. Несмотря на то, что размер тензора меняется, существует inplace вариант функции, так как размер данных в памяти остаётся тем же. - z = x.mm(y)
z = torch.mm(x, y)
Матричное умножение. - z = x.mv(v)
z = torch.mv(x, v)
Умножение матрицы на вектор. - z = x.dot(y)
z = torch.dot(x, y)
Скалярное умножение тензоров. - bz = bx.bmm(by)
bz = torch.bmm(bx, by)
Перемножает матрицы целыми батчами.
>>> bx = torch.randn(10, 3, 4) >>> by = torch.randn(10, 4, 5) >>> bz = bx.bmm(bz) >>> bz.size() torch.Size([10, 3, 5])
Существуют также полные аналоги BLAS функций со сложными сигнатурами, такие как addbmm , addmm , addmv , addr , baddbmm , btrifact , btrisolve , eig , gels и множество других.
Операции редукции похожи друг на друга по сигнатуре. Почти все они своим последним необязательным аргументом принимают dim — размерность, по которой редукция проводится. Если аргумент не задан, операция действует на весь тензор целиком.
- s = x.mean(dim)
s = torch.mean(x, dim)
Выборочное среднее. Определена только для вещественнозначных тензоров. - s = x.std(dim)
s = torch.std(x, dim)
Выборочное стандартное отклонение. Определена только для вещественнозначных тензоров. - s = x.var(dim)
s = torch.var(x, dim)
Выборочная дисперсия. Определена только для вещественнозначных тензоров. - s = x.median(dim)
s = torch.median(x, dim)
Медиана. - s = x.sum(dim)
s = torch.sum(x, dim)
Сумма. - s = x.prod(dim)
s = torch.prod(x, dim)
Произведение. - s = x.max(dim)
s = torch.max(x, dim)
Максимум. - s = x.min(dim)
s = torch.min(x, dim)
Минимум.
Всевозможные операции сравнения ( eq , ne , gt , lt , ge , le ) также определены и возвращают в качестве результата своей работы маску типа ByteTensor .
Операторы + , += , — , -= , * , *= , / , /= , @ работают ровно так, как вы и ожидаете, вызывая соответствующие описанные выше функции. Однако из-за сложности и не полной очевидности API, я не рекомендую пользоваться операторами, а использовать вместо этого явный вызов нужных функций. По крайней мере не стоит смешивать два стиля, это позволит избежать ошибок навроде x += x.mul_(2) .
У PyTorch в запасе ещё много интересных функций вроде сортировки или поэлементного применения функции, но все они крайне редко используются в глубоком обучении. Если же вы захотите использовать PyTorch в качестве библиотеки тензорных вычислений, не забудьте перед этим заглянуть в документацию.
Broadcasting
Broadcasting — сложная тема. На мой взгляд, лучше бы его не было. Но он есть, хотя и появился лишь в одном из последних релизов. Многие операции в PyTorch теперь поддерживают broadcasting в привычном NumPy стиле.
Если говорить в общем, то два непустых тензора называются broadcastable, если, начиная с последнего измерения, размеры обоих тензоров в этом измерении либо равны, либо размер одного из них равен единице, либо измерений в тензоре больше не существует. Легче понять на примерах.
>>> x = torch.FloatTensor(5, 7, 3) >>> y = torch.FloatTensor(5, 7, 3) # broadcastable, тривиальный случай: все размеры, начиная с последнего измерения, равны
>>> x = torch.FloatTensor(5, 3, 4, 1) >>> y = torch.FloatTensor( 3, 1, 1) # broadcastable, размер по последнему измерению равен, по предпоследнему измерению у второго тензора размер равен единице, по второму измерению размеры равны, а дальше измерения у второго тензора заканчиваются
>>> x = torch.FloatTensor(5, 2, 4, 1) >>> y = torch.FloatTensor( 3, 1, 1) # не broadcastable, размер по второму измерению не равен (2 != 3) и при этом ни один из этих размеров не равен единице
>>> x = torch.FloatTensor() >>> y = torch.FloatTensor(2, 2) # не broadcastable, у x нет хотя бы одного (последнего) измерения
Тензор размерности torch.Size([1]) , будучи скаляром, очевидным образом broadcastable с любым другим тензором.
Размер тензора, получившегося в результате broadcasting, рассчитывается следующим образом:
- Если количество измерений у тензоров не равно, то там, где это необходимо, добавляются единицы.
- Затем размер получившегося тензора рассчитывается как поэлементный максимум исходных тензоров.
>>> x = torch.FloatTensor(5, 1, 4, 1) >>> y = torch.FloatTensor( 3, 1, 1) >>> (x+y).size() torch.Size([5, 3, 4, 1])
В примере размерность второго тензора была дополнена единицей в начале, а затем поэлементный максимум определил размерность результирующего тензора.
Подвох кроется в inplace операциях. Broadcasting для них разрешён лишь в том случае, когда размер исходного тензора не изменится.
>>> x = torch.FloatTensor(5, 3, 4, 1) >>> y = torch.FloatTensor( 3, 1, 1) >>> (x.add_(y)).size() torch.Size([5, 3, 4, 1])
>>> x=torch.FloatTensor(1, 3, 1) >>> y=torch.FloatTensor(3, 1, 7) >>> (x.add_(y)).size()
RuntimeError: The expanded size of the tensor (1) must match the existing size (7) at non-singleton dimension 2.
Во втором случае тензоры очевидно broadcastable, однако inplace операция не разрешена, поскольку x в ходе неё изменил бы размер.
Из NumPy и обратно
Функции torch.from_numpy(n) и x.numpy() могут быть использованы, чтобы конвертировать тензоры одной библиотеки в тензоры другой.
Тензоры при этом используют одно и то же внутренне хранилище, копирования данных не происходит.
>>> import torch >>> import numpy as np >>> a = np.random.rand(3, 3) >>> a array([[ 0.3191423 , 0.75283128, 0.31874139], [ 0.0077988 , 0.66912423, 0.3410516 ], [ 0.43789109, 0.39015864, 0.45677317]]) >>> b = torch.from_numpy(a) >>> b 0.3191 0.7528 0.3187 0.0078 0.6691 0.3411 0.4379 0.3902 0.4568 [torch.DoubleTensor of size 3x3] >>> b.sub_(b) 0 0 0 0 0 0 0 0 0 [torch.DoubleTensor of size 3x3] >>> a array([[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]])
На этом все основные моменты тензорной библиотеки PyTorch предлагаю считать рассмотренными. Надеюсь, теперь читателю понятно, что реализовать прямой проход для произвольной функции на PyTorch ничуть не сложнее, чем сделать это с помощью NumPy. Нужно лишь свыкнуться с inplace операциями и запомнить имена основных функций. Для примера, линейный слой с функцией активации softmax:
def LinearSoftmax(x, w, b): s = x.mm(w).add_(b) s.exp_() s.div_(s.sum(1)) return s
CUDA
Здесь всё просто: тензоры могут жить либо «на процессоре», либо «на видеокарте». Правда, они весьма привередливы и живут только на видеокартах от Nvidia, причём не на самых старых. По-умолчанию тензор создаётся на CPU.
x = torch.FloatTensor(1024, 1024).uniform_()
Память видеокарты при этом пуста.
0MiB / 4036MiB
Одним вызовом мы можем переместить тензор на GPU.
x = x.cuda()
При этом nvidia-smi покажет, что процесс python занял некоторое количество видеопамяти.
205MiB / 4036MiB
Свойство x.is_cuda поможет понять где сейчас находится тензор x .
>>> x.is_cuda True
На самом деле x.cuda() возвращает копию тензора, а не перемещает его.
Когда исчезнут все ссылки на тензор, находящийся в видеопамяти, PyTorch не удалит его моментально. Вместо этого при следующем выделении он либо переиспользует этот участок видеопамяти, либо очистит её.
Если у вас несколько видеокарт, функция x.cuda(device=None) с радостью примет в качестве опционального аргумента номер видеокарты, куда стоит положить тензор, а функция x.get_device() покажет на каком устройстве тензор x находится. Функция x.cpu() скопирует тензор из видеокарты «на процессор».
Естественно, мы не можем производить никакие операции с тензорами, находящимеся на разных устройствах.
Вот, например, как можно перемножить два тензора на видеокарте и вернуть результат обратно в оперативную память:
>>> import torch >>> a = torch.FloatTensor(10000, 10000).uniform_() >>> b = torch.FloatTensor(10000, 10000).uniform_() >>> c = a.cuda().mul_(b.cuda()).cpu()
И всё это доступно прямо из интерпретатора! Представьте аналогичный код на TensorFlow, где вам придётся создать граф, сессию, скомпилировать граф, инициализировать переменные и запустить граф на сессии. С помощью PyTorch я могу даже отсортировать тензор на видеокарте одной строчкой кода!
Тензоры можно не только копировать на видеокарту, но и создавать их прямо на ней. Для этого используется модуль torch.cuda .
В torch.cuda нет сокращения Tensor = FloatTensor .
Контекстный менеджер torch.cuda.device(device) позволяет создавать все определённые внутри него тензоры на указанной видеокарте. Результаты операций над тензорами с других устройств останутся там, где и должны быть. Переданное в x.cuda(device=None) значение приоритетнее, чем то, которое диктует контекстный менеджер.
x = torch.cuda.FloatTensor(1) # x.get_device() == 0 y = torch.FloatTensor(1).cuda() # y.get_device() == 0 with torch.cuda.device(1): a = torch.cuda.FloatTensor(1) # a.get_device() == 1 b = torch.FloatTensor(1).cuda() # a.get_device() == b.get_device() == 1 c = a + b # c.get_device() == 1 z = x + y # z.get_device() == 0 d = torch.FloatTensor(1).cuda(2) # d.get_device() == 2
Функция x.pin_memory() , доступная только для тензоров на CPU, копирует тензор в page-locked область памяти. Особенность её в том, что данные из неё могут быть очень быстро скопированы на GPU без участия процессора. Метод x.is_pinned() покажет текущий статус тензора. После того, как тензор окажется в page-locked памяти, мы можем передать именованный параметр async=True функции x.cuda(device=None, async=False) , чтобы попросить её загружать тензор на видеокарту асинхронно. Таким образом, ваш код может не ждать завершения копирования и сделать за это время что-нибудь полезное.
Параметр async не имеет эффекта, если x.is_pinned() == False . Ошибки это тоже не вызовет.
Как видите, адаптация произвольного вашего кода для вычислений на видеокарте требует лишь копирования всех тензоров в видеопамять. Большинству операций без разницы, где находятся ваши тензоры, если они находятся на одном устройстве.
Автоматическое дифференцирование
Механизм автоматического дифференцирования, заключённый в модуле torch.autograd , является хоть и не главным, но, без сомнения, важнейшим компонентом библиотеки, без которого та потеряла бы всякий смысл.
Вычисление градиента функции в заданной точке — центральная операция методов оптимизации, на которых, в свою очередь, держится всё глубокое обучение. Обучение здесь — синоним оптимизации. Существует три основных способа вычислить градиент функции в точке:
- Численно методом конечных разностей;
- Символьно;
- Использовать технику автоматического дифференцирования.
Первым методом пользуются лишь для проверки результатов из-за его низкой точности. Символьное вычисление производной эквивалентно тому, что вы делаете вручную, используя бумагу и карандаш, и заключается в применении списка правил к дереву символов. Про автоматическое же дифференцирование я расскажу в следующих параграфах. Библиотеки вроде Caffe и CNTK используют заранее предпосчитанную производную функции в символьном виде. Theano и TensorFlow используют комбинацию методов 2 и 3.
Автоматическое дифференцирование (AD) — достаточно простая и весьма очевидная техника вычисления градиента функции. Если вы, не используя интернет, попытаетесь решить задачу дифференцирования функции в заданной точке, вы совершенно точно придёте к AD.
Вот как AD работает. Любую из интересующих нас функций можно выразить как композицию некоторых элементарных функций, производные которых нам известны. Затем, используя правило дифференцирования сложной функции, мы можем подниматься всё выше и выше, пока не придём к искомой производной. Например, рассмотрим функцию двух переменных
Каждая из получившихся функций является элементарной функцией — мы можем с лёгкостью вычислить её производную.
Допустим, нас интересует
По правилу дифференцирования сложной функции можем записать
Все компоненты этого уравнения — производные элементарных функций и начальные значения — нам известны. Осталось лишь подставить их и вычислить результат. Однако подстановку мы будем совершать не в произвольном порядке, а начнём с начала — самых вложенных элементов.
Вот так за один проход мы можем вычислить производную функции в заданной точке. И необходимо для этого ровно такое же количество операций, как и для вычисления самой функции. Обратите внимание, что икс со звёздочкой — не символ, а некоторое числовое значение. Поэтому на каждом шаге рассмотренного алгоритма мы сохраняем не символьное выражение, а одно единственное число — текущее значение производной.
Чтобы лучше понять AD, мы можем реализовать простейший его вариант всего в 20 строчек кода на чистом Python! Будем вычислять значение функции и её производную в одной и той же точке одновременно. Первым делом запомним значение переменной в точке и её производную.
class Varaible: def __init__(self, value, derivative): self.value = value self.derivative = derivative
При сложении двух переменных мы сконструируем новую переменную, значение которой будет равняться сумме исходных переменных, а производная будет вычисляться по правилу вычисления производной суммы двух функций.
def __add__(self, other): return Varaible( self.value + other.value, self.derivative + other.derivative )
Аналогично для умножения и возведения в степень
def __mul__(self, other): return Varaible( self.value * other.value, self.derivative * other.value + self.value * other.derivative ) def __pow__(self, other): return Varaible( self.value ** other, other * self.value ** (other - 1) )
Теперь мы с лёгкостью можем одновременно вычислить и значение нашей функции и её частную производную по переменной x1 за один проход.
def f(x1, x2): vx1 = Varaible(x1, 1) vx2 = Varaible(x2, 0) vf = vx1 * vx2 + vx1 ** 2 return vf.value, vf.derivative print(f(2, 3)) # (10, 7)
Ровно таким же поведением обладает класс Variable из модуля torch.autograd . Конечно, в отличии от нашей наивной реализации, он хорошо оптимизирован, поддерживает тензоры PyTorch и все возможные с ними дифференцируемые операции. К тому же, он вычисляет производную не по одному аргументу за раз, а по всем сразу. Давайте рассмотрим пример.
>>> from torch.autograd import Variable >>> x = torch.FloatTensor(3, 1).uniform_() >>> w = torch.FloatTensor(3, 3).uniform_() >>> b = torch.FloatTensor(3, 1).uniform_() >>> x = Variable(x, requires_grad=True) >>> w = Variable(w) >>> b = Variable(b) >>> y = w.mv(x).add_(b) >>> y Variable containing: 0.7737 0.6839 1.1542 [torch.FloatTensor of size 3] >>> loss = y.sum() >>> loss Variable containing: 2.6118 [torch.FloatTensor of size 1] >>> loss.backward() >>> x.grad Variable containing: 0.2743 1.0872 1.6053 [torch.FloatTensor of size 3]
И снова нам никак не нужно изменять наш код для вычислений: достаточно лишь обернуть тензоры в Variable и он сам позаботится о сохранении всех методов и свойств, а также добавит парочку новых. x.backward() выполнит обратный проход и посчитает производную по всем переменным, при создании которых мы указали requires_grad=True . Производная при этом запишется в свойство x.grad . Получить же свой тензор обратно мы можем используя свойство x.data .
В операциях нельзя смешивать обычные тензоры и тензоры, обёрнутые в Variable .
Свойство x.requires_grad покажет, нуждается ли узел графа в вычислении градиента. Правило такое: если хоть у одного дочернего узла это свойство установлено, оно будет установлено и у родителя.
Что стоит обсудить отдельно, так это inplace операции. Далеко не все такие операции, доступные для тензора, доступны для Variable , так как исходное значение тензора может быть необходимо для вычисления производной. Общее правило: пока у вас нет серьёзных ограничений по памяти, лучше всегда использовать immutable операции. В противном случае придётся рассматривать каждую отдельно взятую операцию на предмет возможности её замены mutable версией. Волноваться здесь не стоит: PyTorch никогда не разрешит вам сделать что-то, что молча приведёт к неправильному результату вычислений.
Пример
В качестве примера мы не будем обучать однослойный перцептрон и даже не будем строить FaceResNet-1337 . С этими задачами PyTorch, как и остальные фреймворки, справляется играючи, а массу подобных примеров легко найти в сети. Мы посмотрим на задачу, от которой остальные фреймворки в ужасе разбегутся.
Допустим, вы прочитали статью Deep Function Machines: Generalized Neural Networks for Topological Layer Expression и решили попробовать реализовать её на практике. Статья обобщает слой нейронной сети как отображение между произвольными Хаусдорфовыми пространствами. Таким образом мы можем построить слой нейронной сети с непрерывным ядром, отображающий одно функциональное пространство в другое. Автор доказывает теорему, о том, что произвольному такому слою соответствует дискретный слой стандартной нейронной сети, причём единственный. Отношение это определяется следующим образом:
Без вывода и доказательства результат кажется достаточно простым. w — непрерывное параметризованное ядро, W — дискретное ядро, которое мы строим. Осталось лишь для каждого прохода вычислить W , применить его к каким-нибудь данным, посчитать производные, распространить ошибку через интеграл и обновить параметры w . Звучит легко? Для PyTorch — да. В качестве ядра используем волновое ядро из статьи.
import numpy as np import torch from torch.autograd import Variable def kernel(u, v, s, w, p): uv = Variable(torch.FloatTensor([u, v])) return s[0] + w.mv(uv).sub_(p).cos().dot(s[1:])
Быстро реализуем простейшую схему интегрирования.
def integrate(fun, a, b, N=100): res = 0 h = (b - a) / N for i in np.linspace(a, b, N): res += fun(a + i) * h return res
Теперь так же быстро накидаем всё остальное.
def V(v, n, s, w, p): fun = lambda u: kernel(u, v, s, w, p).mul_(u - n) return integrate(fun, n, n+1) def Q(v, n, s, w, p): fun = lambda u: kernel(u, v, s, w, p) return integrate(fun, n, n+1) def W(N, s, w, p): Qp = lambda v, n: Q(v, n, s, w, p) Vp = lambda v, n: V(v, n, s, w, p) W = [None] * N W[0] = torch.cat([Qp(v, 1) - Vp(v, 1) for v in range(1, N + 1)]) for j in range(2, N): W[j-1] = torch.cat([Qp(v, j) - Vp(v, j) + Vp(v, j - 1) for v in range(1, N + 1)]) W[N-1] = torch.cat([ Vp(v, N-1) for v in range(1, N + 1)]) W = torch.cat(W) return W.view(N, N).t()
Инициализируем наши веса.
s = Variable(torch.FloatTensor([1e-5, 1, 1]), requires_grad=True) w = Variable(torch.FloatTensor(2, 2).uniform_(-1e-5, 1e-5), requires_grad=True) p = Variable(torch.FloatTensor(2).uniform_(-1e-5, 1e-5), requires_grad=True)
data_x_t = torch.FloatTensor(100, 3).uniform_() data_y_t = data_x_t.mm(torch.FloatTensor([[1, 2, 3]]).t_()).view(-1)
И обучим наш прототип.
alpha = -1e-3 for i in range(1000): data_x, data_y = Variable(data_x_t), Variable(data_y_t) Wc = W(3, s, w, p) y = data_x.mm(Wc).sum(1) loss = data_y.sub(y).pow(2).mean() print(loss.data[0]) loss.backward() s.data.add_(s.grad.data.mul(alpha)) s.grad.data.zero_() w.data.add_(w.grad.data.mul(alpha)) w.grad.data.zero_() p.data.add_(p.grad.data.mul(alpha)) p.grad.data.zero_()
Запустим и увидим в терминале стройную колонну убывающих значений. Ниже показано как ядро меняется в процессе обучения.
Я даже не вспотел. Я разбирался с 3d графиками в matplotlib дольше, чем писал этот код. Да, он не идеален, но это работающий прототип, созданный за 15 минут. Считать вручную все эти производные нереально. TensorFlow… Я не представляю, как реализовать это на TensorFlow. PyTorch же наплевал на сложность и безумность вашей идеи. Если функция дифференцируема, PyTorch её дифференцирует. Если вам нужно перемножить тензоры на видеокарте, PyTorch не заставит вас определять граф, он позволит вам просто перемножить тензоры на видеокарте.
Рассмотрим код обучения поближе.
data_x, data_y = Variable(data_x_t), Variable(data_y_t)
Этот код оборачивает наши тензоры в переменные, чтобы все дальнейшие вычисления строили за собой граф. Каждый раз это необходимо делать из-за того что наш граф динамический и, следовательно, одноразовый: совершить обратный проход по нему можно лишь единожды.
loss.backward()
Этот вызов, как вам уже должно быть известно, считает обратный проход по графу.
s.data.add_(s.grad.data.mul(alpha))
Здесь мы берём тензор из посчитанного градиента (переменной), домножаем его на некоторое маленькое по модулю отрицательное число и прибавляем к тензору исходных весов на месте. Таким образом совершается один шаг градиентного спуска.
s.grad.data.zero_()
Вызов backward() не обнуляет градиент, поэтому нужно сделать это вручную.
Естественно, вам не придётся каждый раз вручную писать весь этот код: для стандартных задач в PyTorch предусмотрены все те же высокоуровневые абстракции, что и, например, в Keras.
Заключение
В данной статье мы рассмотрели основы PyTorch: тензорные вычисления, cuda вычисления и автоматическое дифференцирование. Теперь вы с лёгкостью можете взять PyTorch и начать заниматься глубоким обучением с его помощью.
Читателю может показаться, что PyTorch слишком низкоуровневый. Кажется, что для продуктивной работы над всем этим ещё предстоит написать удобную обёртку. С одной стороны это так: никто не запрещает вам написать поверх PyTorch ту обёртку, которая удобна лично вам. Вы можете написать её в модульном стиле, как в Caffe, а можете описать свой собственный декларативный DSL как в TensorFlow. PyTorch низкоуровневый настолько, насколько это только возможно и позволяет вам создавать поверх него те инструменты, с которыми будет максимально удобно работать лично вам.
С другой стороны, существуют официальные реализации таких высокоуровневых обёрток, заключённые в модулях torch.nn и torch.optim . Модуль torch.utils.data позволяет крайне удобно асинхронно работать с данными. Отдельный пакет torchvision содержит всё необходимое для быстрого старта работы с изображениями. Обо всём этом поговорим в следующей статье.
PyTorch великолепен. Он предоставляет вам различные уровни абстракции и позволяет с лёгкостью реализовывать свои. Он не загоняет вас в рамки какой-то одной парадигмы. Он быстрый, эффективный и распределённый. Попробуйте его и он вам непременно понравится.
Спасибо за прочтение! Ставьте лайки, подписывайтесь на профиль, оставляйте комментарии, не тратьте время на борьбу с инструментами. Дополнения приветствуются.
PyTorch – Введение
PyTorch определяется как библиотека машинного обучения с открытым исходным кодом для Python. Он используется для приложений, таких как обработка естественного языка. Первоначально он был разработан исследовательской группой по искусственному интеллекту Facebook и программным обеспечением Uber Pyro для вероятностного программирования, которое основано на нем.
Первоначально PyTorch был разработан Хью Перкинсом как оболочка Python для LusJIT, основанная на платформе Torch. Есть два варианта PyTorch.
PyTorch перепроектирует и внедряет Torch в Python, совместно используя те же основные библиотеки C для внутреннего кода. Разработчики PyTorch настроили этот внутренний код для эффективной работы с Python. Они также сохранили аппаратное ускорение на основе графического процессора, а также функции расширяемости, которые сделали Torch на базе Lua.
Характеристики
Основные функции PyTorch упомянуты ниже –
Простой интерфейс – PyTorch предлагает простой в использовании API; следовательно, он считается очень простым в работе и работает на Python. Выполнение кода в этой среде довольно просто.
Использование Python – эта библиотека считается Pythonic, которая плавно интегрируется со стеком данных Python. Таким образом, он может использовать все сервисы и функциональные возможности, предлагаемые средой Python.
Вычислительные графы – PyTorch предоставляет отличную платформу, которая предлагает динамические вычислительные графы. Таким образом, пользователь может изменить их во время выполнения. Это очень полезно, когда разработчик не знает, сколько памяти требуется для создания модели нейронной сети.
PyTorch известен тем, что имеет три уровня абстракции, как указано ниже –
- Тензор – императивный n-мерный массив, работающий на GPU.
- Переменная – узел в вычислительном графе. Это хранит данные и градиент.
- Модуль – Уровень нейронной сети, в котором будут храниться данные о состоянии или изучаемых весах.
Тензор – императивный n-мерный массив, работающий на GPU.
Переменная – узел в вычислительном графе. Это хранит данные и градиент.
Модуль – Уровень нейронной сети, в котором будут храниться данные о состоянии или изучаемых весах.
Преимущества PyTorch
Ниже приведены преимущества PyTorch –
- Код легко отлаживать и понимать.
- Он включает в себя много слоев, как факел.
- Включает в себя множество функций потери.
- Это можно рассматривать как расширение NumPy для графических процессоров.
- Это позволяет строить сети, структура которых зависит от самих вычислений.
Код легко отлаживать и понимать.
Он включает в себя много слоев, как факел.
Включает в себя множество функций потери.
Это можно рассматривать как расширение NumPy для графических процессоров.
Это позволяет строить сети, структура которых зависит от самих вычислений.
TensorFlow vs. PyTorch
Мы рассмотрим основные различия между TensorFlow и PyTorch ниже –
PyTorch тесно связан с основанным на lua фреймворком Torch, который активно используется в Facebook.
TensorFlow разработан Google Brain и активно используется в Google.
PyTorch является относительно новым по сравнению с другими конкурентными технологиями.
TensorFlow не является новым и рассматривается многими исследователями и профессионалами в качестве инструмента, необходимого для работы.
PyTorch включает в себя все в императивной и динамичной манере.
TensorFlow включает в себя статические и динамические графики в виде комбинации.
Граф вычислений в PyTorch определяется во время выполнения.
TensorFlow не включает опцию времени выполнения.
PyTorch включает в себя возможность развертывания для мобильных и встроенных платформ.
TensorFlow лучше работает для встроенных фреймворков.
PyTorch тесно связан с основанным на lua фреймворком Torch, который активно используется в Facebook.
TensorFlow разработан Google Brain и активно используется в Google.
PyTorch является относительно новым по сравнению с другими конкурентными технологиями.
TensorFlow не является новым и рассматривается многими исследователями и профессионалами в качестве инструмента, необходимого для работы.
PyTorch включает в себя все в императивной и динамичной манере.
TensorFlow включает в себя статические и динамические графики в виде комбинации.
Граф вычислений в PyTorch определяется во время выполнения.
TensorFlow не включает опцию времени выполнения.
PyTorch включает в себя возможность развертывания для мобильных и встроенных платформ.
TensorFlow лучше работает для встроенных фреймворков.
PyTorch – Установка
PyTorch является популярной средой глубокого обучения. В этом уроке мы рассматриваем «Windows 10» как нашу операционную систему. Шаги для успешной настройки среды следующие:
Шаг 1
Следующая ссылка содержит список пакетов, который включает в себя подходящие пакеты для PyTorch.
Все, что вам нужно сделать, это загрузить соответствующие пакеты и установить его, как показано на следующих скриншотах –
Шаг 2
Он включает проверку установки платформы PyTorch с использованием Anaconda Framework.
Следующая команда используется для проверки того же –
conda list
«Conda list» показывает список установленных фреймворков.
Выделенная часть показывает, что PyTorch был успешно установлен в нашей системе.
Математические строительные блоки нейронных сетей
Математика жизненно важна для любого алгоритма машинного обучения и включает в себя различные основные понятия математики, чтобы получить правильный алгоритм, разработанный особым образом.
Важность тем математики для машинного обучения и науки о данных упомянута ниже –
Теперь давайте сосредоточимся на основных математических концепциях машинного обучения, которые важны с точки зрения обработки естественного языка –
векторы
Вектор считается массивом чисел, который является либо непрерывным, либо дискретным, а пространство, состоящее из векторов, называется векторным пространством. Пространственные измерения векторов могут быть либо конечными, либо бесконечными, но было замечено, что проблемы машинного обучения и науки о данных имеют дело с векторами фиксированной длины.
Векторное представление отображается как указано ниже –
temp = torch.FloatTensor([23,24,24.5,26,27.2,23.0]) temp.size() Output - torch.Size([6])
В машинном обучении мы имеем дело с многомерными данными. Таким образом, векторы становятся очень важными и рассматриваются как входные функции для любой задачи прогнозирования.
Скаляры
Скаляры имеют нулевые размеры, содержащие только одно значение. Когда дело доходит до PyTorch, он не включает специальный тензор с нулевыми размерами; следовательно, заявление будет сделано следующим образом –
x = torch.rand(10) x.size() Output - torch.Size([10])
Матрицы
Большая часть структурированных данных обычно представляется в виде таблиц или конкретной матрицы. Мы будем использовать набор данных под названием Boston House Prices, который легко доступен в библиотеке машинного обучения scthonit-learn Python.
boston_tensor = torch.from_numpy(boston.data) boston_tensor.size() Output: torch.Size([506, 13]) boston_tensor[:2] Output: Columns 0 to 7 0.0063 18.0000 2.3100 0.0000 0.5380 6.5750 65.2000 4.0900 0.0273 0.0000 7.0700 0.0000 0.4690 6.4210 78.9000 4.9671 Columns 8 to 12 1.0000 296.0000 15.3000 396.9000 4.9800 2.0000 242.0000 17.8000 396.9000 9.1400
PyTorch – Основы нейронной сети
Основной принцип нейронной сети включает в себя набор базовых элементов, то есть искусственного нейрона или персептрона. Он включает в себя несколько основных входов, таких как x1, x2… .. xn, которые создают двоичный выход, если сумма больше, чем потенциал активации.
Схематическое изображение образца нейрона упоминается ниже –
Сгенерированный выход можно рассматривать как взвешенную сумму с потенциалом активации или смещением.
В ы х о д = s u m j w j x j + B i a s
Типичная архитектура нейронной сети описана ниже –
Слои между входом и выходом называются скрытыми слоями, а плотность и тип соединений между слоями – это конфигурация. Например, полностью подключенная конфигурация имеет все нейроны слоя L, связанные с нейронами L + 1. Для более выраженной локализации мы можем подключить только локальную окрестность, скажем, девять нейронов, к следующему слою. Рисунок 1-9 иллюстрирует два скрытых слоя с плотными связями.
Различные типы нейронных сетей следующие:
Нейронные сети с прямой связью
Нейронные сети с прямой связью включают в себя базовые единицы семейства нейронных сетей. Движение данных в нейронной сети этого типа происходит от входного слоя к выходному слою через существующие скрытые слои. Выход одного уровня служит входным слоем с ограничениями на любой вид петель в сетевой архитектуре.
Рекуррентные нейронные сети
Рекуррентные нейронные сети – это когда последовательность данных изменяется в течение периода. В RNN этот же слой применяется для приема входных параметров и отображения выходных параметров в указанной нейронной сети.
Нейронные сети могут быть построены с использованием пакета torch.nn.
Это простая сеть прямой связи. Он принимает входные данные, пропускает их через несколько слоев один за другим, а затем, наконец, выдает выходные данные.
С помощью PyTorch мы можем использовать следующие шаги для типичной процедуры обучения для нейронной сети –
- Определите нейронную сеть, которая имеет некоторые обучаемые параметры (или веса).
- Перебрать набор входных данных.
- Процесс ввода через сеть.
- Вычислите потери (насколько далек результат от правильности).
- Распространять градиенты обратно в параметры сети.
- Обновите вес сети, как правило, используя простое обновление, как указано ниже
rule: weight = weight -learning_rate * gradient
Универсальный рабочий процесс машинного обучения
Искусственный интеллект в настоящее время имеет тенденцию к росту. Машинное обучение и глубокое обучение составляют искусственный интеллект. Упомянутая ниже диаграмма Венна объясняет взаимосвязь машинного обучения и глубокого обучения.
Машинное обучение
Машинное обучение – это искусство науки, которое позволяет компьютерам действовать в соответствии с разработанными и запрограммированными алгоритмами. Многие исследователи считают, что машинное обучение – лучший способ достичь ИИ на уровне человека. Он включает в себя различные типы шаблонов, таких как –
- Образец обучения под наблюдением
- Необучаемая модель обучения
Глубокое обучение
Глубокое обучение – это подполе машинного обучения, где соответствующие алгоритмы вдохновлены структурой и функциями мозга, называемыми искусственными нейронными сетями.
Глубокое обучение приобрело большое значение благодаря контролируемому обучению или обучению на основе маркированных данных и алгоритмов. Каждый алгоритм глубокого обучения проходит через один и тот же процесс. Он включает иерархию нелинейного преобразования входных данных и использует их для создания статистической модели в качестве выходных данных.
Процесс машинного обучения определяется с помощью следующих шагов –
- Идентифицирует соответствующие наборы данных и подготавливает их для анализа.
- Выбирает тип используемого алгоритма.
- Создает аналитическую модель на основе используемого алгоритма.
- Обучает модель на тестовых наборах данных, пересматривая ее по мере необходимости.
- Запускает модель для генерации результатов тестов.
PyTorch – машинное обучение против глубокого обучения
В этой главе мы обсудим основное различие между концепциями машинного и глубокого обучения.
Количество данных
Машинное обучение работает с различными объемами данных и в основном используется для небольших объемов данных. Глубокое обучение, с другой стороны, работает эффективно, если объем данных быстро увеличивается. Следующая диаграмма изображает работу машинного обучения и глубокого обучения относительно количества данных –
Аппаратные зависимости
Алгоритмы глубокого обучения предназначены для сильной зависимости от высокопроизводительных машин, в отличие от традиционных алгоритмов машинного обучения. Алгоритмы глубокого обучения выполняют большое количество операций умножения матриц, что требует огромной аппаратной поддержки.
Характеристика
Проектирование функций – это процесс внедрения знаний в предметные области в определенные функции, чтобы уменьшить сложность данных и создать шаблоны, которые будут видны алгоритмам обучения.
Например, традиционные шаблоны машинного обучения фокусируются на пикселях и других атрибутах, необходимых для процесса разработки функций. Алгоритмы глубокого обучения фокусируются на высокоуровневых особенностях данных. Это уменьшает задачу разработки экстрактора новых функций для каждой новой проблемы.
PyTorch – внедрение первой нейронной сети
PyTorch включает в себя особенность создания и реализации нейронных сетей. В этой главе мы создадим простую нейронную сеть с одним скрытым слоем, развивающую один выходной блок.
Мы будем использовать следующие шаги для реализации первой нейронной сети с использованием PyTorch –
Шаг 1
Сначала нам нужно импортировать библиотеку PyTorch с помощью следующей команды:
import torch import torch.nn as nn
Шаг 2
Определите все слои и размер пакета, чтобы начать выполнение нейронной сети, как показано ниже –
# Defining input size, hidden layer size, output size and batch size respectively n_in, n_h, n_out, batch_size = 10, 5, 1, 10
Шаг 3
Поскольку нейронная сеть включает в себя комбинацию входных данных для получения соответствующих выходных данных, мы будем следовать той же процедуре, что и приведенная ниже:
# Create dummy input and target tensors (data) x = torch.randn(batch_size, n_in) y = torch.tensor([[1.0], [0.0], [0.0], [1.0], [1.0], [1.0], [0.0], [0.0], [1.0], [1.0]])
Шаг 4
Создайте последовательную модель с помощью встроенных функций. Используя приведенные ниже строки кода, создайте последовательную модель –
# Create a model model = nn.Sequential(nn.Linear(n_in, n_h), nn.ReLU(), nn.Linear(n_h, n_out), nn.Sigmoid())
Шаг 5
Построить функцию потерь с помощью оптимизатора Gradient Descent, как показано ниже –
Construct the loss function criterion = torch.nn.MSELoss() # Construct the optimizer (Stochastic Gradient Descent in this case) optimizer = torch.optim.SGD(model.parameters(), lr = 0.01)
Шаг 6
Реализовать модель градиентного спуска с помощью итерационного цикла с заданными строками кода –
# Gradient Descent for epoch in range(50): # Forward pass: Compute predicted y by passing x to the model y_pred = model(x) # Compute and print loss loss = criterion(y_pred, y) print('epoch: ', epoch,' loss: ', loss.item()) # Zero gradients, perform a backward pass, and update the weights. optimizer.zero_grad() # perform a backward pass (backpropagation) loss.backward() # Update the parameters optimizer.step()
Шаг 7
Сгенерированный вывод выглядит следующим образом:
epoch: 0 loss: 0.2545787990093231 epoch: 1 loss: 0.2545052170753479 epoch: 2 loss: 0.254431813955307 epoch: 3 loss: 0.25435858964920044 epoch: 4 loss: 0.2542854845523834 epoch: 5 loss: 0.25421255826950073 epoch: 6 loss: 0.25413978099823 epoch: 7 loss: 0.25406715273857117 epoch: 8 loss: 0.2539947032928467 epoch: 9 loss: 0.25392240285873413 epoch: 10 loss: 0.25385022163391113 epoch: 11 loss: 0.25377824902534485 epoch: 12 loss: 0.2537063956260681 epoch: 13 loss: 0.2536346912384033 epoch: 14 loss: 0.25356316566467285 epoch: 15 loss: 0.25349172949790955 epoch: 16 loss: 0.25342053174972534 epoch: 17 loss: 0.2533493936061859 epoch: 18 loss: 0.2532784342765808 epoch: 19 loss: 0.25320762395858765 epoch: 20 loss: 0.2531369626522064 epoch: 21 loss: 0.25306645035743713 epoch: 22 loss: 0.252996027469635 epoch: 23 loss: 0.2529257833957672 epoch: 24 loss: 0.25285571813583374 epoch: 25 loss: 0.25278574228286743 epoch: 26 loss: 0.25271597504615784 epoch: 27 loss: 0.25264623761177063 epoch: 28 loss: 0.25257670879364014 epoch: 29 loss: 0.2525072991847992 epoch: 30 loss: 0.2524380087852478 epoch: 31 loss: 0.2523689270019531 epoch: 32 loss: 0.25229987502098083 epoch: 33 loss: 0.25223103165626526 epoch: 34 loss: 0.25216227769851685 epoch: 35 loss: 0.252093642950058 epoch: 36 loss: 0.25202515721321106 epoch: 37 loss: 0.2519568204879761 epoch: 38 loss: 0.251888632774353 epoch: 39 loss: 0.25182053446769714 epoch: 40 loss: 0.2517525553703308 epoch: 41 loss: 0.2516847252845764 epoch: 42 loss: 0.2516169846057892 epoch: 43 loss: 0.2515493929386139 epoch: 44 loss: 0.25148195028305054 epoch: 45 loss: 0.25141456723213196 epoch: 46 loss: 0.2513473629951477 epoch: 47 loss: 0.2512802183628082 epoch: 48 loss: 0.2512132525444031 epoch: 49 loss: 0.2511464059352875
PyTorch – Нейронные сети к функциональным блокам
Обучение алгоритму глубокого обучения включает в себя следующие шаги –
- Построение конвейера данных
- Построение сетевой архитектуры
- Оценка архитектуры с использованием функции потерь
- Оптимизация весов сетевой архитектуры с использованием алгоритма оптимизации
Обучение конкретному алгоритму глубокого обучения является точным требованием преобразования нейронной сети в функциональные блоки, как показано ниже –
Что касается вышеприведенной диаграммы, любой алгоритм глубокого обучения включает получение входных данных, построение соответствующей архитектуры, которая включает в себя множество слоев, встроенных в них.
Если вы наблюдаете вышеупомянутую диаграмму, точность оценивается с использованием функции потерь в отношении оптимизации весов нейронной сети.
PyTorch – Терминологии
В этой главе мы обсудим некоторые наиболее часто используемые термины в PyTorch.
PyTorch NumPy
Тензор PyTorch идентичен массиву NumPy. Тензор – это n-мерный массив, и по отношению к PyTorch он предоставляет множество функций для работы с этими тензорами.
Тензоры PyTorch обычно используют графические процессоры для ускорения своих числовых вычислений. Эти тензоры, созданные в PyTorch, можно использовать для подгонки двухслойной сети к случайным данным. Пользователь может вручную реализовать прямой и обратный проходы по сети.
Переменные и автоград
При использовании autograd прямая передача вашей сети будет определять вычислительный граф – узлы в графе будут Tensors, а ребра будут функциями, которые производят выходные Tensors из входных Tensors.
Тензор PyTorch может быть создан как переменные объекты, где переменная представляет узел в вычислительном графе.
Динамические Графики
Статические графики хороши тем, что пользователь может оптимизировать график заранее. Если программисты повторно используют один и тот же график снова и снова, то эта потенциально дорогостоящая предварительная оптимизация может быть сохранена, так как один и тот же график повторяется снова и снова.
Основное различие между ними заключается в том, что вычислительные графы Tensor Flow являются статическими, а PyTorch использует динамические вычислительные графы.
Оптим Пакет
Пакет optim в PyTorch абстрагирует идею алгоритма оптимизации, который реализован многими способами, и предоставляет иллюстрации часто используемых алгоритмов оптимизации. Это можно вызвать в операторе импорта.
многопроцессорная обработка
Многопроцессорная обработка поддерживает одни и те же операции, поэтому все тензоры работают на нескольких процессорах. Их очередь будет перемещена в общую память и отправит дескриптор другому процессу.
PyTorch – загрузка данных
PyTorch включает в себя пакет torchvision, который используется для загрузки и подготовки набора данных. Он включает в себя две основные функции, а именно Dataset и DataLoader, которые помогают в преобразовании и загрузке набора данных.
Dataset
Набор данных используется для чтения и преобразования точки данных из данного набора данных. Основной синтаксис для реализации упомянут ниже:
trainset = torchvision.datasets.CIFAR10(root = './data', train = True, download = True, transform = transform)
DataLoader используется для перемешивания и пакетной обработки данных. Он может использоваться для загрузки данных параллельно с многопроцессорными рабочими.
trainloader = torch.utils.data.DataLoader(trainset, batch_size = 4, shuffle = True, num_workers = 2)
Пример: загрузка файла CSV
Мы используем пакет Panda Python для загрузки файла CSV. Исходный файл имеет следующий формат: (имя изображения, 68 ориентиров – каждый ориентир имеет координаты топора и y).
landmarks_frame = pd.read_csv('faces/face_landmarks.csv') n = 65 img_name = landmarks_frame.iloc[n, 0] landmarks = landmarks_frame.iloc[n, 1:].as_matrix() landmarks = landmarks.astype('float').reshape(-1, 2)
PyTorch – линейная регрессия
В этой главе мы сосредоточимся на базовом примере реализации линейной регрессии с использованием TensorFlow. Логистическая регрессия или линейная регрессия – это контролируемый подход машинного обучения для классификации категорий дискретных порядков. Наша цель в этой главе – создать модель, с помощью которой пользователь может предсказать взаимосвязь между переменными предиктора и одной или несколькими независимыми переменными.
Соотношение между этими двумя переменными считается линейным, т. Е. Если y является зависимой переменной, а x рассматривается как независимая переменная, то отношение линейной регрессии двух переменных будет выглядеть как уравнение, которое упоминается ниже:
Y = Ax+b
Далее мы разработаем алгоритм линейной регрессии, который позволит нам понять две важные концепции, приведенные ниже:
- Функция стоимости
- Алгоритмы градиентного спуска
Схематическое представление линейной регрессии упоминается ниже
Интерпретация результата
- Значение а является наклоном.
- Значение b – это y – пересечение .
- r – коэффициент корреляции .
- r 2 – коэффициент корреляции .
Значение а является наклоном.
Значение b – это y – пересечение .
r – коэффициент корреляции .
r 2 – коэффициент корреляции .
Графическое представление уравнения линейной регрессии упоминается ниже –
Следующие шаги используются для реализации линейной регрессии с использованием PyTorch –
Шаг 1
Импортируйте необходимые пакеты для создания линейной регрессии в PyTorch, используя следующий код –
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation import seaborn as sns import pandas as pd %matplotlib inline sns.set_style(style = 'whitegrid') plt.rcParams["patch.force_edgecolor"] = True
Шаг 2
Создайте единый тренировочный набор с доступным набором данных, как показано ниже –
m = 2 # slope c = 3 # interceptm = 2 # slope c = 3 # intercept x = np.random.rand(256) noise = np.random.randn(256) / 4 y = x * m + c + noise df = pd.DataFrame() df['x'] = x df['y'] = y sns.lmplot(x ='x', y ='y', data = df)
Шаг 3
Реализуйте линейную регрессию с библиотеками PyTorch, как упомянуто ниже –
import torch import torch.nn as nn from torch.autograd import Variable x_train = x.reshape(-1, 1).astype('float32') y_train = y.reshape(-1, 1).astype('float32') class LinearRegressionModel(nn.Module): def __init__(self, input_dim, output_dim): super(LinearRegressionModel, self).__init__() self.linear = nn.Linear(input_dim, output_dim) def forward(self, x): out = self.linear(x) return out input_dim = x_train.shape[1] output_dim = y_train.shape[1] input_dim, output_dim(1, 1) model = LinearRegressionModel(input_dim, output_dim) criterion = nn.MSELoss() [w, b] = model.parameters() def get_param_values(): return w.data[0][0], b.data[0] def plot_current_fit(title = ""): plt.figure(figsize = (12,4)) plt.title(title) plt.scatter(x, y, s = 8) w1 = w.data[0][0] b1 = b.data[0] x1 = np.array([0., 1.]) y1 = x1 * w1 + b1 plt.plot(x1, y1, 'r', label = 'Current Fit (, )'.format(w1, b1)) plt.xlabel('x (input)') plt.ylabel('y (target)') plt.legend() plt.show() plot_current_fit('Before training')
Сюжет генерируется следующим образом –
PyTorch – сверточная нейронная сеть
Глубокое обучение является разделом машинного обучения и считается решающим шагом, предпринятым исследователями в последние десятилетия. Примеры реализации глубокого обучения включают в себя такие приложения, как распознавание изображений и распознавание речи.
Два важных типа глубоких нейронных сетей приведены ниже –
- Сверточные нейронные сети
- Рекуррентные нейронные сети.
В этой главе мы сосредоточимся на первом типе, то есть на сверточных нейронных сетях (CNN).
Сверточные нейронные сети
Сверточные нейронные сети предназначены для обработки данных через несколько уровней массивов. Этот тип нейронных сетей используется в таких приложениях, как распознавание изображений или распознавание лиц.
Основное различие между CNN и любой другой обычной нейронной сетью состоит в том, что CNN принимает входные данные в виде двумерного массива и работает непосредственно с изображениями, а не фокусируется на извлечении признаков, на котором сосредоточены другие нейронные сети.
Доминирующий подход CNN включает решение проблем распознавания. Ведущие компании, такие как Google и Facebook, инвестировали в исследовательские и опытно-конструкторские проекты проектов по распознаванию, чтобы выполнять действия быстрее.
Каждая сверточная нейронная сеть включает в себя три основные идеи –
- Местные соответствующие поля
- свертка
- объединение
Давайте разберемся с каждой из этих терминов в деталях.
Местные соответствующие поля
CNN использует пространственные корреляции, которые существуют во входных данных. Каждый в параллельных слоях нейронных сетей соединяет несколько входных нейронов. Этот конкретный регион называется Местное поле восприятия. Он фокусируется только на скрытых нейронах. Скрытый нейрон будет обрабатывать входные данные внутри упомянутого поля, не осознавая изменений за пределами определенной границы.
Представление диаграммы генерации локальных соответствующих полей упоминается ниже –
свертка
На приведенном выше рисунке мы видим, что каждое соединение учитывает вес скрытого нейрона, связанного с перемещением из одного слоя в другой. Здесь отдельные нейроны время от времени выполняют сдвиг. Этот процесс называется «свертка».
Отображение соединений от входного слоя к карте скрытых объектов определяется как «общие веса», а включенное смещение называется «общим смещением».
объединение
Сверточные нейронные сети используют слои пула, которые располагаются сразу после объявления CNN. Он принимает входные данные пользователя в виде карты объектов, которая выходит из сверточных сетей и подготавливает сжатую карту объектов. Объединение слоев помогает в создании слоев с нейронами предыдущих слоев.
Реализация PyTorch
Следующие шаги используются для создания сверточной нейронной сети с использованием PyTorch.
Шаг 1
Импортируйте необходимые пакеты для создания простой нейронной сети.
from torch.autograd import Variable import torch.nn.functional as F
Шаг 2
Создайте класс с пакетным представлением сверточной нейронной сети. Наша форма пакета для ввода x имеет размерность (3, 32, 32).
class SimpleCNN(torch.nn.Module): def __init__(self): super(SimpleCNN, self).__init__() #Input channels = 3, output channels = 18 self.conv1 = torch.nn.Conv2d(3, 18, kernel_size = 3, stride = 1, padding = 1) self.pool = torch.nn.MaxPool2d(kernel_size = 2, stride = 2, padding = 0) #4608 input features, 64 output features (see sizing flow below) self.fc1 = torch.nn.Linear(18 * 16 * 16, 64) #64 input features, 10 output features for our 10 defined classes self.fc2 = torch.nn.Linear(64, 10)
Шаг 3
Вычислить активацию первого изменения размера свертки от (3, 32, 32) до (18, 32, 32).
Размер измерения изменяется от (18, 32, 32) до (18, 16, 16). Изменение размера данных входного слоя нейронной сети, из-за которого размер изменяется с (18, 16, 16) на (1, 4608).
Напомним, что -1 выводит это измерение из другого данного измерения.
def forward(self, x): x = F.relu(self.conv1(x)) x = self.pool(x) x = x.view(-1, 18 * 16 *16) x = F.relu(self.fc1(x)) #Computes the second fully connected layer (activation applied later) #Size changes from (1, 64) to (1, 10) x = self.fc2(x) return(x)
PyTorch – рекуррентная нейронная сеть
Рекуррентные нейронные сети – это один тип алгоритма, ориентированного на глубокое обучение, который следует последовательному подходу. В нейронных сетях мы всегда предполагаем, что каждый вход и выход не зависит от всех других слоев. Нейронные сети такого типа называются рекуррентными, поскольку они выполняют математические вычисления последовательно, выполняя одну задачу за другой.
Диаграмма ниже определяет полный подход и работу рекуррентных нейронных сетей –
На приведенном выше рисунке c1, c2, c3 и x1 рассматриваются как входные данные, которые включают в себя некоторые скрытые входные значения, а именно h1, h2 и h3, обеспечивающие соответствующий выходной сигнал o1. Теперь мы сосредоточимся на реализации PyTorch для создания синусоиды с помощью рекуррентных нейронных сетей.
Во время обучения мы будем следовать обучающему подходу к нашей модели с одной точкой данных за раз. Входная последовательность x состоит из 20 точек данных, и целевая последовательность считается такой же, как входная последовательность.
Шаг 1
Импортируйте необходимые пакеты для реализации рекуррентных нейронных сетей, используя следующий код –
import torch from torch.autograd import Variable import numpy as np import pylab as pl import torch.nn.init as init
Шаг 2
Мы установим гиперпараметры модели с размером входного слоя 7. Для создания целевой последовательности будет 6 контекстных нейронов и 1 входной нейрон.
dtype = torch.FloatTensor input_size, hidden_size, output_size = 7, 6, 1 epochs = 300 seq_length = 20 lr = 0.1 data_time_steps = np.linspace(2, 10, seq_length + 1) data = np.sin(data_time_steps) data.resize((seq_length + 1, 1)) x = Variable(torch.Tensor(data[:-1]).type(dtype), requires_grad=False) y = Variable(torch.Tensor(data[1:]).type(dtype), requires_grad=False)
Мы сгенерируем обучающие данные, где x – последовательность входных данных, а y – требуемая последовательность.
Шаг 3
Веса инициализируются в рекуррентной нейронной сети с использованием нормального распределения с нулевым средним. W1 будет представлять принятие входных переменных, а w2 будет представлять вывод, который генерируется, как показано ниже –
w1 = torch.FloatTensor(input_size, hidden_size).type(dtype) init.normal(w1, 0.0, 0.4) w1 = Variable(w1, requires_grad = True) w2 = torch.FloatTensor(hidden_size, output_size).type(dtype) init.normal(w2, 0.0, 0.3) w2 = Variable(w2, requires_grad = True)
Шаг 4
Теперь важно создать функцию для прямой связи, которая однозначно определяет нейронную сеть.
def forward(input, context_state, w1, w2): xh = torch.cat((input, context_state), 1) context_state = torch.tanh(xh.mm(w1)) out = context_state.mm(w2) return (out, context_state)
Шаг 5
Следующим шагом является запуск процедуры обучения реализации синусоидальной волны в текущей нейронной сети. Внешний цикл повторяется по каждому циклу, а внутренний цикл повторяется по элементу последовательности. Здесь мы также вычислим среднеквадратичную ошибку (MSE), которая помогает в прогнозировании непрерывных переменных.
for i in range(epochs): total_loss = 0 context_state = Variable(torch.zeros((1, hidden_size)).type(dtype), requires_grad = True) for j in range(x.size(0)): input = x[jj+1)] target = y[jj+1)] (pred, context_state) = forward(input, context_state, w1, w2) loss = (pred - target).pow(2).sum()/2 total_loss += loss loss.backward() w1.data -= lr * w1.grad.data w2.data -= lr * w2.grad.data w1.grad.data.zero_() w2.grad.data.zero_() context_state = Variable(context_state.data) if i % 10 == 0: print("Epoch: <> loss <>".format(i, total_loss.data[0])) context_state = Variable(torch.zeros((1, hidden_size)).type(dtype), requires_grad = False) predictions = [] for i in range(x.size(0)): input = x[i:i+1] (pred, context_state) = forward(input, context_state, w1, w2) context_state = context_state predictions.append(pred.data.numpy().ravel()[0])
Шаг 6
Теперь пришло время построить синусоидальную волну так, как она нужна.
pl.scatter(data_time_steps[:-1], x.data.numpy(), s = 90, label = "Actual") pl.scatter(data_time_steps[1:], predictions, label = "Predicted") pl.legend() pl.show()
Выход
Выход для вышеуказанного процесса выглядит следующим образом:
PyTorch – наборы данных
В этой главе мы сосредоточимся больше на torchvision.datasets и его различных типах. PyTorch включает следующие загрузчики наборов данных –
- MNIST
- COCO (субтитры и обнаружение)
Набор данных включает в себя большинство из двух типов функций, приведенных ниже –
- Transform – функция, которая берет изображение и возвращает модифицированную версию стандартного материала. Они могут быть составлены вместе с преобразованиями.
- Target_transform – функция, которая берет цель и трансформирует ее. Например, принимает строку заголовка и возвращает тензор мировых индексов.
Transform – функция, которая берет изображение и возвращает модифицированную версию стандартного материала. Они могут быть составлены вместе с преобразованиями.
Target_transform – функция, которая берет цель и трансформирует ее. Например, принимает строку заголовка и возвращает тензор мировых индексов.
MNIST
Ниже приведен пример кода для набора данных MNIST:
dset.MNIST(root, train = TRUE, transform = NONE, target_transform = None, download = FALSE)
- root – корневой каталог набора данных, в котором существуют обработанные данные.
- train – True = обучающий набор, False = тестовый набор
- download – True = загружает набор данных из Интернета и помещает его в корневой каталог.
root – корневой каталог набора данных, в котором существуют обработанные данные.
train – True = обучающий набор, False = тестовый набор
download – True = загружает набор данных из Интернета и помещает его в корневой каталог.
COCO
Это требует установки COCO API. Следующий пример используется для демонстрации реализации набора данных COCO с использованием PyTorch –
import torchvision.dataset as dset import torchvision.transforms as transforms cap = dset.CocoCaptions(root = ‘ dir where images are’, annFile = ’json annotation file’, transform = transforms.ToTensor()) print(‘Number of samples: ‘, len(cap)) print(target)
Достигнутый результат следующий:
Number of samples: 82783 Image Size: (3L, 427L, 640L)
PyTorch – Введение в Конвенты
Convents – это все о создании модели CNN с нуля. Архитектура сети будет содержать комбинацию следующих шагов –
- Conv2d
- MaxPool2d
- Выпрямленная линейная единица
- Посмотреть
- Линейный слой
Обучение модели
Обучение модели – тот же процесс, что и при классификации изображений. Следующий фрагмент кода завершает процедуру обучающей модели на предоставленном наборе данных –
def fit(epoch,model,data_loader,phase = 'training',volatile = False): if phase == 'training': model.train() if phase == 'training': model.train() if phase == 'validation': model.eval() volatile=True running_loss = 0.0 running_correct = 0 for batch_idx , (data,target) in enumerate(data_loader): if is_cuda: data,target = data.cuda(),target.cuda() data , target = Variable(data,volatile),Variable(target) if phase == 'training': optimizer.zero_grad() output = model(data) loss = F.nll_loss(output,target) running_loss + = F.nll_loss(output,target,size_average = False).data[0] preds = output.data.max(dim = 1,keepdim = True)[1] running_correct + = preds.eq(target.data.view_as(preds)).cpu().sum() if phase == 'training': loss.backward() optimizer.step() loss = running_loss/len(data_loader.dataset) accuracy = 100. * running_correct/len(data_loader.dataset) print(f' loss is .> and accuracy is />)
Метод включает в себя различную логику для обучения и проверки. Есть две основные причины использования разных режимов:
- В режиме поезда выпадение удаляет процент значений, что не должно происходить на этапе проверки или тестирования.
- Для режима обучения мы вычисляем градиенты и изменяем значение параметров модели, но обратное распространение не требуется на этапах тестирования или проверки.
В режиме поезда выпадение удаляет процент значений, что не должно происходить на этапе проверки или тестирования.
Для режима обучения мы вычисляем градиенты и изменяем значение параметров модели, но обратное распространение не требуется на этапах тестирования или проверки.
PyTorch – обучение монастыря с нуля
В этой главе мы сосредоточимся на создании монастыря с нуля. Это подразумевает создание соответствующего монастыря или образца нейронной сети с факелом.
Шаг 1
Создайте необходимый класс с соответствующими параметрами. Параметры включают веса со случайным значением.
class Neural_Network(nn.Module): def __init__(self, ): super(Neural_Network, self).__init__() self.inputSize = 2 self.outputSize = 1 self.hiddenSize = 3 # weights self.W1 = torch.randn(self.inputSize, self.hiddenSize) # 3 X 2 tensor self.W2 = torch.randn(self.hiddenSize, self.outputSize) # 3 X 1 tensor
Шаг 2
Создайте шаблон функции прямой связи с сигмовидными функциями.
def forward(self, X): self.z = torch.matmul(X, self.W1) # 3 X 3 ".dot" does not broadcast in PyTorch self.z2 = self.sigmoid(self.z) # activation function self.z3 = torch.matmul(self.z2, self.W2) o = self.sigmoid(self.z3) # final activation function return o def sigmoid(self, s): return 1 / (1 + torch.exp(-s)) def sigmoidPrime(self, s): # derivative of sigmoid return s * (1 - s) def backward(self, X, y, o): self.o_error = y - o # error in output self.o_delta = self.o_error * self.sigmoidPrime(o) # derivative of sig to error self.z2_error = torch.matmul(self.o_delta, torch.t(self.W2)) self.z2_delta = self.z2_error * self.sigmoidPrime(self.z2) self.W1 + = torch.matmul(torch.t(X), self.z2_delta) self.W2 + = torch.matmul(torch.t(self.z2), self.o_delta)
Шаг 3
Создайте модель обучения и прогнозирования, как указано ниже –
def train(self, X, y): # forward + backward pass for training o = self.forward(X) self.backward(X, y, o) def saveWeights(self, model): # Implement PyTorch internal storage functions torch.save(model, "NN") # you can reload model with all the weights and so forth with: # torch.load("NN") def predict(self): print ("Predicted data based on trained weights: ") print ("Input (scaled): \n" + str(xPredicted)) print ("Output: \n" + str(self.forward(xPredicted)))
PyTorch – функция извлечения в конвентах
Сверточные нейронные сети включают в себя основной признак, извлечение . Следующие шаги используются для реализации функции извлечения сверточной нейронной сети.
Шаг 1
Импортируйте соответствующие модели, чтобы создать модель извлечения объектов с помощью «PyTorch».
import torch import torch.nn as nn from torchvision import models
Шаг 2
Создайте класс экстрактора объектов, который можно вызывать по мере необходимости.
class Feature_extractor(nn.module): def forward(self, input): self.feature = input.clone() return input new_net = nn.Sequential().cuda() # the new network target_layers = [conv_1, conv_2, conv_4] # layers you want to extract` i = 1 for layer in list(cnn): if isinstance(layer,nn.Conv2d): name = "conv_"+str(i) art_net.add_module(name,layer) if name in target_layers: new_net.add_module("extractor_"+str(i),Feature_extractor()) i+=1 if isinstance(layer,nn.ReLU): name = "relu_"+str(i) new_net.add_module(name,layer) if isinstance(layer,nn.MaxPool2d): name = "pool_"+str(i) new_net.add_module(name,layer) new_net.forward(your_image) print (new_net.extractor_3.feature)
PyTorch – Визуализация конвенций
В этой главе мы сосредоточимся на модели визуализации данных с помощью конвенций. Следующие шаги необходимы, чтобы получить идеальную картину визуализации с помощью обычной нейронной сети.
Шаг 1
Импортируйте необходимые модули, что важно для визуализации обычных нейронных сетей.
import os import numpy as np import pandas as pd from scipy.misc import imread from sklearn.metrics import accuracy_score import keras from keras.models import Sequential, Model from keras.layers import Dense, Dropout, Flatten, Activation, Input from keras.layers import Conv2D, MaxPooling2D import torch
Шаг 2
Чтобы остановить потенциальную случайность с данными обучения и тестирования, вызовите соответствующий набор данных, как показано в коде ниже –
seed = 128 rng = np.random.RandomState(seed) data_dir = "../../datasets/MNIST" train = pd.read_csv('../../datasets/MNIST/train.csv') test = pd.read_csv('../../datasets/MNIST/Test_fCbTej3.csv') img_name = rng.choice(train.filename) filepath = os.path.join(data_dir, 'train', img_name) img = imread(filepath, flatten=True)
Шаг 3
Нанесите необходимые изображения для идеального определения данных обучения и тестирования с использованием приведенного ниже кода.
pylab.imshow(img, cmap ='gray') pylab.axis('off') pylab.show()
Вывод отображается как ниже –
PyTorch – обработка последовательности с помощью конвентов
В этой главе мы предлагаем альтернативный подход, который вместо этого опирается на одну двумерную сверточную нейронную сеть в обеих последовательностях. Каждый слой нашей сети перекодирует исходные токены на основе созданной до сих пор выходной последовательности. Поэтому свойства, подобные вниманию, распространены по всей сети.
Здесь мы сосредоточимся на создании последовательной сети с определенным пулированием из значений, включенных в набор данных . Этот процесс также лучше всего применять в «Модуль распознавания изображений».
Следующие шаги используются для создания модели обработки последовательности с конвенциями с использованием PyTorch –
Шаг 1
Импортируйте необходимые модули для выполнения обработки последовательности с использованием конвентов.
import keras from keras.datasets import mnist from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten from keras.layers import Conv2D, MaxPooling2D import numpy as np
Шаг 2
Выполните необходимые операции для создания шаблона в соответствующей последовательности, используя следующий код –
batch_size = 128 num_classes = 10 epochs = 12 # input image dimensions img_rows, img_cols = 28, 28 # the data, split between train and test sets (x_train, y_train), (x_test, y_test) = mnist.load_data() x_train = x_train.reshape(60000,28,28,1) x_test = x_test.reshape(10000,28,28,1) print('x_train shape:', x_train.shape) print(x_train.shape[0], 'train samples') print(x_test.shape[0], 'test samples') y_train = keras.utils.to_categorical(y_train, num_classes) y_test = keras.utils.to_categorical(y_test, num_classes)
Шаг 3
Скомпилируйте модель и поместите шаблон в упомянутую традиционную модель нейронной сети, как показано ниже –
model.compile(loss = keras.losses.categorical_crossentropy, optimizer = keras.optimizers.Adadelta(), metrics = ['accuracy']) model.fit(x_train, y_train, batch_size = batch_size, epochs = epochs, verbose = 1, validation_data = (x_test, y_test)) score = model.evaluate(x_test, y_test, verbose = 0) print('Test loss:', score[0]) print('Test accuracy:', score[1])
Сгенерированный вывод выглядит следующим образом:
PyTorch – вложение слов
В этой главе мы разберемся с известной моделью встраивания слов – word2vec. Модель Word2vec используется для встраивания слов с помощью группы связанных моделей. Модель Word2vec реализована с использованием чистого C-кода, а градиент вычисляется вручную.
Реализация модели word2vec в PyTorch объясняется в следующих шагах:
Шаг 1
Реализовать библиотеки для встраивания слов, как указано ниже –
import torch from torch.autograd import Variable import torch.nn as nn import torch.nn.functional as F
Шаг 2
Реализуйте модель встраивания слов Пропустить Грамма с классом word2vec. Включает атрибуты типа emb_size, emb_dimension, u_embedding, v_embedding .
class SkipGramModel(nn.Module): def __init__(self, emb_size, emb_dimension): super(SkipGramModel, self).__init__() self.emb_size = emb_size self.emb_dimension = emb_dimension self.u_embeddings = nn.Embedding(emb_size, emb_dimension, sparse=True) self.v_embeddings = nn.Embedding(emb_size, emb_dimension, sparse = True) self.init_emb() def init_emb(self): initrange = 0.5 / self.emb_dimension self.u_embeddings.weight.data.uniform_(-initrange, initrange) self.v_embeddings.weight.data.uniform_(-0, 0) def forward(self, pos_u, pos_v, neg_v): emb_u = self.u_embeddings(pos_u) emb_v = self.v_embeddings(pos_v) score = torch.mul(emb_u, emb_v).squeeze() score = torch.sum(score, dim = 1) score = F.logsigmoid(score) neg_emb_v = self.v_embeddings(neg_v) neg_score = torch.bmm(neg_emb_v, emb_u.unsqueeze(2)).squeeze() neg_score = F.logsigmoid(-1 * neg_score) return -1 * (torch.sum(score)+torch.sum(neg_score)) def save_embedding(self, id2word, file_name, use_cuda): if use_cuda: embedding = self.u_embeddings.weight.cpu().data.numpy() else: embedding = self.u_embeddings.weight.data.numpy() fout = open(file_name, 'w') fout.write('%d %d\n' % (len(id2word), self.emb_dimension)) for wid, w in id2word.items(): e = embedding[wid] e = ' '.join(map(lambda x: str(x), e)) fout.write('%s %s\n' % (w, e)) def test(): model = SkipGramModel(100, 100) id2word = dict() for i in range(100): id2word[i] = str(i) model.save_embedding(id2word)
Шаг 3
Реализуйте метод main, чтобы правильно отобразить модель встраивания слов.
if __name__ == '__main__': test()
PyTorch – Рекурсивные нейронные сети
Глубокие нейронные сети имеют эксклюзивную функцию, позволяющую сделать прорыв в машинном обучении пониманию процесса естественного языка. Замечено, что большинство этих моделей рассматривают язык как плоскую последовательность слов или символов и используют своего рода модель, которая упоминается как рекуррентная нейронная сеть или RNN.
Многие исследователи приходят к выводу, что язык лучше всего понимать по отношению к иерархическому древу фраз. Этот тип включен в рекурсивные нейронные сети, которые принимают во внимание определенную структуру.
PyTorch имеет особую функцию, которая помогает сделать эти сложные модели обработки естественного языка намного проще. Это полнофункциональная структура для всех видов глубокого обучения с сильной поддержкой компьютерного зрения.
Особенности рекурсивной нейронной сети
- Рекурсивная нейронная сеть создается таким образом, что включает применение одного и того же набора весов с различными графоподобными структурами.
- Узлы пройдены в топологическом порядке.
- Этот тип сети обучается в режиме обратного автоматического дифференцирования.
- Обработка естественного языка включает в себя особый случай рекурсивных нейронных сетей.
- Эта рекурсивная нейронная тензорная сеть включает в себя различные функциональные узлы композиции в дереве.
Рекурсивная нейронная сеть создается таким образом, что включает применение одного и того же набора весов с различными графоподобными структурами.
Узлы пройдены в топологическом порядке.
Этот тип сети обучается в режиме обратного автоматического дифференцирования.
Обработка естественного языка включает в себя особый случай рекурсивных нейронных сетей.
Эта рекурсивная нейронная тензорная сеть включает в себя различные функциональные узлы композиции в дереве.
Пример рекурсивной нейронной сети показан ниже –