Skip to content

Commit ff7ec4e

Browse files
committed
fix
1 parent e074137 commit ff7ec4e

5 files changed

Lines changed: 132 additions & 108 deletions

File tree

.github/workflows/fast_tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@ jobs:
4242
run: pip install -e ".[dev]" torch transformers tokenizers
4343

4444
- name: Run fast tests
45-
run: pytest tests/fast -v
45+
run: DOCLEAN=1&&pytest tests/fast -v

modelbuilder/ext_test_case.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ def long_test(msg: Optional[Union[Callable[[], str], str]] = None) -> Callable:
8989

9090
class ExtTestCase(unittest.TestCase):
9191
_warns = []
92+
_do_clean = os.environ.get("DOCLEAN", "") in (1, "1", "True", "true")
9293

9394
def shortDescription(self):
9495
# To remove annoying display on the screen every time verbosity is enabled.
@@ -99,18 +100,20 @@ def clean_dir(self, path: str):
99100
if os.path.exists(path):
100101
shutil.rmtree(path)
101102

102-
def get_dirs(self, prefix: str) -> Tuple[str]:
103+
def get_dirs(self, prefix: str, clean: bool = True) -> Tuple[str]:
103104
output_dir = f"dump_models/{prefix}/output"
104105
cache_dir = f"dump_models/{prefix}/cache"
105106
os.makedirs(output_dir, exist_ok=True)
106107
os.makedirs(cache_dir, exist_ok=True)
107-
self.addCleanup(self.clean_dir, f"dump_models/{prefix}")
108+
if clean or self._do_clean:
109+
self.addCleanup(self.clean_dir, f"dump_models/{prefix}")
108110
return output_dir, cache_dir
109111

110-
def get_model_dir(self, prefix: str) -> Tuple[str]:
112+
def get_model_dir(self, prefix: str, clean: bool = False) -> Tuple[str]:
111113
model_dir = f"dump_models/{prefix}/checkpoint"
112114
os.makedirs(model_dir, exist_ok=True)
113-
self.addCleanup(self.clean_dir, f"dump_models/{prefix}")
115+
if clean or self._do_clean:
116+
self.addCleanup(self.clean_dir, f"dump_models/{prefix}")
114117
return model_dir
115118

116119
def assertExists(self, name):

