The problem
People find it easier to say which of two answers is better than to write the perfect answer. A reward model learns from such comparisons: it gives every answer a score, its reward, so that the answer people preferred (the chosen one) gets a higher score than the one they did not (the rejected one). You will write the loss that trains it, its gradient, and a way to measure how often it ranks the pair correctly.
The Bradley-Terry loss
The Bradley-Terry model says that the probability that the chosen answer beats the rejected one depends only on the difference of their rewards:
where is the sigmoid, the reward of the chosen answer and the reward of the rejected one. The loss is the negative log of that probability, averaged over pairs:
Example. One pair.
- , : the difference is , , loss . Correct ranking: small loss.
- , : , , loss . Wrong ranking: large loss.
- , : the same difference as the first case, the same loss . Only the difference counts, never the absolute scale.
Why not compute directly? For a very wrong ranking, such as , rounds to 0 and the log is . The identity is computed safely by np.logaddexp(0, -d), which gives .
The gradient
With , the derivative of with respect to is : the more wrong the ranking, the closer to . So the gradient with respect to is that, and for it is the opposite, both divided by because of the mean.
Example. For the first pair above (): for and for (with ). Training moves against the gradient, so it raises and lowers , only a little: the ranking is already right. For the second pair (): and , a strong push.
Accuracy
The accuracy is the fraction of pairs where the chosen answer gets the higher reward.
Example. Three pairs with
r_chosen = [2, 1, 0]andr_rejected = [1, 2, -1]: the chosen answer wins in pairs 1 and 3, loses in pair 2: accuracy .
What to implement
For arrays r_chosen and r_rejected of shape :
preference_loss(r_chosen, r_rejected): the mean loss, stable for any rewards.preference_grads(r_chosen, r_rejected): the gradients with respect tor_chosenand tor_rejected, as a pair of arrays.preference_accuracy(r_chosen, r_rejected): the accuracy.
Requirements
- Use
np.logaddexp(0, -d)for the loss, so that gives 1000 instead ofinf. - No loops.