summarize_from_feedback/policy.py (51 lines of code) (raw):
import functools
from summarize_from_feedback import tasks
from summarize_from_feedback.query_response_model import QueryResponseModel
def _wrap_policy_fn(fn, heads=()):
@functools.wraps(fn)
def wrapped(outputs_mb, inputs_mb):
for key in heads:
outputs_mb[key] = outputs_mb[key]["response"]
return fn(outputs_mb, inputs_mb)
return wrapped
class Policy(QueryResponseModel):
"""
Represents a RL policy + value function, containing a value and logit head.
Only returns pre-token values (never evaluates on the final response token)
"""
def __init__(self, task_hparams: tasks.TaskHParams = None, logit_head=True, **kwargs):
super().__init__(logit_head=logit_head, heads=("value",), **kwargs)
self.task_hparams = task_hparams
def sample(
self, query_tokens, partial_responses=None, responses_per_query=1, sample_len=None, **kwargs
):
"""
Samples from the policy given the context (query_tokens). partial_responses, if provided,
should be a torch tensor of size (batch_size, responses_per_query, X) where X = response length so far.
:return: A dict with structure:
tokens: [batch, num_responses, sample_len]
logprobs: [batch, num_responses, sample_len]
value: [batch, num_responses, sample_len]
"""
response_len = 0 # length of responses so far
if partial_responses is not None:
response_len = partial_responses.size(2)
assert self.logit_head
# sample_len is length of sample to be completed
if sample_len is None:
assert self.task_hparams is not None
sample_len = self.task_hparams.response.length - response_len
if self.task_hparams is not None:
assert query_tokens.size(1) == self.task_hparams.query.length, f"{query_tokens.size()}"
ret = self._sample(
query_tokens,
partial_responses=partial_responses,
sample_len=sample_len,
responses_per_query=responses_per_query,
**kwargs,
)
for key in self.heads:
ret[key] = ret[key]["response"]
return ret
def eval(self, query_tokens, response_tokens, eval_fn=None, **kwargs):
"""
:return: A dict with structure:
value: [batch, num_responses, response_length]
eval_stats: dict of stats returned by eval_fn
"""
if self.task_hparams is not None:
assert query_tokens.size(1) == self.task_hparams.query.length, f"{query_tokens.size()}"
assert (
response_tokens.size(2) == self.task_hparams.response.length
), f"{response_tokens.size()}"
assert query_tokens.size(0) == response_tokens.size(0)
if eval_fn is not None:
eval_fn = _wrap_policy_fn(
eval_fn, list(self.heads) + (["logits"] if self.logit_head else [])
)
ret = self._eval(query_tokens, response_tokens[:, :, :-1], eval_fn=eval_fn, **kwargs)
for key in self.heads:
ret[key] = ret[key]["response"]
return ret