Как спроектировать евклидово потерянный слой dlib dnn?

В газетеТочное разрешение изображения с использованием очень глубоких сверточных сетей«, они определяют функцию потерь как расстояние [high resolution image - low resolution image] == [true residual] а также [network output] == [predict residual], Цель функции потерь — минимизировать потери между истинным остатком и прогнозируемым остатком.

Точнее, целью является минимизация ((r-f(x))^2)/2
r — истинный остаток, f (x) — прогнозируемый остаток.

Ниже приводится реализация функции потерь

double compute_loss_value_and_gradient (
const tensor& input_tensor,
const_label_iterator truth,
SUBNET& sub
) const
{
DLIB_CASSERT(input_tensor.num_samples() != 0);

auto const &output_tensor = sub.get_output();
tensor& grad = sub.get_gradient_input();
DLIB_CASSERT(input_tensor.num_samples() == output_tensor.num_samples());
DLIB_CASSERT(grad.nc() == input_tensor.nc() && grad.nr() == input_tensor.nr());
DLIB_CASSERT(input_tensor.nr() == output_tensor.nr() &&
input_tensor.nc() == output_tensor.nc() &&
input_tensor.k() == output_tensor.k());

double loss = 0;
output_label_type update_gradient(grad.nr(), grad.nc());
update_gradient = 0;
for(long i = 0; i != output_tensor.num_samples(); ++i){
auto const predict_residual = image_plane(output_tensor, i);
DLIB_CASSERT(predict_residual.nr() == truth->nr() &&
predict_residual.nc() == truth->nc());
//I set the label(truth) as
//[high_res_img - low_res_img] == [truth residual_img].
auto const error_residual = *truth - predict_residual;
auto const eu_dist = sum(pointwise_multiply(error_residual, error_residual));
//after derivative, gradient of euclidean distance are
//input - reference, since output_tensor is a mini batch
//I sum all of them together
update_gradient += error_residual;
loss += eu_dist;
++truth;
}
//I take the average as the gradient value
update_gradient /= output_tensor.num_samples();
auto *grad_ptr = grad.host_write_only();
std::copy(&update_gradient(0), &update_gradient(0) + update_gradient.size(),
grad_ptr);

return loss / 2.0 / output_tensor.num_samples();
}

Однако эта функция потерь не работает хорошо (потери остаются на уровне 8,88 ~ 8,9 и, похоже, не сходятся), я полагаю, это связано с ошибкой обновления градиента.

Если вам нужны целые исходные коды, я поставлю их на Pastebin. Сетевая архитектура

template<typename SUBNET>
using con_net = relu<bn_con<con<64,3,3,1,1, SUBNET>>>;

using net_00 = loss_euclidean_dist<
relu<bn_con<con<1,3,3,1,1,
repeat<3, con_net,
con_net<
input<matrix<float>>
>>>>>>;

Бумага реализует сеть с помощью 20 слоев свертки и использует градиентный зажим, чтобы избежать проблемы градиентного взрыва / исчезновения, вместо этого я использую сеть 5 сверток для целей эксперимента и предпочитаю пакетную нормализацию, чтобы избежать градиентного взрыва / исчезновения.

параметры трейнера и оптимизатора

    dnn_trainer<vsdr::net_00, adam> trainer(net, adam(0.0001, 0.9, 0.999));
trainer.set_learning_rate(0.001);
trainer.set_min_learning_rate(1e-7);
trainer.set_mini_batch_size(128);

Редактировать 0: функция to_label никогда не звонил, когда тренируется.

Редактировать 1: я не применяю sqrt на расстоянии, потому что это может упростить производную

Редактировать 2: Нашел проблему Евклидова утерянного, неправильный способ обновления градиента.

double compute_loss_value_and_gradient (
const tensor& input_tensor,
const_label_iterator truth,
SUBNET& sub
) const
{
DLIB_CASSERT(input_tensor.num_samples() != 0);

auto const &output_tensor = sub.get_output();
tensor& grad = sub.get_gradient_input();
DLIB_CASSERT(input_tensor.num_samples() == output_tensor.num_samples());
DLIB_CASSERT(grad.nc() == input_tensor.nc() && grad.nr() == input_tensor.nr());
DLIB_CASSERT(input_tensor.nr() == output_tensor.nr() &&
input_tensor.nc() == output_tensor.nc() &&
input_tensor.k() == output_tensor.k());

double loss = 0;
for(long i = 0; i != output_tensor.num_samples(); ++i){

auto const error_residual = image_plane(output_tensor, i) - *truth;
auto const eu_dist = sum(pointwise_multiply(error_residual, error_residual));
grad.set_sample(i, error_residual);
loss += eu_dist;
++truth;
}

return loss / 2.0 / output_tensor.num_samples() / output_tensor.nc()/ output_tensor.nr();
}

Еще одна проблема — сетевая архитектура, она не должна добавлять relu поверх последнего уровня.

using net_00 = loss_euclidean_dist<
bn_con<con<1,3,3,1,1,
repeat<3, con_net,
con_net<
input<matrix<float>>
>>>>>;

Теперь тренировочный процесс выглядит намного лучше, но прогнозируемый остаток плох, psnr увеличится даже хуже, чем бикубический. Я пытаюсь удалить нормализацию партии и использую очень низкую скорость обучения (1e-5), чтобы избежать градиентного взрыва, но это также не может работать.

0

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]