Сравнение пользовательских оптимизаторов с использованием API -интерфейсов TensorFlow Core

Сравнение пользовательских оптимизаторов с использованием API -интерфейсов TensorFlow Core

19 июля 2025 г.

Обзор контента

  • Введение
  • Обзор оптимизаторов
  • Настраивать
  • Градиент спуск
  • Градиент спуск с импульсом
  • Оценка адаптивного момента (Адам)
  • Заключение

Введение

АКерас оптимизаторыМодуль является рекомендуемым инструментом оптимизации для многих общих учебных целей. Он включает в себя различные предварительно построенные оптимизации, а также функциональность подкласса для настройки. Оптимизаторы Keras также совместимы с пользовательскими слоями, моделями и тренировочными петлями, построенными с основными API. Эти предвзятые и настраиваемые оптимизаторы подходят для большинства случаев, но основные API позволяют полностью контролировать процесс оптимизации. Например, такие методы, как минимизация с учетом резкости (SAM), требуют соединения модели и оптимизатора, что не соответствует традиционному определению оптимизаторов ML. Это руководство проходит через процесс создания пользовательских оптимизаторов с нуля с основными API, что дает вам возможность полного контроля над структурой, реализацией и поведением ваших оптимизаторов.

Обзор оптимизаторов

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

Настраивать

import matplotlib
from matplotlib import pyplot as plt
# Preset Matplotlib figure sizes.
matplotlib.rcParams['figure.figsize'] = [9, 6]

import tensorflow as tf
print(tf.__version__)
# set random seed for reproducible results 
tf.random.set_seed(22)

2024-08-15 02:37:52.102563: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:485] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered
2024-08-15 02:37:52.123704: E external/local_xla/xla/stream_executor/cuda/cuda_dnn.cc:8454] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered
2024-08-15 02:37:52.130222: E external/local_xla/xla/stream_executor/cuda/cuda_blas.cc:1452] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered
2.17.0

Градиент спуск

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

class GradientDescent(tf.Module):

  def __init__(self, learning_rate=1e-3):
    # Initialize parameters
    self.learning_rate = learning_rate
    self.title = f"Gradient descent optimizer: learning rate={self.learning_rate}"

  def apply_gradients(self, grads, vars):
    # Update variables
    for grad, var in zip(grads, vars):
      var.assign_sub(self.learning_rate*grad)

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

L = 2x4+3x3+2

dldx = 8x3+9x2

Dldx составляет 0 при x = 0, которая является седлой точкой и при x = −98, что является минимальным глобальным. Следовательно, функция потерь оптимизирована при x⋆ = −98.

x_vals = tf.linspace(-2, 2, 201)
x_vals = tf.cast(x_vals, tf.float32)

def loss(x):
  return 2*(x**4) + 3*(x**3) + 2

def grad(f, x):
  with tf.GradientTape() as tape:
    tape.watch(x)
    result = f(x)
  return tape.gradient(result, x)

plt.plot(x_vals, loss(x_vals), c='k', label = "Loss function")
plt.plot(x_vals, grad(loss, x_vals), c='tab:blue', label = "Gradient function")
plt.plot(0, loss(0),  marker="o", c='g', label = "Inflection point")
plt.plot(-9/8, loss(-9/8),  marker="o", c='r', label = "Global minimum")
plt.legend()
plt.ylim(0,5)
plt.xlabel("x")
plt.ylabel("loss")
plt.title("Sample loss function and gradient");

WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1723689474.551312  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689474.555159  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689474.558916  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689474.562138  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689474.573919  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689474.577367  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689474.580786  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689474.583786  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689474.587331  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689474.590801  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689474.594185  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689474.597140  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.839513  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.841532  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.844269  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.846345  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.848395  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.850251  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.852146  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.854142  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.856078  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.857966  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.859861  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.861827  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.899762  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.901715  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.903659  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.905680  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.907718  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.909592  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.911505  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.913511  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.915419  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.917727  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.919991  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723689475.922410  124187 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355

