| """Reader for training log. | |
| See lib/Analysis/TrainingLogger.cpp for a description of the format. | |
| """ | |
| import ctypes | |
| import dataclasses | |
| import json | |
| import math | |
| import sys | |
| import typing | |
| _element_types = { | |
| 'float': ctypes.c_float, | |
| 'double': ctypes.c_double, | |
| 'int8_t': ctypes.c_int8, | |
| 'uint8_t': ctypes.c_uint8, | |
| 'int16_t': ctypes.c_int16, | |
| 'uint16_t': ctypes.c_uint16, | |
| 'int32_t': ctypes.c_int32, | |
| 'uint32_t': ctypes.c_uint32, | |
| 'int64_t': ctypes.c_int64, | |
| 'uint64_t': ctypes.c_uint64 | |
| } | |
| @dataclasses.dataclass(frozen=True) | |
| class TensorSpec: | |
| name: str | |
| port: int | |
| shape: list[int] | |
| element_type: type | |
| @staticmethod | |
| def from_dict(d: dict): | |
| name = d['name'] | |
| port = d['port'] | |
| shape = [int(e) for e in d['shape']] | |
| element_type_str = d['type'] | |
| if element_type_str not in _element_types: | |
| raise ValueError(f'uknown type: {element_type_str}') | |
| return TensorSpec( | |
| name=name, | |
| port=port, | |
| shape=shape, | |
| element_type=_element_types[element_type_str]) | |
| class TensorValue: | |
| def __init__(self, spec: TensorSpec, buffer: bytes): | |
| self._spec = spec | |
| self._buffer = buffer | |
| self._view = ctypes.cast(self._buffer, | |
| ctypes.POINTER(self._spec.element_type)) | |
| self._len = math.prod(self._spec.shape) | |
| def spec(self) -> TensorSpec: | |
| return self._spec | |
| def __len__(self) -> int: | |
| return self._len | |
| def __getitem__(self, index): | |
| if index < 0 or index >= self._len: | |
| raise IndexError(f'Index {index} out of range [0..{self._len})') | |
| return self._view[index] | |
| def read_tensor(fs: typing.BinaryIO, ts: TensorSpec) -> TensorValue: | |
| size = math.prod(ts.shape) * ctypes.sizeof(ts.element_type) | |
| data = fs.read(size) | |
| return TensorValue(ts, data) | |
| def pretty_print_tensor_value(tv: TensorValue): | |
| print(f'{tv.spec().name}: {",".join([str(v) for v in tv])}') | |
| def read_stream(fname: str): | |
| with open(fname, 'rb') as f: | |
| header = json.loads(f.readline()) | |
| tensor_specs = [TensorSpec.from_dict(ts) for ts in header['features']] | |
| score_spec = TensorSpec.from_dict( | |
| header['score']) if 'score' in header else None | |
| context = None | |
| while event_str := f.readline(): | |
| event = json.loads(event_str) | |
| if 'context' in event: | |
| context = event['context'] | |
| continue | |
| observation_id = int(event['observation']) | |
| features = [] | |
| for ts in tensor_specs: | |
| features.append(read_tensor(f, ts)) | |
| f.readline() | |
| score = None | |
| if score_spec is not None: | |
| score_header = json.loads(f.readline()) | |
| assert int(score_header['outcome']) == observation_id | |
| score = read_tensor(f, score_spec) | |
| f.readline() | |
| yield context, observation_id, features, score | |
| def main(args): | |
| last_context = None | |
| for ctx, obs_id, features, score in read_stream(args[1]): | |
| if last_context != ctx: | |
| print(f'context: {ctx}') | |
| last_context = ctx | |
| print(f'observation: {obs_id}') | |
| for fv in features: | |
| pretty_print_tensor_value(fv) | |
| if score: | |
| pretty_print_tensor_value(score) | |
| if __name__ == '__main__': | |
| main(sys.argv) |