Skip to content

Commit 2e64c33

Browse files
authored
Merge branch 'ggml-org:master' into master
2 parents bcbce3e + 7cbec34 commit 2e64c33

1 file changed

Lines changed: 223 additions & 148 deletions

File tree

‎examples/model-conversion/scripts/embedding/run-original-model.py‎

Lines changed: 223 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -2,176 +2,251 @@
22

33
import argparse
44
import os
5+
import sys
56
import numpy as np
67
import importlib
78
from pathlib import Path
89

910
from transformers import AutoTokenizer, AutoConfig, AutoModel
1011
import torch
1112

12-
unreleased_model_name = os.getenv('UNRELEASED_MODEL_NAME')
13-
14-
parser = argparse.ArgumentParser(description='Process model with specified path')
15-
parser.add_argument('--model-path', '-m', help='Path to the model')
16-
parser.add_argument('--prompts-file', '-p', help='Path to file containing prompts (one per line)')
17-
parser.add_argument('--use-sentence-transformers', action='store_true',
18-
help='Use SentenceTransformer to apply all numbered layers (01_Pooling, 02_Dense, 03_Dense, 04_Normalize)')
19-
args = parser.parse_args()
20-
21-
def read_prompt_from_file(file_path):
22-
try:
23-
with open(file_path, 'r', encoding='utf-8') as f:
24-
return f.read().strip()
25-
except FileNotFoundError:
26-
print(f"Error: Prompts file '{file_path}' not found")
27-
exit(1)
28-
except Exception as e:
29-
print(f"Error reading prompts file: {e}")
30-
exit(1)
31-
32-
model_path = os.environ.get('EMBEDDING_MODEL_PATH', args.model_path)
33-
if model_path is None:
34-
parser.error("Model path must be specified either via --model-path argument or EMBEDDING_MODEL_PATH environment variable")
35-
36-
# Determine if we should use SentenceTransformer
37-
use_sentence_transformers = args.use_sentence_transformers or os.environ.get('USE_SENTENCE_TRANSFORMERS', '').lower() in ('1', 'true', 'yes')
38-
39-
if use_sentence_transformers:
40-
from sentence_transformers import SentenceTransformer
41-
print("Using SentenceTransformer to apply all numbered layers")
42-
model = SentenceTransformer(model_path)
43-
tokenizer = model.tokenizer
44-
config = model[0].auto_model.config # type: ignore
45-
else:
46-
tokenizer = AutoTokenizer.from_pretrained(model_path)
47-
48-
config = AutoConfig.from_pretrained(model_path, trust_remote_code=True)
49-
50-
# This can be used to override the sliding window size for manual testing. This
51-
# can be useful to verify the sliding window attention mask in the original model
52-
# and compare it with the converted .gguf model.
53-
if hasattr(config, 'sliding_window'):
54-
original_sliding_window = config.sliding_window
55-
#original_sliding_window = 6
56-
print(f"Modified sliding window: {original_sliding_window} -> {config.sliding_window}")
57-
58-
print(f"Using unreleased model: {unreleased_model_name}")
59-
if unreleased_model_name:
60-
model_name_lower = unreleased_model_name.lower()
61-
unreleased_module_path = f"transformers.models.{model_name_lower}.modular_{model_name_lower}"
62-
class_name = f"{unreleased_model_name}Model"
63-
print(f"Importing unreleased model module: {unreleased_module_path}")
6413

14+
def parse_arguments():
15+
parser = argparse.ArgumentParser(description='Run original embedding model')
16+
parser.add_argument(
17+
'--model-path',
18+
'-m',
19+
help='Path to the model'
20+
)
21+
parser.add_argument(
22+
'--prompts-file',
23+
'-p',
24+
help='Path to file containing prompts (one per line)'
25+
)
26+
parser.add_argument(
27+
'--use-sentence-transformers',
28+
action='store_true',
29+
help=('Use SentenceTransformer to apply all numbered layers '
30+
'(01_Pooling, 02_Dense, 03_Dense, 04_Normalize)')
31+
)
32+
parser.add_argument(
33+
'--device',
34+
'-d',
35+
help='Device to use (cpu, cuda, mps, auto)',
36+
default='auto'
37+
)
38+
return parser.parse_args()
39+
40+
41+
def load_model_and_tokenizer(model_path, use_sentence_transformers=False, device="auto"):
42+
if device == "cpu":
43+
device_map = {"": "cpu"}
44+
print("Forcing CPU usage")
45+
elif device == "auto":
46+
# On Mac, "auto" device_map can cause issues with accelerate
47+
# So we detect the best device manually
48+
if torch.cuda.is_available():
49+
device_map = {"": "cuda"}
50+
print("Using CUDA")
51+
elif torch.backends.mps.is_available():
52+
device_map = {"": "mps"}
53+
print("Using MPS (Apple Metal)")
54+
else:
55+
device_map = {"": "cpu"}
56+
print("Using CPU")
57+
else:
58+
device_map = {"": device}
59+
60+
if use_sentence_transformers:
61+
from sentence_transformers import SentenceTransformer
62+
print("Using SentenceTransformer to apply all numbered layers")
63+
model = SentenceTransformer(model_path)
64+
tokenizer = model.tokenizer
65+
config = model[0].auto_model.config # type: ignore
66+
else:
67+
tokenizer = AutoTokenizer.from_pretrained(model_path)
68+
config = AutoConfig.from_pretrained(model_path, trust_remote_code=True)
69+
70+
# This can be used to override the sliding window size for manual testing. This
71+
# can be useful to verify the sliding window attention mask in the original model
72+
# and compare it with the converted .gguf model.
73+
if hasattr(config, 'sliding_window'):
74+
original_sliding_window = config.sliding_window
75+
print(f"Modified sliding window: {original_sliding_window} -> {config.sliding_window}")
76+
77+
unreleased_model_name = os.getenv('UNRELEASED_MODEL_NAME')
78+
print(f"Using unreleased model: {unreleased_model_name}")
79+
if unreleased_model_name:
80+
model_name_lower = unreleased_model_name.lower()
81+
unreleased_module_path = f"transformers.models.{model_name_lower}.modular_{model_name_lower}"
82+
class_name = f"{unreleased_model_name}Model"
83+
print(f"Importing unreleased model module: {unreleased_module_path}")
84+
85+
try:
86+
model_class = getattr(importlib.import_module(unreleased_module_path), class_name)
87+
model = model_class.from_pretrained(
88+
model_path,
89+
device_map=device_map,
90+
offload_folder="offload",
91+
trust_remote_code=True,
92+
config=config
93+
)
94+
except (ImportError, AttributeError) as e:
95+
print(f"Failed to import or load model: {e}")
96+
sys.exit(1)
97+
else:
98+
model = AutoModel.from_pretrained(
99+
model_path,
100+
device_map=device_map,
101+
offload_folder="offload",
102+
trust_remote_code=True,
103+
config=config
104+
)
105+
print(f"Model class: {type(model)}")
106+
print(f"Model file: {type(model).__module__}")
107+
108+
# Verify the model is using the correct sliding window
109+
if hasattr(model.config, 'sliding_window'): # type: ignore
110+
print(f"Model's sliding_window: {model.config.sliding_window}") # type: ignore
111+
else:
112+
print("Model config does not have sliding_window attribute")
113+
114+
return model, tokenizer, config
115+
116+
117+
def get_prompt(args):
118+
if args.prompts_file:
65119
try:
66-
model_class = getattr(importlib.import_module(unreleased_module_path), class_name)
67-
model = model_class.from_pretrained(model_path, config=config, trust_remote_code=True)
68-
except (ImportError, AttributeError) as e:
69-
print(f"Failed to import or load model: {e}")
70-
exit(1)
120+
with open(args.prompts_file, 'r', encoding='utf-8') as f:
121+
return f.read().strip()
122+
except FileNotFoundError:
123+
print(f"Error: Prompts file '{args.prompts_file}' not found")
124+
sys.exit(1)
125+
except Exception as e:
126+
print(f"Error reading prompts file: {e}")
127+
sys.exit(1)
71128
else:
72-
model = AutoModel.from_pretrained(model_path, config=config, trust_remote_code=True)
73-
print(f"Model class: {type(model)}")
74-
print(f"Model file: {type(model).__module__}")
75-
76-
# Verify the model is using the correct sliding window
77-
if not use_sentence_transformers:
78-
if hasattr(model.config, 'sliding_window'): # type: ignore
79-
print(f"Model's sliding_window: {model.config.sliding_window}") # type: ignore
129+
return "Hello world today"
130+
131+
132+
def main():
133+
args = parse_arguments()
134+
135+
model_path = os.environ.get('EMBEDDING_MODEL_PATH', args.model_path)
136+
if model_path is None:
137+
print("Error: Model path must be specified either via --model-path argument "
138+
"or EMBEDDING_MODEL_PATH environment variable")
139+
sys.exit(1)
140+
141+
# Determine if we should use SentenceTransformer
142+
use_st = (
143+
args.use_sentence_transformers or os.environ.get('USE_SENTENCE_TRANSFORMERS', '').lower() in ('1', 'true', 'yes')
144+
)
145+
146+
model, tokenizer, config = load_model_and_tokenizer(model_path, use_st, args.device)
147+
148+
# Get the device the model is on
149+
if not use_st:
150+
device = next(model.parameters()).device
80151
else:
81-
print("Model config does not have sliding_window attribute")
152+
# For SentenceTransformer, get device from the underlying model
153+
device = next(model[0].auto_model.parameters()).device # type: ignore
82154

