debugml-env / inference.py
shae2977's picture
update inference
b657dcc
Raw
History Blame Contribute Delete
6.34 kB
import os
import random
from typing import List, Optional
from openai import OpenAI
from env.environments import DebugMLEnv
from env.models import Action
API_BASE_URL = os.environ.get("API_BASE_URL", "https://router.huggingface.co/v1")
MODEL_NAME = os.environ.get("MODEL_NAME", "Qwen/Qwen2.5-72B-Instruct")
MAX_STEPS = 15
SUCCESS_THRESHOLD = 0.8
def get_api_key() -> str:
api_key = os.environ.get("HF_TOKEN") or os.environ.get("OPENAI_API_KEY")
if not api_key:
raise RuntimeError(
"Missing API key. Set HF_TOKEN for the Hugging Face router or OPENAI_API_KEY before running this script."
)
return api_key
client = OpenAI(base_url=API_BASE_URL, api_key=get_api_key())
# ---------------------------------------------------------------------------
# Loggers
# ---------------------------------------------------------------------------
def log_start(task: str, env: str, model: str):
print(f"[START] task={task} env={env} model={model}", flush=True)
def log_step(step: int, action: str, reward: float, done: bool, error: Optional[str]):
error_val = error if error else "null"
print(
f"[STEP] step={step} action={action} reward={reward:.2f} done={str(done).lower()} error={error_val}",
flush=True,
)
def clamp_score(score: float) -> float:
return max(0.01, min(0.99, score))
def log_end(success: bool, steps: int, rewards: List[float], score: float):
rewards_str = ",".join(f"{r:.2f}" for r in rewards)
print(
f"[END] success={str(success).lower()} steps={steps} rewards={rewards_str} score={clamp_score(score):.2f}",
flush=True,
)
# ---------------------------------------------------------------------------
# Agent
# ---------------------------------------------------------------------------
def build_prompt(obs, last_action: Optional[str], last_reward: Optional[float]) -> str:
current_score = round(0.5 * obs.accuracy + 0.25 * obs.precision + 0.25 * obs.recall, 2)
target_score = 0.85
prev_info = ""
if last_action and last_reward is not None:
prev_info = f"""
Previous info:
last action: {last_action}
last reward: {last_reward}"""
return f"""
You are an agent optimizing a machine learning pipeline. Your goal is to maximize accuracy by selecting the best action at each step.
Current state:
- Accuracy: {obs.accuracy}
- Scaling applied: {obs.scaling}
- Feature count: {obs.feature_count}
- Model type: {obs.model_type}
{prev_info}
Score:
- current_score: {current_score}
- target_score: {target_score}
- gap: {round(target_score - current_score, 2)}
Available actions:
- add_scaling → enables feature scaling (only useful if scaling is False)
- fix_split → adjusts train/test split
- add_feature → adds a feature (only useful if feature_count < 6)
- remove_feature → removes a feature (only useful if feature_count > 1)
Constraints you must respect:
- If scaling is True, do NOT choose add_scaling
- If feature_count >= 6, do NOT choose add_feature
- If feature_count <= 1, do NOT choose remove_feature
- Do not repeat an action that returned a negative reward
Reason step by step:
1. Look at the current state
2. Eliminate any actions that violate the constraints above
3. From the remaining actions, pick the one most likely to improve accuracy
4. Focus on actions that reduce the score gap.
5. If score is already high (>0.85), avoid unnecessary changes.
6. If the pipeline is already good, avoid unnecessary changes.
7. Respond with ONLY the action name, nothing else.
Action:
"""
def clean_action(output: str) -> str:
text = output.strip().lower().replace(" ", "_")
valid = ["add_scaling", "fix_split", "add_feature", "remove_feature"]
for action in valid:
if action in text:
return action
return random.choice(valid)
def get_action(obs, last_action: Optional[str], last_reward: Optional[float]) -> str:
prompt = build_prompt(obs, last_action, last_reward)
response = client.chat.completions.create(
model=MODEL_NAME,
messages=[{"role": "system", "content": prompt}],
temperature=0.2,
max_tokens=10,
)
raw = response.choices[0].message.content or ""
return clean_action(raw)
# ---------------------------------------------------------------------------
# Task runner
# ---------------------------------------------------------------------------
def run_task(task_name: str) -> float:
env = DebugMLEnv()
rewards: List[float] = []
info = {}
last_action = None
last_reward = None
log_start(task=task_name, env="debugml", model=MODEL_NAME)
try:
obs = env.reset(task_name)
for step_num in range(1, MAX_STEPS + 1):
action = get_action(obs, last_action, last_reward)
error = None
try:
obs, reward, done, info = env.step(Action(type=action))
except Exception as e:
error = str(e)
reward = 0.0
done = True
rewards.append(reward)
last_action = action
last_reward = reward
log_step(step=step_num, action=action, reward=reward, done=done, error=error)
if done:
break
except Exception as e:
print(f"[CRASH] {e}", flush=True)
log_end(success=False, steps=len(rewards), rewards=rewards, score=0.01)
return 0.01
score = clamp_score(info["task_score"] if "task_score" in info else 0.01)
success = score >= SUCCESS_THRESHOLD
log_end(success=success, steps=len(rewards), rewards=rewards, score=score)
return score
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
tasks = ["fix_basics", "optimize_features", "full_pipeline_optimization", "stability_optimization"]
for task_name in tasks:
run_task(task_name)
print("", flush=True)
if __name__ == "__main__":
main()