Анализ процесса автоградации PyTorch и некоторые мелочи, с которыми столкнулись

искусственный интеллект PyTorch

Мало знаний, большой вызов! Эта статья участвует в "Необходимые знания для программистов«Творческая деятельность.

0. autograd

0.1 autograd

(Этот отрывок из официального перевода) можно использовать в pytorchautogradРеализовать автоматический расчет процесса обратного распространения нейронной сети. Когда мы используем autograd, прямое распространение определяет вычислительный граф, в котором все узлы графа являются тензорами, а ребра графа — функциями, которые генерируют выходные тензоры из входных тензоров. Градиент можно легко получить путем обратного распространения этого графика.

0.2 Расчетный граф

Картинка ниже — это своего рода поток сознания, и его полезно понимать, потому что официальная поговорка гласит: «Узлы — это тензоры, ребра — это функции». Я думаю, что после того, как я нарисовал его, я считал ребра функциями, но это не было похоже на тот «график», который я изучил в традиционном смысле.

import torch

x = torch.tensor([1.0], requires_grad=True)
print(x)		# 输出:tensor([1.])
y = x + 1
print(y)		# 输出:tensor([2.])

Согласно пункту 0.1 выше расчетный график должен выглядеть так:在这里插入图片描述

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

import torch

x = torch.tensor([1.0], requires_grad=True)
k = torch.tensor([2.0])
y = k * x
print(k.is_leaf)    # 输出:True
print(x.is_leaf)    # 输出:True
print(y.is_leaf)    # 输出:False

在这里插入图片描述

1. .requires_grad=True

В приведенном выше коде есть.requires_grad=True, что это?

По умолчанию атрибут require_grad Tensor имеет значение False., чтобы его градиент не отслеживался. Тензор с этим свойством установлен в.requires_grad=TrueПри построении графа вычислений операции, в которых участвует этот тензор, будутотслеживатьи сохраните его в узле результатаgrad_fnв свойствах. После отслеживания,при обратном распространенииГрадиент будет рассчитан во всех местах, где требуется градиент по ссылке расчета, и после завершения расчета тензор будет сохранен..gradв свойствах.

import torch

# 默认情况下requires_grad=False
x = torch.tensor([1.0])
print(x)		# 输出:tensor([1.])
x += 1
print(x)		# 输出:tensor([2.])

# 设置requires_grad=True
y = torch.tensor([2.0], requires_grad=True)
print(y)		# 输出:tensor([2.], requires_grad=True)
z = y + 1.0
print(z)		# 输出:tensor([3.], grad_fn=<AddBackward0>)
print(y)        # 输出:tensor([2.], requires_grad=True)
print(y.grad_fn)# None

Уведомление:

  • Я вывожу y и z соответственно в последних трех строках, вы можете видеть, что y установлен.requires_grad=TrueПосле этого вместо сохранения операции в ygrad_fnатрибут, но есть узел результата графа расчетаgrad_fnв свойствах.

2. Свойство тензора .grad_fn

Как упоминалось выше, когда вы устанавливаете.requires_grad=TrueПосле этого отслеживаются операции над этим тензором. Но только неконечные узлы имеют этот атрибут, а конечные узлы будут отображать None.

3. .grad

import torch

y = torch.tensor([2.0], requires_grad=True)
print(y)		# 输出:tensor([2.], requires_grad=True)
print(y.grad)   # 输出:None
z = y + 1.0
print(z)		# 输出:tensor([3.], grad_fn=<AddBackward0>)
print(y)        # 输出:tensor([2.], requires_grad=True)
print(y.grad)   # 输出:None
print(z.grad)   # 输出:None
# 调用反向传播
z.backward()
print(y.grad)   # 输出:tensor([1.])
print(z.grad)   # 输出:None

Уведомление:

  • После того, как операция y отслежена, ееy.gradпусто,только при обратном распространенииВо время выполнения вычисляется градиент, необходимый для каждого звена, и результат сохраняется вy.gradсередина
  • После запуска кода появляются два UserWarnings, говорящие о том, что неконечные узлы недоступны..gradАтрибуты. Это потому, что я специально написал два вышеprint(z.grad). z является узлом результата, его атрибут grad не может быть выведен.
    • Неконечные узлы по умолчанию.gradотключено, принудительная печать выведет None
    • хотите сохранить узел результата.gradСуществует два метода, один из которых заключается в добавлении после операции построения вычислительного графа.retain_grad(), другой должен использовать.register_hookСам не смотрел, посмотрю, если интересно

4. with torch.no_grad()

Если вы не хотите, чтобы действие отслеживалось, вам нужно использоватьwith torch.no_grad():. Код, заключенный в этот оператор, не будет отслеживать градиенты.

import torch

# 设置requires_grad=True
y = torch.tensor([1.0], requires_grad=True)
print(y)			# tensor([1.], requires_grad=True)
z = y + 1.0
print(z)            # tensor([2.], grad_fn=<AddBackward0>)
print(z.grad_fn)    # <AddBackward0 object at 0x0000017C3CFD21C0>

with torch.no_grad():
    z = y - 2
print(z)            # tensor([-1.])
print(z.grad_fn)    # None

  • во втором выводеgrad_fnДисплей отслеживает операцию добавления
  • Третий вывод печатается отдельноgrad_fn, снова показывая, что операция сложения отслеживается
  • Четвертый выход установленwith torch.no_grad():После этого обнаружилось, что в выводе не былоgrad_fnатрибут
  • Пятый вывод печатается отдельноgrad_fnне существует, т.with torch.no_grad():В пакете эта операция вычитания не отслеживается и свойство также очищается.

with torch.no_grad()какой смысл?

При использовании pytorch не все операции требуют создания вычислительного графа. Для операции расчета тензора по умолчанию строится график расчета, в этом случае вы можете использоватьwith torch.no_grad():, заставляя контент после этого не выполнять построение вычислительного графа.

5. .data

Помимо использованияwith torch.no_grad()Вы также можете использовать тензор.data.

import torch

# 设置requires_grad=True
y = torch.tensor([1.0], requires_grad=True)
print(y)			# tensor([1.], requires_grad=True)
z = y + 1.0
print(z)            # tensor([2.], grad_fn=<AddBackward0>)
print(z.grad_fn)    # <AddBackward0 object at 0x0000017C3CFD21C0>

z.data = y.data - 2
print(z)            # tensor([-1.])
print(z.grad_fn)    # <AddBackward0 object at 0x00000226475F21C0>

относительно вышеуказанногоwith torch.no_grad(),.dataОн также не строит график расчета и не отслеживает операции, но при этом сохранит отслеживание предыдущего шага и не очищает его.


Я Лориан, и, похоже, сегодня я снова преуспел. (^-^)В Он полностью основан на информации, которую я проверил сам, могут быть упущения, и исправления приветствуются. Спасибо♪(・ω・)ノ