physicsrob commited on
Commit
ed03dd6
·
verified ·
1 Parent(s): b9a8681

Publish E1M1 38-layer seed-0 bundle

Browse files
Files changed (5) hide show
  1. README.md +34 -8
  2. config.json +7 -0
  3. doom_bundle_manifest.json +13 -12
  4. generation_config.json +2 -0
  5. infer.py +72 -22
README.md CHANGED
@@ -7,7 +7,8 @@ pipeline_tag: text-generation
7
 
8
  This is a stock Hugging Face `Phi3ForCausalLM` that renders DOOM through
9
  ordinary autoregressive inference. The model and the data-only fast tokenizer
10
- load through ordinary Transformers auto classes without remote code.
 
11
 
12
  The bundled `examples/e1m1_prompt.txt` is the executable prompt. Run
13
  `infer.py` (at the bundle root) to produce canonical emitted row ids and raw
@@ -18,15 +19,40 @@ The protocol is specified in `PROTOCOL.md` in the source repo. Neither
18
  post-processing tool participates in inference or performs geometry,
19
  visibility, lighting, texture selection, or sorting.
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  **This bundle:** screen 320×200, map
22
  E1M1, dense fp32 sharded safetensors, eager attention (the validated
23
  implementation), greedy decode, generation bound
24
  61440 new tokens.
25
 
26
- **What running it takes:** the weights load in full fp32 (total size = the
27
- sum of the shards; the flagship 320×200 bundle is ~98 GB, needing an
28
- H200/B200-class GPU or multi-GPU `device_map`). The flagship render measured
29
- ~42 minutes of greedy decode on one B200 for its 53,747-token rollout from a
30
- 3,614-token prompt, scoring ~99.99% within-option color against the
31
- reference renderer. Canonical numbers and their provenance: `FACTS.md` in
32
- the source repo (github.com/physicsrob/torchwright_doom).
 
 
 
7
 
8
  This is a stock Hugging Face `Phi3ForCausalLM` that renders DOOM through
9
  ordinary autoregressive inference. The model and the data-only fast tokenizer
10
+ load through the ordinary Transformers text-generation pipeline without
11
+ remote code.
12
 
13
  The bundled `examples/e1m1_prompt.txt` is the executable prompt. Run
14
  `infer.py` (at the bundle root) to produce canonical emitted row ids and raw
 
19
  post-processing tool participates in inference or performs geometry,
20
  visibility, lighting, texture selection, or sorting.
21
 
