LLM Comparator

repository·main·Indexed 19 days ago

https://github.com/pair-code/llm-comparator

An interactive visualization tool and Python library for qualitative, side-by-side analysis of LLM evaluation results. It enables researchers to identify model response differences through a workflow of judging, bulletizing, and clustering rationales. The library provides model helpers for Google Vertex AI and generates JSON files compatible with the LLM Comparator web interface for analyzing score distributions and behavioral patterns.

Tokens
12.2K
Snippets
36
Records
46
Agent score
65%

What's inside llm-comparator

  1. Overview of LLM Comparator

    main

    LLM Comparator is an interactive visualization tool and a Python library designed for qualitative, side-by-side analysis of Large Language Model (LLM) evaluation results. It allows users to compare responses from two different models (Model A and Model B) at both the individual example level and across broader data slices (e.g., by prompt category).

    Key analytical capabilities include:

    • Score Distribution: Visualizing win rates and quality differences across categories.
    • Rationale Summary: Diving into the reasons (rationales) provided by LLM judges for specific score differences.
    • Custom Functions: Discovering specific behavioral differences, such as structural patterns (e.g., use of bulleted lists) or stylistic traits (e.g., verbosity).
  2. How the LLM Comparator evaluation workflow works

    main

    The library coordinates comparative evaluations through three main phases managed by the llm_comparator.comparison.run() function:

    1. Judging: An LLMJudgeRunner uses a generation model to compare two model responses for every prompt and generate a verdict and explanation.
    2. Bulletizing: A RationaleBulletGenerator condenses the judge's results into concise bullet points.
    3. Clustering: A RationaleClusterGenerator takes the bullets, embeds them, groups them into clusters based on similarity, and generates labels for those clusters.

    This workflow produces a JSON file compatible with the LLM Comparator app.

  3. Generate evaluation JSON files with the Python library

    main

    The llm-comparator package on PyPI is a Python library used to create the JSON files required by the visualization tool.

    Capabilities include:

    • Generating a complete JSON file from scratch, including side-by-side LLM-based evaluations and rationale clusters, given input prompts and models.
    • Performing only the rationale clustering steps if you already possess prompts and model outputs.

    For a practical implementation guide, refer to the basic_demo.ipynb notebook in the repository.

  4. Install the LLM Comparator Python Library

    main

    You can install the library via PyPI (recommended) or by installing from source for development.

    Via PyPI:

    pip install llm_comparator

    From Source (for contributors):

    git clone https://github.com/PAIR-code/llm-comparator.git
    cd llm-comparator/python
    pip install -e .

    It is recommended to use a Python virtual environment.

    # [OPTIONAL] We recommend using a Python virtual environemnt.
    python3 -m venv ~/.venv/llm_comparator
    source ~/.venv/llm_comparator/bin/activate
    
    # Install via PyPI
    pip install llm_comparator
  5. Set up LLM Comparator for local development

    main

    To run the LLM Comparator visualization locally, follow these steps:

    1. Clone the repository:
      git clone https://github.com/PAIR-code/llm-comparator.git
      cd llm-comparator
    2. Install dependencies:
      npm install
    3. Build the project:
      npm run build
    4. Start the local server:
      npm run serve
    git clone https://github.com/PAIR-code/llm-comparator.git
    cd llm-comparator
    npm install
    npm run build
    npm run serve
  6. JSON Data Format: Custom Fields and Per-Model Data

    main

    You can enrich your JSON data with custom_fields to enable advanced visualizations like histograms and bar charts. These must be defined in the metadata.custom_fields_schema.

    Prompt-level Custom Fields

    • number: Visualized as histograms (e.g., word count).
    • category: Visualized as bar charts (e.g., data source).
    • text: Rendered as long text with scrollable holders.
    • string: Generic string (e.g., an ID).
    • image_path: URL for an image.
    • image_byte: Base64 encoded JPEG string.

    Per-Model Custom Fields

    These allow you to attach values to each model's response separately. They are represented as arrays of length 2 in the custom_fields object:

    • per_model_boolean: e.g., [true, false] (did Model A contain bullets?)
    • per_model_number: e.g., [0.31, 0.15] (word counts for A and B).
    • per_model_category: e.g., ["Verbose", "Neutral"] (tone for A and B).
    {
    	"metadata": {
    		"source_path": "...",
    		"custom_fields_schema": [
    			{"name": "prompt_word_count", "type": "number"},
    			{"name": "data_source", "type": "category"},
    			{"name": "is_over_max_token", "type": "per_model_boolean"},
    			{"name": "writing_style", "type": "per_model_category"}
    		]
    	},
    	"models": [{"name": "Model A"}, {"name": "Model B"}],
    	"examples": [
    		{
    			"input_text": "Which city should I visit in South Korea?",
    			"tags": ["Travel"],
    			"output_text_a": "You can visit Seoul.",
    			"output_text_b": "You can visit Seoul, Busan, and Jeju.",
    			"score": 0.5,
    			"individual_rater_scores": [],
    			"custom_fields": {
    				"prompt_word_count": 8,
    				"data_source": "XYZ",
    				"is_over_max_token": [true, false],
    				"writing_style": ["Verbose", "Neutral"]
    			}
    		}
    	]
    }
  7. How the LLM Comparator pipeline works

    main

    LLM Comparator comparisons follow a structured three-step execution model to transform raw model outputs into clustered rationales:

    1. Judgement Phase: An LLMJudgeRunner evaluates the inputs to determine which model performed better and why.
    2. Bulletization Phase: A RationaleBulletGenerator takes those judgements and extracts specific, granular rationale bullets.
    3. Clustering Phase: A RationaleClusterGenerator takes the bullets and groups them into clusters, calculating similarity scores between them.

    This pipeline allows the web application to show not just which model won, but the thematic reasons (clusters) behind the wins and losses.

  8. JSON Data Format: Individual Rater Scores

    main

    To account for position bias or non-deterministic sampling in LLM-as-a-judge evaluations, you can provide multiple evaluation runs for a single prompt using the individual_rater_scores array. This allows the tool to display score distributions and analyze multiple rationales for the same example.

    Each object in the individual_rater_scores array should contain:

    • is_flipped: Boolean indicating if the model order was swapped for this run.
    • score: The numerical score for this specific run.
    • rationale: The text explanation provided by the judge LLM.
    {
        "examples": [
            {
                "input_text": "Which city should I visit in South Korea?",
                "tags": ["Travel"],
                "output_text_a": "You can visit Seoul.",
                "output_text_b": "You can visit Seoul, Busan, and Jeju.",
                "score": 0.5,
                "individual_rater_scores": [
                    {
                        "is_flipped": false,
                        "score": 1.5,
                        "rationale": "A describes more information about ..."
                    },
                    {
                        "is_flipped": false,
                        "score": -0.5,
                        "rationale": "While A provides one option, B gives ..."
                    }
                ],
                "custom_fields": {}
            }
        ]
    }
  9. JSON Data Format: Minimal Schema

    main

    To use the LLM Comparator visualization, you must provide a JSON file following a specific schema. A minimal valid file requires metadata, models, and examples.

    Requirements:

    • score: A numerical value representing the side-by-side evaluation. A positive score means Model A is better; a negative score means Model B is better; zero indicates a tie. Scores are typically in the range of -1.5 to 1.5.
    • models: An array of objects containing the names of the two models being compared.
    • examples: An array of objects containing the prompt, both model outputs, the score, and tags.
    {
        "metadata": {
            "source_path": "Any string for your records (e.g., run id)",
            "custom_fields_schema": []
        },
        "models": [
            {"name": "Short name of your first model"},
            {"name": "Short name of your second model"}
        ],
        "examples": [
            {
                "input_text": "This is a prompt.",
                "tags": ["Math"],
                "output_text_a": "Response to the prompt from the first model (A)",
                "output_text_b": "Response to the prompt from the other model (B)",
                "score": -1.25,
                "individual_rater_scores": [],
                "custom_fields": {}
            }
        ]
    }
  10. How LLMJudgeRunner handles response flipping and repeats

    main

    To ensure robust evaluation, LLMJudgeRunner uses a technique called 'flipping' to mitigate position bias (where a judge might favor the first response it sees).

    When you specify num_repeats, the runner:

    1. Creates math.ceil(num_repeats * 0.5) runs where response_a is the original A and response_b is the original B (is_flipped=False).
    2. Creates math.floor(num_repeats * 0.5) runs where response_a is the original B and response_b is the original A (is_flipped=True).

    Example: If num_repeats=3:

    • 2 runs with is_flipped=False (A vs B)
    • 1 run with is_flipped=True (B vs A)

    During parsing, if a run was flipped, the score is multiplied by -1.0 to normalize the direction of the comparison.

  11. Handle multimodal data with SequenceChunk

    main

    To support multimodal comparisons (e.g., text and images), the input_text, output_text_a, and output_text_b fields in an Example can accept an array of SequenceChunk objects instead of a simple string.

    Each chunk must specify its type using the SequenceChunkType enum:

    • text: For standard string data.
    • image_byte: For raw image data (base64 encoded JPEG).
    export enum SequenceChunkType {
      TEXT = 'text',
      IMAGE_BYTE = 'image_byte',
    }
    
    export interface SequenceChunk {
      data: string;
      type: SequenceChunkType;
    }