Source code for llmp.components.optimizer.optimizer
"""
Optimization Process:
1. Create 20 Examples per MajorVoteGenerator
2. Create an Instruction Test set
3. Test Instructions
"""
from concurrent.futures import ThreadPoolExecutor, as_completed
from llmp.components.base import BaseOptimizer
from llmp.components.evaluation.engine import EvaluationEngine
from llmp.components.example_manager import ExampleManager
from llmp.data_model import JobRecord, ExampleRecord
from llmp.types import TestSetMode
import llmp.components.optimizer._prompts as prompts
from llmp.integration.structgenie import Engine
[docs]class Optimizer(BaseOptimizer):
MIN_EXAMPLES: int = 20
TEST_SIZE: int = 5
PROMPT_SAMPLE_SIZE: int = 3
SELECT_MODE: str = TestSetMode.ACCURACY
INSTRUCTION_TEST_SIZE: int = 5
RUN_PER_SAMPLE: int = 5
[docs] def __init__(self, job: JobRecord, test_set: list[ExampleRecord] = None):
super().__init__(job)
self.example_manager = ExampleManager(self.job)
self.test_set = test_set
[docs] def prepare_job(self):
# create 20 examples per MajorVoteGenerator
if len(self.job.example_records) <= self.PROMPT_SAMPLE_SIZE:
self.example_manager.fill_examples(self.PROMPT_SAMPLE_SIZE)
if len(self.job.example_records) <= self.MIN_EXAMPLES:
self.example_manager.fill_examples(self.MIN_EXAMPLES)
if not self.test_set:
self.test_set = self.example_manager.get_test_set(self.TEST_SIZE, mode=self.SELECT_MODE)
[docs] def optimize(self, mode: str = "random", metric: str = "accuracy"):
"""Optimize the prompts and examples for a specific job."""
self.prepare_job()
# step 2: create an instruction test set
example_set = self.example_manager.get_test_set(
self.PROMPT_SAMPLE_SIZE, mode=self.SELECT_MODE, exclude_ids=self.test_set_ids
)
instructions = Engine.from_template(prompts.INSTRUCTION_TEMPLATE).run(
{"example_set": example_set, "num_instructions": self.INSTRUCTION_TEST_SIZE})["instructions"]
test_settings = [
{"instruction": instruction, "example_ids": [e.idx for e in example_set]}
for instruction in instructions
]
result = self.evaluate(test_settings)
best_index = result.index(max(result, key=lambda x: x[metric]))
best_setting = test_settings[best_index]
# step 3: select test cases with the highest failing rate
[docs] def evaluate(self, job_settings: list[dict], num_workers: int = 5, **kwargs):
unordered_results = []
with ThreadPoolExecutor(max_workers=num_workers) as executor:
futures = {
executor.submit(self._evaluate, idx, job_setting, **kwargs)
for idx, job_setting in enumerate(job_settings)
}
for future in as_completed(futures):
idx, results = future.result()
unordered_results.append((idx, results))
return [results for _, results in sorted(unordered_results, key=lambda x: x[0])]
def _evaluate(self, idx: int, job_setting: dict, **kwargs):
evaluator = EvaluationEngine(self.job, self.RUN_PER_SAMPLE)
return idx, evaluator.evaluate(self.test_set, job_setting)
@property
def test_set_ids(self):
return [r.idx.hex for r in self.test_set]