|
10 | 10 | """ |
11 | 11 |
|
12 | 12 | import os |
| 13 | +import tempfile |
13 | 14 | import unittest |
14 | 15 |
|
15 | 16 | MODEL_NAME = "arnir0/Tiny-LLM" |
@@ -54,6 +55,93 @@ def test_tiny_llm_fp32_cpu(self): |
54 | 55 |
|
55 | 56 | onnxruntime.InferenceSession(onnx_path, providers=["CPUExecutionProvider"]) |
56 | 57 |
|
| 58 | + def test_tiny_llm_fp32_cpu_random_weights(self): |
| 59 | + """ |
| 60 | + Convert a model with the same architecture as arnir0/Tiny-LLM but with |
| 61 | + randomly initialised weights to an fp32 ONNX model targeting the CPU |
| 62 | + execution provider. |
| 63 | +
|
| 64 | + Using random weights avoids downloading the pretrained weights from |
| 65 | + Hugging Face, making this test completely offline and suitable for CI |
| 66 | + environments without internet access. The full 8-layer config is saved |
| 67 | + locally, but only one hidden layer is materialised during conversion |
| 68 | + (via the ``num_hidden_layers`` extra option passed to ``create_model``) |
| 69 | + to keep the test fast. |
| 70 | +
|
| 71 | + The test verifies that: |
| 72 | + * ``create_model`` completes without error when given a local model directory. |
| 73 | + * The expected ``model.onnx`` file is written to the output directory. |
| 74 | + * The produced ONNX file can be loaded by ``onnxruntime``. |
| 75 | + """ |
| 76 | + from tokenizers import Tokenizer |
| 77 | + from tokenizers.models import WordLevel |
| 78 | + from transformers import AutoModelForCausalLM, LlamaConfig, PreTrainedTokenizerFast |
| 79 | + |
| 80 | + from modelbuilder.builder import create_model |
| 81 | + |
| 82 | + # Config matching the arnir0/Tiny-LLM architecture (LlamaForCausalLM, |
| 83 | + # ~10M parameters). These values are hardcoded so the test runs |
| 84 | + # completely offline without downloading any files from Hugging Face. |
| 85 | + config = LlamaConfig( |
| 86 | + architectures=["LlamaForCausalLM"], |
| 87 | + bos_token_id=1, |
| 88 | + eos_token_id=2, |
| 89 | + hidden_act="silu", |
| 90 | + hidden_size=512, |
| 91 | + intermediate_size=1376, |
| 92 | + max_position_embeddings=2048, |
| 93 | + model_type="llama", |
| 94 | + num_attention_heads=8, |
| 95 | + num_hidden_layers=8, |
| 96 | + num_key_value_heads=4, |
| 97 | + rms_norm_eps=1e-05, |
| 98 | + rope_theta=10000.0, |
| 99 | + vocab_size=32000, |
| 100 | + ) |
| 101 | + |
| 102 | + with tempfile.TemporaryDirectory() as model_dir: |
| 103 | + # Create a model with random weights from the config and save it. |
| 104 | + model = AutoModelForCausalLM.from_config(config) |
| 105 | + model.save_pretrained(model_dir) |
| 106 | + |
| 107 | + # Create and save a minimal tokenizer so that save_processing() |
| 108 | + # inside create_model() can load and copy it to the output folder. |
| 109 | + vocab = {"<unk>": 0, "<s>": 1, "</s>": 2} |
| 110 | + tokenizer = PreTrainedTokenizerFast( |
| 111 | + tokenizer_object=Tokenizer(WordLevel(vocab=vocab, unk_token="<unk>")), |
| 112 | + bos_token="<s>", |
| 113 | + eos_token="</s>", |
| 114 | + unk_token="<unk>", |
| 115 | + ) |
| 116 | + tokenizer.save_pretrained(model_dir) |
| 117 | + |
| 118 | + output_dir = os.path.join(model_dir, "onnx_output") |
| 119 | + cache_dir = os.path.join(model_dir, "cache") |
| 120 | + os.makedirs(output_dir, exist_ok=True) |
| 121 | + os.makedirs(cache_dir, exist_ok=True) |
| 122 | + |
| 123 | + create_model( |
| 124 | + model_name=MODEL_NAME, |
| 125 | + input_path=model_dir, |
| 126 | + output_dir=output_dir, |
| 127 | + precision="fp32", |
| 128 | + execution_provider="cpu", |
| 129 | + cache_dir=cache_dir, |
| 130 | + num_hidden_layers=1, |
| 131 | + ) |
| 132 | + |
| 133 | + onnx_path = os.path.join(output_dir, "model.onnx") |
| 134 | + assert os.path.exists( |
| 135 | + onnx_path |
| 136 | + ), f"Expected ONNX model not found at {onnx_path}" |
| 137 | + |
| 138 | + # Validate that the ONNX model can be loaded by the runtime. |
| 139 | + import onnxruntime |
| 140 | + |
| 141 | + onnxruntime.InferenceSession( |
| 142 | + onnx_path, providers=["CPUExecutionProvider"] |
| 143 | + ) |
| 144 | + |
57 | 145 |
|
58 | 146 | if __name__ == "__main__": |
59 | 147 | unittest.main(verbosity=2) |
0 commit comments