Главная / Блог / Нейросети Часть 5

Знакомимся с PyTorch -
инструментом для создания
нейронных сетей

[ Нейросети Часть 5 ]

Знакомимся с инструментом для создания нейронных сетей. Встречайте - PyTorch

Smartiqa Article
  • Дата: 5 декабря 2020
  • Автор: Калинин Даниил
  • Демо .ipynb файл: Google Colab Notebook

Этот материал относится к циклу статей о нейронных сетях


Как нейронные сети получили свое название, из каких компонентов они состоят, как они обучаются и главное - являются ли они полноценным искусственным интеллектом, способным заменить человека?

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

В статье рассказывается о принципе работы сверточных нейронных сетей

В статье рассказывается о принципе работы GAN-моделей и методах их обучения.

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

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

Google Colab

Для начала разберемся с новыми понятиям.

  1. IPython (англ. Interactive Python) - это интерактивная оболочка языка Python. Часто используется специалистами по машинному обучению, как более удобная (за счет своей интерактивности).
  2. Jupiter notebook - это IDE для исполнения IPython кода. Запускается у вас на компьютере.
  3. Google Colab - это бесплатный облачный сервис Google, основанный на Jupiter Notebook. Отличие в том, что вычислительные мощности вам предоставляет сам Google.
  4. Ноутбук - это IPython-файл, в котором хранится код. Помимо кода в ноутбуке может быть форматированный текст (поддерживается markdown разметка) и картинки. Файл ноутбука имеет расширение .ipynb и является некоторым аналогом файла .py для обычного Python.

Итак, Google Colab – это довольно удобная среда исполнения IPython кода. Можно запускать IPython-код у себя на компьютере с помощью Jupiter Notebook, но часто бывает так, что вычислительных мощностей обычного компьютера недостаточно для нужд машинного обучения. Тут-то к нам на помощь и приходит Google Colab. Google предоставляет свои вычислительные мощности, которые немного больше мощностей обычного компьютера. Кроме того, использование колаба обосновано рядом преимуществ. Вот некоторые из них:

  1. Колаб абсолютно бесплатен. Да, вы можете купить себе премиум-доступ (если вы живете в США или Канаде), который позволит вам получать бóльшие вычислительные мощности, но в 99% случаев можно обойтись и без этого.
  2. Большинство библиотек (в т. ч. PyTorch) уже установлены, так что вам не придется тратить время на их установку.
  3. Google предоставляет вам от 12 Гигабайт оперативной памяти, и по вашему требованию – видеокарту от 8 Гигабайт. Это довольно большие вычислительные мощности, учитывая, что за них не нужно платить. Более того, не каждый компьютер может похвастаться таким набором, поэтому как правило обучение нейросетей на колабе проходит быстрее, чем локально.
  4. Linux. Колаб работает на ядре Linux, что несомненно является преимуществом, поскольку Linux требует меньше ресурсов, а также «Сделана программистами для программистов».
  5. Онлайн-доступ. Чтобы загрузить какой-либо датасет, вам не обязательно скачивать как правило огромный архив данных к себе на компьютер. Вы можете загрузить его на колаб с помощью стандартных утилит Linux. Это позволит вам работать, даже если на жестком диске вашего компьютера осталось мало места.

Тем не менее, если вам будет удобнее работать в Jupiter Notebook или любой другой среде исполнения IPython кода – вы можете скачать готовый ноутбук (напомню, что так называют .ipynb файлы) и запускать его локально.

Перейдем к непосредственной работе в колабе:
  1. Заведите аккаунт Google, если у вас его нет.
  2. Затем переходите на сайт Google Colab: https://colab.research.google.com
  3. Готово! Вам откроется приветственный блокнот с описанием возможностей колаба.

Отметим основные особенности и отличия IPython от обычного Python кода.
1. Это не новый язык, это новый способ его исполнения. Весь код – обычный Python код.
2. Весь код выполняется в ячейках. Чтобы создать ячейку, найдите кнопку + Code в верхней панели слева или по центру между двумя любыми ячейками. Кроме ячеек с кодом можно создавать ячейки с текстом. Для этого найдите кнопку + Text рядом с кнопкой + Code. Текстовые ячейки понимают Markdown-разметку и latex-код.
Google Colab: Ячейки с текстом и кодом
Google Colab: Ячейки с текстом и кодом
3. Чтобы выполнить код в конкретной ячейке, нажмите на кнопку Run Cell (черный треугольник на белом фоне) у соответствующей ячейки. Кроме этого, вы можете установить курсор в соответствующую ячейку и нажать Shift+Enter.
Google Colab: Запуск ячейки
Google Colab: Запуск ячейки
4. Несколько ячеек можно запускать разом. Но выполняться они будут последовательно. Сначала выполнится первая запущенная ячейка, после нее вторая, затем третья и так далее.
5. В ячейках с кодом можно запускать команды терминала Linux, только начать их надо с !. Например: !ls
Google Colab: Выполнение команды Linux
Google Colab: Выполнение команды Linux
В принципе, ничего сложного в этом нет. Вам нужно научиться делить свой код по ячейкам так, чтобы он не потерял читаемость. В любом случае, сейчас мы рассмотрим все вышесказанное на примере.

Читайте также

PyTorch

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

Вся работа с PyTorch сходится к работе с тензорами.
Тензор – это объект линейной алгебры, линейно преобразующий одно пространство в другое. Частными случаями тензора являются скаляры (тензоры нулевого ранга), векторы (тензоры первого ранга), матрицы (тензоры третьего ранга) и т. д.
Не переживайте, если вы плохо знакомы с линейной алгеброй. Ее знание конечно упростит вам понимание некоторых вещей, но не более.

Перед началом работы нам нужно импортировать сам PyTorch. Устанавливать его не надо, если вы работаете в колабе, но если локально – придется установить. Подробная инструкция по установке есть на официальном сайте.

Более того, для вас мы подготовили готовый ноутбук в колабе. Вы можете скопировать его к себе на диск (кнопка в верхнем меню справа), либо, если вам так удобнее, – скачать к себе на компьютер и выполнять в Jupiter Notebook.
Google Colab: Копирование ноутбука на свой GDrive
Google Colab: Копирование ноутбука на свой GDrive
Итак, импортируем PyTorch:
Python - Colab Cell

import torch # для начала импортируем саму библиотеку
Теперь почитаем документацию о тензорах. В колабе можно читать документацию функций, добавив знак ? перед самой функцией.
Python - Colab Cell

?torch.tensor()
Результат: Google Colab Help

Docstring:
tensor(data, *, dtype=None, device=None, requires_grad=False, pin_memory=False) -> Tensor

Constructs a tensor with :attr:`data`.

.. warning::

    :func:`torch.tensor` always copies :attr:`data`. If you have a Tensor
    ``data`` and want to avoid a copy, use :func:`torch.Tensor.requires_grad_`
    or :func:`torch.Tensor.detach`.
    If you have a NumPy ``ndarray`` and want to avoid a copy, use
    :func:`torch.as_tensor`.
...
Google Colab: Документация для torch.tensor()
Google Colab: Документация для torch.tensor()

Читайте также

Создание тензора

Теперь рассмотрим, как создавать тензоры, и что можно с ними сделать. Тензоры можно создавать несколькими способами:

  1. Из обычного списка Python
  2. Заполняя их нормальным распределением
  3. Заполняя их целыми случайными числами
Python - Colab Cell

# Тензоры можно создавать несколькими способами:
 
# Из списка
print('Из списка\n', torch.tensor([1, 2, 3, 4, 5]), '\n---------\n\n')
 
# Заполняя их нормально распределенными случайными числами.
# Такая функция принимает размер тензора на вход 
print('Нормальное распределение\n', torch.randn((5,)), '\n')  # Здесь мы создаем вектор длины 5
print(torch.randn((2, 2)), '\n---------\n\n') # А здесь матрицу 2 на 2
 
# Заполняя их целыми случайными числами
# Такая функция принимает на вход размер тензора
# и верхнюю границу интервала, из которого будут
# выбираться случайные числа.
print('Целые случайные числа\n', torch.randint(high=10, size=(5,)), '\n')  # Здесь мы создаем вектор длины 5
print(torch.randint(high=10, size=(2, 2)), '\n---------\n\n') # А здесь матрицу 2 на 2
Результат выполнения

Из списка
 tensor([1, 2, 3, 4, 5]) 
---------

Нормальное распределение
 tensor([2.2068, 2.0775, 2.5762, 1.4185, 1.6751]) 

tensor([[ 1.3320, -0.2528],
        [-0.4859,  0.9697]]) 
---------

Целые случайные числа
 tensor([5, 5, 4, 4, 2]) 

tensor([[2, 3],
        [8, 2]])

Индексация

Обращение по индексам к тензорам происходит так же, как и к обычным спискам в Python. Но все же разберем его на примере:
Python - Colab Cell

# Создадим целочисленный тензор 3-го ранга и обратимся к нему по индексам
 
our_tensor = torch.randint(high=10, size=(2, 3, 4))
print('А вот и наш тензор:\n', our_tensor)
 
# Теперь обратимся к какому-то его элементу
 
# Обращаемся к нулевому элементу. 
# Нулевой элемент -- это в данном случае матрица 3 на 4
print('\nНулевой элемент:\n', our_tensor[0])
 
# Обращаемся к нулевой строке нулевого элемента. 
print('\nНулевая строка нулевого элемент:\n', our_tensor[0][0])
 
# Обращаемся к элементу с индексом 1, 2, 3
# То есть первая матрица, вторая строка, третий элемент
print('\nПервая матрица, вторая строка, третий элемент:\n', our_tensor[1][2][3])
 
# Кроме того, индексы можно писать через запятую:
print('\nПишем индексы через запятую:\n', our_tensor[1, 2, 3])
Результат выполнения

А вот и наш тензор:
 tensor([[[9, 8, 2, 9],
         [0, 5, 3, 9],
         [2, 1, 2, 8]],

        [[2, 7, 5, 9],
         [5, 9, 8, 5],
         [6, 8, 0, 2]]])

Нулевой элемент:
 tensor([[9, 8, 2, 9],
        [0, 5, 3, 9],
        [2, 1, 2, 8]])

Нулевая строка нулевого элемент:
 tensor([9, 8, 2, 9])

Первая матрица, вторая строка, третий элемент:
 tensor(2)

Пишем индексы через запятую:
 tensor(2)
PyTorch также поддерживает срезы тензоров. Но чтобы понять, как работают срезы, нужно узнать, что такое оси(axis). С их помощью вы можете брать любые срезы сколько-угодно-мерных тензоров. Давайте разберемся с ними.

Оси и срезы тензоров

В общем случае ось – это номер измерения тензора. Например, у вектора всего одно измерение – поэтому у него всего одна ось, равная 0 (оси нумеруются с 0). У матрицы уже два измерения – у нее есть столбцы и строки. Так вот для матрицы ось 0 – это ось столбцов, а ось 1 – ось строк. Все станет понятнее с иллюстрацией:
У вектора всего одна ось.
У вектора всего одна ось.
А у матрицы уже два измерения. Поэтому и оси две.
А у матрицы уже два измерения. Поэтому и оси две.
А вот тензор третьего ранга. Он большой и страшный, но не пугайтесь его. У него есть три измерения, поэтому и оси три.
А вот тензор третьего ранга. Он большой и страшный, но не пугайтесь его. У него есть три измерения, поэтому и оси три.
Кстати, если вы знакомы с numpy, то могли заметить оси в torch – то же самое, что и в numpy.

Индексация тензора тоже происходит по осям, когда вы пишете our_tezor[2, 3, 1], вы говорите, что ищете элемент, который имеет индекс 2 по нулевой оси, индекс 3 – по второй, и индекс 1 – по третьей.

Благодаря этому, оси можно использовать, чтобы делать срезы тензоров. Если вы хотите выбрать все элементы по данной оси, просто укажите :, например our_tezor[2, :, 1] выбирает второй элемент по нулевой оси, все элементы по второй и первый элемент по третьей. Кроме того оси передаются, как параметр в некоторую функцию, но это мы рассмотрим позднее.

Если вы что-то недопоняли, то не переживайте. Переходим от теории к практике, чтобы у вас не осталось сомнений. Выведем весь тензор, используя срезы:
Python - Colab Cell

# Воспользуемся нашим тензором из предыдущей клетки
print('Все тот же наш тензор:\n', our_tensor)
 
# Возьмем срез всех строк, всех столбцов всех матриц,
# то есть просто выведем весь тензор, используя знания о срезах
print('\nСрез всего тензора:\n', our_tensor[:, :, :])
Результат выполнения

Все тот же наш тензор:
 tensor([[[6, 7, 6, 6],
         [0, 3, 2, 6],
         [0, 3, 9, 2]],

        [[9, 9, 6, 2],
         [5, 5, 8, 1],
         [2, 6, 0, 9]]])

Срез всего тензора:
 tensor([[[6, 7, 6, 6],
         [0, 3, 2, 6],
         [0, 3, 9, 2]],

        [[9, 9, 6, 2],
         [5, 5, 8, 1],
         [2, 6, 0, 9]]])
Теперь попробуем поиграться со срезами. Посмотрим на срезы по нулевой оси:
Python - Colab Cell

# Теперь выведем только нулевую матрицу со всеми ее строками и столбцами
print('\nТолько нулевая матрица:\n', our_tensor[0, :, :])
 
# Точно также выведем первую матрицу со всеми ее строками и столбцами
print('\nТолько первая матрица:\n', our_tensor[1, :, :])

Результат выполнения

Только нулевая матрица:
 tensor([[9, 8, 2, 9],
        [0, 5, 3, 9],
        [2, 1, 2, 8]])

Только первая матрица:
 tensor([[2, 7, 5, 9],
        [5, 9, 8, 5],
        [6, 8, 0, 2]])
Теперь по первой:
Python - Colab Cell

# А теперь выведем только нулевые строки всех матриц
print('\nТолько нулевые строки всех матриц:\n', our_tensor[:, 0, :])
 
# Теперь выведем только вторые строки всех матриц
print('\nТолько вторые строки всех матриц:\n', our_tensor[:, 2, :])

Результат выполнения

Только нулевые строки всех матриц:
 tensor([[9, 8, 2, 9],
        [2, 7, 5, 9]])

Только вторые строки всех матриц:
 tensor([[2, 1, 2, 8],
        [6, 8, 0, 2]])

Затем по второй:
Python - Colab Cell

# А сейчас выведем первые столбцы всех матриц
# Не удивляйтесь, они положены на бок
print('\nТолько первые столбцы всех матриц:\n', our_tensor[:, :, 1])
Результат выполнения

Только первые столбцы всех матриц:
 tensor([[8, 5, 1],
        [7, 9, 8]])
А теперь посмотрим на срез сразу по нескольким осям:
Python - Colab Cell

