|
|
import os |
|
|
import torch |
|
|
import argparse |
|
|
import torch.nn as nn |
|
|
import numpy as np |
|
|
from tqdm.auto import tqdm |
|
|
from torch.utils.data import DataLoader |
|
|
import torch.nn as nn |
|
|
import torch |
|
|
import random |
|
|
from torch import nn |
|
|
from torch.optim import * |
|
|
from torch.optim.lr_scheduler import * |
|
|
from torch.utils.data import DataLoader |
|
|
from torchprofile import profile_macs |
|
|
from torchvision.datasets import * |
|
|
from torchvision.transforms import * |
|
|
from proard.model_zoo import DYN_net |
|
|
from proard.nas.accuracy_predictor import AccuracyPredictor,ResNetArchEncoder,RobustnessPredictor,MobileNetArchEncoder,Accuracy_Robustness_Predictor |
|
|
from proard.nas.efficiency_predictor import ResNet50FLOPsModel,Mbv3FLOPsModel,ProxylessNASFLOPsModel |
|
|
from proard.nas.search_algorithm import EvolutionFinder,DynIndividual_mbv,DynIndividual_res,DynRandomSampler,DynProblem_mbv,DynProblem_res,DynSampling,individual_to_arch_res,individual_to_arch_mbv |
|
|
from utils.profile import trainable_param_num |
|
|
from pymoo.core.individual import Individual |
|
|
from pymoo.core.mutation import Mutation |
|
|
from pymoo.core.population import Population |
|
|
from pymoo.core.problem import Problem |
|
|
from pymoo.core.sampling import Sampling |
|
|
from pymoo.core.variable import Choice |
|
|
from pymoo.operators.crossover.ux import UniformCrossover |
|
|
from pymoo.operators.mutation.pm import PolynomialMutation |
|
|
from pymoo.operators.mutation.rm import ChoiceRandomMutation |
|
|
from pymoo.operators.selection.rnd import RandomSelection |
|
|
from pymoo.operators.selection.tournament import TournamentSelection |
|
|
from pymoo.algorithms.moo.nsga2 import NSGA2 |
|
|
from pymoo.algorithms.moo.sms import SMSEMOA |
|
|
from pymoo.algorithms.moo.spea2 import SPEA2 |
|
|
from pymoo.optimize import minimize |
|
|
from pymoo.termination import get_termination |
|
|
from pymoo.termination.default import DefaultMultiObjectiveTermination |
|
|
from pymoo.core.callback import Callback |
|
|
from pymoo.util.display.column import Column |
|
|
from pymoo.util.display.output import Output |
|
|
from proard.classification.run_manager import ClassificationRunConfig, RunManager |
|
|
parser = argparse.ArgumentParser() |
|
|
parser.add_argument( |
|
|
"-p", "--path", help="The path of cifar10", type=str, default="/dataset/cifar10" |
|
|
) |
|
|
parser.add_argument("-g", "--gpu", help="The gpu(s) to use", type=str, default="all") |
|
|
parser.add_argument( |
|
|
"-b", |
|
|
"--batch-size", |
|
|
help="The batch on every device for validation", |
|
|
type=int, |
|
|
default=100, |
|
|
) |
|
|
parser.add_argument("-j", "--workers", help="Number of workers", type=int, default=20) |
|
|
parser.add_argument( |
|
|
"-n", |
|
|
"--net", |
|
|
metavar="DYNNET", |
|
|
default="ResNet50", |
|
|
choices=[ |
|
|
"ResNet50", |
|
|
"MBV3", |
|
|
"ProxylessNASNet", |
|
|
], |
|
|
help="dynamic networks", |
|
|
) |
|
|
parser.add_argument( |
|
|
"--dataset", type=str, default="cifar10" ,choices=["cifar10", "cifar100", "imagenet"] |
|
|
) |
|
|
parser.add_argument( |
|
|
"--attack", type=str, default="linf-pgd" ,choices=['fgsm', 'linf-pgd', 'fgm', 'l2-pgd', 'linf-df', 'l2-df', 'linf-apgd', 'l2-apgd','squar_attack','autoattack','apgd_ce'] |
|
|
) |
|
|
parser.add_argument("--train_criterion", type=str, default="trades",choices=["trades","sat","mart","hat"]) |
|
|
parser.add_argument( |
|
|
"--robust_mode", type=bool, default=True |
|
|
) |
|
|
args = parser.parse_args() |
|
|
if args.gpu == "all": |
|
|
device_list = range(torch.cuda.device_count()) |
|
|
args.gpu = ",".join(str(_) for _ in device_list) |
|
|
else: |
|
|
device_list = [int(_) for _ in args.gpu.split(",")] |
|
|
os.environ["CUDA_VISIBLE_DEVICES"] = args.gpu |
|
|
args.batch_size = args.batch_size * max(len(device_list), 1) |
|
|
run_config = ClassificationRunConfig(attack_type=args.attack, dataset= args.dataset, test_batch_size=args.batch_size, n_worker=args.workers,robust_mode=args.robust_mode) |
|
|
dyn_network = DYN_net(args.net,args.robust_mode,args.dataset,args.train_criterion, pretrained=True,run_config=run_config) |
|
|
if args.net == "ResNet50": |
|
|
efficiency_predictor = ResNet50FLOPsModel(dyn_network) |
|
|
arch = ResNetArchEncoder(image_size_list=[32],depth_list=[0,1,2],expand_list=[0.2,0.25,0.35],width_mult_list=[0.65,0.8,1.0]) |
|
|
accuracy_robustness_predictor = Accuracy_Robustness_Predictor(arch) |
|
|
accuracy_robustness_predictor.load_state_dict(torch.load("./acc_rob_data_{}_{}_{}/src/model_acc_rob.pth".format(args.dataset,args.net,args.train_criterion))) |
|
|
elif args.net == "MBV3": |
|
|
efficiency_predictor = Mbv3FLOPsModel(dyn_network) |
|
|
arch = MobileNetArchEncoder(image_size_list=[32],depth_list=[2,3,4],expand_list=[3,4,6],ks_list=[3,5,7]) |
|
|
accuracy_robustness_predictor = Accuracy_Robustness_Predictor(arch) |
|
|
accuracy_robustness_predictor.load_state_dict(torch.load("./acc_rob_data_{}_{}_{}/src/model_acc_rob.pth".format(args.dataset,args.net,args.train_criterion))) |
|
|
elif args.net == "ProxylessNASNet": |
|
|
efficiency_predictor = ProxylessNASFLOPsModel(dyn_network) |
|
|
arch = MobileNetArchEncoder(image_size_list=[32],depth_list=[2,3,4],expand_list=[3,4,6],width_mult_list=[3,5,7]) |
|
|
accuracy_robustness_predictor = Accuracy_Robustness_Predictor(arch) |
|
|
accuracy_robustness_predictor.load_state_dict(torch.load("./acc_rob_data_{}_{}_{}/src/model_acc_rob.pth".format(args.dataset,args.net,args.train_criterion))) |
|
|
|
|
|
dyn_sampler = DynRandomSampler(arch, efficiency_predictor) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
""" Hyperparameters |
|
|
- P: size of the population in each generation (number of individuals) |
|
|
- N: number of generations to run the algorithm |
|
|
- mutate_prob: probability of gene mutation in the evolutionary search |
|
|
""" |
|
|
P = 100 |
|
|
N = 100 |
|
|
mutation_prob = 0.5 |
|
|
|
|
|
|
|
|
|
|
|
if args.net == 'ResNet50': |
|
|
search_space = { |
|
|
'e': [0.2, 0.25, 0.35], |
|
|
'd': [0, 1, 2], |
|
|
'w': [0 ,1 ,2], |
|
|
'image_size': [32] |
|
|
} |
|
|
else: |
|
|
search_space = { |
|
|
'ks': [3, 5, 7], |
|
|
'e': [3, 4, 6], |
|
|
'd': [2, 3, 4], |
|
|
'image_size': [32] |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
num_blocks = arch.max_n_blocks |
|
|
num_stages = arch.n_stage |
|
|
Flops_constraints = 1600 |
|
|
if args.net == "ResNet50": |
|
|
problem = DynProblem_res(efficiency_predictor, accuracy_robustness_predictor, num_blocks, num_stages, search_space,Flops_constraints) |
|
|
else: |
|
|
problem = DynProblem_mbv(efficiency_predictor, accuracy_robustness_predictor, num_blocks, num_stages, search_space,Flops_constraints) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mutation_rc = ChoiceRandomMutation(prob=1.0, prob_var=0.1) |
|
|
crossover_ux = UniformCrossover(prob=1.0) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
termination_default = DefaultMultiObjectiveTermination( |
|
|
xtol=1e-8, cvtol=1e-6, ftol=0.0025, period=30, n_max_gen=1000, n_max_evals=100000 |
|
|
) |
|
|
termination_gen = get_termination("n_gen", N) |
|
|
np.random.seed(42) |
|
|
random.seed(42) |
|
|
if args.net=="ResNet50": |
|
|
init_pop = Population(individuals=[DynIndividual_res(dyn_sampler.random_sample(), accuracy_robustness_predictor) for _ in range(P)]) |
|
|
else: |
|
|
init_pop = Population(individuals=[DynIndividual_mbv(dyn_sampler.random_sample(), accuracy_robustness_predictor) for _ in range(P)]) |
|
|
|
|
|
algorithm = NSGA2( |
|
|
pop_size=P, |
|
|
sampling=DynSampling(), |
|
|
|
|
|
crossover=crossover_ux, |
|
|
mutation=mutation_rc, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
) |
|
|
res_nsga2 = minimize( |
|
|
problem, |
|
|
algorithm, |
|
|
termination=termination_gen, |
|
|
seed=1, |
|
|
|
|
|
verbose=False, |
|
|
save_history=True, |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
np.savetxt("./results/acc_gen0.csv", 100-res_nsga2.history[0].pop.get('F')[:,0], delimiter=",") |
|
|
|
|
|
np.savetxt("./results/acc_gen99.csv", 100-res_nsga2.history[99].pop.get('F')[:,0], delimiter=",") |
|
|
np.savetxt("./results/rob_gen0.csv", 100-res_nsga2.history[0].pop.get('F')[:,1], delimiter=",") |
|
|
|
|
|
np.savetxt("./results/rob_gen99.csv", 100-res_nsga2.history[99].pop.get('F')[:,1], delimiter=",") |
|
|
np.savetxt("./results/flops_gen99.csv", res_nsga2.history[99].pop.get('G'), delimiter=",") |
|
|
|
|
|
|
|
|
|
|
|
from matplotlib import pyplot as plt |
|
|
from matplotlib.ticker import FormatStrFormatter |
|
|
from matplotlib.ticker import AutoMinorLocator, MultipleLocator |
|
|
|
|
|
x_min, x_max, y_min, y_max = 80, 93, 47, 56 |
|
|
ax_limits = [x_min, x_max, y_min, y_max] |
|
|
|
|
|
|
|
|
fig, ax = plt.subplots(dpi=600) |
|
|
gen0 = 0 |
|
|
gen1 = 99 |
|
|
print(100-res_nsga2.history[gen1].pop.get('F')[:,0], 100 - res_nsga2.history[gen1].pop.get('F')[:,1]) |
|
|
|
|
|
|
|
|
ax.plot(100-res_nsga2.history[gen0].pop.get('F')[:,0], 100 - res_nsga2.history[gen0].pop.get('F')[:,1] , 'o', label=f'Population at generation #{gen0+1}', color='red', alpha=0.5) |
|
|
ax.plot(100-res_nsga2.history[gen1].pop.get('F')[:,0], 100 - res_nsga2.history[gen1].pop.get('F')[:,1] , 'o', label=f'Population at generation #{gen1+1}', color='green', alpha=0.5) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ax.grid(True, linestyle=':') |
|
|
ax.set_xlabel('Accuracy (%)') |
|
|
ax.set_ylabel('Robustness (%)') |
|
|
ax.set_title('NSGA-II solutions progression For Fixed number of FLOPs'), |
|
|
ax.legend() |
|
|
|
|
|
|
|
|
ax.xaxis.set_major_locator(MultipleLocator(1)) |
|
|
ax.xaxis.set_minor_locator(MultipleLocator(1)) |
|
|
|
|
|
ax.yaxis.set_major_locator(MultipleLocator(1)) |
|
|
ax.yaxis.set_minor_locator(MultipleLocator(1)) |
|
|
|
|
|
ax.set(xlim=(ax_limits[0], ax_limits[1]), ylim=(ax_limits[2], ax_limits[3])) |
|
|
|
|
|
plt.savefig('nsga2_pop_progression_debug.png') |
|
|
fig.set_dpi(100) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|