Tom Aarsen commited on
Commit
85f85b8
·
1 Parent(s): 93aadb1

Integrate with Sentence Transformers

Browse files
1_Pooling/config.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "embedding_dimension": 2048,
3
+ "pooling_mode": "lasttoken",
4
+ "include_prompt": true
5
+ }
README.md CHANGED
@@ -1,8 +1,11 @@
1
  ---
2
  license: apache-2.0
 
 
3
  tags:
4
  - multimodal-embedding
5
  - transformers
 
6
  - feature-extraction
7
  ---
8
  # May 2026 update of LCO-Embedding models
@@ -28,6 +31,115 @@ In this version, we make substantial improvements on all 4 modalities (text, ima
28
 
29
  # Usage
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  All inference code is the same with our [OG models](https://huggingface.co/LCO-Embedding/LCO-Embedding-Omni-7B) and can seamlessly support the new checkpoint by changing the model name.
32
 
33
  # Contributors
 
1
  ---
2
  license: apache-2.0
3
+ pipeline_tag: feature-extraction
4
+ library_name: sentence-transformers
5
  tags:
6
  - multimodal-embedding
7
  - transformers
8
+ - sentence-transformers
9
  - feature-extraction
10
  ---
11
  # May 2026 update of LCO-Embedding models
 
31
 
32
  # Usage
33
 
34
+ ## Using Sentence Transformers
35
+
36
+ Install Sentence Transformers with the multimodal extras (for image, audio, and video support):
37
+
38
+ ```bash
39
+ pip install "sentence_transformers[image,audio,video]" "transformers>=5.6.0"
40
+ ```
41
+
42
+ ```python
43
+ import torch
44
+ from sentence_transformers import SentenceTransformer
45
+
46
+ model = SentenceTransformer(
47
+ "LCO-Embedding/LCO-Embedding-Omni-3B-2605",
48
+ model_kwargs={
49
+ "dtype": torch.bfloat16,
50
+ # "attn_implementation": "flash_attention_2", # recommended, if a flash-attn build exists for your platform
51
+ },
52
+ )
53
+ ```
54
+
55
+ The same `Summarize the above <modality> in one word:` instruction used in the paper is baked into the chat template, so `encode()` takes plain text, file paths, URLs, or multimodal dicts directly.
56
+
57
+ ### Text Retrieval
58
+ ```python
59
+ query = "What is the tallest mountain in the world?"
60
+ documents = [
61
+ "Mount Everest is Earth's highest mountain above sea level, located in the Mahalangur Himal sub-range of the Himalayas. Its elevation of 8,848.86 metres was established by a joint Chinese-Nepali survey in 2020.",
62
+ "K2, at 8,611 metres above sea level, is the second-highest mountain on Earth, after Mount Everest. It lies in the Karakoram range on the China-Pakistan border.",
63
+ "Mount Kilimanjaro is a dormant volcano in Tanzania. It is the highest mountain in Africa, with its summit about 5,895 metres above sea level.",
64
+ ]
65
+
66
+ query_embedding = model.encode(query)
67
+ document_embeddings = model.encode(documents)
68
+ print(model.similarity(query_embedding, document_embeddings))
69
+ # tensor([[0.5368, 0.5053, 0.4989]])
70
+ ```
71
+
72
+ ### Image Retrieval
73
+ ```python
74
+ query = "How many input modalities does Qwen2.5-Omni support?"
75
+ documents = [
76
+ "https://huggingface.co/Tevatron/OmniEmbed-v0.1/resolve/main/assets/qwen2.5omni_hgf.png",
77
+ "https://huggingface.co/Tevatron/OmniEmbed-v0.1/resolve/main/assets/llama4_hgf.png",
78
+ ]
79
+
80
+ query_embedding = model.encode(query)
81
+ document_embeddings = model.encode(documents, batch_size=1)
82
+ print(model.similarity(query_embedding, document_embeddings))
83
+ # tensor([[0.6544, 0.3852]])
84
+ ```
85
+
86
+ ### Audio Retrieval
87
+ ```python
88
+ query = "A light piano piece"
89
+ documents = [
90
+ "https://huggingface.co/Tevatron/OmniEmbed-v0.1/resolve/main/assets/joe_hisaishi_summer.mp3",
91
+ "https://huggingface.co/Tevatron/OmniEmbed-v0.1/resolve/main/assets/jay_chou_superman_cant_fly.mp3",
92
+ ]
93
+
94
+ query_embedding = model.encode(query)
95
+ document_embeddings = model.encode(documents, batch_size=1)
96
+ print(model.similarity(query_embedding, document_embeddings))
97
+ # tensor([[0.3649, 0.0662]])
98
+ ```
99
+
100
+ ### Video Retrieval
101
+ ```python
102
+ # For video on smaller GPUs, cap the processor up front:
103
+ model[0].processing_kwargs.update({
104
+ "video": {"max_pixels": 64 * 28 * 28, "do_sample_frames": True, "fps": 1},
105
+ })
106
+
107
+ query = "How to cook Mapo Tofu?"
108
+ documents = [
109
+ "https://huggingface.co/Tevatron/OmniEmbed-v0.1/resolve/main/assets/mapo_tofu.mp4",
110
+ "https://huggingface.co/Tevatron/OmniEmbed-v0.1/resolve/main/assets/zhajiang_noodle.mp4",
111
+ ]
112
+
113
+ query_embedding = model.encode(query)
114
+ document_embeddings = model.encode(documents, batch_size=1)
115
+ print(model.similarity(query_embedding, document_embeddings))
116
+ # tensor([[0.6408, 0.4967]])
117
+ ```
118
+
119
+ ### Multimodal Inputs
120
+
121
+ To embed a document that combines multiple modalities, pass a dict with any combination of `"text"`, `"image"`, `"audio"`, and `"video"` keys instead of a single path or string:
122
+
123
+ ```python
124
+ documents = [
125
+ {
126
+ "text": "A cooking tutorial for Mapo Tofu",
127
+ "video": "https://huggingface.co/Tevatron/OmniEmbed-v0.1/resolve/main/assets/mapo_tofu.mp4",
128
+ },
129
+ {
130
+ "image": "https://huggingface.co/Tevatron/OmniEmbed-v0.1/resolve/main/assets/qwen2.5omni_hgf.png",
131
+ "audio": "https://huggingface.co/Tevatron/OmniEmbed-v0.1/resolve/main/assets/joe_hisaishi_summer.mp3",
132
+ },
133
+ ]
134
+ document_embeddings = model.encode(documents, batch_size=1)
135
+ print(document_embeddings.shape)
136
+ # (2, 2048)
137
+ ```
138
+
139
+ The expected outputs above were produced in `bfloat16` on a CUDA device with the default (`sdpa`) attention. Exact values shift slightly in the fourth decimal with a different dtype or attention implementation.
140
+
141
+ ## Using Transformers
142
+
143
  All inference code is the same with our [OG models](https://huggingface.co/LCO-Embedding/LCO-Embedding-Omni-7B) and can seamlessly support the new checkpoint by changing the model name.
144
 
145
  # Contributors
additional_chat_templates/sentence_transformers.jinja ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- set audio_count = namespace(value=0) -%}
2
+ {%- set image_count = namespace(value=0) -%}
3
+ {%- set video_count = namespace(value=0) -%}
4
+ <|im_start|>system
5
+ You are a helpful assistant.<|im_end|>
6
+ {% for message in messages -%}
7
+ {%- if message['role'] == 'system' -%}
8
+ {#- skip: the fixed system prompt above was already emitted. Any input system
9
+ message is only present to silence Qwen2.5-Omni's default-prompt warning. -#}
10
+ {%- else -%}
11
+ <|im_start|>{{ message['role'] }}
12
+ {% if message['content'] is string -%}
13
+ {{- message['content'] -}}<|im_end|>
14
+ {% else -%}
15
+ {%- set seen = namespace(image=false, audio=false, video=false) -%}
16
+ {%- for content in message['content'] -%}
17
+ {%- if content['type'] == 'image' or 'image' in content or 'image_url' in content -%}
18
+ {%- set image_count.value = image_count.value + 1 -%}
19
+ {%- set seen.image = true -%}
20
+ {%- if add_vision_id -%}Picture {{ image_count.value }}: {% endif -%}
21
+ <|vision_bos|><|IMAGE|><|vision_eos|>
22
+ {%- elif content['type'] == 'audio' or 'audio' in content or 'audio_url' in content -%}
23
+ {%- set audio_count.value = audio_count.value + 1 -%}
24
+ {%- set seen.audio = true -%}
25
+ {%- if add_audio_id -%}Audio {{ audio_count.value }}: {% endif -%}
26
+ <|audio_bos|><|AUDIO|><|audio_eos|>
27
+ {%- elif content['type'] == 'video' or 'video' in content -%}
28
+ {%- set video_count.value = video_count.value + 1 -%}
29
+ {%- set seen.video = true -%}
30
+ {%- if add_vision_id -%}Video {{ video_count.value }}: {% endif -%}
31
+ <|vision_bos|><|VIDEO|><|vision_eos|>
32
+ {%- elif 'text' in content -%}
33
+ {{- content['text'] -}}
34
+ {%- endif -%}
35
+ {%- endfor -%}
36
+ {%- if seen.image -%}
37
+ {{ '\n' }}Summarize the above image in one word:
38
+ {%- elif seen.video -%}
39
+ {{ '\n' }}Summarize the above video in one word:
40
+ {%- elif seen.audio -%}
41
+ {{ '\n' }}Summarize the above audio in one word:
42
+ {%- else -%}
43
+ {{ '\n' }}Summarize the above text in one word:
44
+ {%- endif -%}
45
+ <|im_end|>
46
+ {% endif -%}
47
+ {%- endif -%}
48
+ {%- endfor -%}
49
+ {%- if add_generation_prompt -%}
50
+ <|im_start|>assistant
51
+ {% endif -%}
chat_template.jinja ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {% set audio_count = namespace(value=0) %}{% set image_count = namespace(value=0) %}{% set video_count = namespace(value=0) %}{% for message in messages %}{% if loop.first and message['role'] != 'system' %}<|im_start|>system
2
+ You are a helpful assistant.<|im_end|>
3
+ {% endif %}<|im_start|>{{ message['role'] }}
4
+ {% if message['content'] is string %}{{ message['content'] }}<|im_end|>
5
+ {% else %}{% for content in message['content'] %}{% if content['type'] == 'image' or 'image' in content or 'image_url' in content %}{% set image_count.value = image_count.value + 1 %}{% if add_vision_id %}Picture {{ image_count.value }}: {% endif %}<|vision_bos|><|IMAGE|><|vision_eos|>{% elif content['type'] == 'audio' or 'audio' in content or 'audio_url' in content %}{% set audio_count.value = audio_count.value + 1 %}{% if add_audio_id %}Audio {{ audio_count.value }}: {% endif %}<|audio_bos|><|AUDIO|><|audio_eos|>{% elif content['type'] == 'video' or 'video' in content %}{% set video_count.value = video_count.value + 1 %}{% if add_vision_id %}Video {{ video_count.value }}: {% endif %}<|vision_bos|><|VIDEO|><|vision_eos|>{% elif 'text' in content %}{{ content['text'] }}{% endif %}{% endfor %}<|im_end|>
6
+ {% endif %}{% endfor %}{% if add_generation_prompt %}<|im_start|>assistant
7
+ {% endif %}
chat_template.json DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "chat_template": "{% set audio_count = namespace(value=0) %}{% set image_count = namespace(value=0) %}{% set video_count = namespace(value=0) %}{% for message in messages %}{% if loop.first and message['role'] != 'system' %}<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n{% endif %}<|im_start|>{{ message['role'] }}\n{% if message['content'] is string %}{{ message['content'] }}<|im_end|>\n{% else %}{% for content in message['content'] %}{% if content['type'] == 'image' or 'image' in content or 'image_url' in content %}{% set image_count.value = image_count.value + 1 %}{% if add_vision_id %}Picture {{ image_count.value }}: {% endif %}<|vision_bos|><|IMAGE|><|vision_eos|>{% elif content['type'] == 'audio' or 'audio' in content or 'audio_url' in content %}{% set audio_count.value = audio_count.value + 1 %}{% if add_audio_id %}Audio {{ audio_count.value }}: {% endif %}<|audio_bos|><|AUDIO|><|audio_eos|>{% elif content['type'] == 'video' or 'video' in content %}{% set video_count.value = video_count.value + 1 %}{% if add_vision_id %}Video {{ video_count.value }}: {% endif %}<|vision_bos|><|VIDEO|><|vision_eos|>{% elif 'text' in content %}{{ content['text'] }}{% endif %}{% endfor %}<|im_end|>\n{% endif %}{% endfor %}{% if add_generation_prompt %}<|im_start|>assistant\n{% endif %}"
3
- }
 
 
 
 
config_sentence_transformers.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "__version__": {
3
+ "pytorch": "2.10.0+cu128",
4
+ "sentence_transformers": "5.4.0",
5
+ "transformers": "5.6.0"
6
+ },
7
+ "default_prompt_name": "default",
8
+ "model_type": "SentenceTransformer",
9
+ "prompts": {
10
+ "default": "You are Qwen, a virtual human developed by the Qwen Team, Alibaba Group, capable of perceiving auditory and visual inputs, as well as generating text and speech."
11
+ },
12
+ "similarity_fn_name": "cosine"
13
+ }
modules.json ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "idx": 0,
4
+ "name": "0",
5
+ "path": "",
6
+ "type": "sentence_transformers.base.modules.transformer.Transformer"
7
+ },
8
+ {
9
+ "idx": 1,
10
+ "name": "1",
11
+ "path": "1_Pooling",
12
+ "type": "sentence_transformers.sentence_transformer.modules.pooling.Pooling"
13
+ },
14
+ {
15
+ "idx": 2,
16
+ "name": "2",
17
+ "path": "2_Normalize",
18
+ "type": "sentence_transformers.sentence_transformer.modules.normalize.Normalize"
19
+ }
20
+ ]
sentence_bert_config.json ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "transformer_task": "any-to-any",
3
+ "modality_config": {
4
+ "text": {
5
+ "method": "forward",
6
+ "method_output_name": [
7
+ "hidden_states",
8
+ -1
9
+ ]
10
+ },
11
+ "image": {
12
+ "method": "forward",
13
+ "method_output_name": [
14
+ "hidden_states",
15
+ -1
16
+ ]
17
+ },
18
+ "audio": {
19
+ "method": "forward",
20
+ "method_output_name": [
21
+ "hidden_states",
22
+ -1
23
+ ]
24
+ },
25
+ "video": {
26
+ "method": "forward",
27
+ "method_output_name": [
28
+ "hidden_states",
29
+ -1
30
+ ]
31
+ },
32
+ "message": {
33
+ "method": "forward",
34
+ "method_output_name": [
35
+ "hidden_states",
36
+ -1
37
+ ],
38
+ "format": "structured"
39
+ }
40
+ },
41
+ "module_output_name": "token_embeddings",
42
+ "processing_kwargs": {
43
+ "chat_template": {
44
+ "chat_template": "sentence_transformers",
45
+ "add_generation_prompt": true
46
+ }
47
+ }
48
+ }