tests/fast/test_random_olmo3.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# -------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for
4+
# license information.
5+
# --------------------------------------------------------------------------
6+
import os
7+
import unittest
8+
9+
import numpy as np
10+
11+
from modelbuilder.ext_test_case import ExtTestCase, hide_stdout
12+
13+
MODEL_NAME = "Olmo3ForCausalLM"
14+
15+
16+
class TestOLMo3(ExtTestCase):
17+
@hide_stdout()
18+
def test_olmo3_fp32_cpu_random_weights(self):
19+
"""
20+
Convert a model with the same architecture as allenai/Olmo-3-7B-Instruct
21+
but with randomly initialised weights to an fp32 ONNX model targeting the
22+
CPU execution provider.
23+
24+
The test verifies that:
25+
* ``create_model`` completes without error when given a local model directory.
26+
* The expected ``model.onnx`` file is written to the output directory.
27+
* The produced ONNX file can be loaded by ``onnxruntime``.
28+
"""
29+
import torch
30+
from tokenizers import Tokenizer
31+
from tokenizers.models import WordLevel
32+
from transformers import AutoModelForCausalLM, PreTrainedTokenizerFast
33+
from transformers.models.olmo3 import Olmo3Config
34+
35+
from modelbuilder.builder import create_model
36+
37+
num_hidden_layers = 1
38+
39+
config = Olmo3Config(
40+
architectures=["Olmo3ForCausalLM"],
41+
hidden_act="silu",
42+
hidden_size=512,
43+
intermediate_size=1376,
44+
max_position_embeddings=2048,
45+
model_type="olmo3",
46+
num_attention_heads=8,
47+
num_hidden_layers=num_hidden_layers,
48+
num_key_value_heads=4,
49+
rms_norm_eps=1e-5,
50+
rope_theta=10000.0,
51+
sliding_window=256,
52+
vocab_size=50304,
53+
)
54+
55+
model_dir = self.get_model_dir(
56+
"test_olmo3_fp32_cpu_random_weights", clean=False
57+
)
58+
model = AutoModelForCausalLM.from_config(config)
59+
model.save_pretrained(model_dir)
60+
61+
vocab = {"<unk>": 0, "<s>": 1, "</s>": 2}
62+
tokenizer = PreTrainedTokenizerFast(
63+
tokenizer_object=Tokenizer(WordLevel(vocab=vocab, unk_token="<unk>")),
64+
bos_token="<s>",
65+
eos_token="</s>",
66+
unk_token="<unk>",
67+
)
68+
tokenizer.save_pretrained(model_dir)
69+
70+
output_dir, cache_dir = self.get_dirs(
71+
"test_olmo3_fp32_cpu_random_weights", clean=False
72+
)
73+
74+
create_model(
75+
model_name=MODEL_NAME,
76+
input_path=model_dir,
77+
output_dir=output_dir,
78+
precision="fp32",
79+
execution_provider="cpu",
80+
cache_dir=cache_dir,
81+
)
82+
83+
onnx_path = os.path.join(output_dir, "model.onnx")
84+
self.assertExists(onnx_path)
85+
sess = self.check_ort(onnx_path)
86+
87+
# --- PyTorch inference ---
88+
batch_size = 1
89+
seq_len = 5
90+
input_ids = torch.randint(0, config.vocab_size, (batch_size, seq_len))
91+
with torch.no_grad():
92+
pt_logits = model(input_ids).logits.numpy()
93+
94+
onnx_input_names = {inp.name for inp in sess.get_inputs()}
95+
96+
head_size = config.hidden_size // config.num_attention_heads
97+
onnx_feed = {
98+
"input_ids": input_ids.numpy().astype(np.int64),
99+
"attention_mask": np.ones((batch_size, seq_len), dtype=np.int64),
100+
"position_ids": np.arange(seq_len, dtype=np.int64).reshape(
101+
batch_size, seq_len
102+
),
103+
}
104+
# Provide empty past KV-cache tensors for every materialised layer.
105+
for i in range(num_hidden_layers):
106+
onnx_feed[f"past_key_values.{i}.key"] = np.zeros(
107+
(batch_size, config.num_key_value_heads, 0, head_size),
108+
dtype=np.float32,
109+
)
110+
onnx_feed[f"past_key_values.{i}.value"] = np.zeros(
111+
(batch_size, config.num_key_value_heads, 0, head_size),
112+
dtype=np.float32,
113+
)
114+
# Keep only what the session expects.
115+
onnx_feed = {k: v for k, v in onnx_feed.items() if k in onnx_input_names}
116+
117+
onnx_outputs = sess.run(None, onnx_feed)
118+
onnx_logits = onnx_outputs[0]
119+
120+
np.testing.assert_allclose(pt_logits, onnx_logits, atol=1e-3, rtol=1e-3)
121+
122+
123+
if __name__ == "__main__":
124+
unittest.main(verbosity=2)

tests/fast/test_random_smollm3.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,6 @@ def test_smollm3_fp32_cpu_random_weights(self):
5757
)
5858
tokenizer.save_pretrained(model_dir)
5959

60-
output_dir = os.path.join(model_dir, "onnx_output")
61-
cache_dir = os.path.join(model_dir, "cache")
62-
os.makedirs(output_dir, exist_ok=True)
63-
os.makedirs(cache_dir, exist_ok=True)
64-
6560
output_dir, cache_dir = self.get_dirs("test_smollm3_fp32_cpu_random_weights")
6661
create_model(
6762
model_name=SMOLLM3_MODEL_NAME,

tests/test_olmo3.py

Lines changed: 0 additions & 98 deletions
This file was deleted.

0 commit comments

Comments
 (0)