Skip to content

Commit af87dec

Browse files
Copilotxadupre
andauthored
Add support for allenai/OLMo-3-7B-Instruct (Olmo2ForCausalLM)
Agent-Logs-Url: https://github.com/xadupre/mbext/sessions/319238ac-22d3-4d56-83f2-c2057ae27619 Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com>
1 parent c63b376 commit af87dec

4 files changed

Lines changed: 113 additions & 1 deletion

File tree

modelbuilder/builder.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
MistralModel,
3030
Model,
3131
NemotronModel,
32+
OLMo2Model,
3233
OLMoModel,
3334
Phi3MiniLongRoPEModel,
3435
Phi3MiniModel,
@@ -302,6 +303,10 @@ def create_model(
302303
onnx_model = OLMoModel(
303304
config, io_dtype, onnx_dtype, execution_provider, cache_dir, extra_options
304305
)
306+
elif config.architectures[0] == "Olmo2ForCausalLM":
307+
onnx_model = OLMo2Model(
308+
config, io_dtype, onnx_dtype, execution_provider, cache_dir, extra_options
309+
)
305310
elif config.architectures[0] == "PhiForCausalLM":
306311
onnx_model = PhiModel(
307312
config, io_dtype, onnx_dtype, execution_provider, cache_dir, extra_options

modelbuilder/builders/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from .llama import LlamaModel
1717
from .mistral import MistralModel
1818
from .nemotron import NemotronModel
19-
from .olmo import OLMoModel
19+
from .olmo import OLMo2Model, OLMoModel
2020
from .phi import (
2121
Phi3MiniLongRoPEModel,
2222
Phi3MiniModel,
@@ -50,6 +50,7 @@
5050
"MistralModel",
5151
"Model",
5252
"NemotronModel",
53+
"OLMo2Model",
5354
"OLMoModel",
5455
"Phi3MiniLongRoPEModel",
5556
"Phi3MiniModel",

modelbuilder/builders/olmo.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,13 @@ def make_layernorm(self, layer_id, layernorm, skip, simple, location):
1616
layernorm.weight = torch.ones(self.hidden_size)
1717
layernorm.bias = torch.zeros(self.hidden_size)
1818
super().make_layernorm(layer_id, layernorm, skip, simple, location)
19+
20+
21+
class OLMo2Model(Model):
22+
def __init__(self, config, io_dtype, onnx_dtype, ep, cache_dir, extra_options):
23+
super().__init__(config, io_dtype, onnx_dtype, ep, cache_dir, extra_options)
24+
25+
def make_attention_init(self):
26+
self.attention_attrs["q_norm"] = True
27+
self.attention_attrs["k_norm"] = True
28+
super().make_attention_init()

tests/test_olmo2.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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+
"""
7+
Unit test checking that the model builder works with an OLMo2-style model.
8+
The test uses randomly initialised weights to avoid downloading the pretrained
9+
weights from Hugging Face, making it completely offline and suitable for CI
10+
environments without internet access.
11+
"""
12+
13+
import os
14+
import tempfile
15+
import unittest
16+
17+
18+
class TestOLMo2(unittest.TestCase):
19+
20+
def test_olmo2_fp32_cpu_random_weights(self):
21+
"""
22+
Convert a model with the same architecture as allenai/OLMo-2-7B but
23+
with randomly initialised weights to an fp32 ONNX model targeting the
24+
CPU execution provider.
25+
26+
The test verifies that:
27+
* ``create_model`` completes without error when given a local model directory.
28+
* The expected ``model.onnx`` file is written to the output directory.
29+
* The produced ONNX file can be loaded by ``onnxruntime``.
30+
"""
31+
from tokenizers import Tokenizer
32+
from tokenizers.models import WordLevel
33+
from transformers import AutoModelForCausalLM, PreTrainedTokenizerFast
34+
from transformers.models.olmo2 import Olmo2Config
35+
36+
from modelbuilder.builder import create_model
37+
38+
config = Olmo2Config(
39+
architectures=["Olmo2ForCausalLM"],
40+
hidden_act="silu",
41+
hidden_size=512,
42+
intermediate_size=1376,
43+
max_position_embeddings=2048,
44+
model_type="olmo2",
45+
num_attention_heads=8,
46+
num_hidden_layers=2,
47+
num_key_value_heads=4,
48+
rms_norm_eps=1e-5,
49+
rope_theta=10000.0,
50+
vocab_size=50304,
51+
)
52+
53+
MODEL_NAME = "allenai/Olmo-3-7B-Instruct"
54+
55+
with tempfile.TemporaryDirectory() as model_dir:
56+
model = AutoModelForCausalLM.from_config(config)
57+
model.save_pretrained(model_dir)
58+
59+
vocab = {"<unk>": 0, "<s>": 1, "</s>": 2}
60+
tokenizer = PreTrainedTokenizerFast(
61+
tokenizer_object=Tokenizer(WordLevel(vocab=vocab, unk_token="<unk>")),
62+
bos_token="<s>",
63+
eos_token="</s>",
64+
unk_token="<unk>",
65+
)
66+
tokenizer.save_pretrained(model_dir)
67+
68+
output_dir = os.path.join(model_dir, "onnx_output")
69+
cache_dir = os.path.join(model_dir, "cache")
70+
os.makedirs(output_dir, exist_ok=True)
71+
os.makedirs(cache_dir, exist_ok=True)
72+
73+
create_model(
74+
model_name=MODEL_NAME,
75+
input_path=model_dir,
76+
output_dir=output_dir,
77+
precision="fp32",
78+
execution_provider="cpu",
79+
cache_dir=cache_dir,
80+
num_hidden_layers=1,
81+
)
82+
83+
onnx_path = os.path.join(output_dir, "model.onnx")
84+
assert os.path.exists(
85+
onnx_path
86+
), f"Expected ONNX model not found at {onnx_path}"
87+
88+
import onnxruntime
89+
90+
onnxruntime.InferenceSession(
91+
onnx_path, providers=["CPUExecutionProvider"]
92+
)
93+
94+
95+
if __name__ == "__main__":
96+
unittest.main(verbosity=2)

0 commit comments

Comments
 (0)