TensorFlow.js предоставляет высокоуровневые абстракции для работы с
нейронными сетями, однако иногда требуется полный контроль над процессом
обратного распространения ошибки. Для этого используется функция
tf.customGrad, которая позволяет определить собственный
градиент для произвольной операции.
tf.customGrad принимает один аргумент — функцию, которая
возвращает объект с двумя свойствами:
Простейший синтаксис выглядит так:
const customOp = tf.customGrad((x, save) => {
save([x]); // Сохраняем тензор для использования в gradFunc
const value = x.square();
const gradFunc = (dy, saved) => {
const [x] = saved;
return dy.mul(x.mul(2)); // Производная x^2 равна 2*x
};
return { value, gradFunc };
});
В этом примере создается операция возведения в квадрат с явно
заданной производной. save используется для хранения
промежуточных тензоров, необходимых при вычислении градиента. Это важно
для оптимизации памяти, так как не все промежуточные значения требуется
хранить автоматически.
Функция gradFunc принимает два параметра:
value).save.Возвращаемое значение gradFunc должно соответствовать
количеству входных тензоров, переданных в customGrad. Для
нескольких входов градиенты возвращаются в виде массива:
const customAdd = tf.customGrad((a, b, save) => {
save([a, b]);
const value = a.add(b);
const gradFunc = (dy, saved) => {
return [dy, dy]; // Производная суммы по каждому аргументу равна 1
};
return { value, gradFunc };
});
Функция save используется для того, чтобы не
пересчитывать промежуточные значения при обратном распространении. Это
особенно важно для сложных операций, где повторное вычисление может быть
затратным.
const customExp = tf.customGrad((x, save) => {
const y = x.exp();
save([y]); // Сохраняем результат экспоненты
const gradFunc = (dy, saved) => {
const [y] = saved;
return dy.mul(y); // Производная e^x равна e^x
};
return { value: y, gradFunc };
});
Пользовательские градиенты особенно полезны при:
Пример использования в тренировочном цикле:
const optimizer = tf.train.sgd(0.01);
for (let i = 0; i < 100; i++) {
optimizer.minimize(() => {
const x = tf.tensor1d([1, 2, 3]);
const y = customOp(x);
return y.sum(); // Минимизируем сумму квадратов
});
}
В этом цикле customOp используется так же, как любая
стандартная операция TensorFlow.js, но с гарантией того, что градиенты
вычисляются точно так, как задано пользователем.
save.
Избыточное сохранение приводит к увеличению потребления памяти.tf.tidy внутри customGrad при
необходимости управлять временными тензорами.tf.grad или
tf.valueAndGrad для отладки сложных операций.Градиент с пороговой функцией (clipping)
const customClip = tf.customGrad((x, save) => {
const y = x.relu(); // ReLU
save([x]);
const gradFunc = (dy, saved) => {
const [x] = saved;
return dy.mul(x.greater(0).toFloat()); // Градиент ReLU
};
return { value: y, gradFunc };
});
Градиент с масштабированием
const scaledGrad = tf.customGrad((x, save) => {
const y = x.square();
save([x]);
const gradFunc = (dy, saved) => {
const [x] = saved;
return dy.mul(x.mul(2).mul(0.5)); // Масштабируем производную
};
return { value: y, gradFunc };
});
Эти примеры демонстрируют гибкость tf.customGrad для
создания операций с нестандартным поведением обратного
распространения.
tf.customGrad полностью совместим с автоматическим
дифференцированием. Это означает, что созданные с его помощью операции
можно комбинировать с обычными операциями tf.add,
tf.mul, tf.matMul и другими, и TensorFlow.js
корректно вычислит градиенты по всему графу.
Гибкость и контроль, которые предоставляет
tf.customGrad, делают его мощным инструментом для
разработчиков, которым требуется точная настройка процесса обратного
распространения ошибок, оптимизация нестандартных функций и эксперименты
с новыми методами обучения нейросетей.