Напишите функцию, чтобы проверить сходимость оптимизатора с помощью одной функции потери переменной. Предположим, что конвергенция была достигнута, когда значение обновленного параметра в TimeStep T такое же, как и его значение, хранящееся при TimeStep T - 1. Завершите тест после установленного числа итераций, а также отслеживайте любые взрывающиеся градиенты в процессе. Чтобы по -настоящему оспорить алгоритм оптимизации, плохо инициализируйте параметр. В приведенном выше примере x = 2 является хорошим выбором, поскольку он включает в себя крутой градиент, а также приводит к точке перегиба.

def convergence_test(optimizer, loss_fn, grad_fn=grad, init_val=2., max_iters=2000):
  # Function for optimizer convergence test
  print(optimizer.title)
  print("-------------------------------")
  # Initializing variables and structures
  x_star = tf.Variable(init_val)
  param_path = []
  converged = False

  for iter in range(1, max_iters + 1):
    x_grad = grad_fn(loss_fn, x_star)

    # Case for exploding gradient
    if tf.math.is_nan(x_grad):
      print(f"Gradient exploded at iteration {iter}\n")
      return []

    # Updating the variable and storing its old-version
    x_old = x_star.numpy()
    optimizer.apply_gradients([x_grad], [x_star])
    param_path.append(x_star.numpy())

    # Checking for convergence
    if x_star == x_old:
      print(f"Converged in {iter} iterations\n")
      converged = True
      break

  # Print early termination message
  if not converged:
    print(f"Exceeded maximum of {max_iters} iterations. Test terminated.\n")
  return param_path

Проверьте конвергенцию градиентного оптимизатора спуска для следующих скоростей обучения: 1E-3, 1E-2, 1E-1

param_map_gd = {}
learning_rates = [1e-3, 1e-2, 1e-1]
for learning_rate in learning_rates:
  param_map_gd[learning_rate] = (convergence_test(
      GradientDescent(learning_rate=learning_rate), loss_fn=loss))

Gradient descent optimizer: learning rate=0.001
-------------------------------
Exceeded maximum of 2000 iterations. Test terminated.

Gradient descent optimizer: learning rate=0.01
-------------------------------
Exceeded maximum of 2000 iterations. Test terminated.

Gradient descent optimizer: learning rate=0.1
-------------------------------
Gradient exploded at iteration 6

Визуализируйте путь параметров по контурному графику функции потери.

def viz_paths(param_map, x_vals, loss_fn, title, max_iters=2000):
  # Creating a controur plot of the loss function
  t_vals = tf.range(1., max_iters + 100.)
  t_grid, x_grid = tf.meshgrid(t_vals, x_vals)
  loss_grid = tf.math.log(loss_fn(x_grid))
  plt.pcolormesh(t_vals, x_vals, loss_grid, vmin=0, shading='nearest')
  colors = ['r', 'w', 'c']
  # Plotting the parameter paths over the contour plot
  for i, learning_rate in enumerate(param_map):
    param_path = param_map[learning_rate]
    if len(param_path) > 0:
      x_star = param_path[-1]
      plt.plot(t_vals[:len(param_path)], param_path, c=colors[i])
      plt.plot(len(param_path), x_star, marker='o', c=colors[i], 
              label = f"x*: learning rate={learning_rate}")
  plt.xlabel("Iterations")
  plt.ylabel("Parameter value")
  plt.legend()
  plt.title(f"{title} parameter paths")

viz_paths(param_map_gd, x_vals, loss, "Gradient descent")

/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/IPython/core/events.py:82: UserWarning: Creating legend with loc="best" can be slow with large amounts of data.
  func(*args, **kwargs)

Градиент спуск, кажется, застрял в точке перегиба при использовании меньших уровней обучения. Увеличение уровня обучения может стимулировать более быстрое движение вокруг региона плато из -за большего размера шага; Тем не менее, это рискует взрываться градиентами в ранних итерациях, когда функция потери является чрезвычайно крутой.