22
+ Ordinary Transformers pipeline inference works directly, with no custom or
23
+ remote model code:
24
+
25
+ ```python
26
+ from pathlib import Path
27
+ from huggingface_hub import hf_hub_download
28
+ from transformers import pipeline
29
+
30
+ repo = "physicsrob/torchwright-doom-e1m1"
31
+ prompt = Path(hf_hub_download(repo, "examples/e1m1_prompt.txt")).read_text()
32
+ generate = pipeline("text-generation", model=repo, device_map="auto")
33
+ generated_text = generate(prompt, return_full_text=False)[0]["generated_text"]
34
+ ```
35
+
36
+ The saved generation defaults are greedy and cover the complete frame. Use
37
+ the shipped `infer.py` when canonical integer row IDs, progress reporting, and
38
+ the exact terminal-token-preserving raw text are required.
39
+
40
+ Published checkpoints: [320×200](https://huggingface.co/physicsrob/torchwright-doom-e1m1)
41
+ and [80×50](https://huggingface.co/physicsrob/torchwright-doom-e1m1-80x50).
42
+ The compiler-facing source is
43
+ [torchwright_doom](https://github.com/physicsrob/torchwright_doom).
44
+
45
  **This bundle:** screen 320×200, map
46
  E1M1, dense fp32 sharded safetensors, eager attention (the validated
47
  implementation), greedy decode, generation bound
48
  61440 new tokens.
49
 
50
+ **What running it takes:** the fp32 weight shards total
51
+ 79.97 GiB (85.87 GB), needing a B200-class GPU
52
+ or multi-GPU `device_map`. The flagship pipeline render peaked at 151.00 GiB
53
+ reserved; greedy decode took 39.7 minutes on one B200 for its 53,747-token rollout
54
+ from a 3,614-token prompt, scoring 99.9% within-option color against the
55
+ reference renderer.
56
+
57
+ Canonical numbers and their provenance: `FACTS.md` in the source repo
58
+ (github.com/physicsrob/torchwright_doom).
config.json CHANGED
@@ -3,6 +3,7 @@
3
  "Phi3ForCausalLM"
4
  ],
5
  "attention_dropout": 0.0,
 
6
  "bos_token_id": 133096,
7
  "doom_screen_config": {
8
  "detail": "low",
@@ -34,6 +35,12 @@
34
  "rope_type": "default"
35
  },
36
  "sliding_window": null,
 
 
 
 
 
 
37
  "tie_word_embeddings": true,
38
  "transformers_version": "5.13.1",
39
  "use_cache": true,
 
3
  "Phi3ForCausalLM"
4
  ],
5
  "attention_dropout": 0.0,
6
+ "attn_implementation": "eager",
7
  "bos_token_id": 133096,
8
  "doom_screen_config": {
9
  "detail": "low",
 
35
  "rope_type": "default"
36
  },
37
  "sliding_window": null,
38
+ "task_specific_params": {
39
+ "text-generation": {
40
+ "do_sample": false,
41
+ "max_new_tokens": 61440
42
+ }
43
+ },
44
  "tie_word_embeddings": true,
45
  "transformers_version": "5.13.1",
46
  "use_cache": true,
doom_bundle_manifest.json CHANGED
@@ -2,22 +2,23 @@
2
  "architecture": "Phi3ForCausalLM",
3
  "artifact_kind": "hf_phi3_bundle",
4
  "bos_token_id": 133096,
5
- "bundle_identity": "torchwright_doom.phi3.v1:bfd028379c63520b4e2aa3919fbdeb76c1b598580d425e65059f07dfcd4a33f7",
6
  "compile": {
7
  "n_layers": 38,
8
- "solver_seed": 0
 
9
  },
10
- "compile_payload_sha256": "bfd028379c63520b4e2aa3919fbdeb76c1b598580d425e65059f07dfcd4a33f7",
11
  "dtype": "float32",
12
  "eos_token_id": 133029,
13
  "files": {
14
  "README.md": {
15
- "sha256": "b96069316230d13ed86b2e5829a6d180b45ef331f98e3f0efc4a532bbfd23b5a",
16
- "size": 1562
17
  },
18
  "config.json": {
19
- "sha256": "12cc69eb384a668beb1b65a62a7d32d62f0ae939fae247b1a9ecf548d16b2308",
20
- "size": 1012
21
  },
22
  "doom_palette.json": {
23
  "sha256": "e6a86012014e059fa3dcd22ee185a23d5eac6976fbc59debbdded09fd885f287",
@@ -36,12 +37,12 @@
36
  "size": 53014
37
  },
38
  "generation_config.json": {
39
- "sha256": "4d66e6be7ef4f8ba1237fe25d3973066f9f44e400c1eae69cc8ba1e0a5714084",
40
- "size": 117
41
  },
42
  "infer.py": {
43
- "sha256": "1ddc270d57e9d4a66731c556b27d2de7260ee2cd9f300e4fb1299f0528a63a08",
44
- "size": 10564
45
  },
46
  "model-00001-of-00039.safetensors": {
47
  "size": 2144928208
@@ -256,7 +257,7 @@
256
  },
257
  "source_revisions": {
258
  "torchwright": "b431c4a5a2d39cab0b47033f1c0a89e3ee5cca31",
259
- "torchwright_doom": "49feb52d455f1223d402fa585249ff24b6055c74"
260
  },
261
  "tokenizer_vocab_sha256": "c51d61c03f3496e793b52790e77b670b801255d59da80df5abb6ef7c3a1e212e",
262
  "validation": {
 
2
  "architecture": "Phi3ForCausalLM",
3
  "artifact_kind": "hf_phi3_bundle",
4
  "bos_token_id": 133096,
5
+ "bundle_identity": "torchwright_doom.phi3.v1:aee65882d6900d333fe2c91cc1790f861de4415a18b15a1438749b39a0a925f1",
6
  "compile": {
7
  "n_layers": 38,
8
+ "solver_seed": 0,
9
+ "solver_workers": 64
10
  },
11
+ "compile_payload_sha256": "aee65882d6900d333fe2c91cc1790f861de4415a18b15a1438749b39a0a925f1",
12
  "dtype": "float32",
13
  "eos_token_id": 133029,
14
  "files": {
15
  "README.md": {
16
+ "sha256": "5ce39ee763e70552dfde8dff45ae5b332e0564490b5d5b9b476e1de3052e69e7",
17
+ "size": 2511
18
  },
19
  "config.json": {
20
+ "sha256": "ff7b2b5221b2da242ab5ac08efea64ad3d63f9e9c1ae933a5d6affae0938507a",
21
+ "size": 1166
22
  },
23
  "doom_palette.json": {
24
  "sha256": "e6a86012014e059fa3dcd22ee185a23d5eac6976fbc59debbdded09fd885f287",
 
37
  "size": 53014
38
  },
39
  "generation_config.json": {
40
+ "sha256": "1b438a69a303eb106671ecb93df9bf4007a5af7ff3115245f2aad2095cb61a2d",
41
+ "size": 166
42
  },
43
  "infer.py": {
44
+ "sha256": "a99be58f8d1596e6e2584d926896ee5f0d42951e7d3705b6f7a6fa7788b9f145",
45
+ "size": 12361
46
  },
47
  "model-00001-of-00039.safetensors": {
48
  "size": 2144928208
 
257
  },
258
  "source_revisions": {
259
  "torchwright": "b431c4a5a2d39cab0b47033f1c0a89e3ee5cca31",
260
+ "torchwright_doom": "6c18b01969ceb8e96d003663b63f37d5fe39573b"
261
  },
262
  "tokenizer_vocab_sha256": "c51d61c03f3496e793b52790e77b670b801255d59da80df5abb6ef7c3a1e212e",
263
  "validation": {
generation_config.json CHANGED
@@ -1,6 +1,8 @@
1
  {
2
  "bos_token_id": 133096,
 
3
  "eos_token_id": 133029,
 
4
  "pad_token_id": 133029,
5
  "transformers_version": "5.13.1"
6
  }
 
1
  {
2
  "bos_token_id": 133096,
3
+ "do_sample": false,
4
  "eos_token_id": 133029,
5
+ "max_new_tokens": 61440,
6
  "pad_token_id": 133029,
7
  "transformers_version": "5.13.1"
8
  }
infer.py CHANGED
@@ -31,7 +31,7 @@ os.environ.setdefault("HF_PARALLEL_LOADING_WORKERS", "8")
31
 
32
  import torch
33
  import transformers
34
- from transformers import AutoModelForCausalLM, AutoTokenizer
35
 
36
  _PROGRESS_INTERVAL_SECONDS = 15.0
37
 
@@ -44,6 +44,13 @@ def _sha(data: bytes) -> str:
44
  return hashlib.sha256(data).hexdigest()
45
 
46
 
 
 
 
 
 
 
 
47
  class _ProgressStreamer:
48
  """Report generation throughput without changing or collecting tokens."""
49
 
@@ -118,23 +125,43 @@ def main(argv: list[str] | None = None) -> int:
118
  bundled_prompt = prompt_sha256 == manifest["prompt"]["sha256"]
119
 
120
  load_t0 = time.monotonic()
121
- tokenizer = AutoTokenizer.from_pretrained(model_dir)
122
- load_kwargs = {}
 
 
 
 
 
 
123
  if args.device != "cpu":
124
  # Accelerate builds the skeleton on meta and dispatches each shard
125
  # directly to the target device. This avoids a second full-model
126
  # ``model.to(cuda)`` pass through CPU-backed mmap pages.
127
- load_kwargs["device_map"] = args.device
128
- model = AutoModelForCausalLM.from_pretrained(
129
- model_dir,
130
- attn_implementation="eager",
131
- dtype=torch.float32,
132
- # Read each shard's bytes eagerly: deferring them to mmap page
133
- # faults stalls badly on network filesystems, and eager reads are
134
- # harmless on local disks.
135
- disable_mmap=True,
136
- **load_kwargs,
137
- ).eval()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  attention_implementation = getattr(model.config, "_attn_implementation", None)
139
  if attention_implementation != "eager":
140
  # Eager is the implementation the published render was validated
@@ -154,14 +181,14 @@ def main(argv: list[str] | None = None) -> int:
154
  )
155
  load_seconds = time.monotonic() - load_t0
156
 
157
- inputs = tokenizer(
158
- prompt_bytes.decode("utf-8"),
 
159
  return_tensors="pt",
160
  add_special_tokens=False,
161
  )
162
  input_device = next(model.parameters()).device
163
- inputs = inputs.to(input_device)
164
- prompt_ids = [int(row) for row in inputs.input_ids[0].tolist()]
165
  prompt_ids_sha256 = _sha(_canonical_json(prompt_ids))
166
  # Only the bundled prompt has a manifest row-id expectation; a custom
167
  # prompt is permitted, never verified, and recorded in the payload as
@@ -185,8 +212,10 @@ def main(argv: list[str] | None = None) -> int:
185
  progress = _ProgressStreamer(len(prompt_ids), max_new)
186
  print(f"[infer] generation started; max_new_tokens={max_new}", flush=True)
187
  with torch.inference_mode():
188
- sequences = model.generate(
189
- **inputs,
 
 
190
  do_sample=False,
191
  use_cache=True,
192
  max_new_tokens=max_new,
@@ -195,7 +224,18 @@ def main(argv: list[str] | None = None) -> int:
195
  streamer=progress,
196
  )
197
  generate_seconds = time.monotonic() - generate_t0
198
- generated = [int(row) for row in sequences[0, inputs.input_ids.shape[1] :].tolist()]
 
 
 
 
 
 
 
 
 
 
 
199
  prefill_seconds = progress.prefill_seconds or generate_seconds
200
  decode_seconds = progress.decode_seconds
201
  raw_text = tokenizer.decode(
@@ -223,7 +263,7 @@ def main(argv: list[str] | None = None) -> int:
223
  "emitted_row_ids": generated,
224
  "emitted_row_ids_sha256": emitted_ids_sha256,
225
  "generation": {
226
- "mode": "transformers_generate",
227
  "max_new_tokens": max_new,
228
  "termination_reason": "terminal" if stopped else "cap",
229
  },
@@ -234,6 +274,7 @@ def main(argv: list[str] | None = None) -> int:
234
  "generate": generate_seconds,
235
  },
236
  "attention_implementation": attention_implementation,
 
237
  "transformers_version": transformers.__version__,
238
  }
239
  (args.output / "output.ids.json").write_text(
@@ -245,6 +286,15 @@ def main(argv: list[str] | None = None) -> int:
245
  f"stopped={payload['generation']['termination_reason']}",
246
  flush=True,
247
  )
 
 
 
 
 
 
 
 
 
248
  return 0
249
 
250
 
 
31
 
32
  import torch
33
  import transformers
34
+ from transformers import TextGenerationPipeline, pipeline
35
 
36
  _PROGRESS_INTERVAL_SECONDS = 15.0
37
 
 
44
  return hashlib.sha256(data).hexdigest()
45
 
46
 
47
+ def _cuda_devices(model) -> list[torch.device]:
48
+ return sorted(
49
+ {parameter.device for parameter in model.parameters() if parameter.is_cuda},
50
+ key=str,
51
+ )
52
+
53
+
54
  class _ProgressStreamer:
55
  """Report generation throughput without changing or collecting tokens."""
56
 
 
125
  bundled_prompt = prompt_sha256 == manifest["prompt"]["sha256"]
126
 
127
  load_t0 = time.monotonic()
128
+ model_kwargs = {
129
+ "attn_implementation": "eager",
130
+ # Read each shard's bytes eagerly: deferring them to mmap page faults
131
+ # stalls badly on network filesystems, and eager reads are harmless on
132
+ # local disks.
133
+ "disable_mmap": True,
134
+ }
135
+ generate: TextGenerationPipeline
136
  if args.device != "cpu":
137
  # Accelerate builds the skeleton on meta and dispatches each shard
138
  # directly to the target device. This avoids a second full-model
139
  # ``model.to(cuda)`` pass through CPU-backed mmap pages.
140
+ generate = pipeline(
141
+ "text-generation",
142
+ model=str(model_dir),
143
+ dtype=torch.float32,
144
+ model_kwargs=model_kwargs,
145
+ device_map=args.device,
146
+ )
147
+ else:
148
+ generate = pipeline(
149
+ "text-generation",
150
+ model=str(model_dir),
151
+ dtype=torch.float32,
152
+ model_kwargs=model_kwargs,
153
+ )
154
+ tokenizer = generate.tokenizer
155
+ if tokenizer is None:
156
+ raise RuntimeError("text-generation pipeline loaded without a tokenizer")
157
+ model = generate.model
158
+ model.eval()
159
+ cuda_devices = _cuda_devices(model)
160
+ for cuda_device in cuda_devices:
161
+ # Reset after loading: the current allocation still includes all
162
+ # weights, while the peak will additionally capture generation cache
163
+ # and runtime workspace. This is the consumer-fit measurement.
164
+ torch.cuda.reset_peak_memory_stats(cuda_device)
165
  attention_implementation = getattr(model.config, "_attn_implementation", None)
166
  if attention_implementation != "eager":
167
  # Eager is the implementation the published render was validated
 
181
  )
182
  load_seconds = time.monotonic() - load_t0
183
 
184
+ prompt_text = prompt_bytes.decode("utf-8")
185
+ encoded_prompt = tokenizer(
186
+ prompt_text,
187
  return_tensors="pt",
188
  add_special_tokens=False,
189
  )
190
  input_device = next(model.parameters()).device
191
+ prompt_ids = [int(row) for row in encoded_prompt.input_ids[0].tolist()]
 
192
  prompt_ids_sha256 = _sha(_canonical_json(prompt_ids))
193
  # Only the bundled prompt has a manifest row-id expectation; a custom
194
  # prompt is permitted, never verified, and recorded in the payload as
 
212
  progress = _ProgressStreamer(len(prompt_ids), max_new)
213
  print(f"[infer] generation started; max_new_tokens={max_new}", flush=True)
214
  with torch.inference_mode():
215
+ records = generate(
216
+ prompt_text,
217
+ add_special_tokens=False,
218
+ return_tensors=True,
219
  do_sample=False,
220
  use_cache=True,
221
  max_new_tokens=max_new,
 
224
  streamer=progress,
225
  )
226
  generate_seconds = time.monotonic() - generate_t0
227
+ sequence = records[0]["generated_token_ids"]
228
+ generated = [int(row) for row in sequence[len(prompt_ids) :]]
229
+ for cuda_device in cuda_devices:
230
+ torch.cuda.synchronize(cuda_device)
231
+ cuda_memory = [
232
+ {
233
+ "device": str(cuda_device),
234
+ "peak_allocated_bytes": torch.cuda.max_memory_allocated(cuda_device),
235
+ "peak_reserved_bytes": torch.cuda.max_memory_reserved(cuda_device),
236
+ }
237
+ for cuda_device in cuda_devices
238
+ ]
239
  prefill_seconds = progress.prefill_seconds or generate_seconds
240
  decode_seconds = progress.decode_seconds
241
  raw_text = tokenizer.decode(
 
263
  "emitted_row_ids": generated,
264
  "emitted_row_ids_sha256": emitted_ids_sha256,
265
  "generation": {
266
+ "mode": "transformers_pipeline",
267
  "max_new_tokens": max_new,
268
  "termination_reason": "terminal" if stopped else "cap",
269
  },
 
274
  "generate": generate_seconds,
275
  },
276
  "attention_implementation": attention_implementation,
277
+ "cuda_memory": cuda_memory,
278
  "transformers_version": transformers.__version__,
279
  }
280
  (args.output / "output.ids.json").write_text(
 
286
  f"stopped={payload['generation']['termination_reason']}",
287
  flush=True,
288
  )
289
+ for memory in cuda_memory:
290
+ peak_allocated = int(memory["peak_allocated_bytes"])
291
+ peak_reserved = int(memory["peak_reserved_bytes"])
292
+ print(
293
+ f"[infer] {memory['device']} peak allocated="
294
+ f"{peak_allocated / 1024**3:.2f} GiB "
295
+ f"reserved={peak_reserved / 1024**3:.2f} GiB",
296
+ flush=True,
297
+ )
298
  return 0
299
 
300