|
|
| |
|
|
| import argparse |
| import os |
| import re |
|
|
| import torch |
| from transformers import AutoModelForCausalLM |
|
|
| import fla |
| from fla.models.rwkv7 import RWKV7Config |
|
|
|
|
| def convert( |
| rwkv7: str, |
| output: str, |
| precision: str = 'float32', |
| ): |
| weights = torch.load(rwkv7, weights_only=True, map_location='cpu') |
| config = RWKV7Config() |
| config.vocab_size = weights['emb.weight'].shape[0] |
| config.hidden_size = weights['blocks.0.ffn.key.weight'].shape[1] |
| config.hidden_ratio = weights['blocks.0.ffn.key.weight'].shape[0] / weights['blocks.0.ffn.key.weight'].shape[1] |
| config.intermediate_size = weights['blocks.0.ffn.key.weight'].shape[0] |
| config.num_hidden_layers = 0 |
| while f'blocks.{config.num_hidden_layers}.ffn.key.weight' in weights: |
| config.num_hidden_layers += 1 |
| |
| config.value_dim = [config.hidden_size] * config.num_hidden_layers |
| config.decay_low_rank_dim = weights['blocks.0.att.w1'].shape[1] |
| config.gate_low_rank_dim = weights['blocks.0.att.g1'].shape[1] |
| config.a_low_rank_dim = weights['blocks.0.att.a1'].shape[1] |
| try: |
| config.v_low_rank_dim = weights['blocks.1.att.v1'].shape[1] |
| except KeyError: |
| config.v_low_rank_dim = 32 |
|
|
| if precision in ['bf16', 'bfloat16']: |
| precision = 'bfloat16' |
| dtype = torch.bfloat16 |
| if precision in ['fp16', 'float16']: |
| precision = 'float16' |
| dtype = torch.float16 |
| if precision in ['fp64', 'double', 'float64']: |
| precision = 'float64' |
| dtype = torch.float64 |
|
|
| config.torch_dtype = precision |
| print(f"Creating model with config:\n{config}") |
| model = AutoModelForCausalLM.from_config(config).to(dtype=dtype) |
|
|
| print(model) |
| model_dict = model.state_dict() |
| model_names = [n for n in model_dict] |
|
|
| |
| unused_names = ['blocks.0.att.v0', 'blocks.0.att.v1', 'blocks.0.att.v2'] |
| |
| possible_absent_weights = [ |
| 'model.layers.0.pre_norm.weight', 'model.layers.0.pre_norm.bias', |
| ] |
| |
|
|
| def translate_into_fla(name): |
| transposed = False |
| emb_head = { |
| 'emb.weight': 'model.embeddings.weight', |
| 'ln_out.weight': 'model.norm.weight', |
| 'ln_out.bias': 'model.norm.bias', |
| 'head.weight': 'lm_head.weight', |
| } |
| proj = { |
| 'receptance': 'r_proj', |
| 'key': 'k_proj', |
| 'value': 'v_proj', |
| 'ln_x': 'g_norm', |
| 'output': 'o_proj', |
| } |
| if name in unused_names: |
| return '', False |
| if name in emb_head: |
| return emb_head[name], False |
| name_compo = name.split('.') |
| assert name_compo[0] == 'blocks' |
| name_compo[0] = 'model.layers' |
| assert int(name_compo[1]) in range(config.num_hidden_layers) |
| name_compo[2] = { |
| 'att': 'attn', |
| 'ffn': 'ffn', |
| 'ln0': 'pre_norm', |
| 'ln1': 'attn_norm', |
| 'ln2': 'ffn_norm', |
| }[name_compo[2]] |
| if re.match("[wvag][012]", name_compo[3]): |
| typ, num = name_compo[3] |
| name_compo[3] = f'{typ}_lora.lora.' + { |
| '0': '2.bias', |
| '1': '0.weight', |
| '2': '2.weight', |
| }[num] |
| transposed |= (num in ['1', '2']) |
| elif name_compo[2] == 'attn' and name_compo[3] in proj: |
| name_compo[3] = proj[name_compo[3]] |
| return '.'.join(name_compo), transposed |
|
|
| for name in weights: |
| fla_name, transposed = translate_into_fla(name) |
| print(f'{name:32} -> {fla_name:42}, {transposed}') |
| if not fla_name: |
| print('redundant parameters in source weight: ', name, '\n') |
| continue |
| weight = weights[name] |
| |
| shape1 = list(weight.shape) |
| shape2 = list(model_dict[fla_name].shape) |
| print(f'{str(shape1):32} {str(shape2)}\n') |
|
|
| if transposed: |
| weight.t_() |
| if shape1 == [1, 1, config.hidden_size]: |
| weight.squeeze_() |
|
|
| if "attn.x_" in fla_name: |
| assert model_dict[fla_name].shape[2:] == weight.shape, \ |
| f"Shape mismatch for {fla_name}: model_dict={model_dict[fla_name].shape}, weight={weight.shape}" |
| else: |
| assert model_dict[fla_name].shape == weight.shape, \ |
| f"Shape mismatch for {fla_name}: model_dict={model_dict[fla_name].shape}, weight={weight.shape}" |
|
|
| model_dict[fla_name].data.copy_(weight) |
| model_names.remove(fla_name) |
|
|
| print("uninitialized parameters: ", model_names) |
| for n in model_names: |
| if n not in possible_absent_weights: |
| raise KeyError(n) |
|
|
| os.makedirs(output, exist_ok=True) |
|
|
| model.save_pretrained(output, max_shard_size="1000GB") |
|
|
|
|
| if __name__ == '__main__': |
| parser = argparse.ArgumentParser(description='Convert RWKV7') |
| parser.add_argument('--rwkv7', type=str, help='Path to the input model') |
| parser.add_argument('--output', type=str, help='Directory to save model') |
| parser.add_argument('--precision', type=str, default='float32') |
| args = parser.parse_args() |
| convert(args.rwkv7, args.output, precision=args.precision) |
|
|