TextGrad

repository·main·Indexed 25 days ago

https://github.com/zou-group/textgrad

An autograd engine for textual gradients that implements backpropagation through text feedback provided by LLMs. Using a PyTorch-like API, TextGrad allows developers to optimize prompts, code, and reasoning via textual gradient descent (TGD). It supports a variety of optimization tasks, including code optimization with PythonEvaluator, prompt optimization, and solution optimization for multimodal benchmarks like MathVista and ScienceQA. The library includes support for experimental LiteLLM engines and integration with the Chameleon agentic system.

Tokens
10.4K
Snippets
28
Records
54
Agent score
83%

What's inside TextGrad

  1. Understand LeetCode evaluation limitations

    main

    When using the code optimization evaluation suite, be aware of the following constraints:

    • Result Instability: Minor changes in code snippets can lead to varying results. It is recommended to run optimizations multiple times with different seeds and report averages.
    • Platform Issues: Interfacing with the LeetCode platform via python-leetcode may result in timeouts, network issues, or rate limiting.
    • Character Constraints: The LeetCode API may reject programs containing certain characters (like parentheses or brackets wrapping Python statements). The evaluation pipeline attempts to mitigate this by using GPT-4 to clean the code.
    • Data Source: The evaluation uses data from LeetCodeHardGym. The dataset used is leetcode_with_tests.jsonl.
  2. Understand limitations of Code Optimization evaluations

    main

    When using the code optimization evaluation tasks, be aware of the following behaviors:

    • Result Instability: Code optimization results can be sensitive to minor changes in code snippets. To mitigate this, it is recommended to run optimizations multiple times with different seeds and report average results.
    • LeetCode API Issues: The evaluation relies on pushing code to the LeetCode platform, which is subject to timeouts, network issues, and rate limiting.
    • Character Constraints: The underlying LeetCode API (via python-leetcode) may reject programs containing certain characters like parentheses or brackets wrapping Python statements. The evaluation pipeline attempts to mitigate this by using GPT-4 to clean these characters.
    • External Dependencies: The evaluation utilizes LeetCodeHardGym and the python-leetcode package.
  3. QuickStart: Optimize an LLM response using Textual Gradient Descent

    main

    TextGrad follows a pattern similar to PyTorch. To optimize an LLM's response, follow these three steps:

    1. Initialize: Get an initial response using tg.BlackboxLLM and wrap the target output in a tg.Variable.
    2. Define Loss and Optimizer: Define a tg.TextLoss using natural language instructions to evaluate the reasoning, and initialize a tg.TGD (Textual Gradient Descent) optimizer with the parameters to be optimized.
    3. Backward Pass and Update: Compute the loss by calling the loss function on the variable, call .backward() to compute textual gradients, and call optimizer.step() to update the variable.
    import textgrad as tg
    
    # Setup backward engine
    tg.set_backward_engine("gpt-4o", override=True)
    
    # Step 1: Get initial response
    model = tg.BlackboxLLM("gpt-4o")
    question_string = "If it takes 1 hour to dry 25 shirts under the sun, how long will it take to dry 30 shirts under the sun? Reason step by step"
    
    question = tg.Variable(question_string, role_description="question to the LLM", requires_grad=False)
    answer = model(question)
    
    # Step 2: Define loss and optimizer
    answer.set_role_description("concise and accurate answer to the question")
    optimizer = tg.TGD(parameters=[answer])
    evaluation_instruction = f"Here's a question: {question_string}. Evaluate any given answer to this question, be smart, logical, and very critical. Just provide concise feedback."
    loss_fn = tg.TextLoss(evaluation_instruction)
    
    # Step 3: Backward pass and update
    loss = loss_fn(answer)
    loss.backward()
    optimizer.step()
    
    print(answer)
  4. Run solution optimization experiments for multimodal tasks

    main

    You can run solution optimization experiments for multimodal benchmarks like MathVista and ScienceQA using the solution_optimization_mm.py script. The script allows you to specify the task, the LLM engine used for generation, the evaluation engine, the number of optimization iterations, and concurrency settings.

    MathVista Experiment

    To run an experiment on MathVista:

    cd evaluation
    python solution_optimization_mm.py --task mathvista \
    --engine=gpt-4o \
    --eval_engine=gpt-4o \
    --max_iterations 4 \
    --num_threads 10 \
    --majority_voting

    ScienceQA Experiment

    To run an experiment on ScienceQA:

    cd evaluation
    python solution_optimization_mm.py --task scienceqa \
    --engine=gpt-4o \
    --eval_engine=gpt-4o \
    --max_iterations 8 \
    --num_threads 20
    cd evaluation
    python solution_optimization_mm.py --task mathvista \
    --engine=gpt-4o \
    --eval_engine=gpt-4o \
    --max_iterations 4 \
    --num_threads 10 \
    --majority_voting
  5. Install TextGrad for Chameleon Agentic System

    main

    To use TextGrad for optimizing the Chameleon agentic system, set up a new Conda environment and install the required Python packages and system dependencies.

    Python Packages:

    • textgrad (installed in editable mode)
    • matplotlib
    • easyocr

    System Dependencies:

    • graphviz (required via apt)
    conda create --name textgrad
    conda activate textgrad
    cd textgrad
    pip install -e .
    pip install matplotlib
    pip install easyocr
    
    sudo apt install graphviz
  6. Initialize TextGrad primitives

    main

    To use TextGrad, import the core components: get_engine, Variable, TextualGradientDescent, and TextLoss. Ensure you have an OPENAI_API_KEY set in your environment variables.

    from textgrad.engine import get_engine
    from textgrad import Variable
    from textgrad.optimizer import TextualGradientDescent
    from textgrad.loss import TextLoss
    import os
    
    # Ensure API key is set
    os.environ["OPENAI_API_KEY"] = "your-key-here"
  7. Run the PythonEvaluator for code optimization

    main

    The PythonEvaluator is used to run model-generated code for evaluation.

    Security Warning: This program executes untrusted model-generated code. It is strongly recommended to run this only within a robust security sandbox.

    By default, the evaluation scripts (py_eval.py and utils.py) are commented out and configured to raise exceptions to prevent accidental execution of untrusted code. You must manually edit these files to enable execution.

  8. Optimize Chameleon Agentic System with TextGrad

    main

    TextGrad can be used to optimize the long chain of reasoning steps in the Chameleon agentic system through an interactive self-improvement loop. Chameleon is a system capable of planning, executing, and reasoning about tool sequences to answer questions.

    A practical implementation example is available in the notebook: TextGrad_Chemeleon_ScienceQA.ipynb.

  9. Run a TextGrad optimization loop

    main

    To perform optimization, follow the standard autograd pattern:

    1. Compute the loss: l = loss(x)
    2. Perform the backward pass: l.backward(engine)
    3. Update parameters: optimizer.step()

    To prevent gradient accumulation across steps, call optimizer.zero_grad() at the start of each iteration.

    # Single optimization step
    l = loss(x)
    l.backward(engine)
    optimizer.step()
    
    # Loop pattern
    for i in range(steps):
        optimizer.zero_grad()
        l = loss(x)
        l.backward(engine)
        optimizer.step()
  10. Run the Textual Gradient Descent (TGD) optimization loop

    main

    The optimization process follows a pattern similar to PyTorch:

    1. Forward Pass: Call your loss function to get the loss variable.
    2. Backward Pass: Call loss.backward() to compute textual gradients.
    3. Update: Call optimizer.step() to update the variables.
    4. Reset: Call optimizer.zero_grad() before the next iteration to clear old gradients.
    # Forward pass
    loss = loss_fn(problem, code)
    
    # Backward pass
    loss.backward()
    
    # Update parameters
    optimizer.step()
    
    # Next iteration
    optimizer.zero_grad()
    loss = loss_fn(problem, code)
    loss.backward()
    optimizer.step()