updating main testing function to use configs and seeds, finishing first pass at behavior cloning policy and training loop. not yet tested

This commit is contained in:
Arec
2021-07-21 08:22:08 -07:00
parent 4350cf8cd5
commit 18b8e0c58f
2 changed files with 104 additions and 28 deletions

View File

@@ -1,18 +1,28 @@
import torch import torch
import torch.nn as nn import torch.nn as nn
from torch.utils.data import DataLoader from torch.utils.data import DataLoader, RandomSampler
import pickle import pickle
from torch.utils.tensorboard import SummaryWriter
from src.policies import from src.policies import DeepSetsPolicy
from src.util.transform import SciKitMinMaxScaler
import json5
class BehaviorCloningPolicy(): class BehaviorCloningPolicy():
""" """
Class for (continuous) behavior cloning policy Class for (continuous) behavior cloning policy
""" """
def __init__(self, transforms={}, **kwargs): def __init__(self, config: dict transforms: dict={}):
"""
Initialize BehaviorCloningPolicy
Args:
config (dict): configuration file to initialize DeepSetsPolicy with
transforms (dict): dictionary of transforms to apply to different fields
"""
self._config = config
self._transforms = transforms self._transforms = transforms
self._policy_model = PolicyModel(**kwargs) self._policy_model = DeepSetsPolicy(config["ego_state"], config["deepsets"], config["path_encoder"], config["head"])
@property @property
def transforms(self): def transforms(self):
@@ -35,31 +45,38 @@ class BehaviorCloningPolicy():
# run observation through transforms # run observation through transforms
for key in ['state', 'relative_state', 'path_x', 'path_y']: for key in ['state', 'relative_state', 'path_x', 'path_y']:
if key in self._transforms.keys(): if key in self._transforms.keys():
ob[key] = self._transforms[key](ob[key]) ob[key] = self._transforms[key].transform(ob[key])
# run transformed state through model # run transformed state through model
action = self._policy_model(ob)
assert action.ndim == 2, 'action has incorrect shape'
# untransform action # untransform action
if 'action' in self._transforms.keys():
pass action = self._transforms['action'].inverse_transform(action)
return action
@classmethod @classmethod
def load_model(cls, filestr, **kwargs): def load_model(cls, config: dict, filestr: str):
""" """
Load a model from a file prefix Load a model from a file prefix
Args: Args:
config (dict): configuration dict to set up model
filestr (str): string prefix to load model from filestr (str): string prefix to load model from
Returns Returns
model (BehaviorCloningPolicy): loaded model model (BehaviorCloningPolicy): loaded model
""" """
transforms = pickle.load(open(filestr+'_transforms.pkl', 'rb')) transforms = pickle.load(open(filestr+'_transforms.pkl', 'rb'))
model = cls(transforms=transforms, **kwargs) model = cls(config, transforms=transforms)
model._policy_model.load_state_dict(torch.load(filestr+'_model.pt')) model._policy_model.load_state_dict(torch.load(filestr+'_model.pt'))
return model return model
def eval(self): def eval(self):
self._policy_model.eval() self._policy_model.eval()
def parameters(self):
return self._policy_model.parameters()
def save_model(self, filestr): def save_model(self, filestr):
""" """
Save transforms and state_dict to a location specificed by filestr Save transforms and state_dict to a location specificed by filestr
@@ -72,41 +89,72 @@ class BehaviorCloningPolicy():
def generate_transforms(dataset): def generate_transforms(dataset):
""" """
Generate transform dictionary from dataset Generate transform dictionary from dataset
Args:
dataset (Dataset): dataset of demo observations and actions
""" """
pass transforms = {
'action': SciKitMinMaxScaler()
'state': SciKitMinMaxScaler()
'relative_state': SciKitMinMaxScaler(reduce_dim=2)
'path_x': SciKitMinMaxScaler(reduce_dim=2)
'path_y': SciKitMinMaxScaler(reduce_dim=2)
}
for key in transforms.keys():
transforms[key].fit(dataset[:][key])
def train(train_dataset, cv_dataset, policy_class, filestr=filestr, **kwargs): return transforms
def train(train_dataset, cv_dataset, policy, filestr, **kwargs):
# hyperparams # hyperparams
train_epochs = 10000 train_epochs = 10000
cv_every = 100 cv_every = 100
train_batch_size = 64 train_batch_size = 64
cv_batch_size = 256 cv_batch_size = 256 # doesn't matter
learning_rate = 1e-3
weight_decay=0.1
# generate transform from train_dataset # generate transform from train_dataset
transforms = generate_transforms(train_dataset) transforms = generate_transforms(train_dataset)
# initialize policy # initialize policy
policy = BehaviorCloningPolicy(transforms=transforms, **kwargs) policy.transforms = transforms
# training and testing dataloaders # training and testing dataloaders
training_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True) training_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True)
cv_loader = DataLoader(cv_dataset, batch_size=cv_batch_size, shuffle=True) cv_loader = DataLoader(cv_dataset, batch_size=cv_batch_size, shuffle=True)
# generate loss function, optimizer # generate loss function, optimizer
loss_fn = nn.HuberLoss(reduction='sum')
optimizer = torch.optim.Adam(policy.parameters(), lr=learning_rate, weight_decay=weight_decay)
for i in train_epochs: for i in train_epochs:
epoch_loss = 0
for (batch_idx, batch) in enumerate(training_loader):
# sample mini-batch and run through policy # sample mini-batch and run through policy
pred_action = policy(batch) pred_action = policy(batch)
loss = loss_fn(pred_action, batch['action'])
# compute loss and step optimizer # compute loss and step optimizer
optimizer.zero_grad()
loss.backwards() loss.backwards()
optimizer.step() optimizer.step()
epoch_loss += loss.item() / len(train_dataset)
# measure L2 on every cv epoch # Write epoch loss
if i % 10 == 0:
print('Epoch: {}, Training Loss: {}'.format(i, epoch_loss))
# measure cv loss
if i % cv_every == 0: if i % cv_every == 0:
pass with torch.no_grad():
cv_loss = 0.
for (batch_idx, batch) in enumerate(cv_loader):
pred_action = policy(batch)
loss = loss_fn(pred_action, batch['action'])
cv_loss += loss.item() / len(cv_dataset)
print('Epoch: {}, CV Loss: {}'.format(i, cv_loss))
policy.save_model(filestr) policy.save_model(filestr)

