def test_missing_class(self): with self.test_session(): predicted = tf.constant([[0, 0, 1], [1, 0, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([2, 0], dtype=tf.int32) predicted, labels = [ tf.expand_dims(x, axis=0) for x in (predicted, labels) ] for loss_func in ('Dice', 'Dice_NS', 'Dice_Dense', 'Dice_Dense_NS'): test_loss_func = LossFunction(3, loss_type=loss_func, softmax=False) if 'Dense' in loss_func: loss_value = test_loss_func(predicted, tf.one_hot(labels, 3)) else: loss_value = test_loss_func(predicted, labels) # softmax of zero, Dice loss of -1, so sum \approx -1 self.assertAllClose(loss_value.eval(), 0.0, atol=1e-4)
def test_wrong_prediction(self): with self.test_session(): predicted = tf.constant([[0, 100]], dtype=tf.float32, name='predicted') labels = tf.constant([0], dtype=tf.int64, name='labels') test_loss_func = LossFunction(2, loss_type='Dice') one_minus_dice_score = test_loss_func(predicted, labels) self.assertAlmostEqual(one_minus_dice_score.eval(), 1.0)
def test_dice_score(self): with self.test_session(): predicted = tf.constant([[0, 10], [10, 0], [10, 0], [10, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([1, 0, 0, 0], dtype=tf.int64, name='labels') test_loss_func = LossFunction(2, loss_type='Dice') one_minus_dice_score = test_loss_func(predicted, labels) self.assertAllClose(one_minus_dice_score.eval(), 0.0, atol=1e-5)
def test_sens_spec_loss_by_regression(self): with self.test_session(): predicted = tf.constant([[0, 10], [10, 0], [10, 0], [10, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([1, 0, 0, 0], dtype=tf.int64, name='labels') test_loss_func = LossFunction(2, loss_type='SensSpec') test_loss = test_loss_func(predicted, labels) self.assertAlmostEqual(test_loss.eval(), 2.06106e-9)
def test_dense_dice_nosquare_vs_sparse(self): # regression test vs dense version with self.cached_session(): predicted = tf.constant( [[2, 3], [9, 8], [0, 0], [1, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([1, 0, 0, 0], dtype=tf.int64, name='labels') predicted, labels = [tf.expand_dims(x, axis=0) for x in (predicted, labels)] sparse_loss_func = LossFunction(2, loss_type='Dice_NS') sparse_dice = sparse_loss_func(predicted, labels) one_hot = tf.one_hot(labels, axis=-1, depth=2) dense_loss_func = LossFunction(2, loss_type='Dice_Dense_NS') dense_dice = dense_loss_func(predicted, one_hot) self.assertAllEqual(sparse_dice.eval(), dense_dice.eval())
def test_volume_enforcement_nonexist(self): with self.cached_session(): predicted = tf.constant([[1000, -1000], [1000, -1000], [1000, -1000], [1000, -1000]], dtype=tf.float32, name='predicted') labels = tf.constant([1, 0, 0, 0], dtype=tf.int64, name='labels') predicted, labels = [tf.expand_dims(x, axis=0) for x in (predicted, labels)] venf_loss_func = LossFunction(2, loss_type='VolEnforcement') venf_loss = venf_loss_func(predicted, labels) self.assertAllClose(venf_loss.eval(), 500.75, atol=0.1)
def test_wrong_prediction(self): with self.cached_session(): predicted = tf.constant( [[0, 100]], dtype=tf.float32, name='predicted') labels = tf.constant([0], dtype=tf.int64, name='labels') predicted, labels = [tf.expand_dims(x, axis=0) for x in (predicted, labels)] test_loss_func = LossFunction(2, loss_type='Dice_NS') one_minus_dice_score = test_loss_func(predicted, labels) self.assertAllClose(one_minus_dice_score.eval(), 1.0, atol=1e-4)
def test_dice_plus_weighted(self): with self.cached_session(): predicted = tf.constant( [[0, 9999, 9999], [9999, 0, 0], [0, 9999, 9999], [9999, 0, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([2, 0, 1, 0], dtype=tf.int16, name='labels') weights = tf.expand_dims(tf.constant([0, 1, 0, 0], dtype=tf.float32), axis=0) predicted, labels = [tf.expand_dims(x, axis=0) for x in (predicted, labels)] test_loss_func = LossFunction(3, loss_type='DicePlusXEnt', softmax=True) loss_value = test_loss_func(predicted, labels, weight_map=weights) self.assertAllClose(loss_value.eval(), -1.)
def test_wrong_prediction(self): with self.test_session(): predicted = tf.constant( [[0, 100]], dtype=tf.float32, name='predicted') labels = tf.constant([0], dtype=tf.int64, name='labels') predicted, labels = [tf.expand_dims(x, axis=0) for x in (predicted, labels)] one_hot = tf.one_hot(labels, axis=-1, depth=2) test_loss_func = LossFunction(2, loss_type='Dice_Dense') one_minus_dice_score = test_loss_func(predicted, one_hot) self.assertAlmostEqual(one_minus_dice_score.eval(), 1.0)
def test_dice_plus_multilabel(self): with self.cached_session(): predicted = tf.constant( [[0, 0, 9999], [9999, 0, 0], [0, 9999, 0], [9999, 0, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([2, 0, 1, 0], dtype=tf.int16, name='labels') predicted, labels = [tf.expand_dims(x, axis=0) for x in (predicted, labels)] test_loss_func = LossFunction(3, loss_type='DicePlusXEnt', softmax=False) loss_value = test_loss_func(predicted, labels) # cross-ent of zero, Dice loss of -1, so sum \approx -1 self.assertAllClose(loss_value.eval(), -1.0, atol=1e-3)
def test_generalised_dice_score_regression(self): with self.test_session(): predicted = tf.constant([[0, 10], [10, 0], [10, 0], [10, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([1, 0, 0, 0], dtype=tf.int64, name='labels') weights = tf.cast(labels, tf.float32) test_loss_func = LossFunction(2, loss_type='GDSC') one_minus_generalised_dice_score = test_loss_func( predicted, labels, weights) self.assertAlmostEqual(one_minus_generalised_dice_score.eval(), 0.92424583)
def test_multi_label_sens_spec(self): with self.test_session(): # answer calculated by hand - predicted = tf.constant([[0, 1, 0], [0, 0, 1]], dtype=tf.float32, name='predicted') labels = tf.constant([1, 2], dtype=tf.int64, name='labels') test_loss_func = LossFunction(3, loss_type='SensSpec', loss_func_params={'r': 0.05}) test_loss = test_loss_func(predicted, labels) self.assertAlmostEqual(test_loss.eval(), 0.14598623)
def test_cross_entropy_value(self): # test value is -0.5 * [1 * log(e / (1+e)) + 1 * log(e^2 / (e^2 + 1))] with self.test_session(): predicted = tf.constant([[0, 1], [2, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([1, 0], dtype=tf.int64, name='labels') test_loss_func = LossFunction(2, loss_type='CrossEntropy') computed_cross_entropy = test_loss_func(predicted, labels) self.assertAlmostEqual( computed_cross_entropy.eval(), -.5 * (np.log(np.e / (1 + np.e)) + np.log(np.e**2 / (1 + np.e**2))))
def test_dice_plus_wrong_softmax(self): with self.cached_session(): predicted = tf.constant( [[0, 9999, 9999], [9999, 0, 0], [0, 9999, 9999], [9999, 0, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([2, 0, 1, 0], dtype=tf.int16, name='labels') predicted, labels = [tf.expand_dims(x, axis=0) for x in (predicted, labels)] test_loss_func = LossFunction(3, loss_type='DicePlusXEnt', softmax=True) loss_value = test_loss_func(predicted, labels) # cross-ent of mean(ln(2), 0, 0, ln(2)) = .5*ln(2) # Dice loss of -mean(1, .5, .5)=-2/3 self.assertAllClose(loss_value.eval(), .5 * np.log(2) - 2. / 3., atol=1e-3)
def test_wrong_prediction(self): with self.cached_session(): predicted = tf.constant( [[0, 100]], dtype=tf.float32, name='predicted') labels = tf.constant([0], dtype=tf.int64, name='labels') predicted, labels = [tf.expand_dims(x, axis=0) for x in (predicted, labels)] test_loss_func = LossFunction(2, loss_type='Tversky') one_minus_tversky_index = test_loss_func(predicted, labels) self.assertAlmostEqual(one_minus_tversky_index.eval(), 1.0)
def test_dice_dense_score(self): with self.cached_session(): predicted = tf.constant( [[0, 10], [10, 0], [10, 0], [10, 0]], dtype=tf.float32, name='predicted') one_hot = tf.constant([[1, 0], [0, 1], [0, 1], [0, 1]], dtype=tf.int64, name='one_hot') predicted, one_hot = [tf.expand_dims(x, axis=0) for x in (predicted, one_hot)] test_loss_func = LossFunction(2, loss_type='Dice_Dense') one_minus_dice_score = test_loss_func(predicted, one_hot) self.assertAllClose(one_minus_dice_score.eval(), 1.0, atol=1e-4)
def test_tversky_index(self): with self.cached_session(): predicted = tf.constant( [[0, 10], [10, 0], [10, 0], [10, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([1, 0, 0, 0], dtype=tf.int64, name='labels') predicted, labels = [tf.expand_dims(x, axis=0) for x in (predicted, labels)] test_loss_func = LossFunction(2, loss_type='Tversky') one_minus_tversky_index = test_loss_func(predicted, labels) self.assertAllClose(one_minus_tversky_index.eval(), 0.0, atol=1e-4)
def test_generalised_dice_score_regression(self): with self.cached_session(): predicted = tf.constant( [[0, 10], [10, 0], [10, 0], [10, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([1, 0, 0, 0], dtype=tf.int64, name='labels') predicted, labels = [tf.expand_dims(x, axis=0) for x in (predicted, labels)] test_loss_func = LossFunction(2, loss_type='GDSC') one_minus_generalised_dice_score = test_loss_func( predicted, labels) self.assertAllClose( one_minus_generalised_dice_score.eval(), 0.0, atol=1e-4)
def connect_data_and_network(self, outputs_collector=None, gradients_collector=None): data_dict = self.get_sampler()[0][0].pop_batch_op() image = tf.cast(data_dict['image'], tf.float32) net_out = self.net(image, self.is_training) if self.is_training: with tf.name_scope('Optimiser'): self.learning_rate = tf.placeholder(tf.float64, shape=[]) optimiser_class = OptimiserFactory.create( name=self.action_param.optimiser) self.optimiser = optimiser_class.get_instance( learning_rate=self.learning_rate) loss_func = LossFunction( n_class=self.segmentation_param.num_classes, loss_type=self.action_param.loss_type) data_loss = loss_func(prediction=net_out, ground_truth=data_dict.get('label', None), weight_map=data_dict.get('weight', None)) self.current_loss = data_loss loss = data_loss reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES) if self.net_param.decay > 0.0 and reg_losses: reg_loss = tf.reduce_mean( [tf.reduce_mean(reg_loss) for reg_loss in reg_losses]) loss = data_loss + reg_loss grads = self.optimiser.compute_gradients(loss) # collecting gradients variables gradients_collector.add_to_collection([grads]) # collecting output variables outputs_collector.add_to_collection(var=self.current_loss, name='loss', average_over_devices=False, collection=CONSOLE) outputs_collector.add_to_collection(var=self.learning_rate, name='lr', average_over_devices=False, collection=CONSOLE) outputs_collector.add_to_collection(var=data_loss, name='dice_loss', average_over_devices=True, summary_type='scalar', collection=TF_SUMMARIES) else: # converting logits into final output for # classification probabilities or argmax classification labels SegmentationApplication.connect_data_and_network( self, outputs_collector, gradients_collector)
def test_cross_entropy_value(self): # test value is -0.5 * [1 * log(e / (1+e)) + 1 * log(e^2 / (e^2 + 1))] with self.cached_session(): predicted = tf.constant( [[0, 1], [2, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([1, 0], dtype=tf.int64, name='labels') predicted, labels = [tf.expand_dims(x, axis=0) for x in (predicted, labels)] test_loss_func = LossFunction(2, loss_type='CrossEntropy') computed_cross_entropy = test_loss_func(predicted, labels) self.assertAlmostEqual( computed_cross_entropy.eval(), -.5 * (np.log(np.e / (1 + np.e)) + np.log( np.e ** 2 / (1 + np.e ** 2)))) test_dense_loss = LossFunction(2, loss_type='CrossEntropy_Dense') labels = tf.sparse_tensor_to_dense(labels_to_one_hot(labels, 2)) computed_cross_entropy = test_loss_func(predicted, tf.to_int32(labels)) self.assertAlmostEqual( computed_cross_entropy.eval(), -.5 * (np.log(np.e / (1 + np.e)) + np.log( np.e ** 2 / (1 + np.e ** 2))))
def test_dice_score_weights(self): with self.cached_session(): weights = tf.constant([[1, 1, 0, 0]], dtype=tf.float32, name='weights') predicted = tf.constant( [[0, 10], [10, 0], [10, 0], [10, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([[1, 0, 0, 0]], dtype=tf.int64, name='labels') predicted, labels, weights = [tf.expand_dims(x, axis=0) for x in (predicted, labels, weights)] test_loss_func = LossFunction(2, loss_type='Dice') one_minus_dice_score = test_loss_func(predicted, labels, weight_map=weights) self.assertAllClose(one_minus_dice_score.eval(), 0.0, atol=1e-4)
def test_gdsc_incorrect_type_weight_error(self): with self.test_session(): with self.assertRaises(ValueError) as cm: predicted = tf.constant([[0, 10], [10, 0], [10, 0], [10, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([1, 0, 0, 0], dtype=tf.int64, name='labels') test_loss_func = LossFunction( 2, loss_type='GDSC', loss_func_params={'type_weight': 'unknown'}) one_minus_generalised_dice_score = test_loss_func( predicted, labels)
def test_cross_entropy_value_weight(self): with self.test_session(): weights = tf.constant([[1], [2]], dtype=tf.float32, name='weights') predicted = tf.constant([[0, 1], [2, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([[1], [0]], dtype=tf.int64, name='labels') predicted, labels, weights = \ [tf.expand_dims(x, axis=0) for x in (predicted, labels, weights)] test_loss_func = LossFunction(2, loss_type='CrossEntropy') computed_cross_entropy = test_loss_func(predicted, labels, weights) self.assertAlmostEqual( computed_cross_entropy.eval(), -.5 * (2.0 / 3.0 * np.log(np.e / (1 + np.e)) + 4.0 / 3.0 * np.log(np.e**2 / (1 + np.e**2))))
def test_dice_batch_size_greater_than_one(self): # test for Github issue #22: need to take mean per-image before # averaging Dice of ~1 and ~0.16, should get dice ~ 1 - 0.5816 with self.cached_session(): # predictions ~ [1, 0, 0]; [0, 0, 1]; [0, .5, .5]; [.333, .333, .333] predictions_numpy = np.array([[[10., 0, 0], [0, 0, 10]], [[-10, 0, 0], [0, 0, 0]]]).reshape([2, 2, 1, 1, 3]) labels_numpy = np.array([[[0, 2]], [[0, 1]]]).reshape([2, 2, 1, 1, 1]) predicted = tf.constant(predictions_numpy, dtype=tf.float32, name='predicted') labels = tf.constant(labels_numpy, dtype=tf.int64, name='labels') test_loss_func = LossFunction(3, loss_type='Dice') one_minus_dice_score = test_loss_func(predicted, labels) self.assertAllClose(one_minus_dice_score.eval(), 1 - 0.5816, atol=1e-4)
def test_generalised_dice_score_uniform_regression(self): with self.cached_session(): predicted = tf.constant([[0, 10], [10, 0], [10, 0], [10, 0]], dtype=tf.float32, name='predicted') labels = tf.constant([[1, 0, 0, 0]], dtype=tf.int64, name='labels') weights = tf.cast(labels, tf.float32) predicted, labels, weights = [tf.expand_dims(x, axis=0) for x in (predicted, labels, weights)] test_loss_func = LossFunction( 2, loss_type='GDSC', loss_func_params={'type_weight': 'Uniform'}) one_minus_generalised_dice_score = test_loss_func( predicted, labels, weights) self.assertAllClose(one_minus_generalised_dice_score.eval(), 0.3333, atol=1e-4)
def train(config_file): # 1, load configuration parameters config = parse_config(config_file) config_data = config['data'] config_net = config['network'] config_train = config['training'] random.seed(config_train.get('random_seed', 1)) assert (config_data['with_ground_truth']) net_type = config_net['net_type'] net_name = config_net['net_name'] class_num = config_net['class_num'] batch_size = config_data.get('batch_size', 5) # 2, construct graph full_data_shape = [batch_size] + config_data['data_shape'] full_label_shape = [batch_size] + config_data['label_shape'] x = tf.placeholder(tf.float32, shape=full_data_shape) w = tf.placeholder(tf.float32, shape=full_label_shape) y = tf.placeholder(tf.int64, shape=full_label_shape) w_regularizer = regularizers.l2_regularizer(config_train.get( 'decay', 1e-7)) b_regularizer = regularizers.l2_regularizer(config_train.get( 'decay', 1e-7)) net_class = NetFactory.create(net_type) net = net_class(num_classes=class_num, w_regularizer=w_regularizer, b_regularizer=b_regularizer, name=net_name) net.set_params(config_net) predicty = net(x, is_training=True) proby = tf.nn.softmax(predicty) loss_func = LossFunction(n_class=class_num) loss = loss_func(predicty, y, weight_map=w) print('size of predicty:', predicty) # 3, initialize session and saver lr = config_train.get('learning_rate', 1e-3) opt_step = tf.train.AdamOptimizer(lr).minimize(loss) tf.summary.FileWriter("./graphs/" + config_net['net_name'], tf.get_default_graph()).close()
def adv_image_dynamic_shape(temp_imgs, data_shape, label_shape, data_channel, class_num, batch_size, sess, net): ''' Create one adversarial image with sub regions along z-axis The height and width of input tensor is adapted to those of the input image ''' # construct graph [D, H, W] = temp_imgs[0].shape Hx = max(int((H+3)/4)*4, data_shape[1]) Wx = max(int((W+3)/4)*4, data_shape[2]) data_slice = data_shape[0] label_slice = label_shape[0] full_data_shape = [batch_size, data_slice, Hx, Wx, data_channel] x = tf.placeholder(tf.float32, full_data_shape) predicty = net(x, is_training = True) proby = tf.nn.softmax(predicty) preds_max = reduce_max(predicty, 1, keepdims=True) y = tf.to_float(tf.equal(predicty, preds_max)) y = tf.stop_gradient(y) y = y / reduce_sum(y, 1, keepdims=True) # Create adversarial attack loss_func = LossFunction(n_class=class_num) loss = loss_func(predicty, y) fgsm = FastGradientMethod(net) adv_steps = 2 fgsm_params = {'eps': 0.4/adv_steps, 'loss_func': loss} adv_x = fgsm.generate(x, **fgsm_params) new_data_shape = [data_slice, Hx, Wx] new_label_shape = [label_slice, Hx, Wx] print("Running adversarial attack with %d steps" % adv_steps) for i in range(adv_steps): temp_imgs = volume_probability_prediction(temp_imgs, new_data_shape, new_label_shape, data_channel, class_num, batch_size, sess, adv_x, x) return temp_imgs
def test_suggestion_for_dice_typo(self): with self.cached_session(): with self.assertRaisesRegexp(ValueError, 'Dice'): LossFunction(1, loss_type='dice')
def test_suggestion_for_gdsc_typo(self): with self.cached_session(): with self.assertRaisesRegexp(ValueError, 'GDSC'): LossFunction(1, loss_type='GSDC')
def test_value_error_for_bad_loss_function(self): with self.cached_session(): with self.assertRaises(ValueError): LossFunction(1, loss_type='wrong answer')