lm_human_preferences/lm_tasks.py (73 lines of code) (raw):

from dataclasses import dataclass, field from typing import Optional import tensorflow as tf from lm_human_preferences.language import datasets from lm_human_preferences.utils import core as utils from lm_human_preferences.utils import hyperparams @dataclass class PolicyHParams(hyperparams.HParams): temperature: float = 1.0 initial_model: str = None @dataclass class TaskHParams(hyperparams.HParams): # Query params query_length: int = None query_dataset: str = None query_prefix: str = '' query_suffix: str = '' start_text: Optional[str] = '.' end_text: Optional[str] = None # Response params response_length: int = None # Truncate response after the first occurrence of this token at or after index after when sampling. truncate_token: Optional[int] = None truncate_after: int = 0 penalty_reward_value: int = -1 policy: PolicyHParams = field(default_factory=PolicyHParams) #returns a postprocessing function #it is applied to responses before they are scored #central example: replace all tokens after truncate_token with padding_token def postprocess_fn_from_hparams(hparams: TaskHParams, padding_token: int): def get_mask(responses, truncate_token, truncate_after): # We want to truncate at the first occurrence of truncate_token that appears at or after # position truncate_after in the responses mask = tf.cast(tf.equal(responses, truncate_token), tf.int32) mask = tf.concat([tf.zeros_like(mask)[:,:truncate_after], mask[:,truncate_after:]], axis=1) return tf.cast(tf.cumsum(mask, axis=1) - mask, tf.bool) if hparams.truncate_token is not None: def truncate(responses): mask = get_mask(responses, hparams.truncate_token, hparams.truncate_after) return tf.where(mask, padding_token * tf.ones_like(responses), responses) return truncate else: return lambda responses: responses #returns a filter function #responses not passing that function will receive a low (fixed) score #only query humans on responses that pass that function #central example: ensure that the sample contains truncate_token def filter_fn_from_hparams(hparams: TaskHParams): def filter(responses): if hparams.truncate_token is not None: matches_token = tf.equal(responses[:, hparams.truncate_after:], hparams.truncate_token) return tf.reduce_any(matches_token, axis=-1) else: return tf.ones(tf.shape(responses)[0], dtype=tf.bool) return filter def query_formatter(hparams: TaskHParams, encoder): """Turns a query into a context to feed to the language model NOTE: Both of these are lists of tokens """ def query_formatter(queries): batch_size = tf.shape(queries)[0] prefix_tokens = tf.constant(encoder.encode(hparams.query_prefix), dtype=tf.int32) tiled_prefix = utils.expand_tile(prefix_tokens, batch_size, axis=0) suffix_tokens = tf.constant(encoder.encode(hparams.query_suffix), dtype=tf.int32) tiled_suffix = utils.expand_tile(suffix_tokens, batch_size, axis=0) return tf.concat([tiled_prefix, queries, tiled_suffix], 1) return query_formatter def make_query_sampler(*, hparams: TaskHParams, encoder, batch_size: int, mode='train', comm=None): if hparams.start_text: start_token, = encoder.encode(hparams.start_text) else: start_token = None if hparams.end_text: end_token, = encoder.encode(hparams.end_text) else: end_token = None data = datasets.get_dataset(hparams.query_dataset).tf_dataset( sequence_length=hparams.query_length, mode=mode, comm=comm, encoder=encoder, start_token=start_token, end_token=end_token, ) data = data.map(lambda d: tf.cast(d['tokens'], tf.int32)) data = data.batch(batch_size, drop_remainder=True) context_iterator = data.make_one_shot_iterator() def sampler(scope=None): with tf.name_scope(scope, 'sample_corpus'): context_tokens = context_iterator.get_next() return dict(tokens=context_tokens) return sampler