Skip to content

Commit

Permalink
update code and readme
Browse files Browse the repository at this point in the history
  • Loading branch information
RoyalSkye committed May 31, 2023
1 parent 8db4b45 commit f08d740
Show file tree
Hide file tree
Showing 15 changed files with 407 additions and 25 deletions.
Binary file removed EAS/pretrained/CVRP/maml_fomaml_batch.pt
Binary file not shown.
Binary file removed EAS/pretrained/CVRP/pomo_batch.pt
Binary file not shown.
Binary file removed EAS/pretrained/CVRP/reptile_instance.pt
Binary file not shown.
Binary file removed EAS/pretrained/TSP/maml_fomaml_batch.pt
Binary file not shown.
Binary file removed EAS/pretrained/TSP/pomo_batch.pt
Binary file not shown.
Binary file removed EAS/pretrained/TSP/reptile_instance.pt
Binary file not shown.
4 changes: 2 additions & 2 deletions EAS/run_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def get_config():

parser.add_argument('-problem', default="TSP", type=str, choices=['TSP', 'CVRP'])
parser.add_argument('-method', default="eas-emb", type=str, choices=['eas-emb', 'eas-lay', 'eas-tab'], help="EAS method")
parser.add_argument('-model_path', default="../pretrained/pomo_pretrained/checkpoint-30500.pt", type=str, help="Path of the trained model weights")
parser.add_argument('-model_path', default="../pretrained/POMO-TSP/checkpoint-3000-tsp100-instance-norm.pt", type=str, help="Path of the trained model weights")
parser.add_argument('-instances_path', default="../data/TSP/Size_Distribution/tsp200_rotation.pkl", type=str, help="Path of the instances")
parser.add_argument('-sol_path', default="../data/TSP/Size_Distribution/concorde/tsp200_rotationoffset0n1000-concorde.pkl", type=str, help="Path of the optimal sol")
parser.add_argument('-num_instances', default=1000, type=int, help="Maximum number of instances that should be solved")
Expand All @@ -46,7 +46,7 @@ def get_config():
parser.add_argument('-batch_size', default=150, type=int) # Set to 1 for single instance search
parser.add_argument('-p_runs', default=1, type=int) # If batch_size is 1, set this to > 1 to do multiple runs for the instance in parallel
parser.add_argument('-output_path', default="EAS_results", type=str)
parser.add_argument('-norm', default="batch_no_track", choices=['instance', 'batch', 'batch_no_track', 'none'], type=str)
parser.add_argument('-norm', default="instance", choices=['instance', 'batch', 'batch_no_track', 'none'], type=str)
parser.add_argument('-gpu_id', default=0, type=int)
parser.add_argument('-seed', default=2023, type=int, help="random seed")

Expand Down
4 changes: 3 additions & 1 deletion POMO/CVRP/CVRPTrainer_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def __init__(self,
self.meta_model = Model(**self.model_params)
self.meta_optimizer = Optimizer(self.meta_model.parameters(), **self.optimizer_params['optimizer'])
self.alpha = self.meta_params['alpha'] # for reptile
self.early_stop = True if self.meta_params['meta_method'] == 'maml_fomaml' else False
self.task_set = generate_task_set(self.meta_params)
self.val_data, self.val_opt = {}, {} # for lkh3_offline
if self.meta_params["data_type"] == "size_distribution":
Expand Down Expand Up @@ -180,6 +181,7 @@ def _train_one_epoch(self, epoch):
self.meta_optimizer.zero_grad()
score_AM, loss_AM = AverageMeter(), AverageMeter()
meta_batch_size = self.meta_params['meta_batch_size']
self.meta_params['meta_method'] = 'fomaml' if self.early_stop and epoch > self.meta_params['early_stop_epoch'] else "maml"

# Adaptive task scheduler - Not implemented for "size" and "distribution"
if self.meta_params['curriculum']:
Expand Down Expand Up @@ -212,7 +214,7 @@ def _train_one_epoch(self, epoch):
if self.meta_params['meta_method'] in ['fomaml', 'reptile']:
task_model = copy.deepcopy(self.meta_model)
optimizer = Optimizer(task_model.parameters(), **self.optimizer_params['optimizer'])
# optimizer.load_state_dict(self.meta_optimizer.state_dict())
# optimizer.load_state_dict(self.meta_optimizer.state_dict()) # may cause unstable meta-training for fomaml
elif self.meta_params['meta_method'] == 'maml':
if self.model_params['meta_update_encoder']:
fast_weight = OrderedDict(self.meta_model.named_parameters())
Expand Down
3 changes: 2 additions & 1 deletion POMO/CVRP/train.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@
meta_params = {
'enable': True, # whether use meta-learning or not
'curriculum': True, # adaptive sample task
'meta_method': 'maml', # choose from ['maml', 'fomaml', 'reptile']
'meta_method': 'maml', # choose from ['maml', 'fomaml', 'maml_fomaml', 'reptile']
'data_type': 'size_distribution', # choose from ["size", "distribution", "size_distribution"]
'epochs': 250000, # the number of meta-model updates: (250*100000) / (1*5*64)
'early_stop_epoch': 50000, # switch from maml to fomaml
'B': 1, # the number of tasks in a mini-batch
'k': 1, # gradient decent steps in the inner-loop optimization of meta-learning method
'L': 0, # bootstrap steps
Expand Down
4 changes: 3 additions & 1 deletion POMO/TSP/TSPTrainer_Meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def __init__(self,
self.meta_model = Model(**self.model_params)
self.meta_optimizer = Optimizer(self.meta_model.parameters(), **self.optimizer_params['optimizer'])
self.alpha = self.meta_params['alpha'] # for reptile
self.early_stop = True if self.meta_params['meta_method'] == 'maml_fomaml' else False
self.task_set = generate_task_set(self.meta_params)
self.val_data, self.val_opt = {}, {} # for lkh3_offline
if self.meta_params["data_type"] == "size_distribution":
Expand Down Expand Up @@ -181,6 +182,7 @@ def _train_one_epoch(self, epoch):
self.meta_optimizer.zero_grad()
score_AM, loss_AM = AverageMeter(), AverageMeter()
meta_batch_size = self.meta_params['meta_batch_size']
self.meta_params['meta_method'] = 'fomaml' if self.early_stop and epoch > self.meta_params['early_stop_epoch'] else "maml"

# Adaptive task scheduler:
if self.meta_params['curriculum']:
Expand Down Expand Up @@ -213,7 +215,7 @@ def _train_one_epoch(self, epoch):
if self.meta_params['meta_method'] in ['fomaml', 'reptile']:
task_model = copy.deepcopy(self.meta_model)
optimizer = Optimizer(task_model.parameters(), **self.optimizer_params['optimizer'])
# optimizer.load_state_dict(self.meta_optimizer.state_dict())
# optimizer.load_state_dict(self.meta_optimizer.state_dict()) # may cause unstable meta-training for fomaml
elif self.meta_params['meta_method'] == 'maml':
if self.model_params['meta_update_encoder']:
fast_weight = OrderedDict(self.meta_model.named_parameters())
Expand Down
3 changes: 2 additions & 1 deletion POMO/TSP/train.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@
meta_params = {
'enable': True, # whether use meta-learning or not
'curriculum': True, # adaptive sample task
'meta_method': 'maml', # choose from ['maml', 'fomaml', 'reptile']
'meta_method': 'maml', # choose from ['maml', 'fomaml', 'maml_fomaml', 'reptile']
'data_type': 'size_distribution', # choose from ["size", "distribution", "size_distribution"]
'epochs': 250000, # the number of meta-model updates: (250*100000) / (1*5*64)
'early_stop_epoch': 50000, # switch from maml to fomaml
'B': 1, # the number of tasks in a mini-batch
'k': 1, # gradient decent steps in the inner-loop optimization of meta-learning method
'L': 0, # bootstrap steps
Expand Down
89 changes: 89 additions & 0 deletions POMO/call_tspgen.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Rscript call_tspgen.R operator point_lower point_upper ins_num seed

library("netgen")
source("tspgen-master/R/utilities.R")
source("tspgen-master/R/mutator.explosion.R")
source("tspgen-master/R/mutator.implosion.R")
source("tspgen-master/R/mutator.cluster.R")
source("tspgen-master/R/mutator.compression.R")
source("tspgen-master/R/mutator.expansion.R")
source("tspgen-master/R/mutator.grid.R")
source("tspgen-master/R/mutator.linearprojection.R")
source("tspgen-master/R/mutator.rotation.R")

library(ggplot2)
library(gridExtra)

args <- commandArgs(TRUE)
operator <- toString(args[1])
points.num <- as.integer(args[2])
ins.num <- as.integer(args[4])
seed <- as.integer(args[5])
choice <- toString(args[6])
path <- toString(args[7])

#check_internal = ins.num / 5

check_internal = 1

# points.num <- sample(points.lower:points.upper, 1, replace=TRUE)

set.seed(seed)

for (i in 1:ins.num)
{

x = generateRandomNetwork(n.points = points.num, lower = 0, upper = 1)
rue = x
if (operator == "explosion")
{
x$coordinates = doExplosionMutation(x$coordinates, min.eps=0.3, max.eps=0.3)
}
if (operator == "implosion")
{
x$coordinates = doImplosionMutation(x$coordinates, min.eps=0.3, max.eps=0.3)
}
if (operator == "cluster")
{
x$coordinates = doClusterMutation(x$coordinates, pm=0.4)
}
if (operator == "compression")
{
x$coordinates = doCompressionMutation(x$coordinates, min.eps=0.3, max.eps=0.3)
}
if (operator == "expansion")
{
x$coordinates = doExpansionMutation(x$coordinates, min.eps=0.3, max.eps=0.3)
}
if (operator == "grid")
{
x$coordinates = doGridMutation(x$coordinates, box.min=0.3, box.max=0.3, p.rot=0, p.jitter=0, jitter.sd=0.05)
}
if (operator == "linearprojection")
{
x$coordinates = doLinearProjectionMutation(x$coordinates, pm=0.4, p.jitter=0, jitter.sd=0.05)
}
if (operator == "rotation")
{
x$coordinates = doRotationMutation(x$coordinates, pm=0.4)
}

x = rescaleNetwork(x, method = "global2")
# x$coordinates = x$coordinates * 1000000
# x$coordinates = round(x$coordinates, 0)
x$coords = relocateDuplicates(x$coords)
x$lower = 0
x$upper = 1
name = sprintf("data/TSP_test/%s/%d.tsp", operator, i)
# if (i %% check_internal == 0)
# {
# jpeg(file = paste("data/check/",operator,toString(i), ".jpg", sep = ""), width=1200, height=600)
# grid.arrange(autoplot(x), autoplot(rue), nrow = 1L)
# dev.off()
# }

# print(x$coordinates)
cat(x$coordinates, "\n", file=path, append=TRUE)

# exportToTSPlibFormat(x, name, use.extended.format=FALSE)
}
83 changes: 65 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,84 @@ The PyTorch Implementation of *ICML 2023 Poster -- "Towards Omni-generalizable N

This paper studies a challenging yet realistic setting, which considers generalization across both size and distribution (a.k.a. omni-generalization) of neural methods in VRPs. Technically, a general meta-learning framework is developed to tackle it.

### TODO

- [ ] Finish Dependencies & How to Run & Take-home Messages.
- [ ] Camera-ready.
- [ ] Slide and Poster.
- [ ] Release Review.

### Dependencies
### Env Setup

```shell
conda create -n omni_vrp python=3.8
conda activate omni_vrp
# install pytorch (we use V.1.12.1), see here: https://pytorch.org/get-started/previous-versions
conda install pytorch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1 cudatoolkit=11.3 -c pytorch
conda install pytz tqdm scikit-learn matplotlib
# for L2D env setup, see here: https://github.com/mit-wu-lab/learning-to-delegate
```

### How to Run

We take the meta-training on POMO as an example. See [here](https://github.com/RoyalSkye/Omni-VRP/tree/main/L2D) for L2D.

```shell
# 1. training
# a. second-order
nohup python -u train.py 2>&1 &
# b. early-stopping
meta_params['meta_method'] = "maml_fomaml"
# c. first-order
meta_params['meta_method'] = "fomaml"
# 2. testing
# a. zero-shot on datasets (.pkl), including 1K test instances and 1K fine-tuning instances.
nohup python -u test.py 2>&1 &
# b. few-shot on datasets (.pkl)
fine_tune_params['enable'] = True
# c. zero-shot on benchmark instances (.tsp or .vrp)
tester_params['test_set_path'] = "../../data/TSP/tsplib"
# 3. Traditional VRP solvers ["concorde","lkh"] for TSP; ["hgs", "lkh"] for CVRP
nohup python -u TSP_baseline.py --method "lkh" --cpus 32 --no_cuda --disable_cache 2>&1 &
# 4. EAS
# Note: change -instances_path to the folder path if conducting EAS on benchmark instances
# Note: Pls carefully check parameters, e.g., -norm, -round_distances, etc.
nohup python -u run_search.py 2>&1 &
```

### How to Run
#### Options

```shell
# Modify the default value in train.py
# 1. Bootstrapped Meta-Learning - ICLR 2022
meta_params['L'] = X (X > 0)
# 2. ANIL (Almost No Inner Loop) - ICLR 2020
model_params["meta_update_encoder"] = False
# 3. Meta-training on the pretrained model
# Note: The type of normalization layers should match (search for model_params["norm"])
# Supported normalization layers: ["no", "batch", "batch_no_track", "instance", "rezero"]
trainer_params['pretrain_load'] = True
# 4. Resume meta-training
trainer_params['model_load'] = True
# 5. No task scheduler
meta_params['curriculum'] = False
# 6. Baselines
# Note: For AMDKD-POMO, refer to: https://github.com/jieyibi/AMDKD
meta_params['enable'] = False # POMO
meta_params['reptile'] = 'reptile' # Meta-POMO
```

### Discussions

In summary: 1) Normalization layers matter in AM-based models (see [here](https://github.com/RoyalSkye/Omni-VRP/blob/main/POMO/TSP/TSPTrainer_Meta.py#L58)); 2) The training efficiency and scalability heavily depend on the base model and meta-learning algorithm; 3) It may be better to conduct meta-training on the pretrained model. For further discussions, refer to Appendix E.

### Take-home Messages
### Acknowledgments

* We would like to thank the anonymous reviewers and (S)ACs of ICML 2023 for their constructive comments and dedicated service to the community. The reviews and meta-review are available [here](https://github.com/RoyalSkye/Omni-VRP/blob/main/Reviews_ICML23.md).

* We also would like to thank the following repositories, which are baselines of our code:

### Reviews
* https://github.com/wouterkool/attention-learn-to-route

We would like to thank the anonymous reviewers and (S)ACs of ICML 2023 for their constructive comments and recommendation. We will share the reviews later.
* https://github.com/yd-kwon/POMO

### Acknowledgments
* https://github.com/mit-wu-lab/learning-to-delegate

Thank the following repositories, which are baselines of our code:
* "On the generalization of neural combinatorial optimization heuristics"

* https://github.com/wouterkool/attention-learn-to-route
* https://github.com/yd-kwon/POMO
* https://github.com/mit-wu-lab/learning-to-delegate
* "On the generalization of neural combinatorial optimization heuristics"

### Citation

Expand Down
Loading

0 comments on commit f08d740

Please sign in to comment.