83-
model_name = os.path.basename(model_path)
155+
model_name = os.path.basename(model_path)
84156

85-
if args.prompts_file:
86-
prompt_text = read_prompt_from_file(args.prompts_file)
157+
prompt_text = get_prompt(args)
87158
texts = [prompt_text]
88-
else:
89-
texts = ["Hello world today"]
90159

91-
with torch.no_grad():
92-
if use_sentence_transformers:
93-
embeddings = model.encode(texts, convert_to_numpy=True)
94-
all_embeddings = embeddings # Shape: [batch_size, hidden_size]
95-
96-
encoded = tokenizer(
97-
texts,
98-
padding=True,
99-
truncation=True,
100-
return_tensors="pt"
101-
)
102-
tokens = encoded['input_ids'][0]
103-
token_strings = tokenizer.convert_ids_to_tokens(tokens)
104-
for i, (token_id, token_str) in enumerate(zip(tokens, token_strings)):
105-
print(f"{token_id:6d} -> '{token_str}'")
106-
107-
print(f"Embeddings shape (after all SentenceTransformer layers): {all_embeddings.shape}")
108-
print(f"Embedding dimension: {all_embeddings.shape[1] if len(all_embeddings.shape) > 1 else all_embeddings.shape[0]}") # type: ignore
109-
else:
110-
# Standard approach: use base model output only
111-
encoded = tokenizer(
112-
texts,
113-
padding=True,
114-
truncation=True,
115-
return_tensors="pt"
116-
)
117-
118-
tokens = encoded['input_ids'][0]
119-
token_strings = tokenizer.convert_ids_to_tokens(tokens)
120-
for i, (token_id, token_str) in enumerate(zip(tokens, token_strings)):
121-
print(f"{token_id:6d} -> '{token_str}'")
122-
123-
outputs = model(**encoded)
124-
hidden_states = outputs.last_hidden_state # Shape: [batch_size, seq_len, hidden_size]
125-
126-
all_embeddings = hidden_states[0].float().cpu().numpy() # Shape: [seq_len, hidden_size]
127-
128-
print(f"Hidden states shape: {hidden_states.shape}")
129-
print(f"All embeddings shape: {all_embeddings.shape}")
130-
print(f"Embedding dimension: {all_embeddings.shape[1]}")
131-
132-
if len(all_embeddings.shape) == 1:
133-
n_embd = all_embeddings.shape[0] # type: ignore
134-
n_embd_count = 1
135-
all_embeddings = all_embeddings.reshape(1, -1)
136-
else:
137-
n_embd = all_embeddings.shape[1] # type: ignore
138-
n_embd_count = all_embeddings.shape[0] # type: ignore
160+
with torch.no_grad():
161+
if use_st:
162+
embeddings = model.encode(texts, convert_to_numpy=True)
163+
all_embeddings = embeddings # Shape: [batch_size, hidden_size]
164+
165+
encoded = tokenizer(
166+
texts,
167+
padding=True,
168+
truncation=True,
169+
return_tensors="pt"
170+
)
171+
tokens = encoded['input_ids'][0]
172+
token_strings = tokenizer.convert_ids_to_tokens(tokens)
173+
for i, (token_id, token_str) in enumerate(zip(tokens, token_strings)):
174+
print(f"{token_id:6d} -> '{token_str}'")
175+
176+
print(f"Embeddings shape (after all SentenceTransformer layers): {all_embeddings.shape}")
177+
print(f"Embedding dimension: {all_embeddings.shape[1] if len(all_embeddings.shape) > 1 else all_embeddings.shape[0]}") # type: ignore
178+
else:
179+
# Standard approach: use base model output only
180+
encoded = tokenizer(
181+
texts,
182+
padding=True,
183+
truncation=True,
184+
return_tensors="pt"
185+
)
186+
187+
tokens = encoded['input_ids'][0]
188+
token_strings = tokenizer.convert_ids_to_tokens(tokens)
189+
for i, (token_id, token_str) in enumerate(zip(tokens, token_strings)):
190+
print(f"{token_id:6d} -> '{token_str}'")
191+
192+
# Move inputs to the same device as the model
193+
encoded = {k: v.to(device) for k, v in encoded.items()}
194+
outputs = model(**encoded)
195+
hidden_states = outputs.last_hidden_state # Shape: [batch_size, seq_len, hidden_size]
196+
197+
all_embeddings = hidden_states[0].float().cpu().numpy() # Shape: [seq_len, hidden_size]
198+
199+
print(f"Hidden states shape: {hidden_states.shape}")
200+
print(f"All embeddings shape: {all_embeddings.shape}")
201+
print(f"Embedding dimension: {all_embeddings.shape[1]}")
202+
203+
if len(all_embeddings.shape) == 1:
204+
n_embd = all_embeddings.shape[0] # type: ignore
205+
n_embd_count = 1
206+
all_embeddings = all_embeddings.reshape(1, -1)
207+
else:
208+
n_embd = all_embeddings.shape[1] # type: ignore
209+
n_embd_count = all_embeddings.shape[0] # type: ignore
210+
211+
print()
212+
213+
for j in range(n_embd_count):
214+
embedding = all_embeddings[j]
215+
print(f"embedding {j}: ", end="")
139216

