В мире машинного обучения есть группа алхимиков, их повседневная жизнь заключается в следующем:
Берут ингредиенты (данные), строят алхимические печи (модели), разжигают шесть видов истинного огня (алгоритмы оптимизации) и затем машут веером в ожидании, пока эликсир будет готов.
Однако любой, кто когда-либо был поваром, знает, что из одних и тех же ингредиентов, по одному и тому же рецепту, но при разной температуре получаются совершенно разные вкусы. Если температура слишком низкая, то блюдо не доварится, если слишком высокая — подгорит, а если температура неравномерная, то получится наполовину готовое блюдо.
То же самое и с машинным обучением, выбор алгоритма оптимизации модели напрямую влияет на производительность конечной модели. Иногда плохие результаты связаны не с характеристиками или дизайном модели, а именно с алгоритмом оптимизации.
Алгоритмы оптимизации глубокого обучения, вероятно, прошли путь развития от SGD -> SGDM -> NAG -> Adagrad -> Adadelta(RMSprop) -> Adam -> Nadam.
Подробнее см. в статье «Понимание различий и сходств между SGD/AdaGrad/Adam с помощью одной структуры».
Для начинающих алхимиков достаточно просто использовать Adam с его параметрами по умолчанию.
Некоторые алхимики, любящие писать статьи, могут предпочесть сначала использовать Adam для быстрого снижения показателей, а затем перейти на SGD и точно настроить параметры оптимизатора для получения лучших результатов.
Кроме того, существуют передовые алгоритмы оптимизации, которые, как утверждается, работают лучше, чем Adam, например, LazyAdam, Look-ahead, RAdam, Ranger и другие.
Оптимизаторы в основном используют метод apply_gradients для передачи переменных и соответствующих градиентов для итеративной обработки заданных переменных или непосредственно используют метод minimize для итерационной оптимизации целевой функции.
Конечно, более распространённым является использование оптимизатора при компиляции путём передачи его в keras Model, и вызов model.fit реализует итеративную оптимизацию Loss.
При инициализации оптимизатора создаётся переменная optimier.iterations для записи количества итераций. Поэтому, как и tf.Variable, оптимизатор обычно необходимо создавать вне @tf.function.
import tensorflow as tf
import numpy as np
# Печатаем разделительную линию времени
@tf.function
def printbar():
ts = tf.timestamp()
today_ts = ts % (24 * 60 * 60)
hour = tf.cast(today_ts // 3600 + 8, tf.int32) % tf.constant(24)
minute = tf.cast((today_ts % 3600) // 60, tf.int32)
second = tf.cast(tf.floor(today_ts % 60), tf.int32)
def timeformat(m):
if tf.strings.length(tf.strings.format("{}", m)) == 1:
return (tf.strings.format("0{}", m))
else:
return (tf.strings.format("{}", m))
timestring = tf.strings.join([timeformat(hour), timeformat(minute),
timeformat(second)], separator=":")
tf.print("==========" * 8, end="")
tf.print(timestring)
# Находим минимальное значение f(x) = a*x**2 + b*x + c
# Используем optimizer.apply_gradients
x = tf.Variable(0.0, name="x", dtype=tf.float32)
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)
@tf.function
def minimizef():
a = tf.constant(1.0)
b = tf.constant(-2.0)
c = tf.constant(1.0)
while tf.constant(True):
with tf.GradientTape() as tape:
y = a * tf.pow(x, 2) + b * x + c
dy_dx = tape.gradient(y, x)
optimizer.apply_gradients(grads_and_vars=[(dy_dx, x)])
# Условия завершения итерации
if tf.abs(dy_dx) < tf.constant(0.00001):
break
if tf.math.mod(optimizer.iterations, 100) == 0:
printbar()
tf.print("step = ", optimizer.iterations)
tf.print("x = ", x)
tf.print("")
y = a * tf.pow(x, 2) + b * x + c
return y
tf.print("y =", minimizef())
tf.print("x =", x)
# Находим минимальное значение f(x) = a*x**2 + b*x + c
# Используем optimizer.minimize
x = tf.Variable(0.0, name="x", dtype=tf.float32)
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)
def f():
a = tf.constant(1.0)
b = tf.constant(-2.0)
c = tf.constant(1.0)
y = a * tf.pow(x, 2) + b * x + c
return(y)
@tf.function
def train(epoch=1000):
for _ in tf.range(epoch):
optimizer.minimize(f, [x])
tf.print("epoch = ", optimizer.iterations)
return(f())
train(1000)
tf.print("y = ", f())
tf.print("x = ", x)
# Находим минимальное значение f(x) = a*x**2 + b*x + c
# Используем model.fit
tf.keras.backend.clear_session()
class FakeModel(tf.keras.models.Model):
def __init__(self, a, b, c):
super(FakeModel, self).__init__()
self.a = a
self.b = b
self.c = c
def build(self):
self.x = tf.Variable(0.0, name="x")
self.built = True
def call(self, features):
loss = self.a * (self.x)**2 + self.b * self.x + self.c
return (tf.ones_like(features) * loss)
def myloss(y_true, y_pred):
return tf.reduce_mean(y_pred)
model = FakeModel(tf.constant(1.0), tf.constant(-2.0), tf.constant(1.0))
model.build()
model.summary()
model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=0.01), loss=myloss)
history = model.fit(tf.zeros((100, 2)),
tf.ones(100), batch_size=1, epochs=10) # Итерация 1000 раз
tf.print("x=", model.x)
tf.print("loss=", model(tf.constant(0.0)))
Алгоритмы оптимизации глубокого обучения, вероятно, прошли путь развития от SGD -> SGDM -> NAG -> Adagrad -> Adadelta(RMSprop) -> Adam -> Nadam. В подмодуле keras.optimizers в основном есть реализации соответствующих классов.
SGD, параметры по умолчанию — это чистый SGD. Если установить параметр momentum не равным 0, фактически получится SGDM, учитывающий первый порядок импульса. Установка nesterov в значение True приводит к NAG, то есть Nesterov Accelerated Gradient, который при вычислении градиента вычисляет градиент позиции, продвигаясь на шаг вперёд.
Adagrad, учитывает второй порядок импульса и имеет разные скорости обучения для разных параметров, то есть адаптивную скорость обучения. Недостатком является то, что скорость обучения монотонно убывает, и в конце концов скорость обучения может стать слишком медленной или обучение может прекратиться досрочно.
RMSprop, учитывает второй порядок импульса и имеет разные скорости обучения для разных параметров, то есть адаптивную скорость обучения, оптимизированную по сравнению с Adagrad путём экспоненциального сглаживания, которое учитывает только второй порядок импульса в определённом окне.
Adadelta, учитывает второй порядок импульса, аналогично RMSprop, но более сложен и обладает большей адаптивностью.
Adam, одновременно учитывает первый и второй порядки импульса, можно рассматривать как дальнейшее рассмотрение первого порядка импульса на основе RMSprop.
Nadam, на основе Adam дополнительно рассматривает Nesterov Acceleration.
Если у вас есть вопросы по содержанию этой книги, вы можете оставить комментарий в общедоступной учётной записи «Алгоритмная кухня». Автор ответит по мере возможности.
Вы также можете отправить сообщение через интерфейс общедоступной учётной записи, ответив ключевым словом: присоединиться к группе обсуждения читателей.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )