I've run into this problem myself. You want to do a groupby operation on the metrics against information in x, but that information is not in y or y_hat.
Metrics are like losses in that they only get to see y_true and y_hat. Since the information about U and I are not provided to the loss/metric, you wouldn't have enough information to build a custom metric that does this groupby.
A hard way to solve this would be to build a model around your real model that serializes y_hat, U, and I. Then your custom metric can deserialize y_hat, U, and I, and store the information in a grouped way. If the AUC per item is defined as the average of the AUC per interaction, then this is compact enough to stick into memory. If not, then you may need to store information to disk in your custom metric. I'd recommend using gdbm, which has an easy interface in python.
def serialize(u, i, y):
return tf.concat(
[tf.reshape(u, [-1]),
tf.reshape(i, [-1]),
tf.reshape(y, [-1])])
def deserialize(s):
u = tf.reshape(s[:xyz], [..., ..., ...])
i = tf.reshape(s[xyz, abc], [..., ..., ...])
y = tf.reshape(s[abc:], [..., ..., ...])
return u, i, y
def AUCPerItem(tf.keras.metrics.Metric):
def __init__(self):
self.auc_per_item = {}
def update(y_true, y_pred):
for serialized_example in y_pred:
u, i, y = deserialize(serialized_example)
# do calcualtions and store in self.auc_per_item
# Model takes U and I as inputs, and outputs y_hat.
model = get_and_train_model()
input_u = tf.keras.Input(...)
input_i = tf.keras.Input(...)
y_hat = model(input_u, input_i)
output = serialize(input_u, input_i, y_hat)
wrapper = tf.keras.Model([input_u, input_i], output)