Градиент спуск с импульсом

Градиент спуск с импульсом не только использует градиент для обновления переменной, но и включает изменение в положении переменной на основе ее предыдущего обновления. Параметр импульса определяет уровень влияния, который обновляется на TimeStep T -1 на обновление при TimeStep T. Накапливающийся импульс помогает перемещать переменные в районах платчи быстрее, чем базовый градиентный спуск. Правило обновления импульса заключается в следующем:

Δx [t] = lr> ′ (x [t -1])+p % [t - 1]

x [t] = x [t - 1] −Δx [t]

где

  • X: оптимизированная переменная
  • Δx: изменение в x
  • LR: скорость обучения
  • L ′ (x): градиент функции потери относительно x
  • П: Параметр импульса

class Momentum(tf.Module):

  def __init__(self, learning_rate=1e-3, momentum=0.7):
    # Initialize parameters
    self.learning_rate = learning_rate
    self.momentum = momentum
    self.change = 0.
    self.title = f"Gradient descent optimizer: learning rate={self.learning_rate}"

  def apply_gradients(self, grads, vars):
    # Update variables 
    for grad, var in zip(grads, vars):
      curr_change = self.learning_rate*grad + self.momentum*self.change
      var.assign_sub(curr_change)
      self.change = curr_change

Проверьте конвергенцию оптимизатора импульса для следующих скоростей обучения: 1E-3, 1E-2, 1E-1

param_map_mtm = {}
learning_rates = [1e-3, 1e-2, 1e-1]
for learning_rate in learning_rates:
  param_map_mtm[learning_rate] = (convergence_test(
      Momentum(learning_rate=learning_rate),
      loss_fn=loss, grad_fn=grad))

Gradient descent optimizer: learning rate=0.001
-------------------------------
Exceeded maximum of 2000 iterations. Test terminated.

Gradient descent optimizer: learning rate=0.01
-------------------------------
Converged in 80 iterations

Gradient descent optimizer: learning rate=0.1
-------------------------------
Gradient exploded at iteration 6

Визуализируйте путь параметров по контурному графику функции потери.

viz_paths(param_map_mtm, x_vals, loss, "Momentum")

Оценка адаптивного момента (Адам)

Алгоритм оценки адаптивного момента (ADAM) является эффективной и очень обобщаемой методикой оптимизации, которая использует две ключевые метологии спуска градиента: импульс и среднее квадратное пропагандинг (RMSP). Импульс помогает ускорить спуск градиента, используя первый момент (сумма градиентов) вместе с параметром распада. RMSP похож; Тем не менее, это использует второй момент (сумма градиентов в квадрате).

Алгоритм ADAM объединяет как первый, так и второй момент, чтобы обеспечить более обобщаемое правило обновления. Знак переменной, x, может быть определен путем вычисления XX2. Оптимизатор ADAM использует этот факт для расчета шага обновления, который фактически является сглаженным знаком. Вместо расчета XX2 оптимизатор рассчитывает сглаженную версию X (первый момент) и X2 (второй момент) для каждого обновления переменной.

Адам Алгоритм

β1 ← 0,9 -х литературное значение

β2 ← 0,999 -х литературное значение

LR ← 1E-3▹configerawere Screed обучения

ϵ ← 1e-7▹prevents Разделите на 0 ошибку

VDV ← 0N × 1 → ▹Stores Обновления импульса для каждой переменной

SDV ← 0n × 1 → ▹Stores Обновления RMSP для каждой переменной

T ← 1

На итерации t:

Для (dldv, v) в парах градиентных переменных:

Vdv_i = β1vdv_i+(1 - β1) обновление dldv▹momentum

SDV_I = β2VDV_I+(1 - β2) (DLDV) обновление 2▹RMSP

VDVBC = VDV_I (1 -β1) Коррекция смещения t▹momentum

