juancopi81's picture
Add t5x and mt3 models
b100e1c
# Copyright 2022 The T5X Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for t5x.utils."""
import dataclasses
import os
import re
from typing import Optional
from absl import flags
from absl.testing import absltest
from absl.testing import parameterized
import flax.core
from flax.linen import partitioning as flax_partitioning
import jax
import numpy as np
import seqio
from t5x import checkpoints
from t5x import partitioning
from t5x import test_utils
from t5x import train_state as train_state_lib
from t5x import utils
import tensorflow as tf
mock = absltest.mock
Evaluator = seqio.Evaluator
PartitionSpec = partitioning.PartitionSpec
AxisMetadata = flax_partitioning.AxisMetadata
# Parse absl flags test_srcdir and test_tmpdir.
jax.config.parse_flags_with_absl()
FLAGS = flags.FLAGS
def get_mock_train_state(params, param_states=None, step=0):
"""Returns a mock TrainState."""
step = np.array(step) if step is not None else None
state = mock.Mock(param_states=param_states, step=step)
state_dict = dict(
target=params, state=dict(param_states=param_states, step=step))
return mock.Mock(
params=params,
param_states=param_states,
step=step,
state_dict=lambda: state_dict,
optimizer=mock.Mock(
target=params, state=state, state_dict=lambda: state_dict),
)
class UtilsTest(parameterized.TestCase):
def round_vocab_size_to_multiple(self):
self.assertEqual(utils.round_vocab_size_to_multiple(1), 128)
self.assertEqual(utils.round_vocab_size_to_multiple(128), 128)
self.assertEqual(utils.round_vocab_size_to_multiple(129), 256)
self.assertEqual(utils.round_vocab_size_to_multiple(129), 256)
self.assertEqual(
utils.round_vocab_size_to_multiple(25600, divisor=384), 256128)
def test_get_zeros_batch_like_spec(self):
test_utils.assert_same(
utils.get_zeros_batch_like_spec({
"i": jax.ShapeDtypeStruct((2, 5), dtype=np.int32),
"j": jax.ShapeDtypeStruct((1,), dtype=np.float32),
}), {
"i": np.zeros((2, 5), dtype=np.int32),
"j": np.zeros((1,), dtype=np.float32)
})
def test_get_zeros_batch_like_dataset(self):
ds = tf.data.Dataset.from_tensors({
"i": np.arange(10, dtype=np.int32).reshape((2, 5)),
"j": np.ones((1,), dtype=np.float32)
})
test_utils.assert_same(
utils.get_zeros_batch_like_dataset(ds), {
"i": np.zeros((2, 5), dtype=np.int32),
"j": np.zeros((1,), dtype=np.float32)
})
test_utils.assert_same(
utils.get_zeros_batch_like_dataset(ds, batch_size=4), {
"i": np.zeros((4, 5), dtype=np.int32),
"j": np.zeros((4,), dtype=np.float32)
})
@parameterized.named_parameters(
dict(testcase_name="write_to_file", write_to_log_file=True),
dict(testcase_name="do_not_write_to_file", write_to_log_file=False),
)
def test_log_model_info(self, write_to_log_file):
log_file = self.create_tempfile() if write_to_log_file else None
mock_train_state = get_mock_train_state(
params={
"a": {
"aa": jax.ShapeDtypeStruct(shape=(2, 3), dtype=np.int32)
},
"c": jax.ShapeDtypeStruct(shape=(7, 8), dtype=np.int32)
},
param_states={
"a": {
"aa": {
"v_row": jax.ShapeDtypeStruct(shape=(2,), dtype=np.int32),
"v_col": jax.ShapeDtypeStruct(shape=(3,), dtype=np.int32)
}
},
"c": {
"v_row": jax.ShapeDtypeStruct(shape=(2, 4), dtype=np.int32),
"v_col": None
}
})
mock_logical_axes = get_mock_train_state(
params={
"a": {
"aa": partitioning.AxisNames("a1", None)
},
"c": partitioning.AxisNames(None, "a1")
},
param_states={
"a": {
"aa": {
"v_row": partitioning.AxisNames(None,),
"v_col": partitioning.AxisNames(None,)
}
},
"c": {
"v_row": partitioning.AxisNames("a1",),
"v_col": partitioning.AxisNames("a2",)
}
},
step=None)
mock_mesh_axes = get_mock_train_state(
params={
"a": {
"aa": PartitionSpec("b1", None)
},
"c": PartitionSpec(None, "b1")
},
param_states={
"a": {
"aa": {
"v_row": partitioning.AxisNames(None,),
"v_col": partitioning.AxisNames(None,)
}
},
"c": {
"v_row": partitioning.AxisNames("b1",),
"v_col": partitioning.AxisNames("b2",)
}
},
step=None)
partitioner = mock.Mock(
get_logical_axes=lambda _: mock_logical_axes,
get_mesh_axes=lambda _: mock_mesh_axes)
with self.assertLogs(level="INFO") as logs:
utils.log_model_info(log_file and log_file.full_path, mock_train_state,
partitioner)
relevant_logs = [
re.sub(r"\s+", " ", output)
for record, output in zip(logs.records, logs.output)
if "t5x/utils.py" in record.pathname
]
self.assertLen(relevant_logs, 9)
self.assertIn(
"Variable a/aa size 6 shape (a1=2, None=3) partition spec ('b1', None)",
relevant_logs[0])
self.assertIn(
"Variable c size 56 shape (None=7, a1=8) partition spec (None, 'b1')",
relevant_logs[1])
if write_to_log_file:
self.assertEqual(
re.sub(r"\s+", " ", log_file.read_text()),
"Variable a/aa size 6 shape (a1=2, None=3) partition spec ('b1', None) "
"Variable c size 56 shape (None=7, a1=8) partition spec (None, 'b1') "
"Total number of parameters: 62 "
"Variable param_states/a/aa/v_col size 3 shape (None=3) partition spec (None,) "
"Variable param_states/a/aa/v_row size 2 shape (None=2) partition spec (None,) "
"Variable param_states/c/v_col None "
"Variable param_states/c/v_row size 8 shape (2, 4) partition spec ('b1',) "
"Variable step size 1 shape () partition spec None ")
def test_get_training_eval_datasets_task(self):
task = mock.create_autospec(seqio.Task, instance=True)
task.name = "mock_task"
task.splits = set(["train", "test"])
seqio.TaskRegistry.add_provider("mock_task", task)
mock_get_dataset_fn = mock.Mock(
return_value=tf.data.Dataset.range(10).batch(1))
mock_fc_cls = mock.Mock()
cfg = utils.DatasetConfig(
mixture_or_task_name="mock_task",
task_feature_lengths={},
split="test",
batch_size=4,
shuffle=False,
seed=None)
# Single shard.
ds = utils.get_training_eval_datasets(
cfg,
shard_id=0,
num_shards=1,
eval_steps=3,
feature_converter_cls=mock_fc_cls,
get_dataset_fn=mock_get_dataset_fn)
mock_get_dataset_fn.assert_called_once_with(
dataclasses.replace(cfg, batch_size=1),
shard_id=0,
num_shards=1,
feature_converter_cls=mock_fc_cls,
num_epochs=12,
continue_from_last_checkpoint=False)
self.assertSameElements(ds.keys(), ["mock_task"])
jax.tree_map(np.testing.assert_equal, list(ds["mock_task"]), [
np.array([0, 1, 2, 3]),
np.array([4, 5, 6, 7]),
np.array([8, 9, 0, 1]),
])
# 2 shards, shard 0
mock_get_dataset_fn.reset_mock()
ds = utils.get_training_eval_datasets(
cfg,
shard_id=0,
num_shards=2,
eval_steps=3,
feature_converter_cls=mock_fc_cls,
get_dataset_fn=mock_get_dataset_fn)
# Call the underlying function loading all shards since the fn shards at the
# example level.
mock_get_dataset_fn.assert_called_once_with(
dataclasses.replace(cfg, batch_size=1),
shard_id=0,
num_shards=1,
feature_converter_cls=mock_fc_cls,
num_epochs=12,
continue_from_last_checkpoint=False)
self.assertSameElements(ds.keys(), ["mock_task"])
jax.tree_map(np.testing.assert_equal, list(ds["mock_task"]), [
np.array([0, 2]),
np.array([4, 6]),
np.array([8, 0]),
])
# 2 shards, shard 1
mock_get_dataset_fn.reset_mock()
ds = utils.get_training_eval_datasets(
cfg,
shard_id=1,
num_shards=2,
eval_steps=3,
feature_converter_cls=mock_fc_cls,
get_dataset_fn=mock_get_dataset_fn)
# Call the underlying function loading all shards since the fn shards at the
# example level.
mock_get_dataset_fn.assert_called_once_with(
dataclasses.replace(cfg, batch_size=1),
shard_id=0,
num_shards=1,
feature_converter_cls=mock_fc_cls,
num_epochs=12,
continue_from_last_checkpoint=False)
self.assertSameElements(ds.keys(), ["mock_task"])
jax.tree_map(np.testing.assert_equal, list(ds["mock_task"]), [
np.array([1, 3]),
np.array([5, 7]),
np.array([9, 1]),
])
# 3 shards
with self.assertRaisesWithLiteralMatch(
ValueError,
"Batch size (4) must be divisible by number of shards (3)."):
_ = utils.get_training_eval_datasets(
cfg,
shard_id=0,
num_shards=3,
eval_steps=3,
feature_converter_cls=mock_fc_cls,
get_dataset_fn=mock_get_dataset_fn)
def test_get_training_eval_datasets_mixture(self):
# Register a mock SeqIO mixture.
task1 = mock.create_autospec(seqio.Task, instance=True)
task1.name = "mock_task1"
task1.splits = set(["train", "test"])
task2 = mock.create_autospec(seqio.Task, instance=True)
task2.name = "mock_task2"
task2.splits = set(["train", "test"])
seqio.TaskRegistry.add_provider("mock_task1", task1)
seqio.TaskRegistry.add_provider("mock_task2", task2)
mixture = seqio.Mixture(
"mock_mix", ["mock_task1", "mock_task2"], default_rate=1.0)
seqio.MixtureRegistry.add_provider("mock_mix", mixture)
mock_get_dataset = mock.Mock(
return_value=tf.data.Dataset.range(10).batch(1))
# Verify calls to utils.get_dataset
cfg = utils.DatasetConfig(
mixture_or_task_name="mock_mix",
task_feature_lengths={},
split="test",
batch_size=4,
shuffle=False,
seed=23)
res = utils.get_training_eval_datasets(
cfg,
shard_id=0,
num_shards=2,
eval_steps=3,
feature_converter_cls=seqio.FeatureConverter,
get_dataset_fn=mock_get_dataset)
expected_calls = [
mock.call(
dataclasses.replace(
cfg, mixture_or_task_name="mock_task1", batch_size=1),
shard_id=0,
num_shards=1,
feature_converter_cls=seqio.FeatureConverter,
continue_from_last_checkpoint=False,
num_epochs=12),
mock.call(
dataclasses.replace(
cfg, mixture_or_task_name="mock_task2", batch_size=1),
shard_id=0,
num_shards=1,
feature_converter_cls=seqio.FeatureConverter,
continue_from_last_checkpoint=False,
num_epochs=12),
mock.call(
dataclasses.replace(
cfg, mixture_or_task_name="mock_mix", batch_size=1),
shard_id=0,
num_shards=1,
feature_converter_cls=seqio.FeatureConverter,
continue_from_last_checkpoint=False,
num_epochs=12)
]
mock_get_dataset.assert_has_calls(expected_calls)
self.assertSameElements(res.keys(),
["mock_task1", "mock_task2", "mock_mix"])
for ds in res.values():
jax.tree_map(np.testing.assert_equal, list(ds), [
np.array([0, 2]),
np.array([4, 6]),
np.array([8, 0]),
])
def test_override_params_axes_names(self):
model_variables = flax.core.freeze({
"params": {
"logits_dense": np.zeros((2, 4)),
"mlp": {
"wo": {
"kernel": np.zeros((4, 6)),
"bias": np.zeros(6),
}
}
},
"params_axes": {
"logits_dense_axes": AxisMetadata(names=("vocab", "embed")),
"mlp": {
"wo": {
"kernel_axes": AxisMetadata(names=("embed", "mlp"))
}
}
}
})
with self.assertRaisesWithLiteralMatch(
ValueError,
"Model variables do not contain a 'params_axes' collection to apply an "
"override to."):
utils.override_params_axes_names({"params": model_variables["params"]},
[("mlp/wo/kernel", ("embed",))])
with self.assertRaisesWithLiteralMatch(
ValueError,
"Provided axis name override for mlp/wo/kernel does not match param "
"rank (2): ('embed',)"):
utils.override_params_axes_names(model_variables,
[("mlp/wo/kernel", ("embed",))])
overridden_variables = utils.override_params_axes_names(
model_variables,
[
("wo/kernel", ("batch",)), # unused since not a full match
(".*/wo/kernel", ("batch", "embed")), # this one is used
("mlp/wo/kernel", ("embed",)), # unused since already matched
("mlp/wo/bias", ("embed",)), # used
])
jax.tree_multimap(
np.testing.assert_equal, overridden_variables,
flax.core.freeze({
"params": {
"logits_dense": np.zeros((2, 4)),
"mlp": {
"wo": {
"kernel": np.zeros((4, 6)),
"bias": np.zeros(6),
}
}
},
"params_axes": {
"logits_dense_axes": AxisMetadata(names=("vocab", "embed")),
"mlp": {
"wo": {
"kernel_axes": AxisMetadata(names=("batch", "embed")),
"bias_axes": AxisMetadata(names=("embed",)),
}
}
}
}))
@dataclasses.dataclass
class MockTrainState:
path: Optional[str] = None
from_scratch: Optional[bool] = None
class MockCheckpointer(checkpoints.Checkpointer):
def __init__(self, *args, **kwargs):
pass
# restore should return TrainState, but we force it to return Mock with path
# for simplicity.
def restore(self, path, *args, **kwargs):
return MockTrainState(path=path, from_scratch=False)
class TrainStateInitializerTest(parameterized.TestCase):
def setUp(self):
super().setUp()
def _partition(train_state, in_axis_resources, out_axis_resources):
del train_state, in_axis_resources, out_axis_resources
partitioned_fn = lambda _: MockTrainState(from_scratch=True)
return partitioned_fn
partitioner = mock.Mock(get_mesh_axes=lambda _: None, partition=_partition)
mock_inference_state_create = self.enter_context(
mock.patch.object(train_state_lib.InferenceState, "create"))
mock_inference_state_create.return_value = None
shapes = {
"ones": (1, 1),
"twos": (2, 2),
"threes": (3, 3),
}
types = {
"ones": int,
"twos": float,
"threes": int,
}
def _init_fn(rng, input_shapes, input_types):
del rng
return {
"ones":
np.ones(input_shapes["ones"], dtype=input_types["ones"]),
"twos":
np.ones(input_shapes["twos"], dtype=input_types["twos"]) * 2,
"threes":
np.ones(input_shapes["threes"], dtype=input_types["threes"]) * 3
}
init_fn = mock.Mock()
init_fn.__call__ = _init_fn
init_fn.__self__ = None
self.train_state_init = utils.TrainStateInitializer(None, init_fn, shapes,
partitioner, types)
self.ckptdir = self.create_tempdir(name="primary_checkpoints")
steps = (2, 3)
self.paths = []
for s in steps:
step_dir = self.ckptdir.mkdir(f"checkpoint_{s}")
step_dir.create_file("checkpoint")
self.paths += [step_dir.full_path]
def test_from_checkpoints_specific(self):
# multiple paths
ckpt_cfg = utils.RestoreCheckpointConfig(
path=self.paths, mode="specific", checkpointer_cls=MockCheckpointer)
restored = self.train_state_init.from_checkpoints([ckpt_cfg])
self.assertSequenceEqual(self.paths, [state.path for state in restored])
with self.assertRaisesRegex(ValueError, r"^Expected at most 1 checkpoint"):
self.train_state_init.from_checkpoint([ckpt_cfg])
def test_from_checkpoints_latest(self):
# only restore single latest
ckpt_cfg = utils.RestoreCheckpointConfig(
path=self.ckptdir.full_path,
mode="latest",
checkpointer_cls=MockCheckpointer)
restored = list(self.train_state_init.from_checkpoints([ckpt_cfg]))
assert len(restored) == 1
self.assertEqual(self.paths[-1], restored[0].path)
restored = self.train_state_init.from_checkpoint([ckpt_cfg])
self.assertEqual(self.paths[-1], restored.path)
def test_from_checkpoints_multiple_configs(self):
# uses first checkpoint with files present.
ckpt_cfg = utils.RestoreCheckpointConfig(
path=self.ckptdir.full_path,
mode="latest",
checkpointer_cls=MockCheckpointer)
secondary_ckptdir = self.create_tempdir(name="secondary_checkpoints")
for s in (4, 5):
step_dir = secondary_ckptdir.mkdir(f"checkpoint_{s}")
step_dir.create_file("checkpoint")
secondary_ckpt_cfg = utils.RestoreCheckpointConfig(
path=secondary_ckptdir.full_path,
mode="latest",
checkpointer_cls=MockCheckpointer)
restored = self.train_state_init.from_checkpoint(
[ckpt_cfg, secondary_ckpt_cfg])
self.assertEqual(self.paths[-1], restored.path)
def test_from_checkpoints_multiple_configs_one_empty(self):
# skips empty_checkpoints directory with no checkpoints present.
ckpt_cfg = utils.RestoreCheckpointConfig(
path=self.ckptdir.full_path,
mode="latest",
checkpointer_cls=MockCheckpointer)
empty_ckptdir = self.create_tempdir(name="empty_checkpoints")
empty_ckpt_cfg = utils.RestoreCheckpointConfig(
path=empty_ckptdir.full_path,
mode="latest",
checkpointer_cls=MockCheckpointer)
restored = self.train_state_init.from_checkpoint([empty_ckpt_cfg, ckpt_cfg])
self.assertEqual(self.paths[-1], restored.path)
def test_from_scratch(self):
self.assertTrue(
self.train_state_init.from_scratch(jax.random.PRNGKey(13)).from_scratch)
def test_from_checkpoint_or_scratch(self):
ckpt_cfg = utils.RestoreCheckpointConfig(
path=self.ckptdir.full_path,
mode="latest",
checkpointer_cls=MockCheckpointer)
empty_ckptdir = self.create_tempdir(name="empty_checkpoints")
empty_ckpt_cfg = utils.RestoreCheckpointConfig(
path=empty_ckptdir.full_path,
mode="latest",
checkpointer_cls=MockCheckpointer)
init_rng = jax.random.PRNGKey(13)
# ckpt_cfg has checkpoints, restore from there
restored = self.train_state_init.from_checkpoint_or_scratch(
[empty_ckpt_cfg, ckpt_cfg], init_rng=init_rng)
self.assertEqual(self.paths[-1], restored.path)
self.assertFalse(restored.from_scratch)
# no checkpoints available, init from scratch
initialized = self.train_state_init.from_checkpoint_or_scratch(
[empty_ckpt_cfg], init_rng=init_rng)
self.assertTrue(initialized.from_scratch)
if __name__ == "__main__":
absltest.main()