semantic_check.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #!/usr/bin/env python3
  2. import numpy as np
  3. import argparse
  4. import os
  5. import importlib
  6. from transformers import AutoTokenizer, AutoConfig, AutoModelForCausalLM, AutoModel
  7. unreleased_model_name = os.getenv('UNRELEASED_MODEL_NAME')
  8. def cosine_similarity(a, b=None):
  9. a = np.asarray(a)
  10. if b is None:
  11. b = a
  12. else:
  13. b = np.asarray(b)
  14. if a.ndim == 1:
  15. a = a.reshape(1, -1)
  16. if b.ndim == 1:
  17. b = b.reshape(1, -1)
  18. a_norms = np.linalg.norm(a, axis=1, keepdims=True)
  19. b_norms = np.linalg.norm(b, axis=1, keepdims=True)
  20. a_norms = np.where(a_norms == 0, 1e-8, a_norms)
  21. b_norms = np.where(b_norms == 0, 1e-8, b_norms)
  22. a_normalized = a / a_norms
  23. b_normalized = b / b_norms
  24. # Compute cosine similarity
  25. return np.dot(a_normalized, b_normalized.T)
  26. def load_embeddings_from_file(filename, n_tokens, n_embd):
  27. embeddings = np.fromfile(filename, dtype=np.float32)
  28. return embeddings.reshape(n_tokens, n_embd)
  29. def test_single_prompt_similarity(python_emb, cpp_emb, tokens, prompt):
  30. np.set_printoptions(suppress=True, precision=6)
  31. print("pytorch embeddings:");
  32. print(python_emb)
  33. print("llama.cpp embeddings:");
  34. print(cpp_emb)
  35. print(f"\n=== Prompt: '{prompt}' ===")
  36. print(f"Tokens: {tokens}")
  37. print(f"Embeddings shape: Python {python_emb.shape}, llama.cpp {cpp_emb.shape}")
  38. n_tokens = len(tokens)
  39. # 1. Direct embedding comparison
  40. print(f"\n1. Raw Embedding Magnitude Comparison:")
  41. # Check if the distance of each token embedding from the origin and compare
  42. # if the vectors are on the same "sphere". This does not tell us about
  43. # direction (meaning of the token embedding), just magnitude.
  44. for i in range(n_tokens):
  45. py_mag = np.linalg.norm(python_emb[i]) # calculate standard euclidean norm for Python embeddings
  46. cpp_mag = np.linalg.norm(cpp_emb[i]) # calculate standard euclidean norm for llama.cpp embeddings
  47. ratio = py_mag / cpp_mag if cpp_mag > 0 else float('inf')
  48. print(f" Token {i} ({tokens[i]}): Python={py_mag:.3f}, llama.cpp={cpp_mag:.3f}, ratio={ratio:.3f}")
  49. # 2. Cosine similarity between tokens within each model
  50. # Here we check the direction of token embeddings to see if the have the
  51. # same meaning (similarity). This is done by calculating cosine similarity
  52. # of a pair of token embeddings within each model.
  53. print(f"\n2. Within-Model Token Similarities:")
  54. print(" Python model:")
  55. for i in range(n_tokens):
  56. for j in range(i+1, n_tokens):
  57. sim = cosine_similarity([python_emb[i]], [python_emb[j]])[0][0]
  58. print(f" {tokens[i]} ↔ {tokens[j]}: {sim:.4f}")
  59. print(" llama.cpp model:")
  60. for i in range(n_tokens):
  61. for j in range(i+1, n_tokens):
  62. sim = cosine_similarity([cpp_emb[i]], [cpp_emb[j]])[0][0]
  63. print(f" {tokens[i]} ↔ {tokens[j]}: {sim:.4f}")
  64. # 3. Cross-model similarity (same token position)
  65. print(f"\n3. Cross-Model Same-Token Similarities:")
  66. for i in range(n_tokens):
  67. sim = cosine_similarity([python_emb[i]], [cpp_emb[i]])[0][0]
  68. print(f" Token {i} ({tokens[i]}): {sim:.4f}")
  69. # 4. Similarity matrix comparison
  70. print(f"\n4. Similarity Matrix Differences:")
  71. py_sim_matrix = cosine_similarity(python_emb)
  72. cpp_sim_matrix = cosine_similarity(cpp_emb)
  73. diff_matrix = np.abs(py_sim_matrix - cpp_sim_matrix)
  74. print(f" Max difference: {np.max(diff_matrix):.4f}")
  75. print(f" Mean difference: {np.mean(diff_matrix):.4f}")
  76. print(f" RMS difference: {np.sqrt(np.mean(diff_matrix**2)):.4f}")
  77. return {
  78. 'cross_model_similarities': [cosine_similarity([python_emb[i]], [cpp_emb[i]])[0][0] for i in range(n_tokens)],
  79. 'similarity_matrix_diff': diff_matrix,
  80. 'max_diff': np.max(diff_matrix),
  81. 'mean_diff': np.mean(diff_matrix),
  82. 'rms_diff': np.sqrt(np.mean(diff_matrix**2))
  83. }
  84. def read_prompt_from_file(file_path):
  85. try:
  86. with open(file_path, 'r', encoding='utf-8') as f:
  87. return f.read().strip()
  88. except FileNotFoundError:
  89. print(f"Error: Prompts file '{file_path}' not found")
  90. exit(1)
  91. except Exception as e:
  92. print(f"Error reading prompts file: {e}")
  93. exit(1)
  94. def main():
  95. parser = argparse.ArgumentParser(description='Test semantic similarity between Python and llama.cpp embeddings')
  96. parser.add_argument('--model-path', '-m', required=True, help='Path to the original Python model')
  97. parser.add_argument('--python-embeddings', '-pe', help='Path to pytorch embeddings "logits" binary file')
  98. parser.add_argument('--cpp-embeddings', '-ce', help='Path to llama.cpp embeddings "logits" binary file')
  99. parser.add_argument('--causal', '-c', default=False, help='if the model is causal (default: false)', action='store_true')
  100. parser.add_argument('--prompt', '-p', default='Hello world today', help='Test prompt')
  101. parser.add_argument('--prompts-file', '-pf', help='Path to file containing prompts')
  102. args = parser.parse_args()
  103. if args.prompts_file:
  104. prompt = read_prompt_from_file(args.prompts_file)
  105. else:
  106. prompt = args.prompt
  107. print("Semantic Similarity Test Between Python and llama.cpp Embedding Models")
  108. print("=" * 70)
  109. # Single prompt detailed comparison
  110. print(f"\nTesting with prompt: '{prompt}'")
  111. # Load the python model to get configuration information and also to load the tokenizer.
  112. print("Loading model and tokenizer using AutoTokenizer:", args.model_path)
  113. tokenizer = AutoTokenizer.from_pretrained(args.model_path)
  114. config = AutoConfig.from_pretrained(args.model_path)
  115. if unreleased_model_name:
  116. model_name_lower = unreleased_model_name.lower()
  117. unreleased_module_path = f"transformers.models.{model_name_lower}.modular_{model_name_lower}"
  118. if args.causal:
  119. class_name = f"{unreleased_model_name}ForCausalLM"
  120. else:
  121. class_name = f"{unreleased_model_name}Model"
  122. print(f"Model class: {class_name}")
  123. print(f"Importing unreleased model module: {unreleased_module_path}")
  124. try:
  125. model_class = getattr(importlib.import_module(unreleased_module_path), class_name)
  126. model = model_class.from_pretrained(args.model_path)
  127. except (ImportError, AttributeError) as e:
  128. print(f"Failed to import or load model: {e}")
  129. exit(1)
  130. else:
  131. if args.causal:
  132. model = AutoModelForCausalLM.from_pretrained(args.model_path)
  133. else:
  134. model = AutoModel.from_pretrained(args.model_path)
  135. encoded = tokenizer(prompt, return_tensors="pt")
  136. tokens = tokenizer.convert_ids_to_tokens(encoded['input_ids'][0])
  137. n_tokens = len(tokens)
  138. print(f"n_tokens: {n_tokens}");
  139. print(f"hidden_size: {model.config.hidden_size}")
  140. # Load binary embeddings from data directory.
  141. llamacpp_embeddings = load_embeddings_from_file(args.cpp_embeddings, n_tokens, model.config.hidden_size)
  142. python_embeddings = load_embeddings_from_file(args.python_embeddings, n_tokens, model.config.hidden_size)
  143. # Run comparison
  144. results = test_single_prompt_similarity(python_embeddings, llamacpp_embeddings, tokens, prompt)
  145. # Summary
  146. print(f"\n=== SUMMARY ===")
  147. avg_cross_sim = np.mean(results['cross_model_similarities'])
  148. print(f"Average cross-model similarity: {avg_cross_sim:.4f}")
  149. print(f"Similarity matrix RMS difference: {results['rms_diff']:.4f}")
  150. # Quality assessment
  151. if avg_cross_sim > 0.95:
  152. print("✅ EXCELLENT: Models are highly similar")
  153. elif avg_cross_sim > 0.90:
  154. print("✅ VERY GOOD: Models are very similar")
  155. elif avg_cross_sim > 0.80:
  156. print("⚠️ GOOD: Models are reasonably similar")
  157. elif avg_cross_sim > 0.70:
  158. print("⚠️ FAIR: Models have some differences")
  159. else:
  160. print("❌ POOR: Models are significantly different")
  161. if __name__ == "__main__":
  162. main()