140-
print()
217+
# Print first 3 values
218+
for i in range(min(3, n_embd)):
219+
print(f"{embedding[i]:9.6f} ", end="")
141220

142-
for j in range(n_embd_count):
143-
embedding = all_embeddings[j]
144-
print(f"embedding {j}: ", end="")
221+
print(" ... ", end="")
145222

146-
# Print first 3 values
147-
for i in range(min(3, n_embd)):
148-
print(f"{embedding[i]:9.6f} ", end="")
223+
# Print last 3 values
224+
for i in range(n_embd - 3, n_embd):
225+
print(f"{embedding[i]:9.6f} ", end="")
149226

150-
print(" ... ", end="")
227+
print() # New line
151228

152-
# Print last 3 values
153-
for i in range(n_embd - 3, n_embd):
154-
print(f"{embedding[i]:9.6f} ", end="")
229+
print()
155230

156-
print() # New line
231+
data_dir = Path("data")
232+
data_dir.mkdir(exist_ok=True)
233+
bin_filename = data_dir / f"pytorch-{model_name}-embeddings.bin"
234+
txt_filename = data_dir / f"pytorch-{model_name}-embeddings.txt"
157235

158-
print()
236+
flattened_embeddings = all_embeddings.flatten()
237+
flattened_embeddings.astype(np.float32).tofile(bin_filename)
159238

160-
data_dir = Path("data")
161-
data_dir.mkdir(exist_ok=True)
162-
bin_filename = data_dir / f"pytorch-{model_name}-embeddings.bin"
163-
txt_filename = data_dir / f"pytorch-{model_name}-embeddings.txt"
239+
with open(txt_filename, "w") as f:
240+
idx = 0
241+
for j in range(n_embd_count):
242+
for value in all_embeddings[j]:
243+
f.write(f"{idx}: {value:.6f}\n")
244+
idx += 1
245+
print(f"Total values: {len(flattened_embeddings)} ({n_embd_count} embeddings × {n_embd} dimensions)")
246+
print("")
247+
print(f"Saved bin embeddings to: {bin_filename}")
248+
print(f"Saved txt embeddings to: {txt_filename}")
164249

165-
flattened_embeddings = all_embeddings.flatten()
166-
flattened_embeddings.astype(np.float32).tofile(bin_filename)
167250

168-
with open(txt_filename, "w") as f:
169-
idx = 0
170-
for j in range(n_embd_count):
171-
for value in all_embeddings[j]:
172-
f.write(f"{idx}: {value:.6f}\n")
173-
idx += 1
174-
print(f"Total values: {len(flattened_embeddings)} ({n_embd_count} embeddings × {n_embd} dimensions)")
175-
print("")
176-
print(f"Saved bin embeddings to: {bin_filename}")
177-
print(f"Saved txt embeddings to: {txt_filename}")
251+
if __name__ == "__main__":
252+
main()

0 commit comments

Comments
 (0)