SDVBC = SDV_I (1 -β2) Коррекция смещения T▹RMSP

v = v -lrvdvbcsdvbc+ϵ▹parameter Обновление

t = t+1

Конец алгоритма

Учитывая, что VDV и SDV инициализируются до 0 и что β1 и β2 близки к 1, обновления импульса и среднеквадратичного среднего уровня естественным образом смещены в сторону 0; Следовательно, переменные могут извлечь выгоду из коррекции смещения. Коррекция смещения также помогает контролировать оскамируцию весов по мере приближения к минимальному минимальному минимальному минимальности.

class Adam(tf.Module):

    def __init__(self, learning_rate=1e-3, beta_1=0.9, beta_2=0.999, ep=1e-7):
      # Initialize the Adam parameters
      self.beta_1 = beta_1
      self.beta_2 = beta_2
      self.learning_rate = learning_rate
      self.ep = ep
      self.t = 1.
      self.v_dvar, self.s_dvar = [], []
      self.title = f"Adam: learning rate={self.learning_rate}"
      self.built = False

    def apply_gradients(self, grads, vars):
      # Set up moment and RMSprop slots for each variable on the first call
      if not self.built:
        for var in vars:
          v = tf.Variable(tf.zeros(shape=var.shape))
          s = tf.Variable(tf.zeros(shape=var.shape))
          self.v_dvar.append(v)
          self.s_dvar.append(s)
        self.built = True
      # Perform Adam updates
      for i, (d_var, var) in enumerate(zip(grads, vars)):
        # Moment calculation
        self.v_dvar[i] = self.beta_1*self.v_dvar[i] + (1-self.beta_1)*d_var
        # RMSprop calculation
        self.s_dvar[i] = self.beta_2*self.s_dvar[i] + (1-self.beta_2)*tf.square(d_var)
        # Bias correction
        v_dvar_bc = self.v_dvar[i]/(1-(self.beta_1**self.t))
        s_dvar_bc = self.s_dvar[i]/(1-(self.beta_2**self.t))
        # Update model variables
        var.assign_sub(self.learning_rate*(v_dvar_bc/(tf.sqrt(s_dvar_bc) + self.ep)))
      # Increment the iteration counter
      self.t += 1.

Проверьте производительность оптимизатора ADAM с теми же показателями обучения, которые использовались с примерами градиентного спуска.

param_map_adam = {}
learning_rates = [1e-3, 1e-2, 1e-1]
for learning_rate in learning_rates:
  param_map_adam[learning_rate] = (convergence_test(
      Adam(learning_rate=learning_rate), loss_fn=loss))

Adam: learning rate=0.001
-------------------------------
Exceeded maximum of 2000 iterations. Test terminated.

Adam: learning rate=0.01
-------------------------------
Exceeded maximum of 2000 iterations. Test terminated.

Adam: learning rate=0.1
-------------------------------
Converged in 1156 iterations

Визуализируйте путь параметров по контурному графику функции потери.

viz_paths(param_map_adam, x_vals, loss, "Adam")

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

Заключение

В этом записном книжке были представлены основы написания и сравнения оптимизаторов сTensorflow Core APIПолем Хотя предвзятые оптимизаторы, такие как Адам, являются обобщенными, они не всегда могут быть лучшим выбором для каждой модели или набора данных. Показанный контроль над процессом оптимизации может помочь оптимизировать учебные процессы ML и повысить общую производительность. Обратитесь к следующей документации для получения дополнительных примеров пользовательских оптимизаторов:

  • Этот оптимизатор Адама используется вМногослойные персептроныУчебник иРаспределенное обучение
  • Модельный садимеет разнообразиеПользовательские оптимизаторынаписано с основными API.

Первоначально опубликовано наTensorflowВеб -сайт, эта статья появляется здесь под новым заголовком и имеет лицензию в CC на 4.0. Образцы кода, разделенные по лицензии Apache 2.0.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE