ehartford commited on
Commit
9b51f42
·
verified ·
1 Parent(s): 5f73d3c

untrained initialization, seed 1

Browse files
init/README.md ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: cc-by-4.0
3
+ language: [en]
4
+ library_name: transformers
5
+ pipeline_tag: text-generation
6
+ base_model: QuixiAI/MaleCNS
7
+ base_model_relation: finetune
8
+ datasets: [karpathy/tiny_shakespeare]
9
+ tags: [connectome, fruit-fly, drosophila, malecns, recurrent, sparse, tiny-shakespeare, custom_code]
10
+ ---
11
+
12
+ # QuixiAI/FlyGPT
13
+
14
+ A character-level language model whose recurrent architecture is **a real subgraph of the fruit-fly brain
15
+ connectome** ([MaleCNS v1.0](https://male-cns.janelia.org/)). Unlike the earlier frozen-reservoir approach in
16
+ [ngxson/fly-llm-hf](https://huggingface.co/ngxson/fly-llm-hf), which keeps the connectome's synaptic weights fixed and
17
+ trains only the projections and readout, FlyGPT **trains one value per real synaptic connection with gradient
18
+ descent** while keeping the fly's edge topology fixed, and compares the result against the same neurons with
19
+ degree-preserving scrambled connections across paired seeds.
20
+
21
+ **Base model: [QuixiAI/MaleCNS](https://huggingface.co/QuixiAI/MaleCNS)**, the lossless packaging of the MaleCNS v1.0
22
+ connectivity tables. FlyGPT's graph is extracted from it deterministically (`build_graph.py`, revision pinned in
23
+ `data/fly/build_edges.py`); `graph.node_id` and `graph.synapse_count` map every edge back to that repository.
24
+
25
+ This checkpoint's wiring is the original MaleCNS wiring.
26
+
27
+ **Untrained initialization.** The recurrent edge values and adapters are the exact random initialization that FlyGPT's `train.py` starts from for seed 1. Text generated from this checkpoint is noise by design.
28
+
29
+ This is not a biological simulation of a living fly. The "weights" in the MaleCNS release are anatomical synapse
30
+ counts; they are stored here as `graph.synapse_count` and are **not** the model's parameters.
31
+
32
+ ## The graph
33
+
34
+ Every number below is produced by FlyGPT's extraction script (`build_graph.py`), not typed by hand.
35
+
36
+ | | |
37
+ |---|---|
38
+ | Source | MaleCNS v1.0 flat connectome (`gs://flyem-male-cns/v1.0/connectome-data/flat-connectome/`) |
39
+ | Candidate pool | central brain: `superclass` starting with `cb_` (37,108 neurons) |
40
+ | Minimum synapses per connection | 3 (engineering choice, not a biological claim) |
41
+ | Extraction | largest SCC → largest directed (k,k)-core with ≥ target nodes (k = 40) → trim by weighted degree |
42
+ | Neurons used | 5,000 |
43
+ | Directed connections used | 524,324 |
44
+ | Synaptic contacts represented | 8,300,915 |
45
+ | Largest SCC fraction | 1.0 |
46
+ | Reciprocal pairs | 93,055 |
47
+ | Input / output neurons | top 256 by out-degree / top 512 by in-degree |
48
+ | Input→output shortest path (median / p90 / max hops) | 1.0 / 1.0 / 1.0 |
49
+ | Graph hash | `f82b783b7ccb5a354fc4cf3de6de4a98d75029303c55f8faae28ab807828a007` |
50
+
51
+ `graph.node_id` holds the MaleCNS body ids, so every neuron maps back to the release.
52
+
53
+ ## What is in `model.safetensors`
54
+
55
+ | tensor | shape | dtype | size |
56
+ |---|---|---|---|
57
+ | `graph.edge_index` | (2, 524324) | int32 | 4.19 MB |
58
+ | `graph.synapse_count` | (524324,) | int32 | 2.10 MB |
59
+ | `graph.node_id` | (5000,) | int64 | 0.04 MB |
60
+ | `graph.input_nodes` | (256,) | int64 | 0.00 MB |
61
+ | `graph.output_nodes` | (512,) | int64 | 0.00 MB |
62
+ | `recurrent.edge_values` | (524324,) | bfloat16 | 1.05 MB |
63
+ | `recurrent.bias` | (5000,) | bfloat16 | 0.01 MB |
64
+ | `recurrent.raw_leak` | (5000,) | bfloat16 | 0.01 MB |
65
+ | `embed.weight` | (65, 32) | bfloat16 | 0.00 MB |
66
+ | `input_proj.weight` | (256, 32) | bfloat16 | 0.02 MB |
67
+ | `input_proj.bias` | (256,) | bfloat16 | 0.00 MB |
68
+ | `lm_head.weight` | (65, 512) | bfloat16 | 0.07 MB |
69
+ | `lm_head.bias` | (65,) | bfloat16 | 0.00 MB |
70
+
71
+ `graph.*` is the anatomy (integer, never trained). `recurrent.*`, `embed.*`, `input_proj.*`, `lm_head.*` are the
72
+ learned state, stored in bf16. The sparse recurrent matmul is rebuilt in fp32 at runtime (rows = destination,
73
+ columns = source), with each incoming edge scaled by `1/sqrt(in_degree)`.
74
+
75
+ ## Dynamics
76
+
77
+ ```text
78
+ character → embedding (32) → linear → 256 input neurons
79
+ proposal_i = tanh( Σ_j W_ij h_j / sqrt(in_degree_i) + external_input_i + bias_i )
80
+ h_i ← (1 − leak_i) h_i + leak_i · proposal_i (2 microsteps per character, leak_i = sigmoid(raw_leak_i))
81
+ 512 output neuron states → linear → 65 logits
82
+ ```
83
+
84
+ ## Usage
85
+
86
+ ```python
87
+ import torch
88
+ from transformers import AutoModelForCausalLM, AutoTokenizer
89
+
90
+ tok = AutoTokenizer.from_pretrained("QuixiAI/FlyGPT") # tokenizer is shared
91
+ model = AutoModelForCausalLM.from_pretrained("QuixiAI/FlyGPT", subfolder="init", trust_remote_code=True, dtype=torch.float32)
92
+
93
+ ids = tok("ROMEO:", return_tensors="pt").input_ids
94
+ out = model.generate(ids, max_new_tokens=300, do_sample=True, temperature=0.8)
95
+ print(tok.decode(out[0]))
96
+ ```
97
+
98
+ The tokenizer is strict: only the 65 characters of Tiny Shakespeare are encodable.
99
+
100
+ ## Citation
101
+
102
+ If you use this model, please cite it, its base model, and the MaleCNS dataset paper.
103
+
104
+ This model:
105
+
106
+ ```bibtex
107
+ @misc{hartford2026flygpt,
108
+ title = {FlyGPT: a language model whose recurrent architecture is a real subgraph of the fruit-fly connectome},
109
+ author = {Hartford, Eric},
110
+ year = {2026},
111
+ publisher = {Hugging Face},
112
+ howpublished = {\url{https://huggingface.co/QuixiAI/FlyGPT}},
113
+ note = {Base model: QuixiAI/MaleCNS (MaleCNS v1.0, Berg et al. 2026, CC-BY 4.0). Code: https://github.com/QuixiAI/FlyGPT}
114
+ }
115
+ ```
116
+
117
+ The base model (lossless connectome packaging):
118
+
119
+ ```bibtex
120
+ @misc{hartford2026malecns,
121
+ title = {QuixiAI/MaleCNS: the MaleCNS v1.0 fruit-fly connectome as lossless Safetensors},
122
+ author = {Hartford, Eric},
123
+ year = {2026},
124
+ publisher = {Hugging Face},
125
+ doi = {10.57967/hf/10410},
126
+ howpublished = {\url{https://huggingface.co/QuixiAI/MaleCNS}},
127
+ note = {Repackaging of Berg et al. (2026), CC-BY 4.0}
128
+ }
129
+ ```
130
+
131
+ The dataset (required by the CC-BY 4.0 license):
132
+
133
+ ```bibtex
134
+ @article{berg2026malecns,
135
+ title = {Sexual dimorphism in the complete {Drosophila} male central nervous system connectome},
136
+ author = {Berg, Stuart and Beckett, Isabella R. and Costa, Marta and Schlegel, Philipp and Januszewski, Michał and Marin, Elizabeth C. and Nern, Aljoscha and Preibisch, Stephan and Qiu, Wei and Takemura, Shin-ya and Fragniere, Alexandra M.C. and Champion, Andrew S. and Adjavon, Diane-Yayra and Cook, Michael and Gkantia, Marina and Hayworth, Kenneth J. and Huang, Gary B. and Katz, William T. and Kämpf, Florian and Lu, Zhiyuan and Ordish, Christopher and Paterson, Tyler and Stürner, Tomke and Trautman, Eric T. and Whittle, Catherine R. and Burnett, Laura E. and Hoeller, Judith and Li, Feng and Loesche, Frank and Morris, Billy J. and Pietzsch, Tobias and Pleijzier, Markus W. and Silva, Valeria and Yin, Yijie and Ali, Iris and Badalamente, Griffin and Bates, Alexander Shakeel and Beresford, Rory J. and Bogovic, John and Brooks, Paul and Cachero, Sebastian and Canino, Brandon S. and Chaisrisawatsuk, Bhumpanya and Clements, Jody and Crowe, Arthur and de Haan Vicente, Inês and Dempsey, Georgia and Donà, Erika and Dos Santos, Márcia and Dreher, Marisa and Dunne, Christopher R. and Eichler, Katharina and Finley-May, Samantha and Flynn, Miriam A. and Hameed, Imran and Hopkins, Gary Patrick and Hubbard, Philip M. and Kiassat, Ladann and Kovalyak, Julie and Lauchie, Shirley A. and Leonard, Meghan and Lohff, Alanna and Longden, Kit D. and Maldonado, Charli A. and Moitra, Ilina and Moon, Sung Soo and Mooney, Caroline and Munnelly, Eva J. and Okeoma, Nneoma and Olbris, Donald J. and Pai, Anika and Patel, Birava and Phillips, Emily M. and Plaza, Stephen M. and Richards, Alana and Rivas Salinas, Jennifer and Roberts, Ruairí J.V. and Rogers, Edward M. and Scott, Ashley L. and Scuderi, Louis A. and Seenivasan, Pavithraa and Serratosa Capdevila, Laia and Smith, Claire and Svirskas, Rob and Takemura, Satoko and Tastekin, Ibrahim and Thomson, Alexander and Umayam, Lowell and Walsh, John J. and Whittome, Holly and Xu, C. Shan and Yakal, Emily A. and Yang, Tansy and Zhao, Arthur and George, Reed and Jain, Viren and Jayaraman, Vivek and Korff, Wyatt and Meissner, Geoffrey W. and Romani, Sandro and Funke, Jan and Knecht, Christopher and Saalfeld, Stephan and Scheffer, Louis K. and Waddell, Scott and Card, Gwyneth M. and Ribeiro, Carlos and Reiser, Michael B. and Hess, Harald F. and Rubin, Gerald M. and Jefferis, Gregory S.X.E.},
137
+ journal = {Cell},
138
+ volume = {189},
139
+ number = {18},
140
+ pages = {5504--5526.e15},
141
+ year = {2026},
142
+ month = sep,
143
+ publisher = {Elsevier},
144
+ doi = {10.1016/j.cell.2026.08.015},
145
+ url = {https://doi.org/10.1016/j.cell.2026.08.015},
146
+ note = {Preprint: bioRxiv 10.1101/2025.10.09.680999. Data: MaleCNS v1.0, CC-BY 4.0, https://male-cns.janelia.org}
147
+ }
148
+ ```
149
+
150
+ ## License
151
+
152
+ The connectome is released under CC-BY 4.0 by the FlyEM Project Team (HHMI Janelia), the University of Cambridge,
153
+ the MRC Laboratory of Molecular Biology, and Google Research. This checkpoint is a derivative and carries the same
154
+ license.
155
+
156
+ Prior art: [ngxson/fly-llm-hf](https://huggingface.co/ngxson/fly-llm-hf) (frozen MaleCNS reservoir LM) and
157
+ [eob/gpt-fly](https://huggingface.co/eob/gpt-fly) (FlyWire-masked GPT-2). Code and experiment:
158
+ [github.com/QuixiAI/FlyGPT](https://github.com/QuixiAI/FlyGPT).
init/config.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "flygpt",
3
+ "architectures": [
4
+ "FlyGPTForCausalLM"
5
+ ],
6
+ "auto_map": {
7
+ "AutoConfig": "configuration_flygpt.FlyGPTConfig",
8
+ "AutoModelForCausalLM": "modeling_flygpt.FlyGPTForCausalLM"
9
+ },
10
+ "vocab_size": 65,
11
+ "num_neurons": 5000,
12
+ "num_edges": 524324,
13
+ "embedding_dim": 32,
14
+ "num_input_neurons": 256,
15
+ "num_output_neurons": 512,
16
+ "microsteps": 2,
17
+ "activation": "tanh",
18
+ "learned_leak": true,
19
+ "leak_init": 0.5,
20
+ "degree_normalization": true,
21
+ "init_scale": 1.0,
22
+ "dtype": "bfloat16",
23
+ "graph": {
24
+ "graph_name": "cb5k",
25
+ "condition": "real",
26
+ "control_seed": 1,
27
+ "source": "malecns-v1.0",
28
+ "region_filter": "central_brain",
29
+ "min_synapses": 3,
30
+ "hash": "f82b783b7ccb5a354fc4cf3de6de4a98d75029303c55f8faae28ab807828a007"
31
+ },
32
+ "training_state": {
33
+ "status": "init",
34
+ "condition": "real",
35
+ "seed": 1,
36
+ "project": "flygpt-v0"
37
+ }
38
+ }
init/configuration_flygpt.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlyGPT configuration for Hugging Face `transformers` (loaded with trust_remote_code=True)."""
2
+ from transformers import PretrainedConfig
3
+
4
+
5
+ class FlyGPTConfig(PretrainedConfig):
6
+ model_type = "flygpt"
7
+
8
+ def __init__(
9
+ self,
10
+ vocab_size: int = 65,
11
+ num_neurons: int = 0,
12
+ num_edges: int = 0,
13
+ embedding_dim: int = 32,
14
+ num_input_neurons: int = 256,
15
+ num_output_neurons: int = 512,
16
+ microsteps: int = 2,
17
+ activation: str = "tanh",
18
+ learned_leak: bool = True,
19
+ leak_init: float = 0.5,
20
+ degree_normalization: bool = True,
21
+ init_scale: float = 0.1,
22
+ graph: dict | None = None,
23
+ training_state: dict | None = None,
24
+ **kwargs,
25
+ ):
26
+ self.vocab_size = vocab_size
27
+ self.num_neurons = num_neurons
28
+ self.num_edges = num_edges
29
+ self.embedding_dim = embedding_dim
30
+ self.num_input_neurons = num_input_neurons
31
+ self.num_output_neurons = num_output_neurons
32
+ self.microsteps = microsteps
33
+ self.activation = activation
34
+ self.learned_leak = learned_leak
35
+ self.leak_init = leak_init
36
+ self.degree_normalization = degree_normalization
37
+ self.init_scale = init_scale
38
+ self.graph = graph or {} # source, hash, region filter, stats, provenance
39
+ self.training_state = training_state or {} # "init" or the run that produced the values
40
+ super().__init__(**kwargs)
init/generation_config.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "do_sample": true,
3
+ "temperature": 0.8,
4
+ "max_new_tokens": 300
5
+ }
init/graph_metadata.json ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "graph_name": "cb5k",
3
+ "condition": "real",
4
+ "control_seed": 1,
5
+ "source": "malecns-v1.0",
6
+ "region_filter": "central_brain",
7
+ "min_synapses": 3,
8
+ "selector": "directed_core",
9
+ "target_neurons": 5000,
10
+ "hash": "f82b783b7ccb5a354fc4cf3de6de4a98d75029303c55f8faae28ab807828a007",
11
+ "stats": {
12
+ "candidate_pool": 37108,
13
+ "candidate_edges": 7294218,
14
+ "min_synapses": 3,
15
+ "target_neurons": 5000,
16
+ "edges_after_threshold": 2805370,
17
+ "scc_after_threshold": 36461,
18
+ "core_size_before_trim": 6576,
19
+ "trim_iterations": 1,
20
+ "core_k": 40,
21
+ "final_neurons": 5000,
22
+ "final_edges": 524324,
23
+ "source": "malecns-v1.0",
24
+ "n_neurons": 5000,
25
+ "n_edges": 524324,
26
+ "synaptic_contacts": 8300915.0,
27
+ "largest_scc_fraction": 1.0,
28
+ "reciprocal_pairs": 93055,
29
+ "in_degree": {
30
+ "mean": 104.8648,
31
+ "min": 4,
32
+ "max": 881
33
+ },
34
+ "out_degree": {
35
+ "mean": 104.8648,
36
+ "min": 5,
37
+ "max": 1539
38
+ },
39
+ "density": 0.020977155431086216,
40
+ "hash": "f82b783b7ccb5a354fc4cf3de6de4a98d75029303c55f8faae28ab807828a007",
41
+ "region_breakdown": {
42
+ "central_brain": 5000
43
+ }
44
+ },
45
+ "diagnostics": {
46
+ "provenance": {
47
+ "control": "real"
48
+ },
49
+ "io": {
50
+ "n_input": 256,
51
+ "n_output": 512,
52
+ "input_rule": "top_out_degree",
53
+ "output_rule": "top_in_degree",
54
+ "overlap": 160
55
+ },
56
+ "diagnostics": {
57
+ "n_neurons": 5000,
58
+ "n_edges": 524324,
59
+ "largest_scc_fraction": 1.0,
60
+ "reciprocal_pairs": 93055,
61
+ "io_reachable_fraction": 1.0,
62
+ "io_path_median": 1.0,
63
+ "io_path_p90": 1.0,
64
+ "io_path_max": 1.0
65
+ },
66
+ "gate": {
67
+ "microsteps": 2,
68
+ "max_p90_hops": 6,
69
+ "min_reachable_fraction": 0.95,
70
+ "reachable_ok": true,
71
+ "p90_ok": true,
72
+ "passed": true
73
+ }
74
+ }
75
+ }
init/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3de08dbf41a2a81048e0aa851b6a5b19cca07f97fb9eb62094279b720ceb4b81
3
+ size 7495610
init/modeling_flygpt.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlyGPT: a character-level language model whose recurrent core is a real subgraph of the
2
+ fruit-fly connectome (MaleCNS v1.0). Hugging Face `transformers` implementation; self-contained.
3
+
4
+ Dynamics (one scalar state per neuron, plan.md §7 of the FlyGPT spec):
5
+
6
+ proposal_i = tanh( sum_j W_ij h_j / sqrt(in_degree_i) + external_input_i + bias_i )
7
+ h_i_new = (1 - leak_i) * h_i + leak_i * proposal_i
8
+
9
+ The connectome is stored in `model.safetensors` as integer tensors (`graph.*`); only the learned
10
+ per-edge values and the adapters are floating point (bf16 on disk). The sparse recurrent matmul is
11
+ rebuilt in fp32 at runtime (rows = destination, columns = source).
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import math
16
+ from dataclasses import dataclass
17
+ from typing import Optional
18
+
19
+ import torch
20
+ import torch.nn as nn
21
+ import torch.nn.functional as F
22
+ from transformers import PreTrainedModel
23
+ from transformers.generation import GenerationMixin
24
+ from transformers.utils import ModelOutput
25
+
26
+ from .configuration_flygpt import FlyGPTConfig
27
+
28
+
29
+ @dataclass
30
+ class FlyGPTOutput(ModelOutput):
31
+ loss: Optional[torch.FloatTensor] = None
32
+ logits: Optional[torch.FloatTensor] = None
33
+ state: Optional[torch.FloatTensor] = None # [B, N] neuron states after the last character
34
+
35
+
36
+ class FlyGraph(nn.Module):
37
+ """The anatomy. Integer buffers only; never trained."""
38
+
39
+ def __init__(self, num_neurons: int, num_edges: int, num_input: int, num_output: int):
40
+ super().__init__()
41
+ self.register_buffer("edge_index", torch.zeros(2, num_edges, dtype=torch.int32)) # [source, destination]
42
+ self.register_buffer("synapse_count", torch.zeros(num_edges, dtype=torch.int32)) # MaleCNS synaptic contacts
43
+ self.register_buffer("node_id", torch.zeros(num_neurons, dtype=torch.int64)) # MaleCNS body ids
44
+ self.register_buffer("input_nodes", torch.zeros(num_input, dtype=torch.int64))
45
+ self.register_buffer("output_nodes", torch.zeros(num_output, dtype=torch.int64))
46
+
47
+
48
+ class FlyRecurrentCore(nn.Module):
49
+ """The learned state: one value per real edge, plus per-neuron bias and leak."""
50
+
51
+ def __init__(self, num_neurons: int, num_edges: int, leak_init: float, learned_leak: bool):
52
+ super().__init__()
53
+ self.edge_values = nn.Parameter(torch.zeros(num_edges))
54
+ self.bias = nn.Parameter(torch.zeros(num_neurons))
55
+ self.raw_leak = nn.Parameter(torch.full((num_neurons,), math.log(leak_init / (1 - leak_init))),
56
+ requires_grad=learned_leak)
57
+
58
+
59
+ class FlyGPTPreTrainedModel(PreTrainedModel):
60
+ config_class = FlyGPTConfig
61
+ base_model_prefix = "flygpt"
62
+ _is_stateful = True
63
+ _supports_cache_class = False
64
+ supports_gradient_checkpointing = False
65
+
66
+ def _init_weights(self, module):
67
+ if isinstance(module, FlyRecurrentCore):
68
+ nn.init.normal_(module.edge_values, std=self.config.init_scale)
69
+ nn.init.zeros_(module.bias)
70
+ elif isinstance(module, nn.Linear):
71
+ nn.init.normal_(module.weight, std=0.02)
72
+ if module.bias is not None:
73
+ nn.init.zeros_(module.bias)
74
+ elif isinstance(module, nn.Embedding):
75
+ nn.init.normal_(module.weight, std=1.0)
76
+
77
+
78
+ class FlyGPTForCausalLM(FlyGPTPreTrainedModel, GenerationMixin):
79
+ def __init__(self, config: FlyGPTConfig):
80
+ super().__init__(config)
81
+ c = config
82
+ self.graph = FlyGraph(c.num_neurons, c.num_edges, c.num_input_neurons, c.num_output_neurons)
83
+ self.recurrent = FlyRecurrentCore(c.num_neurons, c.num_edges, c.leak_init, c.learned_leak)
84
+ self.embed = nn.Embedding(c.vocab_size, c.embedding_dim)
85
+ self.input_proj = nn.Linear(c.embedding_dim, c.num_input_neurons)
86
+ self.lm_head = nn.Linear(c.num_output_neurons, c.vocab_size)
87
+ self.post_init()
88
+
89
+ # ---- sparse recurrent matrix -------------------------------------------------------------
90
+ @property
91
+ def num_neurons(self) -> int:
92
+ return self.config.num_neurons
93
+
94
+ def edge_scale(self) -> torch.Tensor:
95
+ """1/sqrt(in_degree) per edge (degree normalization), or ones."""
96
+ dst = self.graph.edge_index[1].long()
97
+ if not self.config.degree_normalization:
98
+ return torch.ones_like(dst, dtype=torch.float32)
99
+ in_deg = torch.bincount(dst, minlength=self.num_neurons).clamp(min=1).float()
100
+ return 1.0 / in_deg[dst].sqrt()
101
+
102
+ def sparse_weight(self) -> torch.Tensor:
103
+ src, dst = self.graph.edge_index[0].long(), self.graph.edge_index[1].long()
104
+ values = self.recurrent.edge_values.float() * self.edge_scale()
105
+ return torch.sparse_coo_tensor(torch.stack([dst, src]), values, (self.num_neurons, self.num_neurons))
106
+
107
+ def dense_weight(self) -> torch.Tensor:
108
+ """Convenience for analysis; [N, N] with rows = destination. Never used in the forward pass."""
109
+ return self.sparse_weight().to_dense()
110
+
111
+ @property
112
+ def leak(self) -> torch.Tensor:
113
+ return torch.sigmoid(self.recurrent.raw_leak.float())
114
+
115
+ # ---- dynamics ------------------------------------------------------------------------------
116
+ def init_state(self, batch: int, device=None) -> torch.Tensor:
117
+ return torch.zeros(batch, self.num_neurons, device=device or self.recurrent.edge_values.device)
118
+
119
+ def drive(self, x: torch.Tensor) -> torch.Tensor:
120
+ d = torch.zeros(x.shape[0], self.num_neurons, device=x.device, dtype=torch.float32)
121
+ d[:, self.graph.input_nodes] = self.input_proj(self.embed(x)).float()
122
+ return d
123
+
124
+ def step(self, state: torch.Tensor, x: torch.Tensor, W: torch.Tensor | None = None) -> torch.Tensor:
125
+ W = self.sparse_weight() if W is None else W
126
+ drive, leak, bias = self.drive(x), self.leak, self.recurrent.bias.float()
127
+ for _ in range(self.config.microsteps):
128
+ incoming = torch.sparse.mm(W, state.float().T).T
129
+ proposal = torch.tanh(incoming + drive + bias)
130
+ state = (1 - leak) * state + leak * proposal
131
+ return state
132
+
133
+ def logits_from_state(self, state: torch.Tensor) -> torch.Tensor:
134
+ return self.lm_head(state[:, self.graph.output_nodes].to(self.lm_head.weight.dtype)).float()
135
+
136
+ def forward(self, input_ids: torch.LongTensor, state: Optional[torch.Tensor] = None,
137
+ labels: Optional[torch.LongTensor] = None, use_cache: Optional[bool] = None,
138
+ return_dict: Optional[bool] = None, **kwargs) -> FlyGPTOutput:
139
+ B, T = input_ids.shape
140
+ state = self.init_state(B, input_ids.device) if state is None else state
141
+ W = self.sparse_weight()
142
+ outs = []
143
+ for t in range(T):
144
+ state = self.step(state, input_ids[:, t], W)
145
+ outs.append(self.logits_from_state(state))
146
+ logits = torch.stack(outs, 1)
147
+ loss = None
148
+ if labels is not None:
149
+ loss = F.cross_entropy(logits[:, :-1].reshape(-1, logits.shape[-1]), labels[:, 1:].reshape(-1))
150
+ return FlyGPTOutput(loss=loss, logits=logits, state=state)
151
+
152
+ # ---- generation: carry the neuron state instead of a KV cache ------------------------------
153
+ @classmethod
154
+ def _supports_default_dynamic_cache(cls) -> bool:
155
+ return False # stateful recurrent model: no KV cache, the neuron state is carried in `state`
156
+
157
+ def prepare_inputs_for_generation(self, input_ids, state=None, **kwargs):
158
+ if state is not None:
159
+ input_ids = input_ids[:, -1:]
160
+ return {"input_ids": input_ids, "state": state}
161
+
162
+ def _update_model_kwargs_for_generation(self, outputs, model_kwargs, is_encoder_decoder=False, **kwargs):
163
+ model_kwargs["state"] = outputs.state
164
+ return model_kwargs
init/tokenizer.json ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "1.0",
3
+ "truncation": null,
4
+ "padding": null,
5
+ "added_tokens": [],
6
+ "normalizer": null,
7
+ "pre_tokenizer": {
8
+ "type": "Split",
9
+ "pattern": {
10
+ "Regex": "[\\s\\S]"
11
+ },
12
+ "behavior": "Isolated",
13
+ "invert": false
14
+ },
15
+ "post_processor": null,
16
+ "decoder": {
17
+ "type": "Fuse"
18
+ },
19
+ "model": {
20
+ "type": "WordLevel",
21
+ "vocab": {
22
+ "\n": 0,
23
+ " ": 1,
24
+ "!": 2,
25
+ "$": 3,
26
+ "&": 4,
27
+ "'": 5,
28
+ ",": 6,
29
+ "-": 7,
30
+ ".": 8,
31
+ "3": 9,
32
+ ":": 10,
33
+ ";": 11,
34
+ "?": 12,
35
+ "A": 13,
36
+ "B": 14,
37
+ "C": 15,
38
+ "D": 16,
39
+ "E": 17,
40
+ "F": 18,
41
+ "G": 19,
42
+ "H": 20,
43
+ "I": 21,
44
+ "J": 22,
45
+ "K": 23,
46
+ "L": 24,
47
+ "M": 25,
48
+ "N": 26,
49
+ "O": 27,
50
+ "P": 28,
51
+ "Q": 29,
52
+ "R": 30,
53
+ "S": 31,
54
+ "T": 32,
55
+ "U": 33,
56
+ "V": 34,
57
+ "W": 35,
58
+ "X": 36,
59
+ "Y": 37,
60
+ "Z": 38,
61
+ "a": 39,
62
+ "b": 40,
63
+ "c": 41,
64
+ "d": 42,
65
+ "e": 43,
66
+ "f": 44,
67
+ "g": 45,
68
+ "h": 46,
69
+ "i": 47,
70
+ "j": 48,
71
+ "k": 49,
72
+ "l": 50,
73
+ "m": 51,
74
+ "n": 52,
75
+ "o": 53,
76
+ "p": 54,
77
+ "q": 55,
78
+ "r": 56,
79
+ "s": 57,
80
+ "t": 58,
81
+ "u": 59,
82
+ "v": 60,
83
+ "w": 61,
84
+ "x": 62,
85
+ "y": 63,
86
+ "z": 64
87
+ },
88
+ "unk_token": "<unk>"
89
+ }
90
+ }
init/tokenizer_config.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "tokenizer_class": "PreTrainedTokenizerFast",
3
+ "model_max_length": 1000000,
4
+ "clean_up_tokenization_spaces": false,
5
+ "add_prefix_space": false
6
+ }