Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keras custom metric iteration

I'm pretty new to Keras and I'm trying to define my own metric. It calculates concordance index which is a measure for regression problems.

def cindex_score(y_true, y_pred):
    sum = 0
    pair = 0    
    for i in range(1, len(y_true)):
        for j in range(0, i):
            if i is not j:
                if(y_true[i] > y_true[j]):
                  pair +=1
                  sum +=  1* (y_pred[i] > y_pred[j]) + 0.5 * (y_pred[i] == y_pred[j])
    if pair is not 0:
        return sum/pair
    else:
        return 0


def baseline_model(hidden_neurons, inputdim):
    model = Sequential()
    model.add(Dense(hidden_neurons, input_dim=inputdim, init='normal', activation='relu'))
    model.add(Dense(hidden_neurons, init='normal', activation='relu'))
    model.add(Dense(1, init='normal')) #output layer

    model.compile(loss='mean_squared_error', optimizer='adam', metrics=[cindex_score])
    return model

def run_model(P_train, Y_train, P_test, model):
    history = model.fit(numpy.array(P_train), numpy.array(Y_train), batch_size=50, nb_epoch=200)
    plotLoss(history)
    return model.predict(P_test)

baseline_model, run_model and cindex_score functions are in one.py and the following function is in two.py where I called the model,

def experiment():
    hidden_neurons = 250
    dmodel=baseline_model(hidden_neurons, train_pair.shape[1])
    predicted_Y = run_model(train_pair,train_Y, test_pair, dmodel)

But I get the following error, "object of type 'Tensor' has no len()". It does not work with shape attribute as well.

For instance, y_true is represented as Tensor("dense_4_target:0", shape=(?, ?), dtype=float32) and its shape is Tensor("strided_slice:0", shape=(), dtype=int32).

Could you please help me about how to iterate within a Tensor object?

Best,

like image 585
patti_jane Avatar asked Apr 23 '17 21:04

patti_jane


3 Answers

If you are comfortable using tensorflow, then you can try using this code instead:

def cindex_score(y_true, y_pred):

    g = tf.subtract(tf.expand_dims(y_pred, -1), y_pred)
    g = tf.cast(g == 0.0, tf.float32) * 0.5 + tf.cast(g > 0.0, tf.float32)

    f = tf.subtract(tf.expand_dims(y_true, -1), y_true) > 0.0
    f = tf.matrix_band_part(tf.cast(f, tf.float32), -1, 0)

    g = tf.reduce_sum(tf.multiply(g, f))
    f = tf.reduce_sum(f)

    return tf.where(tf.equal(g, 0), 0.0, g/f)

Here is some code that verifies that both approaches are equivalent:

def _ref(J, K):
    _sum = 0
    _pair = 0
    for _i in range(1, len(J)):
        for _j in range(0, _i):
            if _i is not _j:
                if(J[_i] > J[_j]):
                  _pair +=1
                  _sum +=  1* (K[_i] > K[_j]) + 0.5 * (K[_i] == K[_j])
    return 0 if _pair == 0 else _sum / _pair

def _raw(J, K):

    g = tf.subtract(tf.expand_dims(K, -1), K)
    g = tf.cast(g == 0.0, tf.float32) * 0.5 + tf.cast(g > 0.0, tf.float32)

    f = tf.subtract(tf.expand_dims(J, -1), J) > 0.0
    f = tf.matrix_band_part(tf.cast(f, tf.float32), -1, 0)

    g = tf.reduce_sum(tf.multiply(g, f))
    f = tf.reduce_sum(f)

    return tf.where(tf.equal(g, 0), 0.0, g/f)


for _ in range(100):
    with tf.Session() as sess:
        inputs = [tf.placeholder(dtype=tf.float32),
                  tf.placeholder(dtype=tf.float32)]
        D = np.random.randint(low=10, high=1000)
        data = [np.random.rand(D), np.random.rand(D)]

        r1 = sess.run(_raw(inputs[0], inputs[1]),
                      feed_dict={x: y for x, y in zip(inputs, data)})
        r2 = _ref(data[0], data[1])

        assert np.isclose(r1, r2)

Please note that this only works for 1D-tensors (rarely a case you will have in keras).

like image 62
Pedia Avatar answered Sep 30 '22 13:09

Pedia


I used @Pedia code for 3D-tensors to compute Rank loss for multi label classification:

def rloss(y_true, y_pred):
    g = tf.subtract(tf.expand_dims(y_pred[1], -1), y_pred[1])
    g = tf.cast(g == 0.0, tf.float32) * 0.5 + tf.cast(g > 0.0, tf.float32)
    f = tf.subtract(tf.expand_dims(y_true[1], -1), y_true[1]) > 0.0
    f = tf.matrix_band_part(tf.cast(f, tf.float32), -1, 0)
    g = tf.reduce_sum(tf.multiply(g, f))
    f = tf.reduce_sum(f)
return tf.where(tf.equal(g, 0), 0.0, g/f)


model = Sequential()
model.add(Dense(label_length, activation='relu'))
model.add(Dense(label_length, activation='relu'))
model.add(Dense(label_length, activation='sigmoid'))
model.summary()


adgard = optimizers.Adagrad(lr=0.01, epsilon=1e-08, decay=0.0)
model.compile(loss='binary_crossentropy',
          optimizer=adgard, metrics=[rloss])
model.fit(X_train, y_train,
      batch_size=batch_size,
      epochs=n_epoch,
      validation_data=(X_test, y_test),
      shuffle=True)
like image 35
Sepideh Shamsizadeh Avatar answered Sep 30 '22 13:09

Sepideh Shamsizadeh


Replace len(y_true) with y_true.shape[0]

If on an older version of TensorFlow use y_true.get_shape()

like image 35
pyCthon Avatar answered Sep 30 '22 13:09

pyCthon