Spaces:
Sleeping
Sleeping
Commit ·
d43715d
1
Parent(s): adc77b7
grader&rewards
Browse files- grader.py +120 -0
- rewards.py +92 -0
grader.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Grader module for the Cat Rescue Reinforcement Learning environment.
|
| 3 |
+
Evaluates agent performance based on episode logs.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
class CatRescueGrader:
|
| 7 |
+
"""
|
| 8 |
+
Evaluates an episode log from the Cat Rescue environment and produces
|
| 9 |
+
structured grading results and human-readable summaries.
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
def grade(self, episode_log: dict) -> dict:
|
| 13 |
+
"""
|
| 14 |
+
Grades an episode based on the provided episode log.
|
| 15 |
+
|
| 16 |
+
Args:
|
| 17 |
+
episode_log (dict): A dictionary containing:
|
| 18 |
+
- total_steps (int): Total steps taken by the agent.
|
| 19 |
+
- cats_rescued (int): Number of cats successfully rescued.
|
| 20 |
+
- total_cats (int): Total number of cats in the episode.
|
| 21 |
+
- total_reward (float): Total reward accumulated.
|
| 22 |
+
- hit_walls (int): Number of times the agent hit a wall.
|
| 23 |
+
- rescue_log (list of bool): True if rescued, one per cat.
|
| 24 |
+
|
| 25 |
+
Returns:
|
| 26 |
+
dict: A structured evaluation result containing:
|
| 27 |
+
- cats_rescued_count (int)
|
| 28 |
+
- all_cats_rescued (bool)
|
| 29 |
+
- rescue_details (list of dict)
|
| 30 |
+
- total_steps (int)
|
| 31 |
+
- wall_hits (int)
|
| 32 |
+
- total_reward (float)
|
| 33 |
+
- efficiency_score (float)
|
| 34 |
+
- passed (bool)
|
| 35 |
+
- grade_label (str)
|
| 36 |
+
"""
|
| 37 |
+
total_steps = episode_log.get("total_steps", 0)
|
| 38 |
+
cats_rescued = episode_log.get("cats_rescued", 0)
|
| 39 |
+
total_cats = episode_log.get("total_cats", 0)
|
| 40 |
+
total_reward = episode_log.get("total_reward", 0.0)
|
| 41 |
+
hit_walls = episode_log.get("hit_walls", 0)
|
| 42 |
+
rescue_log = episode_log.get("rescue_log", [])
|
| 43 |
+
|
| 44 |
+
all_cats_rescued = (cats_rescued == total_cats) and (total_cats > 0)
|
| 45 |
+
passed = all_cats_rescued
|
| 46 |
+
|
| 47 |
+
rescue_details = [{"cat_id": i, "rescued": rescued} for i, rescued in enumerate(rescue_log)]
|
| 48 |
+
|
| 49 |
+
efficiency_score = 0.0
|
| 50 |
+
if total_steps > 0:
|
| 51 |
+
efficiency_score = min(1.0, float(cats_rescued) / total_steps)
|
| 52 |
+
|
| 53 |
+
if passed and efficiency_score >= 0.8:
|
| 54 |
+
grade_label = "PERFECT"
|
| 55 |
+
elif passed and efficiency_score >= 0.5:
|
| 56 |
+
grade_label = "GOOD"
|
| 57 |
+
elif cats_rescued > 0 and not passed:
|
| 58 |
+
grade_label = "PARTIAL"
|
| 59 |
+
elif cats_rescued == 0:
|
| 60 |
+
grade_label = "FAIL"
|
| 61 |
+
else:
|
| 62 |
+
# Fallback if passed but efficiency < 0.5
|
| 63 |
+
grade_label = "PASS"
|
| 64 |
+
|
| 65 |
+
return {
|
| 66 |
+
"cats_rescued_count": cats_rescued,
|
| 67 |
+
"all_cats_rescued": all_cats_rescued,
|
| 68 |
+
"rescue_details": rescue_details,
|
| 69 |
+
"total_steps": total_steps,
|
| 70 |
+
"wall_hits": hit_walls,
|
| 71 |
+
"total_reward": total_reward,
|
| 72 |
+
"efficiency_score": efficiency_score,
|
| 73 |
+
"passed": passed,
|
| 74 |
+
"grade_label": grade_label
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
def summary(self, grade_result: dict) -> str:
|
| 78 |
+
"""
|
| 79 |
+
Generates a human-readable summary of the evaluation result.
|
| 80 |
+
|
| 81 |
+
Args:
|
| 82 |
+
grade_result (dict): The result dictionary produced by the grade method.
|
| 83 |
+
|
| 84 |
+
Returns:
|
| 85 |
+
str: A formatted summary string.
|
| 86 |
+
"""
|
| 87 |
+
status = "PASSED" if grade_result.get("passed") else "FAILED"
|
| 88 |
+
grade_label = grade_result.get("grade_label", "UNKNOWN")
|
| 89 |
+
cats_rescued = grade_result.get("cats_rescued_count", 0)
|
| 90 |
+
total_steps = grade_result.get("total_steps", 0)
|
| 91 |
+
efficiency = grade_result.get("efficiency_score", 0.0)
|
| 92 |
+
|
| 93 |
+
return (
|
| 94 |
+
f"--- Grader Summary ---\n"
|
| 95 |
+
f"Status: {status}\n"
|
| 96 |
+
f"Grade Label: {grade_label}\n"
|
| 97 |
+
f"Cats Rescued: {cats_rescued}\n"
|
| 98 |
+
f"Total Steps: {total_steps}\n"
|
| 99 |
+
f"Efficiency Score: {efficiency:.2f}\n"
|
| 100 |
+
f"----------------------"
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
if __name__ == "__main__":
|
| 105 |
+
sample_log = {
|
| 106 |
+
"total_steps": 5,
|
| 107 |
+
"cats_rescued": 3,
|
| 108 |
+
"total_cats": 3,
|
| 109 |
+
"total_reward": 15.5,
|
| 110 |
+
"hit_walls": 1,
|
| 111 |
+
"rescue_log": [True, True, True]
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
grader = CatRescueGrader()
|
| 115 |
+
result = grader.grade(sample_log)
|
| 116 |
+
print("Structured Result:")
|
| 117 |
+
import json
|
| 118 |
+
print(json.dumps(result, indent=2))
|
| 119 |
+
print("\nSummary:")
|
| 120 |
+
print(grader.summary(result))
|
rewards.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Rewards module for the Cat Rescue Reinforcement Learning environment.
|
| 3 |
+
Defines reward constants and computes rewards for different agent events.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
class CatRescueRewards:
|
| 7 |
+
"""
|
| 8 |
+
Manages and calculates rewards for the Cat Rescue environment.
|
| 9 |
+
"""
|
| 10 |
+
STEP_PENALTY = -0.1
|
| 11 |
+
WALL_HIT_PENALTY = -0.3
|
| 12 |
+
CAT_RESCUED_REWARD = 1.0
|
| 13 |
+
ALL_CATS_BONUS = 2.0
|
| 14 |
+
|
| 15 |
+
def calculate(self, event: str, context: dict = None) -> float:
|
| 16 |
+
"""
|
| 17 |
+
Calculates the appropriate reward for an event.
|
| 18 |
+
|
| 19 |
+
Args:
|
| 20 |
+
event (str): The name of the event ("step", "wall_hit", "cat_rescued", "all_cats_rescued").
|
| 21 |
+
context (dict, optional): Additional context for the event (e.g., cats_remaining). Defaults to None.
|
| 22 |
+
|
| 23 |
+
Returns:
|
| 24 |
+
float: The reward value for the selected event. Returns 0.0 for unknown events.
|
| 25 |
+
"""
|
| 26 |
+
if context is None:
|
| 27 |
+
context = {}
|
| 28 |
+
|
| 29 |
+
if event == "step":
|
| 30 |
+
return self.STEP_PENALTY
|
| 31 |
+
elif event == "wall_hit":
|
| 32 |
+
return self.WALL_HIT_PENALTY
|
| 33 |
+
elif event == "cat_rescued":
|
| 34 |
+
return self.CAT_RESCUED_REWARD
|
| 35 |
+
elif event == "all_cats_rescued":
|
| 36 |
+
# If all cats rescued, return CAT_RESCUED_REWARD plus ALL_CATS_BONUS combined
|
| 37 |
+
return self.CAT_RESCUED_REWARD + self.ALL_CATS_BONUS
|
| 38 |
+
else:
|
| 39 |
+
return 0.0
|
| 40 |
+
|
| 41 |
+
def calculate_episode_total(self, events: list) -> float:
|
| 42 |
+
"""
|
| 43 |
+
Calculates the total cumulative reward for a list of event strings.
|
| 44 |
+
|
| 45 |
+
Args:
|
| 46 |
+
events (list of str): A list of event strings that occurred during an episode.
|
| 47 |
+
|
| 48 |
+
Returns:
|
| 49 |
+
float: The total reward accumulated, rounded to 2 decimal places.
|
| 50 |
+
"""
|
| 51 |
+
total_reward = 0.0
|
| 52 |
+
for event in events:
|
| 53 |
+
total_reward += self.calculate(event)
|
| 54 |
+
return round(total_reward, 2)
|
| 55 |
+
|
| 56 |
+
def get_reward_table(self) -> dict:
|
| 57 |
+
"""
|
| 58 |
+
Retrieves a dictionary mapping of all reward constants.
|
| 59 |
+
|
| 60 |
+
Returns:
|
| 61 |
+
dict: The configured reward values for display or debugging purposes.
|
| 62 |
+
"""
|
| 63 |
+
return {
|
| 64 |
+
"STEP_PENALTY": self.STEP_PENALTY,
|
| 65 |
+
"WALL_HIT_PENALTY": self.WALL_HIT_PENALTY,
|
| 66 |
+
"CAT_RESCUED_REWARD": self.CAT_RESCUED_REWARD,
|
| 67 |
+
"ALL_CATS_BONUS": self.ALL_CATS_BONUS
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
if __name__ == "__main__":
|
| 72 |
+
rewards = CatRescueRewards()
|
| 73 |
+
|
| 74 |
+
print("--- Reward Table ---")
|
| 75 |
+
for key, value in rewards.get_reward_table().items():
|
| 76 |
+
print(f"{key}: {value:>5}")
|
| 77 |
+
|
| 78 |
+
print("\n--- Single Event Calculations ---")
|
| 79 |
+
print(f"step: {rewards.calculate('step')}")
|
| 80 |
+
print(f"wall_hit: {rewards.calculate('wall_hit')}")
|
| 81 |
+
print(f"cat_rescued: {rewards.calculate('cat_rescued', {'cats_remaining': 1})}")
|
| 82 |
+
print(f"all_cats_rescued: {rewards.calculate('all_cats_rescued', {'cats_remaining': 0})}")
|
| 83 |
+
print(f"unknown_event: {rewards.calculate('sleep')}")
|
| 84 |
+
|
| 85 |
+
print("\n--- Episode Total Calculation ---")
|
| 86 |
+
sample_events = [
|
| 87 |
+
"step", "step", "wall_hit", "step",
|
| 88 |
+
"cat_rescued", "step", "step", "all_cats_rescued"
|
| 89 |
+
]
|
| 90 |
+
total = rewards.calculate_episode_total(sample_events)
|
| 91 |
+
print(f"Events: {sample_events}")
|
| 92 |
+
print(f"Total Cumulative Reward: {total}")
|