char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
c8ac02fa1
char/aliceai-t5-moe
1#!/usr/bin/env python3 2 3import numpy as np 4import argparse 5import os 6import importlib 7from pathlib import Path 8 9from transformers import AutoTokenizer ,AutoConfig ,AutoModelForCausalLM ,AutoModel 10from common import compare_tokens ,exit_with_warning # type: ignore[import-not-found, ty:unresolved-import] 11 12unreleased_model_name = os .getenv ('UNRELEASED_MODEL_NAME' ) 13 14def cosine_similarity (a ,b = None ): 15a = np .asarray (a ) 16if b is None : 17b = a 18else : 19b = np .asarray (b ) 20 21if a .ndim == 1 : 22a = a .reshape (1 ,- 1 ) 23if b .ndim == 1 : 24b = b .reshape (1 ,- 1 ) 25 26a_norms = np .linalg .norm (a ,axis = 1 ,keepdims = True ) 27b_norms = np .linalg .norm (b ,axis = 1 ,keepdims = True ) 28 29a_norms = np .where (a_norms == 0 ,1e-8 ,a_norms ) 30b_norms = np .where (b_norms == 0 ,1e-8 ,b_norms ) 31 32a_normalized = a / a_norms 33b_normalized = b / b_norms 34 35# Compute cosine similarity 36return np .dot (a_normalized ,b_normalized .T ) 37 38def load_embeddings_from_file (filename ,n_tokens ,n_embd ): 39embeddings = np .fromfile (filename ,dtype = np .float32 ) 40# Check if this is pooled (single embedding) or per-token embeddings 41if len (embeddings )== n_embd : 42return embeddings .reshape (1 ,n_embd ) 43else : 44return embeddings .reshape (n_tokens ,n_embd ) 45 46def test_single_prompt_similarity (python_emb ,cpp_emb ,tokens ,prompt ): 47np .set_printoptions (suppress = True ,precision = 6 ) 48"pytorch embeddings:" ); 49python_emb ) 50"llama.cpp embeddings:" ); 51cpp_emb ) 52f"\n=== Prompt: ' { prompt } ' ===" ) 53f"Tokens: { tokens } " ) 54f"Embeddings shape: Python { python_emb . shape } , llama.cpp { cpp_emb . shape } " ) 55 56n_tokens = len (tokens ) 57is_pooled = python_emb .shape [0 ]== 1 58 59if is_pooled : 60f"\n[Pooled Embeddings Mode - comparing single sentence embeddings]" ) 61 62# 1. Direct embedding comparison for pooled embeddings 63f"\n1. Raw Embedding Magnitude Comparison:" ) 64py_mag = np .linalg .norm (python_emb [0 ]) 65cpp_mag = np .linalg .norm (cpp_emb [0 ]) 66ratio = py_mag / cpp_mag if cpp_mag > 0 else float ('inf' ) 67f" Pooled embedding: Python= { py_mag :.3f } , llama.cpp= { cpp_mag :.3f } , ratio= { ratio :.3f } " ) 68 69# 2. Cross-model similarity for pooled embeddings 70f"\n2. Cross-Model Pooled Embedding Similarity:" ) 71sim = cosine_similarity ([python_emb [0 ]], [cpp_emb [0 ]])[0 ][0 ] 72f" Cosine similarity: { sim :.6f } " ) 73 74return { 75'cross_model_similarities' : [sim ], 76'similarity_matrix_diff' :np .array ([[0.0 ]]), 77'max_diff' :0.0 , 78'mean_diff' :0.0 , 79'rms_diff' :0.0 80 } 81else : 82# Original per-token comparison logic 83# 1. Direct embedding comparison 84f"\n1. Raw Embedding Magnitude Comparison:" ) 85# Check if the distance of each token embedding from the origin and compare 86# if the vectors are on the same "sphere". This does not tell us about 87# direction (meaning of the token embedding), just magnitude. 88for i in range (n_tokens ): 89py_mag = np .linalg .norm (python_emb [i ])# calculate standard euclidean norm for Python embeddings 90cpp_mag = np .linalg .norm (cpp_emb [i ])# calculate standard euclidean norm for llama.cpp embeddings 91ratio = py_mag / cpp_mag if cpp_mag > 0 else float ('inf' ) 92f" Token { i } ( { tokens [ i ] } ): Python= { py_mag :.3f } , llama.cpp= { cpp_mag :.3f } , ratio= { ratio :.3f } " ) 93 94# 2. Cosine similarity between tokens within each model 95# Here we check the direction of token embeddings to see if the have the 96# same meaning (similarity). This is done by calculating cosine similarity 97# of a pair of token embeddings within each model. 98f"\n2. Within-Model Token Similarities:" ) 99" Python model:" ) 100for i in range (n_tokens ): 101for j in range (i + 1 ,n_tokens ): 102sim = cosine_similarity ([python_emb [i ]], [python_emb [j ]])[0 ][0 ] 103f" { tokens [ i ] } ā { tokens [ j ] } : { sim :.4f } " ) 104 105" llama.cpp model:" ) 106for i in range (n_tokens ): 107for j in range (i + 1 ,n_tokens ): 108sim = cosine_similarity ([cpp_emb [i ]], [cpp_emb [j ]])[0 ][0 ] 109f" { tokens [ i ] } ā { tokens [ j ] } : { sim :.4f } " ) 110 111# 3. Cross-model similarity (same token position) 112f"\n3. Cross-Model Same-Token Similarities:" ) 113for i in range (n_tokens ): 114sim = cosine_similarity ([python_emb [i ]], [cpp_emb [i ]])[0 ][0 ] 115f" Token { i } ( { tokens [ i ] } ): { sim :.4f } " ) 116 117# 4. Similarity matrix comparison 118f"\n4. Similarity Matrix Differences:" ) 119py_sim_matrix = cosine_similarity (python_emb ) 120cpp_sim_matrix = cosine_similarity (cpp_emb ) 121diff_matrix = np .abs (py_sim_matrix - cpp_sim_matrix ) 122 123f" Max difference: { np . max ( diff_matrix ):.4f } " ) 124f" Mean difference: { np . mean ( diff_matrix ):.4f } " ) 125f" RMS difference: { np . sqrt ( np . mean ( diff_matrix ** 2 )):.4f } " ) 126 127return { 128'cross_model_similarities' : [cosine_similarity ([python_emb [i ]], [cpp_emb [i ]])[0 ][0 ]for i in range (n_tokens )], 129'similarity_matrix_diff' :diff_matrix , 130'max_diff' :np .max (diff_matrix ), 131'mean_diff' :np .mean (diff_matrix ), 132'rms_diff' :np .sqrt (np .mean (diff_matrix ** 2 )) 133 } 134 135def read_prompt_from_file (file_path ): 136try : 137with open (file_path ,'r' ,encoding = 'utf-8' )as f : 138return f .read ().strip () 139except FileNotFoundError : 140f"Error: Prompts file ' { file_path } ' not found" ) 141exit (1 ) 142except Exception as e : 143f"Error reading prompts file: { e } " ) 144exit (1 ) 145 146def main (): 147parser = argparse .ArgumentParser (description = 'Test semantic similarity between Python and llama.cpp embeddings' ) 148parser .add_argument ('--model-path' ,'-m' ,required = True ,help = 'Path to the original Python model' ) 149parser .add_argument ('--python-embeddings' ,'-pe' ,help = 'Path to pytorch embeddings "logits" binary file' ) 150parser .add_argument ('--cpp-embeddings' ,'-ce' ,help = 'Path to llama.cpp embeddings "logits" binary file' ) 151parser .add_argument ('--causal' ,'-c' ,default = False ,help = 'if the model is causal (default: false)' ,action = 'store_true' ) 152parser .add_argument ('--prompt' ,'-p' ,default = 'Hello world today' ,help = 'Test prompt' ) 153parser .add_argument ('--prompts-file' ,'-pf' ,help = 'Path to file containing prompts' ) 154 155args = parser .parse_args () 156 157if args .prompts_file : 158prompt = read_prompt_from_file (args .prompts_file ) 159else : 160prompt = args .prompt 161 162python_emb_path = Path (args .python_embeddings ) 163cpp_emb_path = Path (args .cpp_embeddings ) 164 165# Extract base names (e.g., "pytorch-model-name-embeddings.bin" -> "pytorch-model-name") 166python_model_name = python_emb_path .stem .replace ("-embeddings" ,"" ) 167cpp_model_name = cpp_emb_path .stem .replace ("-embeddings" ,"" ) 168 169"Semantic Similarity Test Between Python and llama.cpp Embedding Models" ) 170"=" * 70 ) 171 172# First verify tokens match before comparing embeddings 173"\nš Token Comparison Check" ) 174"=" * 70 ) 175data_dir = python_emb_path .parent 176if not compare_tokens (python_model_name ,cpp_model_name ,type_suffix = "-embeddings" ,output_dir = str (data_dir )): 177exit_with_warning ("\nā Token mismatch detected" ,args .model_path ) 178# Single prompt detailed comparison 181f"\nTesting with prompt: ' { prompt } '" ) 182 183# Load the python model to get configuration information and also to load the tokenizer. 184"Loading model and tokenizer using AutoTokenizer:" ,args .model_path ) 185tokenizer = AutoTokenizer .from_pretrained (args .model_path ) 186config = AutoConfig .from_pretrained (args .model_path ,trust_remote_code = True ) 187 188if unreleased_model_name : 189model_name_lower = unreleased_model_name .lower () 190unreleased_module_path = f"transformers.models. { model_name_lower } .modular_ { model_name_lower } " 191if args .causal : 192class_name = f" { unreleased_model_name } ForCausalLM" 193else : 194class_name = f" { unreleased_model_name } Model" 195f"Model class: { class_name } " ) 196f"Importing unreleased model module: { unreleased_module_path } " ) 197 198try : 199model_class = getattr (importlib .import_module (unreleased_module_path ),class_name ) 200model = model_class .from_pretrained (args .model_path ) 201except (ImportError ,AttributeError )as e : 202f"Failed to import or load model: { e } " ) 203exit (1 ) 204else : 205if args .causal : 206model = AutoModelForCausalLM .from_pretrained (args .model_path ,trust_remote_code = True ) 207else : 208model = AutoModel .from_pretrained (args .model_path ,trust_remote_code = True ) 209 210encoded = tokenizer (prompt ,return_tensors = "pt" )# ty: ignore[call-non-callable] 211tokens = tokenizer .convert_ids_to_tokens (encoded ['input_ids' ][0 ])# ty: ignore[unresolved-attribute] 212n_tokens = len (tokens ) 213f"n_tokens: { n_tokens } " ); 214f"hidden_size: { model . config . hidden_size } " ) 215 216# Load binary embeddings from data directory. 217llamacpp_embeddings = load_embeddings_from_file (args .cpp_embeddings ,n_tokens ,model .config .hidden_size ) 218python_embeddings = load_embeddings_from_file (args .python_embeddings ,n_tokens ,model .config .hidden_size ) 219 220# Run comparison 221results = test_single_prompt_similarity (python_embeddings ,llamacpp_embeddings ,tokens ,prompt ) 222 223# Summary 224f"\n=== SUMMARY ===" ) 225avg_cross_sim = np .mean (results ['cross_model_similarities' ]) 226f"Average cross-model similarity: { avg_cross_sim :.4f } " ) 227f"Similarity matrix RMS difference: { results [ 'rms_diff' ]:.4f } " ) 228 229# Quality assessment 230if avg_cross_sim > 0.95 : 231"ā EXCELLENT: Models are highly similar" ) 232elif avg_cross_sim > 0.90 : 233"ā VERY GOOD: Models are very similar" ) 234elif avg_cross_sim > 0.80 : 235"ā ļø GOOD: Models are reasonably similar" ) 236elif avg_cross_sim > 0.70 : 237"ā ļø FAIR: Models have some differences" ) 238else : 239exit_with_warning ("ā POOR: Models are significantly different" ,args .model_path ) 240 241if __name__ == "__main__" : 242main ()