main
py 647 lines 22.2 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from types import SimpleNamespace
5
6 import numpy as np
7 import pytest
8 import torch
9 from transformers import Qwen3Config
10
11 from vllm.model_executor.models.interfaces import SupportsLoRA, supports_lora
12 from vllm.model_executor.models.moss_audio import (
13 MOSS_AUDIO_BOS_TOKEN,
14 MOSS_AUDIO_BOS_TOKEN_ID,
15 MOSS_AUDIO_EOS_TOKEN,
16 MOSS_AUDIO_EOS_TOKEN_ID,
17 MOSS_AUDIO_PLACEHOLDER,
18 MOSS_AUDIO_TOKEN,
19 MOSS_AUDIO_TOKEN_ID,
20 MossAudioConfig,
21 MossAudioDummyInputsBuilder,
22 MossAudioEncoder,
23 MossAudioEncoderConfig,
24 MossAudioModel,
25 MossAudioMultiModalProcessor,
26 MossAudioProcessingInfo,
27 MossAudioProcessor,
28 MossQwen3ForCausalLM,
29 MossQwen3Model,
30 )
31 from vllm.model_executor.models.utils import AutoWeightsLoader
32 from vllm.multimodal.cache import MultiModalProcessorOnlyCache
33 from vllm.multimodal.inputs import batched_tensors_equal
34 from vllm.sequence import IntermediateTensors
35
36
37 class _Tokenizer:
38 def encode(self, text, **kwargs):
39 return [ord(char) for char in text]
40
41 def decode(self, token_ids, **kwargs):
42 return "".join(chr(token_id) for token_id in token_ids)
43
44 def batch_decode(self, batch_token_ids, **kwargs):
45 return [self.decode(token_ids, **kwargs) for token_ids in batch_token_ids]
46
47
48 class _MMConfig:
49 enable_mm_embeds = False
50 mm_processor_cache_gb = 1
51 mm_hasher_algorithm = "blake3"
52
53 def merge_mm_processor_kwargs(self, kwargs):
54 return dict(kwargs)
55
56 def get_limit_per_prompt(self, modality):
57 del modality
58 return 3
59
60
61 class _ModelConfig:
62 def __init__(self):
63 self.model = "OpenMOSS-Team/MOSS-Audio-4B-Instruct"
64 self.revision = None
65 self.max_model_len = 4096
66 self.encoder_config = {}
67 self.dtype = torch.float32
68 self.hf_config = MossAudioConfig(language_config=Qwen3Config())
69 self.multimodal_config = _MMConfig()
70
71 def get_multimodal_config(self):
72 return self.multimodal_config
73
74 def get_inputs_embeds_size(self):
75 return None
76
77
78 class _ProcessingContext:
79 def __init__(self):
80 self.model_config = _ModelConfig()
81 self.tokenizer = _Tokenizer()
82
83 def get_tokenizer(self):
84 return self.tokenizer
85
86 def get_hf_config(self):
87 return self.model_config.hf_config
88
89 def get_mm_config(self):
90 return self.model_config.get_multimodal_config()
91
92 def get_merged_mm_kwargs(self, kwargs):
93 return self.get_mm_config().merge_mm_processor_kwargs(kwargs)
94
95 def call_hf_processor(self, hf_processor, data, kwargs):
96 merged_kwargs = self.get_merged_mm_kwargs(kwargs)
97 merged_kwargs.setdefault("return_tensors", "pt")
98 return hf_processor(**data, **merged_kwargs)
99
100
101 class _TestMossAudioProcessingInfo(MossAudioProcessingInfo):
102 def _get_processor_config_defaults(self):
103 return {}
104
105
106 def _vllm_config(tensor_parallel_size=1, pipeline_parallel_size=1, hf_config=None):
107 if hf_config is None:
108 hf_config = MossAudioConfig(language_config=Qwen3Config())
109 return SimpleNamespace(
110 model_config=SimpleNamespace(
111 hf_config=hf_config,
112 multimodal_config=None,
113 ),
114 quant_config=None,
115 parallel_config=SimpleNamespace(
116 tensor_parallel_size=tensor_parallel_size,
117 pipeline_parallel_size=pipeline_parallel_size,
118 ),
119 )
120
121
122 class _FakeAudioEncoder:
123 dtype = torch.float32
124
125 def __init__(self, deepstack_layers=0):
126 self.deepstack_layers = deepstack_layers
127 self.output_deepstack_hidden_states = None
128 self.input_shape = None
129 self.feature_lens = None
130
131 def __call__(self, audio_data, *, feature_lens, output_deepstack_hidden_states):
132 self.input_shape = tuple(audio_data.shape)
133 self.feature_lens = feature_lens.detach().cpu().clone()
134 self.output_deepstack_hidden_states = output_deepstack_hidden_states
135 lengths = MossAudioEncoder._compute_downsampled_length(feature_lens)
136 hidden_states = torch.ones(1, int(lengths.sum().item()), 8)
137 if not output_deepstack_hidden_states:
138 return hidden_states, None
139 return hidden_states, [
140 hidden_states * scale for scale in range(2, 2 + self.deepstack_layers)
141 ]
142
143
144 def _patch_tensor_parallel_for_linear_layers(monkeypatch, tp_size=1, tp_rank=0):
145 import vllm.model_executor.layers.linear as linear_layers
146 import vllm.model_executor.models.moss_audio as moss_audio_module
147 import vllm.model_executor.parameter as parameter_module
148
149 for module in (moss_audio_module, linear_layers, parameter_module):
150 monkeypatch.setattr(
151 module, "get_tensor_model_parallel_world_size", lambda: tp_size
152 )
153 monkeypatch.setattr(
154 linear_layers, "get_tensor_model_parallel_rank", lambda: tp_rank
155 )
156 monkeypatch.setattr(
157 parameter_module, "get_tensor_model_parallel_rank", lambda: tp_rank
158 )
159 monkeypatch.setattr(
160 linear_layers, "tensor_model_parallel_all_reduce", lambda tensor: tensor
161 )
162
163
164 def _build_moss_audio_processor(cache=None):
165 ctx = _ProcessingContext()
166 info = _TestMossAudioProcessingInfo(ctx)
167 return (
168 MossAudioMultiModalProcessor(
169 info,
170 MossAudioDummyInputsBuilder(info),
171 ),
172 ctx,
173 )
174
175
176 def _assert_mm_inputs_equal(left, right):
177 assert left["prompt_token_ids"] == right["prompt_token_ids"]
178 assert left["mm_hashes"] == right["mm_hashes"]
179
180 left_placeholder = left["mm_placeholders"]["audio"][0]
181 right_placeholder = right["mm_placeholders"]["audio"][0]
182 assert left_placeholder.offset == right_placeholder.offset
183 assert left_placeholder.length == right_placeholder.length
184 assert left_placeholder.is_embed.tolist() == right_placeholder.is_embed.tolist()
185
186 assert batched_tensors_equal(
187 left["mm_kwargs"].get_data(),
188 right["mm_kwargs"].get_data(),
189 )
190
191
192 @pytest.mark.parametrize(
193 ("prompt", "prefix"),
194 [
195 (
196 f"before {MOSS_AUDIO_PLACEHOLDER} after",
197 [*[ord(char) for char in "before "], MOSS_AUDIO_BOS_TOKEN_ID],
198 ),
199 (
200 f"before {MOSS_AUDIO_BOS_TOKEN}{MOSS_AUDIO_TOKEN}"
201 f"{MOSS_AUDIO_TOKEN}{MOSS_AUDIO_EOS_TOKEN} after",
202 [*[ord(char) for char in "before "], MOSS_AUDIO_BOS_TOKEN_ID],
203 ),
204 ("Describe this audio.", [MOSS_AUDIO_BOS_TOKEN_ID]),
205 ],
206 )
207 def test_moss_audio_processor_expands_audio_placeholders(prompt, prefix):
208 raw_mel_len = 17
209 processed = MossAudioProcessor(_Tokenizer())(
210 text=prompt, audio=[torch.zeros(160 * raw_mel_len)]
211 )
212 input_ids = processed["input_ids"][0].tolist()
213
214 assert input_ids[: len(prefix)] == prefix
215 assert input_ids.count(MOSS_AUDIO_BOS_TOKEN_ID) == 1
216 assert input_ids.count(MOSS_AUDIO_EOS_TOKEN_ID) == 1
217 assert input_ids.count(MOSS_AUDIO_TOKEN_ID) == (
218 MossAudioEncoder.compute_num_audio_tokens(raw_mel_len)
219 )
220 assert processed["audio_data"].shape == (1, 128, raw_mel_len)
221 assert processed["audio_data_seqlens"].tolist() == [raw_mel_len]
222
223
224 def test_moss_audio_processor_preserves_placeholder_without_audio():
225 processed = MossAudioProcessor(_Tokenizer())(
226 text=f"before {MOSS_AUDIO_PLACEHOLDER} after"
227 )
228
229 assert processed["input_ids"][0].tolist() == [
230 *[ord(char) for char in "before "],
231 MOSS_AUDIO_BOS_TOKEN_ID,
232 MOSS_AUDIO_TOKEN_ID,
233 MOSS_AUDIO_EOS_TOKEN_ID,
234 *[ord(char) for char in " after"],
235 ]
236 assert "audio_data" not in processed
237 assert "audio_data_seqlens" not in processed
238
239
240 def test_moss_audio_multimodal_processor_handles_token_and_cache_paths():
241 raw_mel_len = 17
242 audio = np.zeros(160 * raw_mel_len, dtype=np.float32)
243 prompt = f"{MOSS_AUDIO_PLACEHOLDER}\nTranscribe this audio."
244
245 baseline_processor, ctx = _build_moss_audio_processor()
246 mm_items = baseline_processor.info.parse_mm_data({"audio": [audio]})
247 token_prompt = ctx.get_tokenizer().encode(prompt, add_special_tokens=False)
248
249 baseline_text = baseline_processor(
250 prompt,
251 mm_items=mm_items,
252 hf_processor_mm_kwargs={},
253 )
254 baseline_token = baseline_processor(
255 token_prompt,
256 mm_items=mm_items,
257 hf_processor_mm_kwargs={},
258 )
259
260 cache = MultiModalProcessorOnlyCache(ctx.model_config)
261 cached_processor, _ = _build_moss_audio_processor()
262 cached_text_miss = cached_processor(
263 prompt,
264 mm_items=mm_items,
265 hf_processor_mm_kwargs={},
266 cache=cache,
267 )
268 cached_text_hit = cached_processor(
269 prompt,
270 mm_items=mm_items,
271 hf_processor_mm_kwargs={},
272 cache=cache,
273 )
274 cached_token_hit = cached_processor(
275 token_prompt,
276 mm_items=mm_items,
277 hf_processor_mm_kwargs={},
278 cache=cache,
279 )
280
281 expected_audio_tokens = MossAudioEncoder.compute_num_audio_tokens(raw_mel_len)
282 prompt_token_ids = baseline_text["prompt_token_ids"]
283 assert prompt_token_ids.count(MOSS_AUDIO_TOKEN_ID) == expected_audio_tokens
284 assert baseline_text["mm_placeholders"]["audio"][0].length == (
285 expected_audio_tokens + 2
286 )
287
288 _assert_mm_inputs_equal(baseline_text, baseline_token)
289 _assert_mm_inputs_equal(baseline_text, cached_text_miss)
290 _assert_mm_inputs_equal(baseline_text, cached_text_hit)
291 _assert_mm_inputs_equal(baseline_text, cached_token_hit)
292
293
294 def test_moss_audio_supports_language_model_lora_only():
295 assert supports_lora(MossAudioModel)
296
297 model = object.__new__(MossAudioModel)
298 assert isinstance(model, SupportsLoRA)
299
300 mapping = model.get_mm_mapping()
301 assert mapping.language_model == ["language_model."]
302 assert mapping.tower_model == []
303 assert mapping.connector == []
304
305
306 def test_moss_audio_error_paths():
307 model = object.__new__(MossAudioModel)
308 with pytest.raises(ValueError, match="DeepStack audio token count mismatch"):
309 model._cache_deepstack_input_embeds(
310 inputs_embeds=torch.zeros(4, 8),
311 deepstack_embeddings=((torch.ones(1, 8),),),
312 is_multimodal=torch.tensor([False, True, True, False]),
313 )
314
315 with pytest.raises(ValueError, match="too short"):
316 MossAudioProcessor(_Tokenizer())(
317 text=MOSS_AUDIO_PLACEHOLDER, audio=[torch.empty(0)]
318 )
319 with pytest.raises(ValueError, match="too short"):
320 model._parse_and_validate_audio_input(
321 audio_data=torch.zeros(1, 128, 1),
322 audio_data_seqlens=torch.tensor([0], dtype=torch.long),
323 )
324
325
326 def test_moss_audio_validates_tp_config():
327 vllm_config = _vllm_config(tensor_parallel_size=2)
328 vllm_config.model_config.hf_config.adapter_hidden_size = 7
329
330 with pytest.raises(ValueError, match="adapter_hidden_size"):
331 MossAudioModel(vllm_config=vllm_config)
332
333 vllm_config = _vllm_config(tensor_parallel_size=2)
334 vllm_config.model_config.hf_config.audio_config.d_model = 6
335 vllm_config.model_config.hf_config.audio_config.encoder_attention_heads = 3
336 with pytest.raises(ValueError, match="encoder_attention_heads"):
337 MossAudioModel(vllm_config=vllm_config)
338
339
340 def test_moss_audio_rejects_audio_data_list_seqlen_count_mismatch():
341 model = object.__new__(MossAudioModel)
342
343 with pytest.raises(ValueError, match="audio_data batch size"):
344 model._parse_and_validate_audio_input(
345 audio_data=[torch.zeros(128, 8), torch.zeros(128, 11)],
346 audio_data_seqlens=torch.tensor([8], dtype=torch.long),
347 )
348
349
350 @pytest.mark.parametrize("deepstack_scales", [(), (7, 11)])
351 def test_moss_audio_embed_multimodal_packs_by_audio(deepstack_scales):
352 model = object.__new__(MossAudioModel)
353 model.audio_encoder = _FakeAudioEncoder(len(deepstack_scales))
354 model.audio_adapter = lambda hidden_states: hidden_states * 5
355 model.deepstack_audio_merger_list = [
356 lambda hidden_states, scale=scale: hidden_states * scale
357 for scale in deepstack_scales
358 ]
359 model.deepstack_input_embeds = None
360
361 embeddings = model.embed_multimodal(
362 audio_data=torch.zeros(2, 128, 9),
363 audio_data_seqlens=torch.tensor([8, 9], dtype=torch.long),
364 )
365
366 assert model.audio_encoder.output_deepstack_hidden_states is bool(deepstack_scales)
367 assert [embeds.shape for embeds in embeddings] == [
368 torch.Size([1, 8 * (1 + len(deepstack_scales))]),
369 torch.Size([2, 8 * (1 + len(deepstack_scales))]),
370 ]
371 if not deepstack_scales:
372 assert model.deepstack_input_embeds is None
373 return
374
375 main_embeddings, deepstack_embeddings = model._split_multimodal_embeddings(
376 embeddings, hidden_size=8
377 )
378 assert [embeds.shape for embeds in main_embeddings] == [
379 torch.Size([1, 8]),
380 torch.Size([2, 8]),
381 ]
382 assert [[e.shape for e in layer] for layer in deepstack_embeddings] == [
383 [torch.Size([1, 8]), torch.Size([2, 8])] for _ in deepstack_scales
384 ]
385 assert torch.equal(main_embeddings[0], torch.full((1, 8), 5.0))
386 for idx, scale in enumerate(deepstack_scales):
387 assert torch.equal(
388 deepstack_embeddings[idx][0],
389 torch.full((1, 8), float((idx + 2) * scale)),
390 )
391
392
393 def test_moss_audio_embed_input_ids_caches_packed_deepstack():
394 class _FakeLanguageModel:
395 def embed_input_ids(self, input_ids):
396 return torch.zeros(input_ids.shape[0], 8)
397
398 model = object.__new__(MossAudioModel)
399 model.language_model = _FakeLanguageModel()
400 model.deepstack_audio_merger_list = [object(), object()]
401 model.deepstack_input_embeds = None
402 multimodal_embeddings = (
403 torch.cat([torch.full((1, 8), x) for x in (5.0, 14.0, 33.0)], dim=-1),
404 torch.cat([torch.full((2, 8), x) for x in (7.0, 22.0, 44.0)], dim=-1),
405 )
406 is_multimodal = torch.tensor([False, True, True, True, False])
407
408 inputs_embeds = model.embed_input_ids(
409 input_ids=torch.arange(5),
410 multimodal_embeddings=multimodal_embeddings,
411 is_multimodal=is_multimodal,
412 )
413
414 assert torch.equal(inputs_embeds[1], torch.full((8,), 5.0))
415 assert torch.equal(inputs_embeds[2], torch.full((8,), 7.0))
416 assert torch.equal(inputs_embeds[3], torch.full((8,), 7.0))
417 assert model.deepstack_input_embeds is not None
418 tensors = model.deepstack_input_embeds.tensors
419 assert set(tensors) == {"deepstack_input_embeds_0", "deepstack_input_embeds_1"}
420 for tensor in tensors.values():
421 assert tensor[is_multimodal].abs().sum() > 0
422 assert torch.equal(tensor[~is_multimodal], torch.zeros(2, 8))
423
424
425 def _patch_pp_group(monkeypatch, *, first=True, last=True):
426 import vllm.model_executor.models.moss_audio as moss_audio_module
427
428 monkeypatch.setattr(
429 moss_audio_module,
430 "get_pp_group",
431 lambda: SimpleNamespace(is_first_rank=first, is_last_rank=last),
432 )
433
434
435 def test_moss_audio_pp_forward_routes_deepstack(monkeypatch):
436 for first in (True, False):
437 calls: list[dict[str, object]] = []
438
439 def fake_lm(*args, _calls=calls, **kwargs):
440 del args
441 _calls.append(kwargs)
442 return torch.ones(1, 1)
443
444 _patch_pp_group(monkeypatch, first=first)
445 model = object.__new__(MossAudioModel)
446 torch.nn.Module.__init__(model)
447 model.language_model = fake_lm
448 cached = IntermediateTensors({"deepstack_input_embeds_0": torch.ones(3, 8)})
449 inter = IntermediateTensors(
450 {
451 "hidden_states": torch.ones(3, 8),
452 "residual": torch.zeros(3, 8),
453 "deepstack_input_embeds_0": torch.full((3, 8), 5.0),
454 }
455 )
456 inputs_embeds = torch.full((3, 8), 9.0)
457 model.deepstack_input_embeds = cached
458
459 model.forward(
460 input_ids=None,
461 positions=torch.arange(3),
462 intermediate_tensors=None if first else inter,
463 inputs_embeds=inputs_embeds if first else None,
464 )
465
466 kwargs = calls[0]
467 assert kwargs["inputs_embeds"] is (inputs_embeds if first else None)
468 assert kwargs["deepstack_input_embeds"] is (cached if first else inter)
469 assert model.deepstack_input_embeds is None
470
471 calls = []
472
473 def fake_lm_non_first_rank(*args, **kwargs):
474 del args
475 calls.append(kwargs)
476 return torch.ones(1, 1)
477
478 _patch_pp_group(monkeypatch, first=False)
479 model = object.__new__(MossAudioModel)
480 torch.nn.Module.__init__(model)
481 model.language_model = fake_lm_non_first_rank
482 model.deepstack_input_embeds = IntermediateTensors({})
483 inter = IntermediateTensors(
484 {
485 "hidden_states": torch.ones(3, 8),
486 "residual": torch.zeros(3, 8),
487 }
488 )
489
490 model.forward(
491 input_ids=None,
492 positions=torch.arange(3),
493 intermediate_tensors=inter,
494 inputs_embeds=torch.ones(3, 8),
495 )
496 assert calls[0]["inputs_embeds"] is None
497 assert calls[0]["deepstack_input_embeds"] is inter
498
499
500 def test_moss_qwen3_deepstack_keys_for_pp(monkeypatch):
501 class AddOne(torch.nn.Module):
502 def forward(self, positions, hidden_states, residual):
503 del positions, residual
504 return hidden_states + 1, torch.zeros_like(hidden_states)
505
506 def make_model(num_layers, deepstack_layers=None):
507 model = object.__new__(MossQwen3Model)
508 torch.nn.Module.__init__(model)
509 model.start_layer, model.end_layer = 0, num_layers
510 model.layers = torch.nn.ModuleList([AddOne() for _ in range(num_layers)])
511 model.norm = lambda hidden_states, residual: (hidden_states, residual)
512 model._maybe_add_hidden_state = lambda aux, *args: aux
513 model.deepstack_inject_layer_indices = (
514 range(0) if deepstack_layers is None else deepstack_layers
515 )
516 return model
517
518 _patch_pp_group(monkeypatch, first=True, last=True)
519 output = make_model(3).forward(
520 input_ids=None,
521 positions=torch.arange(2),
522 inputs_embeds=torch.zeros(2, 4),
523 deepstack_input_embeds=IntermediateTensors(
524 {
525 "deepstack_input_embeds_2": torch.full((2, 4), 5.0),
526 }
527 ),
528 )
529 assert torch.equal(output, torch.full((2, 4), 8.0))
530
531 _patch_pp_group(monkeypatch, first=True, last=False)
532 deepstack = IntermediateTensors(
533 {
534 "deepstack_input_embeds_0": torch.full((2, 4), 7.0),
535 "deepstack_input_embeds_3": torch.full((2, 4), 11.0),
536 }
537 )
538 output = make_model(2, range(4)).forward(
539 input_ids=None,
540 positions=torch.arange(2),
541 inputs_embeds=torch.zeros(2, 4),
542 deepstack_input_embeds=deepstack,
543 )
544 assert isinstance(output, IntermediateTensors)
545 assert set(output.tensors) == {
546 "hidden_states",
547 "residual",
548 "deepstack_input_embeds_2",
549 "deepstack_input_embeds_3",
550 }
551 assert torch.equal(output["hidden_states"], torch.full((2, 4), 9.0))
552 assert torch.equal(output["deepstack_input_embeds_2"], torch.zeros(2, 4))
553 assert output["deepstack_input_embeds_3"] is deepstack["deepstack_input_embeds_3"]
554
555 inner_model = make_model(0, range(2))
556 inner_model.make_empty_intermediate_tensors = lambda batch, dtype, device: (
557 IntermediateTensors(
558 {
559 "hidden_states": torch.zeros(batch, 4, dtype=dtype, device=device),
560 "residual": torch.zeros(batch, 4, dtype=dtype, device=device),
561 }
562 )
563 )
564 language_model = object.__new__(MossQwen3ForCausalLM)
565 torch.nn.Module.__init__(language_model)
566 language_model.model = inner_model
567 language_model.config = SimpleNamespace(hidden_size=4)
568 language_model.deepstack_inject_layer_indices = range(2)
569
570 tensors = MossQwen3ForCausalLM.make_empty_intermediate_tensors(
571 language_model,
572 batch_size=3,
573 dtype=torch.float16,
574 device=torch.device("cpu"),
575 )
576
577 assert set(tensors.tensors) == {
578 "hidden_states",
579 "residual",
580 "deepstack_input_embeds_0",
581 "deepstack_input_embeds_1",
582 }
583 assert tensors["deepstack_input_embeds_0"].shape == (3, 4)
584 assert tensors["deepstack_input_embeds_0"].dtype == torch.float16
585
586 _patch_pp_group(monkeypatch, first=True, last=False)
587 forward_tensors = inner_model.forward(
588 input_ids=None,
589 positions=torch.arange(3),
590 inputs_embeds=torch.ones(3, 4, dtype=torch.float16),
591 deepstack_input_embeds=None,
592 )
593 assert isinstance(forward_tensors, IntermediateTensors)
594 assert set(forward_tensors.tensors) == set(tensors.tensors)
595
596
597 def test_moss_audio_encoder_loads_realistic_attention_weight_names(monkeypatch):
598 from vllm.config import VllmConfig, set_current_vllm_config
599 from vllm.config.device import DeviceConfig
600
601 _patch_tensor_parallel_for_linear_layers(monkeypatch, tp_size=2)
602 config = MossAudioEncoderConfig(
603 d_model=8,
604 output_dim=8,
605 num_mel_bins=8,
606 encoder_layers=1,
607 encoder_attention_heads=2,
608 encoder_ffn_dim=16,
609 downsample_hidden_size=2,
610 deepstack_encoder_layer_indexes=[],
611 )
612 with set_current_vllm_config(VllmConfig(device_config=DeviceConfig(device="cpu"))):
613 encoder = MossAudioEncoder(config)
614
615 attention = encoder.layers[0].self_attn
616 assert all(hasattr(attention, name) for name in ("q_proj", "k_proj", "v_proj"))
617 assert hasattr(attention, "out_proj")
618 assert not hasattr(attention, "qkv")
619 assert attention.k_proj.bias is None
620
621 weight_names = [
622 "layers.0.self_attn.q_proj.weight",
623 "layers.0.self_attn.q_proj.bias",
624 "layers.0.self_attn.k_proj.weight",
625 "layers.0.self_attn.v_proj.weight",
626 "layers.0.self_attn.v_proj.bias",
627 "layers.0.self_attn.out_proj.weight",
628 "layers.0.self_attn.out_proj.bias",
629 "conv1.weight",
630 "conv1.bias",
631 ]
632 params = dict(encoder.named_parameters(remove_duplicate=False))
633 assert "layers.0.self_attn.k_proj.bias" not in params
634 weights = {
635 name: torch.full_like(params[name], fill_value=float(i + 1))
636 for i, name in enumerate(weight_names)
637 }
638
639 loaded = AutoWeightsLoader(encoder).load_weights(weights.items())
640
641 assert "load_weights" not in MossAudioEncoder.__dict__
642 assert loaded == set(weight_names)
643 assert not any(".qkv." in name for name in loaded)
644 assert torch.equal(
645 params["layers.0.self_attn.q_proj.weight"],
646 weights["layers.0.self_attn.q_proj.weight"],
647 )