Skip to content
Snippets Groups Projects
Commit 75a706f1 authored by Tim O'Donnell's avatar Tim O'Donnell
Browse files

add support for smooth maximum

parent 490bcb45
No related branches found
No related tags found
No related merge requests found
from __future__ import print_function
import time import time
import collections import collections
from functools import partial
import numpy import numpy
...@@ -43,7 +46,8 @@ class Class1LigandomePredictor(object): ...@@ -43,7 +46,8 @@ class Class1LigandomePredictor(object):
""" """
compile_hyperparameter_defaults = HyperparameterDefaults( compile_hyperparameter_defaults = HyperparameterDefaults(
loss="custom:mse_with_inequalities", loss_delta=0.2,
loss_alpha=None,
optimizer="rmsprop", optimizer="rmsprop",
learning_rate=None, learning_rate=None,
) )
...@@ -146,18 +150,26 @@ class Class1LigandomePredictor(object): ...@@ -146,18 +150,26 @@ class Class1LigandomePredictor(object):
return network return network
@staticmethod @staticmethod
def loss(y_true, y_pred, delta=0.2): def loss(y_true, y_pred, delta=0.2, alpha=None):
""" """
Loss function for ligandome prediction. Loss function for ligandome prediction.
""" """
import tensorflow as tf import tensorflow as tf
import keras.backend as K
y_pred = tf.squeeze(y_pred, axis=-1) y_pred = tf.squeeze(y_pred, axis=-1)
y_true = tf.reshape(tf.cast(y_true, tf.bool), (-1,)) y_true = tf.reshape(tf.cast(y_true, tf.bool), (-1,))
pos = tf.boolean_mask(y_pred, y_true) pos = tf.boolean_mask(y_pred, y_true)
pos_max = tf.reduce_max(pos, axis=1)
if alpha is None:
pos_max = tf.reduce_max(pos, axis=1)
else:
# Smooth maximum
exp_alpha_x = tf.exp(alpha * pos)
numerator = tf.reduce_sum(tf.multiply(pos, exp_alpha_x), axis=1)
denominator = tf.reduce_sum(exp_alpha_x, axis=1)
pos_max = numerator / denominator
neg = tf.boolean_mask(y_pred, tf.logical_not(y_true)) neg = tf.boolean_mask(y_pred, tf.logical_not(y_true))
result = tf.reduce_sum( result = tf.reduce_sum(
tf.maximum(0.0, tf.reshape(neg, (-1, 1)) - pos_max + delta) ** 2) tf.maximum(0.0, tf.reshape(neg, (-1, 1)) - pos_max + delta) ** 2)
...@@ -246,7 +258,10 @@ class Class1LigandomePredictor(object): ...@@ -246,7 +258,10 @@ class Class1LigandomePredictor(object):
self.set_allele_representations(allele_representations) self.set_allele_representations(allele_representations)
self.network.compile( self.network.compile(
loss=self.loss, loss=partial(
self.loss,
delta=self.hyperparameters['loss_delta'],
alpha=self.hyperparameters['loss_alpha']),
optimizer=self.hyperparameters['optimizer']) optimizer=self.hyperparameters['optimizer'])
if self.hyperparameters['learning_rate'] is not None: if self.hyperparameters['learning_rate'] is not None:
K.set_value( K.set_value(
......
...@@ -109,54 +109,74 @@ def evaluate_loss(loss, y_true, y_pred): ...@@ -109,54 +109,74 @@ def evaluate_loss(loss, y_true, y_pred):
def test_loss(): def test_loss():
delta = 0.4 for delta in [0.0, 0.3]:
for alpha in [None, 1.0, 20.0]:
# Hit labels print("delta", delta)
y_true = [ print("alpha", alpha)
1.0, # Hit labels
0.0, y_true = [
1.0, 1.0,
1.0, 0.0,
0.0 1.0,
] 1.0,
y_true = numpy.array(y_true) 0.0
y_pred = [ ]
[0.3, 0.7, 0.5], y_true = numpy.array(y_true)
[0.2, 0.4, 0.6], y_pred = [
[0.1, 0.5, 0.3], [0.3, 0.7, 0.5],
[0.1, 0.7, 0.1], [0.2, 0.4, 0.6],
[0.8, 0.2, 0.4], [0.1, 0.5, 0.3],
] [0.1, 0.7, 0.1],
y_pred = numpy.array(y_pred) [0.8, 0.2, 0.4],
]
# reference implementation 1 y_pred = numpy.array(y_pred)
contributions = []
for i in range(len(y_true)): # reference implementation 1
if y_true[i] == 1.0:
for j in range(len(y_true)): def smooth_max(x, alpha):
if y_true[j] == 0.0: x = numpy.array(x)
tightest_i = max(y_pred[i]) alpha = numpy.array([alpha])
contribution = sum( return (x * numpy.exp(x * alpha)).sum() / (
max(0, y_pred[j, k] - tightest_i + delta)**2 numpy.exp(x * alpha)).sum()
for k in range(y_pred.shape[1])
) if alpha is None:
contributions.append(contribution) max_func = max
contributions = numpy.array(contributions) else:
expected1 = contributions.sum() max_func = partial(smooth_max, alpha=alpha)
# reference implementation 2: numpy contributions = []
pos = y_pred[y_true.astype(bool)].max(1) for i in range(len(y_true)):
neg = y_pred[~y_true.astype(bool)] if y_true[i] == 1.0:
expected2 = ( for j in range(len(y_true)):
numpy.maximum(0, neg.reshape((-1, 1)) - pos + delta)**2).sum() if y_true[j] == 0.0:
tightest_i = max_func(y_pred[i])
numpy.testing.assert_almost_equal(expected1, expected2) contribution = sum(
max(0, y_pred[j, k] - tightest_i + delta)**2
computed = evaluate_loss( for k in range(y_pred.shape[1])
partial(Class1LigandomePredictor.loss, delta=delta), )
y_true, contributions.append(contribution)
y_pred.reshape(y_pred.shape + (1,))) contributions = numpy.array(contributions)
numpy.testing.assert_almost_equal(computed, expected1) expected1 = contributions.sum()
# reference implementation 2: numpy
pos = numpy.array([
max_func(y_pred[i])
for i in range(len(y_pred))
if y_true[i] == 1.0
])
neg = y_pred[~y_true.astype(bool)]
expected2 = (
numpy.maximum(0, neg.reshape((-1, 1)) - pos + delta)**2).sum()
yield numpy.testing.assert_almost_equal, expected1, expected2, 4
computed = evaluate_loss(
partial(Class1LigandomePredictor.loss, delta=delta, alpha=alpha),
y_true,
y_pred.reshape(y_pred.shape + (1,)))
yield numpy.testing.assert_almost_equal, computed, expected1, 4
AA_DIST = pandas.Series( AA_DIST = pandas.Series(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment