diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bfbd0a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +.DS_Store +*.pkl +*.npy +*.pt +*.pyc +**/__*__/** + + +*.log +.vscode/ +__pycache__/ +*.pyc +*.pt +*.model +*.dgl +*.txt +*results/ +*.npz +*.npy +*.sh +*.log +*.cmd +data/* +*.pdf +*.png +*.json + +.idea/ diff --git a/ColoredMNIST/epo_lp.py b/ColoredMNIST/epo_lp.py new file mode 100644 index 0000000..d92abe1 --- /dev/null +++ b/ColoredMNIST/epo_lp.py @@ -0,0 +1,120 @@ +import numpy as np +import cvxpy as cp +import cvxopt + +from scipy.special import softmax +class EPO_LP(object): + + def __init__(self, m, n, r, eps=1e-4, softmax_norm=False): + # self.solver = cp.GLPK + self.solver = cp.GUROBI + # cvxopt.glpk.options["msg_lev"] = "GLP_MSG_OFF" + self.m = m + self.n = n + self.r = r + self.eps = eps + self.last_move = None + self.a = cp.Parameter(m) # Adjustments + self.C = cp.Parameter((m, m)) # C: Gradient inner products, G^T G + self.Ca = cp.Parameter(m) # d_bal^TG + self.rhs = cp.Parameter(m) # RHS of constraints for balancing + + self.alpha = cp.Variable(m) # Variable to optimize + + obj_bal = cp.Maximize(self.alpha @ self.Ca) # objective for balance + constraints_bal = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Simplex + self.C @ self.alpha >= self.rhs] + self.prob_bal = cp.Problem(obj_bal, constraints_bal) # LP balance + + obj_dom = cp.Maximize(cp.sum(self.alpha @ self.C)) # obj for descent + constraints_res = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Restrict + self.alpha @ self.Ca >= -cp.neg(cp.max(self.Ca)), + self.C @ self.alpha >= 0] + constraints_rel = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Relaxed + self.C @ self.alpha >= 0] + self.prob_dom = cp.Problem(obj_dom, constraints_res) # LP dominance + self.prob_rel = cp.Problem(obj_dom, constraints_rel) # LP dominance + + self.gamma = 0 # Stores the latest Optimum value of the LP problem + self.mu_rl = 0 # Stores the latest non-uniformity + + self.softmax_norm = softmax_norm # use which normalization to calc. non-uniformity + + def get_alpha(self, l, G, r=None, C=False, relax=False): + r = self.r if r is None else r + assert len(l) == len(G) == len(r) == self.m, "length != m" + + if self.softmax_norm: + r = np.exp(r) + l = np.exp(l) + rl, self.mu_rl, self.a.value = self.adjustments(l, r) + + self.C.value = G if C else G @ G.T + self.Ca.value = self.C.value @ self.a.value + + if self.mu_rl > self.eps: + J = self.Ca.value > 0 + # if len(np.where(J)[0]) > 0: + if True: + J_star_idx = np.where(rl == np.max(rl))[0] + self.rhs.value = self.Ca.value.copy() + self.rhs.value[J] = -np.inf # Not efficient; but works. + self.rhs.value[J_star_idx] = 0 + else: + self.rhs.value = np.zeros_like(self.Ca.value) + self.gamma = self.prob_bal.solve(solver=self.solver, verbose=False,reoptimize=True) + self.last_move = "bal" + else: + if relax: + self.gamma = self.prob_rel.solve(solver=self.solver, verbose=False,reoptimize=True) + else: + self.gamma = self.prob_dom.solve(solver=self.solver, verbose=False,reoptimize=True) + self.last_move = "dom" + return self.alpha.value + + + def mu(self, rl, normed=False): + if len(np.where(rl < 0)[0]): + raise ValueError(f"rl<0 \n rl={rl}") + return None + m = len(rl) + if normed: + # if self.softmax_norm: + # l_hat = softmax(rl) + # else: + l_hat = rl/rl.sum() + # l_hat = rl if normed else rl / rl.sum() + eps = np.finfo(rl.dtype).eps + l_hat = l_hat[l_hat > eps] + return np.sum(l_hat * np.log(l_hat * m)) + + + def adjustments(self, l, r=1): + m = len(l) + rl = r * l + # if self.softmax_norm: + # l_hat = softmax(rl) + # else: + # l_hat = rl / rl.sum() + # rl = np.exp(rl) if self.softmax_norm else rl + l_hat = rl/rl.sum() + # print(l_hat[0]/l_hat[2]) + mu_rl = self.mu(l_hat, normed=True) + a = r * (np.log(l_hat * m) - mu_rl) + return rl, mu_rl, a + +# def get_param_dim(model): +# for param in model.parameters(): +# if param.grad is not None: +# cur_grad.append(Variable(param.data.clone().flatten(), requires_grad=False)) +# grads.append(torch.cat(cur_grad)) + + +def getNumParams(params): + numParams, numTrainable = 0, 0 + for param in params: + npParamCount = np.prod(param.data.shape) + numParams += npParamCount + if param.requires_grad: + numTrainable += npParamCount + return numParams, numTrainable diff --git a/ColoredMNIST/misc.py b/ColoredMNIST/misc.py new file mode 100644 index 0000000..e8223b9 --- /dev/null +++ b/ColoredMNIST/misc.py @@ -0,0 +1,400 @@ +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved + +""" +Things that don't belong anywhere else +""" + +import hashlib +import json +import os +import sys +from shutil import copyfile +from collections import OrderedDict +from numbers import Number +import operator + +import numpy as np +import torch +import tqdm +from collections import Counter + +def make_weights_for_balanced_classes(dataset): + counts = Counter() + classes = [] + for _, y in dataset: + y = int(y) + counts[y] += 1 + classes.append(y) + + n_classes = len(counts) + + weight_per_class = {} + for y in counts: + weight_per_class[y] = 1 / (counts[y] * n_classes) + + weights = torch.zeros(len(dataset)) + for i, y in enumerate(classes): + weights[i] = weight_per_class[int(y)] + + return weights + +def pdb(): + sys.stdout = sys.__stdout__ + import pdb + print("Launching PDB, enter 'n' to step to parent function.") + pdb.set_trace() + +def seed_hash(*args): + """ + Derive an integer hash from all args, for use as a random seed. + """ + args_str = str(args) + return int(hashlib.md5(args_str.encode("utf-8")).hexdigest(), 16) % (2**31) + +def print_separator(): + print("="*80) + +def print_row(row, colwidth=10, latex=False): + if latex: + sep = " & " + end_ = "\\\\" + else: + sep = " " + end_ = "" + + def format_val(x): + if np.issubdtype(type(x), np.floating): + x = "{:.10f}".format(x) + return str(x).ljust(colwidth)[:colwidth] + print(sep.join([format_val(x) for x in row]), end_) + +class _SplitDataset(torch.utils.data.Dataset): + """Used by split_dataset""" + def __init__(self, underlying_dataset, keys): + super(_SplitDataset, self).__init__() + self.underlying_dataset = underlying_dataset + self.keys = keys + def __getitem__(self, key): + return self.underlying_dataset[self.keys[key]] + def __len__(self): + return len(self.keys) + +def split_dataset(dataset, n, seed=0): + """ + Return a pair of datasets corresponding to a random split of the given + dataset, with n datapoints in the first dataset and the rest in the last, + using the given random seed + """ + assert(n <= len(dataset)) + keys = list(range(len(dataset))) + np.random.RandomState(seed).shuffle(keys) + keys_1 = keys[:n] + keys_2 = keys[n:] + return _SplitDataset(dataset, keys_1), _SplitDataset(dataset, keys_2) + +def random_pairs_of_minibatches(minibatches): + perm = torch.randperm(len(minibatches)).tolist() + pairs = [] + + for i in range(len(minibatches)): + j = i + 1 if i < (len(minibatches) - 1) else 0 + + xi, yi = minibatches[perm[i]][0], minibatches[perm[i]][1] + xj, yj = minibatches[perm[j]][0], minibatches[perm[j]][1] + + min_n = min(len(xi), len(xj)) + + pairs.append(((xi[:min_n], yi[:min_n]), (xj[:min_n], yj[:min_n]))) + + return pairs + +def accuracy(network, loader, weights, device): + correct = 0 + total = 0 + weights_offset = 0 + + network.eval() + with torch.no_grad(): + for x, y in loader: + x = x.to(device) + y = y.to(device) + p = network.predict(x) + #print() + #print(p) + if weights is None: + batch_weights = torch.ones(len(x)) + else: + batch_weights = weights[weights_offset : weights_offset + len(x)] + weights_offset += len(x) + batch_weights = batch_weights.to(device) + if p.size(1) == 1: + #print(p.flatten().gt(0).eq(y).float()) + #print(p.flatten().gt(0).eq(y).float().sum().item()) + correct += (p.flatten().gt(0).eq(y).float() * batch_weights.flatten()).sum().item() + else: + correct += (p.argmax(1).eq(y).float() * batch_weights).sum().item() + total += batch_weights.sum().item() + #print(correct,total) + #0/0 + network.train() + + return correct / total + +class Tee: + def __init__(self, fname, mode="a"): + self.stdout = sys.stdout + self.file = open(fname, mode) + + def write(self, message): + self.stdout.write(message) + self.file.write(message) + self.flush() + + def flush(self): + self.stdout.flush() + self.file.flush() + +class ParamDict(OrderedDict): + """Code adapted from https://github.com/Alok/rl_implementations/tree/master/reptile. + A dictionary where the values are Tensors, meant to represent weights of + a model. This subclass lets you perform arithmetic on weights directly.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, *kwargs) + + def _prototype(self, other, op): + if isinstance(other, Number): + return ParamDict({k: op(v, other) for k, v in self.items()}) + elif isinstance(other, dict): + return ParamDict({k: op(self[k], other[k]) for k in self}) + else: + raise NotImplementedError + + def __add__(self, other): + return self._prototype(other, operator.add) + + def __rmul__(self, other): + return self._prototype(other, operator.mul) + + __mul__ = __rmul__ + + def __neg__(self): + return ParamDict({k: -v for k, v in self.items()}) + + def __rsub__(self, other): + # a- b := a + (-b) + return self.__add__(other.__neg__()) + + __sub__ = __rsub__ + + def __truediv__(self, other): + return self._prototype(other, operator.truediv) + + + + +def l2_between_dicts(dict_1, dict_2): + assert len(dict_1) == len(dict_2) + dict_1_values = [dict_1[key] for key in sorted(dict_1.keys())] + dict_2_values = [dict_2[key] for key in sorted(dict_1.keys())] + return ( + torch.cat(tuple([t.view(-1) for t in dict_1_values])) - + torch.cat(tuple([t.view(-1) for t in dict_2_values])) + ).pow(2).mean() + +class MovingAverage: + + def __init__(self, ema, oneminusema_correction=True): + self.ema = ema + self.ema_data = {} + self._updates = 0 + self._oneminusema_correction = oneminusema_correction + + def update(self, dict_data): + ema_dict_data = {} + for name, data in dict_data.items(): + data = data.view(1, -1) + if self._updates == 0: + previous_data = torch.zeros_like(data) + else: + previous_data = self.ema_data[name] + + ema_data = self.ema * previous_data + (1 - self.ema) * data + if self._oneminusema_correction: + # correction by 1/(1 - self.ema) + # so that the gradients amplitude backpropagated in data is independent of self.ema + ema_dict_data[name] = ema_data / (1 - self.ema) + else: + ema_dict_data[name] = ema_data + self.ema_data[name] = ema_data.clone().detach() + + self._updates += 1 + return ema_dict_data + + + +def make_weights_for_balanced_classes(dataset): + counts = Counter() + classes = [] + for _, y in dataset: + y = int(y) + counts[y] += 1 + classes.append(y) + + n_classes = len(counts) + + weight_per_class = {} + for y in counts: + weight_per_class[y] = 1 / (counts[y] * n_classes) + + weights = torch.zeros(len(dataset)) + for i, y in enumerate(classes): + weights[i] = weight_per_class[int(y)] + + return weights + +def pdb(): + sys.stdout = sys.__stdout__ + import pdb + print("Launching PDB, enter 'n' to step to parent function.") + pdb.set_trace() + +def seed_hash(*args): + """ + Derive an integer hash from all args, for use as a random seed. + """ + args_str = str(args) + return int(hashlib.md5(args_str.encode("utf-8")).hexdigest(), 16) % (2**31) + +def print_separator(): + print("="*80) + +def print_row(row, colwidth=10, latex=False): + if latex: + sep = " & " + end_ = "\\\\" + else: + sep = " " + end_ = "" + + def format_val(x): + if np.issubdtype(type(x), np.floating): + x = "{:.10f}".format(x) + return str(x).ljust(colwidth)[:colwidth] + print(sep.join([format_val(x) for x in row]), end_) + +class _SplitDataset(torch.utils.data.Dataset): + """Used by split_dataset""" + def __init__(self, underlying_dataset, keys): + super(_SplitDataset, self).__init__() + self.underlying_dataset = underlying_dataset + self.keys = keys + def __getitem__(self, key): + return self.underlying_dataset[self.keys[key]] + def __len__(self): + return len(self.keys) + +def split_dataset(dataset, n, seed=0): + """ + Return a pair of datasets corresponding to a random split of the given + dataset, with n datapoints in the first dataset and the rest in the last, + using the given random seed + """ + assert(n <= len(dataset)) + keys = list(range(len(dataset))) + np.random.RandomState(seed).shuffle(keys) + keys_1 = keys[:n] + keys_2 = keys[n:] + return _SplitDataset(dataset, keys_1), _SplitDataset(dataset, keys_2) + +def random_pairs_of_minibatches(minibatches): + perm = torch.randperm(len(minibatches)).tolist() + pairs = [] + + for i in range(len(minibatches)): + j = i + 1 if i < (len(minibatches) - 1) else 0 + + xi, yi = minibatches[perm[i]][0], minibatches[perm[i]][1] + xj, yj = minibatches[perm[j]][0], minibatches[perm[j]][1] + + min_n = min(len(xi), len(xj)) + + pairs.append(((xi[:min_n], yi[:min_n]), (xj[:min_n], yj[:min_n]))) + + return pairs + +def accuracy(network, loader, weights, device): + correct = 0 + total = 0 + weights_offset = 0 + + network.eval() + with torch.no_grad(): + for x, y in loader: + x = x.to(device) + y = y.to(device) + p = network.predict(x) + if weights is None: + batch_weights = torch.ones(len(x)) + else: + batch_weights = weights[weights_offset : weights_offset + len(x)] + weights_offset += len(x) + batch_weights = batch_weights.to(device) + if p.size(1) == 1: + correct += (p.gt(0).eq(y).float() * batch_weights.view(-1, 1)).sum().item() + else: + correct += (p.argmax(1).eq(y).float() * batch_weights).sum().item() + total += batch_weights.sum().item() + network.train() + + return correct / total + +class Tee: + def __init__(self, fname, mode="a"): + self.stdout = sys.stdout + self.file = open(fname, mode) + + def write(self, message): + self.stdout.write(message) + self.file.write(message) + self.flush() + + def flush(self): + self.stdout.flush() + self.file.flush() + +class ParamDict(OrderedDict): + """Code adapted from https://github.com/Alok/rl_implementations/tree/master/reptile. + A dictionary where the values are Tensors, meant to represent weights of + a model. This subclass lets you perform arithmetic on weights directly.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, *kwargs) + + def _prototype(self, other, op): + if isinstance(other, Number): + return ParamDict({k: op(v, other) for k, v in self.items()}) + elif isinstance(other, dict): + return ParamDict({k: op(self[k], other[k]) for k in self}) + else: + raise NotImplementedError + + def __add__(self, other): + return self._prototype(other, operator.add) + + def __rmul__(self, other): + return self._prototype(other, operator.mul) + + __mul__ = __rmul__ + + def __neg__(self): + return ParamDict({k: -v for k, v in self.items()}) + + def __rsub__(self, other): + # a- b := a + (-b) + return self.__add__(other.__neg__()) + + __sub__ = __rsub__ + + def __truediv__(self, other): + return self._prototype(other, operator.truediv) diff --git a/ColoredMNIST/models.py b/ColoredMNIST/models.py new file mode 100644 index 0000000..2239175 --- /dev/null +++ b/ColoredMNIST/models.py @@ -0,0 +1,83 @@ +import torch +from torchvision import datasets +from torch import nn, optim, autograd +import torchvision +from backpack import backpack, extend +from backpack.extensions import BatchGrad + + +class Net(nn.Module): + def __init__(self, mlp,topmlp): + super(Net, self).__init__() + self.net = nn.Sequential(mlp,topmlp) + def forward(self,data): + return self.net(data) +# Define and instantiate the model +class Linear(nn.Module): + def __init__(self, hidden_dim=1, input_dim=2*14*14): + super(Linear, self).__init__() + + self.input_dim = input_dim + + lin1 = nn.Linear(self.input_dim, hidden_dim) + + nn.init.xavier_uniform_(lin1.weight) + nn.init.zeros_(lin1.bias) + + self._main = lin1 + def forward(self,input): + out = input.view(input.shape[0], self.input_dim) + out = self._main(out) + return out + + +class MLP(nn.Module): + def __init__(self, hidden_dim=390, input_dim=2*14*14): + super(MLP, self).__init__() + + self.input_dim = input_dim + + lin1 = nn.Linear(self.input_dim, hidden_dim) + lin2 = nn.Linear(hidden_dim, hidden_dim) + + nn.init.xavier_uniform_(lin1.weight) + nn.init.zeros_(lin1.bias) + nn.init.xavier_uniform_(lin2.weight) + nn.init.zeros_(lin2.bias) + + self._main = nn.Sequential(lin1, nn.ReLU(True), lin2, nn.ReLU(True)) + + def forward(self, input): + out = input.view(input.shape[0], self.input_dim) + out = self._main(out) + return out + + +class TopMLP(nn.Module): + def __init__(self, hidden_dim=390, n_top_layers=1, n_targets=1, fishr=False): + + super(TopMLP, self).__init__() + + if fishr: + self.lin1 = lin1 = extend(nn.Linear(hidden_dim,n_targets)) + else: + self.lin1 = lin1 = nn.Linear(hidden_dim,n_targets) + nn.init.xavier_uniform_(lin1.weight) + nn.init.zeros_(lin1.bias) + self._main = nn.Sequential(lin1) + self.weights = [lin1.weight, lin1.bias] + + def forward(self,input): + out = self._main(input) + return out + + + +# # from https://github.com/facebookresearch/DomainBed/tree/master/domainbed +class Identity(nn.Module): + """An identity layer""" + def __init__(self): + super(Identity, self).__init__() + + def forward(self, x): + return x diff --git a/ColoredMNIST/mydatasets.py b/ColoredMNIST/mydatasets.py new file mode 100644 index 0000000..c075de5 --- /dev/null +++ b/ColoredMNIST/mydatasets.py @@ -0,0 +1,71 @@ + +import numpy as np +import torch +from torchvision import datasets +import math +import os +import torch +from PIL import Image, ImageFile +from torchvision import transforms +import torchvision.datasets.folder +from torch.utils.data import TensorDataset, Subset +from torchvision.datasets import MNIST, ImageFolder +from torchvision.transforms.functional import rotate + + +from misc import split_dataset,make_weights_for_balanced_classes,seed_hash +# from fast_data_loader import InfiniteDataLoader, FastDataLoader + + +#coloredmnist are modified from https://github.com/facebookresearch/InvariantRiskMinimization +def coloredmnist(label_noise_rate, trenv1, trenv2, int_target=False): + # Load MNIST, make train/val splits, and shuffle train set examples + mnist = datasets.MNIST('./data/MNIST', train=True, download=True) + mnist_train = (mnist.data[:50000], mnist.targets[:50000]) + mnist_val = (mnist.data[50000:], mnist.targets[50000:]) + + rng_state = np.random.get_state() + np.random.shuffle(mnist_train[0].numpy()) + np.random.set_state(rng_state) + np.random.shuffle(mnist_train[1].numpy()) + + # Build environments + def make_environment(images, labels, e): + def torch_bernoulli(p, size): + return (torch.rand(size) < p).float() + def torch_xor(a, b): + return (a-b).abs() # Assumes both inputs are either 0 or 1 + # 2x subsample for computational convenience + images = images.reshape((-1, 28, 28))[:, ::2, ::2] + # Assign a binary label based on the digit; flip label with probability 0.25 + labels = (labels < 5).float() + labels = torch_xor(labels, torch_bernoulli(label_noise_rate, len(labels))) + # Assign a color based on the label; flip the color with probability e + colors = torch_xor(labels, torch_bernoulli(e, len(labels))) + # Apply the color to the image by zeroing out the other color channel + images = torch.stack([images, images], dim=1) + images[torch.tensor(range(len(images))), (1-colors).long(), :, :] *= 0 + + if int_target: + return { + 'images': (images.float() / 255.).cuda(), + 'labels': labels[:, None].long().flatten().cuda() + } + else: + return { + 'images': (images.float() / 255.).cuda(), + 'labels': labels[:, None].cuda() + } + + + envs = [ + make_environment(mnist_train[0][::2], mnist_train[1][::2], trenv1), + make_environment(mnist_train[0][1::2], mnist_train[1][1::2], trenv2)] + + # init 3 test environments [0.1, 0.5, 0.9] + test_envs = [ + make_environment(mnist_val[0], mnist_val[1], 0.9), + make_environment(mnist_val[0], mnist_val[1], 0.1), + make_environment(mnist_val[0], mnist_val[1], 0.5), + ] + return envs, test_envs diff --git a/ColoredMNIST/pair.py b/ColoredMNIST/pair.py new file mode 100644 index 0000000..947f69d --- /dev/null +++ b/ColoredMNIST/pair.py @@ -0,0 +1,452 @@ +import copy +import imp +from pickletools import optimize +import torch +from torch.optim.optimizer import Optimizer, required +from torch.autograd import Variable +import traceback +import torch.nn.functional as F +from torch.optim import SGD + +class PAIR(Optimizer): + r""" + Implements Pareto Invariant Risk Minimization (PAIR) algorithm. + It is proposed in the ICLR 2023 paper + `Pareto Invariant Risk Minimization: Towards Mitigating the Optimization Dilemma in Out-of-Distribution Generalization` + https://arxiv.org/abs/2206.07766 . + + Arguments: + params (iterable): iterable of parameters to optimize or dicts defining parameter groups + optimizer (pytorch optim): inner optimizer + balancer (str, optional): indicates which MOO solver to use + preference (list[float], optional): preference of the objectives + eps (float, optional): precision up to the preference (default: 1e-04) + coe (float, optional): L2 regularization weight onto the yielded objective weights (default: 0) + """ + + def __init__(self, params, optimizer=required, balancer="EPO",preference=[1e-8,1-1e-8], eps=1e-4, coe=0, verbose=False): + # TODO: parameter validty checking + if eps < 0.0: + raise ValueError("Invalid epsilon value: {}".format(eps)) + for _pp in preference: + if _pp < 0.0: + raise ValueError("Invalid preference: {}".format(preference)) + + self.optimizer = optimizer + if type(preference) == list: + preference = np.array(preference) + self.preference = preference + + self.descent = 0 + self.losses = [] + self.params = params + if balancer.lower() == "epo": + self.balancer = EPO(len(self.preference),self.preference,eps=eps,coe=coe,verbose=verbose) + elif balancer.lower() == "sepo": + self.balancer = SEPO(len(self.preference),self.preference,eps=eps,coe=coe,verbose=verbose) + else: + raise NotImplementedError("Nrot supported balancer") + defaults = dict(balancer=balancer, preference=self.preference, eps=eps) + super(PAIR, self).__init__(params, defaults) + + + def __setstate__(self, state): + super(PAIR, self).__setstate__(state) + + def set_losses(self,losses): + self.losses = losses + + def step(self, closure=None): + r"""Performs a single optimization step. + Arguments: + closure (callable, optional): A closure that reevaluates the model + and returns the loss. + """ + if len(self.losses) == 0: + self.optimizer.step() + alphas = np.zeros(len(self.preference)) + alphas[0] = 1 + return -1, 233, alphas + else: + losses = self.losses + if closure is not None: + losses = closure() + + pair_loss = 0 + mu_rl = 0 + alphas = 0 + + grads = [] + for cur_loss in losses: + self.optimizer.zero_grad() + cur_loss.backward(retain_graph=True) + cur_grad = [] + for group in self.param_groups: + for param in group['params']: + if param.grad is not None: + cur_grad.append(Variable(param.grad.data.clone().flatten(), requires_grad=False)) + grads.append(torch.cat(cur_grad)) + + G = torch.stack(grads) + if self.get_grad_sim: + grad_sim = get_grad_sim(G,losses,preference=self.preference,is_G=True) + GG = G @ G.T + moo_losses = np.stack([l.item() for l in losses]) + reset_optimizer = False + try: + # Calculate the alphas from the LP solver + alpha, mu_rl, reset_optimizer = self.balancer.get_alpha(moo_losses, G=GG.cpu().numpy(), C=True,get_mu=True) + if self.balancer.last_move == "dom": + self.descent += 1 + print("dom") + except Exception as e: + print(traceback.format_exc()) + alpha = None + if alpha is None: # A patch for the issue in cvxpy + alpha = self.preference / np.sum(self.preference) + + scales = torch.from_numpy(alpha).float().to(losses[-1].device) + pair_loss = scales.dot(losses) + if reset_optimizer: + self.optimizer.param_groups[0]["lr"]/=5 + # self.optimizer = torch.optim.Adam(self.params,lr=self.optimizer.param_groups[0]["lr"]/5) + self.optimizer.zero_grad() + pair_loss.backward() + self.optimizer.step() + + return pair_loss, moo_losses, mu_rl, alpha + + + +import numpy as np +import cvxpy as cp +import cvxopt + +class EPO(object): + r""" + The original EPO solver proposed in ICML2020 + https://proceedings.mlr.press/v119/mahapatra20a.html + """ + def __init__(self, m, r, eps=1e-4, coe=0, verbose=False): + # self.solver = cp.GLPK + self.solver = cp.GUROBI + # cvxopt.glpk.options["msg_lev"] = "GLP_MSG_OFF" + self.m = m + self.r = r/np.sum(r) + self.eps = eps + self.last_move = None + self.a = cp.Parameter(m) # Adjustments + self.C = cp.Parameter((m, m)) # C: Gradient inner products, G^T G + self.Ca = cp.Parameter(m) # d_bal^TG + self.rhs = cp.Parameter(m) # RHS of constraints for balancing + + self.alpha = cp.Variable(m) # Variable to optimize + self.last_alpha = np.zeros_like(r)-1 + self.coe = coe + + obj_bal = cp.Maximize(self.alpha @ self.Ca) # objective for balance + constraints_bal = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Simplex + self.C @ self.alpha >= self.rhs] + self.prob_bal = cp.Problem(obj_bal, constraints_bal) # LP balance + + obj_dom = cp.Maximize(cp.sum(self.alpha @ self.C)-self.coe*cp.sum_squares(self.alpha-self.last_alpha)) # obj for descent + constraints_dom = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Restrict + self.alpha @ self.Ca >= -cp.neg(cp.max(self.Ca)), + self.C @ self.alpha >= 0] + self.prob_dom = cp.Problem(obj_dom, constraints_dom) # LP dominance + + + self.gamma = 0 # Stores the latest Optimum value of the LP problem + self.mu_rl = 0 # Stores the latest non-uniformity + + self.verbose = verbose + + + def get_alpha(self, l, G, r=None, C=False, get_mu=False): + """calculate weights for all objectives given the gradient information + + Args: + l (ndarray): the values of objective losses + G (ndarray): inner products of the gradients of each objective loss w.r.t. params + r (ndarray, optional): adopt this preference if specified + C (bool, optional): True if the input gradients are inner products + get_mu (bool, optional): return detailed information if True. + + Returns: + alpha: the objective weights + mu_rl (optional): the optimal value to the LP + reset_optimizer (optional): whether to reset the inner optimizer + """ + r = self.r if r is None else r + assert len(l) == len(G) == len(r) == self.m, "length != m" + l_hat, rl, self.mu_rl, self.a.value = self.adjustments(l, r) + reset_optimizer = False + self.C.value = G if C else G @ G.T + self.Ca.value = self.C.value @ self.a.value + + if self.last_alpha.sum() is None: + self.last_alpha = np.array(r) + if self.mu_rl > self.eps: + J = self.Ca.value > 0 + J_star_idx = np.where(rl == np.max(rl))[0] + self.rhs.value = self.Ca.value.copy() + # it's equivalent to setting no constraints to objectives in J + # as maximize alpha^TCa would trivially satisfy the non-negativity + self.rhs.value[J] = -np.inf + self.rhs.value[J_star_idx] = 0 + + self.gamma = self.prob_bal.solve(solver=self.solver, verbose=False) + self.last_move = "bal" + + if self.verbose: + test_alpha = np.ones_like(self.a.value)/self.m + print(self.last_alpha,self.C.value,self.Ca.value,self.rhs.value) + print(self.gamma,test_alpha@self.Ca.value, self.alpha.value @ self.C.value) + print(self.gamma,self.coe*np.linalg.norm(self.alpha.value-self.last_alpha)**2) + + else: + self.gamma = self.prob_dom.solve(solver=self.solver, verbose=False) + self.last_move = "dom" + self.last_alpha = np.array(self.alpha.value) + + if get_mu: + return self.alpha.value, self.mu_rl, reset_optimizer + + return self.alpha.value + + + def mu(self, rl, normed=False): + if len(np.where(rl < 0)[0]): + raise ValueError(f"rl<0 \n rl={rl}") + return None + m = len(rl) + l_hat = rl if normed else rl / rl.sum() + eps = np.finfo(rl.dtype).eps + l_hat = l_hat[l_hat > eps] + return np.sum(l_hat * np.log(l_hat * m)) + + + def adjustments(self, l, r=1): + m = len(l) + rl = r * l + + l_hat = rl / rl.sum() + mu_rl = self.mu(l_hat, normed=True) + uniformity_div = np.log(l_hat * m) - mu_rl + div_r = np.array(r) + a = div_r * uniformity_div + + if self.verbose: + print(a, rl, div_r, uniformity_div, l_hat, a.dot(l)) + return l_hat, rl, mu_rl, a + + +class SEPO(object): + r""" + A smoothed variant of EPO, with two adjustments for unrobust OOD objectives: + a) normalization: unrobust OOD objective can yield large loss values that dominate the solutions of the LP, + hence we adopt the normalized OOD losses in the LP to resolve the issue + b) regularization: solutions yielded by the LP can change sharply among steps, especially when switching descending phases + hence we incorporate a L2 regularization in the LP to resolve the issue + """ + def __init__(self, m, r, eps=1e-4, coe=0, verbose=False): + # self.solver = cp.GLPK + self.solver = cp.GUROBI + # cvxopt.glpk.options["msg_lev"] = "GLP_MSG_OFF" + self.m = m + self.r = r/np.sum(r) + self.eps = eps + self.last_move = None + self.a = cp.Parameter(m) # Adjustments + self.C = cp.Parameter((m, m)) # C: Gradient inner products, G^T G + self.Ca = cp.Parameter(m) # d_bal^TG + self.rhs = cp.Parameter(m) # RHS of constraints for balancing + + self.alpha = cp.Variable(m) # Variable to optimize + self.last_alpha = np.zeros_like(r)-1 + self.coe = coe + + obj_bal = cp.Maximize(self.alpha @ self.Ca-self.coe*cp.sum_squares(self.alpha-self.last_alpha)) # objective for balance + obj_bal_orig = cp.Maximize(self.alpha @ self.Ca) # objective for balance + constraints_bal = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Simplex + self.C @ self.alpha >= self.rhs] + self.prob_bal = cp.Problem(obj_bal, constraints_bal) # LP balance + self.prob_bal_orig = cp.Problem(obj_bal_orig, constraints_bal) # LP balance + + obj_dom = cp.Maximize(cp.sum(self.alpha @ self.C)-self.coe*cp.sum_squares(self.alpha-self.last_alpha)) # obj for descent + constraints_res = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Restrict + self.alpha @ self.Ca >= -cp.neg(cp.max(self.Ca)), + self.C @ self.alpha >= 0] + constraints_rel = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Relaxed + self.C @ self.alpha >= 0] + self.prob_dom = cp.Problem(obj_dom, constraints_res) # LP dominance + self.prob_rel = cp.Problem(obj_dom, constraints_rel) # LP dominance + + self.gamma = 0 # Stores the latest Optimum value of the LP problem + self.mu_rl = 0 # Stores the latest non-uniformity + + self.verbose = verbose + + + def get_alpha(self, l, G, r=None, C=False, get_mu=False): + """calculate weights for all objectives given the gradient information + + Args: + l (ndarray): the values of objective losses + G (ndarray): inner products of the gradients of each objective loss w.r.t. params + r (ndarray, optional): adopt this preference if specified + C (bool, optional): True if the input gradients are inner products + get_mu (bool, optional): return detailed information if True. + + Returns: + alpha: the objective weights + mu_rl (optional): the optimal value to the LP + reset_optimizer (optional): whether to reset the inner optimizer + """ + r = self.r if r is None else r + assert len(l) == len(G) == len(r) == self.m, "length != m" + l_hat, rl, self.mu_rl, self.a.value = self.adjustments(l, r) + reset_optimizer = False + if self.mu_rl <= 0.1: + self.r[0]=max(1e-15,self.r[0]/10000) + self.r = self.r/self.r.sum() + print(f"pua preference {self.r}") + l_hat, rl, self.mu_rl, self.a.value = self.adjustments(l, r) + + + a_norm = np.linalg.norm(self.a.value) + G_norm = np.linalg.norm(G,axis=1) + Ga = G.T @ self.a.value + self.C.value = G if C else G/np.expand_dims(G_norm,axis=1) @ G.T/a_norm + self.Ca.value = G/np.expand_dims(G_norm,axis=1) @ Ga.T/a_norm + + if self.last_alpha.sum() is None: + self.last_alpha = np.array(r) + if self.mu_rl > self.eps: + J = self.Ca.value > 0 + + J_star_idx = np.where(rl == np.max(rl))[0] + self.rhs.value = self.Ca.value.copy() + # it's equivalent to setting no constraints to objectives in J + # as maximize alpha^TCa would trivially satisfy the non-negativity + self.rhs.value[J] = -np.inf # Not efficient; but works. + self.rhs.value[J_star_idx] = max(0,self.Ca.value[J_star_idx]/2) + + if self.last_alpha.sum()<0: + self.gamma = self.prob_bal_orig.solve(solver=self.solver, verbose=False) + else: + self.gamma = self.prob_bal.solve(solver=self.solver, verbose=False) + + self.last_move = "bal" + + if self.verbose: + test_alpha = np.ones_like(self.a.value)/self.m + print(self.last_alpha,self.C.value,self.Ca.value,self.rhs.value) + print(self.gamma,test_alpha@self.Ca.value, self.alpha.value @ self.C.value) + print(self.gamma,self.coe*np.linalg.norm(self.alpha.value-self.last_alpha)**2) + else: + self.gamma = self.prob_dom.solve(solver=self.solver, verbose=False) + self.last_move = "dom" + self.last_alpha = np.array(self.alpha.value) + + if get_mu: + return self.alpha.value, self.mu_rl, reset_optimizer + + return self.alpha.value + + + def mu(self, rl, normed=False): + if len(np.where(rl < 0)[0]): + raise ValueError(f"rl<0 \n rl={rl}") + return None + m = len(rl) + l_hat = rl if normed else rl / rl.sum() + eps = np.finfo(rl.dtype).eps + l_hat = l_hat[l_hat > eps] + return np.sum(l_hat * np.log(l_hat * m)) + + + def adjustments(self, l, r=1): + m = len(l) + rl = r * l + + l_hat = rl / rl.sum() + mu_rl = self.mu(l_hat, normed=True) + uniformity_div = np.log(l_hat * m) - mu_rl + div_r = np.array(r) + a = div_r * uniformity_div + + if self.verbose: + print(a, rl, div_r, uniformity_div, l_hat, a.dot(l)) + return l_hat, rl, mu_rl, a + + +def getNumParams(params): + numParams, numTrainable = 0, 0 + for param in params: + npParamCount = np.prod(param.data.shape) + numParams += npParamCount + if param.requires_grad: + numTrainable += npParamCount + return numParams, numTrainable + +def get_kl_div(losses, preference): + pair_score = losses.dot(preference) + return pair_score + +def pair_selection(losses,val_accs,test_accs,anneal_iter=0,val_acc_bar=-1,pood=None): + + losses = losses[anneal_iter:] + val_accs = val_accs[anneal_iter:] + test_accs = test_accs[anneal_iter:] + if val_acc_bar < 0: + val_acc_bar = (np.max(val_accs)-np.min(val_accs))*0.05+np.min(val_accs) + + try: + preference_base = 10**max(-12,int(np.log10(np.mean(losses[:,-1]))-2)) + except Exception as e: + print(e) + preference_base = 1e-12 + if len(losses[0])==2: + preference = np.array([preference_base,1]) + elif len(losses[0])==4: + preference = np.array([1e-12,1e-4,1e-2,1]) + elif len(losses[0])==5: + preference = np.array([1e-12,1e-6,1e-4,1e-2,1]) + else: + preference = np.array([1e-12,1e-2,1]) + + if pood is not None: + preference = pood + print(f"Use preference: {preference}, validation acc bar: {val_acc_bar}") + + pair_score = np.array([get_kl_div(l,preference) if a>=val_acc_bar else 1e9 for (a,l) in zip(val_accs,losses)]) + sel_idx = np.argmin(pair_score) + return sel_idx+anneal_iter, val_accs[sel_idx], test_accs[sel_idx] + +def get_grad_sim(params,losses,preference=None,is_G=False,cosine=True): + num_ood_losses = len(losses)-1 + if is_G: + G = params + else: + pesudo_opt = SGD(params,lr=1e-6) + grads = [] + for cur_loss in losses: + pesudo_opt.zero_grad() + cur_loss.backward(retain_graph=True) + cur_grad = [] + for param in params: + if param.grad is not None: + cur_grad.append(Variable(param.grad.data.clone().flatten(), requires_grad=False)) + # print(torch.cat(cur_grad).sum()) + grads.append(torch.cat(cur_grad)) + G = torch.stack(grads) + if cosine: + G = F.normalize(G,dim=1) + GG = (G @ G.T).cpu() + if preference is not None: + G_weights = preference[1:]/np.sum(preference[1:]) + else: + G_weights = np.ones(num_ood_losses)/num_ood_losses + grad_sim =G_weights.dot(GG[0,1:]) + return grad_sim.item() diff --git a/ColoredMNIST/pair_alg.py b/ColoredMNIST/pair_alg.py new file mode 100644 index 0000000..10cc2c6 --- /dev/null +++ b/ColoredMNIST/pair_alg.py @@ -0,0 +1,134 @@ +import argparse +import copy +from collections import OrderedDict + +import numpy as np +import torch +import torch.nn.functional as F +from backpack import backpack, extend +from backpack.extensions import BatchGrad +from torch import autograd, nn, optim +from torch.autograd import Variable +from torch.optim import Adam +from torch.utils.data import DataLoader +from torchvision import datasets + +from misc import MovingAverage +from models import MLP, Net, TopMLP +from mydatasets import coloredmnist +from utils import (EMA, GeneralizedCELoss, correct_pred, mean_accuracy, + mean_mse, mean_nll, mean_weight, pretty_print, validation, + validation_details) +from pair import PAIR + + +def IRM_penalty_pair(envs_logits, envs_y, scale, lossf): + + train_penalty = 0 + for i in range(len(envs_logits)): + loss = lossf(envs_logits[i], envs_y[i]) + grad0 = autograd.grad(loss, [scale], create_graph=True)[0] + train_penalty += torch.sum(grad0**2) + + train_penalty /= len(envs_logits) + + return train_penalty + +def IRM_penalty_single(env_logits, env_y, scale, lossf): + + loss = lossf(env_logits*scale, env_y) + grad0 = autograd.grad(loss, [scale], create_graph=True)[0] + train_penalty = torch.sum(grad0**2) + + train_penalty /= len(env_logits) + + return train_penalty + +def pair_train(mlp, topmlp, steps, envs, test_envs, lossf, \ + penalty_anneal_iters, penalty_term_weight, anneal_val, \ + lr,l2_regularizer_weight, freeze_featurizer=False, eval_steps= 5, verbose=True,hparams={}): + net = Net(mlp,topmlp) + if freeze_featurizer: + trainable_params = [var for var in mlp.parameters()] + for param in mlp.parameters(): + param.requires_grad = False + else: + trainable_params = [var for var in mlp.parameters()] + \ + [var for var in topmlp.parameters()] + + if hparams['opt'].lower() == 'sgd': + optimizer = optim.SGD(trainable_params, lr=lr) + elif hparams['opt'].lower() == 'pair': + optimizer = optim.Adam( trainable_params, lr=1e-3) + else: + optimizer = optim.Adam( trainable_params, lr=lr) + + logs = [] + for step in range(steps): + envs_logits = [] + envs_y = [] + erm_losses = [] + scale = torch.tensor([1.])[0].cuda().requires_grad_() + for env in envs: + logits = topmlp(mlp(env['images'])) * scale + env['nll'] = lossf(logits, env['labels']) + env['acc'] = mean_accuracy(logits, env['labels']) + envs_logits.append(logits) + envs_y.append(env['labels']) + erm_losses.append(env['nll']) + + irm_penalty = IRM_penalty_pair(envs_logits, envs_y,scale, lossf) + erm_losses = torch.stack(erm_losses) + vrex_penalty = erm_losses.var() + erm_loss = erm_losses.mean() + alphas = np.array([0]) + device = logits.device + + # Compile loss + losses = torch.stack([erm_loss,irm_penalty,vrex_penalty]).to(device) + + if step >= penalty_anneal_iters: + if step==penalty_anneal_iters: + r = 1e-12 + r2 = 1e10 + r_l2 = r*r2 + preference = np.array([r]+[r_l2,(1-r-r_l2)]) + inner_optimizer = optim.SGD(trainable_params, lr=lr,momentum=0.9) + optimizer = PAIR(topmlp.parameters(),inner_optimizer,preference=preference,eps=1e-1,verbose=hparams['opt_verbose'],coe=hparams['opt_coe']) + print(f"Switch optimizer to {optimizer}") + optimizer.zero_grad() + optimizer.set_losses(losses=losses) + pair_loss, moo_losses, mu_rl, alphas = optimizer.step() + pair_res = np.array([pair_loss, mu_rl, alphas]) + else: + loss = erm_loss + + weight_norm = 0 + for w in [var for var in mlp.parameters()] + [var for var in topmlp.parameters()]: + weight_norm += w.norm().pow(2) + penalty_weight = (penalty_term_weight + if step >= penalty_anneal_iters else anneal_val) + if penalty_weight > 1.0: + # Rescale the entire loss to keep gradients in a reasonable range + weight_norm /= penalty_weight + + loss += l2_regularizer_weight * weight_norm + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + if step % eval_steps == 0: + train_loss, train_acc, test_worst_loss, test_worst_acc = \ + validation(topmlp, mlp, envs, test_envs, lossf) + + print_log = [np.int32(step), train_loss, train_acc, \ + losses.detach().cpu().numpy(),alphas,test_worst_loss, test_worst_acc] + log = [np.int32(step), train_loss, train_acc,\ + losses.detach().cpu().numpy(),test_worst_loss, test_worst_acc] + logs.append(log) + if verbose: + pretty_print(*print_log) + + return (train_acc, train_loss, test_worst_acc, test_worst_loss), logs + diff --git a/ColoredMNIST/run_exp.py b/ColoredMNIST/run_exp.py new file mode 100644 index 0000000..3c63a59 --- /dev/null +++ b/ColoredMNIST/run_exp.py @@ -0,0 +1,246 @@ +import argparse +import copy +import os +from collections import OrderedDict + +import numpy as np +import torch +import torch.nn.functional as F +from backpack import backpack, extend +from backpack.extensions import BatchGrad +from torch import autograd, nn, optim +from torchvision import datasets + +from models import MLP, TopMLP +from mydatasets import coloredmnist +from train import get_train_func +from utils import (EMA, GeneralizedCELoss, correct_pred, mean_accuracy, + mean_mse, mean_nll, mean_weight, parse_bool, pretty_print, + validation) + + +def main(flags): + if flags.save_dir is not None and not os.path.exists(flags.save_dir): + os.makedirs(flags.save_dir) + flags.freeze_featurizer = False if flags.freeze_featurizer.lower() == 'false' else True + final_train_accs = [] + final_train_losses = [] + final_test_accs = [] + final_test_losses = [] + final_grad_sims = [] + logs = [] + + + for restart in range(flags.n_restarts): + if flags.seed>=0 and restart != flags.seed: + print(f"Jump over seed {restart}") + continue + if flags.verbose: + print("Restart", restart) + + + ### loss function binary_cross_entropy + input_dim = 2 * 14 * 14 + if flags.methods in ['rsc', 'lff']: + n_targets = 2 + lossf = F.cross_entropy + int_target = True + else: + n_targets = 1 + lossf = mean_nll + int_target = False + + + np.random.seed(restart) + torch.manual_seed(restart) + ### load datasets + if flags.dataset == 'coloredmnist025' or flags.dataset == 'coloredmnist25': + envs, test_envs = coloredmnist(0.25, 0.1, 0.2, int_target = int_target) + elif flags.dataset == 'coloredmnist025gray': + envs, test_envs = coloredmnist(0.25, 0.5, 0.5,int_target = int_target) + elif flags.dataset == 'coloredmnist01': + envs, test_envs = coloredmnist(0.1, 0.2, 0.25, int_target = int_target) + elif flags.dataset == 'coloredmnist01gray': + envs, test_envs = coloredmnist(0.1, 0.5, 0.5, int_target = int_target) + elif flags.dataset == 'coloredmnist': + envs, test_envs = coloredmnist(flags.flip_p,flags.envs_p[0],flags.envs_p[1], int_target = int_target) + else: + raise NotImplementedError + + + mlp = MLP(hidden_dim = flags.hidden_dim, input_dim=input_dim).cuda() + topmlp = TopMLP(hidden_dim = flags.hidden_dim, n_top_layers=flags.n_top_layers, \ + n_targets=n_targets, fishr= flags.methods in ['fishr']).cuda() + + print(mlp, topmlp) + + if flags.load_model_dir is not None and os.path.exists(flags.load_model_dir): + device = torch.device("cuda") + state = torch.load(os.path.join(flags.load_model_dir,'mlp%d.pth' % restart), map_location=device) + mlp.load_state_dict(state) + + state = torch.load(os.path.join(flags.load_model_dir,'topmlp%d.pth' % restart), map_location=device) + topmlp.load_state_dict(state) + print("Load model from %s" % flags.load_model_dir) + + + if len(flags.group_dirs)>0: + print('load groups') + x = torch.cat([env['images'] for env in envs]) + y = torch.cat([env['labels'] for env in envs]) + #print(x.shape, y.shape) + groups = [np.load(os.path.join(group_dir,'group%d.npy' % restart)) for group_dir in flags.group_dirs] + n_groups = len(groups) + new_envs = [] + + for group in groups: + for val in np.unique(group): + env = {} + env['images'] = x[group == val] + env['labels'] = y[group == val] + + new_envs.append(env) + train_envs = new_envs + + else: + train_envs = envs + + train_func = get_train_func(flags.methods) + params = [mlp, topmlp, flags.steps, train_envs, test_envs,lossf,\ + flags.penalty_anneal_iters, flags.penalty_weight, \ + flags.anneal_val, flags.lr, \ + flags.l2_regularizer_weight, flags.freeze_featurizer, flags.eval_steps, flags.verbose, ] + if flags.methods in ['vrex', 'iga','irm','fishr','gm','lff','erm','dro','pair']: + hparams = {} + elif flags.methods in ['clove']: + hparams = {'batch_size': flags.batch_size, 'kernel_scale': flags.kernel_scale} + elif flags.methods in ['rsc']: + hparams = {'rsc_f_drop_factor' : flags.rsc_f, 'rsc_b_drop_factor': flags.rsc_b} + elif flags.methods in ['sd']: + hparams = {'lr_s2_decay': flags.lr_s2_decay} + else: + raise NotImplementedError + # additional exp configs + hparams['opt'] = flags.opt + # hparams['pair_bal'] = flags.pair_bal + hparams['opt_verbose'] = flags.opt_verbose + hparams['opt_coe'] = flags.opt_coe + hparams['pair_sim'] = flags.pair_sim + + res = train_func(*params,hparams) + (train_acc, train_loss, test_worst_acc, test_worst_loss), per_logs = res + + + logs.extend(per_logs) + final_train_accs.append(train_acc) + final_train_losses.append(train_loss) + final_test_accs.append(test_worst_acc) + final_test_losses.append(test_worst_loss) + + if flags.verbose: + + print('Final train acc (mean/std across restarts so far):') + print(np.mean(final_train_accs), np.std(final_train_accs)) + print('Final train loss (mean/std across restarts so far):') + print(np.mean(final_train_losses), np.std(final_train_losses)) + print('Final worest test acc (mean/std across restarts so far):') + print(np.mean(final_test_accs), np.std(final_test_accs)) + print('Final worest test loss (mean/std across restarts so far):') + print(np.mean(final_test_losses), np.std(final_test_losses)) + + results = [np.mean(final_train_accs), np.std(final_train_accs), + np.mean(final_train_losses), np.std(final_train_losses), + np.mean(final_test_accs), np.std(final_test_accs), + np.mean(final_test_losses), np.std(final_test_losses), + ] + + + + if flags.save_dir is not None: + state = mlp.state_dict() + torch.save(state, os.path.join(flags.save_dir,'mlp%d.pth' % restart)) + state = topmlp.state_dict() + torch.save(state, os.path.join(flags.save_dir,'topmlp%d.pth' % restart)) + + with torch.no_grad(): + x = torch.cat([env['images'] for env in envs]) + y = torch.cat([env['labels'] for env in envs]) + logits = topmlp(mlp(x)) + group, _ = correct_pred(logits, y) + + pseudolabel = np.copy(y.cpu().numpy().flatten()) + pseudolabel[~group] = 1-pseudolabel[~group] + np.save(os.path.join(flags.save_dir,'group%d.npy' % restart), group) + np.save(os.path.join(flags.save_dir,'pseudolabel%d.npy' % restart), pseudolabel ) + + logs = np.array(logs) + + if flags.save_dir is not None: + np.save(os.path.join(flags.save_dir,'logs.npy'), logs) + + return results, logs + +if __name__ == "__main__": + + parser = argparse.ArgumentParser(description='Colored MNIST & CowCamel') + parser.add_argument('--verbose', type=bool, default=False) + # additional exp name id + parser.add_argument('--exp_id', type=str,default='default') + parser.add_argument('--n_restarts', type=int, default=10) + parser.add_argument('--dataset', type=str, default='coloredmnist025') + parser.add_argument('--hidden_dim', type=int, default=256) + parser.add_argument('--n_top_layers', type=int, default=1) + parser.add_argument('--l2_regularizer_weight', '-l2',type=float,default=0.001) + + parser.add_argument('--opt', type=str, default='adam') + parser.add_argument('--opt_verbose',action='store_true') + parser.add_argument('--pair_sim',action='store_true') + parser.add_argument('--opt_coe', type=float, default=0.0) + parser.add_argument('--lr', type=float, default=0.001) + parser.add_argument('--steps', type=int, default=501) + parser.add_argument('--lossf', type=str, default='nll') + parser.add_argument('--penalty_anneal_iters', '-pi', type=int, default=100) + parser.add_argument('--penalty_weight', '-p', type=float, default=10000.0) + parser.add_argument('--irmx_p2', '-p2', type=float, default=-1) + parser.add_argument('--anneal_val', '-av',type=float, default=1) + + parser.add_argument('--methods', type=str, default='irmv2') + parser.add_argument('--lr_s2_decay', type=float, default=500) + parser.add_argument('--freeze_featurizer', type=str, default='False') + parser.add_argument('--eval_steps', type=int, default=5) + parser.add_argument('--seed', type=int, default=-1) # eval at a specific seed + + parser.add_argument('--load_model_dir', type=str, default=None) + parser.add_argument('--save_dir', type=str, default=None) + parser.add_argument('--group_dirs', type=str, nargs='*',default={}) + + #RSC + parser.add_argument('--rsc_f', type=float, default=0.99) + parser.add_argument('--rsc_b', type=float, default=0.97) + + #clove + parser.add_argument('--batch_size', type=int, default=512) + parser.add_argument('--kernel_scale', type=float, default=0.4) + + parser.add_argument('--n_examples', type=int, default=18000) + + parser.add_argument('--flip_p', default=0.25, type=float) + parser.add_argument('--envs_p', nargs='?', default='[0.1,0.2]', help='random seed') + parser.add_argument('--norun',type=parse_bool, default=False) + + parser.add_argument('--no_plot',action='store_true') + flags = parser.parse_args() + flags.envs_p = eval(flags.envs_p) + if flags.norun: + if flags.verbose: + print('Flags:') + for k,v in sorted(vars(flags).items()): + print("\t{}: {}".format(k, v)) + else: + main(flags) + + + + + + diff --git a/ColoredMNIST/train.py b/ColoredMNIST/train.py new file mode 100644 index 0000000..70ccc75 --- /dev/null +++ b/ColoredMNIST/train.py @@ -0,0 +1,954 @@ +import argparse +import copy +from collections import OrderedDict + +import numpy as np +import torch +import torch.nn.functional as F +from backpack import backpack, extend +from backpack.extensions import BatchGrad +from torch import autograd, nn, optim +from torchvision import datasets + +from models import MLP, TopMLP +from mydatasets import coloredmnist +from utils import (EMA, GeneralizedCELoss, correct_pred, mean_accuracy, + mean_mse, mean_nll, mean_weight, pretty_print, validation, + validation_details) +from pair_alg import * + + +def IGA_penalty(envs_logits, envs_y, scale, lossf): + + grads = [] + grad_mean = 0 + for i in range(len(envs_logits)): + + loss = lossf(envs_logits[i], envs_y[i]) + grad0 = [val.view(-1) for val in autograd.grad(loss, scale, create_graph=True)] + grad0 = torch.cat(grad0) + grads.append(grad0) + grad_mean += grad0 / len(envs_logits) + + grad_mean = grad_mean.detach() + + train_penalty = 0 + for i in range(len(grads)): + train_penalty += torch.sum((grads[i] - grad_mean)**2) + + return train_penalty + +def IRM_penalty(envs_logits, envs_y, scale, lossf): + + train_penalty = 0 + for i in range(len(envs_logits)): + loss = lossf(envs_logits[i], envs_y[i]) + grad0 = autograd.grad(loss, [scale], create_graph=True)[0] + train_penalty += torch.sum(grad0**2) + + train_penalty /= len(envs_logits) + + return train_penalty + +def GM_penalty(envs_logits, envs_y, scale, lossf): + + grads = [] + grad_mean = 0 + for i in range(len(envs_logits)): + + loss = lossf(envs_logits[i], envs_y[i]) + grad0 = [val.view(-1) for val in autograd.grad(loss, scale, create_graph=True)] + grad0 = torch.cat(grad0) + grads.append(grad0) + + train_penalty = 0 + for i in range(len(grads)-1): + for j in range(i+1,len(grads)): + train_penalty += -torch.sum(grads[i]*grads[j]) + + return train_penalty + + +def rsc_train(mlp, topmlp, + steps, + envs, test_envs, + lossf, \ + penalty_anneal_iters, penalty_term_weight, anneal_val, \ + lr,l2_regularizer_weight, freeze_featurizer=False, verbose=True,eval_steps=1, hparams={}): + + if freeze_featurizer: + optimizer = optimizer = optim.Adam( [var for var in topmlp.parameters()], lr=lr) + else: + optimizer = optimizer = optim.Adam([var for var in mlp.parameters()] + \ + [var for var in topmlp.parameters()], lr=lr) + + drop_f = (1 - hparams['rsc_f_drop_factor']) * 100 + drop_b = (1 - hparams['rsc_b_drop_factor']) * 100 + num_classes = 2 + logs = [] + for step in range(steps): + # inputs + all_x = torch.cat([envs[i]['images'] for i in range(len(envs))]) + all_y = torch.cat([envs[i]['labels'] for i in range(len(envs))]) + + + + # one-hot labels + all_o = torch.nn.functional.one_hot(all_y, num_classes) + # features + all_f = mlp(all_x) + # predictions + all_p = topmlp(all_f) + + if step < penalty_anneal_iters: + loss = F.cross_entropy(all_p, all_y) + optimizer.zero_grad() + loss.backward() + optimizer.step() + else: + # Equation (1): compute gradients with respect to representation + all_g = autograd.grad((all_p * all_o).sum(), all_f)[0] + + # Equation (2): compute top-gradient-percentile mask + percentiles = np.percentile(all_g.cpu(), drop_f, axis=1) + percentiles = torch.Tensor(percentiles) + percentiles = percentiles.unsqueeze(1).repeat(1, all_g.size(1)) + mask_f = all_g.lt(percentiles.cuda()).float() + + # Equation (3): mute top-gradient-percentile activations + all_f_muted = all_f * mask_f + + # Equation (4): compute muted predictions + all_p_muted = topmlp(all_f_muted) + + # Section 3.3: Batch Percentage + all_s = F.softmax(all_p, dim=1) + all_s_muted = F.softmax(all_p_muted, dim=1) + changes = (all_s * all_o).sum(1) - (all_s_muted * all_o).sum(1) + percentile = np.percentile(changes.detach().cpu(), drop_b) + mask_b = changes.lt(percentile).float().view(-1, 1) + mask = torch.logical_or(mask_f, mask_b).float() + + # Equations (3) and (4) again, this time mutting over examples + all_p_muted_again = topmlp(all_f * mask) + + # Equation (5): update + loss = F.cross_entropy(all_p_muted_again, all_y) + #print(loss) + optimizer.zero_grad() + loss.backward() + optimizer.step() + + + if step % eval_steps == 0: + train_loss, train_acc, test_worst_loss, test_worst_acc = \ + validation(topmlp, mlp, envs, test_envs, lossf) + log = [np.int32(step), train_loss, train_acc,\ + np.int32(0),test_worst_loss, test_worst_acc] + logs.append(log) + if verbose: + pretty_print(*log) + + return (train_acc, train_loss, test_worst_acc, test_worst_loss), logs + +def vrex_train(mlp, topmlp, steps, envs, test_envs, lossf, \ + penalty_anneal_iters, penalty_term_weight, anneal_val, \ + lr,l2_regularizer_weight,freeze_featurizer=False, eval_steps=5, verbose=True ): + logs = [] + + if freeze_featurizer: + optimizer = optimizer = optim.Adam( [var for var in topmlp.parameters()], lr=lr) + for param in mlp.parameters(): + param.requires_grad = False + + else: + optimizer = optimizer = optim.Adam([var for var in mlp.parameters()] + \ + [var for var in topmlp.parameters()], lr=lr) + + for step in range(steps): + + train_penalty = 0 + erm_losses = [] + for env in envs: + logits = topmlp(mlp(env['images'])) + #lossf = mean_nll if flags.lossf == 'nll' else mean_mse + env['nll'] = lossf(logits, env['labels']) + env['acc'] = mean_accuracy(logits, env['labels']) + erm_losses.append(env['nll']) + + erm_losses = torch.stack(erm_losses) + + train_penalty = erm_losses.var() + erm_loss = erm_losses.sum() + + loss = erm_loss.clone() + + weight_norm = 0 + for w in [var for var in mlp.parameters()] + [var for var in topmlp.parameters()]: + weight_norm += w.norm().pow(2) + loss += l2_regularizer_weight * weight_norm + + penalty_weight = (penalty_term_weight if step >= penalty_anneal_iters else anneal_val) + loss += penalty_weight * train_penalty + if penalty_weight > 1.0: + # Rescale the entire loss to keep gradients in a reasonable range + loss /= penalty_weight + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + + if step % eval_steps == 0: + train_loss, train_acc, test_worst_loss, test_worst_acc = \ + validation(topmlp, mlp, envs, test_envs, lossf) + log = [np.int32(step), train_loss, train_acc,\ + train_penalty.detach().cpu().numpy(),test_worst_loss, test_worst_acc] + logs.append(log) + if verbose: + pretty_print(*log) + + return (train_acc, train_loss, test_worst_acc, test_worst_loss), logs + +def iga_train(mlp, topmlp, steps, envs, test_envs, lossf, \ + penalty_anneal_iters, penalty_term_weight, anneal_val, \ + lr,l2_regularizer_weight,freeze_featurizer=False, verbose=True, eval_steps = 5, hparams={}): + + if freeze_featurizer: + optimizer = optimizer = optim.Adam( [var for var in topmlp.parameters()], lr=lr) + + else: + optimizer = optimizer = optim.Adam([var for var in mlp.parameters()] + \ + [var for var in topmlp.parameters()], lr=lr) + + + logs = [] + for step in range(steps): + train_penalty = 0 + envs_logits = [] + envs_y = [] + erm_loss = 0 + for env in envs: + logits = topmlp(mlp(env['images'])) + env['nll'] = lossf(logits, env['labels']) + env['acc'] = mean_accuracy(logits, env['labels']) + envs_logits.append(logits) + envs_y.append(env['labels']) + erm_loss += env['nll'] + + if freeze_featurizer: + params = [var for var in topmlp.parameters()] + else: + params = [var for var in mlp.parameters()] + [var for var in topmlp.parameters()] + train_penalty = IGA_penalty(envs_logits, envs_y, params, lossf) + + + + loss = erm_loss.clone() + + + weight_norm = 0 + for w in [var for var in mlp.parameters()] + [var for var in topmlp.parameters()]: + weight_norm += w.norm().pow(2) + loss += l2_regularizer_weight * weight_norm + + + penalty_weight = (penalty_term_weight + if step >= penalty_anneal_iters else anneal_val) + loss += penalty_weight * train_penalty + if penalty_weight > 1.0: + # Rescale the entire loss to keep gradients in a reasonable range + loss /= penalty_weight + + optimizer.zero_grad() + loss.backward() + optimizer.step() + if step % eval_steps == 0: + train_loss, train_acc, test_worst_loss, test_worst_acc = \ + validation(topmlp, mlp, envs, test_envs, lossf) + log = [np.int32(step), train_loss, train_acc,\ + train_penalty.detach().cpu().numpy(),test_worst_loss, test_worst_acc] + logs.append(log) + if verbose: + pretty_print(*log) + return (train_acc, train_loss, test_worst_acc, test_worst_loss), logs + +def dro_train(mlp, topmlp, steps, envs, test_envs, lossf, \ + penalty_anneal_iters, penalty_term_weight, anneal_val, \ + lr,l2_regularizer_weight,freeze_featurizer=False, verbose=True,eval_steps=5,hparams={}): + + if freeze_featurizer: + optimizer = optimizer = optim.Adam( [var for var in topmlp.parameters()], lr=lr) + for param in mlp.parameters(): + param.requires_grad = False + + else: + optimizer = optimizer = optim.Adam([var for var in mlp.parameters()] + \ + [var for var in topmlp.parameters()], lr=lr) + + logs = [] + for step in range(steps): + train_penalty = 0 + envs_logits = [] + envs_y = [] + + erm_losses = [] + + for env in envs: + logits = topmlp(mlp(env['images'])) + env['nll'] = lossf(logits, env['labels']) + env['acc'] = mean_accuracy(logits, env['labels']) + envs_logits.append(logits) + envs_y.append(env['labels']) + erm_losses.append(env['nll']) + + loss = max(erm_losses) + + weight_norm = 0 + for w in [var for var in mlp.parameters()] + [var for var in topmlp.parameters()]: + weight_norm += w.norm().pow(2) + loss += l2_regularizer_weight * weight_norm + + optimizer.zero_grad() + loss.backward() + optimizer.step() + if step % eval_steps == 0: + train_loss, train_acc, test_losses, test_acces = \ + validation_details(topmlp, mlp, envs, test_envs, lossf) + + log = [np.int32(step), train_loss, train_acc,\ + np.int32(0),*test_losses, *test_acces] + logs.append(log) + if verbose: + pretty_print(*log) + return (train_acc, train_loss, test_losses, test_acces), logs + +def sd_train(mlp, topmlp, steps, envs, test_envs, lossf, \ + penalty_anneal_iters, penalty_term_weight, anneal_val, \ + lr,l2_regularizer_weight,freeze_featurizer=False, verbose=True,eval_steps=5, hparams={'lr_s2_decay':500}): + if freeze_featurizer: + optimizer = optimizer = optim.Adam( [var for var in topmlp.parameters()], lr=lr) + for param in mlp.parameters(): + param.requires_grad = False + + else: + optimizer = optimizer = optim.Adam([var for var in mlp.parameters()] + \ + [var for var in topmlp.parameters()], lr=lr) + logs = [] + for step in range(steps): + + train_penalty = 0 + erm_loss = 0 + for env in envs: + logits = topmlp(mlp(env['images'])) + + #lossf = mean_nll if lossf == 'nll' else mean_mse + + env['nll'] = lossf(logits, env['labels']) + env['acc'] = mean_accuracy(logits, env['labels']) + + train_penalty += (logits**2).mean() + erm_loss += env['nll'] + + + loss = erm_loss.clone() + + + weight_norm = 0 + for w in [var for var in mlp.parameters()] \ + +[var for var in topmlp.parameters()]: + weight_norm += w.norm().pow(2) + + loss += l2_regularizer_weight * weight_norm + + + penalty_weight = (penalty_term_weight + if step >= penalty_anneal_iters else anneal_val) + loss += penalty_weight * train_penalty + if penalty_weight > 1.0: + # Rescale the entire loss to keep gradients in a reasonable range + loss /= penalty_weight + + if penalty_anneal_iters > 0 and step >= penalty_anneal_iters: + # using anneal, so decay lr + loss /= hparams['lr_s2_decay'] + + optimizer.zero_grad() + loss.backward() + optimizer.step() + if step % eval_steps == 0: + train_loss, train_acc, test_worst_loss, test_worst_acc = \ + validation(topmlp, mlp, envs, test_envs, lossf) + log = [np.int32(step), train_loss, train_acc,\ + train_penalty.detach().cpu().numpy(),test_worst_loss, test_worst_acc] + logs.append(log) + if verbose: + pretty_print(*log) + + return (train_acc, train_loss, test_worst_acc, test_worst_loss), logs + +def irm_train(mlp, topmlp, steps, envs, test_envs, lossf, \ + penalty_anneal_iters, penalty_term_weight, anneal_val, \ + lr,l2_regularizer_weight, freeze_featurizer=False, verbose=True, eval_steps= 5,hparams={}): + if freeze_featurizer: + optimizer = optimizer = optim.Adam( [var for var in topmlp.parameters()], lr=lr) + for param in mlp.parameters(): + param.requires_grad = False + + else: + optimizer = optimizer = optim.Adam([var for var in mlp.parameters()] + \ + [var for var in topmlp.parameters()], lr=lr) + + logs = [] + for step in range(steps): + + + envs_logits = [] + envs_y = [] + erm_loss = 0 + scale = torch.tensor([1.])[0].cuda().requires_grad_() + for env in envs: + logits = topmlp(mlp(env['images'])) * scale + env['nll'] = lossf(logits, env['labels']) + env['acc'] = mean_accuracy(logits, env['labels']) + envs_logits.append(logits) + envs_y.append(env['labels']) + erm_loss += env['nll'] + + train_penalty = IRM_penalty(envs_logits, envs_y,scale, lossf) + + loss = erm_loss.clone() + + + weight_norm = 0 + for w in [var for var in mlp.parameters()] + [var for var in topmlp.parameters()]: + weight_norm += w.norm().pow(2) + + loss += l2_regularizer_weight * weight_norm + + + penalty_weight = (penalty_term_weight + if step >= penalty_anneal_iters else anneal_val) + loss += penalty_weight * train_penalty + if penalty_weight > 1.0: + # Rescale the entire loss to keep gradients in a reasonable range + loss /= penalty_weight + + optimizer.zero_grad() + loss.backward() + optimizer.step() + if step % eval_steps == 0: + train_loss, train_acc, test_worst_loss, test_worst_acc = \ + validation(topmlp, mlp, envs, test_envs, lossf) + log = [np.int32(step), train_loss, train_acc,\ + train_penalty.detach().cpu().numpy(),test_worst_loss, test_worst_acc] + logs.append(log) + if verbose: + pretty_print(*log) + + return (train_acc, train_loss, test_worst_acc, test_worst_loss), logs + +def clove_train(mlp, topmlp, steps, envs, test_envs, lossf, \ + penalty_anneal_iters, penalty_term_weight, anneal_val, \ + lr,l2_regularizer_weight,freeze_featurizer=False, verbose=True,eval_steps=5,hparams={}): + if freeze_featurizer: + optimizer = optimizer = optim.Adam( [var for var in topmlp.parameters()], lr=lr) + for param in mlp.parameters(): + param.requires_grad = False + + else: + optimizer = optimizer = optim.Adam([var for var in mlp.parameters()] + \ + [var for var in topmlp.parameters()], lr=lr) + + logs = [] + batch_size = hparams['batch_size'] if 'batch_size' in hparams else 512 + kernel_scale = hparams['kernel_scale'] if 'kernel_scale' in hparams else 0.4 + def mmce_penalty(logits, y, kernel='laplacian'): + + c = ~((logits.flatten() > 0) ^ (y.flatten()>0.5)) + c = c.detach().float() + + preds = F.sigmoid(logits).flatten() + + y_hat = (preds < 0.5).detach().bool() + + confidence = torch.ones(len(y_hat)).cuda() + confidence[y_hat] = 1-preds[y_hat] + confidence[~y_hat] = preds[~y_hat] + + k = (-(confidence.view(-1,1)-confidence).abs() / kernel_scale).exp() + + + conf_diff = (c - confidence).view(-1,1) * (c -confidence) + + res = conf_diff * k + + return res.sum() / (len(logits)**2) + + pretty_print('step', 'train nll', 'train acc', 'train penalty', 'test acc') + + for step in range(steps): + length = min(len(envs[0]['labels']), len(envs[1]['labels'])) + + idx0 = np.arange(length) + np.random.shuffle(idx0) + idx1 = np.arange(length) + np.random.shuffle(idx1) + idx = [idx0, idx1] + + for i in range(length // batch_size): + + train_penalty = 0 + train_nll = 0 + train_acc = 0 + for j, env in enumerate(envs[0:2]): + x, y = env['images'], env['labels'] + x_batch, y_batch = x[idx[j][i*batch_size:(i+1)*batch_size]], y[idx[j][i*batch_size:(i+1)*batch_size]] + logits = topmlp(mlp(x_batch)) + nll = mean_nll(logits, y_batch) + acc = mean_accuracy(logits, y_batch) + mmce = mmce_penalty(logits, y_batch) + train_penalty += mmce + train_nll += nll + train_acc += acc + + train_acc /=2 + + + weight_norm = torch.tensor(0.).cuda() + for w in mlp.parameters(): + weight_norm += w.norm().pow(2) + + loss = train_nll.clone() + penalty_weight = (penalty_term_weight + if step >= penalty_anneal_iters else anneal_val) + loss += penalty_weight * train_penalty + if penalty_weight > 1.0: + # Rescale the entire loss to keep gradients in a reasonable range + loss /= penalty_weight + + optimizer.zero_grad() + + + loss.backward() + optimizer.step() + + + if step % eval_steps == 0: + train_loss, train_acc, test_worst_loss, test_worst_acc = \ + validation(topmlp, mlp, envs, test_envs, lossf) + log = [np.int32(step), train_loss, train_acc,\ + train_penalty.detach().cpu().numpy(),test_worst_loss, test_worst_acc] + logs.append(log) + if verbose: + pretty_print(*log) + + return (train_acc, train_loss, test_worst_acc, test_worst_loss), logs + +def fishr_train(mlp, topmlp, steps, envs, test_envs, lossf, \ + penalty_anneal_iters, penalty_term_weight, anneal_val, \ + lr,l2_regularizer_weight,freeze_featurizer=False, verbose=True, eval_steps=5, hparams={}): + + def compute_grads_variance(features, labels, classifier): + logits = classifier(features) + loss = bce_extended(logits, labels) + + with backpack(BatchGrad()): + loss.backward( + inputs=list(classifier.parameters()), retain_graph=True, create_graph=True + ) + + dict_grads = OrderedDict( + [ + (name, weights.grad_batch.clone().view(weights.grad_batch.size(0), -1)) + for name, weights in classifier.named_parameters() + ] + ) + dict_grads_variance = {} + for name, _grads in dict_grads.items(): + grads = _grads * labels.size(0) # multiply by batch size + env_mean = grads.mean(dim=0, keepdim=True) + + dict_grads_variance[name] = (grads).pow(2).mean(dim=0) + + return dict_grads_variance + + def l2_between_grads_variance(cov_1, cov_2): + assert len(cov_1) == len(cov_2) + cov_1_values = [cov_1[key] for key in sorted(cov_1.keys())] + cov_2_values = [cov_2[key] for key in sorted(cov_2.keys())] + return ( + torch.cat(tuple([t.view(-1) for t in cov_1_values])) - + torch.cat(tuple([t.view(-1) for t in cov_2_values])) + ).pow(2).sum() + + if freeze_featurizer: + optimizer = optimizer = optim.Adam( [var for var in topmlp.parameters()], lr=lr) + for param in mlp.parameters(): + param.requires_grad = False + else: + optimizer = optimizer = optim.Adam([var for var in mlp.parameters()] + \ + [var for var in topmlp.parameters()], lr=lr) + logs = [] + + bce_extended = extend(nn.BCEWithLogitsLoss()) + for step in range(steps): + for edx, env in enumerate(envs): + features = mlp(env['images']) + logits = topmlp(features) + env['nll'] = mean_nll(logits, env['labels']) + env['acc'] = mean_accuracy(logits, env['labels']) + if edx in [0, 1]: + # True when the dataset is in training + optimizer.zero_grad() + env["grads_variance"] = compute_grads_variance(features, env['labels'], topmlp) + + train_nll = torch.stack([envs[0]['nll'], envs[1]['nll']]).sum() + train_acc = torch.stack([envs[0]['acc'], envs[1]['acc']]).mean() + + weight_norm = torch.tensor(0.).cuda() + for w in mlp.parameters(): + weight_norm += w.norm().pow(2) + + loss = train_nll.clone() + loss += l2_regularizer_weight * weight_norm + + dict_grads_variance_averaged = OrderedDict( + [ + ( + name, + torch.stack([envs[0]["grads_variance"][name], envs[1]["grads_variance"][name]], + dim=0).mean(dim=0) + ) for name in envs[0]["grads_variance"] + ] + ) + fishr_penalty = ( + l2_between_grads_variance(envs[0]["grads_variance"], dict_grads_variance_averaged) + + l2_between_grads_variance(envs[1]["grads_variance"], dict_grads_variance_averaged) + ) + train_penalty = fishr_penalty + + + penalty_weight = (penalty_term_weight + if step >= penalty_anneal_iters else anneal_val) + loss += penalty_weight * train_penalty + if penalty_weight > 1.0: + # Rescale the entire loss to keep gradients in a reasonable range + loss /= penalty_weight + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + if step % eval_steps == 0: + train_loss, train_acc, test_worst_loss, test_worst_acc = \ + validation(topmlp, mlp, envs, test_envs, lossf) + log = [np.int32(step), train_loss, train_acc,\ + train_penalty.detach().cpu().numpy(),test_worst_loss, test_worst_acc] + logs.append(log) + if verbose: + pretty_print(*log) + + return (train_acc, train_loss, test_worst_acc, test_worst_loss), logs + +def gm_train(mlp, topmlp, steps, envs, test_envs, lossf, \ + penalty_anneal_iters, penalty_term_weight, anneal_val, \ + lr,l2_regularizer_weight, freeze_featurizer=False, verbose=True, eval_steps=5, hparams={}): + + if freeze_featurizer: + optimizer = optimizer = optim.Adam( [var for var in topmlp.parameters()], lr=lr) + else: + optimizer = optimizer = optim.Adam([var for var in mlp.parameters()] + \ + [var for var in topmlp.parameters()], lr=lr) + logs = [] + for step in range(steps): + + train_penalty = 0 + envs_logits = [] + envs_y = [] + for env in envs: + logits = topmlp(mlp(env['images'])) + env['nll'] = lossf(logits, env['labels']) + env['acc'] = mean_accuracy(logits, env['labels']) + envs_logits.append(logits) + envs_y.append(env['labels']) + + + + train_penalty = GM_penalty(envs_logits, envs_y, [var for var in mlp.parameters()] + [var for var in topmlp.parameters()], lossf) + + erm_loss = (envs[0]['nll'] + envs[1]['nll']) + + + loss = erm_loss.clone() + + + weight_norm = 0 + for w in [var for var in mlp.parameters()] + [var for var in topmlp.parameters()]: + weight_norm += w.norm().pow(2) + + loss += flags.l2_regularizer_weight * weight_norm + + + penalty_weight = (flags.penalty_weight + if step >= flags.penalty_anneal_iters else flags.anneal_val) + loss += penalty_weight * train_penalty + if penalty_weight > 1.0: + # Rescale the entire loss to keep gradients in a reasonable range + loss /= penalty_weight + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + if step % eval_steps == 0: + train_loss, train_acc, test_worst_loss, test_worst_acc = \ + validation(topmlp, mlp, envs, test_envs, lossf) + log = [np.int32(step), train_loss, train_acc,\ + train_penalty.detach().cpu().numpy(),test_worst_loss, test_worst_acc] + logs.append(log) + if verbose: + pretty_print(*log) + + return (train_acc, train_loss, test_worst_acc, test_worst_loss), logs + +def lff_train(mlp, topmlp, steps, envs, test_envs, lossf, \ + penalty_anneal_iters, penalty_term_weight, anneal_val, \ + lr,l2_regularizer_weight, freeze_featurizer=False, verbose=True,eval_steps=5, hparams={}): + + if freeze_featurizer is False: + raise NotImplementedError + + x = torch.cat([envs[i]['images'] for i in range(len(envs))]) + y = torch.cat([envs[i]['labels'] for i in range(len(envs))]) + + y = y.long().flatten() + logs = [] + if penalty_anneal_iters > 0: + optimizer = torch.optim.Adam([var for var in mlp.parameters()] \ + + [var for var in topmlp.parameters()], + lr=lr, weight_decay=l2_regularizer_weight,) + + for step in range(penalty_anneal_iters): + logits = topmlp(mlp(x)) + loss = F.cross_entropy(logits, y) + optimizer.zero_grad() + loss.backward() + optimizer.step() + + train_penalty = torch.tensor([0]).cuda()[0] + + if step % 5 == 0: + train_loss, train_acc, test_worst_loss, test_worst_acc = \ + validation(topmlp, mlp, envs, test_envs, lossf) + log = [np.int32(step), train_loss, train_acc,\ + train_penalty.detach().cpu().numpy(),test_worst_loss, test_worst_acc] + logs.append(log) + if verbose: + pretty_print(*log) + + + _mlp = copy.deepcopy(mlp) + _topmlp = copy.deepcopy(topmlp) + + model_b = torch.nn.Sequential(_mlp, _topmlp) + + model_d = torch.nn.Sequential(mlp, topmlp) + + optimizer_b = torch.optim.Adam( + model_b.parameters(), + lr=lr / 100, + weight_decay=l2_regularizer_weight, + ) + optimizer_d = torch.optim.Adam( + model_d.parameters(), + lr=lr / 100, + weight_decay= l2_regularizer_weight, + ) + lossf = nn.CrossEntropyLoss(reduction='mean') + criterion = nn.CrossEntropyLoss(reduction='none') + bias_criterion = GeneralizedCELoss(q = penalty_term_weight) + + sample_loss_ema_b = EMA(y.cpu().numpy(), alpha=0.7) + sample_loss_ema_d = EMA(y.cpu().numpy(), alpha=0.7) + + index = np.arange(len(y)) + for step in range(penalty_anneal_iters, steps): + + logit_b = model_b(x) + logit_d = model_d(x) + + loss_b = criterion(logit_b, y).cpu().detach() + loss_d = criterion(logit_d, y).cpu().detach() + + sample_loss_ema_b.update(loss_b,index) + sample_loss_ema_d.update(loss_d,index) + + loss_b = sample_loss_ema_b.parameter[index].clone().detach() + loss_d = sample_loss_ema_d.parameter[index].clone().detach() + + # mnist target has one class, so I can do in this way. + label_cpu = y.cpu() + num_classes = 2 + for c in range(num_classes): + class_index = np.where(label_cpu == c)[0] + max_loss_b = sample_loss_ema_b.max_loss(c) + max_loss_d = sample_loss_ema_d.max_loss(c) + loss_b[class_index] /= max_loss_b + loss_d[class_index] /= max_loss_d + + loss_weight = loss_b / (loss_b + loss_d + 1e-8) + + loss_b_update = bias_criterion(logit_b, y) + loss_d_update = criterion(logit_d, y) * loss_weight.cuda() + loss = loss_b_update.mean() + loss_d_update.mean() + + optimizer_b.zero_grad() + optimizer_d.zero_grad() + loss.backward() + optimizer_b.step() + optimizer_d.step() + + train_penalty = torch.tensor([0]).cuda()[0] + + if step % eval_steps == 0: + train_loss, train_acc, test_worst_loss, test_worst_acc = \ + validation(topmlp, mlp, envs, test_envs, lossf) + log = [np.int32(step), train_loss, train_acc,\ + train_penalty.detach().cpu().numpy(),test_worst_loss, test_worst_acc] + logs.append(log) + if verbose: + pretty_print(*log) + return (train_acc, train_loss, test_worst_acc, test_worst_loss), logs + +def erm_train(mlp, topmlp, steps, envs, test_envs, lossf, \ + penalty_anneal_iters, penalty_term_weight, anneal_val, \ + lr,l2_regularizer_weight, freeze_featurizer=False, verbose=True,eval_steps=5, hparams={}): + + + x = torch.cat([envs[i]['images'] for i in range(len(envs))]) + y = torch.cat([envs[i]['labels'] for i in range(len(envs))]) + + if freeze_featurizer: + optimizer = optimizer = optim.Adam( [var for var in topmlp.parameters()], lr=lr) + for param in mlp.parameters(): + param.requires_grad = False + print('freeze_featurizer') + + else: + optimizer = optimizer = optim.Adam([var for var in mlp.parameters()] + \ + [var for var in topmlp.parameters()], lr=lr) + + logs = [] + for step in range(steps): + + logits = topmlp(mlp(x)) + #print(logits) + loss = lossf(logits, y) + #print(loss) + #0/0 + weight_norm = 0 + for w in [var for var in mlp.parameters()] + [var for var in topmlp.parameters()]: + weight_norm += w.norm().pow(2) + + loss += l2_regularizer_weight * weight_norm + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + train_penalty = torch.tensor([0]).cuda()[0] + + if step % eval_steps == 0: + train_loss, train_acc, test_worst_loss, test_worst_acc = \ + validation(topmlp, mlp, envs, test_envs, lossf) + log = [np.int32(step), train_loss, train_acc,\ + train_penalty.detach().cpu().numpy(),test_worst_loss, test_worst_acc] + logs.append(log) + if verbose: + pretty_print(*log) + + return (train_acc, train_loss, test_worst_acc, test_worst_loss), logs + +def syn_train(mlp, topmlp, steps, envs, test_envs, lossf, \ + penalty_anneal_iters, penalty_term_weight, anneal_val, \ + lr,l2_regularizer_weight, verbose=True,eval_steps=50, hparams={}): + + x = torch.cat([envs[i]['images'] for i in range(len(envs))]) + y = torch.cat([envs[i]['labels'] for i in range(len(envs))]) + + optimizer = optim.Adam([var for var in mlp.parameters()] \ + +[var for var in topmlp.parameters()], lr=lr) + logs = [] + ntasks = hparams['ntasks'] + for step in range(steps): + logits = topmlp(mlp(x)) + + per_logits_size = logits.shape[1] // ntasks + per_y_size = y.shape[1] // ntasks + loss = 0 + for i in range(ntasks): + + loss += lossf(logits[:, i*per_logits_size:(i+1)*per_logits_size],y[:,i*per_y_size:(i+1)*per_y_size]) + loss /= ntasks + + + weight_norm = 0 + for w in [var for var in mlp.parameters()] + [var for var in topmlp.parameters()]: + weight_norm += w.norm().pow(2) + + + loss += l2_regularizer_weight * weight_norm + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + train_penalty = torch.tensor([0]).cuda()[0] + + + if step % eval_steps == 0: + + with torch.no_grad(): + for j, env in enumerate(envs + test_envs): + logits = topmlp(mlp(env['images'])) + loss = 0 + acc = 0 + for i in range(ntasks): + per_logits = logits[:, i*per_logits_size:(i+1)*per_logits_size] + + if j < len(envs): + per_y = env['labels'][:,i*per_y_size:(i+1)*per_y_size] + else: + per_y = env['labels'] + loss += lossf(per_logits,per_y) + + acc += mean_accuracy(per_logits, per_y) + + loss /= ntasks + acc /=ntasks + + env['nll'] = loss + env['acc'] = acc + + test_worst_loss = torch.stack([env['nll'] for env in test_envs]).max() + test_worst_acc = torch.stack([env['acc'] for env in test_envs]).min() + train_loss = torch.stack([env['nll'] for env in envs]).mean() + train_acc = torch.stack([env['acc'] for env in envs]).mean() + + train_loss, train_acc, test_worst_loss, test_worst_acc = train_loss.detach().cpu().numpy(), \ + train_acc.detach().cpu().numpy(), \ + test_worst_loss.detach().cpu().numpy(),\ + test_worst_acc.detach().cpu().numpy() + log = [np.int32(step), train_loss, train_acc,\ + train_penalty.detach().cpu().numpy(),test_worst_loss, test_worst_acc] + logs.append(log) + + if verbose: + pretty_print(*log) + + return (train_acc, train_loss, test_worst_acc, test_worst_loss), logs + +def get_train_func(methods): + assert methods in ['rsc', 'vrex', 'iga','sd','irm','clove','fishr','gm','lff','erm','dro','syn','pair'] + return eval("%s_train" % methods) diff --git a/ColoredMNIST/utils.py b/ColoredMNIST/utils.py new file mode 100644 index 0000000..03bf54b --- /dev/null +++ b/ColoredMNIST/utils.py @@ -0,0 +1,161 @@ +import numpy as np +from torch import nn +import torch +import torch.nn.functional as F + +import numpy as np + +def parse_bool(v): + if v.lower()=='true': + return True + elif v.lower()=='false': + return False + else: + raise argparse.ArgumentTypeError('Boolean value expected.') + +def pretty_print(*values): + col_width = 13 + def format_val(v): + if not isinstance(v, str): + v = np.array2string(v, precision=5, floatmode='fixed') + return v.ljust(col_width) + str_values = [format_val(v) for v in values] + print(" ".join(str_values)) + +# Define loss function helpers +def mean_weight(weights): + + weight = copy.deepcopy(weights[0]) + for key in weight: + for val in weights[1:]: + weight[key] += val[key] + + for key in weight: + weight[key] /= len(weights) + + return weight + + +def mean_nll(logits, y, reduction='mean'): + return nn.functional.binary_cross_entropy_with_logits(logits, y,reduction=reduction) + +def mean_mse(logits, y, reduction = 'mean'): + if reduction == 'mean': + return ((logits - (2*y-1))**2).mean()/2 + elif reduction == 'none': + return ((logits - (2*y-1))**2)/2 + +def mean_accuracy(logits, y, reduction = 'mean'): + if logits.size(1) == 1: + preds = (logits > 0.).float() + if reduction == 'mean': + return ((preds - y).abs() < 1e-2).float().mean() + else: + return ((preds - y).abs() < 1e-2).float() + else: + if reduction == 'mean': + return (logits.argmax(1).eq(y).float()).mean() + else: + return (logits.argmax(1).eq(y).float()) + +def correct_pred(logits, y): + if logits.size(1) == 1: + preds = (logits > 0.).float() + correct = ((preds - y).abs() < 1e-2).float().cpu().detach().numpy().flatten().astype(bool) + + else: + correct = logits.argmax(1).eq(y).float().cpu().detach().numpy().flatten().astype(bool) + + return correct, ~correct + +def validation(topmlp, mlp, envs, test_envs, lossf): + + with torch.no_grad(): + for env in envs + test_envs: + logits = topmlp(mlp(env['images'])) + + env['nll'] = lossf(logits, env['labels']) + env['acc'] = mean_accuracy(logits, env['labels']) + + test_worst_loss = torch.stack([env['nll'] for env in test_envs]).max() + test_worst_acc = torch.stack([env['acc'] for env in test_envs]).min() + train_loss = torch.stack([env['nll'] for env in envs]).mean() + train_acc = torch.stack([env['acc'] for env in envs]).mean() + + return train_loss.detach().cpu().numpy(), train_acc.detach().cpu().numpy(), \ + test_worst_loss.detach().cpu().numpy(),test_worst_acc.detach().cpu().numpy() + +def validation_details(topmlp, mlp, envs, test_envs, lossf): + + with torch.no_grad(): + for env in envs + test_envs: + logits = topmlp(mlp(env['images'])) + + env['nll'] = lossf(logits, env['labels']) + env['acc'] = mean_accuracy(logits, env['labels']) + + train_loss = torch.stack([env['nll'] for env in envs]).mean() + train_acc = torch.stack([env['acc'] for env in envs]).mean() + + return train_loss.detach().cpu().numpy(), train_acc.detach().cpu().numpy(), \ + [env['nll'].detach().cpu().numpy() for env in test_envs], \ + [env['acc'].detach().cpu().numpy() for env in test_envs] + +def validation2(model, envs, test_envs, lossf): + + with torch.no_grad(): + for env in envs + test_envs: + logits = model(env['images']) + + env['nll'] = lossf(logits, env['labels']) + env['acc'] = mean_accuracy(logits, env['labels']) + + test_worst_loss = torch.stack([env['nll'] for env in test_envs]).max() + test_worst_acc = torch.stack([env['acc'] for env in test_envs]).min() + train_loss = torch.stack([env['nll'] for env in envs]).mean() + train_acc = torch.stack([env['acc'] for env in envs]).mean() + + return train_loss.detach().cpu().numpy(), train_acc.detach().cpu().numpy(), \ + test_worst_loss.detach().cpu().numpy(),test_worst_acc.detach().cpu().numpy() + + + + +# from https://github.com/alinlab/LfF/blob/e66796ec117ea52d2e44176055b7ef7959680a1b/module/loss.py#L8 +class GeneralizedCELoss(nn.Module): + + def __init__(self, q=0.7): + super(GeneralizedCELoss, self).__init__() + self.q = q + + def forward(self, logits, targets): + p = F.softmax(logits, dim=1) + if np.isnan(p.mean().item()): + raise NameError('GCE_p') + Yg = torch.gather(p, 1, torch.unsqueeze(targets, 1)) + # modify gradient of cross entropy + loss_weight = (Yg.squeeze().detach()**self.q)*self.q + if np.isnan(Yg.mean().item()): + raise NameError('GCE_Yg') + + loss = F.cross_entropy(logits, targets, reduction='none') * loss_weight + + return loss + +# https://github.com/alinlab/LfF/blob/e66796ec117ea52d2e44176055b7ef7959680a1b/util.py#L33 +class EMA: + + def __init__(self, label, alpha=0.9): + self.label = label + self.alpha = alpha + self.parameter = torch.zeros(label.shape[0]) + self.updated = torch.zeros(label.shape[0]) + + def update(self, data, index): + self.parameter[index] = self.alpha * self.parameter[index] + (1-self.alpha*self.updated[index]) * data + self.updated[index] = 1 + + def max_loss(self, label): + label_index = np.where(self.label == label)[0] + return self.parameter[label_index].max() + diff --git a/DomainBed/model_selection.py b/DomainBed/model_selection.py new file mode 100644 index 0000000..71e40c5 --- /dev/null +++ b/DomainBed/model_selection.py @@ -0,0 +1,483 @@ +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved + +import itertools +from tkinter.filedialog import test +import numpy as np +from numpy.linalg import norm + +top_percentile = 0.9 +adjust_coe = 1 + +def get_kl_div(losses, preference): + pair_score = losses.dot(preference) + return pair_score + +def get_losses(record): + if "groupdro" in record['args']['output_dir'] and 'penalty' in record.keys(): + record['loss'] = record['loss'] + record.pop("penalty") + if 'nll' in record.keys(): + erm_loss = record['nll'] + if 'mu_rl' in record.keys(): + pass + if "vrex_penalty" in record.keys() and "IRM_penalty" in record.keys(): + losses = np.array([erm_loss,record["IRM_penalty"],record["vrex_penalty"]]) + elif "nvrex_penalty" in record.keys() and "nIRM_penalty" in record.keys(): + losses = np.array([erm_loss,record["nIRM_penalty"],record["nvrex_penalty"]]) + elif 'penalty' in record.keys(): + ood_loss = record['penalty'] + losses = np.array([erm_loss,ood_loss]) + else: + if 'disc_loss' in record.keys() and 'gen_loss' not in record.keys(): + losses = np.array([(2-record['disc_loss']) if record['disc_loss']<=2 else 3]) + losses = np.array([1e9]) + elif 'gen_loss' in record.keys(): + losses = np.array([1e9]) + if np.abs(record['gen_loss'])>=50: + losses = np.array([1e9]) + else: + losses = np.array([(1e9+record['gen_loss']) if record['gen_loss']>=-1e9 else 1e9]) + else: + losses = np.array([record['loss']]) + + return losses + +def get_pair_score(record, get_loss=False,preference_base=1e-6): + if "groupdro" in record['args']['output_dir']and 'penalty' in record.keys(): + record['loss'] = record['loss'] + record.pop("penalty") + if 'nll' in record.keys(): + erm_loss = record['nll'] + if 'mu_rl' in record.keys(): + pass + if "vrex_penalty" in record.keys() and "IRM_penalty" in record.keys(): + losses = np.array([erm_loss,record["IRM_penalty"],record["vrex_penalty"]]) + if record["IRM_penalty"] < 0: + losses[1] *=-adjust_coe + preference = np.array([preference_base,1e-2,1]) + elif "nvrex_penalty" in record.keys() and "nIRM_penalty" in record.keys(): + losses = np.array([erm_loss,record["nIRM_penalty"],record["nvrex_penalty"]]) + if record["nIRM_penalty"] < 0: + losses[1] *=-adjust_coe + preference = np.array([preference_base,1e-2,1]) + elif 'penalty' in record.keys(): + ood_loss = record['penalty'] + losses = np.array([erm_loss,ood_loss]) + if record["penalty"] < 0: + losses[1] *=-adjust_coe + preference = np.array([preference_base,1]) + else: + if 'disc_loss' in record.keys() and 'gen_loss' not in record.keys(): + losses = np.array([(2-record['disc_loss']) if record['disc_loss']<=2 else 3]) + elif 'gen_loss' in record.keys(): + if np.abs(record['gen_loss'])>=50: + losses = np.array([1e9]) + else: + losses = np.array([(1e9+record['gen_loss']) if record['gen_loss']>=-1e9 else 1e9]) + else: + losses = np.array([record['loss']]) + preference = np.array([1]) if len(losses)==1 else np.array([preference_base,1]) + + pair_score = get_kl_div(losses,preference) + if get_loss: + return -pair_score, losses + return -pair_score + +def get_test_records(records): + """Given records with a common test env, get the test records (i.e. the + records with *only* that single test env and no other test envs)""" + return records.filter(lambda r: len(r['args']['test_envs']) == 1) + +class SelectionMethod: + """Abstract class whose subclasses implement strategies for model + selection across hparams and timesteps.""" + + def __init__(self): + raise TypeError + + @classmethod + def run_acc(self, run_records): + """ + Given records from a run, return a {val_acc, test_acc} dict representing + the best val-acc and corresponding test-acc for that run. + """ + raise NotImplementedError + + @classmethod + def hparams_accs(self, records): + """ + Given all records from a single (dataset, algorithm, test env) pair, + return a sorted list of (run_acc, records) tuples. + """ + return (records.group('args.hparams_seed') + .map(lambda _, run_records: + ( + self.run_acc(run_records), + run_records + ) + ).filter(lambda x: x[0] is not None) + .sorted(key=lambda x: x[0]['val_acc'])[::-1] + ) + + @classmethod + def sweep_acc(self, records): + """ + Given all records from a single (dataset, algorithm, test env) pair, + return the mean test acc of the k runs with the top val accs. + """ + _hparams_accs = self.hparams_accs(records) + if len(_hparams_accs): + return _hparams_accs[0][0]['test_acc'] + else: + return None + +class OracleSelectionMethod(SelectionMethod): + """Like Selection method which picks argmax(test_out_acc) across all hparams + and checkpoints, but instead of taking the argmax over all + checkpoints, we pick the last checkpoint, i.e. no early stopping.""" + name = "test-domain validation set (oracle)" + + @classmethod + def run_acc(self, run_records): + run_records = run_records.filter(lambda r: + len(r['args']['test_envs']) == 1) + if not len(run_records): + return None + test_env = run_records[0]['args']['test_envs'][0] + test_out_acc_key = 'env{}_out_acc'.format(test_env) + test_in_acc_key = 'env{}_in_acc'.format(test_env) + chosen_record = run_records.sorted(lambda r: r['step'])[-1] + return { + 'val_acc': chosen_record[test_out_acc_key], + 'test_acc': chosen_record[test_in_acc_key] + } + +class IIDAccuracySelectionMethod(SelectionMethod): + """Picks argmax(mean(env_out_acc for env in train_envs))""" + name = "training-domain validation set" + + @classmethod + def _step_acc(self, record): + """Given a single record, return a {val_acc, test_acc} dict.""" + test_env = record['args']['test_envs'][0] + val_env_keys = [] + for i in itertools.count(): + if f'env{i}_out_acc' not in record: + break + if i != test_env: + val_env_keys.append(f'env{i}_out_acc') + test_in_acc_key = 'env{}_in_acc'.format(test_env) + return { + 'val_acc': np.mean([record[key] for key in val_env_keys]), + 'test_acc': record[test_in_acc_key] + } + + @classmethod + def run_acc(self, run_records): + test_records = get_test_records(run_records) + if not len(test_records): + return None + return test_records.map(self._step_acc).argmax('val_acc') + +class LeaveOneOutSelectionMethod(SelectionMethod): + """Picks (hparams, step) by leave-one-out cross validation.""" + name = "leave-one-domain-out cross-validation" + + @classmethod + def _step_acc(self, records): + """Return the {val_acc, test_acc} for a group of records corresponding + to a single step.""" + test_records = get_test_records(records) + if len(test_records) != 1: + return None + + test_env = test_records[0]['args']['test_envs'][0] + n_envs = 0 + for i in itertools.count(): + if f'env{i}_out_acc' not in records[0]: + break + n_envs += 1 + val_accs = np.zeros(n_envs) - 1 + # it implicitly assumes there is a test env, and n-1 training env + # hence given n envs, it does the eval with all 2-test-env combinations + for r in records.filter(lambda r: len(r['args']['test_envs']) == 2): + val_env = (set(r['args']['test_envs']) - set([test_env])).pop() + val_accs[val_env] = r['env{}_in_acc'.format(val_env)] + + val_accs = list(val_accs[:test_env]) + list(val_accs[test_env+1:]) + if any([v==-1 for v in val_accs]): + return None + val_acc = np.sum(val_accs) / (n_envs-1) + return { + 'val_acc': val_acc, + 'test_acc': test_records[0]['env{}_in_acc'.format(test_env)] + } + + @classmethod + def run_acc(self, records): + step_accs = records.group('step').map(lambda step, step_records: + self._step_acc(step_records) + ).filter_not_none() + if len(step_accs): + return step_accs.argmax('val_acc') + else: + return None + +from domainbed.lib.query import Q +class PAIRIIDAccuracySelectionMethod(SelectionMethod): + """Model selection according to PAIR score from + Pareto Invariant Risk Minimization.""" + name = "pair training-domain validation set" + preference_base=1e-6 + + @classmethod + def _step_acc(self, record): + """Given a single record, return a {val_acc, test_acc} dict.""" + test_env = record['args']['test_envs'][0] + val_env_keys = [] + for i in itertools.count(): + if f'env{i}_out_acc' not in record: + break + if i != test_env: + val_env_keys.append(f'env{i}_out_acc') + test_in_acc_key = 'env{}_in_acc'.format(test_env) + + pair_score,losses = get_pair_score(record=record,get_loss=True,preference_base=self.preference_base) + return { + 'losses': losses, + 'pair_score': pair_score, + 'val_acc': np.mean([record[key] for key in val_env_keys]), + 'test_acc': record[test_in_acc_key] + } + + @classmethod + def run_acc(self, run_records): + """ + Given records from a run, return a {val_acc, test_acc} dict representing + the best val-acc and corresponding test-acc for that run. + """ + + test_records = get_test_records(run_records) + if not len(test_records): + return None + num_records = len(test_records) + + test_records = test_records.map(self._step_acc) + # filter out worst top_percentile% records in val acc to avoid trivial case + # return test_records.argmax('val_acc') + train_accs = [r['val_acc'] for r in test_records] + train_acc_bar = (np.max(train_accs)-np.min(train_accs))*0.8+np.min(train_accs) + pair_scores = [r['pair_score'] for r in test_records] + pair_score_bar = (np.max(pair_scores)-np.min(pair_scores))*0.9+np.min(pair_scores) + + if "coloredmnist" in run_records[0]['args']['dataset'].lower()or ("irm" in run_records[0]['args']['output_dir']): + test_records = Q(test_records[-5:]) + else: + test_records = Q(test_records[-10:]) + + return test_records.argmax(lambda x: x['val_acc']*(-1 if x['pair_score']0: + tmp_records.append(r) + self.preference_base = 10**int(np.log10(np.mean([np.min([np.abs(get_losses(r)[-1]) for r in rr]) for rr in tmp_records]))-2) + records = (records.group('args.hparams_seed') + .map(lambda _, run_records: + ( + self.run_acc(run_records), + run_records + ) + ).filter(lambda x: x[0] is not None) + ) + + num_records = len(records) + # filter out worst top_percentile% records in val acc to avoid trivial case + train_accs = [r[0]['val_acc'] for r in records] + train_acc_bar = (np.max(train_accs)-np.min(train_accs))*0.5+np.min(train_accs) + pair_scores = [r[0]['pair_score'] for r in records] + pair_score_bar = (np.max(pair_scores)-np.min(pair_scores))*0.9+np.min(pair_scores) + if "dann" not in records[0][1][0]['args']['output_dir'] and "groupdro" not in records[0][1][0]['args']['output_dir']: + return records.sorted(key=lambda x: x[0]['pair_score']*(1e8 if x[0]['val_acc'] 1: + return None + test_env = record['args']['test_envs'][0] + test_out_acc_key = 'env{}_out_acc'.format(test_env) + test_in_acc_key = 'env{}_in_acc'.format(test_env) + train_accs = [] + for i in range(1,10): + if i == test_env or 'env{}_out_acc'.format(i) not in record.keys(): + continue + train_accs.append(record['env{}_out_acc'.format(i)]) + pair_score,losses = get_pair_score(record=record,get_loss=True,preference_base=self.preference_base) + return { + 'losses': losses, + 'train_acc': np.mean(train_accs), + 'pair_score': pair_score, + 'val_acc': record[test_out_acc_key], + 'test_acc': record[test_in_acc_key] + } + + @classmethod + def run_acc(self, run_records): + """ + Given records from a run, return a {val_acc, test_acc} dict representing + the best val-acc and corresponding test-acc for that run. + """ + run_records = run_records.filter(lambda r: + len(r['args']['test_envs']) == 1) + if not len(run_records): + return None + + test_records = get_test_records(run_records) + if not len(test_records): + return None + num_records = len(test_records) + test_records = test_records.map(self._step_acc) + train_acc_bar = 0 + train_accs = [r['train_acc'] for r in test_records] + train_acc_bar = (np.max(train_accs)-np.min(train_accs))*0.1+np.min(train_accs) + + erm_bar = 1 + erm_losses = [r['losses'][0] for r in test_records] + erm_bar = (np.max(erm_losses)-np.min(erm_losses))*0.8+np.min(erm_losses) + + pair_scores = [r['pair_score'] for r in test_records] + pair_score_bar = (np.max(pair_scores)-np.min(pair_scores))*0.9+np.min(pair_scores) + + for r in test_records: + r['train_bar']=train_acc_bar + r['erm_bar']=erm_bar + r['pair_score_bar']=pair_score_bar + + if "dann" in run_records[0]['args']['output_dir']: + test_records = Q(test_records[-5:]) + else: + test_records = Q(test_records[-10:]) + + return test_records.argmax('pair_score') + return test_records[-1] + + @classmethod + def hparams_accs(self, records): + """ + Given all records from a single (dataset, algorithm, test env) pair, + return a sorted list of (run_acc, records) tuples. + """ + tmp_records = [] + for r in records.group('args.hparams_seed'): + r = get_test_records(r[1]) + if len(r)>0: + tmp_records.append(r) + + self.preference_base = 10**int(np.log10(np.mean([np.min([np.abs(get_losses(r)[-1]) for r in rr]) for rr in tmp_records]))-2) + return (records.group('args.hparams_seed') + .map(lambda _, run_records: + ( + self.run_acc(run_records), + run_records + ) + ).filter(lambda x: x[0] is not None) + .sorted(key=lambda x: x[0]['val_acc'])[::-1] + ) + + @classmethod + def sweep_acc(self, records): + """ + Given all records from a single (dataset, algorithm, test env) pair, + return the mean test acc of the k runs with the top val accs. + """ + _hparams_accs = self.hparams_accs(records) + if len(_hparams_accs): + return _hparams_accs[0][0]['test_acc'] + else: + return None + +class PAIRLeaveOneOutSelectionMethod(SelectionMethod): + """Model selection according to PAIR score from + Pareto Invariant Risk Minimization.""" + name = "pair leave-one-domain-out cross-validation" + + @classmethod + def _step_acc(self, records): + """Return the {val_acc, test_acc} for a group of records corresponding + to a single step.""" + test_records = get_test_records(records) + if len(test_records) != 1: + return None + + test_env = test_records[0]['args']['test_envs'][0] + n_envs = 0 + for i in itertools.count(): + if f'env{i}_out_acc' not in records[0]: + break + n_envs += 1 + val_accs = np.zeros(n_envs) - 1 + pair_scores = np.zeros(n_envs) - 1 + # it implicitly assumes there is a test env, and n-1 training env + # hence given n envs, it does the eval with all 2-test-env combinations + for r in records.filter(lambda r: len(r['args']['test_envs']) == 2): + val_env = (set(r['args']['test_envs']) - set([test_env])).pop() + val_accs[val_env] = r['env{}_in_acc'.format(val_env)] + pair_scores[val_env] = get_pair_score(r) + + val_accs = list(val_accs[:test_env]) + list(val_accs[test_env+1:]) + if any([v==-1 for v in val_accs]): + return None + val_acc = np.sum(val_accs) / (n_envs-1) + pair_score = np.sum(pair_scores) / (n_envs-1) + return { + 'pair_score': pair_score, + 'val_acc': val_acc, + 'test_acc': test_records[0]['env{}_in_acc'.format(test_env)] + } + + + @classmethod + def run_acc(self, records): + step_accs = records.group('step').map(lambda step, step_records: + self._step_acc(step_records) + ).filter_not_none() + + if len(step_accs): + num_records = len(step_accs) + # filter out worst top_percentile% records in val acc to avoid trivial case + step_accs = Q(step_accs.sorted(key=lambda x: x['val_acc'])[int(num_records*top_percentile):]) + return step_accs.argmax('pair_score') + else: + return None diff --git a/Extrapolation/pair.py b/Extrapolation/pair.py new file mode 100644 index 0000000..947f69d --- /dev/null +++ b/Extrapolation/pair.py @@ -0,0 +1,452 @@ +import copy +import imp +from pickletools import optimize +import torch +from torch.optim.optimizer import Optimizer, required +from torch.autograd import Variable +import traceback +import torch.nn.functional as F +from torch.optim import SGD + +class PAIR(Optimizer): + r""" + Implements Pareto Invariant Risk Minimization (PAIR) algorithm. + It is proposed in the ICLR 2023 paper + `Pareto Invariant Risk Minimization: Towards Mitigating the Optimization Dilemma in Out-of-Distribution Generalization` + https://arxiv.org/abs/2206.07766 . + + Arguments: + params (iterable): iterable of parameters to optimize or dicts defining parameter groups + optimizer (pytorch optim): inner optimizer + balancer (str, optional): indicates which MOO solver to use + preference (list[float], optional): preference of the objectives + eps (float, optional): precision up to the preference (default: 1e-04) + coe (float, optional): L2 regularization weight onto the yielded objective weights (default: 0) + """ + + def __init__(self, params, optimizer=required, balancer="EPO",preference=[1e-8,1-1e-8], eps=1e-4, coe=0, verbose=False): + # TODO: parameter validty checking + if eps < 0.0: + raise ValueError("Invalid epsilon value: {}".format(eps)) + for _pp in preference: + if _pp < 0.0: + raise ValueError("Invalid preference: {}".format(preference)) + + self.optimizer = optimizer + if type(preference) == list: + preference = np.array(preference) + self.preference = preference + + self.descent = 0 + self.losses = [] + self.params = params + if balancer.lower() == "epo": + self.balancer = EPO(len(self.preference),self.preference,eps=eps,coe=coe,verbose=verbose) + elif balancer.lower() == "sepo": + self.balancer = SEPO(len(self.preference),self.preference,eps=eps,coe=coe,verbose=verbose) + else: + raise NotImplementedError("Nrot supported balancer") + defaults = dict(balancer=balancer, preference=self.preference, eps=eps) + super(PAIR, self).__init__(params, defaults) + + + def __setstate__(self, state): + super(PAIR, self).__setstate__(state) + + def set_losses(self,losses): + self.losses = losses + + def step(self, closure=None): + r"""Performs a single optimization step. + Arguments: + closure (callable, optional): A closure that reevaluates the model + and returns the loss. + """ + if len(self.losses) == 0: + self.optimizer.step() + alphas = np.zeros(len(self.preference)) + alphas[0] = 1 + return -1, 233, alphas + else: + losses = self.losses + if closure is not None: + losses = closure() + + pair_loss = 0 + mu_rl = 0 + alphas = 0 + + grads = [] + for cur_loss in losses: + self.optimizer.zero_grad() + cur_loss.backward(retain_graph=True) + cur_grad = [] + for group in self.param_groups: + for param in group['params']: + if param.grad is not None: + cur_grad.append(Variable(param.grad.data.clone().flatten(), requires_grad=False)) + grads.append(torch.cat(cur_grad)) + + G = torch.stack(grads) + if self.get_grad_sim: + grad_sim = get_grad_sim(G,losses,preference=self.preference,is_G=True) + GG = G @ G.T + moo_losses = np.stack([l.item() for l in losses]) + reset_optimizer = False + try: + # Calculate the alphas from the LP solver + alpha, mu_rl, reset_optimizer = self.balancer.get_alpha(moo_losses, G=GG.cpu().numpy(), C=True,get_mu=True) + if self.balancer.last_move == "dom": + self.descent += 1 + print("dom") + except Exception as e: + print(traceback.format_exc()) + alpha = None + if alpha is None: # A patch for the issue in cvxpy + alpha = self.preference / np.sum(self.preference) + + scales = torch.from_numpy(alpha).float().to(losses[-1].device) + pair_loss = scales.dot(losses) + if reset_optimizer: + self.optimizer.param_groups[0]["lr"]/=5 + # self.optimizer = torch.optim.Adam(self.params,lr=self.optimizer.param_groups[0]["lr"]/5) + self.optimizer.zero_grad() + pair_loss.backward() + self.optimizer.step() + + return pair_loss, moo_losses, mu_rl, alpha + + + +import numpy as np +import cvxpy as cp +import cvxopt + +class EPO(object): + r""" + The original EPO solver proposed in ICML2020 + https://proceedings.mlr.press/v119/mahapatra20a.html + """ + def __init__(self, m, r, eps=1e-4, coe=0, verbose=False): + # self.solver = cp.GLPK + self.solver = cp.GUROBI + # cvxopt.glpk.options["msg_lev"] = "GLP_MSG_OFF" + self.m = m + self.r = r/np.sum(r) + self.eps = eps + self.last_move = None + self.a = cp.Parameter(m) # Adjustments + self.C = cp.Parameter((m, m)) # C: Gradient inner products, G^T G + self.Ca = cp.Parameter(m) # d_bal^TG + self.rhs = cp.Parameter(m) # RHS of constraints for balancing + + self.alpha = cp.Variable(m) # Variable to optimize + self.last_alpha = np.zeros_like(r)-1 + self.coe = coe + + obj_bal = cp.Maximize(self.alpha @ self.Ca) # objective for balance + constraints_bal = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Simplex + self.C @ self.alpha >= self.rhs] + self.prob_bal = cp.Problem(obj_bal, constraints_bal) # LP balance + + obj_dom = cp.Maximize(cp.sum(self.alpha @ self.C)-self.coe*cp.sum_squares(self.alpha-self.last_alpha)) # obj for descent + constraints_dom = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Restrict + self.alpha @ self.Ca >= -cp.neg(cp.max(self.Ca)), + self.C @ self.alpha >= 0] + self.prob_dom = cp.Problem(obj_dom, constraints_dom) # LP dominance + + + self.gamma = 0 # Stores the latest Optimum value of the LP problem + self.mu_rl = 0 # Stores the latest non-uniformity + + self.verbose = verbose + + + def get_alpha(self, l, G, r=None, C=False, get_mu=False): + """calculate weights for all objectives given the gradient information + + Args: + l (ndarray): the values of objective losses + G (ndarray): inner products of the gradients of each objective loss w.r.t. params + r (ndarray, optional): adopt this preference if specified + C (bool, optional): True if the input gradients are inner products + get_mu (bool, optional): return detailed information if True. + + Returns: + alpha: the objective weights + mu_rl (optional): the optimal value to the LP + reset_optimizer (optional): whether to reset the inner optimizer + """ + r = self.r if r is None else r + assert len(l) == len(G) == len(r) == self.m, "length != m" + l_hat, rl, self.mu_rl, self.a.value = self.adjustments(l, r) + reset_optimizer = False + self.C.value = G if C else G @ G.T + self.Ca.value = self.C.value @ self.a.value + + if self.last_alpha.sum() is None: + self.last_alpha = np.array(r) + if self.mu_rl > self.eps: + J = self.Ca.value > 0 + J_star_idx = np.where(rl == np.max(rl))[0] + self.rhs.value = self.Ca.value.copy() + # it's equivalent to setting no constraints to objectives in J + # as maximize alpha^TCa would trivially satisfy the non-negativity + self.rhs.value[J] = -np.inf + self.rhs.value[J_star_idx] = 0 + + self.gamma = self.prob_bal.solve(solver=self.solver, verbose=False) + self.last_move = "bal" + + if self.verbose: + test_alpha = np.ones_like(self.a.value)/self.m + print(self.last_alpha,self.C.value,self.Ca.value,self.rhs.value) + print(self.gamma,test_alpha@self.Ca.value, self.alpha.value @ self.C.value) + print(self.gamma,self.coe*np.linalg.norm(self.alpha.value-self.last_alpha)**2) + + else: + self.gamma = self.prob_dom.solve(solver=self.solver, verbose=False) + self.last_move = "dom" + self.last_alpha = np.array(self.alpha.value) + + if get_mu: + return self.alpha.value, self.mu_rl, reset_optimizer + + return self.alpha.value + + + def mu(self, rl, normed=False): + if len(np.where(rl < 0)[0]): + raise ValueError(f"rl<0 \n rl={rl}") + return None + m = len(rl) + l_hat = rl if normed else rl / rl.sum() + eps = np.finfo(rl.dtype).eps + l_hat = l_hat[l_hat > eps] + return np.sum(l_hat * np.log(l_hat * m)) + + + def adjustments(self, l, r=1): + m = len(l) + rl = r * l + + l_hat = rl / rl.sum() + mu_rl = self.mu(l_hat, normed=True) + uniformity_div = np.log(l_hat * m) - mu_rl + div_r = np.array(r) + a = div_r * uniformity_div + + if self.verbose: + print(a, rl, div_r, uniformity_div, l_hat, a.dot(l)) + return l_hat, rl, mu_rl, a + + +class SEPO(object): + r""" + A smoothed variant of EPO, with two adjustments for unrobust OOD objectives: + a) normalization: unrobust OOD objective can yield large loss values that dominate the solutions of the LP, + hence we adopt the normalized OOD losses in the LP to resolve the issue + b) regularization: solutions yielded by the LP can change sharply among steps, especially when switching descending phases + hence we incorporate a L2 regularization in the LP to resolve the issue + """ + def __init__(self, m, r, eps=1e-4, coe=0, verbose=False): + # self.solver = cp.GLPK + self.solver = cp.GUROBI + # cvxopt.glpk.options["msg_lev"] = "GLP_MSG_OFF" + self.m = m + self.r = r/np.sum(r) + self.eps = eps + self.last_move = None + self.a = cp.Parameter(m) # Adjustments + self.C = cp.Parameter((m, m)) # C: Gradient inner products, G^T G + self.Ca = cp.Parameter(m) # d_bal^TG + self.rhs = cp.Parameter(m) # RHS of constraints for balancing + + self.alpha = cp.Variable(m) # Variable to optimize + self.last_alpha = np.zeros_like(r)-1 + self.coe = coe + + obj_bal = cp.Maximize(self.alpha @ self.Ca-self.coe*cp.sum_squares(self.alpha-self.last_alpha)) # objective for balance + obj_bal_orig = cp.Maximize(self.alpha @ self.Ca) # objective for balance + constraints_bal = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Simplex + self.C @ self.alpha >= self.rhs] + self.prob_bal = cp.Problem(obj_bal, constraints_bal) # LP balance + self.prob_bal_orig = cp.Problem(obj_bal_orig, constraints_bal) # LP balance + + obj_dom = cp.Maximize(cp.sum(self.alpha @ self.C)-self.coe*cp.sum_squares(self.alpha-self.last_alpha)) # obj for descent + constraints_res = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Restrict + self.alpha @ self.Ca >= -cp.neg(cp.max(self.Ca)), + self.C @ self.alpha >= 0] + constraints_rel = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Relaxed + self.C @ self.alpha >= 0] + self.prob_dom = cp.Problem(obj_dom, constraints_res) # LP dominance + self.prob_rel = cp.Problem(obj_dom, constraints_rel) # LP dominance + + self.gamma = 0 # Stores the latest Optimum value of the LP problem + self.mu_rl = 0 # Stores the latest non-uniformity + + self.verbose = verbose + + + def get_alpha(self, l, G, r=None, C=False, get_mu=False): + """calculate weights for all objectives given the gradient information + + Args: + l (ndarray): the values of objective losses + G (ndarray): inner products of the gradients of each objective loss w.r.t. params + r (ndarray, optional): adopt this preference if specified + C (bool, optional): True if the input gradients are inner products + get_mu (bool, optional): return detailed information if True. + + Returns: + alpha: the objective weights + mu_rl (optional): the optimal value to the LP + reset_optimizer (optional): whether to reset the inner optimizer + """ + r = self.r if r is None else r + assert len(l) == len(G) == len(r) == self.m, "length != m" + l_hat, rl, self.mu_rl, self.a.value = self.adjustments(l, r) + reset_optimizer = False + if self.mu_rl <= 0.1: + self.r[0]=max(1e-15,self.r[0]/10000) + self.r = self.r/self.r.sum() + print(f"pua preference {self.r}") + l_hat, rl, self.mu_rl, self.a.value = self.adjustments(l, r) + + + a_norm = np.linalg.norm(self.a.value) + G_norm = np.linalg.norm(G,axis=1) + Ga = G.T @ self.a.value + self.C.value = G if C else G/np.expand_dims(G_norm,axis=1) @ G.T/a_norm + self.Ca.value = G/np.expand_dims(G_norm,axis=1) @ Ga.T/a_norm + + if self.last_alpha.sum() is None: + self.last_alpha = np.array(r) + if self.mu_rl > self.eps: + J = self.Ca.value > 0 + + J_star_idx = np.where(rl == np.max(rl))[0] + self.rhs.value = self.Ca.value.copy() + # it's equivalent to setting no constraints to objectives in J + # as maximize alpha^TCa would trivially satisfy the non-negativity + self.rhs.value[J] = -np.inf # Not efficient; but works. + self.rhs.value[J_star_idx] = max(0,self.Ca.value[J_star_idx]/2) + + if self.last_alpha.sum()<0: + self.gamma = self.prob_bal_orig.solve(solver=self.solver, verbose=False) + else: + self.gamma = self.prob_bal.solve(solver=self.solver, verbose=False) + + self.last_move = "bal" + + if self.verbose: + test_alpha = np.ones_like(self.a.value)/self.m + print(self.last_alpha,self.C.value,self.Ca.value,self.rhs.value) + print(self.gamma,test_alpha@self.Ca.value, self.alpha.value @ self.C.value) + print(self.gamma,self.coe*np.linalg.norm(self.alpha.value-self.last_alpha)**2) + else: + self.gamma = self.prob_dom.solve(solver=self.solver, verbose=False) + self.last_move = "dom" + self.last_alpha = np.array(self.alpha.value) + + if get_mu: + return self.alpha.value, self.mu_rl, reset_optimizer + + return self.alpha.value + + + def mu(self, rl, normed=False): + if len(np.where(rl < 0)[0]): + raise ValueError(f"rl<0 \n rl={rl}") + return None + m = len(rl) + l_hat = rl if normed else rl / rl.sum() + eps = np.finfo(rl.dtype).eps + l_hat = l_hat[l_hat > eps] + return np.sum(l_hat * np.log(l_hat * m)) + + + def adjustments(self, l, r=1): + m = len(l) + rl = r * l + + l_hat = rl / rl.sum() + mu_rl = self.mu(l_hat, normed=True) + uniformity_div = np.log(l_hat * m) - mu_rl + div_r = np.array(r) + a = div_r * uniformity_div + + if self.verbose: + print(a, rl, div_r, uniformity_div, l_hat, a.dot(l)) + return l_hat, rl, mu_rl, a + + +def getNumParams(params): + numParams, numTrainable = 0, 0 + for param in params: + npParamCount = np.prod(param.data.shape) + numParams += npParamCount + if param.requires_grad: + numTrainable += npParamCount + return numParams, numTrainable + +def get_kl_div(losses, preference): + pair_score = losses.dot(preference) + return pair_score + +def pair_selection(losses,val_accs,test_accs,anneal_iter=0,val_acc_bar=-1,pood=None): + + losses = losses[anneal_iter:] + val_accs = val_accs[anneal_iter:] + test_accs = test_accs[anneal_iter:] + if val_acc_bar < 0: + val_acc_bar = (np.max(val_accs)-np.min(val_accs))*0.05+np.min(val_accs) + + try: + preference_base = 10**max(-12,int(np.log10(np.mean(losses[:,-1]))-2)) + except Exception as e: + print(e) + preference_base = 1e-12 + if len(losses[0])==2: + preference = np.array([preference_base,1]) + elif len(losses[0])==4: + preference = np.array([1e-12,1e-4,1e-2,1]) + elif len(losses[0])==5: + preference = np.array([1e-12,1e-6,1e-4,1e-2,1]) + else: + preference = np.array([1e-12,1e-2,1]) + + if pood is not None: + preference = pood + print(f"Use preference: {preference}, validation acc bar: {val_acc_bar}") + + pair_score = np.array([get_kl_div(l,preference) if a>=val_acc_bar else 1e9 for (a,l) in zip(val_accs,losses)]) + sel_idx = np.argmin(pair_score) + return sel_idx+anneal_iter, val_accs[sel_idx], test_accs[sel_idx] + +def get_grad_sim(params,losses,preference=None,is_G=False,cosine=True): + num_ood_losses = len(losses)-1 + if is_G: + G = params + else: + pesudo_opt = SGD(params,lr=1e-6) + grads = [] + for cur_loss in losses: + pesudo_opt.zero_grad() + cur_loss.backward(retain_graph=True) + cur_grad = [] + for param in params: + if param.grad is not None: + cur_grad.append(Variable(param.grad.data.clone().flatten(), requires_grad=False)) + # print(torch.cat(cur_grad).sum()) + grads.append(torch.cat(cur_grad)) + G = torch.stack(grads) + if cosine: + G = F.normalize(G,dim=1) + GG = (G @ G.T).cpu() + if preference is not None: + G_weights = preference[1:]/np.sum(preference[1:]) + else: + G_weights = np.ones(num_ood_losses)/num_ood_losses + grad_sim =G_weights.dot(GG[0,1:]) + return grad_sim.item() diff --git a/Extrapolation/pair_extrapolation.ipynb b/Extrapolation/pair_extrapolation.ipynb new file mode 100644 index 0000000..664d6b5 --- /dev/null +++ b/Extrapolation/pair_extrapolation.ipynb @@ -0,0 +1,851 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "7XrhbNoymf6H" + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import torch as pt\n", + "from torch import nn\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.patches as patches\n", + "import matplotlib.cm as cm\n", + "from torch.utils.data import TensorDataset, DataLoader\n", + "import sys\n", + "import torch\n", + "import argparse\n", + "from tqdm import tqdm\n", + "from torch.autograd import Variable\n", + "from pair import PAIR\n", + "import os\n", + "\n", + "os.environ['CUDA_VISIBLE_DEVICES'] = '1'\n", + "\n", + "# prepare data generating hyper-parameters\n", + "seed = 2022\n", + "sample_size = [5000, 1000]\n", + "batch_size = 512\n", + "coffe = 2\n", + "n_epoch = 10000\n", + "anneal_epoch = 150\n", + "algorithm = \"erm\"\n", + "penalty_weight = 1\n", + "opt = \"Adam\"\n", + "sampling = \"gaussian\"\n", + "is_uniform = False\n", + "if is_uniform:\n", + " x1_l = -3\n", + " x1_r = 1\n", + " y1_l = -3\n", + " y1_r = -2\n", + "\n", + " x2_l = -1\n", + " x2_r = 3\n", + " y2_l = 2\n", + " y2_r = 3\n", + "else:\n", + " mean1 = (-0.9, -2.2)\n", + " cov1 = [[0.9, 0.11], [0.11, 0.1]]\n", + " mean2 = (1, 2)\n", + " cov2 = [[1, -0.3], [-0.3, 0.1]]\n", + "\n", + "# Choosing and saving a random seed for reproducibility\n", + "device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\n", + "if seed == -1:\n", + " seed = int(torch.randint(0, 2 ** 32 - 1, (1,)).item())\n", + "torch.manual_seed(seed)\n", + "np.random.seed(seed)\n", + "torch.cuda.manual_seed_all(seed)\n", + "torch.manual_seed(seed)\n", + "torch.backends.cudnn.deterministic = True\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 281 + }, + "id": "xFuvbvXQA_yI", + "outputId": "e51a217e-21a3-4f01-81ff-9354fce46901" + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "def f_eval(x):\n", + " x = np.asarray(x, dtype=np.float32)\n", + " x = np.delete(x, 1, 1)\n", + " return np.sin(coffe * x) + 1\n", + "\n", + "def f_eval2(x):\n", + " x = np.asarray(x, dtype=np.float32)\n", + " return np.sin(coffe * x[0]) + 1\n", + "\n", + "\n", + "\n", + "class MLP(nn.Module):\n", + " def __init__(self):\n", + " super(MLP, self).__init__()\n", + " self.lin1 = nn.Linear(2, 128)\n", + " self.lin2 = nn.Linear(128, 128)\n", + " self.lin3 = nn.Linear(128, 1)\n", + " for lin in [self.lin1, self.lin2, self.lin3]:\n", + " nn.init.xavier_uniform_(lin.weight)\n", + " nn.init.zeros_(lin.bias)\n", + " self._main = nn.Sequential(self.lin1, nn.ReLU(True), self.lin2, nn.ReLU(True), self.lin3)\n", + " self.optimizer= pt.optim.Adam(self.parameters(), lr=1e-3)\n", + "\n", + " self.eval()\n", + " r2, r = 1e4, 1e-12\n", + " self.preference = np.array([r]*1+[(1-1*r-r2*r),r2*r])\n", + " self.eps =1e-1\n", + " self.n_tasks = 3\n", + " self.pair_optimizer = PAIR(self.parameters(),self.optimizer,preference=self.preference,eps=self.eps)\n", + " self.descent = 0\n", + "\n", + " def reset_parameters(self):\n", + " self.lin1.reset_parameters()\n", + " self.lin2.reset_parameters()\n", + " self.lin3.reset_parameters()\n", + " self.optimizer= pt.optim.Adam(self.parameters(), lr=1e-3)\n", + "\n", + " def update_preference(self,preference):\n", + " self.preference=preference\n", + " self.pair_optimizer = PAIR(self.parameters(),self.optimizer,preference=self.preference,eps=self.eps)\n", + " self.descent = 0\n", + "\n", + " def forward(self, x, to_numpy=False):\n", + " x = pt.as_tensor(x, dtype=pt.float32).to(device)\n", + " out = self._main(x)\n", + " if to_numpy:\n", + " out = out.to('cpu').detach().numpy()\n", + " return out\n", + " \n", + " def train_step(self, x, y):\n", + " self.train()\n", + " x = pt.as_tensor(x, dtype=pt.float32).to(device)\n", + " y = pt.as_tensor(y, dtype=pt.float32).to(device)\n", + " \n", + " self.optimizer.zero_grad()\n", + " y_pred = self.forward(x)\n", + " loss = nn.functional.mse_loss(y_pred, y)\n", + " loss.backward()\n", + " self.optimizer.step()\n", + " self.eval()\n", + " return loss.item()\n", + " \n", + " def train_step_ood(self, x1, y1, x2, y2, apply_ood_obj=False, penalty_weight=1e4):\n", + " self.train()\n", + " x1 = pt.as_tensor(x1, dtype=pt.float32).to(device)\n", + " y1 = pt.as_tensor(y1, dtype=pt.float32).to(device)\n", + " x2 = pt.as_tensor(x2, dtype=pt.float32).to(device)\n", + " y2 = pt.as_tensor(y2, dtype=pt.float32).to(device)\n", + "\n", + " self.optimizer.zero_grad()\n", + " y1_pred = self.forward(x1)\n", + " loss1 = nn.functional.mse_loss(y1_pred, y1)\n", + " y2_pred = self.forward(x2)\n", + " loss2 = nn.functional.mse_loss(y2_pred, y2)\n", + "\n", + " import torch.autograd as autograd\n", + "\n", + " scale = torch.tensor(1.).to(device).requires_grad_()\n", + " losses1 = nn.functional.mse_loss(y1_pred*scale, y1,reduction='none')\n", + " losses2 = nn.functional.mse_loss(y2_pred*scale, y2,reduction='none')\n", + " grad_1 = autograd.grad(losses1.mean(), [scale], create_graph=True)[0]\n", + " grad_2 = autograd.grad(losses2.mean(), [scale], create_graph=True)[0]\n", + " irm_penalty = pt.stack([pt.sum(grad_1**2), pt.sum(grad_2**2)]).mean()\n", + " vrex_penalty = pt.stack([loss1, loss2]).var()\n", + "\n", + " erm_loss=pt.stack([loss1, loss2]).mean()\n", + "\n", + " if algorithm.lower() == 'vrex':\n", + " penalty = vrex_penalty\n", + " elif algorithm.lower() == 'pair':\n", + " penalty = vrex_penalty\n", + " else:\n", + " penalty = irm_penalty\n", + " losses = torch.stack([erm_loss,irm_penalty,vrex_penalty]).to(device)\n", + " if apply_ood_obj and algorithm.lower() == 'pair':\n", + " self.pair_optimizer.zero_grad()\n", + " self.pair_optimizer.set_losses(losses=losses)\n", + " loss, moo_losses, mu_rl, alphas = self.pair_optimizer.step()\n", + " else:\n", + " loss = erm_loss+penalty*(penalty_weight if apply_ood_obj else 1)\n", + " if apply_ood_obj and penalty_weight>0:\n", + " loss /= penalty_weight\n", + "\n", + " loss.backward()\n", + " self.optimizer.step()\n", + " loss = loss.item()\n", + " self.eval()\n", + " return loss, losses\n", + " \n", + " def valid_loss(self, x, y):\n", + " self.eval()\n", + " with pt.no_grad():\n", + " x = pt.as_tensor(x, dtype=pt.float32).to(device)\n", + " y = pt.as_tensor(y, dtype=pt.float32).to(device)\n", + "\n", + " y_pred = self.forward(x)\n", + " loss = nn.functional.mse_loss(y_pred, y)\n", + " self.train()\n", + " return y_pred.detach(), loss.item()\n", + "\n", + "def prepare_data(gen):\n", + " train_x = []\n", + " train_y = []\n", + " if is_uniform:\n", + " t_x1 = gen.uniform(x1_l, x1_r, [int(sample_size[0]/2), 1])\n", + " t_y1 = gen.uniform(y1_l, y1_r, [int(sample_size[0]/2), 1])\n", + " train_x.append(np.hstack((t_x1, t_y1)))\n", + " t_x2 = gen.uniform(x2_l, x2_r, [int(sample_size[0]/2), 1])\n", + " t_y2 = gen.uniform(y2_l, y2_r, [int(sample_size[0]/2), 1])\n", + " train_x.append(np.hstack((t_x2, t_y2)))\n", + " else:\n", + " t_xy1 = gen.multivariate_normal(mean1, cov1, size=int(sample_size[0]/2))\n", + " train_x.append(t_xy1)\n", + " t_xy2 = gen.multivariate_normal(mean2, cov2, size=int(sample_size[0]/2))\n", + " train_x.append(t_xy2)\n", + " valid_x = gen.uniform(-4, 4, [sample_size[1], 2])\n", + " train_y.append(f_eval(train_x[0]))\n", + " train_y.append(f_eval(train_x[1]))\n", + " valid_y = f_eval(valid_x)\n", + " return train_x, valid_x, train_y, valid_y\n", + "\n", + "def print_truth():\n", + " grid_c = 200\n", + " x = np.linspace(-4, 4, grid_c)\n", + " y = np.linspace(-4, 4, grid_c)\n", + " xy = np.meshgrid(x, y)\n", + " f = f_eval2(xy)\n", + " fig, ax = plt.subplots()\n", + " ax.set_title(\"ground truth\")\n", + " ax.set_xlabel(\"$x_1$\")\n", + " ax.set_ylabel(\"$x_2$\")\n", + "\n", + " im = ax.imshow(np.array(f), cmap = cm.jet, vmin=0., vmax=2., origin = 'lower', extent=[-4, 4, -4, 4])\n", + " if is_uniform:\n", + " eps = 0.05\n", + " rect1 = patches.Rectangle((x1_l+eps,y1_l+eps),(x1_r-x1_l-2*eps),(y1_r-y1_l-2*eps),linewidth=1,edgecolor='r',facecolor='none')\n", + " rect2 = patches.Rectangle((x2_l+eps,y2_l+eps),(x2_r-x2_l-2*eps),(y2_r-y2_l-2*eps),linewidth=1,edgecolor='r',facecolor='none')\n", + " ax.add_patch(rect1)\n", + " ax.add_patch(rect2)\n", + " else:\n", + " w, v = np.linalg.eig(cov1)\n", + " theta = np.sign(cov1[0][1]) * np.degrees(np.arccos(v[0,0]))\n", + " ellip1 = patches.Ellipse(mean1, 6*np.sqrt(np.abs(w[0])),6*np.sqrt(np.abs(w[1])), angle=theta,linewidth=1,edgecolor='r',facecolor='none')\n", + " w, v = np.linalg.eig(cov2)\n", + " theta = np.sign(cov2[0][1]) * np.degrees(np.arccos(v[0,0]))\n", + " ellip2 = patches.Ellipse(mean2, 6*np.sqrt(np.abs(w[0])),6*np.sqrt(np.abs(w[1])), angle=theta,linewidth=1,edgecolor='r',facecolor='none')\n", + " ax.add_patch(ellip1)\n", + " ax.add_patch(ellip2)\n", + " # plt.tight_layout()\n", + " fig.colorbar(im, shrink=0.4, aspect=9)\n", + " plt.show()\n", + " # plt.savefig(f'extrapolate_truth_s{sampling}.png')\n", + "\n", + "def print_samples(train_x):\n", + " fig, ax = plt.subplots()\n", + " ax.set_title(\"training samples\")\n", + " plt.plot(np.vstack(train_x)[:, 0], np.vstack(train_x)[:, 1], '.', alpha=0.5)\n", + " if is_uniform:\n", + " eps = 0.05\n", + " rect1 = patches.Rectangle((x1_l+eps,y1_l+eps),(x1_r-x1_l-2*eps),(y1_r-y1_l-2*eps),linewidth=1,edgecolor='r',facecolor='none')\n", + " rect2 = patches.Rectangle((x2_l+eps,y2_l+eps),(x2_r-x2_l-2*eps),(y2_r-y2_l-2*eps),linewidth=1,edgecolor='r',facecolor='none')\n", + " ax.add_patch(rect1)\n", + " ax.add_patch(rect2)\n", + " else:\n", + " w, v = np.linalg.eig(cov1)\n", + " theta = np.sign(cov1[0][1]) * np.degrees(np.arccos(v[0,0]))\n", + " ellip1 = patches.Ellipse(mean1, 6*np.sqrt(np.abs(w[0])),6*np.sqrt(np.abs(w[1])), angle=theta,linewidth=1,edgecolor='r',facecolor='none')\n", + " w, v = np.linalg.eig(cov2)\n", + " theta = np.sign(cov2[0][1]) * np.degrees(np.arccos(v[0,0]))\n", + " ellip2 = patches.Ellipse(mean2, 6*np.sqrt(np.abs(w[0])),6*np.sqrt(np.abs(w[1])), angle=theta,linewidth=1,edgecolor='r',facecolor='none')\n", + " ax.add_patch(ellip1)\n", + " ax.add_patch(ellip2)\n", + " plt.show()\n", + "\n", + "@torch.no_grad()\n", + "def print_predict(model, valid_loss, exp_name=None):\n", + " model.eval()\n", + " grid_c = 200\n", + " x = np.linspace(-4, 4, grid_c)\n", + " y = np.linspace(-4, 4, grid_c)\n", + " f = []\n", + " for _y in y:\n", + " f_r = []\n", + " for _x in x:\n", + " f_r.append(model.forward(x=[_x,_y], to_numpy=True))\n", + " f.append(f_r)\n", + " fig, ax = plt.subplots()\n", + " if exp_name != None:\n", + " ax.set_title(f\"{exp_name} {valid_loss}\")\n", + " else:\n", + " ax.set_title(f\"valid_loss {valid_loss}\")\n", + "\n", + " im = ax.imshow(np.array(f).squeeze(-1), cmap = cm.jet, vmin=0., vmax=2., origin = 'lower', extent=[-4, 4, -4, 4])\n", + " if is_uniform:\n", + " eps = 0.05\n", + " rect1 = patches.Rectangle((x1_l+eps,y1_l+eps),(x1_r-x1_l-2*eps),(y1_r-y1_l-2*eps),linewidth=1,edgecolor='r',facecolor='none')\n", + " rect2 = patches.Rectangle((x2_l+eps,y2_l+eps),(x2_r-x2_l-2*eps),(y2_r-y2_l-2*eps),linewidth=1,edgecolor='r',facecolor='none')\n", + " ax.add_patch(rect1)\n", + " ax.add_patch(rect2)\n", + " else:\n", + " w, v = np.linalg.eig(cov1)\n", + " theta = np.sign(cov1[0][1]) * np.degrees(np.arccos(v[0,0]))\n", + " ellip1 = patches.Ellipse(mean1, 6*np.sqrt(np.abs(w[0])),6*np.sqrt(np.abs(w[1])), angle=theta,linewidth=1,edgecolor='r',facecolor='none')\n", + " w, v = np.linalg.eig(cov2)\n", + " theta = np.sign(cov2[0][1]) * np.degrees(np.arccos(v[0,0]))\n", + " ellip2 = patches.Ellipse(mean2, 6*np.sqrt(np.abs(w[0])),6*np.sqrt(np.abs(w[1])), angle=theta,linewidth=1,edgecolor='r',facecolor='none')\n", + " ax.add_patch(ellip1)\n", + " ax.add_patch(ellip2)\n", + " # plt.tight_layout()\n", + " fig.colorbar(im, shrink=0.4, aspect=9)\n", + " plt.show()\n", + " # plt.savefig(f'extrapolate_{algorithm}_s{sampling}_{opt}_p{penalty_weight}.png')\n", + " model.train()\n", + "\n", + "@torch.no_grad()\n", + "def print_predict3D(model, valid_loss, exp_name=None):\n", + " model.eval()\n", + " grid_c = 200\n", + " x = np.linspace(-4, 4, grid_c)\n", + " y = np.linspace(-4, 4, grid_c)\n", + " f = []\n", + " for _y in y:\n", + " f_r = []\n", + " for _x in x:\n", + " f_r.append(model.forward(x=[_x,_y], to_numpy=True)[0])\n", + " f.append(f_r)\n", + " fig = plt.figure()\n", + " ax = plt.axes(projection='3d')\n", + " if exp_name != None:\n", + " ax.set_title(f\"{exp_name} {valid_loss}\")\n", + " else:\n", + " ax.set_title(f\"valid_loss {valid_loss}\")\n", + " X_grid, Y_grid = np.meshgrid(x, y)\n", + " f_grid = np.array(f)\n", + " print(f_grid.shape)\n", + " surf = ax.plot_surface(X_grid, Y_grid, np.array(f), cmap=cm.jet, vmin=0., vmax=2., edgecolors='None', antialiased=True, rcount=70, ccount=70)\n", + " fig.colorbar(surf, shrink=0.4, aspect=9)\n", + " ax.set_box_aspect((np.ptp(X_grid), np.ptp(Y_grid), np.ptp(f_grid)))\n", + " plt.clabel(surf)\n", + " plt.show()\n", + " model.train()\n", + "\n", + "rng = np.random.default_rng(seed)\n", + "train_x, valid_x, train_y, valid_y = prepare_data(rng)\n", + "model = MLP().cuda()\n", + "train_xx = np.vstack(train_x)\n", + "train_yy = np.vstack(train_y)\n", + "data_loader = DataLoader(TensorDataset(\n", + " pt.as_tensor(train_xx, dtype=pt.float32),\n", + " pt.as_tensor(train_yy, dtype=pt.float32)\n", + "), batch_size=batch_size, shuffle=True)\n", + "\n", + "data_loader1 = DataLoader(TensorDataset(\n", + " pt.as_tensor(train_x[0], dtype=pt.float32),\n", + " pt.as_tensor(train_y[0], dtype=pt.float32)\n", + "), batch_size=int(batch_size/2), shuffle=True)\n", + "data_loader2 = DataLoader(TensorDataset(\n", + " pt.as_tensor(train_x[1], dtype=pt.float32),\n", + " pt.as_tensor(train_y[1], dtype=pt.float32)\n", + "), batch_size=int(batch_size/2), shuffle=True)\n", + "\n", + "print_truth()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 281 + }, + "id": "c5uGpIPG6oNd", + "outputId": "8a2f1d7f-ae52-40d9-faa3-d1971497bab6" + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "print_samples(train_x)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TjBlN98I7uJL" + }, + "source": [ + "# ERM" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 307 + }, + "id": "CuFAW2bkBTwy", + "outputId": "8c7b5f0c-2b81-455e-a55c-4297b2380d03" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[10000|10000] train_loss=3.57042e-02, valid_loss=8.02226e-01: 100%|██████████| 10000/10000 [11:01<00:00, 15.12it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(200, 200)\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "def train_ERM():\n", + " pbar = tqdm(range(n_epoch))\n", + " for ep in pbar:\n", + " train_loss = 0\n", + " for sub_x, sub_y in data_loader:\n", + " sub_x=sub_x.to(device)\n", + " sub_y=sub_y.to(device)\n", + " loss = model.train_step(sub_x, sub_y)\n", + " train_loss += len(sub_y) * loss\n", + " train_loss /= len(train_y)\n", + "\n", + " y_pred, valid_loss = model.valid_loss(valid_x, valid_y)\n", + "\n", + " pbar.set_description(f'[{ep+1}|{n_epoch}] train_loss={train_loss:0.5e}, valid_loss={valid_loss:0.5e}')\n", + " \n", + " exp_name = f\"{algorithm}_s{sampling}_{opt}_p{penalty_weight}\"\n", + " # print_predict(model,valid_loss,exp_name=exp_name)\n", + " print_predict3D(model,valid_loss,exp_name=exp_name)\n", + "train_ERM()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "X-838VHgBVoI" + }, + "outputs": [], + "source": [ + "def train_OOD():\n", + " losses = []\n", + " pbar = tqdm(range(n_epoch))\n", + " for ep in pbar:\n", + " train_loss = 0\n", + " cur_losses = [0]*3\n", + " if ep == anneal_epoch:\n", + " if algorithm=='pair':\n", + " if opt.lower() == 'adam':\n", + " model.optimizer = pt.optim.Adam(model.parameters(), lr=1e-3)\n", + " elif opt.lower() == 'sgd':\n", + " model.optimizer = pt.optim.SGD(model.parameters(), lr=2e-3, momentum=0.9)\n", + " else:\n", + " model.optimizer = pt.optim.Adam(model.parameters(), lr=1e-3)\n", + " for _, ((sub_x, sub_y), (sub_x2, sub_y2)) in enumerate(zip(data_loader1, data_loader2)):\n", + " if ep < anneal_epoch:\n", + " loss, sep_losses = model.train_step_ood(sub_x, sub_y, sub_x2, sub_y2)\n", + " else:\n", + " loss, sep_losses = model.train_step_ood(sub_x, sub_y, sub_x2, sub_y2, apply_ood_obj=True, penalty_weight=penalty_weight)\n", + " train_loss += len(sub_y) * loss\n", + " cur_losses = [l1+len(sub_y)*l2.item() for (l1,l2) in zip(cur_losses,sep_losses)]\n", + " train_loss /= len(train_y)\n", + " losses.append([l/len(train_y) for l in cur_losses])\n", + "\n", + " _, valid_loss = model.valid_loss(valid_x, valid_y)\n", + " losses[-1][0] = valid_loss\n", + "\n", + " pbar.set_description(f'[{ep+1}|{n_epoch}] train_loss={train_loss:0.5e}, valid_loss={valid_loss:0.5e}')\n", + " \n", + " \n", + " exp_name = f\"{algorithm}_s{sampling}_{opt}_p{penalty_weight}\"\n", + " print_predict(model,valid_loss,exp_name=exp_name)\n", + " # print_predict3D(model,valid_loss,exp_name=exp_name)\n", + " plt.close()\n", + " num_epochs = len(losses)\n", + " fig, ax1 = plt.subplots()\n", + " ax1.set_title(exp_name+f\" {valid_loss}\")\n", + " ax1.set_xlabel(\"epoch\")\n", + " ax1.set_ylabel(\"val loss\")\n", + " # heuristic approach to beautify the visualization\n", + " erm_vis_max = np.max([log_i[0] for log_i in losses[140:200]])+1e9\n", + " erm_pens = np.array([min(log_i[0],erm_vis_max) for log_i in losses])\n", + " ax1.plot(np.arange(num_epochs),erm_pens,label=f'erm_pen')\n", + " ax2 = ax1.twinx()\n", + " ax2.set_ylabel(\"penalty\")\n", + " if len(losses[0])>=3:\n", + " irm_pens = np.array([min(log_i[-2],1) for log_i in losses])\n", + " vrex_pens = np.array([min(log_i[-1],1) for log_i in losses])\n", + " ax2.plot(np.arange(num_epochs),irm_pens,label=f'irm_pen',c='r',alpha=0.2)\n", + " ax2.plot(np.arange(num_epochs),vrex_pens,label=f'vrex_pen',c='g',alpha=0.2)\n", + " else:\n", + " irm_pens = np.array([log_i[-1] for log_i in losses])\n", + " ax2.plot(np.arange(num_epochs),irm_pens,label=f'irm_pen',c='r',alpha=0.2)\n", + " plt.legend()\n", + " plt.show()\n", + " # plt.savefig(f\"{exp_name}_s{sampling}.png\")\n", + " plt.close()\n", + "\n", + "# if __name__ == '__main__':\n", + "# print_truth()\n", + "# if algorithm.lower() in ['vrex','irm','pair']:\n", + "# train_VREx()\n", + "# else:\n", + "# train_ERM()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NS2kBnBn7zJT" + }, + "source": [ + "# IRM" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 861 + }, + "id": "n87poM15BxWo", + "outputId": "8574a66c-8eac-4b58-8406-efaf089c378d" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[10000|10000] train_loss=4.81506e+00, valid_loss=1.01370e+00: 100%|██████████| 10000/10000 [16:49<00:00, 9.90it/s]\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# IRM\n", + "algorithm = \"irm\"\n", + "penalty_weight = 1e-2\n", + "model = MLP().to(device)\n", + "train_OOD()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "U5Sya9VA71ml" + }, + "source": [ + "# VREX" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 861 + }, + "id": "jltOpblmB1CZ", + "outputId": "7a61b33e-e969-442b-9c91-3347a01fec60" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[10000|10000] train_loss=2.12006e-01, valid_loss=6.44612e-01: 100%|██████████| 10000/10000 [15:56<00:00, 10.46it/s]\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# vrex\n", + "algorithm = \"vrex\"\n", + "penalty_weight = 1e-1\n", + "model = MLP().to(device)\n", + "train_OOD()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "J9-5hNDn7367" + }, + "source": [ + "# PAIR" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "3uED5QFc_wxx", + "outputId": "05b5a0a9-1aa2-465e-fbf4-3721b41e051a" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[150|10000] train_loss=3.15613e+00, valid_loss=9.29974e-01: 1%|▏ | 149/10000 [00:14<17:05, 9.61it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Restricted license - for non-production use only - expires 2023-10-25\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[1204|10000] train_loss=2.06298e+00, valid_loss=7.60535e-01: 12%|█▏ | 1204/10000 [05:15<41:19, 3.55it/s]/home/yqchen/miniconda3/envs/gnn/lib/python3.8/site-packages/cvxpy/problems/problem.py:1339: UserWarning: \n", + " The problem is either infeasible or unbounded, but the solver\n", + " cannot tell which. Disable any solver-specific presolve methods\n", + " and re-solve to determine the precise problem status.\n", + "\n", + " For GUROBI and CPLEX you can automatically perform this re-solve\n", + " with the keyword argument prob.solve(reoptimize=True, ...).\n", + " \n", + " warnings.warn(INF_OR_UNB_MESSAGE)\n", + "[10000|10000] train_loss=9.97147e-02, valid_loss=1.59599e+00: 100%|██████████| 10000/10000 [46:17<00:00, 3.60it/s]\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# PAIR\n", + "# define the preferences for the epo solver\n", + "algorithm = \"PAIR\"\n", + "r2, r = 1e4, 1e-8\n", + "preference = np.array([r]*1+[(1-r)/2,(1-r)/2])\n", + "model = MLP().to(device)\n", + "model.update_preference(preference)\n", + "train_OOD()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "id": "rQmF7csfJP9p", + "outputId": "591578a5-2696-4eef-d9c3-643749f75415" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[10000|10000] train_loss=2.41655e-01, valid_loss=6.84301e-01: 100%|██████████| 10000/10000 [41:39<00:00, 4.00it/s]\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAUQAAAEICAYAAAAncI3RAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAAsTAAALEwEAmpwYAAB1s0lEQVR4nO2dd7gkRbn/P3Wm54TN7Fl22QS7sktYySDJhCyKIooBFRNmMOdwkZ/hmq6i1+s1XBUVkWtArgnBCChJXQSWIDkum1n2LBtP7Dn1+6O6Zqqrq8PM9JyZc858n6ef6TTd1dVV335TvSWklLTRRhtttAEdzS5AG2200UaroE2IbbTRRhsB2oTYRhtttBGgTYhttNFGGwHahNhGG220EaBNiG200UYbAdqEmBOEEN8RQnyi2eUwIYRYI4Q4pdnlaKON8YJUQgw61YAQYrcQ4nEhxMVCiGnG8TcKIaQQ4lXW/04SQqw3tq8VQgwG19kqhPiVEGJ+vo/TPEgp3y6l/Gyj7yOEmBbU4R8afa+8IRS+JIToC5YvCSFEzLnzhRC/FUJsDNrXkpRrzxZC/FoIsUcI8ZgQ4jUp5x8lhLjeaNfvM44dIYS4QQixQwixPu5DJ4T4ZFC2U4x9Fwgh1gkhdgbl+Lj1nyOEELcKIfqD3yOMY88RQvw1uO8ax/0+K4T4lxDCF0J82jomhBDnCyHWBve+VAgxwzh+d/CsevGFEFcYx2VQd/r4941jHxFC3CWE2CWEeFQI8RHHM8XWlxDilUKIe4P/3yOEeIlx7CwhxP3Bf7cIIX5klftgIcRfguMPCSFeahzrFEL8IuAoKYQ4yfWeqoKUMnEB1gCnBOsLgbuALxrH/wr0Ab+z/ncSsN7YvhZ4a7A+C/gz8JO0+7eXyPt4Q1DfPrBP1nfXCgtwLnA/sChoS/cAb485dx7wTuAEQAJLUq79M+DnwDTgGcAO4Kkx584BtgCvBbqA6cDBxvF7gM8DBWB/YBPwYusa+wP/AjaadQwcCEwN1hcCdwMvC7Y7gceADwT3fW+w3RkcPxZ4PXAOsCbm3b8AuBz4tOPYfcDioA4uB34U8/wCeBQ429gngWUx538UOArwgud7DDgrS30FdTAclFsALwT6gbnB8cXAnGB9GvAT4OvBtgc8AHwwuPbJwB7gAKM+3x+8703ASXW30QyNONSpgC8DVwbr+wGjwMuxOigJhBhsvxO4O8P9jwVuAXYCjwNfNY6dHbycPuAThMn7WOAfwPagsr5pNLwlQQPwXOUDlgHXoTrVVuDnRkP6L1Rn2onqEIcExy4GPhes7wVcCTwBPBmsL7Lu9Vngb8Au1MdhTkZS+UvQ+FYDH7aOvd6oj/Oz1ofRId4JPBiU6bOoxv334FkvM8+PKdtJwHrg40G9rQFeaxz/O3COsf0WYFXKNT1SCBGYiup0Bxj7/hfjw22d/wXgfxOu1w+sMLb/DzjPOuePwGkkfHRQZPAv4KPB9vOADYAwzlkLPN/63yk4CNE4/mOihPgL4CPG9onAIDDF8f9nB+94qvX+nYTo+P/XgW9kqS/gOGCL9f8ngBMc150GXAL8Ptg+BNht1defgc86/rueHAixKhuiEGJx0AhuC3adDdwipfwlcC/qi5vlOr3Ay4CHMpz+38B/SylnoDroZcE1VgD/E9xzPjAT1QA1Sqgv8RyUlLES1eGz4LOoit8LJc18I9j/POBZwAHB/V6JIh8bHcAPUR+MfYEBFAGZeA3wJmAu6kv34bRCCSH2Q5HOT4LlbOPYCuDbKFJcAPQGZdfIUh+nAkcDx6OkgguB16G+4ocAr04rI7BPcI+FKKnlQiHEgcGxpwJ3GOfeEeyrFwcAvpTygYzXPh7YJoT4e6CmXSGE2Nc4/jXgbCFEMSj7CcDV+qAQ4hXAkJTy966LCyH+TQixG9VJpwI/DQ49FbhTBj04wJ0J5awWwlrvApY7znsD8Esp5R5r//VCiM1CmbOWOG+gTBzPREm+Gl8jvr5uAe4VQrxYCFEI1OUh1HPraz5DCLEDRdIvD66X9IyHJByvC1kJ8TdCiO3AjSjJ6QvB/rOpvOyfYnTQGHw9ePCtqE7zngz3HgGWCSHmSCl3SylXBfvPBK6QUt4opRwGPon6ygEgpbxVSrlKSulLKdcA30V9GbNgBEVmC6SUg1LKG43904GDUF+te6WUm+w/Syn7pJS/lFL2Syl3oSQ6+94/lFI+IKUcQJH8ERnK9XpUh7oHuBR4qhDiyODYmSjJ/Xop5RBKYh41ypSlPi6QUu6UUt6NMo38WUr5iJRyB/AH4Eiy4RNSyiEp5XXA71AfDlASwA7jvB3AtKCT1YNpKCnWxA7Uu3JhEYoU3of6YD2KUrk1rkTV5wBKDf2BlPJmACHEdFT7fx8xkFJ+Mbj3UShJVT+z/fxp5awGfwTeKoRYIoSYCXws2D/FPEkIMQX1bBdb/382SnM6CGUGuFII4Tnu82kqH3yN2PqSUpZQUt9PUUT4U+Bck4yDPjwT9V6+jJK6QZlXtgAfCcj2eUE5Q8+UJ7IS4kuklLOklPtJKd8ppRwQQjwdWIrqmKAe9FDTSOzAe4MHP4yK9JWGt6AkgPuEEDcLIU4P9i8A1umTpJT9GNKaEOIAIcSVwRdvJ6oRz8n0tEo6EsA/A2P0m4N7/AUl6X0L2CKEuNA0ABv3niKE+G5gVN8JXA/MEkIUjNM2G+v9qM6ShrNRkiFSyg2oj9MbgmN2feyh+vp43FgfcGxnKeOTluTxWFA2UOqPWV8zgN2WxFQL7Ovqa++KOX8A+LWU8mYp5SDw78CJQoiZQojZKHL5DNCNko5PFUJoafrTKHV7TVKBpMJtwb3+vcZyVoOLUKR+LUp6+2uwf7113suAbai2Y5b3einlsJRyO4rslwIHm+cIId6NaoMvDD66pNWXUA6nC1CaTSeK0L7v4omgTf+RgFOklCPAS1B2x83Ah1DCg/1MuaGesJs3oEjjdiHEZuAmY38ipJT/Aj4HfCtNOpBSPiilfDVKtfwS8AshxFSUHaxMqEKIHpSaqPFt1NdqeaBuf5yKSqE7rPml2ce452Yp5duklAtQjoD/EUIsC459XUp5NLACRdQhj1uAD6GMz8cF936WLmbSsyZBCHEiSv05LyC1zSj7zGuCL/kmVGPU508he33kib2C96OxL0riANVRDzeOHU5Y9aoVDwCeEMJUD5OufSeGNmGtPwUoSSkvCaTp9agOelpwfCXwXuMdLAYuE0J8DDc8lKmHoDyHWW3+sIRyZoaUclRK+Skp5RIp5aLgmhuCxcQbgEsyfIQkRvsIhIJ/A1YGdaKRVl9HANdLKW8JyngziiviwsHM+kJKeaeU8tlSyl4p5anB/f6ZUvaaURMhCiG6UWrQOagH1st7qHTQNPwI5Ul8ccq9XieE2FtKOYpyCIBSBX8BvEgIcaIQohP15TYb2nSUGrVbCHEQ8A59QEr5BKqhvC6wa7wZ4yUIIV4hhNBk+ySqcYwKIZ4mhDhOCFFEkeoghlpq3XsA2B58QT+VWhvpeANwFYqIjwiWQ4AelAfvF8DpgT2mE/XFNt9vbH00AP8ehEQ8EzgdZWQHpTp9UAixUAixAPXhuDjuIkE76wo2u4LtCAKJ9FfAZ4QQUwPt5QyUuurCD4GXChUuUkSZF24MTAMPqFuL1wghOoQQ+wCvomLzWomq9yOCZSPqo/mt4PxzhRB7CYVjgXcB1wT/vRZly32vEKIrkLhAOcoI/t8NFIMydAfvUtdHMTjegfoAdGutQ6iwo/2D+64Avgp8Jug3+v+LgOeg+p5Zz08N6qIgVEjdf6L6x73B8deiNIrnSikfseoyrb5uBp6pJUKhTDzP1MeFEK8Vgf1WKBv55436QghxWPCcU4QQH0b5Cy42jpvtojM4t/YPfZrXBYcXDTgLJZEUrf09KDXtdFK8zMG+j6GcMkn3/zHKjrAb9dV7iXHsjSgvnfYybwCeGRx7Fkoi2g3cgCKIG43/vgBlO9qOagDXUfEyXxBcazfwMIFnFNUZ7gz2b0Wpr9OCYxdT8TIvCJ53N6rBnIvh1bbrIniOGxPqoBtFzC9yHPsf4BfB+huM+rC9zGn1EfIyouzFbzS2Pwd8P+VdnYRSZ84P6mct8HrjuAjqdluwXEDYg7hbvz+jTKEl4d6zgd+gPlRrgdcYx56JUs3N898RvOMngSuAxcaxk1EdeQdKVfseDm+t3T9QRPXH4Nn0u/+49YxHAreiPpirgSOt+rOf+Vrj+MWO428Mjh2Asrn1o8wUH3SU9TzgBsf+k4P/7kH1td+gNAl9/FGU/Xy3sXwna30B70Y5UHcBjwAfMo59Pmgze4LfC4Fe4/iXg3e0G2XHXuaof7tOlqTxWtwigouOewRftu2oF/lok4szKSFUYOyPpVLZ2mhj3GFcD90TQrwoEKWnAl9BxXytaW6p2mijjfGKliBEIcQfRHhYkV7OF0LcJoS4MuavZ6BsOBtRDoez5DgWeQN7iqse8nA85AIhxMdjyjjuhhK20YaNllaZhRAfBI4BZkgpT087v4022mijHrSEhOhC4BF7IfD9tHPbaKONNvJAlvCYZuFrqADp2Ch+IcQ5qNAfinB01qjrVkYHKnq1GPx2FkCUN4KlC+V37oQhz2OYTkYoBr+d4e2RThgUKkBoCDXidwQVLDSKCgLRv2VI4wTTeadhjxDzVMk7gtVCsBSDbbP8XRKvc9guJcXyorYLw1KVcwQ1Sl4vZnntZ7CLKoKlQKVsnrWuyxuUb8QrhEpTKVURX2/7ReRQQdXlcFCvI1S2fYJC6R1mYXX9SmPdrnsXuoyKLKpC6/J3GrvNU4qjeEUfD7UU8CkG60VGyvvL66USwjfqXBfdeAe37mKrlHLvmEKOe7QkIQo1GmWLlPJWkZDSR0p5IcpNzwIh5DljU7yGQfPHQlTE+X7AomlQnI8Kb94XNX5gKbAc5FNg7ey9WMNSNjKfdSxmEwtYx2I2Br/rNyyGh7pV0MN9qMCGzSh/vA6gGAx+fVAd0kdFhQygojgG9EHCTaaIirSaDUxRYe5zULmMZgXr+wQPswhYAh3L9rDPvI0sZh2LWccCNjKfjSxgEwtQ+xcMbWTqWqksw1uCpQ8VzNKHCtCwl4DwpQ++Qe7FLtSo86lBMXuDbb2ufxer+t08dxobWcAW5rKRBeVFbz/OPNb1LWZkzQzlvlsT1Kn+1fXLzmDjsWB9Z1CHI8YvRr1qjODGElTL0L9TKvVr1vGiynbHPnuYM+9x5tBHL33MYSu9wfo8HmcujzOPLczjcbV/xw6Km1Djk7YYy+NB3W8BcR2PxRRwQqAlCRF4OvBiIcRpKFlohhDix1LK1zW5XA1HkUoXCXUVu98YKFgHC2FxLx2Ra48QFs9cJ3vBcc992AUPCl6JAuHFo0QBP9j2KfijSjIpoaSvQevXXAYJkeHAEPg+eEGxvAJK6tHSpb1u/eoymNDbmerVs1fiulgRN/nF7dfHYu7pWduourZRMOo6chl7lx+zPoHRkjZEKeV5UspFUsolqCDwv0wGMjQR1yXKcPRNz9oZafR2xzHhmys+0d5gkiSO4ykI7l3wVGfsZCiWFLtMovNxE6BBhJTCZDjiq19fS4tD1mPZS8wj6LLp9fJ+zzh5zEQKk2CLYZXfPMXiYa9cx1ES9KwPExD+SMCkIUKNVpUQ28gJHV4pOrZQv3Wb94AwFdsk6DrHgNmJTEHSWDyvZEhcfqhDdjGMR6ki0Wky9HFLh8EiBytkODBkPaoPRVsSNNfNcpdUmTyTIKh8aELSYxoZZSYS18lxUmJMd40pS4i4A1SeL0yQBXxV7y7Y9TSB0fKEKKW8FjXUbcLD9dEHwtJgLY0y7S2Xr6kN+rY3w75xjc0mUJm9QCIETYphYiyTlakm26RoSJAmGWoaGSkF5gdfSY/Clnwizx6FSYBVmyGAsIprGkPqQWBpTpMQA+hnsEm+ctwPS4cu88IkQssT4mTHiG90K0OSSUNqB45t6K5eYDoBauzYRksz1eTKPkWMnQyFO6OpMuttgxhHLDK05SovUJuLSeqycT+vVKJQcJNH+Zq2sS2xF8XY/SIXSKrTlGs4iVFGy0lFTY4jSCcmETG2pA1xMiJLt7FhqjimWpcZEXOh2eqT1OUEC6dLkDR+tVNFl9Wzl1IpLKVosjLV5OC49GFgUH00dKkGKoeVjFtSx51OFLu8FmzJSm+HYJNhebuYcFIWuFqE3ieqkhBNW6ENrT57pRgpcZKhLSG2EHQYmRMxjTPSQeOQ+U271GXT2+wZ+2IkmwRSLBTCxG17nAt+KWo7NJ0rhnQ4MKTIbqCkiNDlEx9BSZCxviL9q73MvltCTHVQVdWTXPWW1fDoaCFVe5ljpENXU6rXXDPO0CbEFkaqpzlAZlJ0IUQSdoxc5ASixKj/h5sIzXVP2Shtu1yYEEfDUqGWEi2P88iQkg5tMtR0btJGj7YjmlJhBvODLVmVpXAH0YSfkwaQh0WEqV5mP5DGTftsuFChYzp40zZXwKSSFtuE2CJIiohxdq4UZ0DsTWKhA7I1bJXZJEltR0zp9QkSlMuZUu6wLluf4UyRg4oM/VJYnnVJiKDO8wMnS6hz252/BAV/lEKXX10sYmy91tu9XN5mxzWrkFbjQnAK/uikkADT0CbEFkM9L8Sl5hW8EqNJsYeRTmCPlbOpRgdk26pbKON8BZbUEi2zHyZHl5fTVJ9LSlUeGKqMoRkwHsclVZf32bbDFAKIUy8LXikhJECjljdZhWiZKiEq1d+MQ7Q1CdPBAkRtt3qfxiSQEtuE2AJItB3aaFijNBVO23HiUplBDdtzXMYFp2ATjvVTThXc4R9BGI6WDk0yNInQvr2WsUb8wNMMUWI07lnwCZOEgUSHVdrAlEiJ7PUsSJAOLWLsiFHr45wshSQtxBWuNEHRJsQWQpEoOfo1NsRMarPTfuhSmyGqMuPYb90nQYrShONZHVTYJGgJqwNDFbuhSYYu6dCkG98kQ3M9BqYab0pX5Xr1HBKxszdV28VsKVGPcHec5vDim/tNk0TYW26HO6XYDycR2oTYAkjUvEzYjdRorK7GXjtcKrOpJtsk6Yd+nDA6qQnbyxyRDC21eWAwngxtd09EanQNSbPuo7TheOkKKp7y8s0yvby8uprDueL4tR0/tp3WtiOGTo9zOE0CcmwTYgvB7FsRFTqmMWYmwCQ7YnklTmW2CbCWqEmzKAkhLQ6p0JQY/VIyGbqkRAhiEe2OntE+ZsciGgdyQFa12YtuxpCh/vWI8y6HTRWRccx63cQkIENoB2Y3HS5qqbaf1SwVlhu5OWTPhJ3MIY52QheLV5cTs68Enl3bC2xJdXYeHhcZ2vt0gHaomDEqtPDD0lRsSJPtIMrk5c36ZqtoAbESotuBZZoqzHbjHMdsq8+TAG1CbAEUcdsPE5Fi/6r+/zbxmepynFMlY7ky9O/IKBV7GVSxhGYp7djDOLlWF0m61OWMsNXN6lCfRB2LFAkxbhyzWX5nuNMk8CbHoa0ytyCyqss2omE3WTuufV5i8ErCObWhrNr5JffQukBS9EtRmna5fEbK13XItHbYjUM1t4nElfmmfIPMNsQkZFWbi+H7Ev+bNI4ZHHkfs6jIk0BSbEuITUaKM7ZuZM/dZ1INRBXTpP9YSHiYcEKHhOBgh3NlYDCZqu3S2GTo2yRbD+JGq+SGTGJ1zK8eEeQOsbHfgXMcs962P1ATHG0JscWQ+kIa+pW2qSUuFhHCMYjGeXEPELPfKcU6yBA/cIwQpWyzdKnBQTHSp0kEtnRoS1N1DZWsCQmJIjJ6mSGa8Db0HPbpk4QAbbQlxCYizmaYmuAhwQbm9IbGoXxKEvHZlro4pKt8Olg4EhRcVplxEqGtMsc9RpxLqHzcln5cyOhtzh9x7rWET2SshFixdboIPRKH6BrH7EJbZW5jLFCTU8VC/R3VlrvMQO2kEJza4SxzHCn67jtG1GJHycpP4nKq1OBgyQfVKmfZ4g/jJEQ7BCcyntlFdq5A7QmOtsrcRMR9/6t5KVUTobNhp4lN5rGU8bZ24VOFnARSNIhRJtzSldRBb9uh5BEp25YafUJJYl3hNwVKamqGVuk9EVIMh9YkxSGWMwxB1JwwCdGyEqIQolsI8U8hxB1CiLuFEP/e7DI1CqZTpWjsS4TBI/mQost+6HKspPSUOG41QkE0bI9nxOxlSCjlbDUJsFVku0gjZtnsUSsZCCA2j2BuSHtCL/QTWreH7RmjaUx7qB2HGErsoBH3iJPArtiyhIgarHWylPJw4Ajg+UKI45tbpPyQpB7XrjZX8VkPnRqnBtsKaT6hN7ZxPwRXWExCR4zzg8fSuEMiDN3bD5LEWtJhY4mwRsQklIiz1WpEvfvBSlLdTBK0itAfgZRSoqZPhwp/yPh/jE/kEYidD0zyM+Usl8hne5izEWN0fG3MQyWEx5gNNo6+484zJ7B3EeHYI8X8EDpPI2VuZseY8XCiirBUHkrsoOFb602rn7FHK0uICCEKQojbgS3AVVLKm5pcpNyRWcbSfaKhDdO8uCuoxTyWoSAxEowJM7RF2CSVEUlqsnkOBLbIOMINtgv+aHkoYX4hNlk/fTXoDZadtuCFSc+Ea1/5OzWJiC8OLSshAkgpS8ARQohZwK+FEIdIKe/Sx4UQ5wDnAMxsThEbD4kyHmwGdgD3AncA+wLLofMAn6m9/Uybs4fiwhGYXuuN4vy0trqcmiUifJpjOJn+jXWo2NuOThpnqrQzDXpU5Fk9prno+mMCxo26bBCj7QRyOYY880OURSqcBGTZ0oSoIaXcLoT4K/B84C5j/4XAhQALhJgw6rTuyEXUfCA8GRzoBPYBpqDI8Z/QfcMIC/u2sPCJJ3jWhpvo36uHdQcv4sGD9ueOgw9l1aJjuaNwHJvlPjgzWgPuSD4/Zn/9CId/pCVRCG/a/Vb/umyIplAd6st22I1FAM5kqXWhSCWvt952DTA0j1eBiIRYCa2JZhZKqOuk0JsW/CY0Ai1LiEKIvYGRgAx7gOcCX2pyscYc3iiwANgv+F0KPAVYrtZ3Lu1hHYvVMrqI/rVTEffC1Pv6Ofj2+znlf6/lwPsepDg8wn17HcTd3U9ldcdR3Dp4NHeMHM5gWX4yyc9FO3HbdoHj99kZWGqVvFwEGHeeidB/4m6tSXHMVeUqrhVnigjZEN3SoLkvktgBJg3xxaFlCRGYD/xICFFA2Tovk1Je2eQyjTlGitC5BSUZpr2tDsH2JbNYt2QxG18wn3UsZk3fUkYemkHvbVs5+B/3csh9d3HUutW8ZdsPOGjoPh5iGbeygpvZn1Xsx7+YE/QN21niZyhAAiIjziojaiKhHzEhO0nJyey/2SbXsuLvW1MJmEvktq3CDmbWbEfojbXtefGT0Ts95pNAFc6KliVEKeWdwJHNLkez4RehsxtlN5wFLM72v5L1avt65nDjvs/kxtFnKtV7KnSuG+LQoTs5mms5lr/zLn7CfjzObezLKpZwEwtZxT5sYKouDW6JJ0ZmcwRll/MeUpsU5vJpxzlV7KBsp9rs2Fd13gaXDS5XaAOKR3mSer3bXLqB7hHn9KN2ZnK9zyuV3El545YJjpYlxDYCCJTHaB5wI8q58rocruvDsOjiVo7hVvblQk4FtjGDtTyNeziOe3gDN/FtHmOYDlaxmFUs5ib251YOpt+8kAuO4GEzJVU0VVkCCxWiu7KEiY8QnQbLOUdNXMhJFowJSWgytNJ/ORc106JHiU6G6WKILobL65oM1b4hNY7ZtKGa667fCY42ITYRpuE/FbOBFcB64LPAmcBHkv9iS4lZSrSTqVzDQVzDfihHwDBLeYLjWcfxrONMruRQvse9LOIvHM01PJ0beA79zK5cJuW2TnU5DgYZmqOrcfy6Mt1oUgzJsK6wm1qIrWYyrNZJpckwnRA7vBKdhSGHZFhZ72K4rFJ3Do6W57tmiMQJvtoSYhtjjkSS7ASeDbwIuBo4Bbq+OAKvl9kjSmMbdVwirRKPMptHmc3POByYThcex/AEJ/MQ53EJv+B8VnME1/A8rhleyU2jx+FTDKvMjnAQjYh06KGI0LSLFazZCK1SJnmZQ0FEcXZK9aj1IVUnrxVaQjTsh91ECbEbOruHAulwqGwvrEiJQ2UpUf96NgFqYnRIi0njyScK2oQ4HmDb4uYAnwB2Qs8FI6z44iOID3ew4XULoKueG8XRjBndB0MU+RsH8TeO5bNMZwrdPIO1rGQV/73zfSy//UFu3PAMrulbyTXeSu58ymFIOqyA4Yzs47BDxpXO9TRO37kr7Kaezu4SW6v6cxoMCTFOOgwI0vOUqqxV5s6ymhxOFlsISFMMUiFCV9o149dvq8xtNApZ1GUPKCaddDRs/1sPT17by4ILHucNn7iM6997Ar95+4uUA6bmkmU9T53bzxT+zCn8mZfBHJi9Tx8ndV/Lyh3XcM6PLmT2hdv46wnP4e+nH8N9zz8Q9pfxIZFVljKJh5zunxpU5JKht+v1Ud9h2GwYDAkxhgjV+gid3cMU8OlkODQOW6vMWkrsZKjiUNESoUmKppQYLCNtCbGNsYTpGc0cvSYEu58zlXufcxD9d07lgK88zFf2P5/fv+lUvv7+d7KuJ6Nb2lkaSB4x7MY2r5dfLXo5v1ryclgCi6au4+TNf+EF/7iS8/7jKwhP8tDKpWxbOQt5slQBVnWUMA6m/VBvg1L9RIYL+C5vDgZBZpYs6wlsN+2HMRJimRRV2jJtI9TOlE7DsWJKiZ2DI2Fp0GU/NPY5HVITDG1CHE+IEUp0B+07bDaXXXI4g+u6OfFr/+R3h72MK573Qi540XncxaEZb5JF/0vpGVarWr/3Yi454Q1c/oFTWdy5lhPuX8XJ11zL0b+8nf3es57S/A5GV0oVZLXAuo6lMrsabFanSuicKtW/UlzlmwWI2878RxdM+6FDQrQcKhXnSeXXJSV6BBnKh6g4VWwHiiUlDgxlfa7xi5ZO7jBZkdhNYj5hZofdtng23/7Pt3HSI3/kvgMO4E/vP5W//sdJvPLun1MsDeda1qzlK0MI1h+0iOvf9Qyu+NXzufKJlTzyo0WMLuyAi4GXAp9COY02UCYu4UWlZjMmsZrEZLXawhJJ0X2n2m4UgikhikRCLHhaOvRDnmSTGE37ohcjCbqkRHMK2ImMNiGOB9j9MGO/3DVrBl9+3wfZ7zeP8c1T3s25q7/LY1fsx2ce+wSL/HUp/3Y1/+ypvpIQGjJWEPQf08Oej3XBz4DLgbNQOu2tKGJ8CNhT4VoXMbpgS4W5any2DbFhYSkZYhCD/QXPD4XVKFthWGU2JUfhkgrtfYZDpU2IbYwZ4pwCnu53ddjwfa/IL592Jitf/xdWPucaZvo7uGPj4fx2+EW8mMsdeQlrGSlsoRZjTAkVWnQwsBIlLZ4M7A2Mwgzg8GBZQrxDPY7K447ZyOwvcZFgeTtOZq2FVoyhezFkqD3MUZU5KiWGRqjYITbglBJH/EkRhtgmxImIWNXOh3tnruB9+3+dxYvW8auOl/FRvsxajubzfIunsMn6Q1znjRtBnL47M3Rn70aN1JkO24B7gD5Uros3Aa8Bno7iTNft6y2G7VjRVOO8mRNp8msaNPOlB2XrESqmlGgTY6c9QkXbCku4Q28Mh8pkkBDbTpXxhFrflqPv9XdM5WLvTVzsv5EVXM9buJBVfIA7Wcj3OJbfsIzsNnQz+UBtSIxLNC49gsoWfAfQD+yFIsfXolJHPhAsG+oqTStB2w+9WMnQrB+XQ6USalMJxSmPUDFVZZdNcZJJiG1CbAGY4TYjhK1Gwg5Mtt5YJkN/ylu+h4P4EB/kPF7FS/gDb+UavsH/8WMO5fscxT3MtUqrUZ3MUPDqn5vEtB+WgEeAu4HfA3OBA1CDeeYCa4GNKImyWpj1Ghd+E4tcmcOQEBOkQzxZrl877MaeZCo0QsUcrmcTYxCKIyeRDbFNiOMF9bypjGEhwxS5jOO4jOUsZQNv5mb+zI95jJl8n6P4JQezM9f8fimwPgRegcRwmS3BciPK3rgcOAg4ATUqezdB/KGkap3at1Vl36vq/zWcHCBGQowsfjB1qnasuMcza5VZONRil2TIoAq3yThpxLhH24bYZFQlb5kE4RBaqg8LicejTOcTnMx+vJ//4Bm8mPtZy9f4JT/lFdxCT1mhtpi6Bm+rU2pM+QCkBbDvRNkbrwYuRUmRHShbo7cL2IrKRF6PwOpb67kzhtYTPNJCbvAUCXoWCZqZbnRIjlcqVeIPzXjDhIDsAdoSYhtNhqcTHIzJW3JTS4kOruRAruRAZjHAS7iPt3ATF/ILfsfRXMrp/InT3Z0lgSASp/ZM4XXP+jUT8rsgUWqznp5m0dRgSORa4EGUy/ppqBRrKQiyCNo73esJV8mGhJAbK7lDsXs4oha7pURrhIpjiJ69b8RvS4htNAGGchSlJ90HrSwwmVA1obrpZTs9XMyRPJ9zOYDP8jcO4iP8jE08g+/xdlZyNQU7JYqjFyXOH+0qa06Cb7lOC6ix3k8FTkFN2HUbarqyt0PHhVBYW4oS35jDQ42zyeJhDpOeSYZ2bsTQCJUkldmwH/q0JcQ2GgzTmdLwG8WitiwLTzCDb/N8vs1rWUQHr+QG/oPz2PeJtfzf6Cv42bRX84+FJyC9bN/cWJLMoYXalxjxjXrvRnliXoAaU70JxC2w6Etbmb3vHmadsYv+l0xj3WGL3FXVULHJ/ETiJkMjy01EEnQGZJfcI1RibIramdKWENtoOooeYYmwxhEr2ZGVfcJjP9Yzn6/yHo7lZp4++29sLu7Dhbeew5qLlnDBrz/CUY/eClIGRQ53q8xqcyFcwiwlrboD9wArofQdeGzzXB7+r33p3DHCy156BZ9e+iXe8r4fcfS1t9FRKoGf9iHJgz6yepjNGMTkgOyu0lB4hIrtVda/wTmmujwZJMSWJEQhxGIhxF+FEPcIIe4WQryv2WUaa0Q6fL3kl9g/PeOkrM3e7Q562FvG5xf8Pw459W5OO+P3DHldXPaFV3L/Cw7k4/9+AUvveTT90jGsZ6ZCa7i04gl2PHsGt371cL7z8Jv49hVvYfves/jgB77B7YtP4L++8H6Ou3tVmeiTUSuVZPUwV0KabFI0pcRyQLYeoZKWITtwqOhWMRkIsVVVZh/4kJRytRBiOnCrEOIqKeU9zS5Yo2EkincjjzeWeo186ObuOYfwieM/xyfe/1mOGbiZN9/4Pb71vA/R39vD3WcdxBOvmg1PUVJj2V6XULZa8vGFU9vWCCHYeOh8bjr0WL72/96Nd88op37nWn74jTfRvWeQny99FT+b9mruHDmMdBNENQ9huI3iyBBUDGLBjjX0Q1mzy1KiP+pWkWNsiaaEOBlU5pYkRCnlJlDjyKSUu4QQ9wILUZEUExb2y/B0o7elw6a/Nd01YmQGu3xCcMthT+Oxl+7DD778el5w45859dKrOfn46xla2kn/WZ10vHK0ASaAcGnzQAmPNQcu5p/vehafPfkTHHbDnZx17aX85uaXMDjazaXFs7iUs3ig1iSPISQkhsXcVk9oq8uugOwu25nicqgEv7ZDpS0htgCEEEtQmfJuchw7B+UbZObYFit32C8i1suc543qumaGhFuhjqs6rezo4N5nHcTuZ01l9dcP5Zi/rOagSx9i1mf7VRT1M1FODhvW1M1Nhw8IwZ1zD+fOEw/n4/O/wLEP/JNXr/sZf+U5PM4cfsbz+TnHsrYmx5WONUhP+4VXyR4UN+VoJCDbHqHiWGyHSsvUfQPRkjZEDSHENOCXwPullDvt41LKC6WUx0gpj5ky9sXLDZl5qV5SjP2/KQNU2+xT5AbjnvYse9Lr4InnzWHdRfPo2zQV3oYKf3kv8D2UPpBT+sZqpRtTvsoEIfjn1OP4wLSvsZh1fID/4Cms4xbeyt/4OO/hD8xjRxUlcEwsFbN0eBWJMBqYbQVkW6NQ4qRDSlGHSltCbCKEEEUUGf5ESvmrZpenEYiLuS5bjmwVyTxYbyxi4v/Tmn6GrhH3cAFM73IJD7oEnAocigqW/h2wCngMCL52o02a5MgmxZLOD6a/HdY3ZJQC1/FMruMA3sO5nMJVnMW1/Du/ZjULuZQj+RWHso2pCXc1JUQyOVRsG2JEUjQdKnGxh6aE6Nf3qRyPaElCFEII4AfAvVLKrza7PGOJTHw3lvMbxSJj3GACMZqJYksUKud1o0aOzEFlb7gH2Ai9wDRgKmrUyU7j8i6KboXG7ePxR47ijyyni52cxmrO4na+whXcwFIu5Ugu56nsptv6Z/bJ6e2g7CgpGhludMiNa4SKNeWo7VAZaGhNtQZaoc248HTg9cC/hBC3B/s+LqX8ffOKVD/MjDZJx0No5PC93D751oVcDoBa4aFyfO2CJwZgV7DrBOBE4GHgXlTKr9E6b6VRcpTZpJtYpNTnEEV+zWH8msOYxiAv4h7O4ja+xa+4igO4lCP4HSsYDJFhesiNKyg7PIRvuDJlgGuqUT0Xs6FC+yUYsBwqbZW5SZBS3kjdE1W2BkySS6rsomPda5R32cPovJJcLUVxNgDPre+mDo+zSKYfletwJ8oAvhB4FnAGStO+HzXjgJ3LMXe1r6YLVep2N938jKP4GUexF/28lH9xLv/ge/wfV7KCS1nJVSxjpOqgbJsYjSkDtN3QdqgkBGSbEmJbZW6jZpRJzdrW664Z4lz/L0NfqGBt14KGsIJR4iolRC11Sc/4CmYo43ZUbNYNwW0OAI4AXgysQ0mPDU0U6zvWa6jbJ5nCRRzHRRzHXHbxCu7gPH7Lj7iQX/MSfsrZXFd4thoGqeu0nNxB5UFUY5WHI8Sot7uGhpMTwsYEZA+gPkLtbDdt1AWbCOMqOilguJwctl5JsVZHTCJiwmxi7qE9oXFwqqIZzZSgVOlbg6UTWIbK3fAsVIfeQsVhXe/8wqUGTlK/hel8i2fwLd7IYqbwSm7ja8PvZ9qDu/mB/xYu3ueNbPIWGB+ccB7EcAyiMemUDsjWThV7QvqUgOxWI0MhxGLgElSOIglcKKX8b+scAfw3cBqqGbxRSrk66botHXYzXmFMK15ut8WY/a7/Gab0MOohNNd/c5MUraexCTgUelND1uwqyzmM8sNcCXwfFcDqoUhyKdA9gtK5U67rshnmmXMyHqoFrGM//pOPcMRet3PW/pey39Bj3PWzQ/jN/57B6bdfoRxSnjlWOawum4HZ5YBs10x74zMg20eNZlsBHA+8SwixwjrnBag8wctR8crfTrtomxAbgB6iRGhrkfYQvZq4LuVPBXwKXkyv982VHMNvE8jQhVTJsM5QG4mSDu9FhTg+CgxLYE+w40ZgNfBEuLxmuepxpNQG65NZFNwy82m8/Zjvsu+5a7n80DP4+K+/wGMv3Y/PfeWTLH10TUK4jdoWNvklhNvoDNmtHGojpdykpT0p5S7UK15onXYGcIlUWAXMEkIkDiFqE2ID4CJCW0I0STPxQrbanIE56523pGYkSYaO+VQ00YyN1KUwAgx1ALOBo4D9UZmzvw28G/gydFwHjISTNkSkRZf9MHKnWmF9Mo12sGfKNH74jDdz4pf+wanf/hPTB3ZzxbFn8rXnfYSnX/YPuoaGHBlvfLdEGBOQrWfYsy3EYzh5RFVIGM22EGVO1lhPlDRDaNsQGwBzDro0G2LasRBMUkzhEGd+wToM/27YVG8d0r+WyhwHkaNkmBnzUQliFwXbj4L3SVj+2vXMem4/3acOs3PlDNYtWZx+rdzqNVsM4t3LDuFjp3yOb3/trbzq17/gzO/8hiXvXst9Zy9j09vnUFhmzcGsnSlJDhU/OsNenkR4oBByT4bzNqhZHwaNXRdKKS+0z0sbzVYt2oSYM2wbYKw9MAWRkJsYVC1dNVIHShF5NRk2TYJNggCeAjwfhi+AdbsWMPyHHhb+eTNHfvx/6Z82hVtWHsnVK0/m/455GbsjM0EHqLt+TZ3CC++KiUEsdRe49tXP4oFXL+OIh27n5Auv47kn3sDAkV0MvLOTwil+xYFixiI6fu2E53kTxCBwfobz3gmDUspjks7JMJptA2B+yRaREnjQJsScYTXlTDMWm8pVhECrCF+xiSbJqwsEnddUjqoxoScoUU6VOSNT2EVulDad5lDZp8DGN81j3ZsWs04uYvQuj/nXPM7pP/4DnzrnC6yZt5Rrlq/kmt6VXC+exW6m51g447OaGIMoI4lhty+bxaoLnsb6z+zDIf93L4u+tJmud0s4EziJVIeKn9Bk8iCLAmpGxHqRcTTbb4F3CyEuBY4DdgSZtGLRJsScYc4G54pBTELkZaSE20jH28ssfUUIoVbRxhRXrN3m4YTyJZa5kcKk6UNK6glCsOHQBaw69Dgeff8SNvbN59DfPszKP17Dh2/8Cj9f9yrumHY41xRWck1pJas4PgjxqdWOaHxWEwnRdySGVYvohidfP5Pi6wdZcMMTFP8beB1qiM9xKO+BS2W2SuFarwd5ESIxo9lQBhCklN9BTdd9GipWvx94U9pF24SYM8wKTZsqUwdoV6VSa/thomoazioDZHAC5ISEXlRJURVDin5oR+U3o4RcM2Lqw3ak6HmZS57HqhUnsGrKCXz+sP9Hz0P9PP2Ov7HysWv4ytCHOYj7+DtP4xqO5BpWcDvTGa2KHLNKiKVIYlg7BtGjhPdU4NPAy4GfAF8F9gMORrnhtZRIxVTj0nDysCV2QC6ydJbRbFJKCbyrmuu2CbEBGCE0V5qTJNOyCZbT5Reobaa9hsN+qoS2Gdi6TNQUj9hI+JRJuGRNjOWn6O0D3hSu7n0uVw88F4Zh1u4nOYkrWMkf+TG/YC7buJblXMP+XMP+PMDeJPflbFMHdHimVBif3KEccjMTeAlKQvwr8CfUiJd54eIUPaAU/qDn5VjJUUJsCFqum0009FjbWSrci6yko2CoTIm2w0RVudo4RMvi6VKTY6TEynaDjVZZ4Hhk21llkmLJTy7YdvbiN7yQ33AMsIUFPMDJrGIld3MeVwHwF5ZzTbBsDKU3NoO1khPDFjx3DkQ7MDtiMyygQo7mo3y5D6IyY0xHSYxEP+Z5CekF8pEQG4U2ITYQWlKMU6NHgmM+GdVn621lHkHmCrcJkUCDxiN49qab/MqkmMEckDtKxm8GgTUxGUXMt2QjvfyYE/gxRwAjLGcrK3mQF3M3/8XlPMHUMjn+lSPYnjkPYjTe0BWYXX42M9RmEKW/7ouSHNcDG0CUoNNwbkN+0iHBLdsS4iSGJr0s8Yix0ESh1zN+rn0KqdJMPoiREo19BafKnCCNjoeWmfqhcUHwIHvzIHvzHU5EMMoRbGQlD3ION3ExP+cB9uNvPJObeA6rOJ5HC0vBs6TFboK0X+bk9GZyByMG0eVZDhdJBap3AFuhc4dKRTlA/mpzoQNm2GqTC1mCFRuA8dDsJgQs5TL9/BiVM/ketXqYa4E5FidSEPd6AJMIE0mxytLULeOmjm12PEydxZd0cBuLuI1FfIXnUmQGxzHE8WziZfyKC574KJ19w9z0yHGsWn48q4aP5+a9n8YuZgQe5iT7oa+yZNteZNOzbn9sp0BpFPydKixzKrC7vkcMoaMDepIShWu0CXFywOYKU11OPBkSY/JSiSX2cI6qsl1eM9zGCA8Z72hktpsReriRQ7mR5eDNgCWwcNF6juu8ieP7V/Gpy/+do76+mjWLl3D7Mw7hgROWsfH4fSitEI6pSINpR21zgEvDML36AoYKsKUEs4BjUSnW6h4GAkoKnZbhvC153Kx6tAkxZyTRUpqUGGtHNBtwXhPW+/ZOM8lDDggxf3Qyd5fzR3pByrMWQTir4FgVTLeSiod5w7RF/GrJIn616OWwBLx9Rjh0922csvYqnn7j33nbV25n7w19PH743uw4ehr+0QU6jx7CO8in4GqQceYXbTsMjgtUMoyZwHOAP+TxeAVInEqmyWih5jd50OhK1x157OCFV2MeUDtPNBGObRkdqEHdjR0qmbsZIj4G0e8uctuyw9j0wjn8+R0rWcBG9t/+EEfedgcrbr2PxX9cT+/nt9O5cRixDDVgbS6K2aaipLQ4aT74NUNv1qOkw+fk8YgC6MrjQo1ByxKiEOIi4HRgi5TykGaXp1poD7MLVQdiu9YdcBFMyS80IKlDhsLoU4LTCgX3OGYtecUSTRNaaMkb6w+KiewxiKYDZXhWF+ueswieI9nOdOazkX12Ps7cm3bCP4G7gH+hcr/0oWJfZqBmNDRHrQyBEIBUpdBxgx7ZNN1UdNCWEGvExcA3UVlxxw3skcEm+fmEvXZx8DACs82d9i6DW1wqXayal0iM1bgnEp4iROR+oo1zLNN/tT4cMYjmbiMGMS4HokbHjFE4GpXe4BnANhQZbkHNr7AO2Iyai2EAFYozDPgwfVRx5vxg904IIijrRJsQa4OU8vogz9m4hCvc2aYPr4Az9q18Xh7zp6RC2w5zgKucHmXmzsuj3CzE5kPMDdp+WGsMYkUKjzivzHfTBewT3GYesAPFeHuC9T0wsB129Sv+3IXi0m15PGLbhtg4CCHOQaUGD8X6NxOaXnqsdRdCUqDRfsckEWejuMlhpHdh7JwUY4i66tRwpmQgRHvqUVtSBEXgfgGKcePCzVdQCP8WgyLUm8YugraE2DgECSMvBFggRNSV2UTogOwea91GT7fKUFz0VGJOSkpy9KwGWi0ihJOqJutfj3SJsWitJ6vO9gRT5SQJac3P9Kw3Uqt2SOm2Z3ks51KpuHsdC6DTfkH80MeQDdR5Dcc+q81pDSZXksgadtMktKcQyBn2zCQj1jFXYIvnqaXoqUYYsR/mibHSWq3O1nSPchxSHE5a9qpsZ58dsHrEeJi7g8PlfX4kD6KpJptTM5RMsvOsXxOO0JuiV9mdJa9nJmiVOW1JgRDiIiHEFiHEXTHHTxJC7BBC3B4sn8xSvHEtIbYqTJUZElPOARXpUI9O8TxjCtIMMNNU2VLNqMvL7OzAOTQFl/QRKuf4am5xUuGoDszO9eNiqstetA6tbR2EDdHMQZoefQoqc483mqh+u+6l40FN+b/FVOaLSXe63iClPL2ai7ashCiE+BnwD+BAIcR6IcRbml2maqClQJfLohwC7VOeI7ioJcTgNwt32AMmKiEs1RCPfW6dzT7l1vbEUnmoomklrifcPPPse7kgJQYRvR51npiyYqSsLunQlBLNd2aslz/QZGqO2aAJsU4JUUp5PTn5eUy07CdbSvnqZpehVpjqsm1H9Ix9fqkiHWo1WavNWbO+5KOKuqzsNV5Cr8dIiRAlbN2Zy4gLSYzxyjcarvmZy8gtxlNLh+lzqeg8iHHwMVXmQlhCjCNFs70Z7822FteN7BLiHCHELca2c5KpFJwghLgD2Ah8WEp5d9ofWpYQxztsO6LdmPS+kKqsh0zl+kl2oJHXt689viNtkskwdxiVl+T8cMD0NettAL9QQHojKv9r3LVsUtSeZofaXDeyh91sTZtkKgWrgf2klLuFEKcBv0FNWJ+IllWZxzNMFc1OMmIu5XP8wMNswtX4qyCXSCdO/W+1CcrMLuL4j1MyrIFYmvDJ9gtRtXNsSFHXqXATYLCvYHnto2aIiumkLI2b6rEZfhOnHJieZnL8huakMqdBSrlTSrk7WP89UBRCzEn7X1tCbCDMGERbZdZLUedG9QIVuouqyTDOdlSikCGLrEspqma0ihdedZR91C+UO6Y/5iRTP3xH3eYPy3URZ0OMKZtdRrNN+DpywbYZ2nGJ5j6IzK+SC8YoDlEIsQ/wuJRSCiGODe7cl/a/NiE2CLYdUVuIXFKiRyUWUfqOjC8ZJcNMjopGq+P6HgloOSJMmELAJJzGJc0IfSLduwmvuz6CtmNF7yt5hqPOpYpb1y5vU0nyAGNuQ0xE4HQ9CWVrXA98iqCIwYx7ZwLvEEL4qIGJZwWTTiWiTYgNRNYZSrRzJfRHDZcTIWi8Ja8Kcolp8ArVms7HoNlUcYtcJz+IcQLVHjJUjQnCcqi4LmM1qErmQ8/YF8qIqEJvCqNup4p9L6OduEJv6kaBXAKz05yuUspvosJyqkKbEBsEU122pcGI2kzgXAkauwdqpjSsCzQUdq/IQjPZbIcutIyUWIWXOJYY6343Dvttgrpc8sOEVylGhRgjnnvzmiYZFqxj5rkEDr+hBtgQWxRtQmwgXASYpDZDxblSrJEEU9XmxDdeZ3NIUPVLpQKlQiFCKHkRYwOmyIqHGewe2l/PRXW9FONfQ9AmRi27sK0mm0MjfTP0xnamxIXeWJJjrmPr24Q4eWETny0l2mObfaNDFc0Z0lKgG35l20EysfaiON2sDtjqf8aU+35hjBJb1ICy+llKsc/WjIQYRIiqyqUCfsG2F0bDblSCByP0xlaZTXIk+pt36I3sAL9NiJMTSSqyfbxo2ApHfOjpAmE3nJSgZDcRlsBLa8p2Myii7NBjg5Yd5xwgGsJUa7fR+oEN3Soc9kNn3FZBJf4t6PLZ460N+2Gg5pc9zfbisiVapJhnQLzsgKGuLNF+o/ncsEq0CTEHxEXw2clhk8yCIxBqdH7JUJvjEg94HbHGfnt8azrqlAHinELjHHZGb5WBXGSU3mupTyPsxoTjfiYRmili7e2Qpxncc6rYpKi5Oec4xFHRwUDXlAxn7s7hbtWjTYh1oEhF7U0iQ/O4qTbrBBCuzDjT/ZTuFCNUpdrkIoZ6+y45kmJZsvEo+YVywLNGallzEhwboobbZJjb0L1gNS4jiE+5Pktd0fHLfhwpmp7mOOkwhhTN0Jt6MUoH/bQJccKhJ1hcaZFcYzhM0nMFa9vw7Y6QoUG6VM8Or8RojLcyHmknVkExEalmLIfC5YtyuW0VNhPi1GV9TMNzk22CthAfdmPsM8c0Q9iWaL4OiwzzDr1RhJhlpvrmoE2INaAHNfFOD9kkQ5/K2A/XVKNmO/esY1mI0O4QmaEL50zwlGW0SkwHr0daalCLTOzMsY+RQNy5mwX0pzVoLTYBhqREge8XEogwLCX6pmPFdqYk2RK12uwBQ/k85SgdDGSSEJuDNiHWgNm4pUOX8mmSoI04ASPSefPM8BIrKdq9oUZUOapmvCE0SX2ihFhLnhg/9BO5h0GSJcOxY4fdgOEVN+2M5hA+07NskqI9vM+QEHOxIWZWmZuDNiFWiRlUpEOIT3GgSbAshBEmRlNd1qTpGcciyR4gVl3KDC9mPYIclCNbVfYLlLqiN211D3MsbNLKDUFrcNkOI0Vw2w9D6b8M9Tl2CJ8pHULYy6xJNCe0VeYJhNmoScpmE+80sfcl9RU9yDJWlihZJ9eD2Dcdx5KaqquArd6VJRpR3XVyREMdKi4JseYeZeRIyiQhmqNSKr+hbNnWEipjnOpse6L1Xwrh0LBa0VaZJwCKwFwqZDgj2J9EF3Y/saVD139NqTL2QuZczF5G6cp+y6Z0ULYh1poo3k/czIpIh3XdqTTGI1KyItehlYbanBSnFSAuy1HUtmh5miFMjEk2xByF+LbKPM4xg4pkqEnRDKweIRrCbDpR4mCTopMMzQvmhUxvPIdmMYFiEU34mW2I1cCemsy8IU5iHPWDUTOFaLYbc93M1lP2NEMqCeZlUrbRJsQaIYR4PvDfqNf2fSnlF8e6DFoiLJNhAWbPrIQijAwpW9/AoJJe+nGToa0625qlSybzM6gnBb9UXaxeqg3RVt6rHK3i8ohaGK/OlEiQu0t6cz53tRK3oTZnkBCj+RDN4XteSH2O9TSnjGUWge3Ry01lrt+GKIS4CDgd2CKlPMRxXKD44zRU13yjlHJ12nVbkhCFEAXgW8BzgfXAzUKI30op7xmL+/egSFAT4lxg3lSYMhuYGZzkK5tKcRCmDCly9HbDQCmeDD3CFBNK6oDjZSQZ1attnJkdKjl6mQ2iMCUrO9dgJjRyLpVqr51kQ6xZYtQ6g6UyxxFjMHyvVAiPXzadK6ZTRR33op5miNoPYTxIiBeTPOveC1BTBiwHjgO+HfwmoiUJETgWeEhK+QiAEOJS4Ayg4YQ4A1iIQyrsRbGjHl9cAgaD391Q3Kn+6+9Qh82oMgiToVnpPgmqsj7B5azIiIJXShkVqh0eSQFESYgz+FVxibTLZiCsanJ8R65fL+pWm3UrSFCZI0s0/tCWEm1SdCaLtW2IrtAbVCxiMYdYxLwIUUp5vRBiScIpZwCXBElhVwkhZgkh5kspNyVdt1UJcSGwzthej4PdhRDnAOdARXCrBzOA/YAlBNKhKRVqQtSThvsoQtwTbJdUp5xuqNAamhjNJWncQmL/qqXzxX35PdfONJhkOVLZFxdEbMCVoqplkEC6iWPCE99HLd0rQWW2oIfvAU4p0eVpdjpWXIutRucEiciqMtc7656LQxYC45IQMyGooAsBFgiRmh48CZoMlwMLC9DbiyJCmxB1jVlkqDHFp5LGqxSWXGytBMLjmu390q/IbxH4UPBHKXRVoe9V9barIUdj7E0N8Xn1Jl1N+rjUA4/odJ+eNqRlsO9VBy0lAkjKbz4m7EaPVlG73fZDfcyMTwwN4XNJh654RJRUmd/QvUwSYr2z7tWEViXEDcBiY3tRsK8hMMlwSRfMmEuYBM11UA1yZ7BeokKIQWOd7sOuPZTnXMboQ1ptjnNZaOkxtEPfx95XKxIdKjU2+ziJJtHBEi5IKadO1ygkOlZc24luMxMj0fUawm7s5A7lgGxDnY44VuJ+Y4ixXoyhl7kmDmlVQrwZWC6EWIp6iLOA1zTiRj1U1OQlXTBjAcp4qElwBmFiDGyG5b5hE5Wv0v/3lCpzLvs+MFQJwja1FXu6UmfXcd2rWrjCKVLfftWWOYUqHA21z1XSIsgsHZr6qOsPZuiND75jPDPhU0q+9iCbgdjhoGwIj3EG4nMjmkV1reeAvLzMGfBb4N2B/+E4YEea/RBalBCllL4Q4t3An1Dfp4uklHfnfZ8iARECSwoGGc5FkaCtMk9FqcoQkQqZSqixFkvgmbbEoZzamdE5CrnpifXKZb5ztSF6bJUwswtlQkyZM+eWrPmZzT9a6T/iPM4BouOYw04VfU5kThhbRbZDbox1r6CSFteb5CEvCTHDrHu/R4XcPIQKu3lTluu2JCFCeXLp3zfyHloyXAT0ajK0JUSTFLVKodP7Q7ihalIM+o4gSPIK5azDHhV1Oc325ZcSqCr4o1aSqkLsW49LdZsRSZKgr0M/GjWVZ6uhljE11vC9OEdVWUIMh9QkJogNOVYMCRHcarOG5WnOgxCH6KzvImSadU8C76r2ui1LiI3GQhQRLgTmLUC5lRcEi0tVnkpYdXVJhhBuuCWU4yVoRKbdUJNhXLcp7zclUXufAdv4n4q63rxLz7KQ6nHOp+npejTND3p/XojUrV30up0quiU4Ki3WqVI5xR1/6IUcKuZ5zmSxtnRoSY+iO5C091AX2mOZWxCzUZLhfsAirSLPp6Iuz0MR4kwqZDgVRWxxJOgS0gxPdNFTQdsQpZLUvmR3iBh4WSTFhtiHDCqK+zg0AfWQopa6E+s0F++y66LRVdd9zOF7YMyyZ4h4JjGWCdIcwmcL664IrGA9jxQd7aF7LYYZKDJcCCyZTUUqNBetKs9ATardFSxmQGEXFakxjhC7gQKhwfGmlKjVZy0fVGvr8kpV2LZSYTeFKnP4ZTglbdhexCaa8mhZwm1cTxFJZxVTLFfdFvIYv+aEqS8kfGAcEmJ8PkQP23aoh/DBSDTu0JYM9XpXTo8IjCLa6b9aBUUCIgSWTAWhJUKtMuvhKZoQA9KT3VSmyOxGSYpDqA6rpUazn2hVOUDSuGQ74MKUHkf8GFqyWMBU6TIRZOJbz0qEjvPycqiMlWTZkq3fIkPXenlfePienTE7oiobDyw9VOgNJIfeFKgMRsjB9CvbKnPrQJPhwgJM0aqx6UyZH+zTanIXjHTBwLQiBb+EVxpFDFGRGLX6bGPQWA/4yZnwNUBccHYINahnEXIcq7c9BoTmepQag4Si1y65k2bEfmxyNz1UZ0NUhyokaI9SgWiy2PIHPi7sRi/dwb0K5DKNQFtlbhHMo6Iq95rq8VzrNwivGemC4e4Ohro6GWAKnYVhCv5uuruoSImmt1lDW/jrrFk/shLA6JPCr9i4TFtX9Wq0y3CUhAQp0lUfWKn3yTfrje1QSUKk5KZ6mICqnVaJJUiMLXCuuiVELzR8LzxixZ0kNuJptj3KZn3oj36Ow/dGZQf9w22VuamIOFFcdsOADOVM6J+qiHCYLoZQvwDD3UW6hkaUlKglwwJhz5spHVbZ52O7ietAaKx0KbhdChFW7exIoxpNL9WRhe1hLrhIoE7+yeGblH6D3KEfOqMN0THZlJ3QIZIT0bQ3JtkQu6m0MW0WygGjpQ4GdrclxKahByUV7gcsmklFGtSeZb09W5HhrplF+gtTykQ4HMRMFfDpLHTR2TVCt0tNhljp0FSX4yabcl4uTkJw/MEmQ71d8Pwa1MgkO2KCNFlFpymXL/hTNVJt7sP7UnpBrKc5195jO1QkoakXEkwm5igVe7oAe+heeb8OvYFo6I2WDE3kJNDL0Q6G24TYHJgjURZ1QVGH1miJ0HCiyF5FhrsK08tk2M8Uhumkk2EKlBhmmP6p3cBg2c5chuVIyQIXxyVyit0pfMpdoaabhtCAUcR5SHvWf11z2ehxHa7bNERSbFivSfEym9vBYga8uyeYsmyHZugNRpIHDS0d2tAf+3oxKmB369JO65YsByykEoA9w3SiaCI0grA1Ge6iQoj616cQNKPgyzYVIqSY0GCKXnzuaadpCKNLxIqP7t35heEkIaXZxEgzpvoGUbtcOaIl4yPEKfTOUJtslywjTnKtPewmawZy4yviO4bvWQiPVolRjYlOS+oXCuCNhAV+7Sx0FSkvO2IJ2JXDdRqECUuI86gQ4jxNhnONZQHl4OvBmZTJsJ8p9NNTVpd1Bx6we6lJiklfzxhVQ5OeXnpIiEW0+6BzpEpUUiwTTsPf8ghlFS+GqEf9/JwoUNMEB8mk6Kij2Pp0nZ9LHTtE6hQJUQdnq9hCopKgM0ZRFVZ6QbC1aTucatxHnayQlx1xlLpHuzQSE5IQe1Cct5DAbmiPRNFjlWfCyAzYNXUa/UwpE+IwnQyhvMtmpzC+r2q9u0TBH0mdtzYp5Cbz/3SaMbPPlOKIMEGKqeqNZyl4ioUyxxAcPR489rixnioRxozXjb+248YN6T2G2pwmIRoe/HAsYkVqjJuOtOzYcjlR4uo4j2+azhbVopiQhKjV5EVdUDSH4+mgax14PVPFGO5iWpkMB+ihn4pTpUCJEgU8StH4qQIUundR9GKS9MfUrikdmn7FRCnRvkAtSHzbtSYjyACnE0jt1Bn7ks614RpT4/qbixQ9UEkK6kEm6bCW6VyrtCECGJK3a9iefcxcQvOrdOEmXbuI9WKUNiGOJTQZLiGwG5oTKpsTK8+EPTM7yqqyWqYxwBRDSuyiK4hGdQ7josQUbZy2YdhmtA3Rpg/fsQ+s2TUymqzMcNz6US056hIXE73hSQkdMo3DpvoGa844XSvyk7izwLQhWrudxBiORQR36I2JspSoYxG1dGhrIOYvhEPKakWOEmLazJxCiDcCX6aSGPabUsrvJ11zQhHiDIwMNi67oc5co1XlrjAZ7mJ6mRD76SkryCbMvCKdDFfGhWaEJkEXb8RKh67GGTReTYQtATteLnQs6kyxSVC0yGOYiJgjCo10WlVhQzROM2MR45PDhmMSQ1lvskiHeSEnCbGKmTl/LqV8d9brTihC1E6UhVMJE6Emx5mVZdfM7jIZ7g5+tzOrTIhDdEY6rOlpDh2zbSvW9ogPfilMhrbK7EJoDLRp5M694boYN49BcFFUlfYrT/sjjsZu7qzWjlhTz8mSjsKhNoObGAEdq2hnzIbwaKA4YpTdIMxpMBqN/FTmhszMOWEIsQtjnLI5FG8elfyGARnumdlRJsPtzOJJZrGdWeV92sPcyXC54djOi06Gw9KjXnWNCQ3gIkM7hq4FhaSMsLI8a2R4oJAUZuaatFD0KHdcrQrrU80QnEyN2jzJ+oAVfDWWOTVpRuYgx2q7WfY4RIxYREKnuRwpYYfLUFcnBX+QLjKm9solDpGsqnfarHuZZuYEXi6EeBbwAPABKeU6xzllTBhC7EQRYmScsmEzZCaMzIbtXYoAw8tegcqsnColCnQyzJQyIVZaZSZ7ly0lWodt1bnHcay84VJFg/W4RATQhPlK4tQuxz57uGHe8ZNxT17Ekf7L+f+EseGeMSterjAyZtuV5lCVK4RoZ7lJD70ZpkuFkgXhYxFStCXnvJpSdgkxj1n3rgB+JqUcEkKcC/wIODnpDy1HiEKIVwCfBg4GjpVS3pL8D4VOgszX+1IhxNko6VBLibNh+8xpZQI0yVBLiDoGEQgZo80OMj0tstSs1UHw/bBEaP/q0xOFqRRJKxNJZxI/HTPAVQvbEwrK+G8kM439X52wBfS8EWuvzfVmKTZE1/4ArqF7Lg/zsJnG3yZF1zvK6/nyc6qkzqonpewzNr8PXJB20ZYjROAu4GXAd6v5U1eBaJLXXipS4mzYNrc7pCJvZxZbmVNeNwmxk6FQw9JSQpehRkdg7/aBocCGSDwpgjvsJpaOIlm98jYAJTFTXEKH6tksb6nQlf4rUast4H5noVNi1GZTcmoIGcbYEO2PjR8OzradKZW/V6RFM3ys/HwuSdErn5SvhJjPSJWbSZmZUwgx35hp78XAvWkXbTlClFLeCyBElSpJJ5Xg69nGEkwWtae3o2wzjFt0LKJSlzspGWMhMoe06BoNksfKwKFi2w/THCoaiZPVR25dD8HUKp4ZvbPGS1Q9HrsGmOE3nt3BM/SCMBn66moNHdOcMPonojoXysHZ4B7HrPZ7DAWxtU5MhZI3zBRGK/M2Q76EWMOYfxfiZuYUQnwGuEVK+VvgvUKIF6NqahvwxrTrthwhVgMhxDnAOQD7dlNRjc1ltgqxCdsN9ypLiiZJ6nAbICIdDlBiikGQqfn8Ap4YGIpKhDEyQBkRsnSpTDFwqnQ1EVWN7Fal4JgnEWZR8GNjEjOOwshfGs+AGMnQVptdyWHtMcymquxsK10Aw3QWRqNzN+eBHAOzXTNzSik/aayfB5xXzTWbQohCiKuBfRyHzpdSXp71OoHX6UKAY3qFLEuFMyjbD2Wvshsq4turHHPokg53MT1ChGqESo+z40ZIUdemHvdp2A8hPtzGVptjkUdfHAs3dkZSzJS/MaG8pguiZrjGMPsqNi9TnkmbLDxyrGOf0PA9Y3eYGFVwNl2Vscwaps1wOGb6T/v5Sl0eJW+IkjdSSWCS11D09tC9KKSUp+R+UY/KkDz9OwOenN0d61E2nSlaQixRcHaA6cFbdA6UNzuFbqxBRm3Tfghu+2EmVEGGmQK1a+q0SWM+UuLmssAMuWmCIJaGVALPpTfZekSAOCnRgNk27cSwvmU7hLAZyBzj7FOgVChAdz8wQpdnzL9SL/KzITYE41plDsEj1m6oCa8yKkVvTwsd6y9NoeQX6OnqD126Mxi+l1VNLi+DlIOr40ixCorJH3VLMlVcwBdKinFUYR4jbUyOqLpRZ8h0k+U/DUGSl7l8XGXOtudlrniYvcjETubz2URa3i6AP3WoYlfM43na2W6qgxDipcA3gL2B3wkhbpdSnpr6xwIV6XB2ZWieqRLbKnJIQtwzi6HBSjK4UpdXblqZ4/n0LHzmrHyE1WS769tqcqbkDub9YqBJxhW0G4+GU3AElbyD1ZFiLSV1jlTJiLBTpUTZqZJ3nF4IxifTJRkav3ai2Iqq3MUww6FniCN7e6hq+TpdHtBPZyEmiUk1aKvM1UFK+Wvg11X/0VCZtd3QlApdpGiS4e7t08EvgFdCy4f9XT3l5A5xWUTK+7Xd0FjkYDS0xpQOTcT5TfxStqlIUzEWtsNWuq8DqUVx9Ibo8M2xSgMWIIuEGMAVczhEJwXHLHdmeI7+b3TqASN0pwtK3hB1fzTb2W7GCAXKKrPLbmgPzyuT4dAsdm+dBdsD2ukuMuIXyqQ4vctt8NAevQKlsN3QWLSH2STFrPbDULOz+2ALkUwEQdmKpWFm79pG74Y+Zu/aRvdjg0x/cCvzC5vZZ3QT80c3M7+0kXmjW5g5uovO0RI8AWwBthrLk6gONAwdo+o15zpGpOV6gOl6s3a7PMzBUgpiEUuFyggVRYaV9GquGEV7Aip9XG+HjhcKwI76H1HWf4lGoeWaQ80IVGbbbmg7TkIEWZrFDk2G24PrdAPTiowApe6hUEp1s4GEoJ0Bg+HF9ytt1h6U5frO1sNz+cbxhUtXYJSF7GQfBumlxGxG6WWYXkr0Msxs+ullgF520rupj9mbttEzOsC2KbPpm9HLtr1mMzCrh44pI3R5Q3R3DNLTMUhPRz89hQGmdPRT7Cipehswln5gJ4oQB6DHh/2BA6hYJ/RfBoPT9bIn2J/nt2NsArMTkOBUGQ1iEXXzLOExTFeZDH3CqrCdAMKdVNazjudBiKNUn+t87DBxCNELj1Puozc0IqUvGJFS2bcX27fOgq3dShLZTThkwkvPoudRoqs0pHqjthkadkQtIUKYGG1UZTe0oBMRQBWkaLGEYJSFbGJ/7mAJ97OEx1jCZpbwBPuxgwXsYgtT2cx0+phGHzODZS73sYg+FtDHUrXs3cu2JbPZuf8MWCqCGb6AJTBzyWaWdK1hCY+ylDXsz8Pl9flspGvdIKwFHgU2otbXApvU9p4+WD+kImx3oqp5FCUxelSCr2cBTwH2AqZRIU2Jit/Hx5nCMlK3ZZtbuMI6vJL6e829p8rYnBT7oVoK5byIigw7jb+HCc6WBtWvTX6VsdF+ORI3r5TZO3O4TmMwoQixb+bMEPm5lj7msJVetvfNYnTzVNiMIkTtnpxGqL26p3ZUqnInw3QOjlREFb0EjTRudEqSFSZfiSZ8tbm7HufAjfezfP2DLN/8IMv3PMgBPMD+PMwOpvEQ83mUWaxhOtfzFC5hBWuYxXpmMEIBRTc9wPTgtzdY19l3eysSUy16bUJIietULQVqSVDXrxeUrghMQQW8zg1+pxH8cReKaDcAK4A5NZQXGuBUMVuHI/RG/0YWr5wX0R5c4Jb2KqNXShToob9MfsN0hrZt8qwPo7QJcQzgeyIyTnk7e9FHb3m88lZ61f4dsxjZPKNip9qOalQ6CtU1DWOAEhXvcwGfQoz9kCrth3VJibZkKCVzNmzlgFsfYtENP+Xwm+7m6LtupWtoiPtmHMSDheU80HEAPy++igdHlvMQc9lNP/B4sGxDMYbdcOP0w/qnMPVK9av8tu+hiJIKtwMdKEG6A+ieSUV8FMDdwDXAMuAUEK8BniZDpO6chS/3jDAJXwObDM31IPSm5IeH5sXZBM3fKQHxqfM8wtLhQGS7frT2tHsThhBLFCKqsa02b2cvtg/NYnDrXmHDvfZ6WUFsvh/+MurpSLsYCpZhipoALRuiHIx6l/Utkobs9SQcd0JKimt85q7eytxbt/HM1auYd+sTlCjwr6MP4e+HHMdFL3wz73rVt1i7fV/YIGANKnvcMMrgxk69khFmhy3GH3Jt54BcLtmBIkRzju49wENQfCXsP7qBnpf5bD9nFmtXLI7+P3MexFq7WAwppjhWBvb0qDHJmWyCbsdKnIMlH7RV5jFBCS8yGiWiMmsnylYRJsRBUmuihJpoqiuYtL4zGAxVVpctG6I5htnlWDFRVQffCawGrgU2wpT7JfsVN/Pk0f2sP3oht779CG46+hjuXHgo68S+rNuxmMH7ZisSzMFBWA+qntM4Q8WYHx3z1yWzliV2VyhTJ3AQcCYML4ONa+fCTwu8cOXVHHPAHfzp3FNYf+YiIqPfGhqHGMD1kXGQ4+hgZ/mzVuo2pyYN2wTVvig5TgmdFyXTfNDaQ1UmECEWyska7BAbvW/X9ukVJ4q5gKqJbqKNLYAZ3dXJEOW5TDQZmnZEv+JhtkenVA3dfh4Abg229wcOA14HAy8QrF2yiHUsLi9Pshchfc98y85CxMmkdkItXTEpU8FntANmQk4ttKqiCBhe0cm6zy3muk+dSM/lwzzrf/7Giz/xB774hQ/xnZVvCztVxqIXueyG5v5BYHeRUd+jn0C7mQYUEsJoHLZBl73cNbdQ7RilOm0kHhkmmeoCLgGOBvqAV0kp1yRdc0IRYlJqr+07ZjGydUbFZridyrpJho7rAmWpUKvKXQzTNTQcDrmxYhAhbDfMQoodKPv+YpQjwNuFusc+wOHAocB+wFLgKSDnZ/RepKqyOY9ScdVlzNC9WJjpp2pErbZZbYeTxQ7uP3M5fzzzufT+ZRtv++gPef2XfsZ7zv0WN3UfH/1jI3pUXKMxybGcll8wyhQGtbmnW400saU8F8HZqrFNovkgHxtixkmm3gI8KaVcJoQ4C/gS8Kqk604oQrTHLIfshtunVwjQth/GmHr0cCgtFXYFszV36qnsB0crRGjZENWw+HT7oUBx3UEonpuHai67Ua6NOTOgOA9l57IEvzYUbLUZY7uWBl6IIaDbTz6cM/95Gsd9bzWXn3cGFzzvo3x12QcBkWM8onlzx9wq9qLbnPlXXwBdDKLacMkvUOo2YxSjo67iyDCNRKtHbjbELJNMnYHKvg/wC+CbQgghpYwNDZ9QhFjJYmOpzNunw9ZilAy3oxpTglc5bI4OR2YVTVXZsiHaWo3ZWbW56kCUoNePmi3nHuCfVPJULARFgHl+oJuBoCLibIi1SiBJ0rZJhnnIvjrAWXZ0cOmZr+C6eadw2YdeydPv+BuvOfKnDCU1oqrhGKVib5vLbsc5CPC7GTGmf6UbK6u2m+wiw/aIZuCuHbl5mbNMMlU+J0gouwMVK7aVGEwoQtR5DkOz6fXNYnTr1DAJmovpWbbthp5f9iprqdBUmcsN0rIhSj8aciNR5HcAygT4GCqf+Z9QcXQ6um8GY/BSYpmkAS7hMUIuJc/IywXPZ+38/XjmJ2/gxxe8jh/94w28er+fIelI+Fe1bzXDFAKmdGieZ55PMOrKL6g4xe5CefSVe7pSd9xtfjbETX+CT2eJ+uxOmXWvIZhQhBjxLNt2Q9uZsp2KdBhpSOB5pWBC0ortsKwuM1QhQsuGODBUudwsFAkuQsWA3wFcSXjwUv1RfBlRFWtU0zTq1xeVRzRBlquiP9aqKkdvGZZo7UQPI14nZ7/0Eq763nP53GP/j/O9L+RwVxN+5SduMaVDP/pXta6cLYOGtKgy2ATrMfGK6jL52hGllM/P5UIZJpkyzlkvhPBQ+bD6SMAEI0Qj5GaPI97QXrT90AUPOruHLduhISEOWSE3xsDakQFl8jsIFdr2MHAjihBbdxRnTrDrM4WEI2pYxsgcO8xmzBF8A4Z6unnJ837DfT8/iIsWv5mHvWXGCbXAoVvoeVVs6XA3oQ+466Ne+Y9SoU27YjXxivlJiLkhdZIp4LfAG4B/AGcCf0myH8IEIsSRIA7xSWbRN9SrMtjY8Yb2EqMqK6+zpLMQlgr1eoFS2KEyiBrgcTewFqaOqsuvRiVvqTbsuaGIkyJqQtPoKISxGgoJ7jlVtnX38q1F7+Lf+r7I28T3Hf+qVQcIiNEvuglRIxMh6n3KrlgyvNBZ4hVbjRAzTjL1A+B/hRAPoXyUZ6Vdd8IQYgmPPnrZzl5B8HUxTH6bjd/NVJwpTjIEuqNkqL3NnQwph8oA8DfgN6hxsYE3eMd62DAaNe+0EUZsJ2shU6YrYYannUOGpeAbS9/D2jX78vYZ30lxPnikf0i0G04vxXgyhHizj4+KRbRJcZpSoXf7Bbqn9Zftiq6hfa4xza2CDJNMDQKvqOaaE4gQC2UnSiT4ejNu6TCWDKHYHVaRK6E3w3TvGoLvAN9DffyPQTW8DahMLY0MjWnYXCNmZRSpTrnXRtfks6qOQ7QwYr2vkZh1s1Spspm+plX22PmYNbzw+rbOXjZ7+7C/eJgHODDtrhkQEuvCNsO401wkGHtOVIUuTbXHLkfHNE90TJgnLFEIO1Fci5nZRsNcNwixKyIhDjF9624WfW0Te397JxwPfBzV+NahiFBfsgUnSAohIoGNjUgWF3aTqaPVMOqvaKzXi9jUasZolT6vl9lym3GwHlVZ/wbrWjK0Rx25FmLWXefEqNBxY5onOlqOEIUQXwZehEo98DDwJinl9rT/lWSB4Tgnynbj1/zCmj2lO7yYDpW9N27l5K9cz4qL72fXK6aw8+ZOZhWH4RHCkVA1op5MNzWjERyYD/OkwjWYsCkwh+5Jyf7DD/Noz9JgZ9H6zQr9ROZI+MCx4rLBuIgOkgnRPAfKXmg95M8c3VI5tfVU5kag5QgRuAo4LzCafgk10fTH0v5U8r10J8pW4w8xkqFalENl3hNbOOdTP+QZl/6dh85eyvV3Hsvei55gMevcRNjU3tkIZLF35YPydK4ZUW011zyW3EBSAt6Fgxvwhcfjna7pxmuBaUfErS6De1BBHDkmSpCVIX/26JYW9TI3BC1HiFLKPxubq1Du8nT4IiwJuqREIDL5Z3d0EZ0DnH3hT/i3//ef/OO1x/KV+97N3nMDInTeO1MJM8GWFr2mt0M7wUNzUW9VV/MkmTKQBz3odZt+zJ+mp08OmQ12OuEBYEqUFONU5mqkw9B6ZXSLVqHt0S0THS1HiBbeDPw87qAQ4hzgHADm7RtVj/X6VgJ1Qzc0j9D4U4MMD3/0dr79oXPp7BzmA1d9keHDO1nMOvbmifDNJ5QkWAt0BaRQTEzMtmuURCMQa46o8f25SLJH9vP+h7/GymXXqLBfl8OOItklblM69Kk4uBykaP4lSXVOUqcj+4vlnDT26JaJjqYQohDialROAxvnSykvD845H/V6fhJ3nWAoz4UAYv9jZKz9cDdUvrQGDCKcVtrFZy79JK+96Sd88hP/jz+97xQWdWyIlwo1Jj0x1o6xIMN6G7iLAAteKXThf7vli/xt9tO5p+epxn6PiiZSy7A93bAGCCdDMEjRJLssqrNrn0uFBtJGt0xUNOUJpZSnJB0XQrwROB1YmRZZXoZPvMpcJkNLXQ6kw5Oe+CuX/PZsrj7yFJ7607vZ8bQuFnRsjL2VM919zsSYOVwkNzSgKTT4Y1H35eu9gAen3H8Vb7nrBxxz0i0qOD8XmKqylixN9FScLFnUZmL2J6nQgV0xMrplgqPlKD9I+vhR4NlSyuwDPEq4PctIKlMRGepKN4iuUf7tsS/y3k1f5+xXXcJVL3ge7A1FdpYlAz2YScM1UiErqo3uyx0t97ZjUEU547KQ55ntJs6WuHjLWi75ydm85vk/ZbOYH1PuWivdVJXt/VAmxSRpL86OaBNi6rmWXXECoxW7yDdRFourhBAAq6SUb0/9l09UXfZBpRoyybAICGbJJ/nfTa9nr+KTHPPSW9iwfFHkkmmD+xsXJN0qyEHE05fw6pidPKjnMYnvzNgjlq57hGvOW8kXnvdxrl30HJV8yrxGXVWnad7OG2izXzC3oJYU4yZJS3OspEmSUFahJzpa7gmllMvSz3LAJsTdoBrUTsITVMLebOHq4VO4dsZJfOjo/8SfWgwZ/wtetDXXIxm2BFxvehzbP+uR+kb8dJOE/TE0t/d/8GF+/eZX8x8vP4/vrHiHmq9Go1zP9Q5XypIzULuMAmnRtismLdOISoXTUs71J3524pYjxJrhUxmJsh0qZKilQx+Yznw2cQ0v4bKOV/LpfT4NHTW+ZJNMGsSVY5YWrJXhIO1m8XiBEsdd808+97rPcN57vsjFh7+pQoZOb7reUST8NovEP4UZkB13HCoSom+sF7PZFatWmVOKNIEwcQhxBMOJshMV/7ALUzrci+1cy5n8kLfyxeJ57o94g2qkJSs610Kl9JZAuiiMca+q525mWcXoKK/8wi859X+u4a0//DZXLD0zLBmaCNVrvcP34vYZiR/K61VIixjr02LW4yTKCYyW7Kc1QavM5QnXtXSo3qKgi0v4OFfyPL7IeZHB+XXZvh2w5YEJnwexDlQTfmNTQlIf9clHyp66aQ/veMsPELvgtbdcxOp5R8FDwUG73eTSo9KyPZpMpWfyHrH2ZZAWXWpzHClCmxDHFUYA2Y/6bG/DJEPw+Aj/x2x28DE+VflPAhHGSTIFShRM635KDY4fMqy2tdfHBOPCJluSHPQ/D7DiMw9x3blP57ufejNPFPeOmkhiH92m47TW4KL4JGupKRm64MVLixpxKnGbEMc5pESR4RZUo9MRO0UWsJ2P8WOO4Lf4STKDIwVUef7lJMQIOC1buU0sWM0ppPzEzVTEhefEofOfIxzx9vvYM2MKP77uTG5fcUS47J4kdu7r3GGX3JQEzW29WCo0jphFcBOg63I4zp+gaNk+Wz2GUWTYR+UrrMjvQ1zGxZzGOha4/5qxFjKNbR0LTIKG6XontXqWq+rTO6Djv2Dv3+/k4QsWs/p1h7NdzMxWxth2ZKoiSQ4ViH9Kc3+PtX+K41yHCu3jzvaUJB3a505wTDBCfBx7RMo0RngjV3MoPyXUqDzr14DnyNuXFxm2TKqECfTmc8EwcAUq+/mLYdM9e/HEXr1oKbAqZ5AXu5GALPl4XE6VHmvbVqEd0qJWoTWykGGbEMcbfJTtMDw873Ae4wEWsJG9on+xHSttBGg8ZdeTbNQMPEkraWquyRHgFuAiYAXwLRg9FUZnJk0pamDM241Nerb6TMx+h8NF77b/4rpMmxDHG3yUZzlsIzycR7iTJcHWGET2eUHKrhbRrpsCu1VV05nGqkUOoUaX3AYsAd4JnAg8RR2uKvFE7Ic16WFcw1mq+RBpp6FLGjT3uyTJHmBKxa4I2ZwqkwATjBBtD57HHHbxOLOip8c9ea01MoFqsulI4KJq+qYr200n0DmEmhRsDvBslGQ4P0uxAidboQReLUE9cY2k1vS19lQDtpSYJkk6YhbNUyehHXECdWM9VrYSagM+G5nJiaw1zrMacUzojQ4LyTM8ZHxVtk8Og3KdqMrTHHNqtUr9Pijemw74Erz5qCnMZ1V5obiypcayNko7Mf3nNpPZI1lMO6PePz1Miq7LQJQcJyjGVx/NBLNxwC0s4Tz+QAclRu1TU7yE4yJWbiKjztY5DzgCeCrKgqFjEOZ60N3IxC2RkSpZyLBeu60Zd2te01aVIRyeA067YpsQJxqUwnQ7+7KR2byCv/JzXls53Ignn8C1malT1/H8eSWL3Qs4FDgEpR7fB/wRpT/MAxa6/lTPe6tnlBOQz2wv5rVsb3MWaXEGZS/0oGgT4sRDWIX4PC/lv/ghv+MMdjPP/RejJuzpMm1JseQVICpvJqIlRqy0YIMOEWEKJ9oylN6ejpICDwVmAvegImjWo17rdIh767XDK+H8SOQyjrleaGnRJj973USMCq2XabRk+8kbE5QQwXzpf+RIXsJdXMb7eBG/qVoRdoaIpNRcsyJ6YsNZxqox5/WwKeTYBRwOHIiyD94H/AU1PXZ1n6pkJJpNqn7WOIJsRJhTkqRo2hDNxZQYrbHQMCkIMWOw1XiFbmiCd/EBAC7hXHowEnFXOSR33M8+lrkT55VvOh5VxSJKRSfzgKcBZ6GiZW4C/hO4HDWJdxoZ1vI0Nik6g7RTVWfXJzJPdTkOA1SSJJsp8fqN7Z3WsZ3BcVkZ3bId9+RWEwwTnBArKOFxJt9AIvgHJ7DMf7BysIovfbUjVqoRIpr/Aa6V/LI/pf6gZPI07wbWosS+PkWGU4Jdl6BI8D7Gpt68YHbiiMSY+hiuOVHGGjr7tp5KYxcVkrQXTYz6nKBNDDIpCLHZb2pM0c8UXsf3eDs/5e99J/LF7n/jm4vfzXDGORZLFKqyeVVrN2zYy7AZIzJs0byzSz+q0RaW8YHKdboL+AdK910N7ABmo+xXEtZvgw2oAZrVUnfecm6HZ0Qt1JX4ZywHcqYFc8ep0IbDZYKjJSVEIcRnhRB3CiFuF0L8WQgRk5WhpqvzHd7Bs3qv59l7ruO+Px3Eq+/9KWLUrWzVM8SsjWR0DJeYc0Mfiz61mdlP3w1HA5ehXMWvBt4BHAfsjZNkskiGY6GUOpGJFJtRMi0tmiryAG712ZQWB6jE+k5ctKqE+GUp5ScAhBDvBT4JpE80FYuohHNf98Gcsei3PGvKdXxp9cf47E2f4MLTz+GHb3sTw0uiV4i1HVYZduExUZM7JEuRXUODHHHLHTz79hs49tabOXj1/Sy4ZzO7Vkxl4Lld7PzMFOYs2aWS/K4FNgLrgnKO8TeplOowK+kV18G4DcJ11GwDidZd4sZCu9DsMjceLUmIUkpzurGpNPDTdP3cZ3PCkf/gmI5bePvt3+H+FxzIX059Nje88kQee+4iFccxRmhEcytlninNdV5tJeouDXBY350cve1Wjr7uVo56fDUHbryfR5Yv5cFj9ueRo5bwrzetoHD4MIunbGAx61jMOhU5vbWmW2ZCQz9EVTmrWuKTSDiY245hjFOhJzZakhABhBCfB85GWZKe0+Cbcct+T+OtK5/Gh2b/J29e9V1edtFvOfrNq1l31CIeO20Ru06bCk+V9U+mNsEwk12s4FaO5nKOLt3OURtWs2ztQ9w38yBWLz6KWw4+hu+eeS7/WnkoXQftYHGXIr8lrGEJj7ovmip55YQ8rpk5vsolQbcCwbhCc2wborlMbDSNEIUQV6NCyGycL6W8XEp5PnC+EOI84N1g5v4vX+Mc4By1lY8ot2PGLC465w388T3PZVn/Q5x07XWc+PtVnPiif+KVfAae30nxqFHl8syiZRgYy+Bs7RMNIZNHNIqpDLGcvmDZyXKe5AC2spwtdDPCfSxjNcfxd3Ei35jzHu5aegjD+3apuJhFqN9u6GJH5NoRG62tHuc0ljlvFJKcKiF4ROu1FaUtLS26SHCSDFOhiYQopTwl46k/AX6PgxCllBcCFwIIsaB2tVrXgvXO90yZwp2nHcKTp83kX984mIPuf4Blf36EaTftVjn0HkQ5AOagxokNq0U0yfZcVUhLAMEoM9nNQh5kCfewhAdYwgaW8DhL2Mp+bGcqIzzEbB6klweZxw3sz0Ws5EGWsZn9Uay3CDqKKmI6L1edJkfrcartmmNOPS2rd6VhcqvL0KKvTgixXEqpAwXPQIWb1YEMLzPNOSIEew6awraDZjK1tJPOtSPwCHAzKqfeQyj715Ow16iK9df+u35UCNcelP6vfXaDxm8eHFLo95nRt5N9t61jTl8fh/Tdg3xMMPPhAXof66N3cx+9W/vo3dXH7P5t9I70sRdPsoepbGQOa5jDGmawhmnczGGsYSprmMYWZlGxFejMy9NRT5liQ8grnVqheQPhCkHAlb1ee+9pmu87I1xSYpsQm4kvCiEORA08eIy6PMwZ4cr/lgYPJRyNoiTFR4G1sOsR2DQcvuRocPocVOq9HqCbCr10ozKyDBuX9gCeRLHowyju0fwT/E6RoxxYeowDRx+jVOygv3cKO2bPoK+3l82981g/dSFPiPlsmjWfu7ufSt+cXvp29tLX10vftl6e3LYXPgOokb9bUFF+etZCPR1DEullpClHvdY84VSAarpoHqPPEoPyI7GdSSeNF5hjoieH2tySb0hK+fJml6Em6NoUitx2E4722mWs25gRLLNRBLmPXmaiWHQfYC6KTRej7HIHQP9TBI/1LGFdx2LWFhbzqHhK4LMNlr7FjDw0Q0mwa4JlPUpcLYulDbBsVjEEUtsRpRdQr6kqFyrXyppIq6by1SPJxjmBfGHtgPGngk4uKbElA7PzQ21ftJKfHPjmF6oLjMuaDW8EpV73oyTFEqg35KFslF0oUXIaSmPtAboFslMgPQGi0gEjo2pqRr6KalyZys6VlvxER1HwYsYzOxFjpB5X0J/0iY0JTog2HF+4mEZc7wiVovU7FvDLTpVK2SPkHttpaylpA58uQYKrljMbRkOZCmLX0XiWtMZrubNjEhFi7d3CaevK0BnGQthRuRmbjaDTVzt2eZyiZebnbiN3TAJCTCBCL/lwrfDyZsIM13NJh+MWzUom2VCMJ7vh5MUkIMQxRhM6cNXqfZNIxmWbTfQ060PavFhocNEzXFxLh55XT+jNeLYlTmxMEkLMrwGGyKcBvbOqkraUMFibWSH2Pw14toaF3IxZKdpoNCYJIeYDpyRmhIU0AiLl2qbHu964PoXm6aiJzn2jWM0M0I5FarWZzpS26tyqmOCEOBKzHiDDx1qTTKpaGpOmyhwMNVao3o5YbVCea3xujsjAyQ27eya12XqbruoL7WtLheMFE5wQoZ4wh3wkrpzhGN9rJnKoz6nSoOd1XNaMk0y1I+rA7BZ8HdWjLR22MiYBIZrwnav23LN+SmB2Nch1dEXGTDCpyCywjB0DOYncsSt3ybCeR8z038mXMWY8YxIQoj0OM9sX2pRaQoHOWhqzh5elIFdqibnYeAi5yWR6MH81jL81y4YIGZwrqS+6TYqtjElAiLXDVuuqzy/YeFSl1rdAeaHKMKGEmRtaDi1ZqDaqwSQiREsytD/U9X64c+gMtuyaKAlZ98tfOsyxdwd16zJFhKRvUzo0HRPBuueFS9VUSdFLkRR9c8VMvNpGK2MSEGJCuIPLrOOreUhqJhivwcZ/xyiOzGVNJP1qCp1x1IXLTlveNKXvQkXSTbCT6nqtx8ftEYwkMgk3j2+Js/pcmYLbpNjKmASEaCOBFWIOtYRtLi7NVD3Xi8DlAsqX3Z2mB33MC5pjgXB8p5UCrNaSVXO+Lmd7GtrJhUlAiBnyzyUQYdihkjJeeKxsSHn10dTMNzmn/nLMABipy6QsN4VKqXKvauODUybmAFWnUgu1pxFjZ9uh0uqYBIRoop6MN4XmZTlx9f6gf1YtvdbVJ5NoyI+ELyXdz5S+NOGUM/docjI9+Y5bZ6Vr2+4YMmnU+HFJy5npJsU2Wh2TgBBjGmMcMfgZGju0hkcxN22uXknQqOMUwk2VthJGfQivdstB5D9aLbcPGh+akG0zK/zYjYR9bbQKJgEhQuIQvhhJxu601UqHufFlnOfVQKqUWFMfzGKtyyj5xGiLyk7nKLsrFtE6Lfd0tglV6JfJsV7nVVtSbHW0NCEKIT4khJBCiDn1X83RSqsMvammUzQ0iqch0mkaxdRwU/sj40ftsXq9REElqrClNtO54oXdPllLlEjtLq99QtLd6kmxndBhPKFlCVEIsRh4HrA2v6tWqT6noQrNrxZ4LpXOukGqo6e+EpBOlNV1dPujEptBSN8ex3p0MxUhUtQk65JEjXqtWm2ObUdtNXm8oGUJEfgv4KO4g7mqhN0gc5xJvgHSWh6+3aaECrmcKjEwidGnEKilXlQyNJ0rhYqHuZox4nGjAatBaviN89ldH4u2pNjKEFLmSA45QQhxBnCylPJ9Qog1wDFSyq2O884Bzgk2DwHuGrtSjhnmAJFnnyCYqM82UZ8L4EAp5fRmF6JRaBohCiGuRs02bON84OPA86SUO5II0breLVLKY/IvaXMxUZ8LJu6zTdTngon9bNDE4BEp5Smu/UKIQ4GlwB1CzTO8CFgthDhWSrl5DIvYRhttTDK0QjRdCFLKfwFz9XZWCbGNNtpoo160slOlWlzY7AI0CBP1uWDiPttEfS6Y2M/Wmk6VNtpoo41mYCJJiG200UYbdaFNiG200UYbASYkIeY75K/5EEJ8WQhxnxDiTiHEr4UQs5pdpnoghHi+EOJ+IcRDQoh/a3Z58oIQYrEQ4q9CiHuEEHcLId7X7DLlCSFEQQhxmxDiymaXpVGYcITYmCF/TcdVwCFSysOAB4DzmlyemiGEKADfAl4ArABeLYRY0dxS5QYf+JCUcgVwPPCuCfRsAO8D7m12IRqJCUeI5DrkrzUgpfyzlFIPCFuFis0crzgWeEhK+YiUchi4FDijyWXKBVLKTVLK1cH6LhR5LGxuqfKBEGIR8ELg+80uSyMxoQgxGPK3QUp5R7PL0kC8GfhDswtRBxYC64zt9UwQ0jAhhFgCHAnc1OSi5IWvoQSN0SaXo6FoucDsNGQZ8je2JcoHSc8lpbw8OOd8lFr2k7EsWxvVQQgxDfgl8H4p5c5ml6deCCFOB7ZIKW8VQpzU5OI0FOOOECfqkL+459IQQrwROB1YKcd38OgGYLGxvSjYNyEghCiiyPAnUspfNbs8OeHpwIuFEKcB3cAMIcSPpZSva3K5cseEDcyeSEP+hBDPB74KPFtK+USzy1MPhBAeyjG0EkWENwOvkVLe3dSC5QChvsQ/ArZJKd/f5OI0BIGE+GEp5elNLkpDMKFsiBMY3wSmA1cJIW4XQnyn2QWqFYFz6N3An1BOh8smAhkGeDrweuDk4D3dHkhVbYwTTFgJsY022mijWrQlxDbaaKONAG1CbKONNtoI0CbENtpoo40AbUJso4022gjQJsQ22mijjQBtQmyjjTbaCNAmxDbaaKONAP8fIEIxNYz/ncwAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "algorithm = \"PAIR\"\n", + "r2, r = 1e4, 1e-12\n", + "preference = np.array([r]*1+[(1-1*r-r2*r),r2*r])\n", + "model = MLP().to(device)\n", + "model.update_preference(preference)\n", + "train_OOD()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "u7z9miU74Mio" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [], + "provenance": [], + "toc_visible": true + }, + "gpuClass": "standard", + "kernelspec": { + "display_name": "Python 3.8.11 ('gnn')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.11" + }, + "vscode": { + "interpreter": { + "hash": "ddf6e3d325859c53183a922f8f4a4c99a98233dd957a6c2162fb2135209fdc50" + } + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..31617e1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Yongqiang Chen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/PAIR/pair.py b/PAIR/pair.py new file mode 100644 index 0000000..947f69d --- /dev/null +++ b/PAIR/pair.py @@ -0,0 +1,452 @@ +import copy +import imp +from pickletools import optimize +import torch +from torch.optim.optimizer import Optimizer, required +from torch.autograd import Variable +import traceback +import torch.nn.functional as F +from torch.optim import SGD + +class PAIR(Optimizer): + r""" + Implements Pareto Invariant Risk Minimization (PAIR) algorithm. + It is proposed in the ICLR 2023 paper + `Pareto Invariant Risk Minimization: Towards Mitigating the Optimization Dilemma in Out-of-Distribution Generalization` + https://arxiv.org/abs/2206.07766 . + + Arguments: + params (iterable): iterable of parameters to optimize or dicts defining parameter groups + optimizer (pytorch optim): inner optimizer + balancer (str, optional): indicates which MOO solver to use + preference (list[float], optional): preference of the objectives + eps (float, optional): precision up to the preference (default: 1e-04) + coe (float, optional): L2 regularization weight onto the yielded objective weights (default: 0) + """ + + def __init__(self, params, optimizer=required, balancer="EPO",preference=[1e-8,1-1e-8], eps=1e-4, coe=0, verbose=False): + # TODO: parameter validty checking + if eps < 0.0: + raise ValueError("Invalid epsilon value: {}".format(eps)) + for _pp in preference: + if _pp < 0.0: + raise ValueError("Invalid preference: {}".format(preference)) + + self.optimizer = optimizer + if type(preference) == list: + preference = np.array(preference) + self.preference = preference + + self.descent = 0 + self.losses = [] + self.params = params + if balancer.lower() == "epo": + self.balancer = EPO(len(self.preference),self.preference,eps=eps,coe=coe,verbose=verbose) + elif balancer.lower() == "sepo": + self.balancer = SEPO(len(self.preference),self.preference,eps=eps,coe=coe,verbose=verbose) + else: + raise NotImplementedError("Nrot supported balancer") + defaults = dict(balancer=balancer, preference=self.preference, eps=eps) + super(PAIR, self).__init__(params, defaults) + + + def __setstate__(self, state): + super(PAIR, self).__setstate__(state) + + def set_losses(self,losses): + self.losses = losses + + def step(self, closure=None): + r"""Performs a single optimization step. + Arguments: + closure (callable, optional): A closure that reevaluates the model + and returns the loss. + """ + if len(self.losses) == 0: + self.optimizer.step() + alphas = np.zeros(len(self.preference)) + alphas[0] = 1 + return -1, 233, alphas + else: + losses = self.losses + if closure is not None: + losses = closure() + + pair_loss = 0 + mu_rl = 0 + alphas = 0 + + grads = [] + for cur_loss in losses: + self.optimizer.zero_grad() + cur_loss.backward(retain_graph=True) + cur_grad = [] + for group in self.param_groups: + for param in group['params']: + if param.grad is not None: + cur_grad.append(Variable(param.grad.data.clone().flatten(), requires_grad=False)) + grads.append(torch.cat(cur_grad)) + + G = torch.stack(grads) + if self.get_grad_sim: + grad_sim = get_grad_sim(G,losses,preference=self.preference,is_G=True) + GG = G @ G.T + moo_losses = np.stack([l.item() for l in losses]) + reset_optimizer = False + try: + # Calculate the alphas from the LP solver + alpha, mu_rl, reset_optimizer = self.balancer.get_alpha(moo_losses, G=GG.cpu().numpy(), C=True,get_mu=True) + if self.balancer.last_move == "dom": + self.descent += 1 + print("dom") + except Exception as e: + print(traceback.format_exc()) + alpha = None + if alpha is None: # A patch for the issue in cvxpy + alpha = self.preference / np.sum(self.preference) + + scales = torch.from_numpy(alpha).float().to(losses[-1].device) + pair_loss = scales.dot(losses) + if reset_optimizer: + self.optimizer.param_groups[0]["lr"]/=5 + # self.optimizer = torch.optim.Adam(self.params,lr=self.optimizer.param_groups[0]["lr"]/5) + self.optimizer.zero_grad() + pair_loss.backward() + self.optimizer.step() + + return pair_loss, moo_losses, mu_rl, alpha + + + +import numpy as np +import cvxpy as cp +import cvxopt + +class EPO(object): + r""" + The original EPO solver proposed in ICML2020 + https://proceedings.mlr.press/v119/mahapatra20a.html + """ + def __init__(self, m, r, eps=1e-4, coe=0, verbose=False): + # self.solver = cp.GLPK + self.solver = cp.GUROBI + # cvxopt.glpk.options["msg_lev"] = "GLP_MSG_OFF" + self.m = m + self.r = r/np.sum(r) + self.eps = eps + self.last_move = None + self.a = cp.Parameter(m) # Adjustments + self.C = cp.Parameter((m, m)) # C: Gradient inner products, G^T G + self.Ca = cp.Parameter(m) # d_bal^TG + self.rhs = cp.Parameter(m) # RHS of constraints for balancing + + self.alpha = cp.Variable(m) # Variable to optimize + self.last_alpha = np.zeros_like(r)-1 + self.coe = coe + + obj_bal = cp.Maximize(self.alpha @ self.Ca) # objective for balance + constraints_bal = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Simplex + self.C @ self.alpha >= self.rhs] + self.prob_bal = cp.Problem(obj_bal, constraints_bal) # LP balance + + obj_dom = cp.Maximize(cp.sum(self.alpha @ self.C)-self.coe*cp.sum_squares(self.alpha-self.last_alpha)) # obj for descent + constraints_dom = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Restrict + self.alpha @ self.Ca >= -cp.neg(cp.max(self.Ca)), + self.C @ self.alpha >= 0] + self.prob_dom = cp.Problem(obj_dom, constraints_dom) # LP dominance + + + self.gamma = 0 # Stores the latest Optimum value of the LP problem + self.mu_rl = 0 # Stores the latest non-uniformity + + self.verbose = verbose + + + def get_alpha(self, l, G, r=None, C=False, get_mu=False): + """calculate weights for all objectives given the gradient information + + Args: + l (ndarray): the values of objective losses + G (ndarray): inner products of the gradients of each objective loss w.r.t. params + r (ndarray, optional): adopt this preference if specified + C (bool, optional): True if the input gradients are inner products + get_mu (bool, optional): return detailed information if True. + + Returns: + alpha: the objective weights + mu_rl (optional): the optimal value to the LP + reset_optimizer (optional): whether to reset the inner optimizer + """ + r = self.r if r is None else r + assert len(l) == len(G) == len(r) == self.m, "length != m" + l_hat, rl, self.mu_rl, self.a.value = self.adjustments(l, r) + reset_optimizer = False + self.C.value = G if C else G @ G.T + self.Ca.value = self.C.value @ self.a.value + + if self.last_alpha.sum() is None: + self.last_alpha = np.array(r) + if self.mu_rl > self.eps: + J = self.Ca.value > 0 + J_star_idx = np.where(rl == np.max(rl))[0] + self.rhs.value = self.Ca.value.copy() + # it's equivalent to setting no constraints to objectives in J + # as maximize alpha^TCa would trivially satisfy the non-negativity + self.rhs.value[J] = -np.inf + self.rhs.value[J_star_idx] = 0 + + self.gamma = self.prob_bal.solve(solver=self.solver, verbose=False) + self.last_move = "bal" + + if self.verbose: + test_alpha = np.ones_like(self.a.value)/self.m + print(self.last_alpha,self.C.value,self.Ca.value,self.rhs.value) + print(self.gamma,test_alpha@self.Ca.value, self.alpha.value @ self.C.value) + print(self.gamma,self.coe*np.linalg.norm(self.alpha.value-self.last_alpha)**2) + + else: + self.gamma = self.prob_dom.solve(solver=self.solver, verbose=False) + self.last_move = "dom" + self.last_alpha = np.array(self.alpha.value) + + if get_mu: + return self.alpha.value, self.mu_rl, reset_optimizer + + return self.alpha.value + + + def mu(self, rl, normed=False): + if len(np.where(rl < 0)[0]): + raise ValueError(f"rl<0 \n rl={rl}") + return None + m = len(rl) + l_hat = rl if normed else rl / rl.sum() + eps = np.finfo(rl.dtype).eps + l_hat = l_hat[l_hat > eps] + return np.sum(l_hat * np.log(l_hat * m)) + + + def adjustments(self, l, r=1): + m = len(l) + rl = r * l + + l_hat = rl / rl.sum() + mu_rl = self.mu(l_hat, normed=True) + uniformity_div = np.log(l_hat * m) - mu_rl + div_r = np.array(r) + a = div_r * uniformity_div + + if self.verbose: + print(a, rl, div_r, uniformity_div, l_hat, a.dot(l)) + return l_hat, rl, mu_rl, a + + +class SEPO(object): + r""" + A smoothed variant of EPO, with two adjustments for unrobust OOD objectives: + a) normalization: unrobust OOD objective can yield large loss values that dominate the solutions of the LP, + hence we adopt the normalized OOD losses in the LP to resolve the issue + b) regularization: solutions yielded by the LP can change sharply among steps, especially when switching descending phases + hence we incorporate a L2 regularization in the LP to resolve the issue + """ + def __init__(self, m, r, eps=1e-4, coe=0, verbose=False): + # self.solver = cp.GLPK + self.solver = cp.GUROBI + # cvxopt.glpk.options["msg_lev"] = "GLP_MSG_OFF" + self.m = m + self.r = r/np.sum(r) + self.eps = eps + self.last_move = None + self.a = cp.Parameter(m) # Adjustments + self.C = cp.Parameter((m, m)) # C: Gradient inner products, G^T G + self.Ca = cp.Parameter(m) # d_bal^TG + self.rhs = cp.Parameter(m) # RHS of constraints for balancing + + self.alpha = cp.Variable(m) # Variable to optimize + self.last_alpha = np.zeros_like(r)-1 + self.coe = coe + + obj_bal = cp.Maximize(self.alpha @ self.Ca-self.coe*cp.sum_squares(self.alpha-self.last_alpha)) # objective for balance + obj_bal_orig = cp.Maximize(self.alpha @ self.Ca) # objective for balance + constraints_bal = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Simplex + self.C @ self.alpha >= self.rhs] + self.prob_bal = cp.Problem(obj_bal, constraints_bal) # LP balance + self.prob_bal_orig = cp.Problem(obj_bal_orig, constraints_bal) # LP balance + + obj_dom = cp.Maximize(cp.sum(self.alpha @ self.C)-self.coe*cp.sum_squares(self.alpha-self.last_alpha)) # obj for descent + constraints_res = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Restrict + self.alpha @ self.Ca >= -cp.neg(cp.max(self.Ca)), + self.C @ self.alpha >= 0] + constraints_rel = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Relaxed + self.C @ self.alpha >= 0] + self.prob_dom = cp.Problem(obj_dom, constraints_res) # LP dominance + self.prob_rel = cp.Problem(obj_dom, constraints_rel) # LP dominance + + self.gamma = 0 # Stores the latest Optimum value of the LP problem + self.mu_rl = 0 # Stores the latest non-uniformity + + self.verbose = verbose + + + def get_alpha(self, l, G, r=None, C=False, get_mu=False): + """calculate weights for all objectives given the gradient information + + Args: + l (ndarray): the values of objective losses + G (ndarray): inner products of the gradients of each objective loss w.r.t. params + r (ndarray, optional): adopt this preference if specified + C (bool, optional): True if the input gradients are inner products + get_mu (bool, optional): return detailed information if True. + + Returns: + alpha: the objective weights + mu_rl (optional): the optimal value to the LP + reset_optimizer (optional): whether to reset the inner optimizer + """ + r = self.r if r is None else r + assert len(l) == len(G) == len(r) == self.m, "length != m" + l_hat, rl, self.mu_rl, self.a.value = self.adjustments(l, r) + reset_optimizer = False + if self.mu_rl <= 0.1: + self.r[0]=max(1e-15,self.r[0]/10000) + self.r = self.r/self.r.sum() + print(f"pua preference {self.r}") + l_hat, rl, self.mu_rl, self.a.value = self.adjustments(l, r) + + + a_norm = np.linalg.norm(self.a.value) + G_norm = np.linalg.norm(G,axis=1) + Ga = G.T @ self.a.value + self.C.value = G if C else G/np.expand_dims(G_norm,axis=1) @ G.T/a_norm + self.Ca.value = G/np.expand_dims(G_norm,axis=1) @ Ga.T/a_norm + + if self.last_alpha.sum() is None: + self.last_alpha = np.array(r) + if self.mu_rl > self.eps: + J = self.Ca.value > 0 + + J_star_idx = np.where(rl == np.max(rl))[0] + self.rhs.value = self.Ca.value.copy() + # it's equivalent to setting no constraints to objectives in J + # as maximize alpha^TCa would trivially satisfy the non-negativity + self.rhs.value[J] = -np.inf # Not efficient; but works. + self.rhs.value[J_star_idx] = max(0,self.Ca.value[J_star_idx]/2) + + if self.last_alpha.sum()<0: + self.gamma = self.prob_bal_orig.solve(solver=self.solver, verbose=False) + else: + self.gamma = self.prob_bal.solve(solver=self.solver, verbose=False) + + self.last_move = "bal" + + if self.verbose: + test_alpha = np.ones_like(self.a.value)/self.m + print(self.last_alpha,self.C.value,self.Ca.value,self.rhs.value) + print(self.gamma,test_alpha@self.Ca.value, self.alpha.value @ self.C.value) + print(self.gamma,self.coe*np.linalg.norm(self.alpha.value-self.last_alpha)**2) + else: + self.gamma = self.prob_dom.solve(solver=self.solver, verbose=False) + self.last_move = "dom" + self.last_alpha = np.array(self.alpha.value) + + if get_mu: + return self.alpha.value, self.mu_rl, reset_optimizer + + return self.alpha.value + + + def mu(self, rl, normed=False): + if len(np.where(rl < 0)[0]): + raise ValueError(f"rl<0 \n rl={rl}") + return None + m = len(rl) + l_hat = rl if normed else rl / rl.sum() + eps = np.finfo(rl.dtype).eps + l_hat = l_hat[l_hat > eps] + return np.sum(l_hat * np.log(l_hat * m)) + + + def adjustments(self, l, r=1): + m = len(l) + rl = r * l + + l_hat = rl / rl.sum() + mu_rl = self.mu(l_hat, normed=True) + uniformity_div = np.log(l_hat * m) - mu_rl + div_r = np.array(r) + a = div_r * uniformity_div + + if self.verbose: + print(a, rl, div_r, uniformity_div, l_hat, a.dot(l)) + return l_hat, rl, mu_rl, a + + +def getNumParams(params): + numParams, numTrainable = 0, 0 + for param in params: + npParamCount = np.prod(param.data.shape) + numParams += npParamCount + if param.requires_grad: + numTrainable += npParamCount + return numParams, numTrainable + +def get_kl_div(losses, preference): + pair_score = losses.dot(preference) + return pair_score + +def pair_selection(losses,val_accs,test_accs,anneal_iter=0,val_acc_bar=-1,pood=None): + + losses = losses[anneal_iter:] + val_accs = val_accs[anneal_iter:] + test_accs = test_accs[anneal_iter:] + if val_acc_bar < 0: + val_acc_bar = (np.max(val_accs)-np.min(val_accs))*0.05+np.min(val_accs) + + try: + preference_base = 10**max(-12,int(np.log10(np.mean(losses[:,-1]))-2)) + except Exception as e: + print(e) + preference_base = 1e-12 + if len(losses[0])==2: + preference = np.array([preference_base,1]) + elif len(losses[0])==4: + preference = np.array([1e-12,1e-4,1e-2,1]) + elif len(losses[0])==5: + preference = np.array([1e-12,1e-6,1e-4,1e-2,1]) + else: + preference = np.array([1e-12,1e-2,1]) + + if pood is not None: + preference = pood + print(f"Use preference: {preference}, validation acc bar: {val_acc_bar}") + + pair_score = np.array([get_kl_div(l,preference) if a>=val_acc_bar else 1e9 for (a,l) in zip(val_accs,losses)]) + sel_idx = np.argmin(pair_score) + return sel_idx+anneal_iter, val_accs[sel_idx], test_accs[sel_idx] + +def get_grad_sim(params,losses,preference=None,is_G=False,cosine=True): + num_ood_losses = len(losses)-1 + if is_G: + G = params + else: + pesudo_opt = SGD(params,lr=1e-6) + grads = [] + for cur_loss in losses: + pesudo_opt.zero_grad() + cur_loss.backward(retain_graph=True) + cur_grad = [] + for param in params: + if param.grad is not None: + cur_grad.append(Variable(param.grad.data.clone().flatten(), requires_grad=False)) + # print(torch.cat(cur_grad).sum()) + grads.append(torch.cat(cur_grad)) + G = torch.stack(grads) + if cosine: + G = F.normalize(G,dim=1) + GG = (G @ G.T).cpu() + if preference is not None: + G_weights = preference[1:]/np.sum(preference[1:]) + else: + G_weights = np.ones(num_ood_losses)/num_ood_losses + grad_sim =G_weights.dot(GG[0,1:]) + return grad_sim.item() diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f0f5f2 --- /dev/null +++ b/README.md @@ -0,0 +1,140 @@ +

PAIR: Pareto Invariant Risk Minimization

+

+ Paper + Github + + License + License + + + +

+ +This repo contains the sample code for reproducing the results of our ICLR 2023: *[Pareto Invariant Risk Minimization](https://arxiv.org/abs/2206.07766)*, which has also been presented at [ICML PODS](https://sites.google.com/view/scis-workshop/home) Workshop. 😆😆😆 + +TODO items: +- [] Camera ready version of the paper. + +- [] Full instructions to reproduce results. + +## Introduction +Recently, there has been a growing surge of interest in enabling machine learning systems to generalize well to Out-of-Distribution (OOD) data. Most efforts are devoted to advancing *optimization objectives* that regularize Empirical Risk Minimization (ERM) to capture the underlying invariance; however, little attention is paid to the *optimization process* of the objectives. +In fact, the optimization process of the OOD objectives turns out to be substantially more challenging than ERM. +When optimizing the ERM and OOD objectives, +$$\min_f (\mathcal{L}_\text{ERM},\mathcal{L}_\text{OOD})^T$$ +there often exists an **optimization dilemma** in the training of the OOD objectives: + + + +1. The original OOD objectives are often hard to be optimized directly (e.g., IRM), hence they are **relaxed as regularization terms** of ERM (e.g., IRMv1), i.e., $\min_f \mathcal{L}_\text{ERM}+\lambda \widehat{\mathcal{L}}_\text{OOD}$, which can behave very differently and introduce huge gaps with the original one. +As shown in figure *(a)*, the ellipsoids denote solutions that satisfy the invariance constraints of practical IRM variant IRMv1. When optimized with ERM, IRMv1 prefers $f_1$ instead of $f_\text{IRM}$(The predictor produced by IRM). + +2. The **intrinsic conflicts** between ERM and OOD objectives brings conflicts in gradients that further increases the optimization difficulty, as shown in figure *(b)*. Consequently, it often require careful tuning of the penalty weights (the $\lambda$). Figure (d) shows an example that IRMv1 usually requires exhaustive tuning of hyperparameters ($y$-axis: penalty weights; $x$-axis: ERM pre-training epochs before applying IRMv1 penalty), +Especially, the Multi-Objective Optimization (MOO) theory the typically used linear weighting scheme, i.e., $\min_f \mathcal{L}_\text{ERM}+\lambda \widehat{\mathcal{L}}_\text{OOD}$ cannot reach any solutions in the non-convex part of the Pareto front, as shown in figure *(c)*, and lead to suboptimal OOD generalization performance. + +3. Along with the optimization dilemma is another challenge, i.e., **model selection** during the training with the OOD objectives. As we lack the access to a validation set that have a similar distribution with the test data, DomainBed provides 3 options to choose and construct a validation set from: training domain data; leave-one-out validation data; test domain data. However, all three validation set construction approaches have their own limitations, as they essentially posit different ** assumptions on the test distribution**. + +This work provides understandings and solutions to the aforementioned challenges from the MOO perspective, which leads to a new optimization scheme for OOD generalization, called PAreto Invariant Risk Minimization (`PAIR`), including an optimizer `PAIR-o` and a new model selection criteria `PAIR-s`. + +1. Owing to the MOO formulation, `PAIR-o` allows for **cooperative optimization** with other OOD objectives to improve the robustness of practical OOD objectives. Despite the huge gaps between IRMv1 and IRM, we show that incorporating VREx into IRMv1 (i.e., `IRMX` objective) provably recovers the causal invariance for some group of problem instances. + +2. When given robust OOD objectives, `PAIR-o` finds a descent path with **adaptive penalty weights**, which leads to a Pareto optimal solution that trades off ERM and OOD performance properly, as shown in figure *(c)*. Therefore, `PAIR-o` robustly yields top performances and relieves the needs of exhaustive hyperparameter tunning, as shown in figure *(d)*. + +3. `PAIR-s` addresses the challenge of finding a proper validation set for model selection in OOD generalization, by leveraging **the prior assumed by the OOD objective**. Essentially, different lines of OOD algorithms adopt different priors and assumptions on the causes of the distribution shifts. The main purpose of the OOD evaluation is to validate the correctness of the posed assumptions. To this end, the selected models should properly reflect the preferences implied by the assumptions, i.e., the OOD loss values. When considering the loss values during the model selection, it is natural to leverage the MOO perspective and explicitly consider the trade-offs between ERM and OOD performance. + +We conducted extensive experiments on challenging OOD benchmarks. Empirical results show that `PAIR-o` successfully alleviates the objective conflicts and empowers IRMv1 to achieve high performance in $6$ datasets from WILDS. `PAIR-s` effectively improves the performance of selected OOD models up to $10\%$ across $3$ datasets from DomainBed. + +## Structure of Codebase + +The whole code base contain four parts, corresponding to experiments presented in the paper: +- `Extrapolation`: Recovery of Causal Invariance +- `Extrapolation`: Proof of Concept on ColoredMNIST +- `WILDS`: Verification of PAIR-o in WILDS +- `DomainBed`: Verification of PAIR-s in DomainBed + +## Recovery of Causal Invariance +We provide a minimal demo code for the experiments on the recovery of causal invariance, in [pair_extrapolation.ipynb](./pair_extrapolation.ipynb). + + +## ColoredMNIST +The corresponding code is in the folder [ColoredMNIST](./ColoredMNIST). +The code is modified from [RFC](https://github.com/TjuJianyu/RFC/). +To reproduce results of PAIR, simply run the following commands under the directory: + +For the original ColoredMNIST data (CMNIST-25): +``` +python run_exp.py --methods pair --verbose True --penalty_anneal_iters 150 --dataset coloredmnist025 --n_restarts 10 --lr 0.1 --opt 'test' +``` + +For the modified ColoredMNIST data (CMNIST-01): +``` +python run_exp.py --methods pair --verbose True --penalty_anneal_iters 150 --dataset coloredmnist01 --n_restarts 10 --lr 0.01 --opt 'test' +``` + +## WILDS +The corresponding code is in the folder [WILDS](./WILDS). +The code is modified from [Fish](https://github.com/YugeTen/fish). +The dependencies and running commands are the same as for [Fish](https://github.com/YugeTen/fish). +For example, +``` +python main.py --need_pretrain --data-dir ./data --dataset civil --algorithm pair -pc 3 --seed 0 -ac 1e-4 +``` +We add additional commands to control `PAIR-p`: +- `-pc`: specify preferences; +- `--use_old`: to avoid repeated pretraining of ERM and directly use the pretrained weights; + +To avoid negative loss inputs, we use the following commands to adjust IRMv1 loss values: +- `-al` and `-ac`: adjust negative irm penalties in pair by multiplying a negative number; +- `-ai`: adjust negative irm penalties in pair by adding up a sufficient large number; + +We also provide a accelerated mode by freezing the featurizer by specifying `--frozen`. + +Note that we use `wilds 2.0` following the latest official recommendations. + + + +## DomainBed +The corresponding code is in the folder [DomainBed](./DomainBed). +The code is based on [DomainBed](https://github.com/facebookresearch/DomainBed). + +We provide new [PAIR model selection criteria](./DomainBed/model_selection.py). +Based on three options of validation set choice, we implement corresponding `PAIR-s` variants. + +- `PAIRIIDAccuracySelectionMethod`: `PAIR-s` based on a random subset from the data of the training domains. +- `PAIRLeaveOneOutSelectionMethod`: `PAIR-s` based on a random subset from the data of a held-out (not training, not testing) domain. +- `PAIROracleSelectionMethod`: `PAIR-s` based on a random subset from the data of the test domain. + +To use `PAIR-s`, simply add the corresponding functions or replace the original `model_selection.py` with ours, +and then run the corresponding commands in DomainBed. + + +## Misc + +If you find our paper and repo useful, please cite our paper: + +```bibtex +@inproceedings{pair, +title={Pareto Invariant Risk Minimization}, +author={Yongqiang Chen and Kaiwen Zhou and Yatao Bian and Binghui Xie and Bingzhe Wu and Yonggang Zhang and Kaili Ma and Han Yang and Peilin Zhao and Bo Han and James Cheng}, +booktitle={International Conference on Learning Representations}, +year={2023}, +url={https://openreview.net/forum?id=esFxSb_0pSL} +} +``` diff --git a/WILDS/README.md b/WILDS/README.md new file mode 100644 index 0000000..121c098 --- /dev/null +++ b/WILDS/README.md @@ -0,0 +1,3 @@ +# PAIR for WILDS +The dependencies and running commands are the same as for [Fish](https://github.com/YugeTen/fish). +The only difference is that we use `wilds 2.0` following the latest official recommendations. diff --git a/WILDS/src/config.py b/WILDS/src/config.py new file mode 100644 index 0000000..ac58366 --- /dev/null +++ b/WILDS/src/config.py @@ -0,0 +1,106 @@ + +dataset_defaults = { + 'fmow': { + 'epochs': 12, + 'batch_size': 32, + 'optimiser': 'Adam', + 'optimiser_args': { + 'lr': 1e-4, + + 'amsgrad': True, + + }, + 'pretrain_iters': 24000, + 'meta_lr': 0.01, + 'meta_steps': 5, + 'selection_metric': 'acc_worst_region', + 'reload_inner_optim': True, + 'eval_iters': 500 + }, + 'camelyon': { + 'epochs': 20, + 'batch_size': 32, + 'optimiser': 'SGD', + 'optimiser_args': { + 'momentum': 0.9, + 'lr': 1e-4, + + }, + 'pretrain_iters': 10000, + 'meta_lr': 0.01, + 'meta_steps': 3, + 'selection_metric': 'acc_avg', + 'reload_inner_optim': True, + 'eval_iters': -1 + }, + 'poverty': { + 'epochs': 200, + 'batch_size': 64, + 'optimiser': 'Adam', + 'optimiser_args': { + 'lr': 1e-3, + + 'amsgrad': True, + + }, + 'pretrain_iters': 5000, + 'meta_lr': 0.1, + 'meta_steps': 5, + 'selection_metric': 'r_wg', + 'reload_inner_optim': True, + 'eval_iters': -1, + 'scheduler': 'StepLR', + 'scheduler_kwargs': {'gamma': 0.96,'step_size': 1,}, + }, + 'iwildcam': { + 'epochs': 9, + 'batch_size': 16, + 'optimiser': 'Adam', + 'optimiser_args': { + 'lr': 1e-4, + 'weight_decay': 0.0, + 'amsgrad': True, + + }, + 'pretrain_iters': 24000, + 'meta_lr': 0.01, + 'meta_steps': 10, + 'selection_metric': 'F1-macro_all', + 'reload_inner_optim': True, + 'eval_iters': 1000 + }, + 'civil': { + 'epochs': 5, + 'batch_size': 16, + 'optimiser': 'Adam', + 'optimiser_args': { + 'lr': 1e-5, + 'amsgrad': True, + }, + 'pretrain_iters': 20000, + 'meta_lr': 0.05, + 'meta_steps': 5, + 'selection_metric': 'acc_wg', + 'reload_inner_optim': True, + 'eval_iters': 500 + }, + 'rxrx': { + 'epochs': 90, + 'batch_size': 72, + 'optimiser': 'Adam', + 'optimiser_args': { + 'lr': 1e-3, + 'weight_decay': 1e-5, + 'amsgrad': True, + 'betas': (0.9, 0.999), + }, + 'pretrain_iters': 15000, + 'meta_lr': 0.01, + 'meta_steps': 10, + 'selection_metric': 'acc_avg', + 'reload_inner_optim': True, + 'eval_iters': 2000, + 'scheduler': 'cosine_schedule_with_warmup', + 'scheduler_kwargs': {'num_warmup_steps': 5415}, + }, +} diff --git a/WILDS/src/main.py b/WILDS/src/main.py new file mode 100644 index 0000000..6d1c8c2 --- /dev/null +++ b/WILDS/src/main.py @@ -0,0 +1,896 @@ +import copy +import argparse +import datetime +import json +import os +from statistics import mode +import sys +import csv +from tokenize import group +import tqdm +from collections import defaultdict +from tempfile import mkdtemp + +import numpy as np +import torch +import torch.optim as optim +from scheduler import initialize_scheduler + +import models +from config import dataset_defaults +from utils import get_preference, set_seed, unpack_data, sample_domains, save_best_model, \ + Logger, return_predict_fn, return_criterion, fish_step + +# This is secret and shouldn't be checked into version control +os.environ["WANDB_API_KEY"]=None +# Name and notes optional +# WANDB_NAME="My first run" +# WANDB_NOTES="Smaller learning rate, more regularization." +import wandb +import traceback +from pair import PAIR +from torch.autograd import Variable + + +runId = datetime.datetime.now().isoformat().replace(':', '_') + +parser = argparse.ArgumentParser(description='Pareto Invariant Risk Minimization') +# General +parser.add_argument('--dataset', type=str, + help="Name of dataset, choose from amazon, camelyon, " + "rxrx, civil, fmow, iwildcam, poverty") +parser.add_argument('--algorithm', type=str, + help='training scheme, choose between fish or erm.') +parser.add_argument('--experiment', type=str, default='.', + help='experiment name, set as . for automatic naming.') +parser.add_argument('--data_dir', type=str, + help='path to data dir') +parser.add_argument('--exp_dir', type=str, default="", + help='path to save results of experiments') +parser.add_argument('--stratified', action='store_true', default=False, + help='whether to use stratified sampling for classes') + +parser.add_argument('--sample_domains', type=int, default=-1) +parser.add_argument('--epochs', type=int, default=-1) +parser.add_argument('--batch_size', type=int, default=-1) +parser.add_argument('--print_iters', type=int, default=1) +parser.add_argument('--eval_iters', type=int, default=-1) +parser.add_argument('--lr', type=float, default=-1) +parser.add_argument('--momentum', type=float, default=-1) +parser.add_argument('--penalty_weight','-p', type=float, default=1) +parser.add_argument('--penalty_weight2','-p2', type=float, default=-1) # if there is another penalty weight to be tuned +parser.add_argument('--eps', type=float, default=1e-4) # if there is another penalty weight to be tuned +parser.add_argument('--preference_choice','-pc',type=int,default=0) +parser.add_argument('--num_workers','-nw',type=int,default=4) +parser.add_argument('--frozen', action='store_true', default=False) # whether to frozen the featurizer +parser.add_argument('--adjust_irm', '-ai',action='store_true', default=False) # whether to adjust some negative irm penalties in pair by adding up a positive number +parser.add_argument('--adjust_loss', '-al',action='store_true', default=False) # whether to adjust some negative irm penalties in pair by multiplying a negative number + +parser.add_argument('--need_pretrain', action='store_true') +parser.add_argument('--adjust_lr', '-alr',action='store_true', default=False) # whether to adjust lr as scheduled after pretraining +parser.add_argument('--pretrain_iters', type=int,default=-1) +parser.add_argument('--use_old', action='store_true') +parser.add_argument('--no_plot', action='store_true') +parser.add_argument('--no_test', action='store_true') +parser.add_argument('--no_sch', action='store_true') # not to use any scheduler +parser.add_argument('--opt', type=str, default='') +parser.add_argument('--exp_name', type=str, default='') +parser.add_argument('--scheduler', type=str, default='') +# Computation +parser.add_argument('--no-cuda', action='store_true', default=False, + help='disables CUDA use') +parser.add_argument('--seed', type=int, default=-1, + help='random seed, set as -1 for random.') +parser.add_argument('--no_wandb', action='store_true', default=False) # whether not to use wandb +parser.add_argument('--no_drop_last', action='store_true', default=True) # whether not to drop last batch + + +args = parser.parse_args() +args.cuda = not args.no_cuda and torch.cuda.is_available() +device = torch.device("cuda" if args.cuda else "cpu") +# overwrite some arguments +batch_size = args.batch_size +print_iters = args.print_iters +pretrain_iters = args.pretrain_iters +epochs = args.epochs +optimiser = args.opt +args_dict = args.__dict__ +args_dict.update(dataset_defaults[args.dataset]) +args = argparse.Namespace(**args_dict) + +if len(args.exp_dir) == 0: + args.exp_dir = args.data_dir +os.environ["WANDB_DIR"] = args.exp_dir + +# experiment directory setup +args.experiment = f"{args.dataset}_{args.algorithm}" \ + if args.experiment == '.' else args.experiment +directory_name = os.path.join(args.exp_dir,'experiments/{}'.format(args.experiment)) +if not os.path.exists(directory_name): + os.makedirs(directory_name) +runPath = mkdtemp(prefix=runId, dir=directory_name) + + +if batch_size>0: + args.batch_size=batch_size +if print_iters>0: + args.print_iters = print_iters +if pretrain_iters>0: + args.pretrain_iters = pretrain_iters +if len(optimiser)>0: + args.optimiser = optimiser + + + +exp_name = f"{args.experiment}" +if len(args.exp_name)>0: + args.exp_name ="_"+args.exp_name + +if args.algorithm.lower() in ['pair']: + exp_name += f"_pc{args.preference_choice}" +else: + exp_name += f"_p{args.penalty_weight}" + +if args.sample_domains>0: + exp_name += f"_meta{args.sample_domains}" + args.meta_steps = args.sample_domains +if args.frozen: + exp_name += "_frozen" +if epochs>0: + exp_name += f"_ep{epochs}" + args.epochs = epochs +exp_name += f"_{args.optimiser}_lr{args.lr}{args.exp_name}_seed{args.seed}" + +os.environ["WANDB_NAME"]=exp_name.replace("_","/") +group_name = "/".join(exp_name.split("_")[:-1]) # seed don't participate in the grouping + +if args.dataset.lower() in ["poverty"]: + dataset_name_wandb = args.dataset+"_avg" +else: + dataset_name_wandb = args.dataset +if not args.no_wandb: + # raise Exception("Please specify your own parameters if you wish to use wandb") + wandb_run = wandb.init(project=dataset_name_wandb, entity="entity_name",group=group_name,id=wandb.util.generate_id()) + wandb.config = args + + +# Choosing and saving a random seed for reproducibility +if args.seed == -1: + args.seed = int(torch.randint(0, 2 ** 32 - 1, (1,)).item()) +set_seed(args.seed) + +# logging setup +sys.stdout = Logger('{}/run.log'.format(runPath)) +print('RunID:' + runPath) +with open('{}/args.json'.format(runPath), 'w') as fp: + json.dump(args.__dict__, fp) +torch.save(args, '{}/args.rar'.format(runPath)) + +# load model +modelC = getattr(models, args.dataset) +train_loader, tv_loaders,dataset = modelC.getDataLoaders(args, device=device) +val_loader, test_loader = tv_loaders['val'], tv_loaders['test'] +model = modelC(args, weights=None).to(device) + +# assert args.optimiser in ['SGD', 'Adam'], "Invalid choice of optimiser, choose between 'Adam' and 'SGD'" +if args.optimiser.lower() in ['sgd','adam']: + opt = getattr(optim, args.optimiser) +else: + raise Exception("Invalid choice of optimiser") +if args.lr>0: + args.optimiser_args['lr'] = args.lr +# pop up unnecessary configs +if args.optimiser.lower() not in ['adam'] and 'amsgrad' in args.optimiser_args.keys(): + args.optimiser_args.pop('amsgrad') +if args.momentum > 0: + args.optimiser_args['momentum'] = args.momentum + +if args.dataset.lower() in ["poverty"]: + classifier = model.enc.fc +elif args.dataset.lower() in ["iwildcam","rxrx"]: + classifier = model.fc +else: + classifier = model.classifier +trainable_params = classifier.parameters() if args.frozen else model.parameters() +optimiserC = opt(trainable_params, **args.optimiser_args) +predict_fn, criterion = return_predict_fn(args.dataset), return_criterion(args.dataset) + + + +if args.algorithm not in ['erm'] and not args.adjust_lr: + n_train_steps = train_loader.dataset.training_steps*args.epochs +else: + n_train_steps = len(train_loader) * args.epochs +n_train_steps += (args.need_pretrain and args.pretrain_iters>0 and not args.use_old)*args.pretrain_iters + +if args.no_sch: + args.scheduler = None + +if args.scheduler is not None and len(args.scheduler)>0: + scheduler = initialize_scheduler(args, optimiserC, n_train_steps) +else: + scheduler = None + +if args.adjust_lr: + print("Adjusting learning rate as scheduled after pretraining...") + n_iters = 0 + pretrain_iters = args.pretrain_iters + pretrain_epochs = int(np.ceil(pretrain_iters/len(train_loader))) + pbar = tqdm.tqdm(total = pretrain_iters) + for epoch in range(pretrain_epochs): + for i in range(len(train_loader)): + if scheduler is not None and scheduler.step_every_batch: + scheduler.step() + # display progress + pbar.set_description(f"Pretrain {n_iters}/{pretrain_iters} iters") + pbar.update(1) + if scheduler is not None and not scheduler.step_every_batch: + scheduler.step() +elif args.need_pretrain and args.pretrain_iters>0 and args.use_old: + if args.scheduler is not None and len(args.scheduler)>0: + try: + if 'num_warmup_steps' in args.scheduler_kwargs.keys(): + args.scheduler_kwargs['num_warmup_steps'] = 0 + except Exception as e: + print(e) + scheduler = initialize_scheduler(args, optimiserC, n_train_steps) + else: + scheduler = None +print(optimiserC,scheduler) + +def pretrain(train_loader, pretrain_iters, save_path=None): + aggP = defaultdict(list) + aggP['val_stat'] = [0.] + + n_iters = 0 + pretrain_epochs = int(np.ceil(pretrain_iters/len(train_loader))) + pbar = tqdm.tqdm(total = pretrain_iters) + for epoch in range(pretrain_epochs): + for i, data in enumerate(train_loader): + model.train() + # get the inputs + x, y = unpack_data(data, device) + optimiserC.zero_grad() + y_hat = model(x,frozen_mode=args.frozen) + loss = criterion(y_hat, y) + loss.backward() + optimiserC.step() + if scheduler is not None and scheduler.step_every_batch: + scheduler.step() + n_iters += 1 + # display progress + pbar.set_description(f"Pretrain {n_iters}/{pretrain_iters} iters") + pbar.update(1) + if (i + 1) % args.eval_iters == 0 and args.eval_iters != -1: + test(val_loader, aggP, loader_type='val', verbose=False) + test(test_loader, aggP, loader_type='test', verbose=False) + if save_path is None: + save_path = runPath + save_best_model(model, save_path, aggP, args) + + if n_iters == pretrain_iters: + print("Pretrain is done!") + test(val_loader, aggP, loader_type='val', verbose=False) + test(test_loader, aggP, loader_type='test', verbose=False) + if save_path is None: + save_path = runPath + # save the model at last pretrain epoch no matter whatever + save_best_model(model, save_path, aggP, args, pretrain=True) + break + if scheduler is not None and not scheduler.step_every_batch: + scheduler.step() + pbar.close() + + model.load_state_dict(torch.load(save_path + '/model.rar')) + print('Finished ERM pre-training!') + +def train_erm(train_loader, epoch, agg): + running_loss = 0 + total_iters = len(train_loader) + print('\n====> Epoch: {:03d} '.format(epoch)) + for i, data in enumerate(train_loader): + model.train() + # get the inputs + x, y = unpack_data(data, device) + optimiserC.zero_grad() + y_hat = model(x,frozen_mode=args.frozen) + loss = criterion(y_hat, y) + loss.backward() + optimiserC.step() + if scheduler is not None and scheduler.step_every_batch: + scheduler.step() + running_loss += loss.item() + # print statistics + if (i + 1) % args.print_iters == 0 and args.print_iters != -1 and args.algorithm != 'fish': + if not args.no_wandb: + wandb.log({ "loss": loss.item()}) + agg['train_loss'].append(running_loss / args.print_iters) + agg['losses'].append([running_loss / args.print_iters]) + agg['train_iters'].append(i+1+epoch*total_iters) + print('iteration {:05d}/{:d}: loss: {:6.3f}'.format(i + 1, total_iters, running_loss / args.print_iters)) + if i % args.eval_iters == 0 and args.eval_iters != -1: + test(val_loader, agg, loader_type='val') + test(test_loader, agg, loader_type='test') + if not args.no_wandb: + wandb.log({"val_acc":agg['val_stat'][-1]}) + wandb.log({"test_acc":agg['test_stat'][-1]}) + running_loss=0 + model.train() + save_best_model(model, runPath, agg, args) + + + +from wilds.common.utils import split_into_groups +import torch.autograd as autograd +import torch.nn.functional as F +scale = torch.tensor(1.).to(device).requires_grad_() +def irm_penalty(losses, pos=-1, adjust=False): + grad_1 = autograd.grad(losses[0::2].mean(), [scale], create_graph=True)[0] + grad_2 = autograd.grad(losses[1::2].mean(), [scale], create_graph=True)[0] + result = torch.sum(grad_1 * grad_2) + if pos>0 and not adjust: + # grad = autograd.grad(losses.mean(), [scale], create_graph=True)[0] + # result = torch.sum(grad.pow(2)) + result += pos + if result<0 and adjust: + grad = autograd.grad(losses.mean(), [scale], create_graph=True)[0] + result = torch.sum(grad.pow(2)) + return result + +def train_irmx(train_loader, epoch, agg): + model.train() + train_loader.dataset.reset_batch() + i = 0 + print('\n====> Epoch: {:03d} '.format(epoch)) + running_loss = 0 + total_iters = len(train_loader) + running_losses = [] + while sum([l > 1 for l in train_loader.dataset.batches_left.values()]) >= args.meta_steps: + model.train() + i += 1 + # sample `meta_steps` number of domains to use for the inner loop + domains = sample_domains(train_loader, args.meta_steps, args.stratified).tolist() + # print(domains) + avg_loss = 0. + penalty = 0. + # overall_losses = F.cross_entropy(scale * results['y_pred'],results['y_true'],reduction="none") + losses_bygroup = [] + + # inner loop update + for domain in domains: + data = train_loader.dataset.get_batch(domain) + x, y = unpack_data(data, device) + y_hat = model(x,frozen_mode=args.frozen) + # loss = criterion(y_hat, y) + if 'poverty'in args.dataset.lower(): + loss = F.mse_loss(scale*y_hat,y,reduction="none") + else: + loss = F.cross_entropy(scale * y_hat,y,reduction="none") + losses_bygroup.append(loss.mean()) + penalty += irm_penalty(loss) + avg_loss += loss.mean() + avg_loss /= args.meta_steps + penalty /= args.meta_steps + # losses = losses_bygroup+[ penalty, torch.stack(losses_bygroup).var()] + losses = [avg_loss, penalty, torch.stack(losses_bygroup).var()] + # agg['losses'].append([l.item() for l in losses]) + if len(running_losses)==0: + running_losses = [0]*len(losses) + for (j,loss) in enumerate(running_losses): + running_losses[j]+=losses[j].item() + # print([l.item() for l in losses],sol) + optimiserC.zero_grad() + # loss = scales.dot(torch.stack(losses)) + if args.penalty_weight2 > 0: + loss = avg_loss+args.penalty_weight*penalty+args.penalty_weight2*torch.stack(losses_bygroup).var() + else: + loss = avg_loss+args.penalty_weight*(penalty+torch.stack(losses_bygroup).var()) + # print(loss) + loss.backward() + optimiserC.step() + if scheduler is not None and scheduler.step_every_batch: + scheduler.step() + running_loss += loss.item() + + # log the number of batches left for each domain + for domain in domains: + train_loader.dataset.batches_left[domain] = \ + train_loader.dataset.batches_left[domain] - 1 \ + if train_loader.dataset.batches_left[domain] > 1 else 1 + + if i % args.print_iters == 0 and args.print_iters != -1: + print(avg_loss,penalty) + agg['losses'].append([l / args.print_iters for l in running_losses]) + if not args.no_wandb: + wandb.log({ "loss": loss.item(), + "erm_loss": agg['losses'][-1][0], + "irm_loss": agg['losses'][-1][1], + "vrex_loss": agg['losses'][-1][2], + }) + running_losses = [0]*len(losses) + # agg['losses'].append([l.item() for l in losses]) + agg['train_loss'].append(running_loss / args.print_iters) + agg['train_iters'].append(i+1+epoch*total_iters) + print('iteration {:05d}/{:d}: loss: {:6.3f}'.format(i + 1, total_iters, running_loss / args.print_iters)) + if i % args.eval_iters == 0 and args.eval_iters != -1: + test(val_loader, agg, loader_type='val') + test(test_loader, agg, loader_type='test') + if not args.no_wandb: + wandb.log({"val_acc":agg['val_stat'][-1]}) + wandb.log({"test_acc":agg['test_stat'][-1]}) + model.train() + save_best_model(model, runPath, agg, args) + +def train_irm(train_loader, epoch, agg): + model.train() + train_loader.dataset.reset_batch() + i = 0 + print('\n====> Epoch: {:03d} '.format(epoch)) + running_loss = 0 + total_iters = len(train_loader) + running_losses = [] + while sum([l > 1 for l in train_loader.dataset.batches_left.values()]) >= args.meta_steps: + model.train() + i += 1 + # sample `meta_steps` number of domains to use for the inner loop + domains = sample_domains(train_loader, args.meta_steps, args.stratified).tolist() + # print(domains) + avg_loss = 0. + penalty = 0. + # overall_losses = F.cross_entropy(scale * results['y_pred'],results['y_true'],reduction="none") + losses_bygroup = [] + + # inner loop update + for domain in domains: + data = train_loader.dataset.get_batch(domain) + x, y = unpack_data(data, device) + y_hat = model(x,frozen_mode=args.frozen) + # loss = criterion(y_hat, y) + if 'poverty'in args.dataset.lower(): + loss = F.mse_loss(scale*y_hat,y,reduction="none") + else: + loss = F.cross_entropy(scale * y_hat,y,reduction="none") + losses_bygroup.append(loss.mean()) + penalty += irm_penalty(loss) + avg_loss += loss.mean() + avg_loss /= args.meta_steps + penalty /= args.meta_steps + # losses = losses_bygroup+[ penalty, torch.stack(losses_bygroup).var()] + losses = [avg_loss, penalty, torch.stack(losses_bygroup).var()] + # agg['losses'].append([l.item() for l in losses]) + if len(running_losses)==0: + running_losses = [0]*len(losses) + for (j,loss) in enumerate(running_losses): + running_losses[j]+=losses[j].item() + # print([l.item() for l in losses],sol) + optimiserC.zero_grad() + + # loss = scales.dot(torch.stack(losses)) + loss = avg_loss+args.penalty_weight*penalty + # print(loss) + loss.backward() + optimiserC.step() + running_loss += loss.item() + if scheduler is not None and scheduler.step_every_batch: + scheduler.step() + # log the number of batches left for each domain + for domain in domains: + train_loader.dataset.batches_left[domain] = \ + train_loader.dataset.batches_left[domain] - 1 \ + if train_loader.dataset.batches_left[domain] > 1 else 1 + + if i % args.print_iters == 0 and args.print_iters != -1: + print(avg_loss,penalty) + agg['losses'].append([l / args.print_iters for l in running_losses]) + if not args.no_wandb: + wandb.log({ "loss": loss.item(), + "erm_loss": agg['losses'][-1][0], + "irm_loss": agg['losses'][-1][1], + "vrex_loss": agg['losses'][-1][2], + }) + running_losses = [0]*len(losses) + # agg['losses'].append([l.item() for l in losses]) + agg['train_loss'].append(running_loss / args.print_iters) + agg['train_iters'].append(i+1+epoch*total_iters) + print('iteration {:05d}/{:d}: loss: {:6.3f}'.format(i + 1, total_iters, running_loss / args.print_iters)) + if i % args.eval_iters == 0 and args.eval_iters != -1: + test(val_loader, agg, loader_type='val') + test(test_loader, agg, loader_type='test') + if not args.no_wandb: + wandb.log({"val_acc":agg['val_stat'][-1]}) + wandb.log({"test_acc":agg['test_stat'][-1]}) + model.train() + save_best_model(model, runPath, agg, args) + +def train_vrex(train_loader, epoch, agg): + model.train() + train_loader.dataset.reset_batch() + i = 0 + print('\n====> Epoch: {:03d} '.format(epoch)) + running_loss = 0 + total_iters = len(train_loader) + running_losses = [] + while sum([l > 1 for l in train_loader.dataset.batches_left.values()]) >= args.meta_steps: + model.train() + i += 1 + # sample `meta_steps` number of domains to use for the inner loop + domains = sample_domains(train_loader, args.meta_steps, args.stratified).tolist() + # print(domains) + avg_loss = 0. + penalty = 0. + # overall_losses = F.cross_entropy(scale * results['y_pred'],results['y_true'],reduction="none") + losses_bygroup = [] + + # inner loop update + for domain in domains: + data = train_loader.dataset.get_batch(domain) + x, y = unpack_data(data, device) + y_hat = model(x,frozen_mode=args.frozen) + # loss = criterion(y_hat, y) + if 'poverty'in args.dataset.lower(): + loss = F.mse_loss(scale*y_hat,y,reduction="none") + else: + loss = F.cross_entropy(scale * y_hat,y,reduction="none") + losses_bygroup.append(loss.mean()) + + penalty += irm_penalty(loss) + avg_loss += loss.mean() + avg_loss /= args.meta_steps + penalty /= args.meta_steps + losses = [avg_loss, penalty, torch.stack(losses_bygroup).var()] + if len(running_losses)==0: + running_losses = [0]*len(losses) + for (j,loss) in enumerate(running_losses): + running_losses[j]+=losses[j].item() + + # print([l.item() for l in losses],sol) + optimiserC.zero_grad() + # loss = scales.dot(torch.stack(losses)) + loss = avg_loss+args.penalty_weight*torch.stack(losses_bygroup).var() + # print(loss) + loss.backward() + optimiserC.step() + if scheduler is not None and scheduler.step_every_batch: + scheduler.step() + running_loss += loss.item() + + # log the number of batches left for each domain + for domain in domains: + train_loader.dataset.batches_left[domain] = \ + train_loader.dataset.batches_left[domain] - 1 \ + if train_loader.dataset.batches_left[domain] > 1 else 1 + # print(i) + if i % args.print_iters == 0 and args.print_iters != -1: + print(avg_loss,penalty) + agg['losses'].append([l / args.print_iters for l in running_losses]) + if not args.no_wandb: + wandb.log({ "loss": loss.item(), + "erm_loss": agg['losses'][-1][0], + "irm_loss": agg['losses'][-1][1], + "vrex_loss": agg['losses'][-1][2], + }) + running_losses = [0]*len(losses) + # agg['losses'].append([l.item() for l in losses]) + agg['train_loss'].append(running_loss / args.print_iters) + agg['train_iters'].append(i+1+epoch*total_iters) + print('iteration {:05d}/{:d}: loss: {:6.3f}'.format(i + 1, total_iters, running_loss / args.print_iters)) + if i % args.eval_iters == 0 and args.eval_iters != -1: + test(val_loader, agg, loader_type='val') + test(test_loader, agg, loader_type='test') + if not args.no_wandb: + wandb.log({"val_acc":agg['val_stat'][-1]}) + wandb.log({"test_acc":agg['test_stat'][-1]}) + model.train() + save_best_model(model, runPath, agg, args) + + +amplify = 1e2 if (not args.adjust_irm and not args.adjust_loss) else 1 +preference = get_preference(args.preference_choice) +n_tasks = 1+2 +preference[1]/=amplify+1e-6 +pair_optimizer = PAIR(trainable_params,optimiserC,preference=preference,eps=args.eps) +descent = 0 +def train_pair(train_loader, epoch, agg): + model.train() + train_loader.dataset.reset_batch() + i = 0 + print('\n====> Epoch: {:03d} '.format(epoch)) + running_loss = 0 + total_iters = len(train_loader) + running_losses = [] + while sum([l > 1 for l in train_loader.dataset.batches_left.values()]) >= args.meta_steps: + model.train() + i += 1 + # sample `meta_steps` number of domains to use for the inner loop + domains = sample_domains(train_loader, args.meta_steps, args.stratified).tolist() + + avg_loss = 0. + penalty = 0. + # overall_losses = F.cross_entropy(scale * results['y_pred'],results['y_true'],reduction="none") + losses_bygroup = [] + y_hats = [] + # inner loop update + for domain in domains: + data = train_loader.dataset.get_batch(domain) + x, y = unpack_data(data, device) + y_hat = model(x,frozen_mode=args.frozen) + if 'poverty'in args.dataset.lower(): + loss = F.mse_loss(scale*y_hat,y,reduction="none") + else: + loss = F.cross_entropy(scale * y_hat,y,reduction="none") + losses_bygroup.append(loss.mean()) + + if args.adjust_loss: + penalty += irm_penalty(loss) + else: + penalty += irm_penalty(loss,pos=amplify,adjust=args.adjust_irm) + avg_loss += loss.mean() + avg_loss /= args.meta_steps + penalty /= args.meta_steps + losses = [avg_loss, penalty, torch.stack(losses_bygroup).var()] + if len(running_losses)==0: + running_losses = [0]*len(losses) + for (j,loss) in enumerate(running_losses): + running_losses[j]+=losses[j].item() + pair_optimizer.zero_grad() + pair_optimizer.set_losses(losses) + pair_loss, moo_losses, mu_rl, alphas = pair_optimizer.step() + + if scheduler is not None and scheduler.step_every_batch: + scheduler.step() + running_loss += pair_loss + + # log the number of batches left for each domain + for domain in domains: + train_loader.dataset.batches_left[domain] = \ + train_loader.dataset.batches_left[domain] - 1 \ + if train_loader.dataset.batches_left[domain] > 1 else 1 + + if i % args.print_iters == 0 and args.print_iters != -1: + agg['losses'].append([l / args.print_iters for l in running_losses]) + # compensate the irm penalty + if not args.adjust_irm and not args.adjust_loss: + agg['losses'][-1][1] -= amplify + + if not args.no_wandb: + wandb.log({ "loss": loss.item(), + "erm_loss": agg['losses'][-1][0], + "irm_loss": agg['losses'][-1][1], + "vrex_loss": agg['losses'][-1][2], + "mu_rl":mu_rl, + "erm_alpha":alphas[0], + "irm_alpha":alphas[1], + "vrex_alpha":alphas[2] + }) + running_losses = [0]*len(losses) + # agg['losses'].append([l.item() for l in losses]) + agg['train_loss'].append(running_loss / args.print_iters) + agg['train_iters'].append(i+1+epoch*total_iters) + print('iteration {:05d}/{:d}: loss: {:6.3f}'.format(i + 1, total_iters, running_loss / args.print_iters)) + if i % args.eval_iters == 0 and args.eval_iters != -1: + test(val_loader, agg, loader_type='val') + test(test_loader, agg, loader_type='test') + if not args.no_wandb: + wandb.log({"val_acc":agg['val_stat'][-1]}) + wandb.log({"test_acc":agg['test_stat'][-1]}) + model.train() + save_best_model(model, runPath, agg, args) + + +def train_fish(train_loader, epoch, agg): + model.train() + train_loader.dataset.reset_batch() + i = 0 + print('\n====> Epoch: {:03d} '.format(epoch)) + opt_inner_pre = None + running_losses = [] + + while sum([l > 1 for l in train_loader.dataset.batches_left.values()]) >= args.meta_steps: + i += 1 + # sample `meta_steps` number of domains to use for the inner loop + domains = sample_domains(train_loader, args.meta_steps, args.stratified).tolist() + + + # prepare model for inner loop update + model_inner = copy.deepcopy(model) + + model_inner.train() + if args.dataset.lower() in ["poverty"]: + classifier = model_inner.enc.fc + elif args.dataset.lower() in ["iwildcam","rxrx"]: + classifier = model_inner.fc + else: + classifier = model_inner.classifier + inner_trainable_params = classifier.parameters() if args.frozen else model_inner.parameters() + opt_inner = opt(inner_trainable_params, **args.optimiser_args) + if opt_inner_pre is not None and args.reload_inner_optim: + opt_inner.load_state_dict(opt_inner_pre) + + penalty = 0. + avg_loss = 0 + losses_bygroup = [] + # inner loop update + for domain in domains: + data = train_loader.dataset.get_batch(domain) + x, y = unpack_data(data, device) + opt_inner.zero_grad() + y_hat = model_inner(x) + loss = criterion(y_hat, y) + loss.backward() + opt_inner.step() + losses_bygroup.append(loss.mean()) + if 'poverty'in args.dataset.lower(): + cur_loss = F.mse_loss(scale*y_hat,y,reduction="none") + else: + cur_loss = F.cross_entropy(scale * y_hat,y,reduction="none") + penalty += irm_penalty(cur_loss) + avg_loss += loss.mean() + + avg_loss /= args.meta_steps + penalty /= args.meta_steps + # losses = losses_bygroup+[ penalty, torch.stack(losses_bygroup).var()] + losses = [avg_loss, penalty, torch.stack(losses_bygroup).var()] + # agg['losses'].append([l.item() for l in losses]) + if len(running_losses)==0: + running_losses = [0]*len(losses) + for (j,loss) in enumerate(running_losses): + running_losses[j]+=losses[j].item() + + opt_inner_pre = opt_inner.state_dict() + # fish update + meta_weights = fish_step(meta_weights=model.state_dict(), + inner_weights=model_inner.state_dict(), + meta_lr=args.meta_lr / args.meta_steps) + model.reset_weights(meta_weights) + # log the number of batches left for each domain + for domain in domains: + train_loader.dataset.batches_left[domain] = \ + train_loader.dataset.batches_left[domain] - 1 \ + if train_loader.dataset.batches_left[domain] > 1 else 1 + + if (i + 1) % args.print_iters == 0 and args.print_iters != -1: + agg['losses'].append([l / args.print_iters for l in running_losses]) + if not args.no_wandb: + wandb.log({ "loss": avg_loss, + "erm_loss": agg['losses'][-1][0], + "irm_loss": agg['losses'][-1][1], + "vrex_loss": agg['losses'][-1][2], + }) + print(f"iteration {(i + 1):05d}: {agg['losses'][-1]}") + running_losses = [0]*len(losses) + if i % args.eval_iters == 0 and args.eval_iters != -1: + test(val_loader, agg, loader_type='val') + test(test_loader, agg, loader_type='test') + if not args.no_wandb: + wandb.log({"val_acc":agg['val_stat'][-1]}) + wandb.log({"test_acc":agg['test_stat'][-1]}) + model.train() + save_best_model(model, runPath, agg, args) + +def test(test_loader, agg, loader_type='test', verbose=True, save_ypred=False, return_last=False): + model.eval() + yhats, ys, metas = [], [], [] + import timeit + with torch.no_grad(): + a = timeit.default_timer() + for i, (x, y, meta) in enumerate(test_loader): + # get the inputs + x, y = x.to(device), y.to(device) + y_hat = model(x) + ys.append(y) + yhats.append(y_hat) + metas.append(meta) + # print(timeit.default_timer()-a) + # a = timeit.default_timer() + ypreds, ys, metas = predict_fn(torch.cat(yhats)), torch.cat(ys), torch.cat(metas) + if save_ypred: + if args.dataset == 'poverty': + save_name = f"{args.dataset}_split:{loader_type}_fold:" \ + f"{['A', 'B', 'C', 'D', 'E'][args.seed]}" \ + f"_epoch:best_pred.csv" + else: + save_name = f"{args.dataset}_split:{loader_type}_seed:" \ + f"{args.seed}_epoch:best_pred.csv" + with open(f"{runPath}/{save_name}", 'w') as f: + writer = csv.writer(f) + writer.writerows(ypreds.unsqueeze(1).cpu().tolist()) + test_val = test_loader.dataset.eval(ypreds.cpu(), ys.cpu(), metas) + agg[f'{loader_type}_stat'].append(test_val[0][args.selection_metric]) + if verbose: + print(f"=============== {loader_type} ===============\n{test_val[-1]}") + if return_last: + return test_val[0][args.selection_metric] + + +if __name__ == '__main__': + try: + if args.need_pretrain and args.pretrain_iters != 0: + pretrain_path = os.path.join(args.exp_dir,"experiments",args.dataset,str(args.seed)) + if not os.path.exists(pretrain_path): + os.makedirs(pretrain_path) + if args.use_old: + model.load_state_dict(torch.load(pretrain_path + f'/model.rar')) + print(f"Load pretrained model from {pretrain_path}") + else: + print("="*30 + "ERM pretrain" + "="*30) + pretrain(train_loader, args.pretrain_iters, save_path=pretrain_path) + + torch.cuda.empty_cache() + print("="*30 + f"Training: {args.algorithm}" + "="*30) + train = locals()[f'train_{args.algorithm}'] + agg = defaultdict(list) + agg['val_stat'] = [0.] + + for epoch in range(args.epochs): + train(train_loader, epoch, agg) + + test(val_loader, agg, loader_type='val') + test(test_loader, agg, loader_type='test') + save_best_model(model, runPath, agg, args) + if not args.no_wandb: + wandb.log({"val_acc":agg['val_stat'][-1]}) + wandb.log({"test_acc":agg['test_stat'][-1]}) + if scheduler is not None and not scheduler.step_every_batch: + scheduler.step() + print(optimiserC) + + model.load_state_dict(torch.load(runPath + '/model.rar')) + print('Finished training! Loading best model...') + test_acc = 0 + for split, loader in tv_loaders.items(): + tmp_acc = test(loader, agg, loader_type=split, save_ypred=True,return_last=True) + if split=="test": + test_acc = tmp_acc + + import matplotlib.pyplot as plt + if not args.no_plot: + folder_name = os.path.join(args.exp_dir,"plots",f"{args.dataset}_{args.algorithm}") + if not os.path.exists(folder_name): + os.mkdir(folder_name) + num_epochs = len(agg['losses']) + plt.title(exp_name) + fig, ax1 = plt.subplots() + ax1.set_xlabel("epoch") + ax1.set_ylabel("test acc") + + + if args.algorithm in ['pair','irm','vrex','fish',"irmx"]: + ax2 = ax1.twinx() + ax2.set_ylabel("penalty") + if len(agg['losses'][0])>=3: + irm_pens = np.array([log_i[-2] for log_i in agg['losses']]) + vrex_pens = np.array([log_i[-1] for log_i in agg['losses']]) + ax2.plot(np.arange(num_epochs),irm_pens,label=f'irm_pen',c='r',alpha=0.2) + ax2.plot(np.arange(num_epochs),vrex_pens,label=f'vrex_pen',c='g',alpha=0.2) + erm_pens = np.array([log_i[-3] for log_i in agg['losses']]) + else: + irm_pens = np.array([log_i[-1] for log_i in agg['losses']]) + ax2.plot(np.arange(num_epochs),irm_pens,label=f'{args.algorithm}_pen',c='r',alpha=0.2) + erm_pens = np.array([log_i[-2] for log_i in agg['losses']]) + + max_ratio = irm_pens.max() + ax2.set_title(f"{exp_name}: {test_acc}") + # control the scale of erm loss w.r.t. others + erm_pens = np.clip(erm_pens,erm_pens.min(),erm_pens.min()*max_ratio) + ax1.plot(np.arange(num_epochs),erm_pens,label=f'erm_pen') + # ask matplotlib for the plotted objects and their labels + lines, labels = ax1.get_legend_handles_labels() + lines2, labels2 = ax2.get_legend_handles_labels() + ax2.legend(lines + lines2, labels + labels2, loc=0) + else: + erm_pens = np.array([log_i[0] for log_i in agg['losses']]) + ax1.plot(np.arange(num_epochs),erm_pens,label=f'erm_pen') + + plt.savefig(os.path.join(folder_name,f"{exp_name}.png")) + plt.close() + torch.save(agg,os.path.join(folder_name,f"{exp_name}_agg.pt")) + if not args.no_wandb: + wandb.finish() + except Exception as e: + traceback.print_exc() + print(e) + if not args.no_wandb: + wandb.finish(-1) + print("Exceptions found, delete all wandb files") + import shutil + shutil.rmtree(wandb_run.dir.replace("/files","")) diff --git a/WILDS/src/misc.py b/WILDS/src/misc.py new file mode 100644 index 0000000..e8223b9 --- /dev/null +++ b/WILDS/src/misc.py @@ -0,0 +1,400 @@ +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved + +""" +Things that don't belong anywhere else +""" + +import hashlib +import json +import os +import sys +from shutil import copyfile +from collections import OrderedDict +from numbers import Number +import operator + +import numpy as np +import torch +import tqdm +from collections import Counter + +def make_weights_for_balanced_classes(dataset): + counts = Counter() + classes = [] + for _, y in dataset: + y = int(y) + counts[y] += 1 + classes.append(y) + + n_classes = len(counts) + + weight_per_class = {} + for y in counts: + weight_per_class[y] = 1 / (counts[y] * n_classes) + + weights = torch.zeros(len(dataset)) + for i, y in enumerate(classes): + weights[i] = weight_per_class[int(y)] + + return weights + +def pdb(): + sys.stdout = sys.__stdout__ + import pdb + print("Launching PDB, enter 'n' to step to parent function.") + pdb.set_trace() + +def seed_hash(*args): + """ + Derive an integer hash from all args, for use as a random seed. + """ + args_str = str(args) + return int(hashlib.md5(args_str.encode("utf-8")).hexdigest(), 16) % (2**31) + +def print_separator(): + print("="*80) + +def print_row(row, colwidth=10, latex=False): + if latex: + sep = " & " + end_ = "\\\\" + else: + sep = " " + end_ = "" + + def format_val(x): + if np.issubdtype(type(x), np.floating): + x = "{:.10f}".format(x) + return str(x).ljust(colwidth)[:colwidth] + print(sep.join([format_val(x) for x in row]), end_) + +class _SplitDataset(torch.utils.data.Dataset): + """Used by split_dataset""" + def __init__(self, underlying_dataset, keys): + super(_SplitDataset, self).__init__() + self.underlying_dataset = underlying_dataset + self.keys = keys + def __getitem__(self, key): + return self.underlying_dataset[self.keys[key]] + def __len__(self): + return len(self.keys) + +def split_dataset(dataset, n, seed=0): + """ + Return a pair of datasets corresponding to a random split of the given + dataset, with n datapoints in the first dataset and the rest in the last, + using the given random seed + """ + assert(n <= len(dataset)) + keys = list(range(len(dataset))) + np.random.RandomState(seed).shuffle(keys) + keys_1 = keys[:n] + keys_2 = keys[n:] + return _SplitDataset(dataset, keys_1), _SplitDataset(dataset, keys_2) + +def random_pairs_of_minibatches(minibatches): + perm = torch.randperm(len(minibatches)).tolist() + pairs = [] + + for i in range(len(minibatches)): + j = i + 1 if i < (len(minibatches) - 1) else 0 + + xi, yi = minibatches[perm[i]][0], minibatches[perm[i]][1] + xj, yj = minibatches[perm[j]][0], minibatches[perm[j]][1] + + min_n = min(len(xi), len(xj)) + + pairs.append(((xi[:min_n], yi[:min_n]), (xj[:min_n], yj[:min_n]))) + + return pairs + +def accuracy(network, loader, weights, device): + correct = 0 + total = 0 + weights_offset = 0 + + network.eval() + with torch.no_grad(): + for x, y in loader: + x = x.to(device) + y = y.to(device) + p = network.predict(x) + #print() + #print(p) + if weights is None: + batch_weights = torch.ones(len(x)) + else: + batch_weights = weights[weights_offset : weights_offset + len(x)] + weights_offset += len(x) + batch_weights = batch_weights.to(device) + if p.size(1) == 1: + #print(p.flatten().gt(0).eq(y).float()) + #print(p.flatten().gt(0).eq(y).float().sum().item()) + correct += (p.flatten().gt(0).eq(y).float() * batch_weights.flatten()).sum().item() + else: + correct += (p.argmax(1).eq(y).float() * batch_weights).sum().item() + total += batch_weights.sum().item() + #print(correct,total) + #0/0 + network.train() + + return correct / total + +class Tee: + def __init__(self, fname, mode="a"): + self.stdout = sys.stdout + self.file = open(fname, mode) + + def write(self, message): + self.stdout.write(message) + self.file.write(message) + self.flush() + + def flush(self): + self.stdout.flush() + self.file.flush() + +class ParamDict(OrderedDict): + """Code adapted from https://github.com/Alok/rl_implementations/tree/master/reptile. + A dictionary where the values are Tensors, meant to represent weights of + a model. This subclass lets you perform arithmetic on weights directly.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, *kwargs) + + def _prototype(self, other, op): + if isinstance(other, Number): + return ParamDict({k: op(v, other) for k, v in self.items()}) + elif isinstance(other, dict): + return ParamDict({k: op(self[k], other[k]) for k in self}) + else: + raise NotImplementedError + + def __add__(self, other): + return self._prototype(other, operator.add) + + def __rmul__(self, other): + return self._prototype(other, operator.mul) + + __mul__ = __rmul__ + + def __neg__(self): + return ParamDict({k: -v for k, v in self.items()}) + + def __rsub__(self, other): + # a- b := a + (-b) + return self.__add__(other.__neg__()) + + __sub__ = __rsub__ + + def __truediv__(self, other): + return self._prototype(other, operator.truediv) + + + + +def l2_between_dicts(dict_1, dict_2): + assert len(dict_1) == len(dict_2) + dict_1_values = [dict_1[key] for key in sorted(dict_1.keys())] + dict_2_values = [dict_2[key] for key in sorted(dict_1.keys())] + return ( + torch.cat(tuple([t.view(-1) for t in dict_1_values])) - + torch.cat(tuple([t.view(-1) for t in dict_2_values])) + ).pow(2).mean() + +class MovingAverage: + + def __init__(self, ema, oneminusema_correction=True): + self.ema = ema + self.ema_data = {} + self._updates = 0 + self._oneminusema_correction = oneminusema_correction + + def update(self, dict_data): + ema_dict_data = {} + for name, data in dict_data.items(): + data = data.view(1, -1) + if self._updates == 0: + previous_data = torch.zeros_like(data) + else: + previous_data = self.ema_data[name] + + ema_data = self.ema * previous_data + (1 - self.ema) * data + if self._oneminusema_correction: + # correction by 1/(1 - self.ema) + # so that the gradients amplitude backpropagated in data is independent of self.ema + ema_dict_data[name] = ema_data / (1 - self.ema) + else: + ema_dict_data[name] = ema_data + self.ema_data[name] = ema_data.clone().detach() + + self._updates += 1 + return ema_dict_data + + + +def make_weights_for_balanced_classes(dataset): + counts = Counter() + classes = [] + for _, y in dataset: + y = int(y) + counts[y] += 1 + classes.append(y) + + n_classes = len(counts) + + weight_per_class = {} + for y in counts: + weight_per_class[y] = 1 / (counts[y] * n_classes) + + weights = torch.zeros(len(dataset)) + for i, y in enumerate(classes): + weights[i] = weight_per_class[int(y)] + + return weights + +def pdb(): + sys.stdout = sys.__stdout__ + import pdb + print("Launching PDB, enter 'n' to step to parent function.") + pdb.set_trace() + +def seed_hash(*args): + """ + Derive an integer hash from all args, for use as a random seed. + """ + args_str = str(args) + return int(hashlib.md5(args_str.encode("utf-8")).hexdigest(), 16) % (2**31) + +def print_separator(): + print("="*80) + +def print_row(row, colwidth=10, latex=False): + if latex: + sep = " & " + end_ = "\\\\" + else: + sep = " " + end_ = "" + + def format_val(x): + if np.issubdtype(type(x), np.floating): + x = "{:.10f}".format(x) + return str(x).ljust(colwidth)[:colwidth] + print(sep.join([format_val(x) for x in row]), end_) + +class _SplitDataset(torch.utils.data.Dataset): + """Used by split_dataset""" + def __init__(self, underlying_dataset, keys): + super(_SplitDataset, self).__init__() + self.underlying_dataset = underlying_dataset + self.keys = keys + def __getitem__(self, key): + return self.underlying_dataset[self.keys[key]] + def __len__(self): + return len(self.keys) + +def split_dataset(dataset, n, seed=0): + """ + Return a pair of datasets corresponding to a random split of the given + dataset, with n datapoints in the first dataset and the rest in the last, + using the given random seed + """ + assert(n <= len(dataset)) + keys = list(range(len(dataset))) + np.random.RandomState(seed).shuffle(keys) + keys_1 = keys[:n] + keys_2 = keys[n:] + return _SplitDataset(dataset, keys_1), _SplitDataset(dataset, keys_2) + +def random_pairs_of_minibatches(minibatches): + perm = torch.randperm(len(minibatches)).tolist() + pairs = [] + + for i in range(len(minibatches)): + j = i + 1 if i < (len(minibatches) - 1) else 0 + + xi, yi = minibatches[perm[i]][0], minibatches[perm[i]][1] + xj, yj = minibatches[perm[j]][0], minibatches[perm[j]][1] + + min_n = min(len(xi), len(xj)) + + pairs.append(((xi[:min_n], yi[:min_n]), (xj[:min_n], yj[:min_n]))) + + return pairs + +def accuracy(network, loader, weights, device): + correct = 0 + total = 0 + weights_offset = 0 + + network.eval() + with torch.no_grad(): + for x, y in loader: + x = x.to(device) + y = y.to(device) + p = network.predict(x) + if weights is None: + batch_weights = torch.ones(len(x)) + else: + batch_weights = weights[weights_offset : weights_offset + len(x)] + weights_offset += len(x) + batch_weights = batch_weights.to(device) + if p.size(1) == 1: + correct += (p.gt(0).eq(y).float() * batch_weights.view(-1, 1)).sum().item() + else: + correct += (p.argmax(1).eq(y).float() * batch_weights).sum().item() + total += batch_weights.sum().item() + network.train() + + return correct / total + +class Tee: + def __init__(self, fname, mode="a"): + self.stdout = sys.stdout + self.file = open(fname, mode) + + def write(self, message): + self.stdout.write(message) + self.file.write(message) + self.flush() + + def flush(self): + self.stdout.flush() + self.file.flush() + +class ParamDict(OrderedDict): + """Code adapted from https://github.com/Alok/rl_implementations/tree/master/reptile. + A dictionary where the values are Tensors, meant to represent weights of + a model. This subclass lets you perform arithmetic on weights directly.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, *kwargs) + + def _prototype(self, other, op): + if isinstance(other, Number): + return ParamDict({k: op(v, other) for k, v in self.items()}) + elif isinstance(other, dict): + return ParamDict({k: op(self[k], other[k]) for k in self}) + else: + raise NotImplementedError + + def __add__(self, other): + return self._prototype(other, operator.add) + + def __rmul__(self, other): + return self._prototype(other, operator.mul) + + __mul__ = __rmul__ + + def __neg__(self): + return ParamDict({k: -v for k, v in self.items()}) + + def __rsub__(self, other): + # a- b := a + (-b) + return self.__add__(other.__neg__()) + + __sub__ = __rsub__ + + def __truediv__(self, other): + return self._prototype(other, operator.truediv) diff --git a/WILDS/src/models/__init__.py b/WILDS/src/models/__init__.py new file mode 100644 index 0000000..e6e74a9 --- /dev/null +++ b/WILDS/src/models/__init__.py @@ -0,0 +1,9 @@ +from .camelyon import Model as camelyon +from .cdsprites import Model as cdsprites +from .civil import Model as civil +from .fmow import Model as fmow +from .iwildcam import Model as iwildcam +from .poverty import Model as poverty +from .rxrx import Model as rxrx + +__all__ = [cdsprites, iwildcam, camelyon, amazon, civil, fmow, poverty, rxrx] diff --git a/WILDS/src/models/camelyon.py b/WILDS/src/models/camelyon.py new file mode 100644 index 0000000..1d1db2a --- /dev/null +++ b/WILDS/src/models/camelyon.py @@ -0,0 +1,79 @@ +import os +from copy import deepcopy + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torchvision.transforms as transforms +from torch.utils.data import DataLoader +from torchvision.models import densenet121 +from wilds.common.data_loaders import get_eval_loader +from wilds.datasets.camelyon17_dataset import Camelyon17Dataset + +from .datasets import GeneralWilds_Batched_Dataset + +IMG_HEIGHT = 224 +NUM_CLASSES = 2 + +class Model(nn.Module): + def __init__(self, args, weights): + super(Model, self).__init__() + self.num_classes = NUM_CLASSES + self.enc = densenet121(pretrained=False).features # remove fc layer + self.classifier = nn.Linear(1024, self.num_classes) + if weights is not None: + self.load_state_dict(deepcopy(weights)) + + def reset_weights(self, weights): + self.load_state_dict(deepcopy(weights)) + + @staticmethod + def getDataLoaders(args, device): + full_dataset = Camelyon17Dataset(root_dir=os.path.join(args.data_dir, 'wilds'), download=True) + + transform = transforms.Compose([ + transforms.Resize((224, 224)), + transforms.ToTensor(), + transforms.Normalize([0.485, 0.456, 0.406], + [0.229, 0.224, 0.225]) + ]) + # get all train data + train_data = full_dataset.get_subset('train', transform=transform) + # separate into subsets by distribution + train_sets = GeneralWilds_Batched_Dataset(train_data, args.batch_size, domain_idx=0, drop_last=not args.no_drop_last) + # take subset of test and validation, making sure that only labels appeared in train + # are included + datasets = {} + for split in full_dataset.split_dict: + if split != 'train': + datasets[split] = full_dataset.get_subset(split, transform=transform) + + # get the loaders + kwargs = {'num_workers': args.num_workers, 'pin_memory': True, 'drop_last': False} \ + if device.type == "cuda" else {} + train_loaders = DataLoader(train_sets, batch_size=args.batch_size, shuffle=True, **kwargs) + + kwargs = {'num_workers': args.num_workers, 'pin_memory': True, 'drop_last': False} + tv_loaders = {} + for split, dataset in datasets.items(): + tv_loaders[split] = get_eval_loader('standard', dataset, batch_size=256,**kwargs) + return train_loaders, tv_loaders,full_dataset + + def forward(self, x, get_feat=False,frozen_mode=False): + if frozen_mode: + self.enc.eval() + self.classifier.train() + with torch.no_grad(): + features = self.enc(x) + out = F.relu(features, inplace=True) + out = F.adaptive_avg_pool2d(out, (1, 1)) + out = torch.flatten(out, 1) + else: + features = self.enc(x) + out = F.relu(features, inplace=True) + out = F.adaptive_avg_pool2d(out, (1, 1)) + out = torch.flatten(out, 1) + pred = self.classifier(out) + if get_feat: + return pred, out + return pred diff --git a/WILDS/src/models/civil.py b/WILDS/src/models/civil.py new file mode 100644 index 0000000..5dde9a3 --- /dev/null +++ b/WILDS/src/models/civil.py @@ -0,0 +1,237 @@ +import os +from copy import deepcopy + +import torch +from torch import nn +from torch.utils.data import DataLoader +from transformers import DistilBertForSequenceClassification +from transformers import DistilBertTokenizerFast +from transformers import logging +from wilds.common.data_loaders import get_eval_loader +from wilds.datasets.civilcomments_dataset import CivilCommentsDataset + +from .datasets import CivilComments_Batched_Dataset + +logging.set_verbosity_error() + +MAX_TOKEN_LENGTH = 300 +NUM_CLASSES = 2 + +def initialize_bert_transform(root_dir="../data"): + """Adapted from the Wilds library, available at: https://github.com/p-lambda/wilds""" + try: + tokenizer = DistilBertTokenizerFast.from_pretrained(root_dir) + except Exception as e: + tokenizer = DistilBertTokenizerFast.from_pretrained("distilbert-base-uncased") + tokenizer.save_pretrained(root_dir) + def transform(text): + tokens = tokenizer( + text, + padding='max_length', + truncation=True, + max_length=MAX_TOKEN_LENGTH, + return_tensors='pt') + x = torch.stack( + (tokens['input_ids'], + tokens['attention_mask']), + dim=2) + x = torch.squeeze(x, dim=0) # First shape dim is always 1 + return x + return transform + + +class DistilBertClassifier(DistilBertForSequenceClassification): + def __init__(self, config): + super().__init__(config) + + + def __call__(self, x,output_hidden_states=False): + input_ids = x[:, :, 0] + attention_mask = x[:, :, 1] + outputs = super().__call__( + input_ids=input_ids, + attention_mask=attention_mask, + output_hidden_states=output_hidden_states, + ) + + if output_hidden_states: + outputs = outputs + else: + outputs = outputs[0] + return outputs + +class Model(nn.Module): + def __init__(self, args, weights): + super(Model, self).__init__() + self.num_classes = NUM_CLASSES + + try: + self.model = DistilBertClassifier.from_pretrained( + os.path.join(args.data_dir, 'wilds',args.dataset), + num_labels=2, + ) + except Exception as e: + self.model = DistilBertClassifier.from_pretrained( + 'distilbert-base-uncased', + num_labels=2, + ) + self.model.save_pretrained(os.path.join(args.data_dir, 'wilds',args.dataset)) + # self.model = DistilBertClassifier.from_pretrained( + # 'distilbert-base-uncased', + # num_labels=2, + # cache_dir=os.path.join(args.data_dir, 'wilds',args.dataset) + # ) + if weights is not None: + self.load_state_dict(deepcopy(weights)) + self.classifier = self.model.classifier + + def reset_weights(self, weights): + self.model.load_state_dict(deepcopy(weights)) + self.classifier = self.model.classifier + + @staticmethod + def getDataLoaders(args, device): + dataset = CivilCommentsDataset(root_dir=os.path.join(args.data_dir, 'wilds'), download=True) + # get all train data + transform = initialize_bert_transform(root_dir=os.path.join(args.data_dir, 'wilds', args.dataset)) + train_data = dataset.get_subset('train', transform=transform) + # separate into subsets by distribution + train_sets = CivilComments_Batched_Dataset(train_data, batch_size=args.batch_size, drop_last=not args.no_drop_last) + # take subset of test and validation, making sure that only labels appeared in train + # are included + datasets = {} + for split in dataset.split_dict: + if split != 'train': + datasets[split] = dataset.get_subset(split, transform=transform) + # get the loaders + kwargs = {'num_workers': args.num_workers, 'pin_memory': True, 'drop_last': False} \ + if device.type == "cuda" else {} + train_loaders = DataLoader(train_sets, batch_size=args.batch_size, shuffle=True, **kwargs) + tv_loaders = {} + for split, sep_dataset in datasets.items(): + tv_loaders[split] = get_eval_loader('standard', sep_dataset, batch_size=256, num_workers=args.num_workers) + return train_loaders, tv_loaders, dataset + + def forward(self, x, get_feat=False,frozen_mode=False): + if frozen_mode: + self.model.eval() + self.classifier.train() + with torch.no_grad(): + outs = self.model(x,output_hidden_states=True) + pooled_output = outs[1][-1][:, 0] + # pooled_output = hidden_state[:, 0] # (bs, dim) + pooled_output = self.model.pre_classifier(pooled_output) # (bs, dim) + pooled_output = nn.ReLU()(pooled_output) # (bs, dim) + pooled_output = self.model.dropout(pooled_output) # (bs, dim) + outs = self.classifier(pooled_output) + return outs + if get_feat: + # print(self.model) + outs = self.model(x,output_hidden_states=True) + with torch.no_grad(): + pooled_output = outs[1][-1][:, 0] + # pooled_output = hidden_state[:, 0] # (bs, dim) + pooled_output = self.model.pre_classifier(pooled_output) # (bs, dim) + pooled_output = nn.ReLU()(pooled_output) # (bs, dim) + pooled_output = self.model.dropout(pooled_output) # (bs, dim) + # print(pooled_output.size()) + + # print(self.model.classifier) + # print(outs) + # exit() + return outs[0],pooled_output + return self.model(x) +import os +from copy import deepcopy + +import torch +from torch import nn +from torch.utils.data import DataLoader +from transformers import BertForSequenceClassification +from transformers import BertTokenizerFast +from transformers import logging +from wilds.common.data_loaders import get_eval_loader +from wilds.datasets.civilcomments_dataset import CivilCommentsDataset + +from .datasets import CivilComments_Batched_Dataset + +logging.set_verbosity_error() + +MAX_TOKEN_LENGTH = 300 +NUM_CLASSES = 2 + +# def initialize_bert_transform(): +# """Adapted from the Wilds library, available at: https://github.com/p-lambda/wilds""" +# tokenizer = BertTokenizerFast.from_pretrained('bert-base-uncased') +# def transform(text): +# tokens = tokenizer( +# text, +# padding='max_length', +# truncation=True, +# max_length=MAX_TOKEN_LENGTH, +# return_tensors='pt') +# x = torch.stack( +# (tokens['input_ids'], +# tokens['attention_mask'], +# tokens['token_type_ids']), +# dim=2) +# x = torch.squeeze(x, dim=0) # First shape dim is always 1 +# return x +# return transform + +# class BertClassifier(BertForSequenceClassification): +# """Adapted from the Wilds library, available at: https://github.com/p-lambda/wilds""" +# def __init__(self, args): +# super().__init__(args) +# self.d_out = 2 + +# def __call__(self, x): +# input_ids = x[:, :, 0] +# attention_mask = x[:, :, 1] +# token_type_ids = x[:, :, 2] +# outputs = super().__call__( +# input_ids=input_ids, +# attention_mask=attention_mask, +# token_type_ids=token_type_ids +# )[0] +# return outputs + +# class Model(nn.Module): +# def __init__(self, args, weights): +# super(Model, self).__init__() +# self.num_classes = NUM_CLASSES +# self.model = BertClassifier.from_pretrained( +# 'bert-base-uncased', +# num_labels=2, +# ) +# if weights is not None: +# self.load_state_dict(deepcopy(weights)) + +# def reset_weights(self, weights): +# self.load_state_dict(deepcopy(weights)) + +# @staticmethod +# def getDataLoaders(args, device): +# dataset = CivilCommentsDataset(root_dir=os.path.join(args.data_dir, 'wilds'), download=True) +# # get all train data +# transform = initialize_bert_transform() +# train_data = dataset.get_subset('train', transform=transform) +# # separate into subsets by distribution +# train_sets = CivilComments_Batched_Dataset(train_data, batch_size=args.batch_size) +# # take subset of test and validation, making sure that only labels appeared in train +# # are included +# datasets = {} +# for split in dataset.split_dict: +# if split != 'train': +# datasets[split] = dataset.get_subset(split, transform=transform) +# # get the loaders +# kwargs = {'num_workers': args.num_workers, 'pin_memory': True, 'drop_last': False} \ +# if device.type == "cuda" else {} +# train_loaders = DataLoader(train_sets, batch_size=args.batch_size, shuffle=True, **kwargs) +# tv_loaders = {} +# for split, sep_dataset in datasets.items(): +# tv_loaders[split] = get_eval_loader('standard', sep_dataset, batch_size=256) +# return train_loaders, tv_loaders, dataset + +# def forward(self, x): +# return self.model(x) diff --git a/WILDS/src/models/datasets.py b/WILDS/src/models/datasets.py new file mode 100644 index 0000000..9182ae9 --- /dev/null +++ b/WILDS/src/models/datasets.py @@ -0,0 +1,501 @@ +import copy +import os + +import numpy as np +import torch +from PIL import Image +from torch.utils.data import Dataset + + +class Poverty_Batched_Dataset(Dataset): + """ + Batched dataset for Poverty. Allows for getting a batch of data given + a specific domain index. + """ + def __init__(self, dataset, split, batch_size, transform=None, drop_last=True): + self.split_array = dataset.split_array + self.split_dict = dataset.split_dict + split_mask = self.split_array == self.split_dict[split] + self.split_idx = np.where(split_mask)[0] + + self.root = dataset.root + self.no_nl = dataset.no_nl + + self.metadata_array = torch.stack([dataset.metadata_array[self.split_idx, i] for i in [0, 2]], -1) + self.y_array = dataset.y_array[self.split_idx] + + self.eval = dataset.eval + self.collate = dataset.collate + self.metadata_fields = dataset.metadata_fields + self.data_dir = dataset.data_dir + + self.transform = transform if transform is not None else lambda x: x + + domains = self.metadata_array[:, 1] + self.domain_indices = [torch.nonzero(domains == loc).squeeze(-1) + for loc in domains.unique()] + self.num_envs = len(domains.unique()) + for did in self.domain_indices: + print(len(did)) + self.domains = domains + self.targets = self.y_array + self.batch_size = batch_size + self.drop_last = drop_last + + min_domain_size = np.min([len(didx) for didx in self.domain_indices]) + self.training_steps = int(min_domain_size/self.batch_size)+\ + (not self.drop_last*(min_domain_size//self.batch_size>0)) + + def reset_batch(self): + """Reset batch indices for each domain.""" + self.batch_indices, self.batches_left = {}, {} + for loc, d_idx in enumerate(self.domain_indices): + self.batch_indices[loc] = torch.split(d_idx[torch.randperm(len(d_idx))], self.batch_size) + # mannually drop last + if self.drop_last and len(self.batch_indices[loc][-1])0)) + + def reset_batch(self): + """Reset batch indices for each domain.""" + self.batch_indices, self.batches_left = {}, {} + for loc, d_idx in enumerate(self.domain_indices): + self.batch_indices[loc] = torch.split(d_idx[torch.randperm(len(d_idx))], self.batch_size) + # mannually drop last + if self.drop_last and len(self.batch_indices[loc][-1])0)) + + def reset_batch(self): + """Reset batch indices for each domain.""" + self.batch_indices, self.batches_left = {}, {} + for loc, d_idx in enumerate(self.domain_indices): + print(len(d_idx)) + self.batch_indices[loc] = torch.split(d_idx[torch.randperm(len(d_idx))], self.batch_size) + # mannually drop last + if self.drop_last and len(self.batch_indices[loc][-1])0)) + + def reset_batch(self): + """Reset batch indices for each domain.""" + self.batch_indices, self.batches_left = {}, {} + for loc, d_idx in enumerate(self.domain_indices): + self.batch_indices[loc] = torch.split(d_idx[torch.randperm(len(d_idx))], self.batch_size) + # mannually drop last + if self.drop_last and len(self.batch_indices[loc][-1])0)) + + def reset_batch(self): + """Reset batch indices for each domain.""" + self.batch_indices, self.batches_left = {}, {} + for loc, env_idx in enumerate(self.domain_indices): + self.batch_indices[loc] = torch.split(env_idx[torch.randperm(len(env_idx))], self.batch_size) + # mannually drop last + if self.drop_last and len(self.batch_indices[loc][-1])3: + canvas = torch.zeros_like(image).repeat(1,3,1,1) + for c, (img, l) in enumerate(zip(image, latent)): + canvas[c, ...] = self.get_domain_color_palatte(img, l, env) + else: + canvas = self.get_domain_color_palatte(image, latent, env) + return (canvas, latent, None) + + + def __len__(self): + return len(self.latents) + + def __getitem__(self, idx): + image = torch.Tensor(self.images[idx]) + latent = torch.Tensor(self.latents[idx]) + + if len(image.shape)>3: + canvas = torch.zeros_like(image).repeat(1,3,1,1) + for c, (img, l) in enumerate(zip(image, latent)): + canvas[c, ...] = self.get_color_palatte(img, l) + else: + canvas = self.get_color_palatte(image, latent) + return (canvas, latent[0].long()-1, latent[0:]) + + def get_color_palatte(self, image, latent): + chosen_color = torch.randint(high=len(self.color_palattes) - 1, size=(1,)).item() + cc = int(latent[-1].long()) if self.split == 'train' else \ + torch.randint(high=2, size=(1,)).item() + canvas = self.color_palattes[chosen_color][cc] + return canvas*image + + def get_domain_color_palatte(self, image, latent, chosen_color): + cc = int(latent[-1].long()) + canvas = self.color_palattes[chosen_color][cc] + return canvas*image + + def eval(self, ypreds, ys, metas): + total = ys.size(0) + correct = (ypreds == ys).sum().item() + test_val = [ + {'acc_avg': correct/total}, + f"Accuracy: {correct/total*100:6.2f}%" + ] + return test_val + + +DspritesDataSize = torch.Size([1, 64, 64]) +class DspritesDataset(Dataset): + """2D shapes dataset. + More info here: + https://github.com/deepmind/dsprites-dataset/blob/master/dsprites_reloading_example.ipynb + """ + def __init__(self, data_root, train=True, train_fract=0.8, split=True, clip=False, drop_last=True): + """ + Args: + npz_file (string): Path to the npz file. + """ + filename = 'dsprites_ndarray_co1sh3sc6or40x32y32_64x64.npz' + self.npz_file = data_root + '/' + filename + self.npz_train_file = data_root + '/train_' + filename + self.npz_test_file = data_root + '/test_' + filename + if not os.path.isfile(self.npz_file): + self.download_dataset(self.npz_file) + if split: + if not (os.path.isfile(self.npz_train_file) and os.path.isfile(self.npz_test_file)): + self.split_dataset(data_root, self.npz_file, self.npz_train_file, + self.npz_test_file, train_fract, clip) + dataset = np.load(self.npz_train_file if train else self.npz_test_file, + mmap_mode='r') + else: + rdataset = np.load(self.npz_file, encoding='latin1', mmap_mode='r') + dataset = {'latents': rdataset['latents_values'][:, 1:], # drop colour + 'images': rdataset['imgs']} + + self.latents = dataset['latents'] + self.images = dataset['images'] + + def download_dataset(self, npz_file): + from urllib import request + url = 'https://github.com/deepmind/dsprites-dataset/blob/master/' \ + 'dsprites_ndarray_co1sh3sc6or40x32y32_64x64.npz?raw=true' + print('Downloading ' + url) + data = request.urlopen(url) + with open(npz_file, 'wb') as f: + f.write(data.read()) + + def split_dataset(self, data_root, npz_file, npz_train_file, npz_test_file, train_fract, clip): + print('Splitting dataset') + dataset = np.load(npz_file, encoding='latin1', mmap_mode='r') + latents = dataset['latents_values'][:, 1:] + images = np.array(dataset['imgs'], dtype='float32') + images = images.reshape(-1, *DspritesDataSize) + if clip: + images = np.clip(images, 1e-6, 1 - 1e-6) + + split_idx = np.int(train_fract * len(latents)) + shuffled_range = np.random.permutation(len(latents)) + train_idx = shuffled_range[range(0, split_idx)] + test_idx = shuffled_range[range(split_idx, len(latents))] + + np.savez(npz_train_file, images=images[train_idx], latents=latents[train_idx]) + np.savez(npz_test_file, images=images[test_idx], latents=latents[test_idx]) + + def __len__(self): + return len(self.latents) + + def __getitem__(self, idx): + image = torch.Tensor(self.images[idx]).unsqueeze(0) + latent = torch.Tensor(self.latents[idx]) + return (image, latent) diff --git a/WILDS/src/models/fmow.py b/WILDS/src/models/fmow.py new file mode 100644 index 0000000..372fd39 --- /dev/null +++ b/WILDS/src/models/fmow.py @@ -0,0 +1,72 @@ +import os +from copy import deepcopy + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torchvision.transforms as transforms +from torch.utils.data import DataLoader +from torchvision.models import densenet121 +from wilds.common.data_loaders import get_eval_loader +from wilds.datasets.fmow_dataset import FMoWDataset + +from .datasets import FMoW_Batched_Dataset + +IMG_HEIGHT = 224 +NUM_CLASSES = 62 + +class Model(nn.Module): + def __init__(self, args, weights): + super(Model, self).__init__() + self.num_classes = NUM_CLASSES + self.enc = densenet121(pretrained=True).features + self.classifier = nn.Linear(1024, self.num_classes) + if weights is not None: + self.load_state_dict(deepcopy(weights)) + + def reset_weights(self, weights): + self.load_state_dict(deepcopy(weights)) + + @staticmethod + def getDataLoaders(args, device): + dataset = FMoWDataset(root_dir=os.path.join(args.data_dir, 'wilds'), download=True) + # get all train data + transform = transforms.Compose([ + transforms.ToTensor(), + transforms.Normalize([0.485, 0.456, 0.406], + [0.229, 0.224, 0.225]) + ]) + train_sets = FMoW_Batched_Dataset(dataset, 'train', args.batch_size, transform, drop_last=not args.no_drop_last) + datasets = {} + for split in dataset.split_dict: + if split != 'train': + datasets[split] = dataset.get_subset(split, transform=transform) + + # get the loaders + kwargs = {'num_workers': args.num_workers, 'pin_memory': True, 'drop_last': False} \ + if device.type == "cuda" else {} + train_loaders = DataLoader(train_sets, batch_size=args.batch_size, shuffle=True, **kwargs) + tv_loaders = {} + for split, sep_dataset in datasets.items(): + tv_loaders[split] = get_eval_loader('standard', sep_dataset, batch_size=256, num_workers=args.num_workers) + return train_loaders, tv_loaders, dataset + + def forward(self, x, get_feat=False,frozen_mode=False): + + if frozen_mode: + self.enc.eval() + self.classifier.train() + with torch.no_grad(): + features = self.enc(x) + out = F.relu(features, inplace=True) + out = F.adaptive_avg_pool2d(out, (1, 1)) + out_features = torch.flatten(out, 1) + else: + features = self.enc(x) + out = F.relu(features, inplace=True) + out = F.adaptive_avg_pool2d(out, (1, 1)) + out_features = torch.flatten(out, 1) + out = self.classifier(out_features) + if get_feat: + return out, out_features + return out diff --git a/WILDS/src/models/iwildcam.py b/WILDS/src/models/iwildcam.py new file mode 100644 index 0000000..8d7c265 --- /dev/null +++ b/WILDS/src/models/iwildcam.py @@ -0,0 +1,99 @@ +import os +from copy import deepcopy + +import torch.nn as nn +import torchvision.transforms as transforms +from torch.utils.data import DataLoader +from torchvision.models import resnet50 +from wilds.common.data_loaders import get_eval_loader +from wilds.datasets.iwildcam_dataset import IWildCamDataset + +from .datasets import GeneralWilds_Batched_Dataset +import torch + +IMG_HEIGHT = 224 +NUM_CLASSES = 186 + +def get_image_base_transform_steps(dataset, target_resolution=None): + transform_steps = [] + + if dataset.original_resolution is not None and min( + dataset.original_resolution + ) != max(dataset.original_resolution): + crop_size = min(dataset.original_resolution) + transform_steps.append(transforms.CenterCrop(crop_size)) + + if target_resolution is not None: + transform_steps.append(transforms.Resize(target_resolution)) + + return transform_steps + +class Model(nn.Module): + def __init__(self, args, weights): + super(Model, self).__init__() + self.num_classes = NUM_CLASSES + pretrain_path=os.path.join(args.data_dir,'wilds',args.dataset) + if os.path.exists(pretrain_path): + resnet = resnet50(pretrained=False) + resnet.load_state_dict(torch.load(pretrain_path + f'/resnet50.rar')) + print(f"Load pretrained resnet from {pretrain_path}") + else: + resnet = resnet50(pretrained=True) + if not os.path.exists(pretrain_path): + os.makedirs(pretrain_path) + torch.save(resnet.state_dict(),pretrain_path+ f'/resnet50.rar') + print(f"Load pretrained resnet from url") + self.enc = nn.Sequential(*list(resnet.children())[:-1]) # remove fc layer + self.fc = nn.Linear(2048, self.num_classes) + if weights is not None: + self.load_state_dict(deepcopy(weights)) + + def reset_weights(self, weights): + self.load_state_dict(deepcopy(weights)) + + + @staticmethod + def getDataLoaders(args, device): + dataset = IWildCamDataset(root_dir=os.path.join(args.data_dir, 'wilds'), download=True) + # get all train data + transform = transforms.Compose([ + # transforms.Resize((224, 224)), + transforms.Resize((448, 448)), + transforms.ToTensor(), + transforms.Normalize([0.485, 0.456, 0.406], + [0.229, 0.224, 0.225]) + ]) + train_data = dataset.get_subset('train', transform=transform) + # separate into subsets by distribution + train_sets = GeneralWilds_Batched_Dataset(train_data, args.batch_size, domain_idx=0, drop_last=not args.no_drop_last) + # take subset of test and validation, making sure that only labels appeared in train + # are included + datasets = {} + for split in dataset.split_dict: + if split != 'train': + datasets[split] = dataset.get_subset(split, transform=transform) + + # get the loaders + kwargs = {'num_workers': args.num_workers, 'pin_memory': True, 'drop_last': False} \ + if device.type == "cuda" else {} + train_loaders = DataLoader(train_sets, batch_size=args.batch_size, shuffle=True, **kwargs) + tv_loaders = {} + for split, sep_dataset in datasets.items(): + tv_loaders[split] = get_eval_loader('standard', sep_dataset, batch_size=256, num_workers=args.num_workers) + return train_loaders, tv_loaders, dataset + + def forward(self, x,get_feat=False,frozen_mode=False): + # x = x.expand(-1, 3, -1, -1) # reshape MNIST from 1x32x32 => 3x32x32 + if len(x.shape) == 3: + x.unsqueeze_(0) + if frozen_mode: + self.enc.eval() + self.fc.train() + with torch.no_grad(): + e = self.enc(x) + else: + e = self.enc(x) + out = self.fc(e.squeeze(-1).squeeze(-1)) + if get_feat: + return out, e.squeeze(-1).squeeze(-1) + return out diff --git a/WILDS/src/models/poverty.py b/WILDS/src/models/poverty.py new file mode 100644 index 0000000..1d82056 --- /dev/null +++ b/WILDS/src/models/poverty.py @@ -0,0 +1,87 @@ +import os +from copy import deepcopy +import torch +import torch.nn as nn +import torchvision.transforms as transforms +from torch.utils.data import DataLoader +from wilds.common.data_loaders import get_eval_loader +from wilds.datasets.poverty_dataset import PovertyMapDataset + +from .resnet_multispectral import ResNet18 +from .datasets import Poverty_Batched_Dataset + +IMG_HEIGHT = 224 +NUM_CLASSES = 1 + +def initialize_poverty_train_transform(): + """Adapted from the Wilds library, available at: https://github.com/p-lambda/wilds""" + transforms_ls = [ + transforms.ToPILImage(), + transforms.RandomHorizontalFlip(), + transforms.RandomVerticalFlip(), + transforms.ColorJitter(brightness=0.8, contrast=0.8, saturation=0.8, hue=0.1), + transforms.ToTensor()] + rgb_transform = transforms.Compose(transforms_ls) + + def transform_rgb(img): + # bgr to rgb and back to bgr + img[:3] = rgb_transform(img[:3][[2,1,0]])[[2,1,0]] + return img + transform = transforms.Lambda(lambda x: transform_rgb(x)) + return transform + + +class Model(nn.Module): + def __init__(self, args, weights): + super(Model, self).__init__() + self.num_classes = NUM_CLASSES + + self.enc = ResNet18(num_classes=1, num_channels=8) + if weights is not None: + self.load_state_dict(deepcopy(weights)) + + def reset_weights(self, weights): + self.load_state_dict(deepcopy(weights)) + + + + @staticmethod + def getDataLoaders(args, device): + kwargs = {'no_nl': False, + 'fold': ['A', 'B', 'C', 'D', 'E'][args.seed], + # 'oracle_training_set': False, + 'use_ood_val': True} + dataset = PovertyMapDataset(root_dir=os.path.join(args.data_dir, 'wilds'), + download=True, **kwargs) + # get all train data + # transform = initialize_poverty_train_transform() + # In latest wilds example code, no transfromation is applied + transform = transforms.Compose([]) + + train_sets = Poverty_Batched_Dataset(dataset, 'train', args.batch_size, transform, drop_last=not args.no_drop_last) + datasets = {} + for split in dataset.split_dict: + if split != 'train': + datasets[split] = dataset.get_subset(split, transform=transform) + print(split, len(datasets[split])) + + kwargs = {'num_workers': args.num_workers, 'pin_memory': True, 'drop_last': False, 'shuffle': True} \ + if device.type == "cuda" else {} + train_loaders = DataLoader(train_sets, batch_size=args.batch_size, **kwargs) + tv_loaders = {} + for split, sep_dataset in datasets.items(): + tv_loaders[split] = get_eval_loader('standard', sep_dataset, batch_size=256, num_workers=args.num_workers) + return train_loaders, tv_loaders, dataset + + def forward(self, x, get_feat=False,frozen_mode=False): + if frozen_mode: + self.enc.eval() + self.enc.fc.train() + with torch.no_grad(): + pred, feat = self.enc(x, with_feats=True) + pred = self.enc.fc(feat) + if get_feat: + return pred, feat + else: + return pred + return self.enc(x,with_feats=get_feat) diff --git a/WILDS/src/models/resnet_multispectral.py b/WILDS/src/models/resnet_multispectral.py new file mode 100644 index 0000000..d2f6b19 --- /dev/null +++ b/WILDS/src/models/resnet_multispectral.py @@ -0,0 +1,248 @@ +# Adapted from the WILDS library +import torch +import torch.nn as nn + + +def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1): + """3x3 convolution with padding""" + return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, + padding=dilation, groups=groups, bias=False, dilation=dilation) + + +def conv1x1(in_planes, out_planes, stride=1): + """1x1 convolution""" + return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False) + + +class BasicBlock(nn.Module): + expansion = 1 + __constants__ = ['downsample'] + + def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1, + base_width=64, dilation=1, norm_layer=None): + super(BasicBlock, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2d + if groups != 1 or base_width != 64: + raise ValueError('BasicBlock only supports groups=1 and base_width=64') + if dilation > 1: + raise NotImplementedError("Dilation > 1 not supported in BasicBlock") + # Both self.conv1 and self.downsample layers downsample the input when stride != 1 + self.conv1 = conv3x3(inplanes, planes, stride) + self.bn1 = norm_layer(planes) + self.relu = nn.ReLU(inplace=True) + self.conv2 = conv3x3(planes, planes) + self.bn2 = norm_layer(planes) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + identity = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + out = self.relu(out) + + return out + + +class Bottleneck(nn.Module): + expansion = 4 + __constants__ = ['downsample'] + + def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1, + base_width=64, dilation=1, norm_layer=None): + super(Bottleneck, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2d + width = int(planes * (base_width / 64.)) * groups + # Both self.conv2 and self.downsample layers downsample the input when stride != 1 + self.conv1 = conv1x1(inplanes, width) + self.bn1 = norm_layer(width) + self.conv2 = conv3x3(width, width, stride, groups, dilation) + self.bn2 = norm_layer(width) + self.conv3 = conv1x1(width, planes * self.expansion) + self.bn3 = norm_layer(planes * self.expansion) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + identity = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + out = self.relu(out) + + return out + + +class ResNet(nn.Module): + + def __init__(self, block, layers, num_classes=1000, zero_init_residual=False, + groups=1, width_per_group=64, replace_stride_with_dilation=None, + norm_layer=None, num_channels=3): + super(ResNet, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2d + self._norm_layer = norm_layer + + self.inplanes = 64 + self.dilation = 1 + if replace_stride_with_dilation is None: + # each element in the tuple indicates if we should replace + # the 2x2 stride with a dilated convolution instead + replace_stride_with_dilation = [False, False, False] + if len(replace_stride_with_dilation) != 3: + raise ValueError("replace_stride_with_dilation should be None " + "or a 3-element tuple, got {}".format(replace_stride_with_dilation)) + self.groups = groups + self.base_width = width_per_group + self.conv1 = nn.Conv2d(num_channels, self.inplanes, kernel_size=7, stride=2, padding=3, + bias=False) + self.bn1 = norm_layer(self.inplanes) + self.relu = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + self.layer1 = self._make_layer(block, 64, layers[0]) + self.layer2 = self._make_layer(block, 128, layers[1], stride=2, + dilate=replace_stride_with_dilation[0]) + self.layer3 = self._make_layer(block, 256, layers[2], stride=2, + dilate=replace_stride_with_dilation[1]) + self.layer4 = self._make_layer(block, 512, layers[3], stride=2, + dilate=replace_stride_with_dilation[2]) + self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) + if num_classes is not None: + self.fc = nn.Linear(512 * block.expansion, num_classes) + self.d_out = num_classes + else: + self.fc = None + self.d_out = 512 * block.expansion + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') + elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)): + nn.init.constant_(m.weight, 1) + nn.init.constant_(m.bias, 0) + + # Zero-initialize the last BN in each residual branch, + # so that the residual branch starts with zeros, and each residual block behaves like an identity. + # This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677 + if zero_init_residual: + for m in self.modules(): + if isinstance(m, Bottleneck): + nn.init.constant_(m.bn3.weight, 0) + elif isinstance(m, BasicBlock): + nn.init.constant_(m.bn2.weight, 0) + + def _make_layer(self, block, planes, blocks, stride=1, dilate=False): + norm_layer = self._norm_layer + downsample = None + previous_dilation = self.dilation + if dilate: + self.dilation *= stride + stride = 1 + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + conv1x1(self.inplanes, planes * block.expansion, stride), + norm_layer(planes * block.expansion), + ) + + layers = [] + layers.append(block(self.inplanes, planes, stride, downsample, self.groups, + self.base_width, previous_dilation, norm_layer)) + self.inplanes = planes * block.expansion + for _ in range(1, blocks): + layers.append(block(self.inplanes, planes, groups=self.groups, + base_width=self.base_width, dilation=self.dilation, + norm_layer=norm_layer)) + + return nn.Sequential(*layers) + + def get_feats(self, x, layer=4): + # See note [TorchScript super()] + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + x = self.maxpool(x) + + x = self.layer1(x) + if layer == 1: + return x + x = self.layer2(x) + if layer == 2: + return x + x = self.layer3(x) + if layer == 3: + return x + x = self.layer4(x) + + x = self.avgpool(x) + x = torch.flatten(x, 1) + return x + + + def _forward_impl(self, x, with_feats=False): + x = feats = self.get_feats(x) + if self.fc is not None: + x = self.fc(feats) + + if with_feats: + return x, feats + else: + return x + + def forward(self, x, with_feats=False): + return self._forward_impl(x, with_feats) + + +class ResNet18(ResNet): + def __init__(self, num_classes=10, num_channels=3): + super().__init__( + BasicBlock, [2, 2, 2, 2], num_classes=num_classes, num_channels=num_channels) + +class ResNet34(ResNet): + def __init__(self, num_classes=10, num_channels=3): + super().__init__( + BasicBlock, [3, 4, 6, 3], num_classes=num_classes, num_channels=num_channels) + +class ResNet50(ResNet): + def __init__(self, num_classes=10, num_channels=3): + super().__init__( + Bottleneck, [3, 4, 23, 3], num_classes=num_classes, num_channels=num_channels) + +class ResNet101(ResNet): + def __init__(self, num_classes=10, num_channels=3): + super().__init__( + Bottleneck, [3, 4, 23, 3], num_classes=num_classes, num_channels=num_channels) + +class ResNet152(ResNet): + def __init__(self, num_classes=10, num_channels=3): + super().__init__( + Bottleneck, [3, 8, 36, 3], num_classes=num_classes, num_channels=num_channels) + + +DEPTH_TO_MODEL = {18: ResNet18, 34: ResNet34, 50: ResNet50, 101: ResNet101, 152: ResNet152} + diff --git a/WILDS/src/models/rxrx.py b/WILDS/src/models/rxrx.py new file mode 100644 index 0000000..6f9abbd --- /dev/null +++ b/WILDS/src/models/rxrx.py @@ -0,0 +1,110 @@ +import numpy as np +import os +from copy import deepcopy + +import torch +import torch.nn as nn +import torchvision.transforms as transforms +import torchvision.transforms.functional as TF +from torch.utils.data import DataLoader +from torchvision.models import resnet50 +from wilds.common.data_loaders import get_eval_loader +try: + from wilds.datasets.rxrx1_dataset import RxRx1Dataset +except Exception as e: + print("RxRx1 Dataset not supported") + +from .datasets import GeneralWilds_Batched_Dataset + +IMG_HEIGHT = 224 +NUM_CLASSES = 1139 + +def initialize_rxrx1_transform(is_training): + def standardize(x: torch.Tensor) -> torch.Tensor: + mean = x.mean(dim=(1, 2)) + std = x.std(dim=(1, 2)) + std[std == 0.] = 1. + return TF.normalize(x, mean, std) + t_standardize = transforms.Lambda(lambda x: standardize(x)) + + angles = [0, 90, 180, 270] + def random_rotation(x: torch.Tensor) -> torch.Tensor: + angle = angles[torch.randint(low=0, high=len(angles), size=(1,))] + if angle > 0: + x = TF.rotate(x, angle) + return x + t_random_rotation = transforms.Lambda(lambda x: random_rotation(x)) + + if is_training: + transforms_ls = [ + t_random_rotation, + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + t_standardize, + ] + else: + transforms_ls = [ + transforms.ToTensor(), + t_standardize, + ] + transform = transforms.Compose(transforms_ls) + return transform + +class Model(nn.Module): + def __init__(self, args, weights): + super(Model, self).__init__() + self.num_classes = NUM_CLASSES + resnet = resnet50(pretrained=True) + self.enc = nn.Sequential(*list(resnet.children())[:-1]) # remove fc layer + self.fc = nn.Linear(2048, self.num_classes) + if weights is not None: + self.load_state_dict(deepcopy(weights)) + + def reset_weights(self, weights): + self.load_state_dict(deepcopy(weights)) + + + @staticmethod + def getDataLoaders(args, device): + dataset = RxRx1Dataset(root_dir=os.path.join(args.data_dir, 'wilds'), download=True) + + # initialize transform + train_transform = initialize_rxrx1_transform(is_training=True) + eval_transform = initialize_rxrx1_transform(is_training=False) + + # get all train data + train_data = dataset.get_subset('train', transform=train_transform) + + # separate into subsets by distribution + train_sets = GeneralWilds_Batched_Dataset(train_data, args.batch_size, domain_idx=1, drop_last=not args.no_drop_last) + # take subset of test and validation, making sure that only labels appeared in train + # are included + datasets = {} + for split in dataset.split_dict: + if split != 'train': + datasets[split] = dataset.get_subset(split, transform=eval_transform) + + # get the loaders + kwargs = {'num_workers': args.num_workers, 'pin_memory': True, 'drop_last': False} \ + if device.type == "cuda" else {} + train_loaders = DataLoader(train_sets, batch_size=args.batch_size, shuffle=True, **kwargs) + tv_loaders = {} + for split, sep_dataset in datasets.items(): + tv_loaders[split] = get_eval_loader('standard', sep_dataset, batch_size=256, num_workers=args.num_workers) + return train_loaders, tv_loaders, dataset + + def forward(self, x,get_feat=False,frozen_mode=False): + # x = x.expand(-1, 3, -1, -1) # reshape MNIST from 1x32x32 => 3x32x32 + if len(x.shape) == 3: + x.unsqueeze_(0) + if frozen_mode: + self.enc.eval() + self.fc.train() + with torch.no_grad(): + e = self.enc(x) + else: + e = self.enc(x) + out = self.fc(e.squeeze(-1).squeeze(-1)) + if get_feat: + return out, e.squeeze(-1).squeeze(-1) + return out diff --git a/WILDS/src/pair.py b/WILDS/src/pair.py new file mode 100644 index 0000000..947f69d --- /dev/null +++ b/WILDS/src/pair.py @@ -0,0 +1,452 @@ +import copy +import imp +from pickletools import optimize +import torch +from torch.optim.optimizer import Optimizer, required +from torch.autograd import Variable +import traceback +import torch.nn.functional as F +from torch.optim import SGD + +class PAIR(Optimizer): + r""" + Implements Pareto Invariant Risk Minimization (PAIR) algorithm. + It is proposed in the ICLR 2023 paper + `Pareto Invariant Risk Minimization: Towards Mitigating the Optimization Dilemma in Out-of-Distribution Generalization` + https://arxiv.org/abs/2206.07766 . + + Arguments: + params (iterable): iterable of parameters to optimize or dicts defining parameter groups + optimizer (pytorch optim): inner optimizer + balancer (str, optional): indicates which MOO solver to use + preference (list[float], optional): preference of the objectives + eps (float, optional): precision up to the preference (default: 1e-04) + coe (float, optional): L2 regularization weight onto the yielded objective weights (default: 0) + """ + + def __init__(self, params, optimizer=required, balancer="EPO",preference=[1e-8,1-1e-8], eps=1e-4, coe=0, verbose=False): + # TODO: parameter validty checking + if eps < 0.0: + raise ValueError("Invalid epsilon value: {}".format(eps)) + for _pp in preference: + if _pp < 0.0: + raise ValueError("Invalid preference: {}".format(preference)) + + self.optimizer = optimizer + if type(preference) == list: + preference = np.array(preference) + self.preference = preference + + self.descent = 0 + self.losses = [] + self.params = params + if balancer.lower() == "epo": + self.balancer = EPO(len(self.preference),self.preference,eps=eps,coe=coe,verbose=verbose) + elif balancer.lower() == "sepo": + self.balancer = SEPO(len(self.preference),self.preference,eps=eps,coe=coe,verbose=verbose) + else: + raise NotImplementedError("Nrot supported balancer") + defaults = dict(balancer=balancer, preference=self.preference, eps=eps) + super(PAIR, self).__init__(params, defaults) + + + def __setstate__(self, state): + super(PAIR, self).__setstate__(state) + + def set_losses(self,losses): + self.losses = losses + + def step(self, closure=None): + r"""Performs a single optimization step. + Arguments: + closure (callable, optional): A closure that reevaluates the model + and returns the loss. + """ + if len(self.losses) == 0: + self.optimizer.step() + alphas = np.zeros(len(self.preference)) + alphas[0] = 1 + return -1, 233, alphas + else: + losses = self.losses + if closure is not None: + losses = closure() + + pair_loss = 0 + mu_rl = 0 + alphas = 0 + + grads = [] + for cur_loss in losses: + self.optimizer.zero_grad() + cur_loss.backward(retain_graph=True) + cur_grad = [] + for group in self.param_groups: + for param in group['params']: + if param.grad is not None: + cur_grad.append(Variable(param.grad.data.clone().flatten(), requires_grad=False)) + grads.append(torch.cat(cur_grad)) + + G = torch.stack(grads) + if self.get_grad_sim: + grad_sim = get_grad_sim(G,losses,preference=self.preference,is_G=True) + GG = G @ G.T + moo_losses = np.stack([l.item() for l in losses]) + reset_optimizer = False + try: + # Calculate the alphas from the LP solver + alpha, mu_rl, reset_optimizer = self.balancer.get_alpha(moo_losses, G=GG.cpu().numpy(), C=True,get_mu=True) + if self.balancer.last_move == "dom": + self.descent += 1 + print("dom") + except Exception as e: + print(traceback.format_exc()) + alpha = None + if alpha is None: # A patch for the issue in cvxpy + alpha = self.preference / np.sum(self.preference) + + scales = torch.from_numpy(alpha).float().to(losses[-1].device) + pair_loss = scales.dot(losses) + if reset_optimizer: + self.optimizer.param_groups[0]["lr"]/=5 + # self.optimizer = torch.optim.Adam(self.params,lr=self.optimizer.param_groups[0]["lr"]/5) + self.optimizer.zero_grad() + pair_loss.backward() + self.optimizer.step() + + return pair_loss, moo_losses, mu_rl, alpha + + + +import numpy as np +import cvxpy as cp +import cvxopt + +class EPO(object): + r""" + The original EPO solver proposed in ICML2020 + https://proceedings.mlr.press/v119/mahapatra20a.html + """ + def __init__(self, m, r, eps=1e-4, coe=0, verbose=False): + # self.solver = cp.GLPK + self.solver = cp.GUROBI + # cvxopt.glpk.options["msg_lev"] = "GLP_MSG_OFF" + self.m = m + self.r = r/np.sum(r) + self.eps = eps + self.last_move = None + self.a = cp.Parameter(m) # Adjustments + self.C = cp.Parameter((m, m)) # C: Gradient inner products, G^T G + self.Ca = cp.Parameter(m) # d_bal^TG + self.rhs = cp.Parameter(m) # RHS of constraints for balancing + + self.alpha = cp.Variable(m) # Variable to optimize + self.last_alpha = np.zeros_like(r)-1 + self.coe = coe + + obj_bal = cp.Maximize(self.alpha @ self.Ca) # objective for balance + constraints_bal = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Simplex + self.C @ self.alpha >= self.rhs] + self.prob_bal = cp.Problem(obj_bal, constraints_bal) # LP balance + + obj_dom = cp.Maximize(cp.sum(self.alpha @ self.C)-self.coe*cp.sum_squares(self.alpha-self.last_alpha)) # obj for descent + constraints_dom = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Restrict + self.alpha @ self.Ca >= -cp.neg(cp.max(self.Ca)), + self.C @ self.alpha >= 0] + self.prob_dom = cp.Problem(obj_dom, constraints_dom) # LP dominance + + + self.gamma = 0 # Stores the latest Optimum value of the LP problem + self.mu_rl = 0 # Stores the latest non-uniformity + + self.verbose = verbose + + + def get_alpha(self, l, G, r=None, C=False, get_mu=False): + """calculate weights for all objectives given the gradient information + + Args: + l (ndarray): the values of objective losses + G (ndarray): inner products of the gradients of each objective loss w.r.t. params + r (ndarray, optional): adopt this preference if specified + C (bool, optional): True if the input gradients are inner products + get_mu (bool, optional): return detailed information if True. + + Returns: + alpha: the objective weights + mu_rl (optional): the optimal value to the LP + reset_optimizer (optional): whether to reset the inner optimizer + """ + r = self.r if r is None else r + assert len(l) == len(G) == len(r) == self.m, "length != m" + l_hat, rl, self.mu_rl, self.a.value = self.adjustments(l, r) + reset_optimizer = False + self.C.value = G if C else G @ G.T + self.Ca.value = self.C.value @ self.a.value + + if self.last_alpha.sum() is None: + self.last_alpha = np.array(r) + if self.mu_rl > self.eps: + J = self.Ca.value > 0 + J_star_idx = np.where(rl == np.max(rl))[0] + self.rhs.value = self.Ca.value.copy() + # it's equivalent to setting no constraints to objectives in J + # as maximize alpha^TCa would trivially satisfy the non-negativity + self.rhs.value[J] = -np.inf + self.rhs.value[J_star_idx] = 0 + + self.gamma = self.prob_bal.solve(solver=self.solver, verbose=False) + self.last_move = "bal" + + if self.verbose: + test_alpha = np.ones_like(self.a.value)/self.m + print(self.last_alpha,self.C.value,self.Ca.value,self.rhs.value) + print(self.gamma,test_alpha@self.Ca.value, self.alpha.value @ self.C.value) + print(self.gamma,self.coe*np.linalg.norm(self.alpha.value-self.last_alpha)**2) + + else: + self.gamma = self.prob_dom.solve(solver=self.solver, verbose=False) + self.last_move = "dom" + self.last_alpha = np.array(self.alpha.value) + + if get_mu: + return self.alpha.value, self.mu_rl, reset_optimizer + + return self.alpha.value + + + def mu(self, rl, normed=False): + if len(np.where(rl < 0)[0]): + raise ValueError(f"rl<0 \n rl={rl}") + return None + m = len(rl) + l_hat = rl if normed else rl / rl.sum() + eps = np.finfo(rl.dtype).eps + l_hat = l_hat[l_hat > eps] + return np.sum(l_hat * np.log(l_hat * m)) + + + def adjustments(self, l, r=1): + m = len(l) + rl = r * l + + l_hat = rl / rl.sum() + mu_rl = self.mu(l_hat, normed=True) + uniformity_div = np.log(l_hat * m) - mu_rl + div_r = np.array(r) + a = div_r * uniformity_div + + if self.verbose: + print(a, rl, div_r, uniformity_div, l_hat, a.dot(l)) + return l_hat, rl, mu_rl, a + + +class SEPO(object): + r""" + A smoothed variant of EPO, with two adjustments for unrobust OOD objectives: + a) normalization: unrobust OOD objective can yield large loss values that dominate the solutions of the LP, + hence we adopt the normalized OOD losses in the LP to resolve the issue + b) regularization: solutions yielded by the LP can change sharply among steps, especially when switching descending phases + hence we incorporate a L2 regularization in the LP to resolve the issue + """ + def __init__(self, m, r, eps=1e-4, coe=0, verbose=False): + # self.solver = cp.GLPK + self.solver = cp.GUROBI + # cvxopt.glpk.options["msg_lev"] = "GLP_MSG_OFF" + self.m = m + self.r = r/np.sum(r) + self.eps = eps + self.last_move = None + self.a = cp.Parameter(m) # Adjustments + self.C = cp.Parameter((m, m)) # C: Gradient inner products, G^T G + self.Ca = cp.Parameter(m) # d_bal^TG + self.rhs = cp.Parameter(m) # RHS of constraints for balancing + + self.alpha = cp.Variable(m) # Variable to optimize + self.last_alpha = np.zeros_like(r)-1 + self.coe = coe + + obj_bal = cp.Maximize(self.alpha @ self.Ca-self.coe*cp.sum_squares(self.alpha-self.last_alpha)) # objective for balance + obj_bal_orig = cp.Maximize(self.alpha @ self.Ca) # objective for balance + constraints_bal = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Simplex + self.C @ self.alpha >= self.rhs] + self.prob_bal = cp.Problem(obj_bal, constraints_bal) # LP balance + self.prob_bal_orig = cp.Problem(obj_bal_orig, constraints_bal) # LP balance + + obj_dom = cp.Maximize(cp.sum(self.alpha @ self.C)-self.coe*cp.sum_squares(self.alpha-self.last_alpha)) # obj for descent + constraints_res = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Restrict + self.alpha @ self.Ca >= -cp.neg(cp.max(self.Ca)), + self.C @ self.alpha >= 0] + constraints_rel = [self.alpha >= 0, cp.sum(self.alpha) == 1, # Relaxed + self.C @ self.alpha >= 0] + self.prob_dom = cp.Problem(obj_dom, constraints_res) # LP dominance + self.prob_rel = cp.Problem(obj_dom, constraints_rel) # LP dominance + + self.gamma = 0 # Stores the latest Optimum value of the LP problem + self.mu_rl = 0 # Stores the latest non-uniformity + + self.verbose = verbose + + + def get_alpha(self, l, G, r=None, C=False, get_mu=False): + """calculate weights for all objectives given the gradient information + + Args: + l (ndarray): the values of objective losses + G (ndarray): inner products of the gradients of each objective loss w.r.t. params + r (ndarray, optional): adopt this preference if specified + C (bool, optional): True if the input gradients are inner products + get_mu (bool, optional): return detailed information if True. + + Returns: + alpha: the objective weights + mu_rl (optional): the optimal value to the LP + reset_optimizer (optional): whether to reset the inner optimizer + """ + r = self.r if r is None else r + assert len(l) == len(G) == len(r) == self.m, "length != m" + l_hat, rl, self.mu_rl, self.a.value = self.adjustments(l, r) + reset_optimizer = False + if self.mu_rl <= 0.1: + self.r[0]=max(1e-15,self.r[0]/10000) + self.r = self.r/self.r.sum() + print(f"pua preference {self.r}") + l_hat, rl, self.mu_rl, self.a.value = self.adjustments(l, r) + + + a_norm = np.linalg.norm(self.a.value) + G_norm = np.linalg.norm(G,axis=1) + Ga = G.T @ self.a.value + self.C.value = G if C else G/np.expand_dims(G_norm,axis=1) @ G.T/a_norm + self.Ca.value = G/np.expand_dims(G_norm,axis=1) @ Ga.T/a_norm + + if self.last_alpha.sum() is None: + self.last_alpha = np.array(r) + if self.mu_rl > self.eps: + J = self.Ca.value > 0 + + J_star_idx = np.where(rl == np.max(rl))[0] + self.rhs.value = self.Ca.value.copy() + # it's equivalent to setting no constraints to objectives in J + # as maximize alpha^TCa would trivially satisfy the non-negativity + self.rhs.value[J] = -np.inf # Not efficient; but works. + self.rhs.value[J_star_idx] = max(0,self.Ca.value[J_star_idx]/2) + + if self.last_alpha.sum()<0: + self.gamma = self.prob_bal_orig.solve(solver=self.solver, verbose=False) + else: + self.gamma = self.prob_bal.solve(solver=self.solver, verbose=False) + + self.last_move = "bal" + + if self.verbose: + test_alpha = np.ones_like(self.a.value)/self.m + print(self.last_alpha,self.C.value,self.Ca.value,self.rhs.value) + print(self.gamma,test_alpha@self.Ca.value, self.alpha.value @ self.C.value) + print(self.gamma,self.coe*np.linalg.norm(self.alpha.value-self.last_alpha)**2) + else: + self.gamma = self.prob_dom.solve(solver=self.solver, verbose=False) + self.last_move = "dom" + self.last_alpha = np.array(self.alpha.value) + + if get_mu: + return self.alpha.value, self.mu_rl, reset_optimizer + + return self.alpha.value + + + def mu(self, rl, normed=False): + if len(np.where(rl < 0)[0]): + raise ValueError(f"rl<0 \n rl={rl}") + return None + m = len(rl) + l_hat = rl if normed else rl / rl.sum() + eps = np.finfo(rl.dtype).eps + l_hat = l_hat[l_hat > eps] + return np.sum(l_hat * np.log(l_hat * m)) + + + def adjustments(self, l, r=1): + m = len(l) + rl = r * l + + l_hat = rl / rl.sum() + mu_rl = self.mu(l_hat, normed=True) + uniformity_div = np.log(l_hat * m) - mu_rl + div_r = np.array(r) + a = div_r * uniformity_div + + if self.verbose: + print(a, rl, div_r, uniformity_div, l_hat, a.dot(l)) + return l_hat, rl, mu_rl, a + + +def getNumParams(params): + numParams, numTrainable = 0, 0 + for param in params: + npParamCount = np.prod(param.data.shape) + numParams += npParamCount + if param.requires_grad: + numTrainable += npParamCount + return numParams, numTrainable + +def get_kl_div(losses, preference): + pair_score = losses.dot(preference) + return pair_score + +def pair_selection(losses,val_accs,test_accs,anneal_iter=0,val_acc_bar=-1,pood=None): + + losses = losses[anneal_iter:] + val_accs = val_accs[anneal_iter:] + test_accs = test_accs[anneal_iter:] + if val_acc_bar < 0: + val_acc_bar = (np.max(val_accs)-np.min(val_accs))*0.05+np.min(val_accs) + + try: + preference_base = 10**max(-12,int(np.log10(np.mean(losses[:,-1]))-2)) + except Exception as e: + print(e) + preference_base = 1e-12 + if len(losses[0])==2: + preference = np.array([preference_base,1]) + elif len(losses[0])==4: + preference = np.array([1e-12,1e-4,1e-2,1]) + elif len(losses[0])==5: + preference = np.array([1e-12,1e-6,1e-4,1e-2,1]) + else: + preference = np.array([1e-12,1e-2,1]) + + if pood is not None: + preference = pood + print(f"Use preference: {preference}, validation acc bar: {val_acc_bar}") + + pair_score = np.array([get_kl_div(l,preference) if a>=val_acc_bar else 1e9 for (a,l) in zip(val_accs,losses)]) + sel_idx = np.argmin(pair_score) + return sel_idx+anneal_iter, val_accs[sel_idx], test_accs[sel_idx] + +def get_grad_sim(params,losses,preference=None,is_G=False,cosine=True): + num_ood_losses = len(losses)-1 + if is_G: + G = params + else: + pesudo_opt = SGD(params,lr=1e-6) + grads = [] + for cur_loss in losses: + pesudo_opt.zero_grad() + cur_loss.backward(retain_graph=True) + cur_grad = [] + for param in params: + if param.grad is not None: + cur_grad.append(Variable(param.grad.data.clone().flatten(), requires_grad=False)) + # print(torch.cat(cur_grad).sum()) + grads.append(torch.cat(cur_grad)) + G = torch.stack(grads) + if cosine: + G = F.normalize(G,dim=1) + GG = (G @ G.T).cpu() + if preference is not None: + G_weights = preference[1:]/np.sum(preference[1:]) + else: + G_weights = np.ones(num_ood_losses)/num_ood_losses + grad_sim =G_weights.dot(GG[0,1:]) + return grad_sim.item() diff --git a/WILDS/src/scheduler.py b/WILDS/src/scheduler.py new file mode 100644 index 0000000..b0ddc02 --- /dev/null +++ b/WILDS/src/scheduler.py @@ -0,0 +1,94 @@ +from torch.optim.lr_scheduler import LambdaLR, ReduceLROnPlateau, StepLR, CosineAnnealingLR, MultiStepLR + +def initialize_scheduler(config, optimizer, n_train_steps): + # construct schedulers + if config.scheduler is None: + return None + elif config.scheduler == 'linear_schedule_with_warmup': + from transformers import get_linear_schedule_with_warmup + scheduler = get_linear_schedule_with_warmup( + optimizer, + num_training_steps=n_train_steps, + **config.scheduler_kwargs) + step_every_batch = True + use_metric = False + elif config.scheduler == 'cosine_schedule_with_warmup': + from transformers import get_cosine_schedule_with_warmup + scheduler = get_cosine_schedule_with_warmup( + optimizer, + num_training_steps=n_train_steps, + **config.scheduler_kwargs) + step_every_batch = True + use_metric = False + elif config.scheduler=='ReduceLROnPlateau': + assert config.scheduler_metric_name, f'scheduler metric must be specified for {config.scheduler}' + scheduler = ReduceLROnPlateau( + optimizer, + **config.scheduler_kwargs) + step_every_batch = False + use_metric = True + elif config.scheduler == 'StepLR': + scheduler = StepLR(optimizer, **config.scheduler_kwargs) + step_every_batch = False + use_metric = False + elif config.scheduler == 'FixMatchLR': + scheduler = LambdaLR( + optimizer, + lambda x: (1.0 + 10 * float(x) / n_train_steps) ** -0.75 + ) + step_every_batch = True + use_metric = False + elif config.scheduler == 'MultiStepLR': + scheduler = MultiStepLR(optimizer, **config.scheduler_kwargs) + step_every_batch = False + use_metric = False + else: + raise ValueError(f'Scheduler: {config.scheduler} not supported.') + + # add an step_every_batch field + scheduler.step_every_batch = step_every_batch + scheduler.use_metric = use_metric + return scheduler + +def step_scheduler(scheduler, metric=None): + if isinstance(scheduler, ReduceLROnPlateau): + assert metric is not None + scheduler.step(metric) + else: + scheduler.step() + +class LinearScheduleWithWarmupAndThreshold(): + """ + Linear scheduler with warmup and threshold for non lr parameters. + Parameters is held at 0 until some T1, linearly increased until T2, and then held + at some max value after T2. + Designed to be called by step_scheduler() above and used within Algorithm class. + Args: + - last_warmup_step: aka T1. for steps [0, T1) keep param = 0 + - threshold_step: aka T2. step over period [T1, T2) to reach param = max value + - max value: end value of the param + """ + def __init__(self, max_value, last_warmup_step=0, threshold_step=1, step_every_batch=False): + self.max_value = max_value + self.T1 = last_warmup_step + self.T2 = threshold_step + assert (0 <= self.T1) and (self.T1 < self.T2) + + # internal tracker of which step we're on + self.current_step = 0 + self.value = 0 + + # required fields called in Algorithm when stepping schedulers + self.step_every_batch = step_every_batch + self.use_metric = False + + def step(self): + """This function is first called AFTER step 0, so increment first to set value for next step""" + self.current_step += 1 + if self.current_step < self.T1: + self.value = 0 + elif self.current_step < self.T2: + self.value = (self.current_step - self.T1) / (self.T2 - self.T1) * self.max_value + else: + self.value = self.max_value + diff --git a/WILDS/src/utils.py b/WILDS/src/utils.py new file mode 100644 index 0000000..3cd1936 --- /dev/null +++ b/WILDS/src/utils.py @@ -0,0 +1,262 @@ +import os +import random +import shutil +import sys +import operator +from numbers import Number +from collections import OrderedDict + +import torch +from torch import nn +from torch.utils.data import Dataset +import numpy as np + +def set_seed(seed): + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.backends.cudnn.benchmark = False + torch.backends.cudnn.deterministic = True + # torch.cuda.manual_seed_all(seed) # canceled as we only use one gpu + +def get_preference(preference_choice): + # erm, irm, v-rex + if preference_choice==1: + r = 1e-12 + r2 = 1e10 + r_l2 = r*r2 + n_tasks = 1+2 + preference = np.array([r,r_l2,(1-r-r_l2)]) + preference = np.array([r,1-r-r2*r,r2*r]) + preference = np.array([r,(1-r)/2,(1-r)/2]) + elif preference_choice==2: + r = 1e-12 + r2 = 1e10 + r_l2 = r*r2 + n_tasks = 1+2 + # preference = np.array([r,r_l2,(1-r-r_l2)]) + preference = np.array([r,1-r-r2*r,r2*r]) + # preference = np.array([r,(1-r)/2,(1-r)/2]) + elif preference_choice==3: + r = 1e-12 + r2 = 1e8 + r_l2 = r*r2 + n_tasks = 1+2 + preference = np.array([r,r_l2,(1-r-r_l2)]) + # preference = np.array([r,1-r-r2*r,r2*r]) + # preference = np.array([r,(1-r)/2,(1-r)/2]) + elif preference_choice==4: + r = 1e-12 + r2 = 1e6 + r_l2 = r*r2 + n_tasks = 1+2 + preference = np.array([r,r_l2,(1-r-r_l2)]) + elif preference_choice==11: + r = 1e-6 + r2 = 1e4 + r_l2 = r*r2 + preference = np.array([r,r_l2,(1-r-r_l2)]) + elif preference_choice==111: + r = 1e-3 + r2 = 1e2 + r_l2 = r*r2 + preference = np.array([r,r_l2,(1-r-r_l2)]) + elif preference_choice==22: + r = 1e-6 + r2 = 1e4 + r_l2 = r*r2 + preference = np.array([r,(1-r-r_l2),r_l2]) + elif preference_choice==222: + r = 1e-3 + r2 = 1e2 + r_l2 = r*r2 + preference = np.array([r,(1-r-r_l2),r_l2]) + else: + r = 1e-12 + r2 = 1e10 + r_l2 = r*r2 + # preference = np.array([r,r,1-2*r-r2*r,r2*r]) + preference = np.array([r]+[r_l2,(1-r-r_l2)]) + + return preference + +# https://stackoverflow.com/questions/14906764/how-to-redirect-stdout-to-both-file-and-console-with-scripting +class Logger(object): + def __init__(self, filename): + self.terminal = sys.stdout + self.log = open(filename, "a") + + def write(self, message): + self.terminal.write(message) + self.log.write(message) + self.log.flush() + + def flush(self): + # this flush method is needed for python 3 compatibility. + # this handles the flush command by doing nothing. + # you might want to specify some extra behavior here. + pass + + +# Functions +def save_vars(vs, filepath): + """ + Saves variables to the given filepath in a safe manner. + """ + filepath = filepath + if os.path.exists(filepath): + shutil.copyfile(filepath, '{}.old'.format(filepath)) + torch.save(vs, filepath) + + +def save_model(model, filepath): + """ + To load a saved model, simply use + `model.load_state_dict(torch.load('path-to-saved-model'))`. + """ + save_vars(model.state_dict(), filepath) + + +def unpack_data(data, device): + return data[0].to(device), data[1].to(device) + + +class Subset(Dataset): + r""" + Subset of a dataset at specified indices. + + Arguments: + dataset (Dataset): The whole Dataset + indices (sequence): Indices in the whole set selected for subset + """ + def __init__(self, dataset, indices): + self.dataset = dataset + self.indices = indices + if hasattr(dataset, 'images'): + self.images = dataset.images[indices] + self.latents = dataset.latents[indices, :] + else: + self.targets = dataset.targets[indices] + self.writers = dataset.domains[indices] + self.data = [dataset.data[i] for i in indices] + + def __getitem__(self, idx): + return self.dataset[self.indices[idx]] + + def __len__(self): + return len(self.indices) + + +def sample_domains(train_loader, N=1, stratified=True): + """ + Sample N domains available in the train loader. + """ + Ls = [] + for tl in train_loader.dataset.batches_left.values(): + Ls.append(max(tl, 0)) if stratified else Ls.append(min(tl, 1)) + + positions = range(len(Ls)) + indices = [] + while True: + needed = N - len(indices) + if not needed: + break + for i in random.choices(positions, Ls, k=needed): + if Ls[i]: + Ls[i] = 0.0 + indices.append(i) + return torch.tensor(indices) + + +def save_best_model(model, runPath, agg, args, pretrain=False): + if args.dataset == 'fmow' or agg['val_stat'][-1] > max(agg['val_stat'][:-1]) or pretrain: + print(f"model saved: {runPath}") + save_model(model, f'{runPath}/model.rar') + save_vars(agg, f'{runPath}/losses.rar') + + +def single_class_predict_fn(yhat): + _, predicted = torch.max(yhat.data, 1) + return predicted + + +def return_predict_fn(dataset): + return { + 'fmow': single_class_predict_fn, + 'camelyon': single_class_predict_fn, + 'poverty': lambda yhat: yhat, + 'iwildcam': single_class_predict_fn, + 'amazon': single_class_predict_fn, + 'civil': single_class_predict_fn, + 'cdsprites': single_class_predict_fn, + 'rxrx': single_class_predict_fn, + }[dataset] + +def return_criterion(dataset): + return { + 'fmow': nn.CrossEntropyLoss(), + 'camelyon': nn.CrossEntropyLoss(), + 'poverty': nn.MSELoss(), + 'iwildcam': nn.CrossEntropyLoss(), + 'amazon': nn.CrossEntropyLoss(), + 'civil': nn.CrossEntropyLoss(), + 'cdsprites': nn.CrossEntropyLoss(), + 'rxrx': nn.CrossEntropyLoss(), + }[dataset] + + +class ParamDict(OrderedDict): + """A dictionary where the values are Tensors, meant to represent weights of + a model. This subclass lets you perform arithmetic on weights directly.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, *kwargs) + + def _prototype(self, other, op): + if isinstance(other, Number): + return ParamDict({k: op(v, other) for k, v in self.items()}) + elif isinstance(other, dict): + return ParamDict({k: op(self[k], other[k]) for k in self}) + else: + raise NotImplementedError + + def __add__(self, other): + return self._prototype(other, operator.add) + + def __rmul__(self, other): + return self._prototype(other, operator.mul) + + __mul__ = __rmul__ + + def __neg__(self): + return ParamDict({k: -v for k, v in self.items()}) + + def __rsub__(self, other): + # a- b := a + (-b) + return self.__add__(other.__neg__()) + + __sub__ = __rsub__ + + def __truediv__(self, other): + return self._prototype(other, operator.truediv) + + +def fish_step(meta_weights, inner_weights, meta_lr): + meta_weights, weights = ParamDict(meta_weights), ParamDict(inner_weights) + if 'model.' in list(meta_weights.keys())[0]: + new_meta_weights = {} + new_weights = {} + for k,v in meta_weights.items(): + if 'model.' in k: + new_meta_weights[k[6:]] = v + new_weights[k[6:]] = weights[k] + else: + new_meta_weights[k] = v + new_weights[k] = weights[k] + meta_weights = ParamDict(new_meta_weights) + weights = ParamDict(new_weights) + else: + print(list(meta_weights.keys())[0]) + meta_weights += meta_lr * sum([weights - meta_weights], 0 * meta_weights) + return meta_weights