Duplicate of the tiny-nla weights (same training run)
Browse files- .gitattributes +3 -0
- code-decoder/centre_mean.npy +3 -0
- code-decoder/chat_template.jinja +89 -0
- code-decoder/config.json +71 -0
- code-decoder/generation_config.json +13 -0
- code-decoder/model.safetensors +3 -0
- code-decoder/nla_meta.yaml +33 -0
- code-decoder/tokenizer.json +3 -0
- code-decoder/tokenizer_config.json +29 -0
- nla/actor/chat_template.jinja +89 -0
- nla/actor/config.json +71 -0
- nla/actor/generation_config.json +13 -0
- nla/actor/model.safetensors +3 -0
- nla/actor/nla_meta.yaml +33 -0
- nla/actor/tokenizer.json +3 -0
- nla/actor/tokenizer_config.json +29 -0
- nla/critic/chat_template.jinja +89 -0
- nla/critic/config.json +60 -0
- nla/critic/generation_config.json +13 -0
- nla/critic/model.safetensors +3 -0
- nla/critic/nla_meta.yaml +40 -0
- nla/critic/tokenizer.json +3 -0
- nla/critic/tokenizer_config.json +29 -0
- nla/critic/value_head.safetensors +3 -0
- nla_code_demo.py +181 -0
- nla_demo.py +297 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
code-decoder/tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
nla/actor/tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
nla/critic/tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
code-decoder/centre_mean.npy
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9ea65c6f3af1d03f1f0084b9e6eac98bd8263b28dc843f38c04a4e4274544c1e
|
| 3 |
+
size 16512
|
code-decoder/chat_template.jinja
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- if tools %}
|
| 2 |
+
{{- '<|im_start|>system\n' }}
|
| 3 |
+
{%- if messages[0].role == 'system' %}
|
| 4 |
+
{{- messages[0].content + '\n\n' }}
|
| 5 |
+
{%- endif %}
|
| 6 |
+
{{- "# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
|
| 7 |
+
{%- for tool in tools %}
|
| 8 |
+
{{- "\n" }}
|
| 9 |
+
{{- tool | tojson }}
|
| 10 |
+
{%- endfor %}
|
| 11 |
+
{{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
|
| 12 |
+
{%- else %}
|
| 13 |
+
{%- if messages[0].role == 'system' %}
|
| 14 |
+
{{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
|
| 15 |
+
{%- endif %}
|
| 16 |
+
{%- endif %}
|
| 17 |
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 18 |
+
{%- for message in messages[::-1] %}
|
| 19 |
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 20 |
+
{%- if ns.multi_step_tool and message.role == "user" and message.content is string and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}
|
| 21 |
+
{%- set ns.multi_step_tool = false %}
|
| 22 |
+
{%- set ns.last_query_index = index %}
|
| 23 |
+
{%- endif %}
|
| 24 |
+
{%- endfor %}
|
| 25 |
+
{%- for message in messages %}
|
| 26 |
+
{%- if message.content is string %}
|
| 27 |
+
{%- set content = message.content %}
|
| 28 |
+
{%- else %}
|
| 29 |
+
{%- set content = '' %}
|
| 30 |
+
{%- endif %}
|
| 31 |
+
{%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
|
| 32 |
+
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
| 33 |
+
{%- elif message.role == "assistant" %}
|
| 34 |
+
{%- set reasoning_content = '' %}
|
| 35 |
+
{%- if message.reasoning_content is string %}
|
| 36 |
+
{%- set reasoning_content = message.reasoning_content %}
|
| 37 |
+
{%- else %}
|
| 38 |
+
{%- if '</think>' in content %}
|
| 39 |
+
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 40 |
+
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
| 41 |
+
{%- endif %}
|
| 42 |
+
{%- endif %}
|
| 43 |
+
{%- if loop.index0 > ns.last_query_index %}
|
| 44 |
+
{%- if loop.last or (not loop.last and reasoning_content) %}
|
| 45 |
+
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
|
| 46 |
+
{%- else %}
|
| 47 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 48 |
+
{%- endif %}
|
| 49 |
+
{%- else %}
|
| 50 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 51 |
+
{%- endif %}
|
| 52 |
+
{%- if message.tool_calls %}
|
| 53 |
+
{%- for tool_call in message.tool_calls %}
|
| 54 |
+
{%- if (loop.first and content) or (not loop.first) %}
|
| 55 |
+
{{- '\n' }}
|
| 56 |
+
{%- endif %}
|
| 57 |
+
{%- if tool_call.function %}
|
| 58 |
+
{%- set tool_call = tool_call.function %}
|
| 59 |
+
{%- endif %}
|
| 60 |
+
{{- '<tool_call>\n{"name": "' }}
|
| 61 |
+
{{- tool_call.name }}
|
| 62 |
+
{{- '", "arguments": ' }}
|
| 63 |
+
{%- if tool_call.arguments is string %}
|
| 64 |
+
{{- tool_call.arguments }}
|
| 65 |
+
{%- else %}
|
| 66 |
+
{{- tool_call.arguments | tojson }}
|
| 67 |
+
{%- endif %}
|
| 68 |
+
{{- '}\n</tool_call>' }}
|
| 69 |
+
{%- endfor %}
|
| 70 |
+
{%- endif %}
|
| 71 |
+
{{- '<|im_end|>\n' }}
|
| 72 |
+
{%- elif message.role == "tool" %}
|
| 73 |
+
{%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
|
| 74 |
+
{{- '<|im_start|>user' }}
|
| 75 |
+
{%- endif %}
|
| 76 |
+
{{- '\n<tool_response>\n' }}
|
| 77 |
+
{{- content }}
|
| 78 |
+
{{- '\n</tool_response>' }}
|
| 79 |
+
{%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
|
| 80 |
+
{{- '<|im_end|>\n' }}
|
| 81 |
+
{%- endif %}
|
| 82 |
+
{%- endif %}
|
| 83 |
+
{%- endfor %}
|
| 84 |
+
{%- if add_generation_prompt %}
|
| 85 |
+
{{- '<|im_start|>assistant\n' }}
|
| 86 |
+
{%- if enable_thinking is defined and enable_thinking is false %}
|
| 87 |
+
{{- '<think>\n\n</think>\n\n' }}
|
| 88 |
+
{%- endif %}
|
| 89 |
+
{%- endif %}
|
code-decoder/config.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"Qwen3ForCausalLM"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"bos_token_id": 151643,
|
| 8 |
+
"dtype": "float32",
|
| 9 |
+
"eos_token_id": 151645,
|
| 10 |
+
"head_dim": 128,
|
| 11 |
+
"hidden_act": "silu",
|
| 12 |
+
"hidden_size": 4096,
|
| 13 |
+
"initializer_range": 0.02,
|
| 14 |
+
"intermediate_size": 12288,
|
| 15 |
+
"layer_types": [
|
| 16 |
+
"full_attention",
|
| 17 |
+
"full_attention",
|
| 18 |
+
"full_attention",
|
| 19 |
+
"full_attention",
|
| 20 |
+
"full_attention",
|
| 21 |
+
"full_attention",
|
| 22 |
+
"full_attention",
|
| 23 |
+
"full_attention",
|
| 24 |
+
"full_attention",
|
| 25 |
+
"full_attention",
|
| 26 |
+
"full_attention",
|
| 27 |
+
"full_attention",
|
| 28 |
+
"full_attention",
|
| 29 |
+
"full_attention",
|
| 30 |
+
"full_attention",
|
| 31 |
+
"full_attention",
|
| 32 |
+
"full_attention",
|
| 33 |
+
"full_attention",
|
| 34 |
+
"full_attention",
|
| 35 |
+
"full_attention",
|
| 36 |
+
"full_attention",
|
| 37 |
+
"full_attention",
|
| 38 |
+
"full_attention",
|
| 39 |
+
"full_attention",
|
| 40 |
+
"full_attention",
|
| 41 |
+
"full_attention",
|
| 42 |
+
"full_attention",
|
| 43 |
+
"full_attention",
|
| 44 |
+
"full_attention",
|
| 45 |
+
"full_attention",
|
| 46 |
+
"full_attention",
|
| 47 |
+
"full_attention",
|
| 48 |
+
"full_attention",
|
| 49 |
+
"full_attention",
|
| 50 |
+
"full_attention",
|
| 51 |
+
"full_attention"
|
| 52 |
+
],
|
| 53 |
+
"max_position_embeddings": 40960,
|
| 54 |
+
"max_window_layers": 36,
|
| 55 |
+
"model_type": "qwen3",
|
| 56 |
+
"num_attention_heads": 32,
|
| 57 |
+
"num_hidden_layers": 36,
|
| 58 |
+
"num_key_value_heads": 8,
|
| 59 |
+
"pad_token_id": null,
|
| 60 |
+
"rms_norm_eps": 1e-06,
|
| 61 |
+
"rope_parameters": {
|
| 62 |
+
"rope_theta": 1000000,
|
| 63 |
+
"rope_type": "default"
|
| 64 |
+
},
|
| 65 |
+
"sliding_window": null,
|
| 66 |
+
"tie_word_embeddings": false,
|
| 67 |
+
"transformers_version": "5.3.0",
|
| 68 |
+
"use_cache": true,
|
| 69 |
+
"use_sliding_window": false,
|
| 70 |
+
"vocab_size": 151936
|
| 71 |
+
}
|
code-decoder/generation_config.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bos_token_id": 151643,
|
| 3 |
+
"do_sample": true,
|
| 4 |
+
"eos_token_id": [
|
| 5 |
+
151645,
|
| 6 |
+
151643
|
| 7 |
+
],
|
| 8 |
+
"pad_token_id": 151643,
|
| 9 |
+
"temperature": 0.6,
|
| 10 |
+
"top_k": 20,
|
| 11 |
+
"top_p": 0.95,
|
| 12 |
+
"transformers_version": "5.3.0"
|
| 13 |
+
}
|
code-decoder/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:00e991281cbfd766354a83b798f744b61f2518ca4a7ea5d6ad67d7b97660fcf5
|
| 3 |
+
size 16381517208
|
code-decoder/nla_meta.yaml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
kind: nla_dataset
|
| 2 |
+
schema_version: 2
|
| 3 |
+
dataset_id: av_sft_train.parquet
|
| 4 |
+
split_type: av_sft
|
| 5 |
+
row_count: 124741
|
| 6 |
+
extraction:
|
| 7 |
+
d_model: 4096
|
| 8 |
+
injection_scale: null
|
| 9 |
+
mse_scale: sqrt_d_model
|
| 10 |
+
norm: none
|
| 11 |
+
tokens:
|
| 12 |
+
injection_char: ㈎
|
| 13 |
+
injection_token_id: 149705
|
| 14 |
+
injection_left_neighbor_id: 29
|
| 15 |
+
injection_right_neighbor_id: 522
|
| 16 |
+
prompt_templates:
|
| 17 |
+
actor: 'You are a meticulous AI researcher conducting an important investigation
|
| 18 |
+
into activation vectors from a language model. Your overall task is to describe
|
| 19 |
+
the semantic content of that activation vector.
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
We will pass the vector enclosed in <concept> tags into your context. You must
|
| 23 |
+
then produce an explanation for the vector, enclosed within <explanation> tags.
|
| 24 |
+
The explanation consists of 2-3 text snippets describing that vector.
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
Here is the vector:
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
<concept>{injection_char}</concept>
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
Please provide an explanation.'
|
code-decoder/tokenizer.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ec81cf4cd3712164ba5c12a479975a0d80142cf066a39632c941c5b40e569bff
|
| 3 |
+
size 11422917
|
code-decoder/tokenizer_config.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"add_prefix_space": false,
|
| 3 |
+
"backend": "tokenizers",
|
| 4 |
+
"bos_token": null,
|
| 5 |
+
"clean_up_tokenization_spaces": false,
|
| 6 |
+
"eos_token": "<|im_end|>",
|
| 7 |
+
"errors": "replace",
|
| 8 |
+
"extra_special_tokens": [
|
| 9 |
+
"<|im_start|>",
|
| 10 |
+
"<|im_end|>",
|
| 11 |
+
"<|object_ref_start|>",
|
| 12 |
+
"<|object_ref_end|>",
|
| 13 |
+
"<|box_start|>",
|
| 14 |
+
"<|box_end|>",
|
| 15 |
+
"<|quad_start|>",
|
| 16 |
+
"<|quad_end|>",
|
| 17 |
+
"<|vision_start|>",
|
| 18 |
+
"<|vision_end|>",
|
| 19 |
+
"<|vision_pad|>",
|
| 20 |
+
"<|image_pad|>",
|
| 21 |
+
"<|video_pad|>"
|
| 22 |
+
],
|
| 23 |
+
"is_local": false,
|
| 24 |
+
"model_max_length": 131072,
|
| 25 |
+
"pad_token": "<|endoftext|>",
|
| 26 |
+
"split_special_tokens": false,
|
| 27 |
+
"tokenizer_class": "Qwen2Tokenizer",
|
| 28 |
+
"unk_token": null
|
| 29 |
+
}
|
nla/actor/chat_template.jinja
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- if tools %}
|
| 2 |
+
{{- '<|im_start|>system\n' }}
|
| 3 |
+
{%- if messages[0].role == 'system' %}
|
| 4 |
+
{{- messages[0].content + '\n\n' }}
|
| 5 |
+
{%- endif %}
|
| 6 |
+
{{- "# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
|
| 7 |
+
{%- for tool in tools %}
|
| 8 |
+
{{- "\n" }}
|
| 9 |
+
{{- tool | tojson }}
|
| 10 |
+
{%- endfor %}
|
| 11 |
+
{{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
|
| 12 |
+
{%- else %}
|
| 13 |
+
{%- if messages[0].role == 'system' %}
|
| 14 |
+
{{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
|
| 15 |
+
{%- endif %}
|
| 16 |
+
{%- endif %}
|
| 17 |
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 18 |
+
{%- for message in messages[::-1] %}
|
| 19 |
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 20 |
+
{%- if ns.multi_step_tool and message.role == "user" and message.content is string and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}
|
| 21 |
+
{%- set ns.multi_step_tool = false %}
|
| 22 |
+
{%- set ns.last_query_index = index %}
|
| 23 |
+
{%- endif %}
|
| 24 |
+
{%- endfor %}
|
| 25 |
+
{%- for message in messages %}
|
| 26 |
+
{%- if message.content is string %}
|
| 27 |
+
{%- set content = message.content %}
|
| 28 |
+
{%- else %}
|
| 29 |
+
{%- set content = '' %}
|
| 30 |
+
{%- endif %}
|
| 31 |
+
{%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
|
| 32 |
+
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
| 33 |
+
{%- elif message.role == "assistant" %}
|
| 34 |
+
{%- set reasoning_content = '' %}
|
| 35 |
+
{%- if message.reasoning_content is string %}
|
| 36 |
+
{%- set reasoning_content = message.reasoning_content %}
|
| 37 |
+
{%- else %}
|
| 38 |
+
{%- if '</think>' in content %}
|
| 39 |
+
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 40 |
+
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
| 41 |
+
{%- endif %}
|
| 42 |
+
{%- endif %}
|
| 43 |
+
{%- if loop.index0 > ns.last_query_index %}
|
| 44 |
+
{%- if loop.last or (not loop.last and reasoning_content) %}
|
| 45 |
+
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
|
| 46 |
+
{%- else %}
|
| 47 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 48 |
+
{%- endif %}
|
| 49 |
+
{%- else %}
|
| 50 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 51 |
+
{%- endif %}
|
| 52 |
+
{%- if message.tool_calls %}
|
| 53 |
+
{%- for tool_call in message.tool_calls %}
|
| 54 |
+
{%- if (loop.first and content) or (not loop.first) %}
|
| 55 |
+
{{- '\n' }}
|
| 56 |
+
{%- endif %}
|
| 57 |
+
{%- if tool_call.function %}
|
| 58 |
+
{%- set tool_call = tool_call.function %}
|
| 59 |
+
{%- endif %}
|
| 60 |
+
{{- '<tool_call>\n{"name": "' }}
|
| 61 |
+
{{- tool_call.name }}
|
| 62 |
+
{{- '", "arguments": ' }}
|
| 63 |
+
{%- if tool_call.arguments is string %}
|
| 64 |
+
{{- tool_call.arguments }}
|
| 65 |
+
{%- else %}
|
| 66 |
+
{{- tool_call.arguments | tojson }}
|
| 67 |
+
{%- endif %}
|
| 68 |
+
{{- '}\n</tool_call>' }}
|
| 69 |
+
{%- endfor %}
|
| 70 |
+
{%- endif %}
|
| 71 |
+
{{- '<|im_end|>\n' }}
|
| 72 |
+
{%- elif message.role == "tool" %}
|
| 73 |
+
{%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
|
| 74 |
+
{{- '<|im_start|>user' }}
|
| 75 |
+
{%- endif %}
|
| 76 |
+
{{- '\n<tool_response>\n' }}
|
| 77 |
+
{{- content }}
|
| 78 |
+
{{- '\n</tool_response>' }}
|
| 79 |
+
{%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
|
| 80 |
+
{{- '<|im_end|>\n' }}
|
| 81 |
+
{%- endif %}
|
| 82 |
+
{%- endif %}
|
| 83 |
+
{%- endfor %}
|
| 84 |
+
{%- if add_generation_prompt %}
|
| 85 |
+
{{- '<|im_start|>assistant\n' }}
|
| 86 |
+
{%- if enable_thinking is defined and enable_thinking is false %}
|
| 87 |
+
{{- '<think>\n\n</think>\n\n' }}
|
| 88 |
+
{%- endif %}
|
| 89 |
+
{%- endif %}
|
nla/actor/config.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"Qwen3ForCausalLM"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"bos_token_id": 151643,
|
| 8 |
+
"dtype": "float32",
|
| 9 |
+
"eos_token_id": 151645,
|
| 10 |
+
"head_dim": 128,
|
| 11 |
+
"hidden_act": "silu",
|
| 12 |
+
"hidden_size": 4096,
|
| 13 |
+
"initializer_range": 0.02,
|
| 14 |
+
"intermediate_size": 12288,
|
| 15 |
+
"layer_types": [
|
| 16 |
+
"full_attention",
|
| 17 |
+
"full_attention",
|
| 18 |
+
"full_attention",
|
| 19 |
+
"full_attention",
|
| 20 |
+
"full_attention",
|
| 21 |
+
"full_attention",
|
| 22 |
+
"full_attention",
|
| 23 |
+
"full_attention",
|
| 24 |
+
"full_attention",
|
| 25 |
+
"full_attention",
|
| 26 |
+
"full_attention",
|
| 27 |
+
"full_attention",
|
| 28 |
+
"full_attention",
|
| 29 |
+
"full_attention",
|
| 30 |
+
"full_attention",
|
| 31 |
+
"full_attention",
|
| 32 |
+
"full_attention",
|
| 33 |
+
"full_attention",
|
| 34 |
+
"full_attention",
|
| 35 |
+
"full_attention",
|
| 36 |
+
"full_attention",
|
| 37 |
+
"full_attention",
|
| 38 |
+
"full_attention",
|
| 39 |
+
"full_attention",
|
| 40 |
+
"full_attention",
|
| 41 |
+
"full_attention",
|
| 42 |
+
"full_attention",
|
| 43 |
+
"full_attention",
|
| 44 |
+
"full_attention",
|
| 45 |
+
"full_attention",
|
| 46 |
+
"full_attention",
|
| 47 |
+
"full_attention",
|
| 48 |
+
"full_attention",
|
| 49 |
+
"full_attention",
|
| 50 |
+
"full_attention",
|
| 51 |
+
"full_attention"
|
| 52 |
+
],
|
| 53 |
+
"max_position_embeddings": 40960,
|
| 54 |
+
"max_window_layers": 36,
|
| 55 |
+
"model_type": "qwen3",
|
| 56 |
+
"num_attention_heads": 32,
|
| 57 |
+
"num_hidden_layers": 36,
|
| 58 |
+
"num_key_value_heads": 8,
|
| 59 |
+
"pad_token_id": null,
|
| 60 |
+
"rms_norm_eps": 1e-06,
|
| 61 |
+
"rope_parameters": {
|
| 62 |
+
"rope_theta": 1000000,
|
| 63 |
+
"rope_type": "default"
|
| 64 |
+
},
|
| 65 |
+
"sliding_window": null,
|
| 66 |
+
"tie_word_embeddings": false,
|
| 67 |
+
"transformers_version": "5.3.0",
|
| 68 |
+
"use_cache": true,
|
| 69 |
+
"use_sliding_window": false,
|
| 70 |
+
"vocab_size": 151936
|
| 71 |
+
}
|
nla/actor/generation_config.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bos_token_id": 151643,
|
| 3 |
+
"do_sample": true,
|
| 4 |
+
"eos_token_id": [
|
| 5 |
+
151645,
|
| 6 |
+
151643
|
| 7 |
+
],
|
| 8 |
+
"pad_token_id": 151643,
|
| 9 |
+
"temperature": 0.6,
|
| 10 |
+
"top_k": 20,
|
| 11 |
+
"top_p": 0.95,
|
| 12 |
+
"transformers_version": "5.3.0"
|
| 13 |
+
}
|
nla/actor/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5c0b6aac09dd63b70062b1966ad66a08ec82dc5531d47f6de4206ba4f97fe032
|
| 3 |
+
size 16381517208
|
nla/actor/nla_meta.yaml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
kind: nla_dataset
|
| 2 |
+
schema_version: 2
|
| 3 |
+
dataset_id: av_sft_train.parquet
|
| 4 |
+
split_type: av_sft
|
| 5 |
+
row_count: 124741
|
| 6 |
+
extraction:
|
| 7 |
+
d_model: 4096
|
| 8 |
+
injection_scale: null
|
| 9 |
+
mse_scale: sqrt_d_model
|
| 10 |
+
norm: none
|
| 11 |
+
tokens:
|
| 12 |
+
injection_char: ㈎
|
| 13 |
+
injection_token_id: 149705
|
| 14 |
+
injection_left_neighbor_id: 29
|
| 15 |
+
injection_right_neighbor_id: 522
|
| 16 |
+
prompt_templates:
|
| 17 |
+
actor: 'You are a meticulous AI researcher conducting an important investigation
|
| 18 |
+
into activation vectors from a language model. Your overall task is to describe
|
| 19 |
+
the semantic content of that activation vector.
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
We will pass the vector enclosed in <concept> tags into your context. You must
|
| 23 |
+
then produce an explanation for the vector, enclosed within <explanation> tags.
|
| 24 |
+
The explanation consists of 2-3 text snippets describing that vector.
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
Here is the vector:
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
<concept>{injection_char}</concept>
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
Please provide an explanation.'
|
nla/actor/tokenizer.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:508635c756562e715417f4480d287917c332083cee37d2afca7f9b623fce55d9
|
| 3 |
+
size 11422916
|
nla/actor/tokenizer_config.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"add_prefix_space": false,
|
| 3 |
+
"backend": "tokenizers",
|
| 4 |
+
"bos_token": null,
|
| 5 |
+
"clean_up_tokenization_spaces": false,
|
| 6 |
+
"eos_token": "<|im_end|>",
|
| 7 |
+
"errors": "replace",
|
| 8 |
+
"extra_special_tokens": [
|
| 9 |
+
"<|im_start|>",
|
| 10 |
+
"<|im_end|>",
|
| 11 |
+
"<|object_ref_start|>",
|
| 12 |
+
"<|object_ref_end|>",
|
| 13 |
+
"<|box_start|>",
|
| 14 |
+
"<|box_end|>",
|
| 15 |
+
"<|quad_start|>",
|
| 16 |
+
"<|quad_end|>",
|
| 17 |
+
"<|vision_start|>",
|
| 18 |
+
"<|vision_end|>",
|
| 19 |
+
"<|vision_pad|>",
|
| 20 |
+
"<|image_pad|>",
|
| 21 |
+
"<|video_pad|>"
|
| 22 |
+
],
|
| 23 |
+
"is_local": false,
|
| 24 |
+
"model_max_length": 131072,
|
| 25 |
+
"pad_token": "<|endoftext|>",
|
| 26 |
+
"split_special_tokens": false,
|
| 27 |
+
"tokenizer_class": "Qwen2Tokenizer",
|
| 28 |
+
"unk_token": null
|
| 29 |
+
}
|
nla/critic/chat_template.jinja
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- if tools %}
|
| 2 |
+
{{- '<|im_start|>system\n' }}
|
| 3 |
+
{%- if messages[0].role == 'system' %}
|
| 4 |
+
{{- messages[0].content + '\n\n' }}
|
| 5 |
+
{%- endif %}
|
| 6 |
+
{{- "# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
|
| 7 |
+
{%- for tool in tools %}
|
| 8 |
+
{{- "\n" }}
|
| 9 |
+
{{- tool | tojson }}
|
| 10 |
+
{%- endfor %}
|
| 11 |
+
{{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
|
| 12 |
+
{%- else %}
|
| 13 |
+
{%- if messages[0].role == 'system' %}
|
| 14 |
+
{{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
|
| 15 |
+
{%- endif %}
|
| 16 |
+
{%- endif %}
|
| 17 |
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 18 |
+
{%- for message in messages[::-1] %}
|
| 19 |
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 20 |
+
{%- if ns.multi_step_tool and message.role == "user" and message.content is string and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}
|
| 21 |
+
{%- set ns.multi_step_tool = false %}
|
| 22 |
+
{%- set ns.last_query_index = index %}
|
| 23 |
+
{%- endif %}
|
| 24 |
+
{%- endfor %}
|
| 25 |
+
{%- for message in messages %}
|
| 26 |
+
{%- if message.content is string %}
|
| 27 |
+
{%- set content = message.content %}
|
| 28 |
+
{%- else %}
|
| 29 |
+
{%- set content = '' %}
|
| 30 |
+
{%- endif %}
|
| 31 |
+
{%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
|
| 32 |
+
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
| 33 |
+
{%- elif message.role == "assistant" %}
|
| 34 |
+
{%- set reasoning_content = '' %}
|
| 35 |
+
{%- if message.reasoning_content is string %}
|
| 36 |
+
{%- set reasoning_content = message.reasoning_content %}
|
| 37 |
+
{%- else %}
|
| 38 |
+
{%- if '</think>' in content %}
|
| 39 |
+
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 40 |
+
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
| 41 |
+
{%- endif %}
|
| 42 |
+
{%- endif %}
|
| 43 |
+
{%- if loop.index0 > ns.last_query_index %}
|
| 44 |
+
{%- if loop.last or (not loop.last and reasoning_content) %}
|
| 45 |
+
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
|
| 46 |
+
{%- else %}
|
| 47 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 48 |
+
{%- endif %}
|
| 49 |
+
{%- else %}
|
| 50 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 51 |
+
{%- endif %}
|
| 52 |
+
{%- if message.tool_calls %}
|
| 53 |
+
{%- for tool_call in message.tool_calls %}
|
| 54 |
+
{%- if (loop.first and content) or (not loop.first) %}
|
| 55 |
+
{{- '\n' }}
|
| 56 |
+
{%- endif %}
|
| 57 |
+
{%- if tool_call.function %}
|
| 58 |
+
{%- set tool_call = tool_call.function %}
|
| 59 |
+
{%- endif %}
|
| 60 |
+
{{- '<tool_call>\n{"name": "' }}
|
| 61 |
+
{{- tool_call.name }}
|
| 62 |
+
{{- '", "arguments": ' }}
|
| 63 |
+
{%- if tool_call.arguments is string %}
|
| 64 |
+
{{- tool_call.arguments }}
|
| 65 |
+
{%- else %}
|
| 66 |
+
{{- tool_call.arguments | tojson }}
|
| 67 |
+
{%- endif %}
|
| 68 |
+
{{- '}\n</tool_call>' }}
|
| 69 |
+
{%- endfor %}
|
| 70 |
+
{%- endif %}
|
| 71 |
+
{{- '<|im_end|>\n' }}
|
| 72 |
+
{%- elif message.role == "tool" %}
|
| 73 |
+
{%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
|
| 74 |
+
{{- '<|im_start|>user' }}
|
| 75 |
+
{%- endif %}
|
| 76 |
+
{{- '\n<tool_response>\n' }}
|
| 77 |
+
{{- content }}
|
| 78 |
+
{{- '\n</tool_response>' }}
|
| 79 |
+
{%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
|
| 80 |
+
{{- '<|im_end|>\n' }}
|
| 81 |
+
{%- endif %}
|
| 82 |
+
{%- endif %}
|
| 83 |
+
{%- endfor %}
|
| 84 |
+
{%- if add_generation_prompt %}
|
| 85 |
+
{{- '<|im_start|>assistant\n' }}
|
| 86 |
+
{%- if enable_thinking is defined and enable_thinking is false %}
|
| 87 |
+
{{- '<think>\n\n</think>\n\n' }}
|
| 88 |
+
{%- endif %}
|
| 89 |
+
{%- endif %}
|
nla/critic/config.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"Qwen3ForCausalLM"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"bos_token_id": 151643,
|
| 8 |
+
"dtype": "float32",
|
| 9 |
+
"eos_token_id": 151645,
|
| 10 |
+
"head_dim": 128,
|
| 11 |
+
"hidden_act": "silu",
|
| 12 |
+
"hidden_size": 4096,
|
| 13 |
+
"initializer_range": 0.02,
|
| 14 |
+
"intermediate_size": 12288,
|
| 15 |
+
"layer_types": [
|
| 16 |
+
"full_attention",
|
| 17 |
+
"full_attention",
|
| 18 |
+
"full_attention",
|
| 19 |
+
"full_attention",
|
| 20 |
+
"full_attention",
|
| 21 |
+
"full_attention",
|
| 22 |
+
"full_attention",
|
| 23 |
+
"full_attention",
|
| 24 |
+
"full_attention",
|
| 25 |
+
"full_attention",
|
| 26 |
+
"full_attention",
|
| 27 |
+
"full_attention",
|
| 28 |
+
"full_attention",
|
| 29 |
+
"full_attention",
|
| 30 |
+
"full_attention",
|
| 31 |
+
"full_attention",
|
| 32 |
+
"full_attention",
|
| 33 |
+
"full_attention",
|
| 34 |
+
"full_attention",
|
| 35 |
+
"full_attention",
|
| 36 |
+
"full_attention",
|
| 37 |
+
"full_attention",
|
| 38 |
+
"full_attention",
|
| 39 |
+
"full_attention",
|
| 40 |
+
"full_attention"
|
| 41 |
+
],
|
| 42 |
+
"max_position_embeddings": 40960,
|
| 43 |
+
"max_window_layers": 36,
|
| 44 |
+
"model_type": "qwen3",
|
| 45 |
+
"num_attention_heads": 32,
|
| 46 |
+
"num_hidden_layers": 25,
|
| 47 |
+
"num_key_value_heads": 8,
|
| 48 |
+
"pad_token_id": null,
|
| 49 |
+
"rms_norm_eps": 1e-06,
|
| 50 |
+
"rope_parameters": {
|
| 51 |
+
"rope_theta": 1000000,
|
| 52 |
+
"rope_type": "default"
|
| 53 |
+
},
|
| 54 |
+
"sliding_window": null,
|
| 55 |
+
"tie_word_embeddings": false,
|
| 56 |
+
"transformers_version": "5.3.0",
|
| 57 |
+
"use_cache": true,
|
| 58 |
+
"use_sliding_window": false,
|
| 59 |
+
"vocab_size": 151936
|
| 60 |
+
}
|
nla/critic/generation_config.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bos_token_id": 151643,
|
| 3 |
+
"do_sample": true,
|
| 4 |
+
"eos_token_id": [
|
| 5 |
+
151645,
|
| 6 |
+
151643
|
| 7 |
+
],
|
| 8 |
+
"pad_token_id": 151643,
|
| 9 |
+
"temperature": 0.6,
|
| 10 |
+
"top_k": 20,
|
| 11 |
+
"top_p": 0.95,
|
| 12 |
+
"transformers_version": "5.3.0"
|
| 13 |
+
}
|
nla/critic/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:64ccdb33deb4e857f61d79b2b430827cdc7422b97bb26ed30c15023b749710d4
|
| 3 |
+
size 10892013280
|
nla/critic/nla_meta.yaml
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
kind: nla_dataset
|
| 2 |
+
schema_version: 2
|
| 3 |
+
dataset_id: ar_sft_train.parquet
|
| 4 |
+
split_type: ar_sft
|
| 5 |
+
row_count: 124708
|
| 6 |
+
extraction:
|
| 7 |
+
d_model: 4096
|
| 8 |
+
injection_scale: null
|
| 9 |
+
mse_scale: sqrt_d_model
|
| 10 |
+
norm: none
|
| 11 |
+
tokens:
|
| 12 |
+
injection_char: ㈎
|
| 13 |
+
injection_token_id: 149705
|
| 14 |
+
injection_left_neighbor_id: 29
|
| 15 |
+
injection_right_neighbor_id: 522
|
| 16 |
+
critic_suffix_ids:
|
| 17 |
+
- 1318
|
| 18 |
+
- 29
|
| 19 |
+
- 366
|
| 20 |
+
- 1708
|
| 21 |
+
- 29
|
| 22 |
+
prompt_templates:
|
| 23 |
+
actor: 'You are a meticulous AI researcher conducting an important investigation
|
| 24 |
+
into activation vectors from a language model. Your overall task is to describe
|
| 25 |
+
the semantic content of that activation vector.
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
We will pass the vector enclosed in <concept> tags into your context. You must
|
| 29 |
+
then produce an explanation for the vector, enclosed within <explanation> tags.
|
| 30 |
+
The explanation consists of 2-3 text snippets describing that vector.
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
Here is the vector:
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
<concept>{injection_char}</concept>
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
Please provide an explanation.'
|
| 40 |
+
critic: 'Summary of the following text: <text>{explanation}</text> <summary>'
|
nla/critic/tokenizer.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:508635c756562e715417f4480d287917c332083cee37d2afca7f9b623fce55d9
|
| 3 |
+
size 11422916
|
nla/critic/tokenizer_config.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"add_prefix_space": false,
|
| 3 |
+
"backend": "tokenizers",
|
| 4 |
+
"bos_token": null,
|
| 5 |
+
"clean_up_tokenization_spaces": false,
|
| 6 |
+
"eos_token": "<|im_end|>",
|
| 7 |
+
"errors": "replace",
|
| 8 |
+
"extra_special_tokens": [
|
| 9 |
+
"<|im_start|>",
|
| 10 |
+
"<|im_end|>",
|
| 11 |
+
"<|object_ref_start|>",
|
| 12 |
+
"<|object_ref_end|>",
|
| 13 |
+
"<|box_start|>",
|
| 14 |
+
"<|box_end|>",
|
| 15 |
+
"<|quad_start|>",
|
| 16 |
+
"<|quad_end|>",
|
| 17 |
+
"<|vision_start|>",
|
| 18 |
+
"<|vision_end|>",
|
| 19 |
+
"<|vision_pad|>",
|
| 20 |
+
"<|image_pad|>",
|
| 21 |
+
"<|video_pad|>"
|
| 22 |
+
],
|
| 23 |
+
"is_local": true,
|
| 24 |
+
"model_max_length": 131072,
|
| 25 |
+
"pad_token": "<|endoftext|>",
|
| 26 |
+
"split_special_tokens": false,
|
| 27 |
+
"tokenizer_class": "Qwen2Tokenizer",
|
| 28 |
+
"unk_token": null
|
| 29 |
+
}
|
nla/critic/value_head.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7c723c4a55baf24ee2831146c25dcf0b08b256c1ebd90a05a2bea326d445484c
|
| 3 |
+
size 33554520
|
nla_code_demo.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Standalone demo for the tiny_nla code decoder. No repo checkout needed.
|
| 3 |
+
|
| 4 |
+
pip install torch transformers huggingface_hub numpy pyyaml
|
| 5 |
+
hf auth login
|
| 6 |
+
hf download ASSERT-KTH/tiny-nla nla_code_demo.py --local-dir .
|
| 7 |
+
python nla_code_demo.py --code 'def is_palindrome(s):
|
| 8 |
+
s = s.lower()
|
| 9 |
+
return s == s[::-1]'
|
| 10 |
+
|
| 11 |
+
Encodes a Python function to ONE 4096-float activation vector with the frozen
|
| 12 |
+
base model, injects that vector into the trained decoder, and asks it to write
|
| 13 |
+
the function back out.
|
| 14 |
+
|
| 15 |
+
This is not a lossless codec. On a 300-function held-out set: 1% exact, 7.7%
|
| 16 |
+
structurally near-identical, 71% retrieval@1 (is the output nearest its own
|
| 17 |
+
target among 300). The typical error is a SIBLING -- ask for send_video and get
|
| 18 |
+
send_document, same signature, wrong entity. Always run with --control, which
|
| 19 |
+
generates from a random vector so you can see what the code prior alone gives.
|
| 20 |
+
|
| 21 |
+
Needs one GPU with >=24 GB. The two models load sequentially, not together.
|
| 22 |
+
"""
|
| 23 |
+
import argparse, ast, builtins, difflib, keyword, re, sys
|
| 24 |
+
from pathlib import Path
|
| 25 |
+
import numpy as np, torch, yaml
|
| 26 |
+
from huggingface_hub import hf_hub_download
|
| 27 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 28 |
+
|
| 29 |
+
CODE_RE = re.compile(r"<code>\s*(.*?)\s*</code>", re.DOTALL)
|
| 30 |
+
STOP = set(keyword.kwlist) | set(dir(builtins)) | {"self", "cls"}
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def inject_at_marked_positions(input_ids, embeddings, vectors, inj_id, left_id, right_id):
|
| 34 |
+
"""Overwrite embedding rows at injection markers. Copied verbatim from
|
| 35 |
+
nla/training/injection.py -- the most correctness-critical path: if
|
| 36 |
+
injection fails the model sees the literal marker char and emits Chinese.
|
| 37 |
+
The neighbour check prevents false positives from the marker appearing in
|
| 38 |
+
generated text."""
|
| 39 |
+
out = embeddings.clone()
|
| 40 |
+
vectors = vectors.to(out.device, out.dtype)
|
| 41 |
+
seq_len = input_ids.shape[-1]
|
| 42 |
+
vec_idx = 0
|
| 43 |
+
for b, p in (input_ids == inj_id).nonzero(as_tuple=False).tolist():
|
| 44 |
+
if p == 0 or p == seq_len - 1:
|
| 45 |
+
continue
|
| 46 |
+
if input_ids[b, p - 1] != left_id or input_ids[b, p + 1] != right_id:
|
| 47 |
+
continue
|
| 48 |
+
out[b, p] = vectors[vec_idx]
|
| 49 |
+
vec_idx += 1
|
| 50 |
+
if vec_idx != vectors.shape[0]:
|
| 51 |
+
raise RuntimeError(f"found {vec_idx} injection sites, expected {vectors.shape[0]}")
|
| 52 |
+
return out
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def normalize_activation(v, target_scale):
|
| 56 |
+
if target_scale is None:
|
| 57 |
+
return v
|
| 58 |
+
return v / (v.float().norm(dim=-1, keepdim=True).clamp_min(1e-12) / target_scale).to(v.dtype)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def canon(src):
|
| 62 |
+
try:
|
| 63 |
+
return ast.unparse(ast.parse(src))
|
| 64 |
+
except SyntaxError as e:
|
| 65 |
+
sys.exit(f"input is not valid Python: {e}")
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def skeleton(src):
|
| 69 |
+
try:
|
| 70 |
+
return [type(n).__name__ for n in ast.walk(ast.parse(src))]
|
| 71 |
+
except SyntaxError:
|
| 72 |
+
return []
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def idents(src):
|
| 76 |
+
try:
|
| 77 |
+
t = ast.parse(src)
|
| 78 |
+
except SyntaxError:
|
| 79 |
+
return set()
|
| 80 |
+
o = set()
|
| 81 |
+
for n in ast.walk(t):
|
| 82 |
+
for a in ("id", "arg", "name", "attr"):
|
| 83 |
+
v = getattr(n, a, None)
|
| 84 |
+
if isinstance(v, str) and v not in STOP and not v.startswith("__"):
|
| 85 |
+
o.add(v)
|
| 86 |
+
return o
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def main():
|
| 90 |
+
p = argparse.ArgumentParser(description="reconstruct a Python function from its activation vector")
|
| 91 |
+
g = p.add_mutually_exclusive_group(required=True)
|
| 92 |
+
g.add_argument("--code", help="function source")
|
| 93 |
+
g.add_argument("--file", help="path to a .py file")
|
| 94 |
+
p.add_argument("--repo", default="ASSERT-KTH/tiny-nla")
|
| 95 |
+
p.add_argument("--subfolder", default="code-decoder")
|
| 96 |
+
p.add_argument("--base-model", default="Qwen/Qwen3-8B")
|
| 97 |
+
p.add_argument("--injection-scale", type=float, default=300.0)
|
| 98 |
+
p.add_argument("--max-new-tokens", type=int, default=256)
|
| 99 |
+
p.add_argument("--control", action="store_true",
|
| 100 |
+
help="also generate from a random vector -- the code prior with no information")
|
| 101 |
+
p.add_argument("--device", default="cuda")
|
| 102 |
+
a = p.parse_args()
|
| 103 |
+
|
| 104 |
+
code = canon(Path(a.file).read_text() if a.file else a.code)
|
| 105 |
+
|
| 106 |
+
meta = yaml.safe_load(open(hf_hub_download(a.repo, f"{a.subfolder}/nla_meta.yaml")))["tokens"]
|
| 107 |
+
inj, L, R, ch = (meta["injection_token_id"], meta["injection_left_neighbor_id"],
|
| 108 |
+
meta["injection_right_neighbor_id"], meta["injection_char"])
|
| 109 |
+
# A centred decoder given a raw vector produces fluent, plausible, entirely
|
| 110 |
+
# unrelated code with no error. Fetch the mean rather than trust the caller.
|
| 111 |
+
try:
|
| 112 |
+
mu = np.load(hf_hub_download(a.repo, f"{a.subfolder}/centre_mean.npy")).reshape(-1)
|
| 113 |
+
except Exception:
|
| 114 |
+
mu = None
|
| 115 |
+
print("note: no centre_mean.npy in repo -- treating decoder as raw-vector")
|
| 116 |
+
|
| 117 |
+
tok = AutoTokenizer.from_pretrained(a.base_model)
|
| 118 |
+
if tok.pad_token_id is None:
|
| 119 |
+
tok.pad_token_id = tok.eos_token_id
|
| 120 |
+
|
| 121 |
+
base = AutoModelForCausalLM.from_pretrained(
|
| 122 |
+
a.base_model, torch_dtype=torch.bfloat16, device_map={"": a.device}).eval()
|
| 123 |
+
layer = (2 * len(base.model.layers)) // 3
|
| 124 |
+
cap = {}
|
| 125 |
+
h = base.model.layers[layer].register_forward_hook(
|
| 126 |
+
lambda _m, _i, o: cap.__setitem__("h", (o[0] if isinstance(o, tuple) else o).detach()))
|
| 127 |
+
with torch.no_grad():
|
| 128 |
+
e = tok(code, return_tensors="pt", truncation=True, max_length=1024)
|
| 129 |
+
ids, m = e["input_ids"].to(a.device), e["attention_mask"].to(a.device)
|
| 130 |
+
base(input_ids=ids, attention_mask=m, use_cache=False)
|
| 131 |
+
h.remove()
|
| 132 |
+
v = cap["h"][0, int(m.sum()) - 1].float().cpu().numpy()
|
| 133 |
+
print(f"encoded {int(m.sum())} tokens at layer {layer} ‖v‖ = {np.linalg.norm(v):.1f}")
|
| 134 |
+
if mu is not None:
|
| 135 |
+
v = v - mu
|
| 136 |
+
print(f"centred (‖μ‖ = {np.linalg.norm(mu):.1f})")
|
| 137 |
+
del base
|
| 138 |
+
torch.cuda.empty_cache()
|
| 139 |
+
|
| 140 |
+
actor = AutoModelForCausalLM.from_pretrained(
|
| 141 |
+
a.repo, subfolder=a.subfolder, torch_dtype=torch.bfloat16,
|
| 142 |
+
device_map={"": a.device}).eval()
|
| 143 |
+
embed = actor.get_input_embeddings()
|
| 144 |
+
|
| 145 |
+
def gen(vec):
|
| 146 |
+
bv = normalize_activation(torch.tensor(vec).unsqueeze(0), a.injection_scale).to(a.device)
|
| 147 |
+
s = tok.apply_chat_template([{"role": "user", "content": f"<concept>{ch}</concept>"}],
|
| 148 |
+
tokenize=False, add_generation_prompt=True)
|
| 149 |
+
enc = tok(s, return_tensors="pt", add_special_tokens=False)
|
| 150 |
+
i2, m2 = enc["input_ids"].to(a.device), enc["attention_mask"].to(a.device)
|
| 151 |
+
hk = embed.register_forward_hook(
|
| 152 |
+
lambda _m, i, o: o if o.shape[1] <= 1 else inject_at_marked_positions(i[0], o, bv, inj, L, R))
|
| 153 |
+
try:
|
| 154 |
+
with torch.no_grad():
|
| 155 |
+
out = actor.generate(i2, attention_mask=m2, max_new_tokens=a.max_new_tokens,
|
| 156 |
+
do_sample=False, pad_token_id=tok.pad_token_id,
|
| 157 |
+
eos_token_id=tok.eos_token_id)
|
| 158 |
+
finally:
|
| 159 |
+
hk.remove()
|
| 160 |
+
mm = CODE_RE.search(tok.decode(out[0][i2.shape[1]:], skip_special_tokens=True))
|
| 161 |
+
return mm.group(1) if mm else None
|
| 162 |
+
|
| 163 |
+
pred = gen(v)
|
| 164 |
+
bar = "=" * 70
|
| 165 |
+
print(f"\n{bar}\nORIGINAL\n{bar}\n{code}")
|
| 166 |
+
print(f"{bar}\nRECONSTRUCTED\n{bar}\n{pred or '(no <code> block emitted)'}")
|
| 167 |
+
if pred:
|
| 168 |
+
pu, cu = idents(pred), idents(code)
|
| 169 |
+
print(bar)
|
| 170 |
+
print(f"exact match : {pred.strip() == code.strip()}")
|
| 171 |
+
print(f"AST-skeleton sim : {difflib.SequenceMatcher(None, skeleton(pred), skeleton(code)).ratio():.3f}"
|
| 172 |
+
f" (~0.52 for two arbitrary functions)")
|
| 173 |
+
print(f"identifier recall : {len(pu & cu) / max(len(cu), 1):.2f} shared: {sorted(pu & cu)[:8]}")
|
| 174 |
+
if a.control:
|
| 175 |
+
rng = np.random.default_rng(0)
|
| 176 |
+
print(f"{bar}\nCONTROL (random vector -- the code prior with no information)\n{bar}")
|
| 177 |
+
print(gen(rng.normal(0, np.std(v), size=v.shape).astype(np.float32)) or "(none)")
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
if __name__ == "__main__":
|
| 181 |
+
main()
|
nla_demo.py
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""tiny_nla demo — one entry point, no model to choose.
|
| 3 |
+
|
| 4 |
+
pip install torch transformers accelerate huggingface_hub numpy pyyaml
|
| 5 |
+
hf auth login
|
| 6 |
+
hf download ASSERT-KTH/tiny-nla nla_demo.py --local-dir .
|
| 7 |
+
|
| 8 |
+
python nla_demo.py --mode code 'def gcd(a, b):
|
| 9 |
+
while b: a, b = b, a % b
|
| 10 |
+
return a' # -> reconstructs the function
|
| 11 |
+
|
| 12 |
+
python nla_demo.py --mode text 'The Federal Reserve announced yesterday'
|
| 13 |
+
# -> explains the activation
|
| 14 |
+
|
| 15 |
+
python nla_demo.py --mode code --file mymodule.py --control
|
| 16 |
+
|
| 17 |
+
Compresses the input to ONE 4096-float activation vector from a frozen
|
| 18 |
+
Qwen3-8B, then asks a trained decoder to write it back out. YOU pick the mode;
|
| 19 |
+
the CHECKPOINT is chosen for you -- always whatever currently sits at the
|
| 20 |
+
pinned Hub paths, with no flag to select an older one.
|
| 21 |
+
|
| 22 |
+
Needs one GPU with >=24 GB. Models load sequentially, not together.
|
| 23 |
+
"""
|
| 24 |
+
import argparse, ast, builtins, difflib, keyword, os, re, shutil, subprocess, sys, tempfile
|
| 25 |
+
from pathlib import Path
|
| 26 |
+
|
| 27 |
+
# HF_HOME must be set BEFORE huggingface_hub is imported -- it snapshots the
|
| 28 |
+
# cache location at import time, so doing this inside main() is too late. When
|
| 29 |
+
# the user has not chosen one, prefer this repo's cache: the weights are very
|
| 30 |
+
# likely already there, and the default (~/.cache) would re-download ~33 GB
|
| 31 |
+
# into a home directory that may not have the quota for it.
|
| 32 |
+
if not os.environ.get("HF_HOME"):
|
| 33 |
+
_repo_cache = Path(__file__).resolve().parent.parent / "cache" / "huggingface"
|
| 34 |
+
if _repo_cache.is_dir():
|
| 35 |
+
os.environ["HF_HOME"] = str(_repo_cache)
|
| 36 |
+
|
| 37 |
+
import numpy as np, torch, transformers, yaml
|
| 38 |
+
from huggingface_hub import hf_hub_download
|
| 39 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 40 |
+
|
| 41 |
+
REPO = "ASSERT-KTH/tiny-nla"
|
| 42 |
+
CODE_SUB, TEXT_SUB = "code-decoder", "nla/actor"
|
| 43 |
+
BASE = "Qwen/Qwen3-8B"
|
| 44 |
+
# Verified per mode by A/B (job 17444219) -- do NOT "unify" these.
|
| 45 |
+
# text + minimal -> emits <answer>, drifts off-topic (regex misses, raw falls through)
|
| 46 |
+
# code + sidecar -> ident recall 0.00: writes a function ABOUT activation vectors,
|
| 47 |
+
# following the preamble's subject instead of the injected vector
|
| 48 |
+
# the winners -> code 1.00 recall / AST 0.862 ; text = correct 2-3 snippet format
|
| 49 |
+
# Each checkpoint wants the prompt IT was trained on. code-decoder's sidecar
|
| 50 |
+
# prompt_templates is a stale dataset-builder default and does not record that.
|
| 51 |
+
PROMPT_MODE = {"code": "minimal", "text": "sidecar"}
|
| 52 |
+
|
| 53 |
+
# transformers 5.x renamed torch_dtype -> dtype and warns on the old name; 4.x
|
| 54 |
+
# does not know the new one. An unpinned `pip install transformers` gets 5.x.
|
| 55 |
+
_DT = ({"dtype": torch.bfloat16}
|
| 56 |
+
if int(transformers.__version__.split(".")[0]) >= 5
|
| 57 |
+
else {"torch_dtype": torch.bfloat16})
|
| 58 |
+
CODE_RE = re.compile(r"<code>\s*(.*?)\s*</code>", re.DOTALL)
|
| 59 |
+
EXPL_RE = re.compile(r"<explanation>\s*(.*?)\s*</explanation>", re.DOTALL)
|
| 60 |
+
STOP = set(keyword.kwlist) | set(dir(builtins)) | {"self", "cls"}
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def inject_at_marked_positions(input_ids, embeddings, vectors, inj_id, left_id, right_id):
|
| 64 |
+
"""Verbatim from nla/training/injection.py. The neighbour check prevents
|
| 65 |
+
false positives; if injection silently fails the model emits Chinese."""
|
| 66 |
+
out = embeddings.clone()
|
| 67 |
+
vectors = vectors.to(out.device, out.dtype)
|
| 68 |
+
n, k = input_ids.shape[-1], 0
|
| 69 |
+
for b, p in (input_ids == inj_id).nonzero(as_tuple=False).tolist():
|
| 70 |
+
if p == 0 or p == n - 1:
|
| 71 |
+
continue
|
| 72 |
+
if input_ids[b, p - 1] != left_id or input_ids[b, p + 1] != right_id:
|
| 73 |
+
continue
|
| 74 |
+
out[b, p] = vectors[k]; k += 1
|
| 75 |
+
if k != vectors.shape[0]:
|
| 76 |
+
raise RuntimeError(f"found {k} injection sites, expected {vectors.shape[0]}")
|
| 77 |
+
return out
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def normalize_activation(v, scale):
|
| 81 |
+
return v / (v.float().norm(dim=-1, keepdim=True).clamp_min(1e-12) / scale).to(v.dtype)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def scratch_dir():
|
| 85 |
+
"""A writable scratch directory that is never /tmp.
|
| 86 |
+
|
| 87 |
+
/tmp is off limits on this cluster and is node-local anyway, but both
|
| 88 |
+
tempfile and triton default there. Honour an explicit TMPDIR when it does
|
| 89 |
+
not point into /tmp, else fall back to the user's cache dir.
|
| 90 |
+
"""
|
| 91 |
+
for cand in (os.environ.get("TMPDIR"), os.environ.get("XDG_CACHE_HOME")):
|
| 92 |
+
if cand and not os.path.realpath(cand).startswith("/tmp"):
|
| 93 |
+
d = os.path.join(cand, "tiny_nla")
|
| 94 |
+
try:
|
| 95 |
+
os.makedirs(d, exist_ok=True)
|
| 96 |
+
return d
|
| 97 |
+
except OSError:
|
| 98 |
+
pass
|
| 99 |
+
d = os.path.join(os.path.expanduser("~"), ".cache", "tiny_nla")
|
| 100 |
+
os.makedirs(d, exist_ok=True)
|
| 101 |
+
return d
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def ensure_compiler():
|
| 105 |
+
"""Pick a C compiler that actually works, before torch needs one.
|
| 106 |
+
|
| 107 |
+
Triton JIT-compiles a CUDA helper on first GPU use and shells out to `cc`.
|
| 108 |
+
On NSC/Berzelius the gcc first on PATH is a wrapper that refuses to run
|
| 109 |
+
without a build-env module loaded; the refusal surfaces as a
|
| 110 |
+
CalledProcessError fifteen frames deep inside triton, naming a .c file the
|
| 111 |
+
user never wrote. Probe candidates once and export the first that links.
|
| 112 |
+
Costs one ~100ms compile, and is skipped entirely if CC is already set.
|
| 113 |
+
"""
|
| 114 |
+
if os.environ.get("CC"):
|
| 115 |
+
return
|
| 116 |
+
with tempfile.TemporaryDirectory(dir=scratch_dir()) as d:
|
| 117 |
+
src = os.path.join(d, "probe.c")
|
| 118 |
+
with open(src, "w") as f:
|
| 119 |
+
f.write("int main(){return 0;}")
|
| 120 |
+
for cand in (shutil.which("cc"), shutil.which("gcc"), "/usr/bin/gcc"):
|
| 121 |
+
if not cand or not os.path.exists(cand):
|
| 122 |
+
continue
|
| 123 |
+
try:
|
| 124 |
+
r = subprocess.run([cand, "-shared", "-fPIC", "-o",
|
| 125 |
+
os.path.join(d, "probe.so"), src],
|
| 126 |
+
capture_output=True, timeout=60)
|
| 127 |
+
except Exception:
|
| 128 |
+
continue
|
| 129 |
+
if r.returncode == 0:
|
| 130 |
+
os.environ["CC"] = cand
|
| 131 |
+
return
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def parses(s):
|
| 135 |
+
try:
|
| 136 |
+
ast.parse(s); return True
|
| 137 |
+
except SyntaxError:
|
| 138 |
+
return False
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
# Keywords of the curly-brace languages, so a lexical scan of non-Python source
|
| 142 |
+
# does not report `int` or `return` as recovered identifiers.
|
| 143 |
+
OTHER_KW = {"int", "float", "double", "char", "bool", "void", "long", "short",
|
| 144 |
+
"unsigned", "signed", "const", "static", "struct", "class", "public",
|
| 145 |
+
"private", "protected", "return", "if", "else", "for", "while", "do",
|
| 146 |
+
"switch", "case", "break", "continue", "new", "delete", "using",
|
| 147 |
+
"namespace", "template", "typename", "include", "std", "auto", "let",
|
| 148 |
+
"var", "function", "def", "fn", "pub", "impl", "final", "extends"}
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def idents(src):
|
| 152 |
+
"""Identifiers in `src`, Python-aware when possible, lexical otherwise."""
|
| 153 |
+
try:
|
| 154 |
+
t = ast.parse(src)
|
| 155 |
+
except SyntaxError:
|
| 156 |
+
return {w for w in re.findall(r"[A-Za-z_][A-Za-z0-9_]*", src)
|
| 157 |
+
if w not in STOP and w not in OTHER_KW and not w.startswith("__")}
|
| 158 |
+
return {v for n in ast.walk(t) for a in ("id", "arg", "name", "attr")
|
| 159 |
+
if isinstance(v := getattr(n, a, None), str) and v not in STOP and not v.startswith("__")}
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def main():
|
| 163 |
+
p = argparse.ArgumentParser(description="tiny_nla demo — compress to one vector, decode it back")
|
| 164 |
+
p.add_argument("--prompt", choices=["minimal", "sidecar"], default=None,
|
| 165 |
+
help="override the injection prompt (diagnostic; default is per-mode verified)")
|
| 166 |
+
p.add_argument("--mode", required=True, choices=["code", "text"],
|
| 167 |
+
help="code = reconstruct a Python function; text = explain the activation")
|
| 168 |
+
p.add_argument("input", nargs="?", help="Python function, or any text")
|
| 169 |
+
p.add_argument("--file", help="read the input from a file instead")
|
| 170 |
+
p.add_argument("--control", action="store_true",
|
| 171 |
+
help="also decode a RANDOM vector — what the model produces with no information")
|
| 172 |
+
p.add_argument("--max-new-tokens", type=int, default=256)
|
| 173 |
+
p.add_argument("--device", default="cuda")
|
| 174 |
+
a = p.parse_args()
|
| 175 |
+
if not a.input and not a.file:
|
| 176 |
+
p.error("give some input, or --file")
|
| 177 |
+
raw = Path(a.file).read_text() if a.file else a.input
|
| 178 |
+
|
| 179 |
+
code_mode = a.mode == "code"
|
| 180 |
+
if code_mode and parses(raw):
|
| 181 |
+
raw = ast.unparse(ast.parse(raw)) # canonicalise so formatting is not counted
|
| 182 |
+
elif code_mode:
|
| 183 |
+
# Not Python: encode and decode it anyway. Only the AST-based scoring
|
| 184 |
+
# needs a parse, so it degrades rather than refusing. The DECODER was
|
| 185 |
+
# trained on Python only, so treat any other language as exploratory.
|
| 186 |
+
print("note: input is not valid Python -- skipping canonicalisation and "
|
| 187 |
+
"AST scoring. The decoder is Python-trained; other languages are "
|
| 188 |
+
"out of distribution.")
|
| 189 |
+
# Keep triton's JIT scratch and every tempfile default off /tmp.
|
| 190 |
+
_sd = scratch_dir()
|
| 191 |
+
if os.path.realpath(os.environ.get("TMPDIR", "/tmp")).startswith("/tmp"):
|
| 192 |
+
os.environ["TMPDIR"] = _sd
|
| 193 |
+
tempfile.tempdir = _sd
|
| 194 |
+
os.environ.setdefault("TRITON_CACHE_DIR", os.path.join(_sd, "triton"))
|
| 195 |
+
os.environ.setdefault("TORCHINDUCTOR_CACHE_DIR", os.path.join(_sd, "inductor"))
|
| 196 |
+
ensure_compiler()
|
| 197 |
+
sub = CODE_SUB if code_mode else TEXT_SUB
|
| 198 |
+
print(f"mode: {'CODE reconstruction' if code_mode else 'TEXT explanation'} "
|
| 199 |
+
f"(checkpoint chosen automatically: {REPO}/{sub})")
|
| 200 |
+
|
| 201 |
+
try:
|
| 202 |
+
sc = yaml.safe_load(open(hf_hub_download(REPO, f"{sub}/nla_meta.yaml")))
|
| 203 |
+
except Exception as e:
|
| 204 |
+
if "401" in str(e) or "Repository Not Found" in str(e):
|
| 205 |
+
sys.exit(
|
| 206 |
+
f"cannot read {REPO} (401).\n"
|
| 207 |
+
f" HF_HOME = {os.environ.get('HF_HOME', '<unset, using ~/.cache/huggingface>')}\n"
|
| 208 |
+
f" token = {'found' if os.path.exists(os.path.join(os.environ.get('HF_HOME', os.path.expanduser('~/.cache/huggingface')), 'token')) else 'NOT FOUND in that HF_HOME'}\n"
|
| 209 |
+
f" The usual cause is HF_HOME pointing at a cache with no token,\n"
|
| 210 |
+
f" not a missing account. Point it at the cache you logged into:\n"
|
| 211 |
+
f" export HF_HOME=/path/to/cache/huggingface\n"
|
| 212 |
+
f" Otherwise run `hf auth login`, and ask the repo owner for access.")
|
| 213 |
+
raise
|
| 214 |
+
meta = sc["tokens"]
|
| 215 |
+
inj, L, R, ch = (meta["injection_token_id"], meta["injection_left_neighbor_id"],
|
| 216 |
+
meta["injection_right_neighbor_id"], meta["injection_char"])
|
| 217 |
+
try: # a centred decoder given a raw vector yields fluent, unrelated output
|
| 218 |
+
mu = np.load(hf_hub_download(REPO, f"{sub}/centre_mean.npy")).reshape(-1)
|
| 219 |
+
except Exception:
|
| 220 |
+
mu = None
|
| 221 |
+
|
| 222 |
+
tok = AutoTokenizer.from_pretrained(BASE)
|
| 223 |
+
if tok.pad_token_id is None:
|
| 224 |
+
tok.pad_token_id = tok.eos_token_id
|
| 225 |
+
|
| 226 |
+
base = AutoModelForCausalLM.from_pretrained(
|
| 227 |
+
BASE, **_DT, device_map={"": a.device}).eval()
|
| 228 |
+
layer = (2 * len(base.model.layers)) // 3
|
| 229 |
+
cap = {}
|
| 230 |
+
h = base.model.layers[layer].register_forward_hook(
|
| 231 |
+
lambda _m, _i, o: cap.__setitem__("h", (o[0] if isinstance(o, tuple) else o).detach()))
|
| 232 |
+
with torch.no_grad():
|
| 233 |
+
e = tok(raw, return_tensors="pt", truncation=True, max_length=1024)
|
| 234 |
+
ids, m = e["input_ids"].to(a.device), e["attention_mask"].to(a.device)
|
| 235 |
+
base(input_ids=ids, attention_mask=m, use_cache=False)
|
| 236 |
+
h.remove()
|
| 237 |
+
v = cap["h"][0, int(m.sum()) - 1].float().cpu().numpy()
|
| 238 |
+
print(f"encoded {int(m.sum())} tokens at layer {layer} ‖v‖={np.linalg.norm(v):.1f}"
|
| 239 |
+
+ (f" centred (‖μ‖={np.linalg.norm(mu):.1f})" if mu is not None else ""))
|
| 240 |
+
if mu is not None:
|
| 241 |
+
v = v - mu
|
| 242 |
+
del base; torch.cuda.empty_cache()
|
| 243 |
+
|
| 244 |
+
actor = AutoModelForCausalLM.from_pretrained(
|
| 245 |
+
REPO, subfolder=sub, **_DT, device_map={"": a.device}).eval()
|
| 246 |
+
embed = actor.get_input_embeddings()
|
| 247 |
+
|
| 248 |
+
# Which prompt a checkpoint wants is a property of how it was TRAINED, and the
|
| 249 |
+
# sidecar's prompt_templates is a dataset-builder default that does not always
|
| 250 |
+
# match. Both were measured; see PROMPT_MODE.
|
| 251 |
+
want = a.prompt or PROMPT_MODE[a.mode]
|
| 252 |
+
tmpl = (sc.get("prompt_templates") or {}).get("actor") if want == "sidecar" else None
|
| 253 |
+
user_msg = tmpl.replace("{injection_char}", ch) if tmpl else f"<concept>{ch}</concept>"
|
| 254 |
+
|
| 255 |
+
def gen(vec):
|
| 256 |
+
bv = normalize_activation(torch.tensor(vec).unsqueeze(0), 300.0).to(a.device)
|
| 257 |
+
s = tok.apply_chat_template([{"role": "user", "content": user_msg}],
|
| 258 |
+
tokenize=False, add_generation_prompt=True)
|
| 259 |
+
enc = tok(s, return_tensors="pt", add_special_tokens=False)
|
| 260 |
+
i2, m2 = enc["input_ids"].to(a.device), enc["attention_mask"].to(a.device)
|
| 261 |
+
hk = embed.register_forward_hook(
|
| 262 |
+
lambda _m, i, o: o if o.shape[1] <= 1 else inject_at_marked_positions(i[0], o, bv, inj, L, R))
|
| 263 |
+
try:
|
| 264 |
+
with torch.no_grad():
|
| 265 |
+
out = actor.generate(i2, attention_mask=m2, max_new_tokens=a.max_new_tokens,
|
| 266 |
+
do_sample=False, pad_token_id=tok.pad_token_id,
|
| 267 |
+
eos_token_id=tok.eos_token_id)
|
| 268 |
+
finally:
|
| 269 |
+
hk.remove()
|
| 270 |
+
txt = tok.decode(out[0][i2.shape[1]:], skip_special_tokens=True)
|
| 271 |
+
mm = (CODE_RE if code_mode else EXPL_RE).search(txt)
|
| 272 |
+
return mm.group(1) if mm else txt.strip()[:600]
|
| 273 |
+
|
| 274 |
+
bar = "=" * 70
|
| 275 |
+
pred = gen(v)
|
| 276 |
+
print(f"\n{bar}\nINPUT\n{bar}\n{raw[:600]}")
|
| 277 |
+
print(f"{bar}\n{'RECONSTRUCTED' if code_mode else 'EXPLANATION'}\n{bar}\n{pred}")
|
| 278 |
+
if code_mode and pred:
|
| 279 |
+
pu, cu = idents(pred), idents(raw)
|
| 280 |
+
def _shape(x):
|
| 281 |
+
return [type(n).__name__ for n in ast.walk(ast.parse(x))]
|
| 282 |
+
sk = (difflib.SequenceMatcher(None, _shape(pred), _shape(raw)).ratio()
|
| 283 |
+
if parses(pred) and parses(raw) else None)
|
| 284 |
+
print(bar)
|
| 285 |
+
print(f"exact match : {pred.strip() == raw.strip()}")
|
| 286 |
+
print(f"AST-skeleton sim : "
|
| 287 |
+
+ (f"{sk:.3f} (~0.52 for two arbitrary functions)" if sk is not None
|
| 288 |
+
else "n/a (needs both sides to be valid Python)"))
|
| 289 |
+
print(f"identifier recall : {len(pu & cu)/max(len(cu),1):.2f} shared: {sorted(pu & cu)[:8]}")
|
| 290 |
+
if a.control:
|
| 291 |
+
rng = np.random.default_rng(0)
|
| 292 |
+
print(f"{bar}\nCONTROL — random vector, i.e. the model's prior with no information\n{bar}")
|
| 293 |
+
print(gen(rng.normal(0, np.std(v), size=v.shape).astype(np.float32)))
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
if __name__ == "__main__":
|
| 297 |
+
main()
|