В настоящее время я работаю над проектом, в котором мне нужно добавить пользовательскую функцию потерь в API C ++ tenorflow (1.13.1). Эта функция опирается на некоторые внешние библиотеки, поэтому моя идея заключалась в том, чтобы добавить эту функцию потерь в качестве пользовательской операции. Я пытался следовать этому руководству https://www.tensorflow.org/guide/create_op, но самая важная часть о добавлении градиента в C ++ отсутствует. Итак, вот что я сделал до сих пор:
Я начал с регистрации двух новых операций в nn_ops.cc
, одного для функции потерь MyLoss
и другого для вычисления градиента MyLossGrad
. На следующем шаге я реализовал обе операции в myloss_op.cc
, следуя руководству по тензорному потоку.
Моя проблема сейчас: как реализовать соответствующий градиент? Я зарегистрировал его в nn_grad.cc
и передал предсказания и метки целей на MyLoss
op:
Status MyLossGrad(const Scope& scope, const Operation& op,
const std::vector<Output>& grad_inputs,
std::vector<Output>* grad_outputs) {
auto y_pred = op.input(0); // Predictions passed to MyLoss Op
auto y_true = op.input(1); // Target labels passed to MyLoss Op
auto my_loss = op.output(0); // Calculated Loss from my MyLoss Op
// Do I need these?
auto grad_loss = grad_inputs[0];
auto grad_grad = grad_inputs[1];
// Calculate and pass gradient
auto dx = internal::MyLossGrad(scope, y_pred , y_true);
grad_outputs->push_back(dx);
grad_outputs->push_back(NoGradient());
return scope.status();
}
REGISTER_GRADIENT_OP("MyLoss", MyLossGrad);
Intuition
Это та часть, где я ' Я не уверен, как продолжить. Если я правильно понял, мне нужно рассчитать два градиента, так как у меня есть два входа:
- dL / dx -> Моя функция потерь дифференцирована относительно. мои прогнозы
- dL / dy -> Моя функция потерь дифференцирована по сравнению с метки цели
Затем оба градиента передаются в grad_outputs
. Насколько я понимаю, градиент dL / dy не нужен, поэтому вместо него я передаю NoGradient
.
Вопросы:
- Верна ли моя интуиция сверху?
- Для чего используются другие параметры?
- Что такое grad_inputs? Некоторые другие Ops умножают это с их градиентами? Это необходимо для обратного распространения?
Я не знаю, имеет ли это значение, но эта операция предназначена для использования только в качестве функции потерь, а не в качестве внутреннего узла графа.
Вот небольшой фрагмент, как я использую операционную систему и обучаю свою сеть:
// Build Network:
auto input_x = Placeholder(t_scope.NewSubScope("Input_X"), DT_FLOAT, Placeholder::Shape({bs, x, y, z, c}));
auto input_y = Placeholder(t_scope.NewSubScope("Input_Y"), DT_FLOAT, Placeholder::Shape({bs, x, y, z, c}));
TensorShape filter_size({3, 3, 3, 1, 5});
c_var = Variable(layer_scope.WithOpName("c_var"), filter_size, DT_FLOAT);
c_assign = Assign(layer_scope.WithOpName("c_assign"), c_var , RandomNormal(layer_scope, {3, 3, 3, 1, 5}, DT_FLOAT));
auto conv = Conv3D(t_scope.NewSubScope("Conv"), input_x, c_var, gtl::ArraySlice<int>{1, 1, 1, 1, 1}, "SAME");
// Build Optimization Graph:
auto loss = MyLoss(t_scope.NewSubScope("MyLoss"), conv, input_y);
vector<Output> grad_outputs;
TF_CHECK_OK(AddSymbolicGradients(t_scope, {loss}, {c_var}, &grad_outputs));
auto m_var = Variable(t_scope, m_shapes[i], DT_FLOAT);
auto v_var = Variable(t_scope, m_shapes[i], DT_FLOAT);
auto m_assign = Assign(t_scope, m_var, Input::Initializer(0.f, filter_size)));
auto v_assign = Assign(t_scope, v_var, Input::Initializer(0.f, filter_size)));
auto adam = ApplyAdam(t_scope, c_var, m_var, v_var, 0.f, 0.f, 0.001f, 0.9f, 0.999f, 0.00000001f, {grad_outputs[0]});
out_grads.push_back(adam.operation);
// Initialize:
auto t_session = unique_ptr<ClientSession>(new ClientSession(t_scope));
TF_CHECK_OK(t_session->Run({c_assign, m_assign, v_assign}, nullptr));
// Train:
std::vector<Tensor> outputs;
TF_CHECK_OK(t_session->Run(
{{input_x, x_tensor}, {input_y, y_tensor}},
{loss}, out_grads, &outputs));