# Ну и последнее: выведем нулевые элементы всех вторых строк:
print('\nНулевые элементы всех вторых строк:\n', our_tensor[:, 2, 0]
Результат выполнения

Нулевые элементы всех вторых строк:
 tensor([2, 6])

Основные метрики

Разобравшись с осями и срезами тензоров, перейдем к основным метрикам. У тензора можно посчитать сумму (всех элементов любого среза), найти среднее (арифметическое, геометрическое и другие также для всех элементов любого среза), вычислить стандартное отклонение и многое другое.

Сумма всех элементов:
Python - Colab Cell

# Найдем сумму всех элементов тензора
 
all_sum = our_tensor.sum()
 
# кстати, выводить данные можно без функции print(), 
# но это работает, только если вы выводите данные одной переменной
# вот так:
all_sum
Результат выполнения

tensor(124)
Поэлементная сумма двух матриц нашего тензора:
Python - Colab Cell

# Теперь найдем поэлементную сумму двух матриц нашего тензора
# Для этого укажем параметр axis=0
sum_0 = our_tensor.sum(axis=0)
sum_0
Результат выполнения

tensor([[11, 15,  7, 18],
        [ 5, 14, 11, 14],
        [ 8,  9,  2, 10]])
Сумма всех строк для каждой матрицы:
Python - Colab Cell

# Теперь найдем сумму всех столбцов для каждой матрицы
# Для этого укажем параметр axis=2
sum_2 =  our_tensor.sum(axis=2)
sum_2
Результат выполнения

tensor([[28, 17, 13],
        [23, 27, 16]])
Помимо sum() есть метод mean(). Он находит среднее (арифметическое) значение элементов тензора. Работает он схожим образом. Среднее значение всех элементов тензора:
Python - Colab Cell

# Найдем среднее всех элементов тензора
# mean() работает только с float() тензорами, поэтому преобразуем наш функцией float()
all_mean = our_tensor.float().mean()
all_mean
Результат выполнения

tensor(5.1667)
Среднее всех строк для каждой матрицы:
Python - Colab Cell

# Теперь найдем среднее всех строк для каждой матрицы
# Для этого укажем параметр axis=2
mean_2 = our_tensor.float().mean(axis=2)
mean_2
Результат выполнения

tensor([[7.0000, 4.2500, 3.2500],
        [5.7500, 6.7500, 4.0000]])
В PyTorch есть еще множество подобных методов и функций, но все они работают по такому же принципу, поэтому мы останавливаться на них не будем. Если вас заинтересует та или иная функция – советую читать документацию. Там описана каждая функция с примерами использования.

Модули

PyTorch полон различных модулей. В одной из предыдущих статей мы с вами разбирали, как учатся нейронные сети. Там было сказано о том, что после прямого прохождения данных через сеть считается ошибка. Напомню, что ошибка – это просто отличие ответов сети от правильных ответов. Самая простая функция ошибки – обычный модуль разности . Мы вычитаем из правильных ответов ответы сети, но чтобы всегда получать положительные значения, берем от этой разности модуль (т.е. ее абсолютное значение). В PyTorch есть множество различных функций ошибки. Они содержатся в модуле nn (сокращение от neural networks). Попробуем посчитать ошибку самостоятельно.

Для начала импортируем модуль nn с более удобным сокращением, чтобы не писать каждый раз torch.nn:
Python - Colab Cell

from torch import nn
Зададим наши функции ошибки. Для примера мы рассмотрим среднеквадратичную (MSE (англ. Mean Squared Error) или L2) и абсолютную ошибки (MAE (англ. Mean Absolute Error) или L1).

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

MAE_Loss(x1, x2) = ½ *abs(x1 - x2) – Мы считаем модуль разности между x1 и x2
MSE_Loss(x1, x2) = ½*(x1 - x2)^2 – Мы считаем квадрат разности между x1 и x2

Но если мы рассматриваем вектор длины n, то формулы принимают следующий
вид:
Абсолютная ошибка
Абсолютная ошибка
Среднеквадратичная ошибка
Среднеквадратичная ошибка
Здесь y – верный ответ, y с шапкой – ответ модели. Стоит отметить, что что из чего вычитать – не важно в обоих случаях, потому что в первом случае мы берём абсолютное значение, а во втором – возводим результат в квадрат.
Python - Colab Cell

# Создадим экземпляры классов
 
# Среднеквадратичная ошибка
loss_mse_func = nn.MSELoss()
 
# Абсолютная ошибка
loss_mae_func = nn.L1Loss()
Создадим тензоры, на которых будем считать ошибку и сразу преобразуем их к типу float():
Python - Colab Cell

# Создадим тензоры и сразу зададим им тип float()
t_one = torch.randint(high=7, size=(7,), dtype=torch.float32)
t_two = torch.randint(high=7, size=(7,), dtype=torch.float32)
print(t_one, t_two)
Результат выполнения

tensor([4., 1., 0., 0., 2., 4., 4.]) tensor([0., 2., 2., 6., 2., 3., 1.])
Наконец, посчитаем наши ошибки:
Python - Colab Cell

# Посчитаем ошибки
 
loss_mse = loss_mse_func(t_one, t_two)
loss_mae = loss_mae_func(t_one, t_two)
 
print('Mean square error: ', loss_mse, '\nMean absolute error: ', loss_mae)
Результат выполнения

Mean square error:  tensor(9.5714) 
Mean absolute error:  tensor(2.4286)

Заключение

Сегодня мы познакомились с одним из ведущих фреймворков в среде машинного обучения PyTorch, а также научились работать в Google Colab. Конечно одна статья не охватит всего объема функций фреймворка, который изучают месяцами. Если та или иная функция вам непонятна – ищите ее в документации, выше мы рассматривали, как ей пользоваться. На первый взгляд полученные знания могут показаться неприменимыми, но только на первый взгляд. Все это нужно для того, чтобы уже в следующей статье написать свою первую полносвязную нейронную сеть, которая будет классифицировать рукописные цифры!
5 ДЕКАБРЯ / 2020
Как вам материал?

Читайте также