|

A Coding Guide to Build a Procedural Memory Agent That Learns, Stores, Retrieves, and Reuses Skills as Neural Modules Over Time

In this tutorial, we discover how an clever agent can steadily type procedural reminiscence by studying reusable expertise straight from its interactions with an setting. We design a minimal but highly effective framework wherein expertise behave like neural modules: they retailer motion sequences, carry contextual embeddings, and are retrieved by similarity when a new state of affairs resembles an expertise. As we run our agent by means of a number of episodes, we observe how its behaviour turns into extra environment friendly, transferring from primitive exploration to leveraging a library of expertise that it has realized by itself. Check out the FULL CODES here.

import numpy as np
import matplotlib.pyplot as plt
from collections import defaultdict


class Skill:
   def __init__(self, title, preconditions, action_sequence, embedding, success_count=0):
       self.title = title
       self.preconditions = preconditions
       self.action_sequence = action_sequence
       self.embedding = embedding
       self.success_count = success_count
       self.times_used = 0
  
   def is_applicable(self, state):
       for key, worth in self.preconditions.gadgets():
           if state.get(key) != worth:
               return False
       return True
  
   def __repr__(self):
       return f"Skill({self.title}, used={self.times_used}, success={self.success_count})"


class SkillLibrary:
   def __init__(self, embedding_dim=8):
       self.expertise = []
       self.embedding_dim = embedding_dim
       self.skill_stats = defaultdict(lambda: {"makes an attempt": 0, "successes": 0})
  
   def add_skill(self, ability):
       for existing_skill in self.expertise:
           if self._similarity(ability.embedding, existing_skill.embedding) > 0.9:
               existing_skill.success_count += 1
               return existing_skill
       self.expertise.append(ability)
       return ability
  
   def retrieve_skills(self, state, query_embedding=None, top_k=3):
       relevant = [s for s in self.skills if s.is_applicable(state)]
       if query_embedding shouldn't be None and relevant:
           similarities = [self._similarity(query_embedding, s.embedding) for s in applicable]
           sorted_skills = [s for _, s in sorted(zip(similarities, applicable), reverse=True)]
           return sorted_skills[:top_k]
       return sorted(relevant, key=lambda s: s.success_count / max(s.times_used, 1), reverse=True)[:top_k]
  
   def _similarity(self, emb1, emb2):
       return np.dot(emb1, emb2) / (np.linalg.norm(emb1) * np.linalg.norm(emb2) + 1e-8)
  
   def get_stats(self):
       return {
           "total_skills": len(self.expertise),
           "total_uses": sum(s.times_used for s in self.expertise),
           "avg_success_rate": np.imply([s.success_count / max(s.times_used, 1) for s in self.skills]) if self.expertise else 0
       }

We outline how expertise are represented and saved in a reminiscence construction. We implement similarity-based retrieval in order that the agent can match a new state with previous expertise utilizing cosine similarity. As we work by means of this layer, we see how ability reuse turns into potential as soon as expertise purchase metadata, embeddings, and utilization statistics. Check out the FULL CODES here.

class GridWorld:
   def __init__(self, measurement=5):
       self.measurement = measurement
       self.reset()
  
   def reset(self):
       self.agent_pos = [0, 0]
       self.goal_pos = [self.size-1, self.size-1]
       self.objects = {"key": [2, 2], "door": [3, 3], "field": [1, 3]}
       self.stock = []
       self.door_open = False
       return self.get_state()
  
   def get_state(self):
       return {
           "agent_pos": tuple(self.agent_pos),
           "has_key": "key" in self.stock,
           "door_open": self.door_open,
           "at_goal": self.agent_pos == self.goal_pos,
           "objects": {ok: tuple(v) for ok, v in self.objects.gadgets()}
       }
  
   def step(self, motion):
       reward = -0.1
       if motion == "move_up":
           self.agent_pos[1] = min(self.agent_pos[1] + 1, self.measurement - 1)
       elif motion == "move_down":
           self.agent_pos[1] = max(self.agent_pos[1] - 1, 0)
       elif motion == "move_left":
           self.agent_pos[0] = max(self.agent_pos[0] - 1, 0)
       elif motion == "move_right":
           self.agent_pos[0] = min(self.agent_pos[0] + 1, self.measurement - 1)
       elif motion == "pickup_key":
           if self.agent_pos == self.objects["key"] and "key" not in self.stock:
               self.stock.append("key")
               reward = 1.0
       elif motion == "open_door":
           if self.agent_pos == self.objects["door"] and "key" in self.stock:
               self.door_open = True
               reward = 2.0
       executed = self.agent_pos == self.goal_pos and self.door_open
       if executed:
           reward = 10.0
       return self.get_state(), reward, executed

