Source code for orcanet_contrib.custom_objects

import tensorflow.keras.backend as K
import tensorflow as tf
import math


[docs]def get_custom_objects(): """ Functions that returns a dict with all relevant loss functions in this file. """ custom_objects = { 'loss_direction': loss_direction, 'loss_uncertainty_mse': loss_uncertainty_mse, 'loss_uncertainty_mae': loss_uncertainty_mae, 'loss_uncertainty_gaussian_likelihood': loss_uncertainty_gaussian_likelihood, 'loss_uncertainty_gaussian_likelihood_dir': loss_uncertainty_gaussian_likelihood_dir, 'loss_mean_relative_error_energy': loss_mean_relative_error_energy } return custom_objects
[docs]def loss_mean_relative_error_energy(y_true, y_pred): """ Loss function that calculates the mean relative error. y_true & y_pred are expected to be e_true & e_pred. L = (e_reco - e_true) / e_true Returns ------- mre : Mean relative (energy) error loss """ mre = K.abs(y_pred - y_true)/y_true return mre
[docs]def loss_uncertainty_mae(y_true, y_pred): """ Mean absolute error loss for the uncertainty estimation. L = sigma_pred / abs(label_true - label_reco). Returns ------- loss : Mean absolute error for uncertainty estimations. """ # order in y_pred: 1) pred label 2) pred label error # prevent that the gradient flows back over the label network: y_pred_label = K.stop_gradient(y_pred[:, 0]) y_pred_label_std = y_pred[:, 1] y_true_label = y_true[:, 0] # (s - |y_true - y_pred|) loss = K.abs(y_pred_label_std - K.abs(y_true_label - y_pred_label)) return loss
[docs]def loss_uncertainty_mse(y_true, y_pred): """ Mean squared error loss for the uncertainty estimation. L = sigma_pred / abs(label_true - label_reco)**2. Returns ------- loss : Mean squared error for uncertainty estimations. """ # order in y_pred: 1) pred label 2) pred label error # prevent that the gradient flows back over the label network: y_pred_label = K.stop_gradient(y_pred[:, 0]) y_pred_label_std = y_pred[:, 1] y_true_label = y_true[:, 0] # (s - |y_true - y_pred|)**2 loss = K.pow(y_pred_label_std - K.abs(y_true_label - y_pred_label), 2) return loss
[docs]def loss_uncertainty_gaussian_likelihood(y_true, y_pred): """ Loss function that calculates something similar to a Gaussian Likelihood. Requires that y_pred contains only one predicted value (label). y_true & y_pred are expected to contain the predicted/true label and the predicted std for the label. L = ln(std ** 2) + (y_label_pred - y_label_true) / (std ** 2) Returns ------- loss : Gaussian Likelihood loss """ # order in y_pred: 1) pred label 2) pred label error # prevent that the gradient flows back over the label network: y_pred_label = K.stop_gradient(y_pred[:, 0]) y_pred_label_std = y_pred[:, 1] y_true_label = y_true[:, 0] # equal to a lower std limit of 3.16 e-2 eps = tf.constant(1e-3, dtype="float32") # y_pred_label_std += eps loss = K.log(K.pow(y_pred_label_std, 2) + eps) + K.pow(y_pred_label - y_true_label, 2) / (K.pow(y_pred_label_std, 2) + eps) return loss
[docs]def loss_uncertainty_gaussian_likelihood_dir(y_true, y_pred): """ Loss function that calculates something similar to a Gaussian Likelihood for predicted directions. Requires that y_pred contains three predicted values (labels): dir_x, dir_y, dir_z. y_true & y_pred are expected to contain the predicted/true label and the predicted std for the label. L = ln(std ** 2) + (y_label_pred - y_label_true) / (std ** 2) Returns ------- loss : Gaussian Likelihood loss for the directional error """ # order in y_pred: 1) pred label 2) pred label error # prevent that the gradient flows back over the label network y_pred_dir_x, y_pred_dir_y, y_pred_dir_z = K.stop_gradient(y_pred[:, 0]), K.stop_gradient(y_pred[:, 1]), K.stop_gradient(y_pred[:, 2]) y_pred_std_dir_x, y_pred_std_dir_y, y_pred_std_dir_z = y_pred[:, 3], y_pred[:, 4], y_pred[:, 5] y_true_dir_x, y_true_dir_y, y_true_dir_z = y_true[:, 0], y_true[:, 1], y_true[:, 2] # equal to a lower std limit of 1e-3 eps = tf.constant(1e-6, dtype="float32") loss_dir_x = K.log(K.pow(y_pred_std_dir_x, 2) + eps) + K.pow(y_pred_dir_x - y_true_dir_x, 2) / (K.pow(y_pred_std_dir_x, 2) + eps) loss_dir_y = K.log(K.pow(y_pred_std_dir_y, 2) + eps) + K.pow(y_pred_dir_y - y_true_dir_y, 2) / (K.pow(y_pred_std_dir_y, 2) + eps) loss_dir_z = K.log(K.pow(y_pred_std_dir_z, 2) + eps) + K.pow(y_pred_dir_z - y_true_dir_z, 2) / (K.pow(y_pred_std_dir_z, 2) + eps) loss = loss_dir_x + loss_dir_y + loss_dir_z return loss
[docs]def loss_direction(y_true, y_pred): """ Loss function that calculates the space angle between the predicted and the true direction. Not used anymore, can lead to inf gradients due to tf.acos(space_angle_inner_value)! Converts cartesian dirs to spherical coordinate system and then calculates the space angle between the two vectors. Returns ------- total_loss : Space angle loss """ # define dir_preds y_pred = tf.Print(y_pred, [y_pred], message='y_pred', summarize=300) y_pred_x, y_pred_y, y_pred_z = y_pred[:, 0], y_pred[:, 1], y_pred[:, 2] y_true_x, y_true_y, y_true_z = y_true[:, 0], y_true[:, 1], y_true[:, 2] # convert cartesian coordinates to spherical coordinates r_pred = K.sqrt(K.pow(y_pred_x, 2) + K.pow(y_pred_y, 2) + K.pow(y_pred_z, 2)) # y_true input should be normalized! can also use 1 instead of this: r_true = K.sqrt(K.pow(y_true_x, 2) + K.pow(y_true_y, 2) + K.pow(y_true_z, 2)) eps = tf.constant(1e-7, dtype="float32") # TODO test # alternatively zenith_pred = tf.acos(y_pred_z / r_pred + K.epsilon()): zenith_pred = tf.atan2(y_pred_z + eps, K.sqrt(K.pow(y_pred_x, 2) + K.pow(y_pred_y, 2)) + eps) zenith_true = tf.atan2(y_true_z + eps, K.sqrt(K.pow(y_true_x, 2) + K.pow(y_true_y, 2)) + eps) azimuth_pred, azimuth_true = tf.atan2(y_pred_y + eps, y_pred_x + eps), tf.atan2(y_true_y + eps, y_true_x + eps) # shift azimuth and zenith by pi / pi/2 in order to make the space angle formula work pi = math.pi zenith_pred, zenith_true = zenith_pred + tf.constant(pi/float(2), dtype="float32"), zenith_true + tf.constant(pi/float(2), dtype="float32") azimuth_pred, azimuth_true = azimuth_pred + tf.constant(pi, dtype="float32"), azimuth_true + tf.constant(pi, dtype="float32") # calculate space angle between the two vectors (true, pred) in spherical coordinates, cf. bachelor thesis shallmann # protect space angle from acos values outside of [-1,1] range space_angle_inner_value = tf.sin(zenith_true) * tf.sin(zenith_pred) * tf.cos(azimuth_true - azimuth_pred) + tf.cos(zenith_true) * tf.cos(zenith_pred) space_angle_inner_value = K.clip(space_angle_inner_value, -1, 1) space_angle = tf.acos(space_angle_inner_value) loss_r = K.abs(r_true - r_pred) total_loss = loss_r + space_angle # total_loss = tf.Print(total_loss, [total_loss], message='total_loss', summarize=64) return total_loss
[docs]def loss_direction_grad(): """ Similar loss function as in loss_direction, but here it is just used for manually calculating the gradient in the main function. Done in order to protect the loss_direction function from inf gradients. """ # define dir_preds y_true = np.array([[1, 0, 0], [0, 0, 1]]) y_true = tf.constant(y_true, shape=y_true.shape, dtype='float32') y_pred = np.array([[-1, 0, 0], [0, 0, -1]]) y_pred = tf.Variable(y_pred, dtype='float32') y_pred_x, y_pred_y, y_pred_z = y_pred[:, 0], y_pred[:, 1], y_pred[:, 2] y_true_x, y_true_y, y_true_z = y_true[:, 0], y_true[:, 1], y_true[:, 2] eps = tf.constant(1e-7, dtype="float32") # TODO test # alternatively zenith_pred = tf.acos(y_pred_z / r_pred + K.epsilon()): zenith_pred = tf.atan2(y_pred_z + eps, K.sqrt(K.pow(y_pred_x, 2) + K.pow(y_pred_y, 2)) + eps) zenith_true = tf.atan2(y_true_z + eps, K.sqrt(K.pow(y_true_x, 2) + K.pow(y_true_y, 2)) + eps) azimuth_pred, azimuth_true = tf.atan2(y_pred_y + eps, y_pred_x + eps), tf.atan2(y_true_y + eps, y_true_x + eps) # shift azimuth and zenith by pi / pi/2 in order to make the space angle formula work pi = math.pi zenith_pred, zenith_true = zenith_pred + tf.constant(pi/float(2), dtype="float32"), zenith_true + tf.constant(pi/float(2), dtype="float32") azimuth_pred, azimuth_true = azimuth_pred + tf.constant(pi, dtype="float32"), azimuth_true + tf.constant(pi, dtype="float32") # calculate space angle between the two vectors (true, pred) in spherical coordinates, cf. bachelor thesis shallmann # protect space angle from acos values outside of [-1,1] range space_angle_inner_value = tf.sin(zenith_true) * tf.sin(zenith_pred) * tf.cos(azimuth_true - azimuth_pred) + tf.cos(zenith_true) * tf.cos(zenith_pred) space_angle_inner_value = K.clip(space_angle_inner_value, -1, 1) space_angle = tf.acos(space_angle_inner_value) return space_angle, y_pred
[docs]def mean_absolute_error(y_true, y_pred): """ Copy of the Keras mean absolute error function for testing purposes. """ # y_pred = tf.Print(y_pred, [y_pred], message='y_pred', summarize=5) # y_true = tf.Print(y_true, [y_true], message='y_true', summarize=5) absolute = K.abs(y_pred - y_true) # absolute = tf.Print(absolute, [absolute], message='absolute', summarize=5) mae = K.mean(absolute, axis=-1) return mae
if __name__ == "__main__": import numpy as np with tf.Session() as sess:
[docs] dir_y_true = np.array([[0.4, 0.1, 0.5], [0.2, -0.4, 0.4], [1, 1, 1], [0.4, 0.2, 0.2], [0.1, 0.2, 0.5]])
dir_y_pred = np.array([[0.35, 0.05, 0.6], [0.15, -0.3, 0.55], [-1, -1, -1], [-0.4, -0.2, -0.2], [0, 0, 0]]) x = tf.constant(dir_y_true, shape=dir_y_true.shape, dtype="float32") y = tf.constant(dir_y_pred, shape=dir_y_pred.shape, dtype="float32") # print('Final loss dir: ' + str(sess.run(loss_direction(x,y)))) print('Final loss dir: ' + str(sess.run(mean_absolute_error(x, y)))) # calc gradients for loss_direction space_angle_loss_func, y_pred_p = loss_direction_grad() init = tf.global_variables_initializer() sess.run(init) print('Final loss gradients dir: ' + str(sess.run(tf.gradients(space_angle_loss_func, y_pred_p)))) # errors energy_y_true = np.array([[30], [6.35]]) energy_and_std_y_pred = np.array([[25, 5], [5.84, 0.5]]) x = tf.constant(energy_y_true, shape=energy_y_true.shape, dtype="float32") y = tf.constant(energy_and_std_y_pred, shape=energy_and_std_y_pred.shape, dtype="float32") print('Final loss energy uncertainty: ' + str(sess.run(loss_uncertainty_gaussian_likelihood(x, y)))) dir_y_true = np.array([[0.4, 0.1, 0.5], [0.2, -0.4, 0.4], [1, 1, 1]]) dir_and_std_y_pred = np.array([[0.35, 0.05, 0.6, 0.05, 0.05, 0.05], [0.15, -0.3, 0.55, 0.05, 0.1, 0.15], [-1, -1, -1, 2, 2, 2]]) x = tf.constant(dir_y_true, shape=dir_y_true.shape, dtype="float32") y = tf.constant(dir_and_std_y_pred, shape=dir_and_std_y_pred.shape, dtype="float32") print('Final loss dir uncertainty: ' + str(sess.run(loss_uncertainty_gaussian_likelihood_dir(x, y))))