среда, 26 декабря 2018 г.

PyTorch: Autograd, автоматическая дифференциация

Центральным для всех нейронных сетей в PyTorch является пакет autograd.

Пакет autograd обеспечивает автоматическое разграничение всех операций на тензорах. Это определяемый-по-исполнению (define-by-run) фреймворк, что означает, что ваш backprop (обратное распространение в нейронной сети) определяется тем, как выполняется ваш код, и что каждая отдельная итерация может отличаться.

Рассмотрим это в более простых терминах на нескольких примерах.

Тензор

torch.Tensor является центральным классом пакета. Если вы установите его атрибут .require_grad равным True, тогда он начнет отслеживать все операции над ним. Когда вы закончите вычисления, вы можете вызвать .backward() и получить все градиенты вычисленные автоматически. Градиент для этого тензора будет накапливается в атрибуте .grad.

Чтобы остановить отслеживание тензора в истории вычислений, вы можете вызвать .detach().

Чтобы предотвратить отслеживание истории (и использование памяти), вы также можете обернуть блок кода в with torch.no_grad():. Это может быть особенно полезно при оценке модели, потому что модель может иметь обучаемые параметры с require_grad = True, но для которых нам не нужны градиенты.

Есть еще один класс, который очень важен для autograd реализации - Function.

Tensor и Function связаны между собой и создают ациклический граф, который кодирует полную историю вычислений. Каждый тензор имеет .grad_fn атрибут, который ссылается на Function, которая создала Tensor (за исключением тензоров, созданных пользователем - их grad_fn = None).

Если вы хотите вычислить производные, вы можете вызвать .backward() на Tensor. Если Tensor является скаляром (то есть он содержит один элемент данных), вам не нужно указывать какие-либо аргументы для backward(), однако, если в нем больше элементов, вам нужно указать gradient аргумент, который является тензором подходящей формы.

import torch

Создаем тензор и задаем requires_grad=True, чтобы отслеживать вычисления с ним:

x = torch.ones(2, 2, requires_grad=True)
print(x)

Вывод:

tensor([[1., 1.],
        [1., 1.]], requires_grad=True)

Выполняем операцию на тензоре:

y = x + 2
print(y)

Вывод:

tensor([[3., 3.],
        [3., 3.]], grad_fn=<AddBackward0>)

y был создан как результат операции, поэтому он имеет grad_fn.

print(y.grad_fn)

Вывод:

<AddBackward0 object at 0x7fef6f982438>

Выполняем еще несколько операций на y:

z = y * y * 3
out = z.mean()

print(z, out)

Вывод:

tensor([[27., 27.],
        [27., 27.]], grad_fn=<MulBackward0>) 
tensor(27., grad_fn=<MeanBackward0>)

.requires_grad_( ... ) изменяет существующий requires_grad флаг тензора на месте (in-place). input флаг равен False, если не задан:

a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)

Вывод:

False
True
<SumBackward0 object at 0x7fe1db427dd8>


Градиенты

Вызовем backprop теперь. Поскольку вывод содержит единственный скаляр, out.backward() эквивалентен ut.backward(torch.tensor(1.)).

out.backward()

Напечатаем градиенты d(out)/dx

print(x.grad)

Вывод:

tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])

В общем говоря, torch.autograd - это механизм для вычисления Jacobian-vector произведения.

Рассмотрим пример Jacobian-vector произведения:

x = torch.randn(3, requires_grad=True)

y = x * 2
while y.data.norm() < 1000:
    y = y * 2

print(y)

Вывод:

tensor([ 840.8677,  613.5138, -778.9942], grad_fn=<MulBackward0>)

Теперь в этом случае y больше не скаляр. torch.autograd не может вычислить полный Jacobian напрямую, но если мы просто хотим Jacobian-vector произведение, просто передаем вектор в backward как аргумент:

v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)

print(x.grad)

Вывод:

tensor([1.0240e+02, 1.0240e+03, 1.0240e-01])

Вы можете также остановить autograd от отслежвания истории на тензорах с .requires_grad=True с помощью обертывания блока кода в with torch.no_grad():

print(x.requires_grad)
print((x  2).requires_grad)

with torch.no_grad():
    print((x  2).requires_grad)

Вывод:

True
True
False



Читайте также другие статьи в блоге:

PyTorch: тензоры (tensors)

TensorFlow: классификация текста с отзывами к фильмам

Анатомия нейронных сетей