View File

@@ -2,8 +2,11 @@ import torch
import gym import gym
import intersim import intersim
import numpy as np import numpy as np
import json5
import os import os
opj = os.path.join opj = os.path.join
from src import InteractionDatasetSingleAgent, metrics from src import InteractionDatasetSingleAgent, metrics
def basestr(**kwargs): def basestr(**kwargs):
@@ -16,7 +19,7 @@ def basestr(**kwargs):
""" """
return 'base_' return 'base_'
def main(method='bc', train=False, test=False, loc=0, **kwargs): def main(method='bc', train=False, test=False, loc=0, config_path=None, **kwargs):
""" """
Main loop for training and testing different imitation models Main loop for training and testing different imitation models
Args: Args:
@@ -24,13 +27,26 @@ def main(method='bc', train=False, test=False, loc=0, **kwargs):
test (bool): whether to run test loop test (bool): whether to run test loop
method (str): the method to try for imitation method (str): the method to try for imitation
loc (int): the location index of the roundabout loc (int): the location index of the roundabout
kwargs (dict): remaining kwargs for policy and training loop config_file (str): path to config file
kwargs (dict): remaining kwargs for training loop
""" """
# get/set seed
seed = kwargs.get('seed',0)
torch.manual_seed(seed)
# make prefix of output files
outdir = opj('output',method,'loc%02i'%(loc)) outdir = opj('output',method,'loc%02i'%(loc))
if not os.path.isdir(outdir): if not os.path.isdir(outdir):
os.mkdir(outdir) os.mkdir(outdir)
filestr = opj(outdir, basestr(**kwargs)) filestr = opj(outdir, basestr(**kwargs))
# load config
if config_path:
with open(config_path, 'r') as cfg:
config = json5.load(cfg)
else:
raise Exception('No config path specified')
# method-based training # method-based training
if method=='bc': if method=='bc':
from src import bc from src import bc
@@ -41,14 +57,17 @@ def main(method='bc', train=False, test=False, loc=0, **kwargs):
# default train / cv / test split datasets # default train / cv / test split datasets
if train: if train:
# make policy, train and test datasets, and send to
policy = policy_class(config)
train_dataset = InteractionDatasetSingleAgent(loc=loc, tracks=[0,1,2]) train_dataset = InteractionDatasetSingleAgent(loc=loc, tracks=[0,1,2])
cv_dataset = InteractionDatasetSingleAgent(loc=loc, tracks=[3]) cv_dataset = InteractionDatasetSingleAgent(loc=loc, tracks=[3])
train_fn(train_dataset, cv_dataset, policy_class, filestr=filestr, **kwargs) train_fn(train_dataset, cv_dataset, policy, filestr, **kwargs)
if test: if test:
# load policy # load policy
policy = policy_class.load_model(filestr=filestr, **kwargs) policy = policy_class.load_model(config, filestr)
policy.eval() policy.eval()
# simulate policy # simulate policy
@@ -77,6 +96,7 @@ def simulate_policy(policy, loc=0, track=0, filestr=''):
env.render() env.render()
done = False done = False
while not done: while not done:
# get action # get action
action = policy(ob) action = policy(ob)
@@ -95,6 +115,8 @@ def parse_args():
test (bool): whether to run test loop test (bool): whether to run test loop
method (str): the method to try for imitation method (str): the method to try for imitation
loc (int): the location index of the roundabout loc (int): the location index of the roundabout
config (str): config path
seed (int): RNG seed
""" """
import argparse import argparse
parser = argparse.ArgumentParser(description='Save Expert Trajectories') parser = argparse.ArgumentParser(description='Save Expert Trajectories')
@@ -106,6 +128,10 @@ def parse_args():
action="store_true") action="store_true")
parser.add_argument("--method", help="modeling method", parser.add_argument("--method", help="modeling method",
choices=['bc', 'gail', 'advil'], default='bc') choices=['bc', 'gail', 'advil'], default='bc')
parser.add_argument("--config", help="config file path",
default='config/networks.json5', type=str)
parser.add_argument('--seed', default=0, type=int,
help='seed')
parser.add_argument() parser.add_argument()
parser.add_argument() parser.add_argument()
args = parser.parse_args() args = parser.parse_args()
@@ -113,7 +139,9 @@ def parse_args():
'train'=args.train, 'train'=args.train,
'test'=args.test, 'test'=args.test,
'method'=args.method, 'method'=args.method,
'loc'=args.loc 'loc'=args.loc,
'config'=args.config,
'seed'=args.seed
} }
return kwargs return kwargs