| import torch |
| from torch.export import Dim |
| from transformers import MoonshineForConditionalGeneration |
| from transformers.cache_utils import DynamicCache, EncoderDecoderCache |
|
|
| MODEL, OUT = 'moonshine-ai/moonshine-tiny-uk', '/work/out' |
| m = MoonshineForConditionalGeneration.from_pretrained(MODEL, attn_implementation='eager').eval() |
| cfg = m.config |
| L = cfg.decoder_num_hidden_layers |
| print('layers:', L) |
|
|
| def flatten(cache): |
| """Cache object -> flat tensor list, self layers first, then cross.""" |
| out = [] |
| for sub in (cache.self_attention_cache, cache.cross_attention_cache): |
| for layer in sub.layers: |
| out += [layer.keys, layer.values] |
| return out |
|
|
| def build(self_kv, cross_kv): |
| """Flat tensors -> EncoderDecoderCache, with cross marked filled so the model |
| reuses it instead of projecting the encoder output again on every step.""" |
| sa, ca = DynamicCache(), DynamicCache() |
| for i in range(L): |
| sa.update(self_kv[2*i], self_kv[2*i+1], i) |
| ca.update(cross_kv[2*i], cross_kv[2*i+1], i) |
| cache = EncoderDecoderCache(sa, ca) |
| cache.is_updated = {i: True for i in range(L)} |
| return cache |
|
|
| class DecoderInit(torch.nn.Module): |
| def __init__(self, m): super().__init__(); self.m = m |
| def forward(self, input_ids, encoder_hidden_states): |
| out = self.m.model.decoder(input_ids=input_ids, |
| encoder_hidden_states=encoder_hidden_states, |
| use_cache=True) |
| return (self.m.proj_out(out.last_hidden_state), *flatten(out.past_key_values)) |
|
|
| class DecoderStep(torch.nn.Module): |
| def __init__(self, m): super().__init__(); self.m = m |
| def forward(self, input_ids, cache_position, encoder_hidden_states, *flat): |
| self_kv, cross_kv = list(flat[:2*L]), list(flat[2*L:]) |
| cache = build(self_kv, cross_kv) |
| |
| |
| |
| out = self.m.model.decoder(input_ids=input_ids, |
| past_key_values=cache, |
| cache_position=cache_position, |
| encoder_hidden_states=encoder_hidden_states, |
| use_cache=True) |
| new = flatten(out.past_key_values)[:2*L] |
| return (self.m.proj_out(out.last_hidden_state), *new) |
|
|
| hs = torch.zeros(1, 123, 288) |
| start = torch.tensor([[cfg.decoder_start_token_id]]) |
| init = DecoderInit(m).eval() |
| with torch.no_grad(): |
| init_out = init(start, hs) |
| print('init outputs:', len(init_out), '| logits', tuple(init_out[0].shape), |
| '| self k', tuple(init_out[1].shape), '| cross k', tuple(init_out[1+2*L].shape)) |
|
|
| self_names = [f'{p}_self_{i}' for i in range(L) for p in ('key','value')] |
| cross_names = [f'{p}_cross_{i}' for i in range(L) for p in ('key','value')] |
| frames = Dim('frames', min=4, max=2000) |
| past = Dim('past', min=1, max=448) |
|
|
| torch.onnx.export( |
| init, (start, hs), f'{OUT}/decoder_init.onnx', dynamo=True, |
| input_names=['input_ids', 'encoder_hidden_states'], |
| output_names=['logits'] + [f'present_{n}' for n in self_names + cross_names], |
| dynamic_shapes={'input_ids': {}, 'encoder_hidden_states': {1: frames}}, |
| ) |
| print('decoder_init exported') |
|
|
| flat = list(init_out[1:]) |
| step = DecoderStep(m).eval() |
| pos = torch.tensor([1], dtype=torch.long) |
| nxt = torch.tensor([[100]]) |
| with torch.no_grad(): |
| step_out = step(nxt, pos, hs, *flat) |
| print('step outputs:', len(step_out), '| logits', tuple(step_out[0].shape)) |
|
|
| torch.onnx.export( |
| step, (nxt, pos, hs, *flat), f'{OUT}/decoder_step.onnx', dynamo=True, |
| input_names=['input_ids', 'cache_position', 'encoder_hidden_states'] |
| + [f'past_{n}' for n in self_names + cross_names], |
| output_names=['logits'] + [f'present_{n}' for n in self_names], |
| dynamic_shapes=({}, {}, {1: frames}, |
| tuple([{2: past}] * len(self_names) + [{2: frames}] * len(cross_names))), |
| ) |
| print('decoder_step exported') |
|
|