MiniCheck: Efficient Fact-Checking of LLMs on Grounding Documents

repository·main·Indexed 42 days ago

https://github.com/liyan06/minicheck

A fact-checking framework for evaluating the factual consistency of LLM outputs against grounding documents. It supports multiple models, including lightweight options like flan-t5-large and the high-performance Bespoke-MiniCheck-7B. The framework features a SentenceFusion approach for multi-sentence claims, Automatic Prefix Caching (APC) for increased throughput via vLLM, and tools for generating synthetic C2D and D2C data.

Tokens
5K
Snippets
17
Records
19
Agent score
53%

What's inside MiniCheck

  1. Enable Automatic Prefix Caching (APC) for Bespoke-MiniCheck-7B

    main

    When using the Bespoke-MiniCheck-7B model, you can enable Automatic Prefix Caching (APC) to significantly increase throughput and reduce latency. APC works by caching the KV cache of a document; if you check multiple different claims against the same document, the model reuses the cached document prefix instead of recomputing it.

    To enable this, set enable_prefix_caching=True during initialization.

    # Enable APC for faster inference when reusing the same document
    scorer = MiniCheck(model_name='Bespoke-MiniCheck-7B', enable_prefix_caching=True, cache_dir='./ckpts')
  2. Configure OpenAI API key for synthetic data generation

    main

    To use the OpenAI API for generating synthetic data in the C2D or D2C pipelines, you must manually add your OPENAI_API_KEY to the synthetic_data_gen/prompt_utils.py file.

    # Edit synthetic_data_gen/prompt_utils.py to include:
    OPENAI_API_KEY = 'your-api-key-here'
  3. Generate C2D synthetic data from a claim

    main

    C2D (Claim-to-Document) data generation starts with a human-written claim. The process generates synthetic documents designed to require multi-sentence, multi-fact reasoning to verify the claim. This produces (doc, claim, label) triples.

    Run the C2D_gen.py script with the --claim flag to specify the input claim.

    cd synthetic_data_gen
    python C2D_gen.py --claim "By this date, over 5,000 members of the caravan were staying at the Tijuana Stadium — a structure with a capacity of 3,000."
  4. Install the MiniCheck package

    main

    You can install the core MiniCheck package and its dependencies using pip. If you intend to use the Bespoke-MiniCheck-7B model for inference, you should install the [llm] extra, which includes the vllm dependency (note: vllm support is Linux only).

    # Standard installation
    pip install "minicheck @ git+https://github.com/Liyan06/MiniCheck.git@main"
    
    # Installation with vllm support for Bespoke-MiniCheck-7B (Linux only)
    pip install "minicheck[llm] @ git+https://github.com/Liyan06/MiniCheck.git@main"
  5. Generate D2C synthetic data from a document

    main

    D2C (Document-to-Claim) data generation starts with human-written documents. The process generates claims paired with specific portions of the document, requiring multi-sentence, multi-fact reasoning to check them. This produces (doc, claim, label) triples.

    Note: The generation process assumes a document length of approximately 500 words. Longer documents increase costs due to the "leave-one-sent-out" document augmentation method used.

    Run the D2C_gen.py script with the --doc_path flag to specify the path to the input document.

    cd synthetic_data_gen
    python D2C_gen.py --doc_path D2C-doc-example.txt
  6. How SentenceFusion aggregation works in LLMCheck

    main

    When using LLMCheck, the system uses a technique called SentenceFusion to handle multi-sentence claims:

    1. Chunking: The document is split into chunks of size chunk_size.
    2. Sentence Splitting: The claim is split into individual sentences.
    3. Matrix Construction: For every document chunk and every claim sentence, a probability is calculated, forming a matrix of shape (num_chunks x num_sentences).
    4. Max-over-Chunks: For each sentence, the maximum probability across all document chunks is selected: max_prob_per_sentence = np.max(prob_matrix, axis=0).
    5. Min-over-Sentences: The final score is the minimum of these maximum values: final_score = np.min(max_prob_per_sentence). This ensures that if any part of the claim is unsupported, the overall score reflects that lack of support.
  7. Install NLTK punkt tokenizer dependency

    main

    The minicheck package requires the NLTK punkt tokenizer. The package attempts to verify the existence of this resource on import and automatically downloads it via nltk.download('punkt') if it is missing. If you encounter issues with tokenization, ensure your environment has internet access to allow NLTK to fetch the required data.

    import nltk
    
    try:
        nltk.data.find('tokenizers/punkt')
    except LookupError:
        nltk.download('punkt')
  8. Use the MiniCheck scorer for fact-checking

    main

    The MiniCheck class provides a sentence-level fact-checking model. It takes a document and a claim, returning a prediction label and a probability score.

    Important: For optimal performance with multi-sentence claims, you should break the claim into individual sentences before passing them to the scorer.

    Supported model_name values:

    • roberta-large
    • deberta-v3-large
    • flan-t5-large (Best performance for models < 1B parameters; reaches GPT-4 performance)
    • Bespoke-MiniCheck-7B (Current SOTA, most performant, and commercially usable)
    from minicheck.minicheck import MiniCheck
    import os
    
    os.environ["CUDA_VISIBLE_DEVICES"] = "0"
    
    doc = "A group of students gather in the school library to study for their upcoming final exams."
    claim_1 = "The students are preparing for an examination."
    claim_2 = "The students are on vacation."
    
    # Initialize the scorer
    scorer = MiniCheck(model_name='flan-t5-large', cache_dir='./ckpts')
    
    # Score claims against documents
    # Returns: pred_label, raw_prob, _, _
    pred_label, raw_prob, _, _ = scorer.score(docs=[doc, doc], claims=[claim_1, claim_2])
    
    print(pred_label) # [1, 0]
    print(raw_prob)   # [0.9805923700332642, 0.007121330592781305]
  9. Access the LLM-AggreFact benchmark dataset

    main

    The LLM-AggreFact benchmark is available on HuggingFace. It aggregates 11 datasets for factual consistency evaluation. The dataset contains the following fields:

    FieldDescription
    datasetOne of the 11 datasets in the benchmark
    docDocument used to check the corresponding claim
    claimClaim to be checked by the corresponding document
    label1 if the claim is supported, 0 otherwise
    contamination_identifierAn identification string for contamination detection
    from datasets import load_dataset
    dataset = load_dataset("lytang/LLM-AggreFact")
  10. Troubleshoot VRAM errors with Bespoke-MiniCheck-7B

    main

    If you are using Bespoke-MiniCheck-7B on a GPU with low VRAM, you may encounter the following error:

    ValueError: The model's max seq len (XXXX) is larger than the maximum number of tokens that can be stored in KV cache (YYYY). Try increasing 'gpu_memory_utilization' or decreasing 'max_model_len' when initializing the engine.

    Solution: When initializing the MiniCheck class, explicitly set max_model_len to a smaller value than the default (32768).

  11. Predict fact-checking labels with scorer.score()

    main

    Use the scorer.score() method to evaluate whether claims are supported by grounding documents.

    Parameters:

    • docs: An array/list of grounding documents.
    • claims: An array/list of claims to be checked.

    Returns:

    • pred_label: Binary labels (1 or 0) indicating if the claim is supported. Note: The internal logic converts raw probabilities to 1/0 using a threshold of 0.5.
    • raw_prob: The raw probability scores.
    • Two additional return values (ignored in standard usage).

    Performance Note: Using Bespoke-MiniCheck-7B (via vLLM) on a single NVIDIA A6000 (48GB VRAM) provides a throughput of > 500 docs/min.

    # docs and claims should be arrays of strings
    pred_label, raw_prob, _, _ = scorer.score(docs=docs, claims=claims)
  12. Perform fact-checking with score()

    main

    Use the score method to evaluate whether a list of claims are supported by a list of grounding documents.

    Parameters

    • docs: A list or np.ndarray of grounding document strings.
    • claims: A list or np.ndarray of claim strings to be checked.
    • chunk_size: (Optional) The size of the chunk for long documents. If None, defaults are used:
      • roberta-large / deberta-v3-large: 400
      • flan-t5-large: 500
      • Bespoke-MiniCheck-7B: 32768-300

    Returns

    A tuple containing:

    1. pred_labels: A list of integers (0 for unsupported, 1 for supported).
    2. max_support_probs: The probability of "supported" for the chunk that determined the final label.
    3. used_chunks: The actual chunks used from the input documents.
    4. support_prob_per_chunk: The probability of "supported" for every individual chunk.
    from minicheck import MiniCheck
    
    checker = MiniCheck(model_name='flan-t5-large')
    
    docs = ["The capital of France is Paris."]
    claims = ["Paris is the capital of France.", "Berlin is the capital of France."]
    
    pred_labels, max_probs, used_chunks, per_chunk_probs = checker.score(docs, claims)
    
    # pred_labels will be [1, 0]