We assemble a easy setting wherein the agent learns duties such as choosing up a key, opening a door, and reaching a aim. We use this setting as a playground for our procedural reminiscence system, permitting us to observe how primitive actions evolve into extra advanced, reusable expertise. The setting’s construction helps us observe clear, interpretable enhancements in behaviour throughout episodes. Check out the FULL CODES here.

class ProceduralMemoryAgent:
   def __init__(self, env, embedding_dim=8):
       self.env = env
       self.skill_library = SkillLibrary(embedding_dim)
       self.embedding_dim = embedding_dim
       self.episode_history = []
       self.primitive_actions = ["move_up", "move_down", "move_left", "move_right", "pickup_key", "open_door"]
  
   def create_embedding(self, state, action_seq):
       state_vec = np.zeros(self.embedding_dim)
       state_vec[0] = hash(str(state["agent_pos"])) % 1000 / 1000
       state_vec[1] = 1.0 if state.get("has_key") else 0.0
       state_vec[2] = 1.0 if state.get("door_open") else 0.0
       for i, motion in enumerate(action_seq[:self.embedding_dim-3]):
           state_vec[3+i] = hash(motion) % 1000 / 1000
       return state_vec / (np.linalg.norm(state_vec) + 1e-8)
  
   def extract_skill(self, trajectory):
       if len(trajectory) < 2:
           return None
       start_state = trajectory[0][0]
       actions = [a for _, a, _ in trajectory]
       preconditions = {"has_key": start_state.get("has_key", False), "door_open": start_state.get("door_open", False)}
       end_state = self.env.get_state()
       if end_state.get("has_key") and not start_state.get("has_key"):
           title = "acquire_key"
       elif end_state.get("door_open") and not start_state.get("door_open"):
           title = "open_door_sequence"
       else:
           title = f"navigate_{len(actions)}_steps"
       embedding = self.create_embedding(start_state, actions)
       return Skill(title, preconditions, actions, embedding, success_count=1)
  
   def execute_skill(self, ability):
       ability.times_used += 1
       trajectory = []
       total_reward = 0
       for motion in ability.action_sequence:
           state = self.env.get_state()
           next_state, reward, executed = self.env.step(motion)
           trajectory.append((state, motion, reward))
           total_reward += reward
           if executed:
               ability.success_count += 1
               return trajectory, total_reward, True
       return trajectory, total_reward, False
  
   def discover(self, max_steps=20):
       trajectory = []
       state = self.env.get_state()
       for _ in vary(max_steps):
           motion = self._choose_exploration_action(state)
           next_state, reward, executed = self.env.step(motion)
           trajectory.append((state, motion, reward))
           state = next_state
           if executed:
               return trajectory, True
       return trajectory, False

We deal with constructing embeddings that encode the context of a state-action sequence, enabling us to meaningfully examine expertise. We additionally extract expertise from profitable trajectories, reworking uncooked expertise into reusable behaviours. As we run this code, we observe how easy exploration steadily yields structured data that the agent can apply later. Check out the FULL CODES here.

   def _choose_exploration_action(self, state):
       agent_pos = state["agent_pos"]
       if not state.get("has_key"):
           key_pos = state["objects"]["key"]
           if agent_pos == key_pos:
               return "pickup_key"
           if agent_pos[0] < key_pos[0]:
               return "move_right"
           if agent_pos[0] > key_pos[0]:
               return "move_left"
           if agent_pos[1] < key_pos[1]:
               return "move_up"
           return "move_down"
       if state.get("has_key") and not state.get("door_open"):
           door_pos = state["objects"]["door"]
           if agent_pos == door_pos:
               return "open_door"
           if agent_pos[0] < door_pos[0]:
               return "move_right"
           if agent_pos[0] > door_pos[0]:
               return "move_left"
           if agent_pos[1] < door_pos[1]:
               return "move_up"
           return "move_down"
       goal_pos = (4, 4)
       if agent_pos[0] < goal_pos[0]:
           return "move_right"
       if agent_pos[1] < goal_pos[1]:
           return "move_up"
       return np.random.alternative(self.primitive_actions)
  
   def run_episode(self, use_skills=True):
       self.env.reset()
       total_reward = 0
       steps = 0
       trajectory = []
       whereas steps < 50:
           state = self.env.get_state()
           if use_skills and self.skill_library.expertise:
               query_emb = self.create_embedding(state, [])
               expertise = self.skill_library.retrieve_skills(state, query_emb, top_k=1)
               if expertise:
                   skill_traj, skill_reward, success = self.execute_skill(expertise[0])
                   trajectory.lengthen(skill_traj)
                   total_reward += skill_reward
                   steps += len(skill_traj)
                   if success:
                       return trajectory, total_reward, steps, True
                   proceed
           motion = self._choose_exploration_action(state)
           next_state, reward, executed = self.env.step(motion)
           trajectory.append((state, motion, reward))
           total_reward += reward
           steps += 1
           if executed:
               return trajectory, total_reward, steps, True
       return trajectory, total_reward, steps, False
  
   def practice(self, episodes=10):
       stats = {"rewards": [], "steps": [], "skills_learned": [], "skill_uses": []}
       for ep in vary(episodes):
           trajectory, reward, steps, success = self.run_episode(use_skills=True)
           if success and len(trajectory) >= 3:
               phase = trajectory[-min(5, len(trajectory)):]
               ability = self.extract_skill(phase)
               if ability:
                   self.skill_library.add_skill(ability)
           stats["rewards"].append(reward)
           stats["steps"].append(steps)
           stats["skills_learned"].append(len(self.skill_library.expertise))
           stats["skill_uses"].append(self.skill_library.get_stats()["total_uses"])
           print(f"Episode {ep+1}: Reward={reward:.1f}, Steps={steps}, Skills={len(self.skill_library.expertise)}, Success={success}")
       return stats

