Skip to content

dspy.BetterTogether

dspy.BetterTogether(metric: Callable, prompt_optimizer: Optional[Teleprompter] = None, weight_optimizer: Optional[Teleprompter] = None, seed: Optional[int] = None)

Bases: Teleprompter

Source code in dspy/teleprompt/bettertogether.py
def __init__(self,
    metric: Callable,
    prompt_optimizer: Optional[Teleprompter] = None,
    weight_optimizer: Optional[Teleprompter] = None,
    seed: Optional[int] = None,
  ):
    if not dspy.settings.experimental:
        raise ValueError("This is an experimental optimizer. Set `dspy.settings.experimental` to `True` to use it.")

    # TODO: Note that the BetterTogether optimizer is meaningful when
    # BootstrapFinetune uses a metric to filter the training data before
    # fine-tuning. However, one can also choose to run this optimizer with 
    # a BoostrapFinetune without a metric, say, if there aren't labels
    # available for the training data. Should this be noted somewhere?
    # TODO: We should re-consider if the metric should be required.
    self.prompt_optimizer = prompt_optimizer if prompt_optimizer else BootstrapFewShotWithRandomSearch(metric=metric)
    self.weight_optimizer = weight_optimizer if weight_optimizer else BootstrapFinetune(metric=metric)

    is_supported_prompt = isinstance(self.prompt_optimizer, BootstrapFewShotWithRandomSearch)
    is_supported_weight = isinstance(self.weight_optimizer, BootstrapFinetune)
    if not is_supported_prompt or not is_supported_weight:
        raise ValueError(
            "The BetterTogether optimizer only supports the following optimizers for now: BootstrapFinetune, "
            "BootstrapFewShotWithRandomSearch."
        )

    self.rng = random.Random(seed)

Functions

compile(student: Program, trainset: List[Example], strategy: str = 'p -> w -> p', valset_ratio=0.1) -> Program

Source code in dspy/teleprompt/bettertogether.py
def compile(
    self,
    student: Program,
    trainset: List[Example],
    strategy: str = "p -> w -> p",
    valset_ratio = 0.1,
) -> Program:
    # TODO: We could record acc on a different valset to pick the best
    # strategy within the provided strategy
    logger.info("[BetterTogether] Validating the strategy")
    parsed_strategy = strategy.lower().split(self.STRAT_SEP)

    if not all([s in ["p", "w"] for s in parsed_strategy]):
        raise ValueError(
            f"The strategy should be a sequence of 'p' and 'w' separated by '{self.STRAT_SEP}', but "
            f"found: {strategy}"
        )

    logger.info("[BetterTogether] Preparing the student program...")
    # TODO: Prepare student returns student.reset_copy(), which is what gets
    # optimized. We should make this clear in the doc comments.
    student = prepare_student(student)
    set_missing_predictor_lms(student)

    # Make a shallow copy of the trainset, so that we don't change the order
    # of the examples in the original trainset
    trainset = trainset[:]
    logger.info("[BetterTogether] Compiling the student program...")
    student = self._run_strategies(parsed_strategy, student, trainset, valset_ratio)

    logger.info("[BetterTogether] BetterTogether has finished compiling the student program")
    return student