|
import gradio as gr |
|
import os |
|
import numpy as np |
|
import ujson as json |
|
|
|
from loading import load_data, save_git |
|
from tools import compute_ordered_matrix |
|
from plotting import plot_sim_matrix_fig, plot_umap_fig, plot_tree, update_sim_matrix_fig, update_umap_fig, update_tree_fig |
|
from llm_run import download_llm_to_cache, load_model, llm_run |
|
|
|
def reload_figures(): |
|
global MODEL_SEARCHED_X, MODEL_SEARCHED_Y, ALPHA_EDGES, ALPHA_NAMES, ALPHA_MARKERS, FIGS, ORDERED_MODEL_NAMES |
|
fig1 = update_sim_matrix_fig(FIGS['fig1'], ORDERED_MODEL_NAMES, model_search_x=MODEL_SEARCHED_X, model_search_y=MODEL_SEARCHED_Y) |
|
fig2 = update_umap_fig(FIGS['fig2'], DIST_MATRIX, MODEL_NAMES, FAMILIES, COLORS, model_search_x=MODEL_SEARCHED_X, alpha_edges=ALPHA_EDGES['fig2'], alpha_names=ALPHA_NAMES['fig2'], alpha_markers=ALPHA_MARKERS['fig2']) |
|
fig4 = update_tree_fig(FIGS['fig4'], MODEL_NAMES, model_search=MODEL_SEARCHED_X, alpha_edges=ALPHA_EDGES['fig4'], alpha_names=ALPHA_NAMES['fig4'], alpha_markers=ALPHA_MARKERS['fig4']) |
|
return [fig1,fig2,fig4] |
|
|
|
def search_bar_changeX(value): |
|
global MODEL_SEARCHED_X |
|
MODEL_SEARCHED_X = value |
|
return reload_figures() |
|
|
|
def search_bar_changeY(value): |
|
global MODEL_SEARCHED_Y |
|
MODEL_SEARCHED_Y = value |
|
return reload_figures() |
|
|
|
def slider_changeAlphaMarkers(value,key): |
|
global ALPHA_MARKERS |
|
ALPHA_MARKERS[key] = value |
|
return reload_figures() |
|
|
|
def slider_changeAlphaNames(value,key): |
|
global ALPHA_NAMES |
|
ALPHA_NAMES[key] = value |
|
return reload_figures() |
|
|
|
def slider_changeAlphaEdges(value,key): |
|
global ALPHA_EDGES |
|
ALPHA_EDGES[key] = value |
|
return reload_figures() |
|
|
|
def search_bar_gr(model_names,slider=True,double_search=False,key=None): |
|
global MODEL_SEARCHED_X,MODEL_SEARCHED_Y,ALPHA_EDGES,ALPHA_NAMES, ALPHA_MARKERS |
|
|
|
ret = [] |
|
with gr.Column(scale=1) as col1: |
|
with gr.Group(): |
|
if MODEL_SEARCHED_X is None: |
|
index = 0 |
|
else: |
|
index = model_names.index(MODEL_SEARCHED_X) |
|
ms_x = gr.Dropdown(label='Search'+(' X' if double_search else ''),choices=model_names,value=model_names[index],key='model_search_x_'+key,interactive=True) |
|
|
|
ret.append(ms_x) |
|
if double_search: |
|
if MODEL_SEARCHED_Y is None: |
|
index = 0 |
|
else: |
|
index = model_names.index(MODEL_SEARCHED_Y) |
|
ms_y = gr.Dropdown(label='Search Y',choices=model_names,value=model_names[index],key='model_search_y_'+key,interactive=True) |
|
ret.append(ms_y) |
|
if slider: |
|
with gr.Group(): |
|
values = np.arange(0, 1.05,0.05) |
|
|
|
values = np.round(values,2) |
|
alpha_edges = gr.Slider(label='Alpha Edges', |
|
minimum=0, |
|
maximum=1, |
|
step=0.05, |
|
value=ALPHA_EDGES[key], |
|
key='alpha_edges_'+key, |
|
interactive=True) |
|
|
|
values = np.arange(0, 1.05,0.05) |
|
|
|
values = np.round(values,2) |
|
alpha_names = gr.Slider(label='Alpha Names', |
|
minimum=0, |
|
maximum=1, |
|
step=0.05, |
|
value=ALPHA_NAMES[key], |
|
key='alpha_names_'+key, |
|
interactive=True) |
|
|
|
values = np.arange(0, 1.05,0.05) |
|
|
|
values = np.round(values,2) |
|
alpha_markers = gr.Slider(label='Alpha Markers', |
|
minimum=0, |
|
maximum=1, |
|
step=0.05, |
|
value=ALPHA_MARKERS[key], |
|
key='alpha_markers_'+key, |
|
interactive=True) |
|
ret.append(alpha_edges) |
|
ret.append(alpha_names) |
|
ret.append(alpha_markers) |
|
col2 = gr.Column(scale=5) |
|
ret.insert(0,col2) |
|
return ret |
|
|
|
import spaces |
|
@spaces.GPU(duration=300) |
|
def _run(path,genes,N,progress_bar): |
|
|
|
progress_bar(0.20, desc="Loading Model...",total=100) |
|
try: |
|
model,tokenizer = load_model(path) |
|
except ValueError as e: |
|
print(f"Error loading model '{path}': {e}") |
|
gr.Warning("Model couldn't load. This space currently only works with AutoModelForCausalLM models. Please check the model architecture and try again.") |
|
return None |
|
except OSError as e: |
|
print(f"Error loading model '{path}': {e}") |
|
gr.Warning("Model doesn't seem to exist on the HuggingFace Hub. Please check the model name and try again.") |
|
return None |
|
except RuntimeError as e: |
|
if 'out of memory' in str(e): |
|
print(f"Error loading model '{path}': {e}") |
|
gr.Warning("Loading the model triggered an out of memory error. It may be too big for the GPU (80Go RAM). Please try again with a smaller model.") |
|
return None |
|
else: |
|
print(f"Error loading model '{path}': {e}") |
|
gr.Warning("Model couldn't be loaded. Please check the logs or report an issue.") |
|
return None |
|
except Exception as e: |
|
print(f"Error loading model '{path}': {e}") |
|
gr.Warning("Model couldn't be loaded. Please check logs or report an issue.") |
|
return None |
|
progress_bar(0.25, desc="Generating data...",total=100) |
|
for i,output in enumerate(llm_run(model,tokenizer,genes,N)): |
|
progress_bar(0.25 + i*(70/len(genes))/100, desc=f"Generating data... {i+1}/{len(genes)}",total=100) |
|
return output |
|
|
|
def run(path,progress_bar): |
|
global DEFAULT_FAMILY_NAME, PHYLOLM_N |
|
family = DEFAULT_FAMILY_NAME |
|
N = PHYLOLM_N |
|
|
|
progress_bar(0, desc="Downloading model...",total=100) |
|
try: |
|
|
|
if download_llm_to_cache(path) is None: |
|
gr.Warning("Model not found on Hugging Face Hub. Please check the model name and try again.") |
|
return None |
|
except OSError as e: |
|
print(f"Error downloading model: {e}") |
|
gr.Warning("Model not found on Hugging Face Hub. Please check the model name and try again.") |
|
return None |
|
|
|
|
|
progress_bar(0.10, desc="Loading contexts...",total=100) |
|
|
|
with open('inputs/math.json', 'r') as f: |
|
genes = json.load(f) |
|
|
|
|
|
progress_bar(0.15, desc="Waiting for GPU...",total=100) |
|
|
|
try: |
|
output = _run(path,genes,N,progress_bar) |
|
if output is None: |
|
return None |
|
except Exception as e: |
|
print(f"Error running model: {e}") |
|
gr.Warning("Something unexpected happened during the run or the loading of the model. Please check the logs or report an issue.") |
|
return None |
|
|
|
progress_bar(0.95, desc="Saving data ...",total=100) |
|
|
|
alleles = [[compl[j]['generated_text'][len(gene):][:4] for j in range(len(compl))] for gene,compl in zip(genes,output)] |
|
save_git(alleles,genes,path,family) |
|
|
|
progress_bar(1, desc="Done!",total=100) |
|
|
|
|
|
def prepare_run(model_name,progress_bar=gr.Progress()): |
|
global MODEL_SEARCHED_X,MODEL_NAMES |
|
if model_name in MODEL_NAMES: |
|
gr.Warning('Model already exists in the database.') |
|
MODEL_SEARCHED_X = model_name |
|
reload_figures() |
|
return |
|
run(model_name,progress_bar) |
|
|
|
def reload_env(): |
|
global SIM_MAT_SEARCH_X, SIM_MAT_SEARCH_Y, VIZ_SEARCH, TREE_SEARCH |
|
global MODEL_NAMES, FAMILIES, COLORS, SIM_MATRIX, DIST_MATRIX |
|
global FIGS, FIGS_OBJECTS |
|
|
|
|
|
data, model_names, families, sim_matrix, colors = load_data() |
|
|
|
sim_matrix_safe = np.where(sim_matrix == 0, np.finfo(np.float64).eps, sim_matrix) |
|
dist_matrix = -np.log(sim_matrix_safe) |
|
|
|
|
|
MODEL_NAMES = model_names |
|
FAMILIES = families |
|
COLORS = colors |
|
SIM_MATRIX = sim_matrix |
|
DIST_MATRIX = dist_matrix |
|
|
|
|
|
ordered_sim_matrix, ordered_model_names = compute_ordered_matrix(sim_matrix,dist_matrix, model_names) |
|
ORDERED_MODEL_NAMES = ordered_model_names |
|
FIGS['fig1'] = plot_sim_matrix_fig(ordered_sim_matrix, ordered_model_names, families, colors) |
|
FIGS['fig2'] = plot_umap_fig(dist_matrix, sim_matrix, model_names, families, colors, |
|
alpha_edges=ALPHA_EDGES['fig2'],alpha_names=ALPHA_NAMES['fig2'],alpha_markers=ALPHA_MARKERS['fig2']) |
|
FIGS['fig4'] = plot_tree(sim_matrix, model_names, families, colors,alpha_edges=ALPHA_EDGES['fig4'],alpha_names=ALPHA_NAMES['fig4'],alpha_markers=ALPHA_MARKERS['fig4']) |
|
|
|
|
|
sim_mat_search_x = gr.Dropdown(label='Search X',choices=model_names,value=model_names[0],key='model_search_x_fig1',interactive=True) |
|
sim_mat_search_y = gr.Dropdown(label='Search Y',choices=model_names,value=model_names[0],key='model_search_y_fig1',interactive=True) |
|
viz_search = gr.Dropdown(label='Search',choices=model_names,value=model_names[0],key='model_search_fig2',interactive=True) |
|
tree_search = gr.Dropdown(label='Search',choices=model_names,value=model_names[0],key='model_search_fig4',interactive=True) |
|
|
|
return FIGS['fig1'], FIGS['fig2'], FIGS['fig4'], sim_mat_search_x, sim_mat_search_y, viz_search, tree_search |
|
|
|
|
|
|
|
|
|
|
|
USERNAME = os.environ['GITHUB_USERNAME'] |
|
TOKEN = os.environ['GITHUB_TOKEN'] |
|
MAIL = os.environ['GITHUB_MAIL'] |
|
|
|
MODEL_SEARCHED_X = None |
|
MODEL_SEARCHED_Y = None |
|
ALPHA_EDGES = {'fig2':0.05, 'fig3':0.05,'fig4':1.0} |
|
ALPHA_NAMES = {'fig2':0.0, 'fig3':0.0,'fig4':0.0} |
|
ALPHA_MARKERS = {'fig2':0.8, 'fig3':0.8,'fig4':1.0} |
|
|
|
FIGS = {'fig1':None,'fig2':None,'fig3':None,'fig4':None} |
|
FIGS_OBJECTS = [None,None,None] |
|
MODEL_NAMES = None |
|
FAMILIES = None |
|
COLORS = None |
|
ORDERED_MODEL_NAMES = None |
|
SIM_MATRIX = None |
|
DIST_MATRIX = None |
|
|
|
DEFAULT_FAMILY_NAME = '?' |
|
PHYLOLM_N = 32 |
|
|
|
SIM_MAT_SEARCH_X = None |
|
SIM_MAT_SEARCH_Y = None |
|
VIZ_SEARCH = None |
|
TREE_SEARCH = None |
|
|
|
|
|
with gr.Blocks(title="PhyloLM", theme=gr.themes.Default()) as demo: |
|
gr.Markdown("# PhyloLM: Phylogenetic Mapping of Language Models") |
|
|
|
gr.Markdown( |
|
"Welcome to PhyloLM ([paper](https://arxiv.org/abs/2404.04671) - [code](https://github.com/Nicolas-Yax/PhyloLM)) — a tool for comparing language models based on their **behavioral similarity**, inspired by methods from comparative genomics. " |
|
"Instead of architecture or weights, we use output behavior on diagnostic prompts as a behavioral fingerprint to compute a distance metric, akin to how biologists compare species using genetic data. This makes it possible to draw a unique map of all LLMs (various architectures, gated and non gated, ...)." |
|
"The goal of this space is to create a collaborative space where everyone can visualize these maps and extend them with models of their choice. " |
|
) |
|
|
|
gr.Markdown("## Explore Maps of Models") |
|
|
|
gr.Markdown( |
|
"This interactive space allows users to explore model similarities through four types of visualizations:\n" |
|
"- A similarity matrix (values range from 0 = dissimilar to 1 = highly similar). \n" |
|
"- 2D and 3D scatter plots representing how close or far from each other LLMs are (plotted using UMAP). \n" |
|
"- A tree to visualize distances between models (distance from leaf A to leaf B in the tree is similar to the distance between the two models)\n\n" |
|
) |
|
|
|
|
|
data, model_names, families, sim_matrix, colors = load_data() |
|
|
|
sim_matrix_safe = np.where(sim_matrix == 0, np.finfo(np.float64).eps, sim_matrix) |
|
dist_matrix = -np.log(sim_matrix_safe) |
|
|
|
|
|
MODEL_NAMES = model_names |
|
FAMILIES = families |
|
COLORS = colors |
|
SIM_MATRIX = sim_matrix |
|
DIST_MATRIX = dist_matrix |
|
|
|
|
|
tab_state = gr.State(value="Similarity Matrix") |
|
tabs = gr.Tabs(["Similarity Matrix", "2D Visualization","Tree Visualization"]) |
|
with tabs: |
|
with gr.TabItem("Similarity Matrix"): |
|
|
|
with gr.Row(): |
|
col2,sim_mat_search_x,sim_mat_search_y = search_bar_gr(model_names,slider=False,double_search=True,key='fig1') |
|
with col2: |
|
ordered_sim_matrix, ordered_model_names = compute_ordered_matrix(sim_matrix,dist_matrix, model_names) |
|
fig = plot_sim_matrix_fig(ordered_sim_matrix, ordered_model_names, families, colors) |
|
sim_matrix_output = gr.Plot(fig,label="Similarity Matrix") |
|
FIGS['fig1'] = fig |
|
ORDERED_MODEL_NAMES = ordered_model_names |
|
FIGS_OBJECTS[0] = sim_matrix_output |
|
with gr.TabItem("2D Visualization"): |
|
|
|
with gr.Row(): |
|
col2,viz_search,viz_alpha_edge,viz_alpha_name,viz_alpha_marker = search_bar_gr(model_names,slider=True,double_search=False,key='fig2') |
|
with col2: |
|
fig = plot_umap_fig(dist_matrix, sim_matrix, model_names, families, colors, |
|
alpha_edges=ALPHA_EDGES['fig2'],alpha_names=ALPHA_NAMES['fig2'],alpha_markers=ALPHA_MARKERS['fig2']) |
|
plot_output = gr.Plot(fig,label="2D Visualization") |
|
FIGS['fig2'] = fig |
|
FIGS_OBJECTS[1] = plot_output |
|
with gr.TabItem("Tree Visualization"): |
|
|
|
with gr.Row(): |
|
col2,tree_search,tree_alpha_edge,tree_alpha_name,tree_alpha_marker = search_bar_gr(model_names,slider=True,double_search=False,key='fig4') |
|
with col2: |
|
fig = plot_tree(sim_matrix, model_names, families, colors,alpha_edges=ALPHA_EDGES['fig4'],alpha_names=ALPHA_NAMES['fig4'],alpha_markers=ALPHA_MARKERS['fig4']) |
|
tree_output = gr.Plot(fig,label="Tree Visualization") |
|
FIGS['fig4'] = fig |
|
FIGS_OBJECTS[2] = tree_output |
|
|
|
|
|
|
|
gr.Markdown("## Submitting a Model") |
|
|
|
gr.Markdown( |
|
"You may contribute new models to this collaborative space using compute resources. " |
|
"Once processed, the model will be compared to existing ones, and its results added to a shared public database. " |
|
"Model families (e.g., LLaMA, OPT, Mistral) are extracted from Hugging Face model cards and used only for visualization (e.g., coloring plots); they are **not** involved in the computation of similarity." |
|
) |
|
|
|
gr.Markdown( |
|
"**To add a new model:**\n" |
|
"1. Enter the name of a model hosted on Hugging Face (e.g., `'mistralai/Mistral-7B-Instruct-v0.3'`).\n" |
|
"2. Click on the **Run PhyloLM** button.\n" |
|
"- If the model has already been processed, you'll be notified and no new run will start.\n" |
|
"- If it hasn't been processed, it will be downloaded and be evaluated.\n\n" |
|
"⚠️ Be careful when submitting large LLMs (typically >15B parameters) as they may exceed the GPU RAM or the time limit, leading to failed runs." |
|
) |
|
|
|
with gr.Group(): |
|
model_input = gr.Textbox(label="Model", interactive=True) |
|
submit_btn = gr.Button("Run PhyloLM", variant="primary") |
|
|
|
|
|
|
|
gr.Markdown("## Disclaimer") |
|
gr.Markdown( |
|
"This is a research prototype and may contain bugs or limitations. " |
|
"All computed data are public and hosted on [GitHub](https://github.com/PhyloLM/Data). " |
|
"If you'd like to contribute additional models — especially for gated or large models that cannot be processed via the web interface — " |
|
"you are welcome to submit a pull request to the repository cited above. " |
|
"All results are computed on the 'Math' set of genes used in the original paper." |
|
) |
|
|
|
gr.Markdown("## Citation") |
|
gr.Markdown("If you find this project useful for your research, please consider citing the following paper:") |
|
|
|
|
|
gr.Code('''@inproceedings{ |
|
yax2025phylolm, |
|
title={Phylo{LM}: Inferring the Phylogeny of Large Language Models and Predicting their Performances in Benchmarks}, |
|
author={Nicolas Yax and Pierre-Yves Oudeyer and Stefano Palminteri}, |
|
booktitle={The Thirteenth International Conference on Learning Representations}, |
|
year={2025}, |
|
url={https://openreview.net/forum?id=rTQNGQxm4K} |
|
}''',language=None) |
|
|
|
|
|
sim_mat_search_x.change(fn=search_bar_changeX, inputs=sim_mat_search_x, outputs=FIGS_OBJECTS) |
|
sim_mat_search_y.change(fn=search_bar_changeY, inputs=sim_mat_search_y, outputs=FIGS_OBJECTS) |
|
|
|
viz_search.change(fn=search_bar_changeX, inputs=viz_search, outputs=FIGS_OBJECTS) |
|
|
|
tree_search.change(fn=search_bar_changeX, inputs=tree_search, outputs=FIGS_OBJECTS) |
|
|
|
|
|
viz_alpha_edge.change(fn=lambda x : slider_changeAlphaEdges(x,'fig2'), inputs=viz_alpha_edge, outputs=FIGS_OBJECTS) |
|
viz_alpha_name.change(fn=lambda x : slider_changeAlphaNames(x,'fig2'), inputs=viz_alpha_name, outputs=FIGS_OBJECTS) |
|
viz_alpha_marker.change(fn=lambda x : slider_changeAlphaMarkers(x,'fig2'), inputs=viz_alpha_marker, outputs=FIGS_OBJECTS) |
|
|
|
tree_alpha_edge.change(fn=lambda x : slider_changeAlphaEdges(x,'fig4'), inputs=tree_alpha_edge, outputs=FIGS_OBJECTS) |
|
tree_alpha_name.change(fn=lambda x : slider_changeAlphaNames(x,'fig4'), inputs=tree_alpha_name, outputs=FIGS_OBJECTS) |
|
tree_alpha_marker.change(fn=lambda x : slider_changeAlphaMarkers(x,'fig4'), inputs=tree_alpha_marker, outputs=FIGS_OBJECTS) |
|
|
|
|
|
submit_btn.click(fn=prepare_run, inputs=[model_input], outputs=[model_input]).then(fn=reload_env, inputs=[], outputs=FIGS_OBJECTS+ [sim_mat_search_x, sim_mat_search_y, viz_search, tree_search]) |
|
|
|
|
|
SIM_MAT_SEARCH_X = sim_mat_search_x |
|
SIM_MAT_SEARCH_Y = sim_mat_search_y |
|
VIZ_SEARCH = viz_search |
|
TREE_SEARCH = tree_search |
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
demo.launch() |