We outline how the agent chooses between utilizing recognized expertise and exploring with primitive actions. We practice the agent throughout a number of episodes and report the evolution of realized expertise, utilization counts, and success charges. As we look at this half, we observe that ability reuse reduces episode size and improves general rewards. Check out the FULL CODES here.

def visualize_training(stats):
   fig, axes = plt.subplots(2, 2, figsize=(12, 8))
   axes[0, 0].plot(stats["rewards"])
   axes[0, 0].set_title("Episode Rewards")
   axes[0, 1].plot(stats["steps"])
   axes[0, 1].set_title("Steps per Episode")
   axes[1, 0].plot(stats["skills_learned"])
   axes[1, 0].set_title("Skills in Library")
   axes[1, 1].plot(stats["skill_uses"])
   axes[1, 1].set_title("Cumulative Skill Uses")
   plt.tight_layout()
   plt.savefig("skill_learning_stats.png", dpi=150, bbox_inches='tight')
   plt.present()


if __name__ == "__main__":
   print("=== Procedural Memory Agent Demo ===n")
   env = GridWorld(measurement=5)
   agent = ProceduralMemoryAgent(env)
   print("Training agent to be taught reusable expertise...n")
   stats = agent.practice(episodes=15)
   print("n=== Learned Skills ===")
   for ability in agent.skill_library.expertise:
       print(f"{ability.title}: {len(ability.action_sequence)} actions, used {ability.times_used} occasions, {ability.success_count} successes")
   lib_stats = agent.skill_library.get_stats()
   print(f"n=== Library Statistics ===")
   print(f"Total expertise: {lib_stats['total_skills']}")
   print(f"Total ability makes use of: {lib_stats['total_uses']}")
   print(f"Avg success fee: {lib_stats['avg_success_rate']:.2%}")
   visualize_training(stats)
   print("n✓ Skill studying full! Check the visualization above.")

We deliver all the things collectively by operating coaching, printing realized expertise, and plotting behaviour statistics. We visualize the pattern in rewards and how the ability library grows over time. By operating this snippet, we full the lifecycle of procedural reminiscence formation and verify that the agent learns to behave extra intelligently with expertise.

In conclusion, we see how procedural reminiscence emerges naturally when an agent learns to extract expertise from its personal profitable trajectories. We observe how expertise are gained, construction, metadata, embeddings, and utilization patterns, permitting the agent to reuse them effectively in future conditions. Lastly, we respect how even a small setting and easy heuristics lead to significant studying dynamics, giving us a concrete understanding of what it means for an agent to develop reusable inner competencies over time.


Check out the FULL CODES here. Feel free to take a look at our GitHub Page for Tutorials, Codes and Notebooks. Also, be at liberty to comply with us on Twitter and don’t overlook to be a part of our 100k+ ML SubReddit and Subscribe to our Newsletter. Wait! are you on telegram? now you can join us on telegram as well.

The publish A Coding Guide to Build a Procedural Memory Agent That Learns, Stores, Retrieves, and Reuses Skills as Neural Modules Over Time appeared first on MarkTechPost.

Similar Posts