Update q-config and black for procedures/utils
This commit is contained in:
parent
349d9fcc9f
commit
55c9734c31
@ -147,5 +147,8 @@ If you find that this project helps your research, please consider citing the re
|
|||||||
If you want to contribute to this repo, please see [CONTRIBUTING.md](.github/CONTRIBUTING.md).
|
If you want to contribute to this repo, please see [CONTRIBUTING.md](.github/CONTRIBUTING.md).
|
||||||
Besides, please follow [CODE-OF-CONDUCT.md](.github/CODE-OF-CONDUCT.md).
|
Besides, please follow [CODE-OF-CONDUCT.md](.github/CODE-OF-CONDUCT.md).
|
||||||
|
|
||||||
|
We use `[black](https://github.com/psf/black)` for Python code formatter.
|
||||||
|
Please use `black . -l 120`.
|
||||||
|
|
||||||
# License
|
# License
|
||||||
The entire codebase is under the [MIT license](LICENSE.md).
|
The entire codebase is under the [MIT license](LICENSE.md).
|
||||||
|
82
configs/qlib/workflow_config_mlp_Alpha360.yaml
Normal file
82
configs/qlib/workflow_config_mlp_Alpha360.yaml
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
qlib_init:
|
||||||
|
provider_uri: "~/.qlib/qlib_data/cn_data"
|
||||||
|
region: cn
|
||||||
|
market: &market all
|
||||||
|
benchmark: &benchmark SH000300
|
||||||
|
data_handler_config: &data_handler_config
|
||||||
|
start_time: 2008-01-01
|
||||||
|
end_time: 2020-08-01
|
||||||
|
fit_start_time: 2008-01-01
|
||||||
|
fit_end_time: 2014-12-31
|
||||||
|
instruments: *market
|
||||||
|
infer_processors:
|
||||||
|
- class: RobustZScoreNorm
|
||||||
|
kwargs:
|
||||||
|
fields_group: feature
|
||||||
|
clip_outlier: true
|
||||||
|
- class: Fillna
|
||||||
|
kwargs:
|
||||||
|
fields_group: feature
|
||||||
|
learn_processors:
|
||||||
|
- class: DropnaLabel
|
||||||
|
- class: CSRankNorm
|
||||||
|
kwargs:
|
||||||
|
fields_group: label
|
||||||
|
label: ["Ref($close, -2) / Ref($close, -1) - 1"]
|
||||||
|
|
||||||
|
port_analysis_config: &port_analysis_config
|
||||||
|
strategy:
|
||||||
|
class: TopkDropoutStrategy
|
||||||
|
module_path: qlib.contrib.strategy.strategy
|
||||||
|
kwargs:
|
||||||
|
topk: 50
|
||||||
|
n_drop: 5
|
||||||
|
backtest:
|
||||||
|
verbose: False
|
||||||
|
limit_threshold: 0.095
|
||||||
|
account: 100000000
|
||||||
|
benchmark: *benchmark
|
||||||
|
deal_price: close
|
||||||
|
open_cost: 0.0005
|
||||||
|
close_cost: 0.0015
|
||||||
|
min_cost: 5
|
||||||
|
task:
|
||||||
|
model:
|
||||||
|
class: DNNModelPytorch
|
||||||
|
module_path: qlib.contrib.model.pytorch_nn
|
||||||
|
kwargs:
|
||||||
|
loss: mse
|
||||||
|
input_dim: 360
|
||||||
|
output_dim: 1
|
||||||
|
lr: 0.002
|
||||||
|
lr_decay: 0.96
|
||||||
|
lr_decay_steps: 100
|
||||||
|
optimizer: adam
|
||||||
|
max_steps: 8000
|
||||||
|
batch_size: 4096
|
||||||
|
GPU: 0
|
||||||
|
dataset:
|
||||||
|
class: DatasetH
|
||||||
|
module_path: qlib.data.dataset
|
||||||
|
kwargs:
|
||||||
|
handler:
|
||||||
|
class: Alpha360
|
||||||
|
module_path: qlib.contrib.data.handler
|
||||||
|
kwargs: *data_handler_config
|
||||||
|
segments:
|
||||||
|
train: [2008-01-01, 2014-12-31]
|
||||||
|
valid: [2015-01-01, 2016-12-31]
|
||||||
|
test: [2017-01-01, 2020-08-01]
|
||||||
|
record:
|
||||||
|
- class: SignalRecord
|
||||||
|
module_path: qlib.workflow.record_temp
|
||||||
|
kwargs: {}
|
||||||
|
- class: SigAnaRecord
|
||||||
|
module_path: qlib.workflow.record_temp
|
||||||
|
kwargs:
|
||||||
|
ana_long_short: False
|
||||||
|
ann_scaler: 252
|
||||||
|
- class: PortAnaRecord
|
||||||
|
module_path: qlib.workflow.record_temp
|
||||||
|
kwargs:
|
||||||
|
config: *port_analysis_config
|
85
configs/qlib/workflow_config_sfm_Alpha360.yaml
Normal file
85
configs/qlib/workflow_config_sfm_Alpha360.yaml
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
qlib_init:
|
||||||
|
provider_uri: "~/.qlib/qlib_data/cn_data"
|
||||||
|
region: cn
|
||||||
|
market: &market all
|
||||||
|
benchmark: &benchmark SH000300
|
||||||
|
data_handler_config: &data_handler_config
|
||||||
|
start_time: 2008-01-01
|
||||||
|
end_time: 2020-08-01
|
||||||
|
fit_start_time: 2008-01-01
|
||||||
|
fit_end_time: 2014-12-31
|
||||||
|
instruments: *market
|
||||||
|
infer_processors:
|
||||||
|
- class: RobustZScoreNorm
|
||||||
|
kwargs:
|
||||||
|
fields_group: feature
|
||||||
|
clip_outlier: true
|
||||||
|
- class: Fillna
|
||||||
|
kwargs:
|
||||||
|
fields_group: feature
|
||||||
|
learn_processors:
|
||||||
|
- class: DropnaLabel
|
||||||
|
- class: CSRankNorm
|
||||||
|
kwargs:
|
||||||
|
fields_group: label
|
||||||
|
label: ["Ref($close, -2) / Ref($close, -1) - 1"]
|
||||||
|
port_analysis_config: &port_analysis_config
|
||||||
|
strategy:
|
||||||
|
class: TopkDropoutStrategy
|
||||||
|
module_path: qlib.contrib.strategy.strategy
|
||||||
|
kwargs:
|
||||||
|
topk: 50
|
||||||
|
n_drop: 5
|
||||||
|
backtest:
|
||||||
|
verbose: False
|
||||||
|
limit_threshold: 0.095
|
||||||
|
account: 100000000
|
||||||
|
benchmark: *benchmark
|
||||||
|
deal_price: close
|
||||||
|
open_cost: 0.0005
|
||||||
|
close_cost: 0.0015
|
||||||
|
min_cost: 5
|
||||||
|
task:
|
||||||
|
model:
|
||||||
|
class: SFM
|
||||||
|
module_path: qlib.contrib.model.pytorch_sfm
|
||||||
|
kwargs:
|
||||||
|
d_feat: 6
|
||||||
|
hidden_size: 64
|
||||||
|
output_dim: 32
|
||||||
|
freq_dim: 25
|
||||||
|
dropout_W: 0.5
|
||||||
|
dropout_U: 0.5
|
||||||
|
n_epochs: 20
|
||||||
|
lr: 1e-3
|
||||||
|
batch_size: 1600
|
||||||
|
early_stop: 20
|
||||||
|
eval_steps: 5
|
||||||
|
loss: mse
|
||||||
|
optimizer: adam
|
||||||
|
GPU: 0
|
||||||
|
dataset:
|
||||||
|
class: DatasetH
|
||||||
|
module_path: qlib.data.dataset
|
||||||
|
kwargs:
|
||||||
|
handler:
|
||||||
|
class: Alpha360
|
||||||
|
module_path: qlib.contrib.data.handler
|
||||||
|
kwargs: *data_handler_config
|
||||||
|
segments:
|
||||||
|
train: [2008-01-01, 2014-12-31]
|
||||||
|
valid: [2015-01-01, 2016-12-31]
|
||||||
|
test: [2017-01-01, 2020-08-01]
|
||||||
|
record:
|
||||||
|
- class: SignalRecord
|
||||||
|
module_path: qlib.workflow.record_temp
|
||||||
|
kwargs: {}
|
||||||
|
- class: SigAnaRecord
|
||||||
|
module_path: qlib.workflow.record_temp
|
||||||
|
kwargs:
|
||||||
|
ana_long_short: False
|
||||||
|
ann_scaler: 252
|
||||||
|
- class: PortAnaRecord
|
||||||
|
module_path: qlib.workflow.record_temp
|
||||||
|
kwargs:
|
||||||
|
config: *port_analysis_config
|
@ -4,6 +4,8 @@
|
|||||||
# python exps/trading/baselines.py --alg GRU
|
# python exps/trading/baselines.py --alg GRU
|
||||||
# python exps/trading/baselines.py --alg LSTM
|
# python exps/trading/baselines.py --alg LSTM
|
||||||
# python exps/trading/baselines.py --alg ALSTM
|
# python exps/trading/baselines.py --alg ALSTM
|
||||||
|
# python exps/trading/baselines.py --alg MLP
|
||||||
|
# python exps/trading/baselines.py --alg SFM
|
||||||
# python exps/trading/baselines.py --alg XGBoost
|
# python exps/trading/baselines.py --alg XGBoost
|
||||||
# python exps/trading/baselines.py --alg LightGBM
|
# python exps/trading/baselines.py --alg LightGBM
|
||||||
#####################################################
|
#####################################################
|
||||||
@ -17,6 +19,10 @@ lib_dir = (Path(__file__).parent / ".." / ".." / "lib").resolve()
|
|||||||
if str(lib_dir) not in sys.path:
|
if str(lib_dir) not in sys.path:
|
||||||
sys.path.insert(0, str(lib_dir))
|
sys.path.insert(0, str(lib_dir))
|
||||||
|
|
||||||
|
from procedures.q_exps import update_gpu
|
||||||
|
from procedures.q_exps import update_market
|
||||||
|
from procedures.q_exps import run_exp
|
||||||
|
|
||||||
import qlib
|
import qlib
|
||||||
from qlib.utils import init_instance_by_config
|
from qlib.utils import init_instance_by_config
|
||||||
from qlib.workflow import R
|
from qlib.workflow import R
|
||||||
@ -31,15 +37,19 @@ def retrieve_configs():
|
|||||||
alg2names = OrderedDict()
|
alg2names = OrderedDict()
|
||||||
alg2names["GRU"] = "workflow_config_gru_Alpha360.yaml"
|
alg2names["GRU"] = "workflow_config_gru_Alpha360.yaml"
|
||||||
alg2names["LSTM"] = "workflow_config_lstm_Alpha360.yaml"
|
alg2names["LSTM"] = "workflow_config_lstm_Alpha360.yaml"
|
||||||
|
alg2names["MLP"] = "workflow_config_mlp_Alpha360.yaml"
|
||||||
# A dual-stage attention-based recurrent neural network for time series prediction, IJCAI-2017
|
# A dual-stage attention-based recurrent neural network for time series prediction, IJCAI-2017
|
||||||
alg2names["ALSTM"] = "workflow_config_alstm_Alpha360.yaml"
|
alg2names["ALSTM"] = "workflow_config_alstm_Alpha360.yaml"
|
||||||
# XGBoost: A Scalable Tree Boosting System, KDD-2016
|
# XGBoost: A Scalable Tree Boosting System, KDD-2016
|
||||||
alg2names["XGBoost"] = "workflow_config_xgboost_Alpha360.yaml"
|
alg2names["XGBoost"] = "workflow_config_xgboost_Alpha360.yaml"
|
||||||
# LightGBM: A Highly Efficient Gradient Boosting Decision Tree, NeurIPS-2017
|
# LightGBM: A Highly Efficient Gradient Boosting Decision Tree, NeurIPS-2017
|
||||||
alg2names["LightGBM"] = "workflow_config_lightgbm_Alpha360.yaml"
|
alg2names["LightGBM"] = "workflow_config_lightgbm_Alpha360.yaml"
|
||||||
|
# State Frequency Memory (SFM): Stock Price Prediction via Discovering Multi-Frequency Trading Patterns, KDD-2017
|
||||||
|
alg2names["SFM"] = "workflow_config_sfm_Alpha360.yaml"
|
||||||
|
|
||||||
# find the yaml paths
|
# find the yaml paths
|
||||||
alg2paths = OrderedDict()
|
alg2paths = OrderedDict()
|
||||||
|
print("Start retrieving the algorithm configurations")
|
||||||
for idx, (alg, name) in enumerate(alg2names.items()):
|
for idx, (alg, name) in enumerate(alg2names.items()):
|
||||||
path = config_dir / name
|
path = config_dir / name
|
||||||
assert path.exists(), "{:} does not exist.".format(path)
|
assert path.exists(), "{:} does not exist.".format(path)
|
||||||
@ -48,56 +58,6 @@ def retrieve_configs():
|
|||||||
return alg2paths
|
return alg2paths
|
||||||
|
|
||||||
|
|
||||||
def update_gpu(config, gpu):
|
|
||||||
config = config.copy()
|
|
||||||
if "GPU" in config["task"]["model"]:
|
|
||||||
config["task"]["model"]["GPU"] = gpu
|
|
||||||
return config
|
|
||||||
|
|
||||||
|
|
||||||
def update_market(config, market):
|
|
||||||
config = config.copy()
|
|
||||||
config["market"] = market
|
|
||||||
config["data_handler_config"]["instruments"] = market
|
|
||||||
return config
|
|
||||||
|
|
||||||
|
|
||||||
def run_exp(task_config, dataset, experiment_name, recorder_name, uri):
|
|
||||||
|
|
||||||
# model initiaiton
|
|
||||||
print("")
|
|
||||||
print("[{:}] - [{:}]: {:}".format(experiment_name, recorder_name, uri))
|
|
||||||
print("dataset={:}".format(dataset))
|
|
||||||
|
|
||||||
model = init_instance_by_config(task_config["model"])
|
|
||||||
|
|
||||||
# start exp
|
|
||||||
with R.start(experiment_name=experiment_name, recorder_name=recorder_name, uri=uri):
|
|
||||||
|
|
||||||
log_file = R.get_recorder().root_uri / "{:}.log".format(experiment_name)
|
|
||||||
set_log_basic_config(log_file)
|
|
||||||
|
|
||||||
# train model
|
|
||||||
R.log_params(**flatten_dict(task_config))
|
|
||||||
model.fit(dataset)
|
|
||||||
recorder = R.get_recorder()
|
|
||||||
R.save_objects(**{"model.pkl": model})
|
|
||||||
|
|
||||||
# generate records: prediction, backtest, and analysis
|
|
||||||
for record in task_config["record"]:
|
|
||||||
record = record.copy()
|
|
||||||
if record["class"] == "SignalRecord":
|
|
||||||
srconf = {"model": model, "dataset": dataset, "recorder": recorder}
|
|
||||||
record["kwargs"].update(srconf)
|
|
||||||
sr = init_instance_by_config(record)
|
|
||||||
sr.generate()
|
|
||||||
else:
|
|
||||||
rconf = {"recorder": recorder}
|
|
||||||
record["kwargs"].update(rconf)
|
|
||||||
ar = init_instance_by_config(record)
|
|
||||||
ar.generate()
|
|
||||||
|
|
||||||
|
|
||||||
def main(xargs, exp_yaml):
|
def main(xargs, exp_yaml):
|
||||||
assert Path(exp_yaml).exists(), "{:} does not exist.".format(exp_yaml)
|
assert Path(exp_yaml).exists(), "{:} does not exist.".format(exp_yaml)
|
||||||
|
|
||||||
|
@ -1,25 +1,36 @@
|
|||||||
##################################################
|
##################################################
|
||||||
# Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 #
|
# Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 #
|
||||||
##################################################
|
##################################################
|
||||||
from .starts import prepare_seed, prepare_logger, get_machine_info, save_checkpoint, copy_checkpoint
|
from .starts import prepare_seed
|
||||||
|
from .starts import prepare_logger
|
||||||
|
from .starts import get_machine_info
|
||||||
|
from .starts import save_checkpoint
|
||||||
|
from .starts import copy_checkpoint
|
||||||
from .optimizers import get_optim_scheduler
|
from .optimizers import get_optim_scheduler
|
||||||
from .funcs_nasbench import evaluate_for_seed as bench_evaluate_for_seed
|
from .funcs_nasbench import evaluate_for_seed as bench_evaluate_for_seed
|
||||||
from .funcs_nasbench import pure_evaluate as bench_pure_evaluate
|
from .funcs_nasbench import pure_evaluate as bench_pure_evaluate
|
||||||
from .funcs_nasbench import get_nas_bench_loaders
|
from .funcs_nasbench import get_nas_bench_loaders
|
||||||
|
|
||||||
def get_procedures(procedure):
|
|
||||||
from .basic_main import basic_train, basic_valid
|
|
||||||
from .search_main import search_train, search_valid
|
|
||||||
from .search_main_v2 import search_train_v2
|
|
||||||
from .simple_KD_main import simple_KD_train, simple_KD_valid
|
|
||||||
|
|
||||||
train_funcs = {'basic' : basic_train, \
|
def get_procedures(procedure):
|
||||||
'search': search_train,'Simple-KD': simple_KD_train, \
|
from .basic_main import basic_train, basic_valid
|
||||||
'search-v2': search_train_v2}
|
from .search_main import search_train, search_valid
|
||||||
valid_funcs = {'basic' : basic_valid, \
|
from .search_main_v2 import search_train_v2
|
||||||
'search': search_valid,'Simple-KD': simple_KD_valid, \
|
from .simple_KD_main import simple_KD_train, simple_KD_valid
|
||||||
'search-v2': search_valid}
|
|
||||||
|
train_funcs = {
|
||||||
train_func = train_funcs[procedure]
|
"basic": basic_train,
|
||||||
valid_func = valid_funcs[procedure]
|
"search": search_train,
|
||||||
return train_func, valid_func
|
"Simple-KD": simple_KD_train,
|
||||||
|
"search-v2": search_train_v2,
|
||||||
|
}
|
||||||
|
valid_funcs = {
|
||||||
|
"basic": basic_valid,
|
||||||
|
"search": search_valid,
|
||||||
|
"Simple-KD": simple_KD_valid,
|
||||||
|
"search-v2": search_valid,
|
||||||
|
}
|
||||||
|
|
||||||
|
train_func = train_funcs[procedure]
|
||||||
|
valid_func = valid_funcs[procedure]
|
||||||
|
return train_func, valid_func
|
||||||
|
@ -3,73 +3,100 @@
|
|||||||
##################################################
|
##################################################
|
||||||
import os, sys, time, torch
|
import os, sys, time, torch
|
||||||
from log_utils import AverageMeter, time_string
|
from log_utils import AverageMeter, time_string
|
||||||
from utils import obtain_accuracy
|
from utils import obtain_accuracy
|
||||||
|
|
||||||
|
|
||||||
def basic_train(xloader, network, criterion, scheduler, optimizer, optim_config, extra_info, print_freq, logger):
|
def basic_train(xloader, network, criterion, scheduler, optimizer, optim_config, extra_info, print_freq, logger):
|
||||||
loss, acc1, acc5 = procedure(xloader, network, criterion, scheduler, optimizer, 'train', optim_config, extra_info, print_freq, logger)
|
loss, acc1, acc5 = procedure(
|
||||||
return loss, acc1, acc5
|
xloader, network, criterion, scheduler, optimizer, "train", optim_config, extra_info, print_freq, logger
|
||||||
|
)
|
||||||
|
return loss, acc1, acc5
|
||||||
|
|
||||||
|
|
||||||
def basic_valid(xloader, network, criterion, optim_config, extra_info, print_freq, logger):
|
def basic_valid(xloader, network, criterion, optim_config, extra_info, print_freq, logger):
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
loss, acc1, acc5 = procedure(xloader, network, criterion, None, None, 'valid', None, extra_info, print_freq, logger)
|
loss, acc1, acc5 = procedure(
|
||||||
return loss, acc1, acc5
|
xloader, network, criterion, None, None, "valid", None, extra_info, print_freq, logger
|
||||||
|
)
|
||||||
|
return loss, acc1, acc5
|
||||||
|
|
||||||
|
|
||||||
def procedure(xloader, network, criterion, scheduler, optimizer, mode, config, extra_info, print_freq, logger):
|
def procedure(xloader, network, criterion, scheduler, optimizer, mode, config, extra_info, print_freq, logger):
|
||||||
data_time, batch_time, losses, top1, top5 = AverageMeter(), AverageMeter(), AverageMeter(), AverageMeter(), AverageMeter()
|
data_time, batch_time, losses, top1, top5 = (
|
||||||
if mode == 'train':
|
AverageMeter(),
|
||||||
network.train()
|
AverageMeter(),
|
||||||
elif mode == 'valid':
|
AverageMeter(),
|
||||||
network.eval()
|
AverageMeter(),
|
||||||
else: raise ValueError("The mode is not right : {:}".format(mode))
|
AverageMeter(),
|
||||||
|
)
|
||||||
#logger.log('[{:5s}] config :: auxiliary={:}, message={:}'.format(mode, config.auxiliary if hasattr(config, 'auxiliary') else -1, network.module.get_message()))
|
if mode == "train":
|
||||||
logger.log('[{:5s}] config :: auxiliary={:}'.format(mode, config.auxiliary if hasattr(config, 'auxiliary') else -1))
|
network.train()
|
||||||
end = time.time()
|
elif mode == "valid":
|
||||||
for i, (inputs, targets) in enumerate(xloader):
|
network.eval()
|
||||||
if mode == 'train': scheduler.update(None, 1.0 * i / len(xloader))
|
|
||||||
# measure data loading time
|
|
||||||
data_time.update(time.time() - end)
|
|
||||||
# calculate prediction and loss
|
|
||||||
targets = targets.cuda(non_blocking=True)
|
|
||||||
|
|
||||||
if mode == 'train': optimizer.zero_grad()
|
|
||||||
|
|
||||||
features, logits = network(inputs)
|
|
||||||
if isinstance(logits, list):
|
|
||||||
assert len(logits) == 2, 'logits must has {:} items instead of {:}'.format(2, len(logits))
|
|
||||||
logits, logits_aux = logits
|
|
||||||
else:
|
else:
|
||||||
logits, logits_aux = logits, None
|
raise ValueError("The mode is not right : {:}".format(mode))
|
||||||
loss = criterion(logits, targets)
|
|
||||||
if config is not None and hasattr(config, 'auxiliary') and config.auxiliary > 0:
|
|
||||||
loss_aux = criterion(logits_aux, targets)
|
|
||||||
loss += config.auxiliary * loss_aux
|
|
||||||
|
|
||||||
if mode == 'train':
|
|
||||||
loss.backward()
|
|
||||||
optimizer.step()
|
|
||||||
|
|
||||||
# record
|
# logger.log('[{:5s}] config :: auxiliary={:}, message={:}'.format(mode, config.auxiliary if hasattr(config, 'auxiliary') else -1, network.module.get_message()))
|
||||||
prec1, prec5 = obtain_accuracy(logits.data, targets.data, topk=(1, 5))
|
logger.log(
|
||||||
losses.update(loss.item(), inputs.size(0))
|
"[{:5s}] config :: auxiliary={:}".format(mode, config.auxiliary if hasattr(config, "auxiliary") else -1)
|
||||||
top1.update (prec1.item(), inputs.size(0))
|
)
|
||||||
top5.update (prec5.item(), inputs.size(0))
|
|
||||||
|
|
||||||
# measure elapsed time
|
|
||||||
batch_time.update(time.time() - end)
|
|
||||||
end = time.time()
|
end = time.time()
|
||||||
|
for i, (inputs, targets) in enumerate(xloader):
|
||||||
|
if mode == "train":
|
||||||
|
scheduler.update(None, 1.0 * i / len(xloader))
|
||||||
|
# measure data loading time
|
||||||
|
data_time.update(time.time() - end)
|
||||||
|
# calculate prediction and loss
|
||||||
|
targets = targets.cuda(non_blocking=True)
|
||||||
|
|
||||||
if i % print_freq == 0 or (i+1) == len(xloader):
|
if mode == "train":
|
||||||
Sstr = ' {:5s} '.format(mode.upper()) + time_string() + ' [{:}][{:03d}/{:03d}]'.format(extra_info, i, len(xloader))
|
optimizer.zero_grad()
|
||||||
if scheduler is not None:
|
|
||||||
Sstr += ' {:}'.format(scheduler.get_min_info())
|
|
||||||
Tstr = 'Time {batch_time.val:.2f} ({batch_time.avg:.2f}) Data {data_time.val:.2f} ({data_time.avg:.2f})'.format(batch_time=batch_time, data_time=data_time)
|
|
||||||
Lstr = 'Loss {loss.val:.3f} ({loss.avg:.3f}) Prec@1 {top1.val:.2f} ({top1.avg:.2f}) Prec@5 {top5.val:.2f} ({top5.avg:.2f})'.format(loss=losses, top1=top1, top5=top5)
|
|
||||||
Istr = 'Size={:}'.format(list(inputs.size()))
|
|
||||||
logger.log(Sstr + ' ' + Tstr + ' ' + Lstr + ' ' + Istr)
|
|
||||||
|
|
||||||
logger.log(' **{mode:5s}** Prec@1 {top1.avg:.2f} Prec@5 {top5.avg:.2f} Error@1 {error1:.2f} Error@5 {error5:.2f} Loss:{loss:.3f}'.format(mode=mode.upper(), top1=top1, top5=top5, error1=100-top1.avg, error5=100-top5.avg, loss=losses.avg))
|
features, logits = network(inputs)
|
||||||
return losses.avg, top1.avg, top5.avg
|
if isinstance(logits, list):
|
||||||
|
assert len(logits) == 2, "logits must has {:} items instead of {:}".format(2, len(logits))
|
||||||
|
logits, logits_aux = logits
|
||||||
|
else:
|
||||||
|
logits, logits_aux = logits, None
|
||||||
|
loss = criterion(logits, targets)
|
||||||
|
if config is not None and hasattr(config, "auxiliary") and config.auxiliary > 0:
|
||||||
|
loss_aux = criterion(logits_aux, targets)
|
||||||
|
loss += config.auxiliary * loss_aux
|
||||||
|
|
||||||
|
if mode == "train":
|
||||||
|
loss.backward()
|
||||||
|
optimizer.step()
|
||||||
|
|
||||||
|
# record
|
||||||
|
prec1, prec5 = obtain_accuracy(logits.data, targets.data, topk=(1, 5))
|
||||||
|
losses.update(loss.item(), inputs.size(0))
|
||||||
|
top1.update(prec1.item(), inputs.size(0))
|
||||||
|
top5.update(prec5.item(), inputs.size(0))
|
||||||
|
|
||||||
|
# measure elapsed time
|
||||||
|
batch_time.update(time.time() - end)
|
||||||
|
end = time.time()
|
||||||
|
|
||||||
|
if i % print_freq == 0 or (i + 1) == len(xloader):
|
||||||
|
Sstr = (
|
||||||
|
" {:5s} ".format(mode.upper())
|
||||||
|
+ time_string()
|
||||||
|
+ " [{:}][{:03d}/{:03d}]".format(extra_info, i, len(xloader))
|
||||||
|
)
|
||||||
|
if scheduler is not None:
|
||||||
|
Sstr += " {:}".format(scheduler.get_min_info())
|
||||||
|
Tstr = "Time {batch_time.val:.2f} ({batch_time.avg:.2f}) Data {data_time.val:.2f} ({data_time.avg:.2f})".format(
|
||||||
|
batch_time=batch_time, data_time=data_time
|
||||||
|
)
|
||||||
|
Lstr = "Loss {loss.val:.3f} ({loss.avg:.3f}) Prec@1 {top1.val:.2f} ({top1.avg:.2f}) Prec@5 {top5.val:.2f} ({top5.avg:.2f})".format(
|
||||||
|
loss=losses, top1=top1, top5=top5
|
||||||
|
)
|
||||||
|
Istr = "Size={:}".format(list(inputs.size()))
|
||||||
|
logger.log(Sstr + " " + Tstr + " " + Lstr + " " + Istr)
|
||||||
|
|
||||||
|
logger.log(
|
||||||
|
" **{mode:5s}** Prec@1 {top1.avg:.2f} Prec@5 {top5.avg:.2f} Error@1 {error1:.2f} Error@5 {error5:.2f} Loss:{loss:.3f}".format(
|
||||||
|
mode=mode.upper(), top1=top1, top5=top5, error1=100 - top1.avg, error5=100 - top5.avg, loss=losses.avg
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return losses.avg, top1.avg, top5.avg
|
||||||
|
@ -5,199 +5,348 @@ import os, time, copy, torch, pathlib
|
|||||||
|
|
||||||
import datasets
|
import datasets
|
||||||
from config_utils import load_config
|
from config_utils import load_config
|
||||||
from procedures import prepare_seed, get_optim_scheduler
|
from procedures import prepare_seed, get_optim_scheduler
|
||||||
from utils import get_model_infos, obtain_accuracy
|
from utils import get_model_infos, obtain_accuracy
|
||||||
from log_utils import AverageMeter, time_string, convert_secs2time
|
from log_utils import AverageMeter, time_string, convert_secs2time
|
||||||
from models import get_cell_based_tiny_net
|
from models import get_cell_based_tiny_net
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['evaluate_for_seed', 'pure_evaluate', 'get_nas_bench_loaders']
|
__all__ = ["evaluate_for_seed", "pure_evaluate", "get_nas_bench_loaders"]
|
||||||
|
|
||||||
|
|
||||||
def pure_evaluate(xloader, network, criterion=torch.nn.CrossEntropyLoss()):
|
def pure_evaluate(xloader, network, criterion=torch.nn.CrossEntropyLoss()):
|
||||||
data_time, batch_time, batch = AverageMeter(), AverageMeter(), None
|
data_time, batch_time, batch = AverageMeter(), AverageMeter(), None
|
||||||
losses, top1, top5 = AverageMeter(), AverageMeter(), AverageMeter()
|
losses, top1, top5 = AverageMeter(), AverageMeter(), AverageMeter()
|
||||||
latencies, device = [], torch.cuda.current_device()
|
latencies, device = [], torch.cuda.current_device()
|
||||||
network.eval()
|
network.eval()
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
end = time.time()
|
end = time.time()
|
||||||
for i, (inputs, targets) in enumerate(xloader):
|
for i, (inputs, targets) in enumerate(xloader):
|
||||||
targets = targets.cuda(device=device, non_blocking=True)
|
targets = targets.cuda(device=device, non_blocking=True)
|
||||||
inputs = inputs.cuda(device=device, non_blocking=True)
|
inputs = inputs.cuda(device=device, non_blocking=True)
|
||||||
data_time.update(time.time() - end)
|
data_time.update(time.time() - end)
|
||||||
# forward
|
# forward
|
||||||
features, logits = network(inputs)
|
features, logits = network(inputs)
|
||||||
loss = criterion(logits, targets)
|
loss = criterion(logits, targets)
|
||||||
batch_time.update(time.time() - end)
|
batch_time.update(time.time() - end)
|
||||||
if batch is None or batch == inputs.size(0):
|
if batch is None or batch == inputs.size(0):
|
||||||
batch = inputs.size(0)
|
batch = inputs.size(0)
|
||||||
latencies.append( batch_time.val - data_time.val )
|
latencies.append(batch_time.val - data_time.val)
|
||||||
# record loss and accuracy
|
# record loss and accuracy
|
||||||
prec1, prec5 = obtain_accuracy(logits.data, targets.data, topk=(1, 5))
|
prec1, prec5 = obtain_accuracy(logits.data, targets.data, topk=(1, 5))
|
||||||
losses.update(loss.item(), inputs.size(0))
|
losses.update(loss.item(), inputs.size(0))
|
||||||
top1.update (prec1.item(), inputs.size(0))
|
top1.update(prec1.item(), inputs.size(0))
|
||||||
top5.update (prec5.item(), inputs.size(0))
|
top5.update(prec5.item(), inputs.size(0))
|
||||||
end = time.time()
|
end = time.time()
|
||||||
if len(latencies) > 2: latencies = latencies[1:]
|
if len(latencies) > 2:
|
||||||
return losses.avg, top1.avg, top5.avg, latencies
|
latencies = latencies[1:]
|
||||||
|
return losses.avg, top1.avg, top5.avg, latencies
|
||||||
|
|
||||||
|
|
||||||
def procedure(xloader, network, criterion, scheduler, optimizer, mode: str):
|
def procedure(xloader, network, criterion, scheduler, optimizer, mode: str):
|
||||||
losses, top1, top5 = AverageMeter(), AverageMeter(), AverageMeter()
|
losses, top1, top5 = AverageMeter(), AverageMeter(), AverageMeter()
|
||||||
if mode == 'train' : network.train()
|
if mode == "train":
|
||||||
elif mode == 'valid': network.eval()
|
network.train()
|
||||||
else: raise ValueError("The mode is not right : {:}".format(mode))
|
elif mode == "valid":
|
||||||
device = torch.cuda.current_device()
|
network.eval()
|
||||||
data_time, batch_time, end = AverageMeter(), AverageMeter(), time.time()
|
else:
|
||||||
for i, (inputs, targets) in enumerate(xloader):
|
raise ValueError("The mode is not right : {:}".format(mode))
|
||||||
if mode == 'train': scheduler.update(None, 1.0 * i / len(xloader))
|
device = torch.cuda.current_device()
|
||||||
|
data_time, batch_time, end = AverageMeter(), AverageMeter(), time.time()
|
||||||
|
for i, (inputs, targets) in enumerate(xloader):
|
||||||
|
if mode == "train":
|
||||||
|
scheduler.update(None, 1.0 * i / len(xloader))
|
||||||
|
|
||||||
targets = targets.cuda(device=device, non_blocking=True)
|
targets = targets.cuda(device=device, non_blocking=True)
|
||||||
if mode == 'train': optimizer.zero_grad()
|
if mode == "train":
|
||||||
# forward
|
optimizer.zero_grad()
|
||||||
features, logits = network(inputs)
|
# forward
|
||||||
loss = criterion(logits, targets)
|
features, logits = network(inputs)
|
||||||
# backward
|
loss = criterion(logits, targets)
|
||||||
if mode == 'train':
|
# backward
|
||||||
loss.backward()
|
if mode == "train":
|
||||||
optimizer.step()
|
loss.backward()
|
||||||
# record loss and accuracy
|
optimizer.step()
|
||||||
prec1, prec5 = obtain_accuracy(logits.data, targets.data, topk=(1, 5))
|
# record loss and accuracy
|
||||||
losses.update(loss.item(), inputs.size(0))
|
prec1, prec5 = obtain_accuracy(logits.data, targets.data, topk=(1, 5))
|
||||||
top1.update (prec1.item(), inputs.size(0))
|
losses.update(loss.item(), inputs.size(0))
|
||||||
top5.update (prec5.item(), inputs.size(0))
|
top1.update(prec1.item(), inputs.size(0))
|
||||||
# count time
|
top5.update(prec5.item(), inputs.size(0))
|
||||||
batch_time.update(time.time() - end)
|
# count time
|
||||||
end = time.time()
|
batch_time.update(time.time() - end)
|
||||||
return losses.avg, top1.avg, top5.avg, batch_time.sum
|
end = time.time()
|
||||||
|
return losses.avg, top1.avg, top5.avg, batch_time.sum
|
||||||
|
|
||||||
|
|
||||||
def evaluate_for_seed(arch_config, opt_config, train_loader, valid_loaders, seed: int, logger):
|
def evaluate_for_seed(arch_config, opt_config, train_loader, valid_loaders, seed: int, logger):
|
||||||
|
|
||||||
prepare_seed(seed) # random seed
|
prepare_seed(seed) # random seed
|
||||||
net = get_cell_based_tiny_net(arch_config)
|
net = get_cell_based_tiny_net(arch_config)
|
||||||
#net = TinyNetwork(arch_config['channel'], arch_config['num_cells'], arch, config.class_num)
|
# net = TinyNetwork(arch_config['channel'], arch_config['num_cells'], arch, config.class_num)
|
||||||
flop, param = get_model_infos(net, opt_config.xshape)
|
flop, param = get_model_infos(net, opt_config.xshape)
|
||||||
logger.log('Network : {:}'.format(net.get_message()), False)
|
logger.log("Network : {:}".format(net.get_message()), False)
|
||||||
logger.log('{:} Seed-------------------------- {:} --------------------------'.format(time_string(), seed))
|
logger.log("{:} Seed-------------------------- {:} --------------------------".format(time_string(), seed))
|
||||||
logger.log('FLOP = {:} MB, Param = {:} MB'.format(flop, param))
|
logger.log("FLOP = {:} MB, Param = {:} MB".format(flop, param))
|
||||||
# train and valid
|
# train and valid
|
||||||
optimizer, scheduler, criterion = get_optim_scheduler(net.parameters(), opt_config)
|
optimizer, scheduler, criterion = get_optim_scheduler(net.parameters(), opt_config)
|
||||||
default_device = torch.cuda.current_device()
|
default_device = torch.cuda.current_device()
|
||||||
network = torch.nn.DataParallel(net, device_ids=[default_device]).cuda(device=default_device)
|
network = torch.nn.DataParallel(net, device_ids=[default_device]).cuda(device=default_device)
|
||||||
criterion = criterion.cuda(device=default_device)
|
criterion = criterion.cuda(device=default_device)
|
||||||
# start training
|
# start training
|
||||||
start_time, epoch_time, total_epoch = time.time(), AverageMeter(), opt_config.epochs + opt_config.warmup
|
start_time, epoch_time, total_epoch = time.time(), AverageMeter(), opt_config.epochs + opt_config.warmup
|
||||||
train_losses, train_acc1es, train_acc5es, valid_losses, valid_acc1es, valid_acc5es = {}, {}, {}, {}, {}, {}
|
train_losses, train_acc1es, train_acc5es, valid_losses, valid_acc1es, valid_acc5es = {}, {}, {}, {}, {}, {}
|
||||||
train_times , valid_times, lrs = {}, {}, {}
|
train_times, valid_times, lrs = {}, {}, {}
|
||||||
for epoch in range(total_epoch):
|
for epoch in range(total_epoch):
|
||||||
scheduler.update(epoch, 0.0)
|
scheduler.update(epoch, 0.0)
|
||||||
lr = min(scheduler.get_lr())
|
lr = min(scheduler.get_lr())
|
||||||
train_loss, train_acc1, train_acc5, train_tm = procedure(train_loader, network, criterion, scheduler, optimizer, 'train')
|
train_loss, train_acc1, train_acc5, train_tm = procedure(
|
||||||
train_losses[epoch] = train_loss
|
train_loader, network, criterion, scheduler, optimizer, "train"
|
||||||
train_acc1es[epoch] = train_acc1
|
)
|
||||||
train_acc5es[epoch] = train_acc5
|
train_losses[epoch] = train_loss
|
||||||
train_times [epoch] = train_tm
|
train_acc1es[epoch] = train_acc1
|
||||||
lrs[epoch] = lr
|
train_acc5es[epoch] = train_acc5
|
||||||
with torch.no_grad():
|
train_times[epoch] = train_tm
|
||||||
for key, xloder in valid_loaders.items():
|
lrs[epoch] = lr
|
||||||
valid_loss, valid_acc1, valid_acc5, valid_tm = procedure(xloder , network, criterion, None, None, 'valid')
|
with torch.no_grad():
|
||||||
valid_losses['{:}@{:}'.format(key,epoch)] = valid_loss
|
for key, xloder in valid_loaders.items():
|
||||||
valid_acc1es['{:}@{:}'.format(key,epoch)] = valid_acc1
|
valid_loss, valid_acc1, valid_acc5, valid_tm = procedure(
|
||||||
valid_acc5es['{:}@{:}'.format(key,epoch)] = valid_acc5
|
xloder, network, criterion, None, None, "valid"
|
||||||
valid_times ['{:}@{:}'.format(key,epoch)] = valid_tm
|
)
|
||||||
|
valid_losses["{:}@{:}".format(key, epoch)] = valid_loss
|
||||||
|
valid_acc1es["{:}@{:}".format(key, epoch)] = valid_acc1
|
||||||
|
valid_acc5es["{:}@{:}".format(key, epoch)] = valid_acc5
|
||||||
|
valid_times["{:}@{:}".format(key, epoch)] = valid_tm
|
||||||
|
|
||||||
# measure elapsed time
|
# measure elapsed time
|
||||||
epoch_time.update(time.time() - start_time)
|
epoch_time.update(time.time() - start_time)
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
need_time = 'Time Left: {:}'.format( convert_secs2time(epoch_time.avg * (total_epoch-epoch-1), True) )
|
need_time = "Time Left: {:}".format(convert_secs2time(epoch_time.avg * (total_epoch - epoch - 1), True))
|
||||||
logger.log('{:} {:} epoch={:03d}/{:03d} :: Train [loss={:.5f}, acc@1={:.2f}%, acc@5={:.2f}%] Valid [loss={:.5f}, acc@1={:.2f}%, acc@5={:.2f}%], lr={:}'.format(time_string(), need_time, epoch, total_epoch, train_loss, train_acc1, train_acc5, valid_loss, valid_acc1, valid_acc5, lr))
|
logger.log(
|
||||||
info_seed = {'flop' : flop,
|
"{:} {:} epoch={:03d}/{:03d} :: Train [loss={:.5f}, acc@1={:.2f}%, acc@5={:.2f}%] Valid [loss={:.5f}, acc@1={:.2f}%, acc@5={:.2f}%], lr={:}".format(
|
||||||
'param': param,
|
time_string(),
|
||||||
'arch_config' : arch_config._asdict(),
|
need_time,
|
||||||
'opt_config' : opt_config._asdict(),
|
epoch,
|
||||||
'total_epoch' : total_epoch ,
|
total_epoch,
|
||||||
'train_losses': train_losses,
|
train_loss,
|
||||||
'train_acc1es': train_acc1es,
|
train_acc1,
|
||||||
'train_acc5es': train_acc5es,
|
train_acc5,
|
||||||
'train_times' : train_times,
|
valid_loss,
|
||||||
'valid_losses': valid_losses,
|
valid_acc1,
|
||||||
'valid_acc1es': valid_acc1es,
|
valid_acc5,
|
||||||
'valid_acc5es': valid_acc5es,
|
lr,
|
||||||
'valid_times' : valid_times,
|
)
|
||||||
'learning_rates': lrs,
|
)
|
||||||
'net_state_dict': net.state_dict(),
|
info_seed = {
|
||||||
'net_string' : '{:}'.format(net),
|
"flop": flop,
|
||||||
'finish-train': True
|
"param": param,
|
||||||
}
|
"arch_config": arch_config._asdict(),
|
||||||
return info_seed
|
"opt_config": opt_config._asdict(),
|
||||||
|
"total_epoch": total_epoch,
|
||||||
|
"train_losses": train_losses,
|
||||||
|
"train_acc1es": train_acc1es,
|
||||||
|
"train_acc5es": train_acc5es,
|
||||||
|
"train_times": train_times,
|
||||||
|
"valid_losses": valid_losses,
|
||||||
|
"valid_acc1es": valid_acc1es,
|
||||||
|
"valid_acc5es": valid_acc5es,
|
||||||
|
"valid_times": valid_times,
|
||||||
|
"learning_rates": lrs,
|
||||||
|
"net_state_dict": net.state_dict(),
|
||||||
|
"net_string": "{:}".format(net),
|
||||||
|
"finish-train": True,
|
||||||
|
}
|
||||||
|
return info_seed
|
||||||
|
|
||||||
|
|
||||||
def get_nas_bench_loaders(workers):
|
def get_nas_bench_loaders(workers):
|
||||||
|
|
||||||
torch.set_num_threads(workers)
|
torch.set_num_threads(workers)
|
||||||
|
|
||||||
root_dir = (pathlib.Path(__file__).parent / '..' / '..').resolve()
|
root_dir = (pathlib.Path(__file__).parent / ".." / "..").resolve()
|
||||||
torch_dir = pathlib.Path(os.environ['TORCH_HOME'])
|
torch_dir = pathlib.Path(os.environ["TORCH_HOME"])
|
||||||
# cifar
|
# cifar
|
||||||
cifar_config_path = root_dir / 'configs' / 'nas-benchmark' / 'CIFAR.config'
|
cifar_config_path = root_dir / "configs" / "nas-benchmark" / "CIFAR.config"
|
||||||
cifar_config = load_config(cifar_config_path, None, None)
|
cifar_config = load_config(cifar_config_path, None, None)
|
||||||
get_datasets = datasets.get_datasets # a function to return the dataset
|
get_datasets = datasets.get_datasets # a function to return the dataset
|
||||||
break_line = '-' * 150
|
break_line = "-" * 150
|
||||||
print ('{:} Create data-loader for all datasets'.format(time_string()))
|
print("{:} Create data-loader for all datasets".format(time_string()))
|
||||||
print (break_line)
|
print(break_line)
|
||||||
TRAIN_CIFAR10, VALID_CIFAR10, xshape, class_num = get_datasets('cifar10', str(torch_dir/'cifar.python'), -1)
|
TRAIN_CIFAR10, VALID_CIFAR10, xshape, class_num = get_datasets("cifar10", str(torch_dir / "cifar.python"), -1)
|
||||||
print ('original CIFAR-10 : {:} training images and {:} test images : {:} input shape : {:} number of classes'.format(len(TRAIN_CIFAR10), len(VALID_CIFAR10), xshape, class_num))
|
print(
|
||||||
cifar10_splits = load_config(root_dir / 'configs' / 'nas-benchmark' / 'cifar-split.txt', None, None)
|
"original CIFAR-10 : {:} training images and {:} test images : {:} input shape : {:} number of classes".format(
|
||||||
assert cifar10_splits.train[:10] == [0, 5, 7, 11, 13, 15, 16, 17, 20, 24] and cifar10_splits.valid[:10] == [1, 2, 3, 4, 6, 8, 9, 10, 12, 14]
|
len(TRAIN_CIFAR10), len(VALID_CIFAR10), xshape, class_num
|
||||||
temp_dataset = copy.deepcopy(TRAIN_CIFAR10)
|
)
|
||||||
temp_dataset.transform = VALID_CIFAR10.transform
|
)
|
||||||
# data loader
|
cifar10_splits = load_config(root_dir / "configs" / "nas-benchmark" / "cifar-split.txt", None, None)
|
||||||
trainval_cifar10_loader = torch.utils.data.DataLoader(TRAIN_CIFAR10, batch_size=cifar_config.batch_size, shuffle=True , num_workers=workers, pin_memory=True)
|
assert cifar10_splits.train[:10] == [0, 5, 7, 11, 13, 15, 16, 17, 20, 24] and cifar10_splits.valid[:10] == [
|
||||||
train_cifar10_loader = torch.utils.data.DataLoader(TRAIN_CIFAR10, batch_size=cifar_config.batch_size, sampler=torch.utils.data.sampler.SubsetRandomSampler(cifar10_splits.train), num_workers=workers, pin_memory=True)
|
1,
|
||||||
valid_cifar10_loader = torch.utils.data.DataLoader(temp_dataset , batch_size=cifar_config.batch_size, sampler=torch.utils.data.sampler.SubsetRandomSampler(cifar10_splits.valid), num_workers=workers, pin_memory=True)
|
2,
|
||||||
test__cifar10_loader = torch.utils.data.DataLoader(VALID_CIFAR10, batch_size=cifar_config.batch_size, shuffle=False, num_workers=workers, pin_memory=True)
|
3,
|
||||||
print ('CIFAR-10 : trval-loader has {:3d} batch with {:} per batch'.format(len(trainval_cifar10_loader), cifar_config.batch_size))
|
4,
|
||||||
print ('CIFAR-10 : train-loader has {:3d} batch with {:} per batch'.format(len(train_cifar10_loader), cifar_config.batch_size))
|
6,
|
||||||
print ('CIFAR-10 : valid-loader has {:3d} batch with {:} per batch'.format(len(valid_cifar10_loader), cifar_config.batch_size))
|
8,
|
||||||
print ('CIFAR-10 : test--loader has {:3d} batch with {:} per batch'.format(len(test__cifar10_loader), cifar_config.batch_size))
|
9,
|
||||||
print (break_line)
|
10,
|
||||||
# CIFAR-100
|
12,
|
||||||
TRAIN_CIFAR100, VALID_CIFAR100, xshape, class_num = get_datasets('cifar100', str(torch_dir/'cifar.python'), -1)
|
14,
|
||||||
print ('original CIFAR-100: {:} training images and {:} test images : {:} input shape : {:} number of classes'.format(len(TRAIN_CIFAR100), len(VALID_CIFAR100), xshape, class_num))
|
]
|
||||||
cifar100_splits = load_config(root_dir / 'configs' / 'nas-benchmark' / 'cifar100-test-split.txt', None, None)
|
temp_dataset = copy.deepcopy(TRAIN_CIFAR10)
|
||||||
assert cifar100_splits.xvalid[:10] == [1, 3, 4, 5, 8, 10, 13, 14, 15, 16] and cifar100_splits.xtest[:10] == [0, 2, 6, 7, 9, 11, 12, 17, 20, 24]
|
temp_dataset.transform = VALID_CIFAR10.transform
|
||||||
train_cifar100_loader = torch.utils.data.DataLoader(TRAIN_CIFAR100, batch_size=cifar_config.batch_size, shuffle=True, num_workers=workers, pin_memory=True)
|
# data loader
|
||||||
valid_cifar100_loader = torch.utils.data.DataLoader(VALID_CIFAR100, batch_size=cifar_config.batch_size, sampler=torch.utils.data.sampler.SubsetRandomSampler(cifar100_splits.xvalid), num_workers=workers, pin_memory=True)
|
trainval_cifar10_loader = torch.utils.data.DataLoader(
|
||||||
test__cifar100_loader = torch.utils.data.DataLoader(VALID_CIFAR100, batch_size=cifar_config.batch_size, sampler=torch.utils.data.sampler.SubsetRandomSampler(cifar100_splits.xtest) , num_workers=workers, pin_memory=True)
|
TRAIN_CIFAR10, batch_size=cifar_config.batch_size, shuffle=True, num_workers=workers, pin_memory=True
|
||||||
print ('CIFAR-100 : train-loader has {:3d} batch'.format(len(train_cifar100_loader)))
|
)
|
||||||
print ('CIFAR-100 : valid-loader has {:3d} batch'.format(len(valid_cifar100_loader)))
|
train_cifar10_loader = torch.utils.data.DataLoader(
|
||||||
print ('CIFAR-100 : test--loader has {:3d} batch'.format(len(test__cifar100_loader)))
|
TRAIN_CIFAR10,
|
||||||
print (break_line)
|
batch_size=cifar_config.batch_size,
|
||||||
|
sampler=torch.utils.data.sampler.SubsetRandomSampler(cifar10_splits.train),
|
||||||
|
num_workers=workers,
|
||||||
|
pin_memory=True,
|
||||||
|
)
|
||||||
|
valid_cifar10_loader = torch.utils.data.DataLoader(
|
||||||
|
temp_dataset,
|
||||||
|
batch_size=cifar_config.batch_size,
|
||||||
|
sampler=torch.utils.data.sampler.SubsetRandomSampler(cifar10_splits.valid),
|
||||||
|
num_workers=workers,
|
||||||
|
pin_memory=True,
|
||||||
|
)
|
||||||
|
test__cifar10_loader = torch.utils.data.DataLoader(
|
||||||
|
VALID_CIFAR10, batch_size=cifar_config.batch_size, shuffle=False, num_workers=workers, pin_memory=True
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"CIFAR-10 : trval-loader has {:3d} batch with {:} per batch".format(
|
||||||
|
len(trainval_cifar10_loader), cifar_config.batch_size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"CIFAR-10 : train-loader has {:3d} batch with {:} per batch".format(
|
||||||
|
len(train_cifar10_loader), cifar_config.batch_size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"CIFAR-10 : valid-loader has {:3d} batch with {:} per batch".format(
|
||||||
|
len(valid_cifar10_loader), cifar_config.batch_size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"CIFAR-10 : test--loader has {:3d} batch with {:} per batch".format(
|
||||||
|
len(test__cifar10_loader), cifar_config.batch_size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(break_line)
|
||||||
|
# CIFAR-100
|
||||||
|
TRAIN_CIFAR100, VALID_CIFAR100, xshape, class_num = get_datasets("cifar100", str(torch_dir / "cifar.python"), -1)
|
||||||
|
print(
|
||||||
|
"original CIFAR-100: {:} training images and {:} test images : {:} input shape : {:} number of classes".format(
|
||||||
|
len(TRAIN_CIFAR100), len(VALID_CIFAR100), xshape, class_num
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cifar100_splits = load_config(root_dir / "configs" / "nas-benchmark" / "cifar100-test-split.txt", None, None)
|
||||||
|
assert cifar100_splits.xvalid[:10] == [1, 3, 4, 5, 8, 10, 13, 14, 15, 16] and cifar100_splits.xtest[:10] == [
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
9,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
17,
|
||||||
|
20,
|
||||||
|
24,
|
||||||
|
]
|
||||||
|
train_cifar100_loader = torch.utils.data.DataLoader(
|
||||||
|
TRAIN_CIFAR100, batch_size=cifar_config.batch_size, shuffle=True, num_workers=workers, pin_memory=True
|
||||||
|
)
|
||||||
|
valid_cifar100_loader = torch.utils.data.DataLoader(
|
||||||
|
VALID_CIFAR100,
|
||||||
|
batch_size=cifar_config.batch_size,
|
||||||
|
sampler=torch.utils.data.sampler.SubsetRandomSampler(cifar100_splits.xvalid),
|
||||||
|
num_workers=workers,
|
||||||
|
pin_memory=True,
|
||||||
|
)
|
||||||
|
test__cifar100_loader = torch.utils.data.DataLoader(
|
||||||
|
VALID_CIFAR100,
|
||||||
|
batch_size=cifar_config.batch_size,
|
||||||
|
sampler=torch.utils.data.sampler.SubsetRandomSampler(cifar100_splits.xtest),
|
||||||
|
num_workers=workers,
|
||||||
|
pin_memory=True,
|
||||||
|
)
|
||||||
|
print("CIFAR-100 : train-loader has {:3d} batch".format(len(train_cifar100_loader)))
|
||||||
|
print("CIFAR-100 : valid-loader has {:3d} batch".format(len(valid_cifar100_loader)))
|
||||||
|
print("CIFAR-100 : test--loader has {:3d} batch".format(len(test__cifar100_loader)))
|
||||||
|
print(break_line)
|
||||||
|
|
||||||
imagenet16_config_path = 'configs/nas-benchmark/ImageNet-16.config'
|
imagenet16_config_path = "configs/nas-benchmark/ImageNet-16.config"
|
||||||
imagenet16_config = load_config(imagenet16_config_path, None, None)
|
imagenet16_config = load_config(imagenet16_config_path, None, None)
|
||||||
TRAIN_ImageNet16_120, VALID_ImageNet16_120, xshape, class_num = get_datasets('ImageNet16-120', str(torch_dir/'cifar.python'/'ImageNet16'), -1)
|
TRAIN_ImageNet16_120, VALID_ImageNet16_120, xshape, class_num = get_datasets(
|
||||||
print ('original TRAIN_ImageNet16_120: {:} training images and {:} test images : {:} input shape : {:} number of classes'.format(len(TRAIN_ImageNet16_120), len(VALID_ImageNet16_120), xshape, class_num))
|
"ImageNet16-120", str(torch_dir / "cifar.python" / "ImageNet16"), -1
|
||||||
imagenet_splits = load_config(root_dir / 'configs' / 'nas-benchmark' / 'imagenet-16-120-test-split.txt', None, None)
|
)
|
||||||
assert imagenet_splits.xvalid[:10] == [1, 2, 3, 6, 7, 8, 9, 12, 16, 18] and imagenet_splits.xtest[:10] == [0, 4, 5, 10, 11, 13, 14, 15, 17, 20]
|
print(
|
||||||
train_imagenet_loader = torch.utils.data.DataLoader(TRAIN_ImageNet16_120, batch_size=imagenet16_config.batch_size, shuffle=True, num_workers=workers, pin_memory=True)
|
"original TRAIN_ImageNet16_120: {:} training images and {:} test images : {:} input shape : {:} number of classes".format(
|
||||||
valid_imagenet_loader = torch.utils.data.DataLoader(VALID_ImageNet16_120, batch_size=imagenet16_config.batch_size, sampler=torch.utils.data.sampler.SubsetRandomSampler(imagenet_splits.xvalid), num_workers=workers, pin_memory=True)
|
len(TRAIN_ImageNet16_120), len(VALID_ImageNet16_120), xshape, class_num
|
||||||
test__imagenet_loader = torch.utils.data.DataLoader(VALID_ImageNet16_120, batch_size=imagenet16_config.batch_size, sampler=torch.utils.data.sampler.SubsetRandomSampler(imagenet_splits.xtest) , num_workers=workers, pin_memory=True)
|
)
|
||||||
print ('ImageNet-16-120 : train-loader has {:3d} batch with {:} per batch'.format(len(train_imagenet_loader), imagenet16_config.batch_size))
|
)
|
||||||
print ('ImageNet-16-120 : valid-loader has {:3d} batch with {:} per batch'.format(len(valid_imagenet_loader), imagenet16_config.batch_size))
|
imagenet_splits = load_config(root_dir / "configs" / "nas-benchmark" / "imagenet-16-120-test-split.txt", None, None)
|
||||||
print ('ImageNet-16-120 : test--loader has {:3d} batch with {:} per batch'.format(len(test__imagenet_loader), imagenet16_config.batch_size))
|
assert imagenet_splits.xvalid[:10] == [1, 2, 3, 6, 7, 8, 9, 12, 16, 18] and imagenet_splits.xtest[:10] == [
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
17,
|
||||||
|
20,
|
||||||
|
]
|
||||||
|
train_imagenet_loader = torch.utils.data.DataLoader(
|
||||||
|
TRAIN_ImageNet16_120,
|
||||||
|
batch_size=imagenet16_config.batch_size,
|
||||||
|
shuffle=True,
|
||||||
|
num_workers=workers,
|
||||||
|
pin_memory=True,
|
||||||
|
)
|
||||||
|
valid_imagenet_loader = torch.utils.data.DataLoader(
|
||||||
|
VALID_ImageNet16_120,
|
||||||
|
batch_size=imagenet16_config.batch_size,
|
||||||
|
sampler=torch.utils.data.sampler.SubsetRandomSampler(imagenet_splits.xvalid),
|
||||||
|
num_workers=workers,
|
||||||
|
pin_memory=True,
|
||||||
|
)
|
||||||
|
test__imagenet_loader = torch.utils.data.DataLoader(
|
||||||
|
VALID_ImageNet16_120,
|
||||||
|
batch_size=imagenet16_config.batch_size,
|
||||||
|
sampler=torch.utils.data.sampler.SubsetRandomSampler(imagenet_splits.xtest),
|
||||||
|
num_workers=workers,
|
||||||
|
pin_memory=True,
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"ImageNet-16-120 : train-loader has {:3d} batch with {:} per batch".format(
|
||||||
|
len(train_imagenet_loader), imagenet16_config.batch_size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"ImageNet-16-120 : valid-loader has {:3d} batch with {:} per batch".format(
|
||||||
|
len(valid_imagenet_loader), imagenet16_config.batch_size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"ImageNet-16-120 : test--loader has {:3d} batch with {:} per batch".format(
|
||||||
|
len(test__imagenet_loader), imagenet16_config.batch_size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# 'cifar10', 'cifar100', 'ImageNet16-120'
|
# 'cifar10', 'cifar100', 'ImageNet16-120'
|
||||||
loaders = {'cifar10@trainval': trainval_cifar10_loader,
|
loaders = {
|
||||||
'cifar10@train' : train_cifar10_loader,
|
"cifar10@trainval": trainval_cifar10_loader,
|
||||||
'cifar10@valid' : valid_cifar10_loader,
|
"cifar10@train": train_cifar10_loader,
|
||||||
'cifar10@test' : test__cifar10_loader,
|
"cifar10@valid": valid_cifar10_loader,
|
||||||
'cifar100@train' : train_cifar100_loader,
|
"cifar10@test": test__cifar10_loader,
|
||||||
'cifar100@valid' : valid_cifar100_loader,
|
"cifar100@train": train_cifar100_loader,
|
||||||
'cifar100@test' : test__cifar100_loader,
|
"cifar100@valid": valid_cifar100_loader,
|
||||||
'ImageNet16-120@train': train_imagenet_loader,
|
"cifar100@test": test__cifar100_loader,
|
||||||
'ImageNet16-120@valid': valid_imagenet_loader,
|
"ImageNet16-120@train": train_imagenet_loader,
|
||||||
'ImageNet16-120@test' : test__imagenet_loader}
|
"ImageNet16-120@valid": valid_imagenet_loader,
|
||||||
return loaders
|
"ImageNet16-120@test": test__imagenet_loader,
|
||||||
|
}
|
||||||
|
return loaders
|
||||||
|
@ -8,197 +8,201 @@ from torch.optim import Optimizer
|
|||||||
|
|
||||||
|
|
||||||
class _LRScheduler(object):
|
class _LRScheduler(object):
|
||||||
|
def __init__(self, optimizer, warmup_epochs, epochs):
|
||||||
|
if not isinstance(optimizer, Optimizer):
|
||||||
|
raise TypeError("{:} is not an Optimizer".format(type(optimizer).__name__))
|
||||||
|
self.optimizer = optimizer
|
||||||
|
for group in optimizer.param_groups:
|
||||||
|
group.setdefault("initial_lr", group["lr"])
|
||||||
|
self.base_lrs = list(map(lambda group: group["initial_lr"], optimizer.param_groups))
|
||||||
|
self.max_epochs = epochs
|
||||||
|
self.warmup_epochs = warmup_epochs
|
||||||
|
self.current_epoch = 0
|
||||||
|
self.current_iter = 0
|
||||||
|
|
||||||
def __init__(self, optimizer, warmup_epochs, epochs):
|
def extra_repr(self):
|
||||||
if not isinstance(optimizer, Optimizer):
|
return ""
|
||||||
raise TypeError('{:} is not an Optimizer'.format(type(optimizer).__name__))
|
|
||||||
self.optimizer = optimizer
|
|
||||||
for group in optimizer.param_groups:
|
|
||||||
group.setdefault('initial_lr', group['lr'])
|
|
||||||
self.base_lrs = list(map(lambda group: group['initial_lr'], optimizer.param_groups))
|
|
||||||
self.max_epochs = epochs
|
|
||||||
self.warmup_epochs = warmup_epochs
|
|
||||||
self.current_epoch = 0
|
|
||||||
self.current_iter = 0
|
|
||||||
|
|
||||||
def extra_repr(self):
|
def __repr__(self):
|
||||||
return ''
|
return "{name}(warmup={warmup_epochs}, max-epoch={max_epochs}, current::epoch={current_epoch}, iter={current_iter:.2f}".format(
|
||||||
|
name=self.__class__.__name__, **self.__dict__
|
||||||
|
) + ", {:})".format(
|
||||||
|
self.extra_repr()
|
||||||
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
def state_dict(self):
|
||||||
return ('{name}(warmup={warmup_epochs}, max-epoch={max_epochs}, current::epoch={current_epoch}, iter={current_iter:.2f}'.format(name=self.__class__.__name__, **self.__dict__)
|
return {key: value for key, value in self.__dict__.items() if key != "optimizer"}
|
||||||
+ ', {:})'.format(self.extra_repr()))
|
|
||||||
|
|
||||||
def state_dict(self):
|
def load_state_dict(self, state_dict):
|
||||||
return {key: value for key, value in self.__dict__.items() if key != 'optimizer'}
|
self.__dict__.update(state_dict)
|
||||||
|
|
||||||
def load_state_dict(self, state_dict):
|
def get_lr(self):
|
||||||
self.__dict__.update(state_dict)
|
raise NotImplementedError
|
||||||
|
|
||||||
def get_lr(self):
|
def get_min_info(self):
|
||||||
raise NotImplementedError
|
lrs = self.get_lr()
|
||||||
|
return "#LR=[{:.6f}~{:.6f}] epoch={:03d}, iter={:4.2f}#".format(
|
||||||
|
min(lrs), max(lrs), self.current_epoch, self.current_iter
|
||||||
|
)
|
||||||
|
|
||||||
def get_min_info(self):
|
def get_min_lr(self):
|
||||||
lrs = self.get_lr()
|
return min(self.get_lr())
|
||||||
return '#LR=[{:.6f}~{:.6f}] epoch={:03d}, iter={:4.2f}#'.format(min(lrs), max(lrs), self.current_epoch, self.current_iter)
|
|
||||||
|
|
||||||
def get_min_lr(self):
|
|
||||||
return min( self.get_lr() )
|
|
||||||
|
|
||||||
def update(self, cur_epoch, cur_iter):
|
|
||||||
if cur_epoch is not None:
|
|
||||||
assert isinstance(cur_epoch, int) and cur_epoch>=0, 'invalid cur-epoch : {:}'.format(cur_epoch)
|
|
||||||
self.current_epoch = cur_epoch
|
|
||||||
if cur_iter is not None:
|
|
||||||
assert isinstance(cur_iter, float) and cur_iter>=0, 'invalid cur-iter : {:}'.format(cur_iter)
|
|
||||||
self.current_iter = cur_iter
|
|
||||||
for param_group, lr in zip(self.optimizer.param_groups, self.get_lr()):
|
|
||||||
param_group['lr'] = lr
|
|
||||||
|
|
||||||
|
def update(self, cur_epoch, cur_iter):
|
||||||
|
if cur_epoch is not None:
|
||||||
|
assert isinstance(cur_epoch, int) and cur_epoch >= 0, "invalid cur-epoch : {:}".format(cur_epoch)
|
||||||
|
self.current_epoch = cur_epoch
|
||||||
|
if cur_iter is not None:
|
||||||
|
assert isinstance(cur_iter, float) and cur_iter >= 0, "invalid cur-iter : {:}".format(cur_iter)
|
||||||
|
self.current_iter = cur_iter
|
||||||
|
for param_group, lr in zip(self.optimizer.param_groups, self.get_lr()):
|
||||||
|
param_group["lr"] = lr
|
||||||
|
|
||||||
|
|
||||||
class CosineAnnealingLR(_LRScheduler):
|
class CosineAnnealingLR(_LRScheduler):
|
||||||
|
def __init__(self, optimizer, warmup_epochs, epochs, T_max, eta_min):
|
||||||
|
self.T_max = T_max
|
||||||
|
self.eta_min = eta_min
|
||||||
|
super(CosineAnnealingLR, self).__init__(optimizer, warmup_epochs, epochs)
|
||||||
|
|
||||||
def __init__(self, optimizer, warmup_epochs, epochs, T_max, eta_min):
|
def extra_repr(self):
|
||||||
self.T_max = T_max
|
return "type={:}, T-max={:}, eta-min={:}".format("cosine", self.T_max, self.eta_min)
|
||||||
self.eta_min = eta_min
|
|
||||||
super(CosineAnnealingLR, self).__init__(optimizer, warmup_epochs, epochs)
|
|
||||||
|
|
||||||
def extra_repr(self):
|
|
||||||
return 'type={:}, T-max={:}, eta-min={:}'.format('cosine', self.T_max, self.eta_min)
|
|
||||||
|
|
||||||
def get_lr(self):
|
|
||||||
lrs = []
|
|
||||||
for base_lr in self.base_lrs:
|
|
||||||
if self.current_epoch >= self.warmup_epochs and self.current_epoch < self.max_epochs:
|
|
||||||
last_epoch = self.current_epoch - self.warmup_epochs
|
|
||||||
#if last_epoch < self.T_max:
|
|
||||||
#if last_epoch < self.max_epochs:
|
|
||||||
lr = self.eta_min + (base_lr - self.eta_min) * (1 + math.cos(math.pi * last_epoch / self.T_max)) / 2
|
|
||||||
#else:
|
|
||||||
# lr = self.eta_min + (base_lr - self.eta_min) * (1 + math.cos(math.pi * (self.T_max-1.0) / self.T_max)) / 2
|
|
||||||
elif self.current_epoch >= self.max_epochs:
|
|
||||||
lr = self.eta_min
|
|
||||||
else:
|
|
||||||
lr = (self.current_epoch / self.warmup_epochs + self.current_iter / self.warmup_epochs) * base_lr
|
|
||||||
lrs.append( lr )
|
|
||||||
return lrs
|
|
||||||
|
|
||||||
|
def get_lr(self):
|
||||||
|
lrs = []
|
||||||
|
for base_lr in self.base_lrs:
|
||||||
|
if self.current_epoch >= self.warmup_epochs and self.current_epoch < self.max_epochs:
|
||||||
|
last_epoch = self.current_epoch - self.warmup_epochs
|
||||||
|
# if last_epoch < self.T_max:
|
||||||
|
# if last_epoch < self.max_epochs:
|
||||||
|
lr = self.eta_min + (base_lr - self.eta_min) * (1 + math.cos(math.pi * last_epoch / self.T_max)) / 2
|
||||||
|
# else:
|
||||||
|
# lr = self.eta_min + (base_lr - self.eta_min) * (1 + math.cos(math.pi * (self.T_max-1.0) / self.T_max)) / 2
|
||||||
|
elif self.current_epoch >= self.max_epochs:
|
||||||
|
lr = self.eta_min
|
||||||
|
else:
|
||||||
|
lr = (self.current_epoch / self.warmup_epochs + self.current_iter / self.warmup_epochs) * base_lr
|
||||||
|
lrs.append(lr)
|
||||||
|
return lrs
|
||||||
|
|
||||||
|
|
||||||
class MultiStepLR(_LRScheduler):
|
class MultiStepLR(_LRScheduler):
|
||||||
|
def __init__(self, optimizer, warmup_epochs, epochs, milestones, gammas):
|
||||||
|
assert len(milestones) == len(gammas), "invalid {:} vs {:}".format(len(milestones), len(gammas))
|
||||||
|
self.milestones = milestones
|
||||||
|
self.gammas = gammas
|
||||||
|
super(MultiStepLR, self).__init__(optimizer, warmup_epochs, epochs)
|
||||||
|
|
||||||
def __init__(self, optimizer, warmup_epochs, epochs, milestones, gammas):
|
def extra_repr(self):
|
||||||
assert len(milestones) == len(gammas), 'invalid {:} vs {:}'.format(len(milestones), len(gammas))
|
return "type={:}, milestones={:}, gammas={:}, base-lrs={:}".format(
|
||||||
self.milestones = milestones
|
"multistep", self.milestones, self.gammas, self.base_lrs
|
||||||
self.gammas = gammas
|
)
|
||||||
super(MultiStepLR, self).__init__(optimizer, warmup_epochs, epochs)
|
|
||||||
|
|
||||||
def extra_repr(self):
|
def get_lr(self):
|
||||||
return 'type={:}, milestones={:}, gammas={:}, base-lrs={:}'.format('multistep', self.milestones, self.gammas, self.base_lrs)
|
lrs = []
|
||||||
|
for base_lr in self.base_lrs:
|
||||||
def get_lr(self):
|
if self.current_epoch >= self.warmup_epochs:
|
||||||
lrs = []
|
last_epoch = self.current_epoch - self.warmup_epochs
|
||||||
for base_lr in self.base_lrs:
|
idx = bisect_right(self.milestones, last_epoch)
|
||||||
if self.current_epoch >= self.warmup_epochs:
|
lr = base_lr
|
||||||
last_epoch = self.current_epoch - self.warmup_epochs
|
for x in self.gammas[:idx]:
|
||||||
idx = bisect_right(self.milestones, last_epoch)
|
lr *= x
|
||||||
lr = base_lr
|
else:
|
||||||
for x in self.gammas[:idx]: lr *= x
|
lr = (self.current_epoch / self.warmup_epochs + self.current_iter / self.warmup_epochs) * base_lr
|
||||||
else:
|
lrs.append(lr)
|
||||||
lr = (self.current_epoch / self.warmup_epochs + self.current_iter / self.warmup_epochs) * base_lr
|
return lrs
|
||||||
lrs.append( lr )
|
|
||||||
return lrs
|
|
||||||
|
|
||||||
|
|
||||||
class ExponentialLR(_LRScheduler):
|
class ExponentialLR(_LRScheduler):
|
||||||
|
def __init__(self, optimizer, warmup_epochs, epochs, gamma):
|
||||||
|
self.gamma = gamma
|
||||||
|
super(ExponentialLR, self).__init__(optimizer, warmup_epochs, epochs)
|
||||||
|
|
||||||
def __init__(self, optimizer, warmup_epochs, epochs, gamma):
|
def extra_repr(self):
|
||||||
self.gamma = gamma
|
return "type={:}, gamma={:}, base-lrs={:}".format("exponential", self.gamma, self.base_lrs)
|
||||||
super(ExponentialLR, self).__init__(optimizer, warmup_epochs, epochs)
|
|
||||||
|
|
||||||
def extra_repr(self):
|
def get_lr(self):
|
||||||
return 'type={:}, gamma={:}, base-lrs={:}'.format('exponential', self.gamma, self.base_lrs)
|
lrs = []
|
||||||
|
for base_lr in self.base_lrs:
|
||||||
def get_lr(self):
|
if self.current_epoch >= self.warmup_epochs:
|
||||||
lrs = []
|
last_epoch = self.current_epoch - self.warmup_epochs
|
||||||
for base_lr in self.base_lrs:
|
assert last_epoch >= 0, "invalid last_epoch : {:}".format(last_epoch)
|
||||||
if self.current_epoch >= self.warmup_epochs:
|
lr = base_lr * (self.gamma ** last_epoch)
|
||||||
last_epoch = self.current_epoch - self.warmup_epochs
|
else:
|
||||||
assert last_epoch >= 0, 'invalid last_epoch : {:}'.format(last_epoch)
|
lr = (self.current_epoch / self.warmup_epochs + self.current_iter / self.warmup_epochs) * base_lr
|
||||||
lr = base_lr * (self.gamma ** last_epoch)
|
lrs.append(lr)
|
||||||
else:
|
return lrs
|
||||||
lr = (self.current_epoch / self.warmup_epochs + self.current_iter / self.warmup_epochs) * base_lr
|
|
||||||
lrs.append( lr )
|
|
||||||
return lrs
|
|
||||||
|
|
||||||
|
|
||||||
class LinearLR(_LRScheduler):
|
class LinearLR(_LRScheduler):
|
||||||
|
def __init__(self, optimizer, warmup_epochs, epochs, max_LR, min_LR):
|
||||||
|
self.max_LR = max_LR
|
||||||
|
self.min_LR = min_LR
|
||||||
|
super(LinearLR, self).__init__(optimizer, warmup_epochs, epochs)
|
||||||
|
|
||||||
def __init__(self, optimizer, warmup_epochs, epochs, max_LR, min_LR):
|
def extra_repr(self):
|
||||||
self.max_LR = max_LR
|
return "type={:}, max_LR={:}, min_LR={:}, base-lrs={:}".format(
|
||||||
self.min_LR = min_LR
|
"LinearLR", self.max_LR, self.min_LR, self.base_lrs
|
||||||
super(LinearLR, self).__init__(optimizer, warmup_epochs, epochs)
|
)
|
||||||
|
|
||||||
def extra_repr(self):
|
|
||||||
return 'type={:}, max_LR={:}, min_LR={:}, base-lrs={:}'.format('LinearLR', self.max_LR, self.min_LR, self.base_lrs)
|
|
||||||
|
|
||||||
def get_lr(self):
|
|
||||||
lrs = []
|
|
||||||
for base_lr in self.base_lrs:
|
|
||||||
if self.current_epoch >= self.warmup_epochs:
|
|
||||||
last_epoch = self.current_epoch - self.warmup_epochs
|
|
||||||
assert last_epoch >= 0, 'invalid last_epoch : {:}'.format(last_epoch)
|
|
||||||
ratio = (self.max_LR - self.min_LR) * last_epoch / self.max_epochs / self.max_LR
|
|
||||||
lr = base_lr * (1-ratio)
|
|
||||||
else:
|
|
||||||
lr = (self.current_epoch / self.warmup_epochs + self.current_iter / self.warmup_epochs) * base_lr
|
|
||||||
lrs.append( lr )
|
|
||||||
return lrs
|
|
||||||
|
|
||||||
|
def get_lr(self):
|
||||||
|
lrs = []
|
||||||
|
for base_lr in self.base_lrs:
|
||||||
|
if self.current_epoch >= self.warmup_epochs:
|
||||||
|
last_epoch = self.current_epoch - self.warmup_epochs
|
||||||
|
assert last_epoch >= 0, "invalid last_epoch : {:}".format(last_epoch)
|
||||||
|
ratio = (self.max_LR - self.min_LR) * last_epoch / self.max_epochs / self.max_LR
|
||||||
|
lr = base_lr * (1 - ratio)
|
||||||
|
else:
|
||||||
|
lr = (self.current_epoch / self.warmup_epochs + self.current_iter / self.warmup_epochs) * base_lr
|
||||||
|
lrs.append(lr)
|
||||||
|
return lrs
|
||||||
|
|
||||||
|
|
||||||
class CrossEntropyLabelSmooth(nn.Module):
|
class CrossEntropyLabelSmooth(nn.Module):
|
||||||
|
def __init__(self, num_classes, epsilon):
|
||||||
|
super(CrossEntropyLabelSmooth, self).__init__()
|
||||||
|
self.num_classes = num_classes
|
||||||
|
self.epsilon = epsilon
|
||||||
|
self.logsoftmax = nn.LogSoftmax(dim=1)
|
||||||
|
|
||||||
def __init__(self, num_classes, epsilon):
|
def forward(self, inputs, targets):
|
||||||
super(CrossEntropyLabelSmooth, self).__init__()
|
log_probs = self.logsoftmax(inputs)
|
||||||
self.num_classes = num_classes
|
targets = torch.zeros_like(log_probs).scatter_(1, targets.unsqueeze(1), 1)
|
||||||
self.epsilon = epsilon
|
targets = (1 - self.epsilon) * targets + self.epsilon / self.num_classes
|
||||||
self.logsoftmax = nn.LogSoftmax(dim=1)
|
loss = (-targets * log_probs).mean(0).sum()
|
||||||
|
return loss
|
||||||
def forward(self, inputs, targets):
|
|
||||||
log_probs = self.logsoftmax(inputs)
|
|
||||||
targets = torch.zeros_like(log_probs).scatter_(1, targets.unsqueeze(1), 1)
|
|
||||||
targets = (1 - self.epsilon) * targets + self.epsilon / self.num_classes
|
|
||||||
loss = (-targets * log_probs).mean(0).sum()
|
|
||||||
return loss
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_optim_scheduler(parameters, config):
|
def get_optim_scheduler(parameters, config):
|
||||||
assert hasattr(config, 'optim') and hasattr(config, 'scheduler') and hasattr(config, 'criterion'), 'config must have optim / scheduler / criterion keys instead of {:}'.format(config)
|
assert (
|
||||||
if config.optim == 'SGD':
|
hasattr(config, "optim") and hasattr(config, "scheduler") and hasattr(config, "criterion")
|
||||||
optim = torch.optim.SGD(parameters, config.LR, momentum=config.momentum, weight_decay=config.decay, nesterov=config.nesterov)
|
), "config must have optim / scheduler / criterion keys instead of {:}".format(config)
|
||||||
elif config.optim == 'RMSprop':
|
if config.optim == "SGD":
|
||||||
optim = torch.optim.RMSprop(parameters, config.LR, momentum=config.momentum, weight_decay=config.decay)
|
optim = torch.optim.SGD(
|
||||||
else:
|
parameters, config.LR, momentum=config.momentum, weight_decay=config.decay, nesterov=config.nesterov
|
||||||
raise ValueError('invalid optim : {:}'.format(config.optim))
|
)
|
||||||
|
elif config.optim == "RMSprop":
|
||||||
|
optim = torch.optim.RMSprop(parameters, config.LR, momentum=config.momentum, weight_decay=config.decay)
|
||||||
|
else:
|
||||||
|
raise ValueError("invalid optim : {:}".format(config.optim))
|
||||||
|
|
||||||
if config.scheduler == 'cos':
|
if config.scheduler == "cos":
|
||||||
T_max = getattr(config, 'T_max', config.epochs)
|
T_max = getattr(config, "T_max", config.epochs)
|
||||||
scheduler = CosineAnnealingLR(optim, config.warmup, config.epochs, T_max, config.eta_min)
|
scheduler = CosineAnnealingLR(optim, config.warmup, config.epochs, T_max, config.eta_min)
|
||||||
elif config.scheduler == 'multistep':
|
elif config.scheduler == "multistep":
|
||||||
scheduler = MultiStepLR(optim, config.warmup, config.epochs, config.milestones, config.gammas)
|
scheduler = MultiStepLR(optim, config.warmup, config.epochs, config.milestones, config.gammas)
|
||||||
elif config.scheduler == 'exponential':
|
elif config.scheduler == "exponential":
|
||||||
scheduler = ExponentialLR(optim, config.warmup, config.epochs, config.gamma)
|
scheduler = ExponentialLR(optim, config.warmup, config.epochs, config.gamma)
|
||||||
elif config.scheduler == 'linear':
|
elif config.scheduler == "linear":
|
||||||
scheduler = LinearLR(optim, config.warmup, config.epochs, config.LR, config.LR_min)
|
scheduler = LinearLR(optim, config.warmup, config.epochs, config.LR, config.LR_min)
|
||||||
else:
|
else:
|
||||||
raise ValueError('invalid scheduler : {:}'.format(config.scheduler))
|
raise ValueError("invalid scheduler : {:}".format(config.scheduler))
|
||||||
|
|
||||||
if config.criterion == 'Softmax':
|
if config.criterion == "Softmax":
|
||||||
criterion = torch.nn.CrossEntropyLoss()
|
criterion = torch.nn.CrossEntropyLoss()
|
||||||
elif config.criterion == 'SmoothSoftmax':
|
elif config.criterion == "SmoothSoftmax":
|
||||||
criterion = CrossEntropyLabelSmooth(config.class_num, config.label_smooth)
|
criterion = CrossEntropyLabelSmooth(config.class_num, config.label_smooth)
|
||||||
else:
|
else:
|
||||||
raise ValueError('invalid criterion : {:}'.format(config.criterion))
|
raise ValueError("invalid criterion : {:}".format(config.criterion))
|
||||||
return optim, scheduler, criterion
|
return optim, scheduler, criterion
|
||||||
|
@ -7,11 +7,12 @@ from qlib.utils import init_instance_by_config
|
|||||||
from qlib.workflow import R
|
from qlib.workflow import R
|
||||||
from qlib.utils import flatten_dict
|
from qlib.utils import flatten_dict
|
||||||
from qlib.log import set_log_basic_config
|
from qlib.log import set_log_basic_config
|
||||||
|
from qlib.log import get_module_logger
|
||||||
|
|
||||||
|
|
||||||
def update_gpu(config, gpu):
|
def update_gpu(config, gpu):
|
||||||
config = config.copy()
|
config = config.copy()
|
||||||
if "task" in config and "GPU" in config["task"]["model"]:
|
if "task" in config and "moodel" in config["task"] and "GPU" in config["task"]["model"]:
|
||||||
config["task"]["model"]["GPU"] = gpu
|
config["task"]["model"]["GPU"] = gpu
|
||||||
elif "model" in config and "GPU" in config["model"]:
|
elif "model" in config and "GPU" in config["model"]:
|
||||||
config["model"]["GPU"] = gpu
|
config["model"]["GPU"] = gpu
|
||||||
@ -29,11 +30,6 @@ def update_market(config, market):
|
|||||||
|
|
||||||
def run_exp(task_config, dataset, experiment_name, recorder_name, uri):
|
def run_exp(task_config, dataset, experiment_name, recorder_name, uri):
|
||||||
|
|
||||||
# model initiaiton
|
|
||||||
print("")
|
|
||||||
print("[{:}] - [{:}]: {:}".format(experiment_name, recorder_name, uri))
|
|
||||||
print("dataset={:}".format(dataset))
|
|
||||||
|
|
||||||
model = init_instance_by_config(task_config["model"])
|
model = init_instance_by_config(task_config["model"])
|
||||||
|
|
||||||
# start exp
|
# start exp
|
||||||
@ -41,6 +37,10 @@ def run_exp(task_config, dataset, experiment_name, recorder_name, uri):
|
|||||||
|
|
||||||
log_file = R.get_recorder().root_uri / "{:}.log".format(experiment_name)
|
log_file = R.get_recorder().root_uri / "{:}.log".format(experiment_name)
|
||||||
set_log_basic_config(log_file)
|
set_log_basic_config(log_file)
|
||||||
|
logger = get_module_logger("q.run_exp")
|
||||||
|
logger.info("task_config={:}".format(task_config))
|
||||||
|
logger.info("[{:}] - [{:}]: {:}".format(experiment_name, recorder_name, uri))
|
||||||
|
logger.info("dataset={:}".format(dataset))
|
||||||
|
|
||||||
# train model
|
# train model
|
||||||
R.log_params(**flatten_dict(task_config))
|
R.log_params(**flatten_dict(task_config))
|
||||||
|
@ -3,124 +3,170 @@
|
|||||||
##################################################
|
##################################################
|
||||||
import os, sys, time, torch
|
import os, sys, time, torch
|
||||||
from log_utils import AverageMeter, time_string
|
from log_utils import AverageMeter, time_string
|
||||||
from utils import obtain_accuracy
|
from utils import obtain_accuracy
|
||||||
from models import change_key
|
from models import change_key
|
||||||
|
|
||||||
|
|
||||||
def get_flop_loss(expected_flop, flop_cur, flop_need, flop_tolerant):
|
def get_flop_loss(expected_flop, flop_cur, flop_need, flop_tolerant):
|
||||||
expected_flop = torch.mean( expected_flop )
|
expected_flop = torch.mean(expected_flop)
|
||||||
|
|
||||||
if flop_cur < flop_need - flop_tolerant: # Too Small FLOP
|
if flop_cur < flop_need - flop_tolerant: # Too Small FLOP
|
||||||
loss = - torch.log( expected_flop )
|
loss = -torch.log(expected_flop)
|
||||||
#elif flop_cur > flop_need + flop_tolerant: # Too Large FLOP
|
# elif flop_cur > flop_need + flop_tolerant: # Too Large FLOP
|
||||||
elif flop_cur > flop_need: # Too Large FLOP
|
elif flop_cur > flop_need: # Too Large FLOP
|
||||||
loss = torch.log( expected_flop )
|
loss = torch.log(expected_flop)
|
||||||
else: # Required FLOP
|
else: # Required FLOP
|
||||||
loss = None
|
loss = None
|
||||||
if loss is None: return 0, 0
|
if loss is None:
|
||||||
else : return loss, loss.item()
|
return 0, 0
|
||||||
|
else:
|
||||||
|
return loss, loss.item()
|
||||||
|
|
||||||
|
|
||||||
def search_train(search_loader, network, criterion, scheduler, base_optimizer, arch_optimizer, optim_config, extra_info, print_freq, logger):
|
def search_train(
|
||||||
data_time, batch_time = AverageMeter(), AverageMeter()
|
search_loader,
|
||||||
base_losses, arch_losses, top1, top5 = AverageMeter(), AverageMeter(), AverageMeter(), AverageMeter()
|
network,
|
||||||
arch_cls_losses, arch_flop_losses = AverageMeter(), AverageMeter()
|
criterion,
|
||||||
epoch_str, flop_need, flop_weight, flop_tolerant = extra_info['epoch-str'], extra_info['FLOP-exp'], extra_info['FLOP-weight'], extra_info['FLOP-tolerant']
|
scheduler,
|
||||||
|
base_optimizer,
|
||||||
|
arch_optimizer,
|
||||||
|
optim_config,
|
||||||
|
extra_info,
|
||||||
|
print_freq,
|
||||||
|
logger,
|
||||||
|
):
|
||||||
|
data_time, batch_time = AverageMeter(), AverageMeter()
|
||||||
|
base_losses, arch_losses, top1, top5 = AverageMeter(), AverageMeter(), AverageMeter(), AverageMeter()
|
||||||
|
arch_cls_losses, arch_flop_losses = AverageMeter(), AverageMeter()
|
||||||
|
epoch_str, flop_need, flop_weight, flop_tolerant = (
|
||||||
|
extra_info["epoch-str"],
|
||||||
|
extra_info["FLOP-exp"],
|
||||||
|
extra_info["FLOP-weight"],
|
||||||
|
extra_info["FLOP-tolerant"],
|
||||||
|
)
|
||||||
|
|
||||||
network.train()
|
network.train()
|
||||||
logger.log('[Search] : {:}, FLOP-Require={:.2f} MB, FLOP-WEIGHT={:.2f}'.format(epoch_str, flop_need, flop_weight))
|
logger.log("[Search] : {:}, FLOP-Require={:.2f} MB, FLOP-WEIGHT={:.2f}".format(epoch_str, flop_need, flop_weight))
|
||||||
end = time.time()
|
|
||||||
network.apply( change_key('search_mode', 'search') )
|
|
||||||
for step, (base_inputs, base_targets, arch_inputs, arch_targets) in enumerate(search_loader):
|
|
||||||
scheduler.update(None, 1.0 * step / len(search_loader))
|
|
||||||
# calculate prediction and loss
|
|
||||||
base_targets = base_targets.cuda(non_blocking=True)
|
|
||||||
arch_targets = arch_targets.cuda(non_blocking=True)
|
|
||||||
# measure data loading time
|
|
||||||
data_time.update(time.time() - end)
|
|
||||||
|
|
||||||
# update the weights
|
|
||||||
base_optimizer.zero_grad()
|
|
||||||
logits, expected_flop = network(base_inputs)
|
|
||||||
#network.apply( change_key('search_mode', 'basic') )
|
|
||||||
#features, logits = network(base_inputs)
|
|
||||||
base_loss = criterion(logits, base_targets)
|
|
||||||
base_loss.backward()
|
|
||||||
base_optimizer.step()
|
|
||||||
# record
|
|
||||||
prec1, prec5 = obtain_accuracy(logits.data, base_targets.data, topk=(1, 5))
|
|
||||||
base_losses.update(base_loss.item(), base_inputs.size(0))
|
|
||||||
top1.update (prec1.item(), base_inputs.size(0))
|
|
||||||
top5.update (prec5.item(), base_inputs.size(0))
|
|
||||||
|
|
||||||
# update the architecture
|
|
||||||
arch_optimizer.zero_grad()
|
|
||||||
logits, expected_flop = network(arch_inputs)
|
|
||||||
flop_cur = network.module.get_flop('genotype', None, None)
|
|
||||||
flop_loss, flop_loss_scale = get_flop_loss(expected_flop, flop_cur, flop_need, flop_tolerant)
|
|
||||||
acls_loss = criterion(logits, arch_targets)
|
|
||||||
arch_loss = acls_loss + flop_loss * flop_weight
|
|
||||||
arch_loss.backward()
|
|
||||||
arch_optimizer.step()
|
|
||||||
|
|
||||||
# record
|
|
||||||
arch_losses.update(arch_loss.item(), arch_inputs.size(0))
|
|
||||||
arch_flop_losses.update(flop_loss_scale, arch_inputs.size(0))
|
|
||||||
arch_cls_losses.update (acls_loss.item(), arch_inputs.size(0))
|
|
||||||
|
|
||||||
# measure elapsed time
|
|
||||||
batch_time.update(time.time() - end)
|
|
||||||
end = time.time()
|
end = time.time()
|
||||||
if step % print_freq == 0 or (step+1) == len(search_loader):
|
network.apply(change_key("search_mode", "search"))
|
||||||
Sstr = '**TRAIN** ' + time_string() + ' [{:}][{:03d}/{:03d}]'.format(epoch_str, step, len(search_loader))
|
for step, (base_inputs, base_targets, arch_inputs, arch_targets) in enumerate(search_loader):
|
||||||
Tstr = 'Time {batch_time.val:.2f} ({batch_time.avg:.2f}) Data {data_time.val:.2f} ({data_time.avg:.2f})'.format(batch_time=batch_time, data_time=data_time)
|
scheduler.update(None, 1.0 * step / len(search_loader))
|
||||||
Lstr = 'Base-Loss {loss.val:.3f} ({loss.avg:.3f}) Prec@1 {top1.val:.2f} ({top1.avg:.2f}) Prec@5 {top5.val:.2f} ({top5.avg:.2f})'.format(loss=base_losses, top1=top1, top5=top5)
|
# calculate prediction and loss
|
||||||
Vstr = 'Acls-loss {aloss.val:.3f} ({aloss.avg:.3f}) FLOP-Loss {floss.val:.3f} ({floss.avg:.3f}) Arch-Loss {loss.val:.3f} ({loss.avg:.3f})'.format(aloss=arch_cls_losses, floss=arch_flop_losses, loss=arch_losses)
|
base_targets = base_targets.cuda(non_blocking=True)
|
||||||
logger.log(Sstr + ' ' + Tstr + ' ' + Lstr + ' ' + Vstr)
|
arch_targets = arch_targets.cuda(non_blocking=True)
|
||||||
#Istr = 'Bsz={:} Asz={:}'.format(list(base_inputs.size()), list(arch_inputs.size()))
|
# measure data loading time
|
||||||
#logger.log(Sstr + ' ' + Tstr + ' ' + Lstr + ' ' + Vstr + ' ' + Istr)
|
data_time.update(time.time() - end)
|
||||||
#print(network.module.get_arch_info())
|
|
||||||
#print(network.module.width_attentions[0])
|
|
||||||
#print(network.module.width_attentions[1])
|
|
||||||
|
|
||||||
logger.log(' **TRAIN** Prec@1 {top1.avg:.2f} Prec@5 {top5.avg:.2f} Error@1 {error1:.2f} Error@5 {error5:.2f} Base-Loss:{baseloss:.3f}, Arch-Loss={archloss:.3f}'.format(top1=top1, top5=top5, error1=100-top1.avg, error5=100-top5.avg, baseloss=base_losses.avg, archloss=arch_losses.avg))
|
# update the weights
|
||||||
return base_losses.avg, arch_losses.avg, top1.avg, top5.avg
|
base_optimizer.zero_grad()
|
||||||
|
logits, expected_flop = network(base_inputs)
|
||||||
|
# network.apply( change_key('search_mode', 'basic') )
|
||||||
|
# features, logits = network(base_inputs)
|
||||||
|
base_loss = criterion(logits, base_targets)
|
||||||
|
base_loss.backward()
|
||||||
|
base_optimizer.step()
|
||||||
|
# record
|
||||||
|
prec1, prec5 = obtain_accuracy(logits.data, base_targets.data, topk=(1, 5))
|
||||||
|
base_losses.update(base_loss.item(), base_inputs.size(0))
|
||||||
|
top1.update(prec1.item(), base_inputs.size(0))
|
||||||
|
top5.update(prec5.item(), base_inputs.size(0))
|
||||||
|
|
||||||
|
# update the architecture
|
||||||
|
arch_optimizer.zero_grad()
|
||||||
|
logits, expected_flop = network(arch_inputs)
|
||||||
|
flop_cur = network.module.get_flop("genotype", None, None)
|
||||||
|
flop_loss, flop_loss_scale = get_flop_loss(expected_flop, flop_cur, flop_need, flop_tolerant)
|
||||||
|
acls_loss = criterion(logits, arch_targets)
|
||||||
|
arch_loss = acls_loss + flop_loss * flop_weight
|
||||||
|
arch_loss.backward()
|
||||||
|
arch_optimizer.step()
|
||||||
|
|
||||||
|
# record
|
||||||
|
arch_losses.update(arch_loss.item(), arch_inputs.size(0))
|
||||||
|
arch_flop_losses.update(flop_loss_scale, arch_inputs.size(0))
|
||||||
|
arch_cls_losses.update(acls_loss.item(), arch_inputs.size(0))
|
||||||
|
|
||||||
|
# measure elapsed time
|
||||||
|
batch_time.update(time.time() - end)
|
||||||
|
end = time.time()
|
||||||
|
if step % print_freq == 0 or (step + 1) == len(search_loader):
|
||||||
|
Sstr = "**TRAIN** " + time_string() + " [{:}][{:03d}/{:03d}]".format(epoch_str, step, len(search_loader))
|
||||||
|
Tstr = "Time {batch_time.val:.2f} ({batch_time.avg:.2f}) Data {data_time.val:.2f} ({data_time.avg:.2f})".format(
|
||||||
|
batch_time=batch_time, data_time=data_time
|
||||||
|
)
|
||||||
|
Lstr = "Base-Loss {loss.val:.3f} ({loss.avg:.3f}) Prec@1 {top1.val:.2f} ({top1.avg:.2f}) Prec@5 {top5.val:.2f} ({top5.avg:.2f})".format(
|
||||||
|
loss=base_losses, top1=top1, top5=top5
|
||||||
|
)
|
||||||
|
Vstr = "Acls-loss {aloss.val:.3f} ({aloss.avg:.3f}) FLOP-Loss {floss.val:.3f} ({floss.avg:.3f}) Arch-Loss {loss.val:.3f} ({loss.avg:.3f})".format(
|
||||||
|
aloss=arch_cls_losses, floss=arch_flop_losses, loss=arch_losses
|
||||||
|
)
|
||||||
|
logger.log(Sstr + " " + Tstr + " " + Lstr + " " + Vstr)
|
||||||
|
# Istr = 'Bsz={:} Asz={:}'.format(list(base_inputs.size()), list(arch_inputs.size()))
|
||||||
|
# logger.log(Sstr + ' ' + Tstr + ' ' + Lstr + ' ' + Vstr + ' ' + Istr)
|
||||||
|
# print(network.module.get_arch_info())
|
||||||
|
# print(network.module.width_attentions[0])
|
||||||
|
# print(network.module.width_attentions[1])
|
||||||
|
|
||||||
|
logger.log(
|
||||||
|
" **TRAIN** Prec@1 {top1.avg:.2f} Prec@5 {top5.avg:.2f} Error@1 {error1:.2f} Error@5 {error5:.2f} Base-Loss:{baseloss:.3f}, Arch-Loss={archloss:.3f}".format(
|
||||||
|
top1=top1,
|
||||||
|
top5=top5,
|
||||||
|
error1=100 - top1.avg,
|
||||||
|
error5=100 - top5.avg,
|
||||||
|
baseloss=base_losses.avg,
|
||||||
|
archloss=arch_losses.avg,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return base_losses.avg, arch_losses.avg, top1.avg, top5.avg
|
||||||
|
|
||||||
|
|
||||||
def search_valid(xloader, network, criterion, extra_info, print_freq, logger):
|
def search_valid(xloader, network, criterion, extra_info, print_freq, logger):
|
||||||
data_time, batch_time, losses, top1, top5 = AverageMeter(), AverageMeter(), AverageMeter(), AverageMeter(), AverageMeter()
|
data_time, batch_time, losses, top1, top5 = (
|
||||||
|
AverageMeter(),
|
||||||
|
AverageMeter(),
|
||||||
|
AverageMeter(),
|
||||||
|
AverageMeter(),
|
||||||
|
AverageMeter(),
|
||||||
|
)
|
||||||
|
|
||||||
network.eval()
|
network.eval()
|
||||||
network.apply( change_key('search_mode', 'search') )
|
network.apply(change_key("search_mode", "search"))
|
||||||
end = time.time()
|
end = time.time()
|
||||||
#logger.log('Starting evaluating {:}'.format(epoch_info))
|
# logger.log('Starting evaluating {:}'.format(epoch_info))
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
for i, (inputs, targets) in enumerate(xloader):
|
for i, (inputs, targets) in enumerate(xloader):
|
||||||
# measure data loading time
|
# measure data loading time
|
||||||
data_time.update(time.time() - end)
|
data_time.update(time.time() - end)
|
||||||
# calculate prediction and loss
|
# calculate prediction and loss
|
||||||
targets = targets.cuda(non_blocking=True)
|
targets = targets.cuda(non_blocking=True)
|
||||||
|
|
||||||
logits, expected_flop = network(inputs)
|
logits, expected_flop = network(inputs)
|
||||||
loss = criterion(logits, targets)
|
loss = criterion(logits, targets)
|
||||||
# record
|
# record
|
||||||
prec1, prec5 = obtain_accuracy(logits.data, targets.data, topk=(1, 5))
|
prec1, prec5 = obtain_accuracy(logits.data, targets.data, topk=(1, 5))
|
||||||
losses.update(loss.item(), inputs.size(0))
|
losses.update(loss.item(), inputs.size(0))
|
||||||
top1.update (prec1.item(), inputs.size(0))
|
top1.update(prec1.item(), inputs.size(0))
|
||||||
top5.update (prec5.item(), inputs.size(0))
|
top5.update(prec5.item(), inputs.size(0))
|
||||||
|
|
||||||
# measure elapsed time
|
# measure elapsed time
|
||||||
batch_time.update(time.time() - end)
|
batch_time.update(time.time() - end)
|
||||||
end = time.time()
|
end = time.time()
|
||||||
|
|
||||||
if i % print_freq == 0 or (i+1) == len(xloader):
|
if i % print_freq == 0 or (i + 1) == len(xloader):
|
||||||
Sstr = '**VALID** ' + time_string() + ' [{:}][{:03d}/{:03d}]'.format(extra_info, i, len(xloader))
|
Sstr = "**VALID** " + time_string() + " [{:}][{:03d}/{:03d}]".format(extra_info, i, len(xloader))
|
||||||
Tstr = 'Time {batch_time.val:.2f} ({batch_time.avg:.2f}) Data {data_time.val:.2f} ({data_time.avg:.2f})'.format(batch_time=batch_time, data_time=data_time)
|
Tstr = "Time {batch_time.val:.2f} ({batch_time.avg:.2f}) Data {data_time.val:.2f} ({data_time.avg:.2f})".format(
|
||||||
Lstr = 'Loss {loss.val:.3f} ({loss.avg:.3f}) Prec@1 {top1.val:.2f} ({top1.avg:.2f}) Prec@5 {top5.val:.2f} ({top5.avg:.2f})'.format(loss=losses, top1=top1, top5=top5)
|
batch_time=batch_time, data_time=data_time
|
||||||
Istr = 'Size={:}'.format(list(inputs.size()))
|
)
|
||||||
logger.log(Sstr + ' ' + Tstr + ' ' + Lstr + ' ' + Istr)
|
Lstr = "Loss {loss.val:.3f} ({loss.avg:.3f}) Prec@1 {top1.val:.2f} ({top1.avg:.2f}) Prec@5 {top5.val:.2f} ({top5.avg:.2f})".format(
|
||||||
|
loss=losses, top1=top1, top5=top5
|
||||||
|
)
|
||||||
|
Istr = "Size={:}".format(list(inputs.size()))
|
||||||
|
logger.log(Sstr + " " + Tstr + " " + Lstr + " " + Istr)
|
||||||
|
|
||||||
logger.log(' **VALID** Prec@1 {top1.avg:.2f} Prec@5 {top5.avg:.2f} Error@1 {error1:.2f} Error@5 {error5:.2f} Loss:{loss:.3f}'.format(top1=top1, top5=top5, error1=100-top1.avg, error5=100-top5.avg, loss=losses.avg))
|
logger.log(
|
||||||
|
" **VALID** Prec@1 {top1.avg:.2f} Prec@5 {top5.avg:.2f} Error@1 {error1:.2f} Error@5 {error5:.2f} Loss:{loss:.3f}".format(
|
||||||
return losses.avg, top1.avg, top5.avg
|
top1=top1, top5=top5, error1=100 - top1.avg, error5=100 - top5.avg, loss=losses.avg
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return losses.avg, top1.avg, top5.avg
|
||||||
|
@ -3,85 +3,118 @@
|
|||||||
##################################################
|
##################################################
|
||||||
import os, sys, time, torch
|
import os, sys, time, torch
|
||||||
from log_utils import AverageMeter, time_string
|
from log_utils import AverageMeter, time_string
|
||||||
from utils import obtain_accuracy
|
from utils import obtain_accuracy
|
||||||
from models import change_key
|
from models import change_key
|
||||||
|
|
||||||
|
|
||||||
def get_flop_loss(expected_flop, flop_cur, flop_need, flop_tolerant):
|
def get_flop_loss(expected_flop, flop_cur, flop_need, flop_tolerant):
|
||||||
expected_flop = torch.mean( expected_flop )
|
expected_flop = torch.mean(expected_flop)
|
||||||
|
|
||||||
if flop_cur < flop_need - flop_tolerant: # Too Small FLOP
|
if flop_cur < flop_need - flop_tolerant: # Too Small FLOP
|
||||||
loss = - torch.log( expected_flop )
|
loss = -torch.log(expected_flop)
|
||||||
#elif flop_cur > flop_need + flop_tolerant: # Too Large FLOP
|
# elif flop_cur > flop_need + flop_tolerant: # Too Large FLOP
|
||||||
elif flop_cur > flop_need: # Too Large FLOP
|
elif flop_cur > flop_need: # Too Large FLOP
|
||||||
loss = torch.log( expected_flop )
|
loss = torch.log(expected_flop)
|
||||||
else: # Required FLOP
|
else: # Required FLOP
|
||||||
loss = None
|
loss = None
|
||||||
if loss is None: return 0, 0
|
if loss is None:
|
||||||
else : return loss, loss.item()
|
return 0, 0
|
||||||
|
else:
|
||||||
|
return loss, loss.item()
|
||||||
|
|
||||||
|
|
||||||
def search_train_v2(search_loader, network, criterion, scheduler, base_optimizer, arch_optimizer, optim_config, extra_info, print_freq, logger):
|
def search_train_v2(
|
||||||
data_time, batch_time = AverageMeter(), AverageMeter()
|
search_loader,
|
||||||
base_losses, arch_losses, top1, top5 = AverageMeter(), AverageMeter(), AverageMeter(), AverageMeter()
|
network,
|
||||||
arch_cls_losses, arch_flop_losses = AverageMeter(), AverageMeter()
|
criterion,
|
||||||
epoch_str, flop_need, flop_weight, flop_tolerant = extra_info['epoch-str'], extra_info['FLOP-exp'], extra_info['FLOP-weight'], extra_info['FLOP-tolerant']
|
scheduler,
|
||||||
|
base_optimizer,
|
||||||
|
arch_optimizer,
|
||||||
|
optim_config,
|
||||||
|
extra_info,
|
||||||
|
print_freq,
|
||||||
|
logger,
|
||||||
|
):
|
||||||
|
data_time, batch_time = AverageMeter(), AverageMeter()
|
||||||
|
base_losses, arch_losses, top1, top5 = AverageMeter(), AverageMeter(), AverageMeter(), AverageMeter()
|
||||||
|
arch_cls_losses, arch_flop_losses = AverageMeter(), AverageMeter()
|
||||||
|
epoch_str, flop_need, flop_weight, flop_tolerant = (
|
||||||
|
extra_info["epoch-str"],
|
||||||
|
extra_info["FLOP-exp"],
|
||||||
|
extra_info["FLOP-weight"],
|
||||||
|
extra_info["FLOP-tolerant"],
|
||||||
|
)
|
||||||
|
|
||||||
network.train()
|
network.train()
|
||||||
logger.log('[Search] : {:}, FLOP-Require={:.2f} MB, FLOP-WEIGHT={:.2f}'.format(epoch_str, flop_need, flop_weight))
|
logger.log("[Search] : {:}, FLOP-Require={:.2f} MB, FLOP-WEIGHT={:.2f}".format(epoch_str, flop_need, flop_weight))
|
||||||
end = time.time()
|
|
||||||
network.apply( change_key('search_mode', 'search') )
|
|
||||||
for step, (base_inputs, base_targets, arch_inputs, arch_targets) in enumerate(search_loader):
|
|
||||||
scheduler.update(None, 1.0 * step / len(search_loader))
|
|
||||||
# calculate prediction and loss
|
|
||||||
base_targets = base_targets.cuda(non_blocking=True)
|
|
||||||
arch_targets = arch_targets.cuda(non_blocking=True)
|
|
||||||
# measure data loading time
|
|
||||||
data_time.update(time.time() - end)
|
|
||||||
|
|
||||||
# update the weights
|
|
||||||
base_optimizer.zero_grad()
|
|
||||||
logits, expected_flop = network(base_inputs)
|
|
||||||
base_loss = criterion(logits, base_targets)
|
|
||||||
base_loss.backward()
|
|
||||||
base_optimizer.step()
|
|
||||||
# record
|
|
||||||
prec1, prec5 = obtain_accuracy(logits.data, base_targets.data, topk=(1, 5))
|
|
||||||
base_losses.update(base_loss.item(), base_inputs.size(0))
|
|
||||||
top1.update (prec1.item(), base_inputs.size(0))
|
|
||||||
top5.update (prec5.item(), base_inputs.size(0))
|
|
||||||
|
|
||||||
# update the architecture
|
|
||||||
arch_optimizer.zero_grad()
|
|
||||||
logits, expected_flop = network(arch_inputs)
|
|
||||||
flop_cur = network.module.get_flop('genotype', None, None)
|
|
||||||
flop_loss, flop_loss_scale = get_flop_loss(expected_flop, flop_cur, flop_need, flop_tolerant)
|
|
||||||
acls_loss = criterion(logits, arch_targets)
|
|
||||||
arch_loss = acls_loss + flop_loss * flop_weight
|
|
||||||
arch_loss.backward()
|
|
||||||
arch_optimizer.step()
|
|
||||||
|
|
||||||
# record
|
|
||||||
arch_losses.update(arch_loss.item(), arch_inputs.size(0))
|
|
||||||
arch_flop_losses.update(flop_loss_scale, arch_inputs.size(0))
|
|
||||||
arch_cls_losses.update (acls_loss.item(), arch_inputs.size(0))
|
|
||||||
|
|
||||||
# measure elapsed time
|
|
||||||
batch_time.update(time.time() - end)
|
|
||||||
end = time.time()
|
end = time.time()
|
||||||
if step % print_freq == 0 or (step+1) == len(search_loader):
|
network.apply(change_key("search_mode", "search"))
|
||||||
Sstr = '**TRAIN** ' + time_string() + ' [{:}][{:03d}/{:03d}]'.format(epoch_str, step, len(search_loader))
|
for step, (base_inputs, base_targets, arch_inputs, arch_targets) in enumerate(search_loader):
|
||||||
Tstr = 'Time {batch_time.val:.2f} ({batch_time.avg:.2f}) Data {data_time.val:.2f} ({data_time.avg:.2f})'.format(batch_time=batch_time, data_time=data_time)
|
scheduler.update(None, 1.0 * step / len(search_loader))
|
||||||
Lstr = 'Base-Loss {loss.val:.3f} ({loss.avg:.3f}) Prec@1 {top1.val:.2f} ({top1.avg:.2f}) Prec@5 {top5.val:.2f} ({top5.avg:.2f})'.format(loss=base_losses, top1=top1, top5=top5)
|
# calculate prediction and loss
|
||||||
Vstr = 'Acls-loss {aloss.val:.3f} ({aloss.avg:.3f}) FLOP-Loss {floss.val:.3f} ({floss.avg:.3f}) Arch-Loss {loss.val:.3f} ({loss.avg:.3f})'.format(aloss=arch_cls_losses, floss=arch_flop_losses, loss=arch_losses)
|
base_targets = base_targets.cuda(non_blocking=True)
|
||||||
logger.log(Sstr + ' ' + Tstr + ' ' + Lstr + ' ' + Vstr)
|
arch_targets = arch_targets.cuda(non_blocking=True)
|
||||||
#num_bytes = torch.cuda.max_memory_allocated( next(network.parameters()).device ) * 1.0
|
# measure data loading time
|
||||||
#logger.log(Sstr + ' ' + Tstr + ' ' + Lstr + ' ' + Vstr + ' GPU={:.2f}MB'.format(num_bytes/1e6))
|
data_time.update(time.time() - end)
|
||||||
#Istr = 'Bsz={:} Asz={:}'.format(list(base_inputs.size()), list(arch_inputs.size()))
|
|
||||||
#logger.log(Sstr + ' ' + Tstr + ' ' + Lstr + ' ' + Vstr + ' ' + Istr)
|
|
||||||
#print(network.module.get_arch_info())
|
|
||||||
#print(network.module.width_attentions[0])
|
|
||||||
#print(network.module.width_attentions[1])
|
|
||||||
|
|
||||||
logger.log(' **TRAIN** Prec@1 {top1.avg:.2f} Prec@5 {top5.avg:.2f} Error@1 {error1:.2f} Error@5 {error5:.2f} Base-Loss:{baseloss:.3f}, Arch-Loss={archloss:.3f}'.format(top1=top1, top5=top5, error1=100-top1.avg, error5=100-top5.avg, baseloss=base_losses.avg, archloss=arch_losses.avg))
|
# update the weights
|
||||||
return base_losses.avg, arch_losses.avg, top1.avg, top5.avg
|
base_optimizer.zero_grad()
|
||||||
|
logits, expected_flop = network(base_inputs)
|
||||||
|
base_loss = criterion(logits, base_targets)
|
||||||
|
base_loss.backward()
|
||||||
|
base_optimizer.step()
|
||||||
|
# record
|
||||||
|
prec1, prec5 = obtain_accuracy(logits.data, base_targets.data, topk=(1, 5))
|
||||||
|
base_losses.update(base_loss.item(), base_inputs.size(0))
|
||||||
|
top1.update(prec1.item(), base_inputs.size(0))
|
||||||
|
top5.update(prec5.item(), base_inputs.size(0))
|
||||||
|
|
||||||
|
# update the architecture
|
||||||
|
arch_optimizer.zero_grad()
|
||||||
|
logits, expected_flop = network(arch_inputs)
|
||||||
|
flop_cur = network.module.get_flop("genotype", None, None)
|
||||||
|
flop_loss, flop_loss_scale = get_flop_loss(expected_flop, flop_cur, flop_need, flop_tolerant)
|
||||||
|
acls_loss = criterion(logits, arch_targets)
|
||||||
|
arch_loss = acls_loss + flop_loss * flop_weight
|
||||||
|
arch_loss.backward()
|
||||||
|
arch_optimizer.step()
|
||||||
|
|
||||||
|
# record
|
||||||
|
arch_losses.update(arch_loss.item(), arch_inputs.size(0))
|
||||||
|
arch_flop_losses.update(flop_loss_scale, arch_inputs.size(0))
|
||||||
|
arch_cls_losses.update(acls_loss.item(), arch_inputs.size(0))
|
||||||
|
|
||||||
|
# measure elapsed time
|
||||||
|
batch_time.update(time.time() - end)
|
||||||
|
end = time.time()
|
||||||
|
if step % print_freq == 0 or (step + 1) == len(search_loader):
|
||||||
|
Sstr = "**TRAIN** " + time_string() + " [{:}][{:03d}/{:03d}]".format(epoch_str, step, len(search_loader))
|
||||||
|
Tstr = "Time {batch_time.val:.2f} ({batch_time.avg:.2f}) Data {data_time.val:.2f} ({data_time.avg:.2f})".format(
|
||||||
|
batch_time=batch_time, data_time=data_time
|
||||||
|
)
|
||||||
|
Lstr = "Base-Loss {loss.val:.3f} ({loss.avg:.3f}) Prec@1 {top1.val:.2f} ({top1.avg:.2f}) Prec@5 {top5.val:.2f} ({top5.avg:.2f})".format(
|
||||||
|
loss=base_losses, top1=top1, top5=top5
|
||||||
|
)
|
||||||
|
Vstr = "Acls-loss {aloss.val:.3f} ({aloss.avg:.3f}) FLOP-Loss {floss.val:.3f} ({floss.avg:.3f}) Arch-Loss {loss.val:.3f} ({loss.avg:.3f})".format(
|
||||||
|
aloss=arch_cls_losses, floss=arch_flop_losses, loss=arch_losses
|
||||||
|
)
|
||||||
|
logger.log(Sstr + " " + Tstr + " " + Lstr + " " + Vstr)
|
||||||
|
# num_bytes = torch.cuda.max_memory_allocated( next(network.parameters()).device ) * 1.0
|
||||||
|
# logger.log(Sstr + ' ' + Tstr + ' ' + Lstr + ' ' + Vstr + ' GPU={:.2f}MB'.format(num_bytes/1e6))
|
||||||
|
# Istr = 'Bsz={:} Asz={:}'.format(list(base_inputs.size()), list(arch_inputs.size()))
|
||||||
|
# logger.log(Sstr + ' ' + Tstr + ' ' + Lstr + ' ' + Vstr + ' ' + Istr)
|
||||||
|
# print(network.module.get_arch_info())
|
||||||
|
# print(network.module.width_attentions[0])
|
||||||
|
# print(network.module.width_attentions[1])
|
||||||
|
|
||||||
|
logger.log(
|
||||||
|
" **TRAIN** Prec@1 {top1.avg:.2f} Prec@5 {top5.avg:.2f} Error@1 {error1:.2f} Error@5 {error5:.2f} Base-Loss:{baseloss:.3f}, Arch-Loss={archloss:.3f}".format(
|
||||||
|
top1=top1,
|
||||||
|
top5=top5,
|
||||||
|
error1=100 - top1.avg,
|
||||||
|
error5=100 - top5.avg,
|
||||||
|
baseloss=base_losses.avg,
|
||||||
|
archloss=arch_losses.avg,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return base_losses.avg, arch_losses.avg, top1.avg, top5.avg
|
||||||
|
@ -3,92 +3,143 @@
|
|||||||
#####################################################
|
#####################################################
|
||||||
import os, sys, time, torch
|
import os, sys, time, torch
|
||||||
import torch.nn.functional as F
|
import torch.nn.functional as F
|
||||||
|
|
||||||
# our modules
|
# our modules
|
||||||
from log_utils import AverageMeter, time_string
|
from log_utils import AverageMeter, time_string
|
||||||
from utils import obtain_accuracy
|
from utils import obtain_accuracy
|
||||||
|
|
||||||
|
|
||||||
def simple_KD_train(xloader, teacher, network, criterion, scheduler, optimizer, optim_config, extra_info, print_freq, logger):
|
def simple_KD_train(
|
||||||
loss, acc1, acc5 = procedure(xloader, teacher, network, criterion, scheduler, optimizer, 'train', optim_config, extra_info, print_freq, logger)
|
xloader, teacher, network, criterion, scheduler, optimizer, optim_config, extra_info, print_freq, logger
|
||||||
return loss, acc1, acc5
|
):
|
||||||
|
loss, acc1, acc5 = procedure(
|
||||||
|
xloader,
|
||||||
|
teacher,
|
||||||
|
network,
|
||||||
|
criterion,
|
||||||
|
scheduler,
|
||||||
|
optimizer,
|
||||||
|
"train",
|
||||||
|
optim_config,
|
||||||
|
extra_info,
|
||||||
|
print_freq,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
return loss, acc1, acc5
|
||||||
|
|
||||||
|
|
||||||
def simple_KD_valid(xloader, teacher, network, criterion, optim_config, extra_info, print_freq, logger):
|
def simple_KD_valid(xloader, teacher, network, criterion, optim_config, extra_info, print_freq, logger):
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
loss, acc1, acc5 = procedure(xloader, teacher, network, criterion, None, None, 'valid', optim_config, extra_info, print_freq, logger)
|
loss, acc1, acc5 = procedure(
|
||||||
return loss, acc1, acc5
|
xloader, teacher, network, criterion, None, None, "valid", optim_config, extra_info, print_freq, logger
|
||||||
|
)
|
||||||
|
return loss, acc1, acc5
|
||||||
|
|
||||||
|
|
||||||
def loss_KD_fn(criterion, student_logits, teacher_logits, studentFeatures, teacherFeatures, targets, alpha, temperature):
|
def loss_KD_fn(
|
||||||
basic_loss = criterion(student_logits, targets) * (1. - alpha)
|
criterion, student_logits, teacher_logits, studentFeatures, teacherFeatures, targets, alpha, temperature
|
||||||
log_student= F.log_softmax(student_logits / temperature, dim=1)
|
):
|
||||||
sof_teacher= F.softmax (teacher_logits / temperature, dim=1)
|
basic_loss = criterion(student_logits, targets) * (1.0 - alpha)
|
||||||
KD_loss = F.kl_div(log_student, sof_teacher, reduction='batchmean') * (alpha * temperature * temperature)
|
log_student = F.log_softmax(student_logits / temperature, dim=1)
|
||||||
return basic_loss + KD_loss
|
sof_teacher = F.softmax(teacher_logits / temperature, dim=1)
|
||||||
|
KD_loss = F.kl_div(log_student, sof_teacher, reduction="batchmean") * (alpha * temperature * temperature)
|
||||||
|
return basic_loss + KD_loss
|
||||||
|
|
||||||
|
|
||||||
def procedure(xloader, teacher, network, criterion, scheduler, optimizer, mode, config, extra_info, print_freq, logger):
|
def procedure(xloader, teacher, network, criterion, scheduler, optimizer, mode, config, extra_info, print_freq, logger):
|
||||||
data_time, batch_time, losses, top1, top5 = AverageMeter(), AverageMeter(), AverageMeter(), AverageMeter(), AverageMeter()
|
data_time, batch_time, losses, top1, top5 = (
|
||||||
Ttop1, Ttop5 = AverageMeter(), AverageMeter()
|
AverageMeter(),
|
||||||
if mode == 'train':
|
AverageMeter(),
|
||||||
network.train()
|
AverageMeter(),
|
||||||
elif mode == 'valid':
|
AverageMeter(),
|
||||||
network.eval()
|
AverageMeter(),
|
||||||
else: raise ValueError("The mode is not right : {:}".format(mode))
|
)
|
||||||
teacher.eval()
|
Ttop1, Ttop5 = AverageMeter(), AverageMeter()
|
||||||
|
if mode == "train":
|
||||||
logger.log('[{:5s}] config :: auxiliary={:}, KD :: [alpha={:.2f}, temperature={:.2f}]'.format(mode, config.auxiliary if hasattr(config, 'auxiliary') else -1, config.KD_alpha, config.KD_temperature))
|
network.train()
|
||||||
end = time.time()
|
elif mode == "valid":
|
||||||
for i, (inputs, targets) in enumerate(xloader):
|
network.eval()
|
||||||
if mode == 'train': scheduler.update(None, 1.0 * i / len(xloader))
|
|
||||||
# measure data loading time
|
|
||||||
data_time.update(time.time() - end)
|
|
||||||
# calculate prediction and loss
|
|
||||||
targets = targets.cuda(non_blocking=True)
|
|
||||||
|
|
||||||
if mode == 'train': optimizer.zero_grad()
|
|
||||||
|
|
||||||
student_f, logits = network(inputs)
|
|
||||||
if isinstance(logits, list):
|
|
||||||
assert len(logits) == 2, 'logits must has {:} items instead of {:}'.format(2, len(logits))
|
|
||||||
logits, logits_aux = logits
|
|
||||||
else:
|
else:
|
||||||
logits, logits_aux = logits, None
|
raise ValueError("The mode is not right : {:}".format(mode))
|
||||||
with torch.no_grad():
|
teacher.eval()
|
||||||
teacher_f, teacher_logits = teacher(inputs)
|
|
||||||
|
|
||||||
loss = loss_KD_fn(criterion, logits, teacher_logits, student_f, teacher_f, targets, config.KD_alpha, config.KD_temperature)
|
logger.log(
|
||||||
if config is not None and hasattr(config, 'auxiliary') and config.auxiliary > 0:
|
"[{:5s}] config :: auxiliary={:}, KD :: [alpha={:.2f}, temperature={:.2f}]".format(
|
||||||
loss_aux = criterion(logits_aux, targets)
|
mode, config.auxiliary if hasattr(config, "auxiliary") else -1, config.KD_alpha, config.KD_temperature
|
||||||
loss += config.auxiliary * loss_aux
|
)
|
||||||
|
)
|
||||||
if mode == 'train':
|
|
||||||
loss.backward()
|
|
||||||
optimizer.step()
|
|
||||||
|
|
||||||
# record
|
|
||||||
sprec1, sprec5 = obtain_accuracy(logits.data, targets.data, topk=(1, 5))
|
|
||||||
losses.update(loss.item(), inputs.size(0))
|
|
||||||
top1.update (sprec1.item(), inputs.size(0))
|
|
||||||
top5.update (sprec5.item(), inputs.size(0))
|
|
||||||
# teacher
|
|
||||||
tprec1, tprec5 = obtain_accuracy(teacher_logits.data, targets.data, topk=(1, 5))
|
|
||||||
Ttop1.update (tprec1.item(), inputs.size(0))
|
|
||||||
Ttop5.update (tprec5.item(), inputs.size(0))
|
|
||||||
|
|
||||||
# measure elapsed time
|
|
||||||
batch_time.update(time.time() - end)
|
|
||||||
end = time.time()
|
end = time.time()
|
||||||
|
for i, (inputs, targets) in enumerate(xloader):
|
||||||
|
if mode == "train":
|
||||||
|
scheduler.update(None, 1.0 * i / len(xloader))
|
||||||
|
# measure data loading time
|
||||||
|
data_time.update(time.time() - end)
|
||||||
|
# calculate prediction and loss
|
||||||
|
targets = targets.cuda(non_blocking=True)
|
||||||
|
|
||||||
if i % print_freq == 0 or (i+1) == len(xloader):
|
if mode == "train":
|
||||||
Sstr = ' {:5s} '.format(mode.upper()) + time_string() + ' [{:}][{:03d}/{:03d}]'.format(extra_info, i, len(xloader))
|
optimizer.zero_grad()
|
||||||
if scheduler is not None:
|
|
||||||
Sstr += ' {:}'.format(scheduler.get_min_info())
|
|
||||||
Tstr = 'Time {batch_time.val:.2f} ({batch_time.avg:.2f}) Data {data_time.val:.2f} ({data_time.avg:.2f})'.format(batch_time=batch_time, data_time=data_time)
|
|
||||||
Lstr = 'Loss {loss.val:.3f} ({loss.avg:.3f}) Prec@1 {top1.val:.2f} ({top1.avg:.2f}) Prec@5 {top5.val:.2f} ({top5.avg:.2f})'.format(loss=losses, top1=top1, top5=top5)
|
|
||||||
Lstr+= ' Teacher : acc@1={:.2f}, acc@5={:.2f}'.format(Ttop1.avg, Ttop5.avg)
|
|
||||||
Istr = 'Size={:}'.format(list(inputs.size()))
|
|
||||||
logger.log(Sstr + ' ' + Tstr + ' ' + Lstr + ' ' + Istr)
|
|
||||||
|
|
||||||
logger.log(' **{:5s}** accuracy drop :: @1={:.2f}, @5={:.2f}'.format(mode.upper(), Ttop1.avg - top1.avg, Ttop5.avg - top5.avg))
|
student_f, logits = network(inputs)
|
||||||
logger.log(' **{mode:5s}** Prec@1 {top1.avg:.2f} Prec@5 {top5.avg:.2f} Error@1 {error1:.2f} Error@5 {error5:.2f} Loss:{loss:.3f}'.format(mode=mode.upper(), top1=top1, top5=top5, error1=100-top1.avg, error5=100-top5.avg, loss=losses.avg))
|
if isinstance(logits, list):
|
||||||
return losses.avg, top1.avg, top5.avg
|
assert len(logits) == 2, "logits must has {:} items instead of {:}".format(2, len(logits))
|
||||||
|
logits, logits_aux = logits
|
||||||
|
else:
|
||||||
|
logits, logits_aux = logits, None
|
||||||
|
with torch.no_grad():
|
||||||
|
teacher_f, teacher_logits = teacher(inputs)
|
||||||
|
|
||||||
|
loss = loss_KD_fn(
|
||||||
|
criterion, logits, teacher_logits, student_f, teacher_f, targets, config.KD_alpha, config.KD_temperature
|
||||||
|
)
|
||||||
|
if config is not None and hasattr(config, "auxiliary") and config.auxiliary > 0:
|
||||||
|
loss_aux = criterion(logits_aux, targets)
|
||||||
|
loss += config.auxiliary * loss_aux
|
||||||
|
|
||||||
|
if mode == "train":
|
||||||
|
loss.backward()
|
||||||
|
optimizer.step()
|
||||||
|
|
||||||
|
# record
|
||||||
|
sprec1, sprec5 = obtain_accuracy(logits.data, targets.data, topk=(1, 5))
|
||||||
|
losses.update(loss.item(), inputs.size(0))
|
||||||
|
top1.update(sprec1.item(), inputs.size(0))
|
||||||
|
top5.update(sprec5.item(), inputs.size(0))
|
||||||
|
# teacher
|
||||||
|
tprec1, tprec5 = obtain_accuracy(teacher_logits.data, targets.data, topk=(1, 5))
|
||||||
|
Ttop1.update(tprec1.item(), inputs.size(0))
|
||||||
|
Ttop5.update(tprec5.item(), inputs.size(0))
|
||||||
|
|
||||||
|
# measure elapsed time
|
||||||
|
batch_time.update(time.time() - end)
|
||||||
|
end = time.time()
|
||||||
|
|
||||||
|
if i % print_freq == 0 or (i + 1) == len(xloader):
|
||||||
|
Sstr = (
|
||||||
|
" {:5s} ".format(mode.upper())
|
||||||
|
+ time_string()
|
||||||
|
+ " [{:}][{:03d}/{:03d}]".format(extra_info, i, len(xloader))
|
||||||
|
)
|
||||||
|
if scheduler is not None:
|
||||||
|
Sstr += " {:}".format(scheduler.get_min_info())
|
||||||
|
Tstr = "Time {batch_time.val:.2f} ({batch_time.avg:.2f}) Data {data_time.val:.2f} ({data_time.avg:.2f})".format(
|
||||||
|
batch_time=batch_time, data_time=data_time
|
||||||
|
)
|
||||||
|
Lstr = "Loss {loss.val:.3f} ({loss.avg:.3f}) Prec@1 {top1.val:.2f} ({top1.avg:.2f}) Prec@5 {top5.val:.2f} ({top5.avg:.2f})".format(
|
||||||
|
loss=losses, top1=top1, top5=top5
|
||||||
|
)
|
||||||
|
Lstr += " Teacher : acc@1={:.2f}, acc@5={:.2f}".format(Ttop1.avg, Ttop5.avg)
|
||||||
|
Istr = "Size={:}".format(list(inputs.size()))
|
||||||
|
logger.log(Sstr + " " + Tstr + " " + Lstr + " " + Istr)
|
||||||
|
|
||||||
|
logger.log(
|
||||||
|
" **{:5s}** accuracy drop :: @1={:.2f}, @5={:.2f}".format(
|
||||||
|
mode.upper(), Ttop1.avg - top1.avg, Ttop5.avg - top5.avg
|
||||||
|
)
|
||||||
|
)
|
||||||
|
logger.log(
|
||||||
|
" **{mode:5s}** Prec@1 {top1.avg:.2f} Prec@5 {top5.avg:.2f} Error@1 {error1:.2f} Error@5 {error5:.2f} Loss:{loss:.3f}".format(
|
||||||
|
mode=mode.upper(), top1=top1, top5=top5, error1=100 - top1.avg, error5=100 - top5.avg, loss=losses.avg
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return losses.avg, top1.avg, top5.avg
|
||||||
|
@ -3,62 +3,71 @@
|
|||||||
##################################################
|
##################################################
|
||||||
import os, sys, torch, random, PIL, copy, numpy as np
|
import os, sys, torch, random, PIL, copy, numpy as np
|
||||||
from os import path as osp
|
from os import path as osp
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
|
|
||||||
|
|
||||||
def prepare_seed(rand_seed):
|
def prepare_seed(rand_seed):
|
||||||
random.seed(rand_seed)
|
random.seed(rand_seed)
|
||||||
np.random.seed(rand_seed)
|
np.random.seed(rand_seed)
|
||||||
torch.manual_seed(rand_seed)
|
torch.manual_seed(rand_seed)
|
||||||
torch.cuda.manual_seed(rand_seed)
|
torch.cuda.manual_seed(rand_seed)
|
||||||
torch.cuda.manual_seed_all(rand_seed)
|
torch.cuda.manual_seed_all(rand_seed)
|
||||||
|
|
||||||
|
|
||||||
def prepare_logger(xargs):
|
def prepare_logger(xargs):
|
||||||
args = copy.deepcopy( xargs )
|
args = copy.deepcopy(xargs)
|
||||||
from log_utils import Logger
|
from log_utils import Logger
|
||||||
logger = Logger(args.save_dir, args.rand_seed)
|
|
||||||
logger.log('Main Function with logger : {:}'.format(logger))
|
logger = Logger(args.save_dir, args.rand_seed)
|
||||||
logger.log('Arguments : -------------------------------')
|
logger.log("Main Function with logger : {:}".format(logger))
|
||||||
for name, value in args._get_kwargs():
|
logger.log("Arguments : -------------------------------")
|
||||||
logger.log('{:16} : {:}'.format(name, value))
|
for name, value in args._get_kwargs():
|
||||||
logger.log("Python Version : {:}".format(sys.version.replace('\n', ' ')))
|
logger.log("{:16} : {:}".format(name, value))
|
||||||
logger.log("Pillow Version : {:}".format(PIL.__version__))
|
logger.log("Python Version : {:}".format(sys.version.replace("\n", " ")))
|
||||||
logger.log("PyTorch Version : {:}".format(torch.__version__))
|
logger.log("Pillow Version : {:}".format(PIL.__version__))
|
||||||
logger.log("cuDNN Version : {:}".format(torch.backends.cudnn.version()))
|
logger.log("PyTorch Version : {:}".format(torch.__version__))
|
||||||
logger.log("CUDA available : {:}".format(torch.cuda.is_available()))
|
logger.log("cuDNN Version : {:}".format(torch.backends.cudnn.version()))
|
||||||
logger.log("CUDA GPU numbers : {:}".format(torch.cuda.device_count()))
|
logger.log("CUDA available : {:}".format(torch.cuda.is_available()))
|
||||||
logger.log("CUDA_VISIBLE_DEVICES : {:}".format(os.environ['CUDA_VISIBLE_DEVICES'] if 'CUDA_VISIBLE_DEVICES' in os.environ else 'None'))
|
logger.log("CUDA GPU numbers : {:}".format(torch.cuda.device_count()))
|
||||||
return logger
|
logger.log(
|
||||||
|
"CUDA_VISIBLE_DEVICES : {:}".format(
|
||||||
|
os.environ["CUDA_VISIBLE_DEVICES"] if "CUDA_VISIBLE_DEVICES" in os.environ else "None"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return logger
|
||||||
|
|
||||||
|
|
||||||
def get_machine_info():
|
def get_machine_info():
|
||||||
info = "Python Version : {:}".format(sys.version.replace('\n', ' '))
|
info = "Python Version : {:}".format(sys.version.replace("\n", " "))
|
||||||
info+= "\nPillow Version : {:}".format(PIL.__version__)
|
info += "\nPillow Version : {:}".format(PIL.__version__)
|
||||||
info+= "\nPyTorch Version : {:}".format(torch.__version__)
|
info += "\nPyTorch Version : {:}".format(torch.__version__)
|
||||||
info+= "\ncuDNN Version : {:}".format(torch.backends.cudnn.version())
|
info += "\ncuDNN Version : {:}".format(torch.backends.cudnn.version())
|
||||||
info+= "\nCUDA available : {:}".format(torch.cuda.is_available())
|
info += "\nCUDA available : {:}".format(torch.cuda.is_available())
|
||||||
info+= "\nCUDA GPU numbers : {:}".format(torch.cuda.device_count())
|
info += "\nCUDA GPU numbers : {:}".format(torch.cuda.device_count())
|
||||||
if 'CUDA_VISIBLE_DEVICES' in os.environ:
|
if "CUDA_VISIBLE_DEVICES" in os.environ:
|
||||||
info+= "\nCUDA_VISIBLE_DEVICES={:}".format(os.environ['CUDA_VISIBLE_DEVICES'])
|
info += "\nCUDA_VISIBLE_DEVICES={:}".format(os.environ["CUDA_VISIBLE_DEVICES"])
|
||||||
else:
|
else:
|
||||||
info+= "\nDoes not set CUDA_VISIBLE_DEVICES"
|
info += "\nDoes not set CUDA_VISIBLE_DEVICES"
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
def save_checkpoint(state, filename, logger):
|
def save_checkpoint(state, filename, logger):
|
||||||
if osp.isfile(filename):
|
if osp.isfile(filename):
|
||||||
if hasattr(logger, 'log'): logger.log('Find {:} exist, delete is at first before saving'.format(filename))
|
if hasattr(logger, "log"):
|
||||||
os.remove(filename)
|
logger.log("Find {:} exist, delete is at first before saving".format(filename))
|
||||||
torch.save(state, filename)
|
os.remove(filename)
|
||||||
assert osp.isfile(filename), 'save filename : {:} failed, which is not found.'.format(filename)
|
torch.save(state, filename)
|
||||||
if hasattr(logger, 'log'): logger.log('save checkpoint into {:}'.format(filename))
|
assert osp.isfile(filename), "save filename : {:} failed, which is not found.".format(filename)
|
||||||
return filename
|
if hasattr(logger, "log"):
|
||||||
|
logger.log("save checkpoint into {:}".format(filename))
|
||||||
|
return filename
|
||||||
|
|
||||||
|
|
||||||
def copy_checkpoint(src, dst, logger):
|
def copy_checkpoint(src, dst, logger):
|
||||||
if osp.isfile(dst):
|
if osp.isfile(dst):
|
||||||
if hasattr(logger, 'log'): logger.log('Find {:} exist, delete is at first before saving'.format(dst))
|
if hasattr(logger, "log"):
|
||||||
os.remove(dst)
|
logger.log("Find {:} exist, delete is at first before saving".format(dst))
|
||||||
copyfile(src, dst)
|
os.remove(dst)
|
||||||
if hasattr(logger, 'log'): logger.log('copy the file from {:} into {:}'.format(src, dst))
|
copyfile(src, dst)
|
||||||
|
if hasattr(logger, "log"):
|
||||||
|
logger.log("copy the file from {:} into {:}".format(src, dst))
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from .evaluation_utils import obtain_accuracy
|
from .evaluation_utils import obtain_accuracy
|
||||||
from .gpu_manager import GPUManager
|
from .gpu_manager import GPUManager
|
||||||
from .flop_benchmark import get_model_infos, count_parameters, count_parameters_in_MB
|
from .flop_benchmark import get_model_infos, count_parameters, count_parameters_in_MB
|
||||||
from .affine_utils import normalize_points, denormalize_points
|
from .affine_utils import normalize_points, denormalize_points
|
||||||
from .affine_utils import identity2affine, solve2theta, affine2image
|
from .affine_utils import identity2affine, solve2theta, affine2image
|
||||||
from .hash_utils import get_md5_file
|
from .hash_utils import get_md5_file
|
||||||
from .str_utils import split_str2indexes
|
from .str_utils import split_str2indexes
|
||||||
|
@ -1,125 +1,149 @@
|
|||||||
# functions for affine transformation
|
# functions for affine transformation
|
||||||
import math, torch
|
import math
|
||||||
|
import torch
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import torch.nn.functional as F
|
import torch.nn.functional as F
|
||||||
|
|
||||||
|
|
||||||
def identity2affine(full=False):
|
def identity2affine(full=False):
|
||||||
if not full:
|
if not full:
|
||||||
parameters = torch.zeros((2,3))
|
parameters = torch.zeros((2, 3))
|
||||||
parameters[0, 0] = parameters[1, 1] = 1
|
parameters[0, 0] = parameters[1, 1] = 1
|
||||||
else:
|
else:
|
||||||
parameters = torch.zeros((3,3))
|
parameters = torch.zeros((3, 3))
|
||||||
parameters[0, 0] = parameters[1, 1] = parameters[2, 2] = 1
|
parameters[0, 0] = parameters[1, 1] = parameters[2, 2] = 1
|
||||||
return parameters
|
return parameters
|
||||||
|
|
||||||
|
|
||||||
def normalize_L(x, L):
|
def normalize_L(x, L):
|
||||||
return -1. + 2. * x / (L-1)
|
return -1.0 + 2.0 * x / (L - 1)
|
||||||
|
|
||||||
|
|
||||||
def denormalize_L(x, L):
|
def denormalize_L(x, L):
|
||||||
return (x + 1.0) / 2.0 * (L-1)
|
return (x + 1.0) / 2.0 * (L - 1)
|
||||||
|
|
||||||
|
|
||||||
def crop2affine(crop_box, W, H):
|
def crop2affine(crop_box, W, H):
|
||||||
assert len(crop_box) == 4, 'Invalid crop-box : {:}'.format(crop_box)
|
assert len(crop_box) == 4, "Invalid crop-box : {:}".format(crop_box)
|
||||||
parameters = torch.zeros(3,3)
|
parameters = torch.zeros(3, 3)
|
||||||
x1, y1 = normalize_L(crop_box[0], W), normalize_L(crop_box[1], H)
|
x1, y1 = normalize_L(crop_box[0], W), normalize_L(crop_box[1], H)
|
||||||
x2, y2 = normalize_L(crop_box[2], W), normalize_L(crop_box[3], H)
|
x2, y2 = normalize_L(crop_box[2], W), normalize_L(crop_box[3], H)
|
||||||
parameters[0,0] = (x2-x1)/2
|
parameters[0, 0] = (x2 - x1) / 2
|
||||||
parameters[0,2] = (x2+x1)/2
|
parameters[0, 2] = (x2 + x1) / 2
|
||||||
|
|
||||||
|
parameters[1, 1] = (y2 - y1) / 2
|
||||||
|
parameters[1, 2] = (y2 + y1) / 2
|
||||||
|
parameters[2, 2] = 1
|
||||||
|
return parameters
|
||||||
|
|
||||||
parameters[1,1] = (y2-y1)/2
|
|
||||||
parameters[1,2] = (y2+y1)/2
|
|
||||||
parameters[2,2] = 1
|
|
||||||
return parameters
|
|
||||||
|
|
||||||
def scale2affine(scalex, scaley):
|
def scale2affine(scalex, scaley):
|
||||||
parameters = torch.zeros(3,3)
|
parameters = torch.zeros(3, 3)
|
||||||
parameters[0,0] = scalex
|
parameters[0, 0] = scalex
|
||||||
parameters[1,1] = scaley
|
parameters[1, 1] = scaley
|
||||||
parameters[2,2] = 1
|
parameters[2, 2] = 1
|
||||||
return parameters
|
return parameters
|
||||||
|
|
||||||
|
|
||||||
def offset2affine(offx, offy):
|
def offset2affine(offx, offy):
|
||||||
parameters = torch.zeros(3,3)
|
parameters = torch.zeros(3, 3)
|
||||||
parameters[0,0] = parameters[1,1] = parameters[2,2] = 1
|
parameters[0, 0] = parameters[1, 1] = parameters[2, 2] = 1
|
||||||
parameters[0,2] = offx
|
parameters[0, 2] = offx
|
||||||
parameters[1,2] = offy
|
parameters[1, 2] = offy
|
||||||
return parameters
|
return parameters
|
||||||
|
|
||||||
|
|
||||||
def horizontalmirror2affine():
|
def horizontalmirror2affine():
|
||||||
parameters = torch.zeros(3,3)
|
parameters = torch.zeros(3, 3)
|
||||||
parameters[0,0] = -1
|
parameters[0, 0] = -1
|
||||||
parameters[1,1] = parameters[2,2] = 1
|
parameters[1, 1] = parameters[2, 2] = 1
|
||||||
return parameters
|
return parameters
|
||||||
|
|
||||||
|
|
||||||
# clockwise rotate image = counterclockwise rotate the rectangle
|
# clockwise rotate image = counterclockwise rotate the rectangle
|
||||||
# degree is between [0, 360]
|
# degree is between [0, 360]
|
||||||
def rotate2affine(degree):
|
def rotate2affine(degree):
|
||||||
assert degree >= 0 and degree <= 360, 'Invalid degree : {:}'.format(degree)
|
assert degree >= 0 and degree <= 360, "Invalid degree : {:}".format(degree)
|
||||||
degree = degree / 180 * math.pi
|
degree = degree / 180 * math.pi
|
||||||
parameters = torch.zeros(3,3)
|
parameters = torch.zeros(3, 3)
|
||||||
parameters[0,0] = math.cos(-degree)
|
parameters[0, 0] = math.cos(-degree)
|
||||||
parameters[0,1] = -math.sin(-degree)
|
parameters[0, 1] = -math.sin(-degree)
|
||||||
parameters[1,0] = math.sin(-degree)
|
parameters[1, 0] = math.sin(-degree)
|
||||||
parameters[1,1] = math.cos(-degree)
|
parameters[1, 1] = math.cos(-degree)
|
||||||
parameters[2,2] = 1
|
parameters[2, 2] = 1
|
||||||
return parameters
|
return parameters
|
||||||
|
|
||||||
|
|
||||||
# shape is a tuple [H, W]
|
# shape is a tuple [H, W]
|
||||||
def normalize_points(shape, points):
|
def normalize_points(shape, points):
|
||||||
assert (isinstance(shape, tuple) or isinstance(shape, list)) and len(shape) == 2, 'invalid shape : {:}'.format(shape)
|
assert (isinstance(shape, tuple) or isinstance(shape, list)) and len(shape) == 2, "invalid shape : {:}".format(
|
||||||
assert isinstance(points, torch.Tensor) and (points.shape[0] == 2), 'points are wrong : {:}'.format(points.shape)
|
shape
|
||||||
(H, W), points = shape, points.clone()
|
)
|
||||||
points[0, :] = normalize_L(points[0,:], W)
|
assert isinstance(points, torch.Tensor) and (points.shape[0] == 2), "points are wrong : {:}".format(points.shape)
|
||||||
points[1, :] = normalize_L(points[1,:], H)
|
(H, W), points = shape, points.clone()
|
||||||
return points
|
points[0, :] = normalize_L(points[0, :], W)
|
||||||
|
points[1, :] = normalize_L(points[1, :], H)
|
||||||
|
return points
|
||||||
|
|
||||||
|
|
||||||
# shape is a tuple [H, W]
|
# shape is a tuple [H, W]
|
||||||
def normalize_points_batch(shape, points):
|
def normalize_points_batch(shape, points):
|
||||||
assert (isinstance(shape, tuple) or isinstance(shape, list)) and len(shape) == 2, 'invalid shape : {:}'.format(shape)
|
assert (isinstance(shape, tuple) or isinstance(shape, list)) and len(shape) == 2, "invalid shape : {:}".format(
|
||||||
assert isinstance(points, torch.Tensor) and (points.size(-1) == 2), 'points are wrong : {:}'.format(points.shape)
|
shape
|
||||||
(H, W), points = shape, points.clone()
|
)
|
||||||
x = normalize_L(points[...,0], W)
|
assert isinstance(points, torch.Tensor) and (points.size(-1) == 2), "points are wrong : {:}".format(points.shape)
|
||||||
y = normalize_L(points[...,1], H)
|
(H, W), points = shape, points.clone()
|
||||||
return torch.stack((x,y), dim=-1)
|
x = normalize_L(points[..., 0], W)
|
||||||
|
y = normalize_L(points[..., 1], H)
|
||||||
|
return torch.stack((x, y), dim=-1)
|
||||||
|
|
||||||
|
|
||||||
# shape is a tuple [H, W]
|
# shape is a tuple [H, W]
|
||||||
def denormalize_points(shape, points):
|
def denormalize_points(shape, points):
|
||||||
assert (isinstance(shape, tuple) or isinstance(shape, list)) and len(shape) == 2, 'invalid shape : {:}'.format(shape)
|
assert (isinstance(shape, tuple) or isinstance(shape, list)) and len(shape) == 2, "invalid shape : {:}".format(
|
||||||
assert isinstance(points, torch.Tensor) and (points.shape[0] == 2), 'points are wrong : {:}'.format(points.shape)
|
shape
|
||||||
(H, W), points = shape, points.clone()
|
)
|
||||||
points[0, :] = denormalize_L(points[0,:], W)
|
assert isinstance(points, torch.Tensor) and (points.shape[0] == 2), "points are wrong : {:}".format(points.shape)
|
||||||
points[1, :] = denormalize_L(points[1,:], H)
|
(H, W), points = shape, points.clone()
|
||||||
return points
|
points[0, :] = denormalize_L(points[0, :], W)
|
||||||
|
points[1, :] = denormalize_L(points[1, :], H)
|
||||||
|
return points
|
||||||
|
|
||||||
|
|
||||||
# shape is a tuple [H, W]
|
# shape is a tuple [H, W]
|
||||||
def denormalize_points_batch(shape, points):
|
def denormalize_points_batch(shape, points):
|
||||||
assert (isinstance(shape, tuple) or isinstance(shape, list)) and len(shape) == 2, 'invalid shape : {:}'.format(shape)
|
assert (isinstance(shape, tuple) or isinstance(shape, list)) and len(shape) == 2, "invalid shape : {:}".format(
|
||||||
assert isinstance(points, torch.Tensor) and (points.shape[-1] == 2), 'points are wrong : {:}'.format(points.shape)
|
shape
|
||||||
(H, W), points = shape, points.clone()
|
)
|
||||||
x = denormalize_L(points[...,0], W)
|
assert isinstance(points, torch.Tensor) and (points.shape[-1] == 2), "points are wrong : {:}".format(points.shape)
|
||||||
y = denormalize_L(points[...,1], H)
|
(H, W), points = shape, points.clone()
|
||||||
return torch.stack((x,y), dim=-1)
|
x = denormalize_L(points[..., 0], W)
|
||||||
|
y = denormalize_L(points[..., 1], H)
|
||||||
|
return torch.stack((x, y), dim=-1)
|
||||||
|
|
||||||
|
|
||||||
# make target * theta = source
|
# make target * theta = source
|
||||||
def solve2theta(source, target):
|
def solve2theta(source, target):
|
||||||
source, target = source.clone(), target.clone()
|
source, target = source.clone(), target.clone()
|
||||||
oks = source[2, :] == 1
|
oks = source[2, :] == 1
|
||||||
assert torch.sum(oks).item() >= 3, 'valid points : {:} is short'.format(oks)
|
assert torch.sum(oks).item() >= 3, "valid points : {:} is short".format(oks)
|
||||||
if target.size(0) == 2: target = torch.cat((target, oks.unsqueeze(0).float()), dim=0)
|
if target.size(0) == 2:
|
||||||
source, target = source[:, oks], target[:, oks]
|
target = torch.cat((target, oks.unsqueeze(0).float()), dim=0)
|
||||||
source, target = source.transpose(1,0), target.transpose(1,0)
|
source, target = source[:, oks], target[:, oks]
|
||||||
assert source.size(1) == target.size(1) == 3
|
source, target = source.transpose(1, 0), target.transpose(1, 0)
|
||||||
#X, residual, rank, s = np.linalg.lstsq(target.numpy(), source.numpy())
|
assert source.size(1) == target.size(1) == 3
|
||||||
#theta = torch.Tensor(X.T[:2, :])
|
# X, residual, rank, s = np.linalg.lstsq(target.numpy(), source.numpy())
|
||||||
X_, qr = torch.gels(source, target)
|
# theta = torch.Tensor(X.T[:2, :])
|
||||||
theta = X_[:3, :2].transpose(1, 0)
|
X_, qr = torch.gels(source, target)
|
||||||
return theta
|
theta = X_[:3, :2].transpose(1, 0)
|
||||||
|
return theta
|
||||||
|
|
||||||
|
|
||||||
# shape = [H,W]
|
# shape = [H,W]
|
||||||
def affine2image(image, theta, shape):
|
def affine2image(image, theta, shape):
|
||||||
C, H, W = image.size()
|
C, H, W = image.size()
|
||||||
theta = theta[:2, :].unsqueeze(0)
|
theta = theta[:2, :].unsqueeze(0)
|
||||||
grid_size = torch.Size([1, C, shape[0], shape[1]])
|
grid_size = torch.Size([1, C, shape[0], shape[1]])
|
||||||
grid = F.affine_grid(theta, grid_size)
|
grid = F.affine_grid(theta, grid_size)
|
||||||
affI = F.grid_sample(image.unsqueeze(0), grid, mode='bilinear', padding_mode='border')
|
affI = F.grid_sample(image.unsqueeze(0), grid, mode="bilinear", padding_mode="border")
|
||||||
return affI.squeeze(0)
|
return affI.squeeze(0)
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import torch
|
import torch
|
||||||
|
|
||||||
|
|
||||||
def obtain_accuracy(output, target, topk=(1,)):
|
def obtain_accuracy(output, target, topk=(1,)):
|
||||||
"""Computes the precision@k for the specified values of k"""
|
"""Computes the precision@k for the specified values of k"""
|
||||||
maxk = max(topk)
|
maxk = max(topk)
|
||||||
batch_size = target.size(0)
|
batch_size = target.size(0)
|
||||||
|
|
||||||
_, pred = output.topk(maxk, 1, True, True)
|
_, pred = output.topk(maxk, 1, True, True)
|
||||||
pred = pred.t()
|
pred = pred.t()
|
||||||
correct = pred.eq(target.view(1, -1).expand_as(pred))
|
correct = pred.eq(target.view(1, -1).expand_as(pred))
|
||||||
|
|
||||||
res = []
|
res = []
|
||||||
for k in topk:
|
for k in topk:
|
||||||
correct_k = correct[:k].view(-1).float().sum(0, keepdim=True)
|
correct_k = correct[:k].view(-1).float().sum(0, keepdim=True)
|
||||||
res.append(correct_k.mul_(100.0 / batch_size))
|
res.append(correct_k.mul_(100.0 / batch_size))
|
||||||
return res
|
return res
|
||||||
|
@ -4,191 +4,199 @@ import numpy as np
|
|||||||
|
|
||||||
|
|
||||||
def count_parameters_in_MB(model):
|
def count_parameters_in_MB(model):
|
||||||
return count_parameters(model, "mb")
|
return count_parameters(model, "mb")
|
||||||
|
|
||||||
|
|
||||||
def count_parameters(model_or_parameters, unit="mb"):
|
def count_parameters(model_or_parameters, unit="mb"):
|
||||||
if isinstance(model_or_parameters, nn.Module):
|
if isinstance(model_or_parameters, nn.Module):
|
||||||
counts = np.sum(np.prod(v.size()) for v in model_or_parameters.parameters())
|
counts = np.sum(np.prod(v.size()) for v in model_or_parameters.parameters())
|
||||||
else:
|
else:
|
||||||
counts = np.sum(np.prod(v.size()) for v in model_or_parameters)
|
counts = np.sum(np.prod(v.size()) for v in model_or_parameters)
|
||||||
if unit.lower() == "mb":
|
if unit.lower() == "mb":
|
||||||
counts /= 1e6
|
counts /= 1e6
|
||||||
elif unit.lower() == "kb":
|
elif unit.lower() == "kb":
|
||||||
counts /= 1e3
|
counts /= 1e3
|
||||||
elif unit.lower() == "gb":
|
elif unit.lower() == "gb":
|
||||||
counts /= 1e9
|
counts /= 1e9
|
||||||
elif unit is not None:
|
elif unit is not None:
|
||||||
raise ValueError("Unknow unit: {:}".format(unit))
|
raise ValueError("Unknow unit: {:}".format(unit))
|
||||||
return counts
|
return counts
|
||||||
|
|
||||||
|
|
||||||
def get_model_infos(model, shape):
|
def get_model_infos(model, shape):
|
||||||
#model = copy.deepcopy( model )
|
# model = copy.deepcopy( model )
|
||||||
|
|
||||||
model = add_flops_counting_methods(model)
|
model = add_flops_counting_methods(model)
|
||||||
#model = model.cuda()
|
# model = model.cuda()
|
||||||
model.eval()
|
model.eval()
|
||||||
|
|
||||||
#cache_inputs = torch.zeros(*shape).cuda()
|
# cache_inputs = torch.zeros(*shape).cuda()
|
||||||
#cache_inputs = torch.zeros(*shape)
|
# cache_inputs = torch.zeros(*shape)
|
||||||
cache_inputs = torch.rand(*shape)
|
cache_inputs = torch.rand(*shape)
|
||||||
if next(model.parameters()).is_cuda: cache_inputs = cache_inputs.cuda()
|
if next(model.parameters()).is_cuda:
|
||||||
#print_log('In the calculating function : cache input size : {:}'.format(cache_inputs.size()), log)
|
cache_inputs = cache_inputs.cuda()
|
||||||
with torch.no_grad():
|
# print_log('In the calculating function : cache input size : {:}'.format(cache_inputs.size()), log)
|
||||||
_____ = model(cache_inputs)
|
with torch.no_grad():
|
||||||
FLOPs = compute_average_flops_cost( model ) / 1e6
|
_____ = model(cache_inputs)
|
||||||
Param = count_parameters_in_MB(model)
|
FLOPs = compute_average_flops_cost(model) / 1e6
|
||||||
|
Param = count_parameters_in_MB(model)
|
||||||
|
|
||||||
if hasattr(model, 'auxiliary_param'):
|
if hasattr(model, "auxiliary_param"):
|
||||||
aux_params = count_parameters_in_MB(model.auxiliary_param())
|
aux_params = count_parameters_in_MB(model.auxiliary_param())
|
||||||
print ('The auxiliary params of this model is : {:}'.format(aux_params))
|
print("The auxiliary params of this model is : {:}".format(aux_params))
|
||||||
print ('We remove the auxiliary params from the total params ({:}) when counting'.format(Param))
|
print("We remove the auxiliary params from the total params ({:}) when counting".format(Param))
|
||||||
Param = Param - aux_params
|
Param = Param - aux_params
|
||||||
|
|
||||||
#print_log('FLOPs : {:} MB'.format(FLOPs), log)
|
# print_log('FLOPs : {:} MB'.format(FLOPs), log)
|
||||||
torch.cuda.empty_cache()
|
torch.cuda.empty_cache()
|
||||||
model.apply( remove_hook_function )
|
model.apply(remove_hook_function)
|
||||||
return FLOPs, Param
|
return FLOPs, Param
|
||||||
|
|
||||||
|
|
||||||
# ---- Public functions
|
# ---- Public functions
|
||||||
def add_flops_counting_methods( model ):
|
def add_flops_counting_methods(model):
|
||||||
model.__batch_counter__ = 0
|
model.__batch_counter__ = 0
|
||||||
add_batch_counter_hook_function( model )
|
add_batch_counter_hook_function(model)
|
||||||
model.apply( add_flops_counter_variable_or_reset )
|
model.apply(add_flops_counter_variable_or_reset)
|
||||||
model.apply( add_flops_counter_hook_function )
|
model.apply(add_flops_counter_hook_function)
|
||||||
return model
|
return model
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def compute_average_flops_cost(model):
|
def compute_average_flops_cost(model):
|
||||||
"""
|
"""
|
||||||
A method that will be available after add_flops_counting_methods() is called on a desired net object.
|
A method that will be available after add_flops_counting_methods() is called on a desired net object.
|
||||||
Returns current mean flops consumption per image.
|
Returns current mean flops consumption per image.
|
||||||
"""
|
"""
|
||||||
batches_count = model.__batch_counter__
|
batches_count = model.__batch_counter__
|
||||||
flops_sum = 0
|
flops_sum = 0
|
||||||
#or isinstance(module, torch.nn.AvgPool2d) or isinstance(module, torch.nn.MaxPool2d) \
|
# or isinstance(module, torch.nn.AvgPool2d) or isinstance(module, torch.nn.MaxPool2d) \
|
||||||
for module in model.modules():
|
for module in model.modules():
|
||||||
if isinstance(module, torch.nn.Conv2d) or isinstance(module, torch.nn.Linear) \
|
if (
|
||||||
or isinstance(module, torch.nn.Conv1d) \
|
isinstance(module, torch.nn.Conv2d)
|
||||||
or hasattr(module, 'calculate_flop_self'):
|
or isinstance(module, torch.nn.Linear)
|
||||||
flops_sum += module.__flops__
|
or isinstance(module, torch.nn.Conv1d)
|
||||||
return flops_sum / batches_count
|
or hasattr(module, "calculate_flop_self")
|
||||||
|
):
|
||||||
|
flops_sum += module.__flops__
|
||||||
|
return flops_sum / batches_count
|
||||||
|
|
||||||
|
|
||||||
# ---- Internal functions
|
# ---- Internal functions
|
||||||
def pool_flops_counter_hook(pool_module, inputs, output):
|
def pool_flops_counter_hook(pool_module, inputs, output):
|
||||||
batch_size = inputs[0].size(0)
|
batch_size = inputs[0].size(0)
|
||||||
kernel_size = pool_module.kernel_size
|
kernel_size = pool_module.kernel_size
|
||||||
out_C, output_height, output_width = output.shape[1:]
|
out_C, output_height, output_width = output.shape[1:]
|
||||||
assert out_C == inputs[0].size(1), '{:} vs. {:}'.format(out_C, inputs[0].size())
|
assert out_C == inputs[0].size(1), "{:} vs. {:}".format(out_C, inputs[0].size())
|
||||||
|
|
||||||
overall_flops = batch_size * out_C * output_height * output_width * kernel_size * kernel_size
|
overall_flops = batch_size * out_C * output_height * output_width * kernel_size * kernel_size
|
||||||
pool_module.__flops__ += overall_flops
|
pool_module.__flops__ += overall_flops
|
||||||
|
|
||||||
|
|
||||||
def self_calculate_flops_counter_hook(self_module, inputs, output):
|
def self_calculate_flops_counter_hook(self_module, inputs, output):
|
||||||
overall_flops = self_module.calculate_flop_self(inputs[0].shape, output.shape)
|
overall_flops = self_module.calculate_flop_self(inputs[0].shape, output.shape)
|
||||||
self_module.__flops__ += overall_flops
|
self_module.__flops__ += overall_flops
|
||||||
|
|
||||||
|
|
||||||
def fc_flops_counter_hook(fc_module, inputs, output):
|
def fc_flops_counter_hook(fc_module, inputs, output):
|
||||||
batch_size = inputs[0].size(0)
|
batch_size = inputs[0].size(0)
|
||||||
xin, xout = fc_module.in_features, fc_module.out_features
|
xin, xout = fc_module.in_features, fc_module.out_features
|
||||||
assert xin == inputs[0].size(1) and xout == output.size(1), 'IO=({:}, {:})'.format(xin, xout)
|
assert xin == inputs[0].size(1) and xout == output.size(1), "IO=({:}, {:})".format(xin, xout)
|
||||||
overall_flops = batch_size * xin * xout
|
overall_flops = batch_size * xin * xout
|
||||||
if fc_module.bias is not None:
|
if fc_module.bias is not None:
|
||||||
overall_flops += batch_size * xout
|
overall_flops += batch_size * xout
|
||||||
fc_module.__flops__ += overall_flops
|
fc_module.__flops__ += overall_flops
|
||||||
|
|
||||||
|
|
||||||
def conv1d_flops_counter_hook(conv_module, inputs, outputs):
|
def conv1d_flops_counter_hook(conv_module, inputs, outputs):
|
||||||
batch_size = inputs[0].size(0)
|
batch_size = inputs[0].size(0)
|
||||||
outL = outputs.shape[-1]
|
outL = outputs.shape[-1]
|
||||||
[kernel] = conv_module.kernel_size
|
[kernel] = conv_module.kernel_size
|
||||||
in_channels = conv_module.in_channels
|
in_channels = conv_module.in_channels
|
||||||
out_channels = conv_module.out_channels
|
out_channels = conv_module.out_channels
|
||||||
groups = conv_module.groups
|
groups = conv_module.groups
|
||||||
conv_per_position_flops = kernel * in_channels * out_channels / groups
|
conv_per_position_flops = kernel * in_channels * out_channels / groups
|
||||||
|
|
||||||
active_elements_count = batch_size * outL
|
|
||||||
overall_flops = conv_per_position_flops * active_elements_count
|
|
||||||
|
|
||||||
if conv_module.bias is not None:
|
active_elements_count = batch_size * outL
|
||||||
overall_flops += out_channels * active_elements_count
|
overall_flops = conv_per_position_flops * active_elements_count
|
||||||
conv_module.__flops__ += overall_flops
|
|
||||||
|
if conv_module.bias is not None:
|
||||||
|
overall_flops += out_channels * active_elements_count
|
||||||
|
conv_module.__flops__ += overall_flops
|
||||||
|
|
||||||
|
|
||||||
def conv2d_flops_counter_hook(conv_module, inputs, output):
|
def conv2d_flops_counter_hook(conv_module, inputs, output):
|
||||||
batch_size = inputs[0].size(0)
|
batch_size = inputs[0].size(0)
|
||||||
output_height, output_width = output.shape[2:]
|
output_height, output_width = output.shape[2:]
|
||||||
|
|
||||||
kernel_height, kernel_width = conv_module.kernel_size
|
kernel_height, kernel_width = conv_module.kernel_size
|
||||||
in_channels = conv_module.in_channels
|
in_channels = conv_module.in_channels
|
||||||
out_channels = conv_module.out_channels
|
out_channels = conv_module.out_channels
|
||||||
groups = conv_module.groups
|
groups = conv_module.groups
|
||||||
conv_per_position_flops = kernel_height * kernel_width * in_channels * out_channels / groups
|
conv_per_position_flops = kernel_height * kernel_width * in_channels * out_channels / groups
|
||||||
|
|
||||||
active_elements_count = batch_size * output_height * output_width
|
active_elements_count = batch_size * output_height * output_width
|
||||||
overall_flops = conv_per_position_flops * active_elements_count
|
overall_flops = conv_per_position_flops * active_elements_count
|
||||||
|
|
||||||
if conv_module.bias is not None:
|
if conv_module.bias is not None:
|
||||||
overall_flops += out_channels * active_elements_count
|
overall_flops += out_channels * active_elements_count
|
||||||
conv_module.__flops__ += overall_flops
|
conv_module.__flops__ += overall_flops
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def batch_counter_hook(module, inputs, output):
|
def batch_counter_hook(module, inputs, output):
|
||||||
# Can have multiple inputs, getting the first one
|
# Can have multiple inputs, getting the first one
|
||||||
inputs = inputs[0]
|
inputs = inputs[0]
|
||||||
batch_size = inputs.shape[0]
|
batch_size = inputs.shape[0]
|
||||||
module.__batch_counter__ += batch_size
|
module.__batch_counter__ += batch_size
|
||||||
|
|
||||||
|
|
||||||
def add_batch_counter_hook_function(module):
|
def add_batch_counter_hook_function(module):
|
||||||
if not hasattr(module, '__batch_counter_handle__'):
|
if not hasattr(module, "__batch_counter_handle__"):
|
||||||
handle = module.register_forward_hook(batch_counter_hook)
|
handle = module.register_forward_hook(batch_counter_hook)
|
||||||
module.__batch_counter_handle__ = handle
|
module.__batch_counter_handle__ = handle
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def add_flops_counter_variable_or_reset(module):
|
def add_flops_counter_variable_or_reset(module):
|
||||||
if isinstance(module, torch.nn.Conv2d) or isinstance(module, torch.nn.Linear) \
|
if (
|
||||||
or isinstance(module, torch.nn.Conv1d) \
|
isinstance(module, torch.nn.Conv2d)
|
||||||
or isinstance(module, torch.nn.AvgPool2d) or isinstance(module, torch.nn.MaxPool2d) \
|
or isinstance(module, torch.nn.Linear)
|
||||||
or hasattr(module, 'calculate_flop_self'):
|
or isinstance(module, torch.nn.Conv1d)
|
||||||
module.__flops__ = 0
|
or isinstance(module, torch.nn.AvgPool2d)
|
||||||
|
or isinstance(module, torch.nn.MaxPool2d)
|
||||||
|
or hasattr(module, "calculate_flop_self")
|
||||||
|
):
|
||||||
|
module.__flops__ = 0
|
||||||
|
|
||||||
|
|
||||||
def add_flops_counter_hook_function(module):
|
def add_flops_counter_hook_function(module):
|
||||||
if isinstance(module, torch.nn.Conv2d):
|
if isinstance(module, torch.nn.Conv2d):
|
||||||
if not hasattr(module, '__flops_handle__'):
|
if not hasattr(module, "__flops_handle__"):
|
||||||
handle = module.register_forward_hook(conv2d_flops_counter_hook)
|
handle = module.register_forward_hook(conv2d_flops_counter_hook)
|
||||||
module.__flops_handle__ = handle
|
module.__flops_handle__ = handle
|
||||||
elif isinstance(module, torch.nn.Conv1d):
|
elif isinstance(module, torch.nn.Conv1d):
|
||||||
if not hasattr(module, '__flops_handle__'):
|
if not hasattr(module, "__flops_handle__"):
|
||||||
handle = module.register_forward_hook(conv1d_flops_counter_hook)
|
handle = module.register_forward_hook(conv1d_flops_counter_hook)
|
||||||
module.__flops_handle__ = handle
|
module.__flops_handle__ = handle
|
||||||
elif isinstance(module, torch.nn.Linear):
|
elif isinstance(module, torch.nn.Linear):
|
||||||
if not hasattr(module, '__flops_handle__'):
|
if not hasattr(module, "__flops_handle__"):
|
||||||
handle = module.register_forward_hook(fc_flops_counter_hook)
|
handle = module.register_forward_hook(fc_flops_counter_hook)
|
||||||
module.__flops_handle__ = handle
|
module.__flops_handle__ = handle
|
||||||
elif isinstance(module, torch.nn.AvgPool2d) or isinstance(module, torch.nn.MaxPool2d):
|
elif isinstance(module, torch.nn.AvgPool2d) or isinstance(module, torch.nn.MaxPool2d):
|
||||||
if not hasattr(module, '__flops_handle__'):
|
if not hasattr(module, "__flops_handle__"):
|
||||||
handle = module.register_forward_hook(pool_flops_counter_hook)
|
handle = module.register_forward_hook(pool_flops_counter_hook)
|
||||||
module.__flops_handle__ = handle
|
module.__flops_handle__ = handle
|
||||||
elif hasattr(module, 'calculate_flop_self'): # self-defined module
|
elif hasattr(module, "calculate_flop_self"): # self-defined module
|
||||||
if not hasattr(module, '__flops_handle__'):
|
if not hasattr(module, "__flops_handle__"):
|
||||||
handle = module.register_forward_hook(self_calculate_flops_counter_hook)
|
handle = module.register_forward_hook(self_calculate_flops_counter_hook)
|
||||||
module.__flops_handle__ = handle
|
module.__flops_handle__ = handle
|
||||||
|
|
||||||
|
|
||||||
def remove_hook_function(module):
|
def remove_hook_function(module):
|
||||||
hookers = ['__batch_counter_handle__', '__flops_handle__']
|
hookers = ["__batch_counter_handle__", "__flops_handle__"]
|
||||||
for hooker in hookers:
|
for hooker in hookers:
|
||||||
if hasattr(module, hooker):
|
if hasattr(module, hooker):
|
||||||
handle = getattr(module, hooker)
|
handle = getattr(module, hooker)
|
||||||
handle.remove()
|
handle.remove()
|
||||||
keys = ['__flops__', '__batch_counter__', '__flops__'] + hookers
|
keys = ["__flops__", "__batch_counter__", "__flops__"] + hookers
|
||||||
for ckey in keys:
|
for ckey in keys:
|
||||||
if hasattr(module, ckey): delattr(module, ckey)
|
if hasattr(module, ckey):
|
||||||
|
delattr(module, ckey)
|
||||||
|
@ -1,65 +1,69 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
class GPUManager():
|
|
||||||
queries = ('index', 'gpu_name', 'memory.free', 'memory.used', 'memory.total', 'power.draw', 'power.limit')
|
|
||||||
|
|
||||||
def __init__(self):
|
class GPUManager:
|
||||||
all_gpus = self.query_gpu(False)
|
queries = ("index", "gpu_name", "memory.free", "memory.used", "memory.total", "power.draw", "power.limit")
|
||||||
|
|
||||||
def get_info(self, ctype):
|
def __init__(self):
|
||||||
cmd = 'nvidia-smi --query-gpu={} --format=csv,noheader'.format(ctype)
|
all_gpus = self.query_gpu(False)
|
||||||
lines = os.popen(cmd).readlines()
|
|
||||||
lines = [line.strip('\n') for line in lines]
|
|
||||||
return lines
|
|
||||||
|
|
||||||
def query_gpu(self, show=True):
|
def get_info(self, ctype):
|
||||||
num_gpus = len( self.get_info('index') )
|
cmd = "nvidia-smi --query-gpu={} --format=csv,noheader".format(ctype)
|
||||||
all_gpus = [ {} for i in range(num_gpus) ]
|
lines = os.popen(cmd).readlines()
|
||||||
for query in self.queries:
|
lines = [line.strip("\n") for line in lines]
|
||||||
infos = self.get_info(query)
|
return lines
|
||||||
for idx, info in enumerate(infos):
|
|
||||||
all_gpus[idx][query] = info
|
|
||||||
|
|
||||||
if 'CUDA_VISIBLE_DEVICES' in os.environ:
|
def query_gpu(self, show=True):
|
||||||
CUDA_VISIBLE_DEVICES = os.environ['CUDA_VISIBLE_DEVICES'].split(',')
|
num_gpus = len(self.get_info("index"))
|
||||||
selected_gpus = []
|
all_gpus = [{} for i in range(num_gpus)]
|
||||||
for idx, CUDA_VISIBLE_DEVICE in enumerate(CUDA_VISIBLE_DEVICES):
|
|
||||||
find = False
|
|
||||||
for gpu in all_gpus:
|
|
||||||
if gpu['index'] == CUDA_VISIBLE_DEVICE:
|
|
||||||
assert not find, 'Duplicate cuda device index : {}'.format(CUDA_VISIBLE_DEVICE)
|
|
||||||
find = True
|
|
||||||
selected_gpus.append( gpu.copy() )
|
|
||||||
selected_gpus[-1]['index'] = '{}'.format(idx)
|
|
||||||
assert find, 'Does not find the device : {}'.format(CUDA_VISIBLE_DEVICE)
|
|
||||||
all_gpus = selected_gpus
|
|
||||||
|
|
||||||
if show:
|
|
||||||
allstrings = ''
|
|
||||||
for gpu in all_gpus:
|
|
||||||
string = '| '
|
|
||||||
for query in self.queries:
|
for query in self.queries:
|
||||||
if query.find('memory') == 0: xinfo = '{:>9}'.format(gpu[query])
|
infos = self.get_info(query)
|
||||||
else: xinfo = gpu[query]
|
for idx, info in enumerate(infos):
|
||||||
string = string + query + ' : ' + xinfo + ' | '
|
all_gpus[idx][query] = info
|
||||||
allstrings = allstrings + string + '\n'
|
|
||||||
return allstrings
|
if "CUDA_VISIBLE_DEVICES" in os.environ:
|
||||||
else:
|
CUDA_VISIBLE_DEVICES = os.environ["CUDA_VISIBLE_DEVICES"].split(",")
|
||||||
return all_gpus
|
selected_gpus = []
|
||||||
|
for idx, CUDA_VISIBLE_DEVICE in enumerate(CUDA_VISIBLE_DEVICES):
|
||||||
|
find = False
|
||||||
|
for gpu in all_gpus:
|
||||||
|
if gpu["index"] == CUDA_VISIBLE_DEVICE:
|
||||||
|
assert not find, "Duplicate cuda device index : {}".format(CUDA_VISIBLE_DEVICE)
|
||||||
|
find = True
|
||||||
|
selected_gpus.append(gpu.copy())
|
||||||
|
selected_gpus[-1]["index"] = "{}".format(idx)
|
||||||
|
assert find, "Does not find the device : {}".format(CUDA_VISIBLE_DEVICE)
|
||||||
|
all_gpus = selected_gpus
|
||||||
|
|
||||||
|
if show:
|
||||||
|
allstrings = ""
|
||||||
|
for gpu in all_gpus:
|
||||||
|
string = "| "
|
||||||
|
for query in self.queries:
|
||||||
|
if query.find("memory") == 0:
|
||||||
|
xinfo = "{:>9}".format(gpu[query])
|
||||||
|
else:
|
||||||
|
xinfo = gpu[query]
|
||||||
|
string = string + query + " : " + xinfo + " | "
|
||||||
|
allstrings = allstrings + string + "\n"
|
||||||
|
return allstrings
|
||||||
|
else:
|
||||||
|
return all_gpus
|
||||||
|
|
||||||
|
def select_by_memory(self, numbers=1):
|
||||||
|
all_gpus = self.query_gpu(False)
|
||||||
|
assert numbers <= len(all_gpus), "Require {} gpus more than you have".format(numbers)
|
||||||
|
alls = []
|
||||||
|
for idx, gpu in enumerate(all_gpus):
|
||||||
|
free_memory = gpu["memory.free"]
|
||||||
|
free_memory = free_memory.split(" ")[0]
|
||||||
|
free_memory = int(free_memory)
|
||||||
|
index = gpu["index"]
|
||||||
|
alls.append((free_memory, index))
|
||||||
|
alls.sort(reverse=True)
|
||||||
|
alls = [int(alls[i][1]) for i in range(numbers)]
|
||||||
|
return sorted(alls)
|
||||||
|
|
||||||
def select_by_memory(self, numbers=1):
|
|
||||||
all_gpus = self.query_gpu(False)
|
|
||||||
assert numbers <= len(all_gpus), 'Require {} gpus more than you have'.format(numbers)
|
|
||||||
alls = []
|
|
||||||
for idx, gpu in enumerate(all_gpus):
|
|
||||||
free_memory = gpu['memory.free']
|
|
||||||
free_memory = free_memory.split(' ')[0]
|
|
||||||
free_memory = int(free_memory)
|
|
||||||
index = gpu['index']
|
|
||||||
alls.append((free_memory, index))
|
|
||||||
alls.sort(reverse = True)
|
|
||||||
alls = [ int(alls[i][1]) for i in range(numbers) ]
|
|
||||||
return sorted(alls)
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import os, hashlib
|
import os
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
def get_md5_file(file_path, post_truncated=5):
|
def get_md5_file(file_path, post_truncated=5):
|
||||||
md5_hash = hashlib.md5()
|
md5_hash = hashlib.md5()
|
||||||
if os.path.exists(file_path):
|
if os.path.exists(file_path):
|
||||||
xfile = open(file_path, "rb")
|
xfile = open(file_path, "rb")
|
||||||
content = xfile.read()
|
content = xfile.read()
|
||||||
md5_hash.update(content)
|
md5_hash.update(content)
|
||||||
digest = md5_hash.hexdigest()
|
digest = md5_hash.hexdigest()
|
||||||
else:
|
else:
|
||||||
raise ValueError('[get_md5_file] {:} does not exist'.format(file_path))
|
raise ValueError("[get_md5_file] {:} does not exist".format(file_path))
|
||||||
if post_truncated is None:
|
if post_truncated is None:
|
||||||
return digest
|
return digest
|
||||||
else:
|
else:
|
||||||
return digest[-post_truncated:]
|
return digest[-post_truncated:]
|
||||||
|
@ -10,48 +10,58 @@ from log_utils import time_string
|
|||||||
|
|
||||||
|
|
||||||
def evaluate_one_shot(model, xloader, api, cal_mode, seed=111):
|
def evaluate_one_shot(model, xloader, api, cal_mode, seed=111):
|
||||||
print ('This is an old version of codes to use NAS-Bench-API, and should be modified to align with the new version. Please contact me for more details if you use this function.')
|
print(
|
||||||
weights = deepcopy(model.state_dict())
|
"This is an old version of codes to use NAS-Bench-API, and should be modified to align with the new version. Please contact me for more details if you use this function."
|
||||||
model.train(cal_mode)
|
)
|
||||||
with torch.no_grad():
|
weights = deepcopy(model.state_dict())
|
||||||
logits = nn.functional.log_softmax(model.arch_parameters, dim=-1)
|
model.train(cal_mode)
|
||||||
archs = CellStructure.gen_all(model.op_names, model.max_nodes, False)
|
with torch.no_grad():
|
||||||
probs, accuracies, gt_accs_10_valid, gt_accs_10_test = [], [], [], []
|
logits = nn.functional.log_softmax(model.arch_parameters, dim=-1)
|
||||||
loader_iter = iter(xloader)
|
archs = CellStructure.gen_all(model.op_names, model.max_nodes, False)
|
||||||
random.seed(seed)
|
probs, accuracies, gt_accs_10_valid, gt_accs_10_test = [], [], [], []
|
||||||
random.shuffle(archs)
|
|
||||||
for idx, arch in enumerate(archs):
|
|
||||||
arch_index = api.query_index_by_arch( arch )
|
|
||||||
metrics = api.get_more_info(arch_index, 'cifar10-valid', None, False, False)
|
|
||||||
gt_accs_10_valid.append( metrics['valid-accuracy'] )
|
|
||||||
metrics = api.get_more_info(arch_index, 'cifar10', None, False, False)
|
|
||||||
gt_accs_10_test.append( metrics['test-accuracy'] )
|
|
||||||
select_logits = []
|
|
||||||
for i, node_info in enumerate(arch.nodes):
|
|
||||||
for op, xin in node_info:
|
|
||||||
node_str = '{:}<-{:}'.format(i+1, xin)
|
|
||||||
op_index = model.op_names.index(op)
|
|
||||||
select_logits.append( logits[model.edge2index[node_str], op_index] )
|
|
||||||
cur_prob = sum(select_logits).item()
|
|
||||||
probs.append( cur_prob )
|
|
||||||
cor_prob_valid = np.corrcoef(probs, gt_accs_10_valid)[0,1]
|
|
||||||
cor_prob_test = np.corrcoef(probs, gt_accs_10_test )[0,1]
|
|
||||||
print ('{:} correlation for probabilities : {:.6f} on CIFAR-10 validation and {:.6f} on CIFAR-10 test'.format(time_string(), cor_prob_valid, cor_prob_test))
|
|
||||||
|
|
||||||
for idx, arch in enumerate(archs):
|
|
||||||
model.set_cal_mode('dynamic', arch)
|
|
||||||
try:
|
|
||||||
inputs, targets = next(loader_iter)
|
|
||||||
except:
|
|
||||||
loader_iter = iter(xloader)
|
loader_iter = iter(xloader)
|
||||||
inputs, targets = next(loader_iter)
|
random.seed(seed)
|
||||||
_, logits = model(inputs.cuda())
|
random.shuffle(archs)
|
||||||
_, preds = torch.max(logits, dim=-1)
|
for idx, arch in enumerate(archs):
|
||||||
correct = (preds == targets.cuda() ).float()
|
arch_index = api.query_index_by_arch(arch)
|
||||||
accuracies.append( correct.mean().item() )
|
metrics = api.get_more_info(arch_index, "cifar10-valid", None, False, False)
|
||||||
if idx != 0 and (idx % 500 == 0 or idx + 1 == len(archs)):
|
gt_accs_10_valid.append(metrics["valid-accuracy"])
|
||||||
cor_accs_valid = np.corrcoef(accuracies, gt_accs_10_valid[:idx+1])[0,1]
|
metrics = api.get_more_info(arch_index, "cifar10", None, False, False)
|
||||||
cor_accs_test = np.corrcoef(accuracies, gt_accs_10_test [:idx+1])[0,1]
|
gt_accs_10_test.append(metrics["test-accuracy"])
|
||||||
print ('{:} {:05d}/{:05d} mode={:5s}, correlation : accs={:.5f} for CIFAR-10 valid, {:.5f} for CIFAR-10 test.'.format(time_string(), idx, len(archs), 'Train' if cal_mode else 'Eval', cor_accs_valid, cor_accs_test))
|
select_logits = []
|
||||||
model.load_state_dict(weights)
|
for i, node_info in enumerate(arch.nodes):
|
||||||
return archs, probs, accuracies
|
for op, xin in node_info:
|
||||||
|
node_str = "{:}<-{:}".format(i + 1, xin)
|
||||||
|
op_index = model.op_names.index(op)
|
||||||
|
select_logits.append(logits[model.edge2index[node_str], op_index])
|
||||||
|
cur_prob = sum(select_logits).item()
|
||||||
|
probs.append(cur_prob)
|
||||||
|
cor_prob_valid = np.corrcoef(probs, gt_accs_10_valid)[0, 1]
|
||||||
|
cor_prob_test = np.corrcoef(probs, gt_accs_10_test)[0, 1]
|
||||||
|
print(
|
||||||
|
"{:} correlation for probabilities : {:.6f} on CIFAR-10 validation and {:.6f} on CIFAR-10 test".format(
|
||||||
|
time_string(), cor_prob_valid, cor_prob_test
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for idx, arch in enumerate(archs):
|
||||||
|
model.set_cal_mode("dynamic", arch)
|
||||||
|
try:
|
||||||
|
inputs, targets = next(loader_iter)
|
||||||
|
except:
|
||||||
|
loader_iter = iter(xloader)
|
||||||
|
inputs, targets = next(loader_iter)
|
||||||
|
_, logits = model(inputs.cuda())
|
||||||
|
_, preds = torch.max(logits, dim=-1)
|
||||||
|
correct = (preds == targets.cuda()).float()
|
||||||
|
accuracies.append(correct.mean().item())
|
||||||
|
if idx != 0 and (idx % 500 == 0 or idx + 1 == len(archs)):
|
||||||
|
cor_accs_valid = np.corrcoef(accuracies, gt_accs_10_valid[: idx + 1])[0, 1]
|
||||||
|
cor_accs_test = np.corrcoef(accuracies, gt_accs_10_test[: idx + 1])[0, 1]
|
||||||
|
print(
|
||||||
|
"{:} {:05d}/{:05d} mode={:5s}, correlation : accs={:.5f} for CIFAR-10 valid, {:.5f} for CIFAR-10 test.".format(
|
||||||
|
time_string(), idx, len(archs), "Train" if cal_mode else "Eval", cor_accs_valid, cor_accs_test
|
||||||
|
)
|
||||||
|
)
|
||||||
|
model.load_state_dict(weights)
|
||||||
|
return archs, probs, accuracies
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
|
|
||||||
def split_str2indexes(string: str, max_check: int, length_limit=5):
|
def split_str2indexes(string: str, max_check: int, length_limit=5):
|
||||||
if not isinstance(string, str):
|
if not isinstance(string, str):
|
||||||
raise ValueError('Invalid scheme for {:}'.format(string))
|
raise ValueError("Invalid scheme for {:}".format(string))
|
||||||
srangestr = "".join(string.split())
|
srangestr = "".join(string.split())
|
||||||
indexes = set()
|
indexes = set()
|
||||||
for srange in srangestr.split(','):
|
for srange in srangestr.split(","):
|
||||||
srange = srange.split('-')
|
srange = srange.split("-")
|
||||||
if len(srange) != 2:
|
if len(srange) != 2:
|
||||||
raise ValueError('invalid srange : {:}'.format(srange))
|
raise ValueError("invalid srange : {:}".format(srange))
|
||||||
if length_limit is not None:
|
if length_limit is not None:
|
||||||
assert len(srange[0]) == len(srange[1]) == length_limit, 'invalid srange : {:}'.format(srange)
|
assert len(srange[0]) == len(srange[1]) == length_limit, "invalid srange : {:}".format(srange)
|
||||||
srange = (int(srange[0]), int(srange[1]))
|
srange = (int(srange[0]), int(srange[1]))
|
||||||
if not (0 <= srange[0] <= srange[1] < max_check):
|
if not (0 <= srange[0] <= srange[1] < max_check):
|
||||||
raise ValueError('{:} vs {:} vs {:}'.format(srange[0], srange[1], max_check))
|
raise ValueError("{:} vs {:} vs {:}".format(srange[0], srange[1], max_check))
|
||||||
for i in range(srange[0], srange[1]+1):
|
for i in range(srange[0], srange[1] + 1):
|
||||||
indexes.add(i)
|
indexes.add(i)
|
||||||
return indexes
|
return indexes
|
||||||
|
@ -11,309 +11,350 @@ from sklearn.decomposition import TruncatedSVD
|
|||||||
|
|
||||||
|
|
||||||
def available_module_types():
|
def available_module_types():
|
||||||
return (nn.Conv2d, nn.Linear)
|
return (nn.Conv2d, nn.Linear)
|
||||||
|
|
||||||
|
|
||||||
def get_conv2D_Wmats(tensor: np.ndarray) -> List[np.ndarray]:
|
def get_conv2D_Wmats(tensor: np.ndarray) -> List[np.ndarray]:
|
||||||
"""
|
"""
|
||||||
Extract W slices from a 4 index conv2D tensor of shape: (N,M,i,j) or (M,N,i,j).
|
Extract W slices from a 4 index conv2D tensor of shape: (N,M,i,j) or (M,N,i,j).
|
||||||
Return ij (N x M) matrices
|
Return ij (N x M) matrices
|
||||||
"""
|
"""
|
||||||
mats = []
|
mats = []
|
||||||
N, M, imax, jmax = tensor.shape
|
N, M, imax, jmax = tensor.shape
|
||||||
assert N + M >= imax + jmax, 'invalid tensor shape detected: {}x{} (NxM), {}x{} (i,j)'.format(N, M, imax, jmax)
|
assert N + M >= imax + jmax, "invalid tensor shape detected: {}x{} (NxM), {}x{} (i,j)".format(N, M, imax, jmax)
|
||||||
for i in range(imax):
|
for i in range(imax):
|
||||||
for j in range(jmax):
|
for j in range(jmax):
|
||||||
w = tensor[:, :, i, j]
|
w = tensor[:, :, i, j]
|
||||||
if N < M: w = w.T
|
if N < M:
|
||||||
mats.append(w)
|
w = w.T
|
||||||
return mats
|
mats.append(w)
|
||||||
|
return mats
|
||||||
|
|
||||||
|
|
||||||
def glorot_norm_check(W, N, M, rf_size, lower=0.5, upper=1.5):
|
def glorot_norm_check(W, N, M, rf_size, lower=0.5, upper=1.5):
|
||||||
"""Check if this layer needs Glorot Normalization Fix"""
|
"""Check if this layer needs Glorot Normalization Fix"""
|
||||||
|
|
||||||
kappa = np.sqrt(2 / ((N + M) * rf_size))
|
kappa = np.sqrt(2 / ((N + M) * rf_size))
|
||||||
norm = np.linalg.norm(W)
|
norm = np.linalg.norm(W)
|
||||||
|
|
||||||
check1 = norm / np.sqrt(N * M)
|
check1 = norm / np.sqrt(N * M)
|
||||||
check2 = norm / (kappa * np.sqrt(N * M))
|
check2 = norm / (kappa * np.sqrt(N * M))
|
||||||
|
|
||||||
|
if (rf_size > 1) and (check2 > lower) and (check2 < upper):
|
||||||
|
return check2, True
|
||||||
|
elif (check1 > lower) & (check1 < upper):
|
||||||
|
return check1, True
|
||||||
|
else:
|
||||||
|
if rf_size > 1:
|
||||||
|
return check2, False
|
||||||
|
else:
|
||||||
|
return check1, False
|
||||||
|
|
||||||
if (rf_size > 1) and (check2 > lower) and (check2 < upper):
|
|
||||||
return check2, True
|
|
||||||
elif (check1 > lower) & (check1 < upper):
|
|
||||||
return check1, True
|
|
||||||
else:
|
|
||||||
if rf_size > 1: return check2, False
|
|
||||||
else: return check1, False
|
|
||||||
|
|
||||||
def glorot_norm_fix(w, n, m, rf_size):
|
def glorot_norm_fix(w, n, m, rf_size):
|
||||||
"""Apply Glorot Normalization Fix."""
|
"""Apply Glorot Normalization Fix."""
|
||||||
kappa = np.sqrt(2 / ((n + m) * rf_size))
|
kappa = np.sqrt(2 / ((n + m) * rf_size))
|
||||||
w = w / kappa
|
w = w / kappa
|
||||||
return w
|
return w
|
||||||
|
|
||||||
|
|
||||||
def analyze_weights(weights, min_size, max_size, alphas, lognorms, spectralnorms, softranks, normalize, glorot_fix):
|
def analyze_weights(weights, min_size, max_size, alphas, lognorms, spectralnorms, softranks, normalize, glorot_fix):
|
||||||
results = OrderedDict()
|
results = OrderedDict()
|
||||||
count = len(weights)
|
count = len(weights)
|
||||||
if count == 0: return results
|
if count == 0:
|
||||||
|
return results
|
||||||
|
|
||||||
for i, weight in enumerate(weights):
|
for i, weight in enumerate(weights):
|
||||||
M, N = np.min(weight.shape), np.max(weight.shape)
|
M, N = np.min(weight.shape), np.max(weight.shape)
|
||||||
Q = N / M
|
Q = N / M
|
||||||
results[i] = cur_res = OrderedDict(N=N, M=M, Q=Q)
|
results[i] = cur_res = OrderedDict(N=N, M=M, Q=Q)
|
||||||
check, checkTF = glorot_norm_check(weight, N, M, count)
|
check, checkTF = glorot_norm_check(weight, N, M, count)
|
||||||
cur_res['check'] = check
|
cur_res["check"] = check
|
||||||
cur_res['checkTF'] = checkTF
|
cur_res["checkTF"] = checkTF
|
||||||
# assume receptive field size is count
|
# assume receptive field size is count
|
||||||
if glorot_fix:
|
if glorot_fix:
|
||||||
weight = glorot_norm_fix(weight, N, M, count)
|
weight = glorot_norm_fix(weight, N, M, count)
|
||||||
else:
|
else:
|
||||||
# probably never needed since we always fix for glorot
|
# probably never needed since we always fix for glorot
|
||||||
weight = weight * np.sqrt(count / 2.0)
|
weight = weight * np.sqrt(count / 2.0)
|
||||||
|
|
||||||
if spectralnorms: # spectralnorm is the max eigenvalues
|
if spectralnorms: # spectralnorm is the max eigenvalues
|
||||||
svd = TruncatedSVD(n_components=1, n_iter=7, random_state=10)
|
svd = TruncatedSVD(n_components=1, n_iter=7, random_state=10)
|
||||||
svd.fit(weight)
|
svd.fit(weight)
|
||||||
sv = svd.singular_values_
|
sv = svd.singular_values_
|
||||||
sv_max = np.max(sv)
|
sv_max = np.max(sv)
|
||||||
if normalize:
|
if normalize:
|
||||||
evals = sv * sv / N
|
evals = sv * sv / N
|
||||||
else:
|
else:
|
||||||
evals = sv * sv
|
evals = sv * sv
|
||||||
lambda0 = evals[0]
|
lambda0 = evals[0]
|
||||||
cur_res["spectralnorm"] = lambda0
|
cur_res["spectralnorm"] = lambda0
|
||||||
cur_res["logspectralnorm"] = np.log10(lambda0)
|
cur_res["logspectralnorm"] = np.log10(lambda0)
|
||||||
else:
|
else:
|
||||||
lambda0 = None
|
lambda0 = None
|
||||||
|
|
||||||
if M < min_size:
|
if M < min_size:
|
||||||
summary = "Weight matrix {}/{} ({},{}): Skipping: too small (<{})".format(i + 1, count, M, N, min_size)
|
summary = "Weight matrix {}/{} ({},{}): Skipping: too small (<{})".format(i + 1, count, M, N, min_size)
|
||||||
cur_res["summary"] = summary
|
cur_res["summary"] = summary
|
||||||
continue
|
continue
|
||||||
elif max_size > 0 and M > max_size:
|
elif max_size > 0 and M > max_size:
|
||||||
summary = "Weight matrix {}/{} ({},{}): Skipping: too big (testing) (>{})".format(i + 1, count, M, N, max_size)
|
summary = "Weight matrix {}/{} ({},{}): Skipping: too big (testing) (>{})".format(
|
||||||
cur_res["summary"] = summary
|
i + 1, count, M, N, max_size
|
||||||
continue
|
)
|
||||||
else:
|
cur_res["summary"] = summary
|
||||||
summary = []
|
continue
|
||||||
if alphas:
|
else:
|
||||||
import powerlaw
|
summary = []
|
||||||
svd = TruncatedSVD(n_components=M - 1, n_iter=7, random_state=10)
|
if alphas:
|
||||||
svd.fit(weight.astype(float))
|
import powerlaw
|
||||||
sv = svd.singular_values_
|
|
||||||
if normalize: evals = sv * sv / N
|
|
||||||
else: evals = sv * sv
|
|
||||||
|
|
||||||
lambda_max = np.max(evals)
|
svd = TruncatedSVD(n_components=M - 1, n_iter=7, random_state=10)
|
||||||
fit = powerlaw.Fit(evals, xmax=lambda_max, verbose=False)
|
svd.fit(weight.astype(float))
|
||||||
alpha = fit.alpha
|
sv = svd.singular_values_
|
||||||
cur_res["alpha"] = alpha
|
if normalize:
|
||||||
D = fit.D
|
evals = sv * sv / N
|
||||||
cur_res["D"] = D
|
else:
|
||||||
cur_res["lambda_min"] = np.min(evals)
|
evals = sv * sv
|
||||||
cur_res["lambda_max"] = lambda_max
|
|
||||||
alpha_weighted = alpha * np.log10(lambda_max)
|
|
||||||
cur_res["alpha_weighted"] = alpha_weighted
|
|
||||||
tolerance = lambda_max * M * np.finfo(np.max(sv)).eps
|
|
||||||
cur_res["rank_loss"] = np.count_nonzero(sv > tolerance, axis=-1)
|
|
||||||
|
|
||||||
logpnorm = np.log10(np.sum([ev ** alpha for ev in evals]))
|
lambda_max = np.max(evals)
|
||||||
cur_res["logpnorm"] = logpnorm
|
fit = powerlaw.Fit(evals, xmax=lambda_max, verbose=False)
|
||||||
|
alpha = fit.alpha
|
||||||
|
cur_res["alpha"] = alpha
|
||||||
|
D = fit.D
|
||||||
|
cur_res["D"] = D
|
||||||
|
cur_res["lambda_min"] = np.min(evals)
|
||||||
|
cur_res["lambda_max"] = lambda_max
|
||||||
|
alpha_weighted = alpha * np.log10(lambda_max)
|
||||||
|
cur_res["alpha_weighted"] = alpha_weighted
|
||||||
|
tolerance = lambda_max * M * np.finfo(np.max(sv)).eps
|
||||||
|
cur_res["rank_loss"] = np.count_nonzero(sv > tolerance, axis=-1)
|
||||||
|
|
||||||
summary.append(
|
logpnorm = np.log10(np.sum([ev ** alpha for ev in evals]))
|
||||||
"Weight matrix {}/{} ({},{}): Alpha: {}, Alpha Weighted: {}, D: {}, pNorm {}".format(i + 1, count, M, N, alpha,
|
cur_res["logpnorm"] = logpnorm
|
||||||
alpha_weighted, D,
|
|
||||||
logpnorm))
|
|
||||||
|
|
||||||
if lognorms:
|
summary.append(
|
||||||
norm = np.linalg.norm(weight) # Frobenius Norm
|
"Weight matrix {}/{} ({},{}): Alpha: {}, Alpha Weighted: {}, D: {}, pNorm {}".format(
|
||||||
cur_res["norm"] = norm
|
i + 1, count, M, N, alpha, alpha_weighted, D, logpnorm
|
||||||
lognorm = np.log10(norm)
|
)
|
||||||
cur_res["lognorm"] = lognorm
|
)
|
||||||
|
|
||||||
X = np.dot(weight.T, weight)
|
if lognorms:
|
||||||
if normalize: X = X / N
|
norm = np.linalg.norm(weight) # Frobenius Norm
|
||||||
normX = np.linalg.norm(X) # Frobenius Norm
|
cur_res["norm"] = norm
|
||||||
cur_res["normX"] = normX
|
lognorm = np.log10(norm)
|
||||||
lognormX = np.log10(normX)
|
cur_res["lognorm"] = lognorm
|
||||||
cur_res["lognormX"] = lognormX
|
|
||||||
|
|
||||||
summary.append(
|
X = np.dot(weight.T, weight)
|
||||||
"Weight matrix {}/{} ({},{}): LogNorm: {} ; LogNormX: {}".format(i + 1, count, M, N, lognorm, lognormX))
|
if normalize:
|
||||||
|
X = X / N
|
||||||
|
normX = np.linalg.norm(X) # Frobenius Norm
|
||||||
|
cur_res["normX"] = normX
|
||||||
|
lognormX = np.log10(normX)
|
||||||
|
cur_res["lognormX"] = lognormX
|
||||||
|
|
||||||
if softranks:
|
summary.append(
|
||||||
softrank = norm ** 2 / sv_max ** 2
|
"Weight matrix {}/{} ({},{}): LogNorm: {} ; LogNormX: {}".format(i + 1, count, M, N, lognorm, lognormX)
|
||||||
softranklog = np.log10(softrank)
|
)
|
||||||
softranklogratio = lognorm / np.log10(sv_max)
|
|
||||||
cur_res["softrank"] = softrank
|
if softranks:
|
||||||
cur_res["softranklog"] = softranklog
|
softrank = norm ** 2 / sv_max ** 2
|
||||||
cur_res["softranklogratio"] = softranklogratio
|
softranklog = np.log10(softrank)
|
||||||
summary += "{}. Softrank: {}. Softrank log: {}. Softrank log ratio: {}".format(summary, softrank, softranklog,
|
softranklogratio = lognorm / np.log10(sv_max)
|
||||||
softranklogratio)
|
cur_res["softrank"] = softrank
|
||||||
cur_res["summary"] = "\n".join(summary)
|
cur_res["softranklog"] = softranklog
|
||||||
return results
|
cur_res["softranklogratio"] = softranklogratio
|
||||||
|
summary += "{}. Softrank: {}. Softrank log: {}. Softrank log ratio: {}".format(
|
||||||
|
summary, softrank, softranklog, softranklogratio
|
||||||
|
)
|
||||||
|
cur_res["summary"] = "\n".join(summary)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
def compute_details(results):
|
def compute_details(results):
|
||||||
"""
|
"""
|
||||||
Return a pandas data frame.
|
Return a pandas data frame.
|
||||||
"""
|
"""
|
||||||
final_summary = OrderedDict()
|
final_summary = OrderedDict()
|
||||||
|
|
||||||
metrics = {
|
metrics = {
|
||||||
# key in "results" : pretty print name
|
# key in "results" : pretty print name
|
||||||
"check": "Check",
|
"check": "Check",
|
||||||
"checkTF": "CheckTF",
|
"checkTF": "CheckTF",
|
||||||
"norm": "Norm",
|
"norm": "Norm",
|
||||||
"lognorm": "LogNorm",
|
"lognorm": "LogNorm",
|
||||||
"normX": "Norm X",
|
"normX": "Norm X",
|
||||||
"lognormX": "LogNorm X",
|
"lognormX": "LogNorm X",
|
||||||
"alpha": "Alpha",
|
"alpha": "Alpha",
|
||||||
"alpha_weighted": "Alpha Weighted",
|
"alpha_weighted": "Alpha Weighted",
|
||||||
"spectralnorm": "Spectral Norm",
|
"spectralnorm": "Spectral Norm",
|
||||||
"logspectralnorm": "Log Spectral Norm",
|
"logspectralnorm": "Log Spectral Norm",
|
||||||
"softrank": "Softrank",
|
"softrank": "Softrank",
|
||||||
"softranklog": "Softrank Log",
|
"softranklog": "Softrank Log",
|
||||||
"softranklogratio": "Softrank Log Ratio",
|
"softranklogratio": "Softrank Log Ratio",
|
||||||
"sigma_mp": "Marchenko-Pastur (MP) fit sigma",
|
"sigma_mp": "Marchenko-Pastur (MP) fit sigma",
|
||||||
"numofSpikes": "Number of spikes per MP fit",
|
"numofSpikes": "Number of spikes per MP fit",
|
||||||
"ratio_numofSpikes": "aka, percent_mass, Number of spikes / total number of evals",
|
"ratio_numofSpikes": "aka, percent_mass, Number of spikes / total number of evals",
|
||||||
"softrank_mp": "Softrank for MP fit",
|
"softrank_mp": "Softrank for MP fit",
|
||||||
"logpnorm": "alpha pNorm"
|
"logpnorm": "alpha pNorm",
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics_stats = []
|
metrics_stats = []
|
||||||
for metric in metrics:
|
|
||||||
metrics_stats.append("{}_min".format(metric))
|
|
||||||
metrics_stats.append("{}_max".format(metric))
|
|
||||||
metrics_stats.append("{}_avg".format(metric))
|
|
||||||
|
|
||||||
metrics_stats.append("{}_compound_min".format(metric))
|
|
||||||
metrics_stats.append("{}_compound_max".format(metric))
|
|
||||||
metrics_stats.append("{}_compound_avg".format(metric))
|
|
||||||
|
|
||||||
columns = ["layer_id", "layer_type", "N", "M", "layer_count", "slice",
|
|
||||||
"slice_count", "level", "comment"] + [*metrics] + metrics_stats
|
|
||||||
|
|
||||||
metrics_values = {}
|
|
||||||
metrics_values_compound = {}
|
|
||||||
|
|
||||||
for metric in metrics:
|
|
||||||
metrics_values[metric] = []
|
|
||||||
metrics_values_compound[metric] = []
|
|
||||||
|
|
||||||
layer_count = 0
|
|
||||||
for layer_id, result in results.items():
|
|
||||||
layer_count += 1
|
|
||||||
|
|
||||||
layer_type = np.NAN
|
|
||||||
if "layer_type" in result:
|
|
||||||
layer_type = str(result["layer_type"]).replace("LAYER_TYPE.", "")
|
|
||||||
|
|
||||||
compounds = {} # temp var
|
|
||||||
for metric in metrics:
|
for metric in metrics:
|
||||||
compounds[metric] = []
|
metrics_stats.append("{}_min".format(metric))
|
||||||
|
metrics_stats.append("{}_max".format(metric))
|
||||||
|
metrics_stats.append("{}_avg".format(metric))
|
||||||
|
|
||||||
slice_count, Ntotal, Mtotal = 0, 0, 0
|
metrics_stats.append("{}_compound_min".format(metric))
|
||||||
for slice_id, summary in result.items():
|
metrics_stats.append("{}_compound_max".format(metric))
|
||||||
if not str(slice_id).isdigit():
|
metrics_stats.append("{}_compound_avg".format(metric))
|
||||||
continue
|
|
||||||
slice_count += 1
|
|
||||||
|
|
||||||
N = np.NAN
|
columns = (
|
||||||
if "N" in summary:
|
["layer_id", "layer_type", "N", "M", "layer_count", "slice", "slice_count", "level", "comment"]
|
||||||
N = summary["N"]
|
+ [*metrics]
|
||||||
Ntotal += N
|
+ metrics_stats
|
||||||
|
)
|
||||||
|
|
||||||
M = np.NAN
|
metrics_values = {}
|
||||||
if "M" in summary:
|
metrics_values_compound = {}
|
||||||
M = summary["M"]
|
|
||||||
Mtotal += M
|
|
||||||
|
|
||||||
data = {"layer_id": layer_id, "layer_type": layer_type, "N": N, "M": M, "slice": slice_id, "level": "SLICE",
|
for metric in metrics:
|
||||||
"comment": "Slice level"}
|
metrics_values[metric] = []
|
||||||
for metric in metrics:
|
metrics_values_compound[metric] = []
|
||||||
if metric in summary:
|
|
||||||
value = summary[metric]
|
|
||||||
if value is not None:
|
|
||||||
metrics_values[metric].append(value)
|
|
||||||
compounds[metric].append(value)
|
|
||||||
data[metric] = value
|
|
||||||
|
|
||||||
data = {"layer_id": layer_id, "layer_type": layer_type, "N": Ntotal, "M": Mtotal, "slice_count": slice_count,
|
layer_count = 0
|
||||||
"level": "LAYER", "comment": "Layer level"}
|
for layer_id, result in results.items():
|
||||||
# Compute the compound value over the slices
|
layer_count += 1
|
||||||
for metric, value in compounds.items():
|
|
||||||
count = len(value)
|
|
||||||
if count == 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
compound = np.mean(value)
|
layer_type = np.NAN
|
||||||
metrics_values_compound[metric].append(compound)
|
if "layer_type" in result:
|
||||||
data[metric] = compound
|
layer_type = str(result["layer_type"]).replace("LAYER_TYPE.", "")
|
||||||
|
|
||||||
data = {"layer_count": layer_count, "level": "NETWORK", "comment": "Network Level"}
|
compounds = {} # temp var
|
||||||
for metric, metric_name in metrics.items():
|
for metric in metrics:
|
||||||
if metric not in metrics_values or len(metrics_values[metric]) == 0:
|
compounds[metric] = []
|
||||||
continue
|
|
||||||
|
|
||||||
values = metrics_values[metric]
|
slice_count, Ntotal, Mtotal = 0, 0, 0
|
||||||
minimum = min(values)
|
for slice_id, summary in result.items():
|
||||||
maximum = max(values)
|
if not str(slice_id).isdigit():
|
||||||
avg = np.mean(values)
|
continue
|
||||||
final_summary[metric] = avg
|
slice_count += 1
|
||||||
# print("{}: min: {}, max: {}, avg: {}".format(metric_name, minimum, maximum, avg))
|
|
||||||
data["{}_min".format(metric)] = minimum
|
|
||||||
data["{}_max".format(metric)] = maximum
|
|
||||||
data["{}_avg".format(metric)] = avg
|
|
||||||
|
|
||||||
values = metrics_values_compound[metric]
|
N = np.NAN
|
||||||
minimum = min(values)
|
if "N" in summary:
|
||||||
maximum = max(values)
|
N = summary["N"]
|
||||||
avg = np.mean(values)
|
Ntotal += N
|
||||||
final_summary["{}_compound".format(metric)] = avg
|
|
||||||
# print("{} compound: min: {}, max: {}, avg: {}".format(metric_name, minimum, maximum, avg))
|
|
||||||
data["{}_compound_min".format(metric)] = minimum
|
|
||||||
data["{}_compound_max".format(metric)] = maximum
|
|
||||||
data["{}_compound_avg".format(metric)] = avg
|
|
||||||
|
|
||||||
return final_summary
|
M = np.NAN
|
||||||
|
if "M" in summary:
|
||||||
|
M = summary["M"]
|
||||||
|
Mtotal += M
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"layer_id": layer_id,
|
||||||
|
"layer_type": layer_type,
|
||||||
|
"N": N,
|
||||||
|
"M": M,
|
||||||
|
"slice": slice_id,
|
||||||
|
"level": "SLICE",
|
||||||
|
"comment": "Slice level",
|
||||||
|
}
|
||||||
|
for metric in metrics:
|
||||||
|
if metric in summary:
|
||||||
|
value = summary[metric]
|
||||||
|
if value is not None:
|
||||||
|
metrics_values[metric].append(value)
|
||||||
|
compounds[metric].append(value)
|
||||||
|
data[metric] = value
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"layer_id": layer_id,
|
||||||
|
"layer_type": layer_type,
|
||||||
|
"N": Ntotal,
|
||||||
|
"M": Mtotal,
|
||||||
|
"slice_count": slice_count,
|
||||||
|
"level": "LAYER",
|
||||||
|
"comment": "Layer level",
|
||||||
|
}
|
||||||
|
# Compute the compound value over the slices
|
||||||
|
for metric, value in compounds.items():
|
||||||
|
count = len(value)
|
||||||
|
if count == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
compound = np.mean(value)
|
||||||
|
metrics_values_compound[metric].append(compound)
|
||||||
|
data[metric] = compound
|
||||||
|
|
||||||
|
data = {"layer_count": layer_count, "level": "NETWORK", "comment": "Network Level"}
|
||||||
|
for metric, metric_name in metrics.items():
|
||||||
|
if metric not in metrics_values or len(metrics_values[metric]) == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
values = metrics_values[metric]
|
||||||
|
minimum = min(values)
|
||||||
|
maximum = max(values)
|
||||||
|
avg = np.mean(values)
|
||||||
|
final_summary[metric] = avg
|
||||||
|
# print("{}: min: {}, max: {}, avg: {}".format(metric_name, minimum, maximum, avg))
|
||||||
|
data["{}_min".format(metric)] = minimum
|
||||||
|
data["{}_max".format(metric)] = maximum
|
||||||
|
data["{}_avg".format(metric)] = avg
|
||||||
|
|
||||||
|
values = metrics_values_compound[metric]
|
||||||
|
minimum = min(values)
|
||||||
|
maximum = max(values)
|
||||||
|
avg = np.mean(values)
|
||||||
|
final_summary["{}_compound".format(metric)] = avg
|
||||||
|
# print("{} compound: min: {}, max: {}, avg: {}".format(metric_name, minimum, maximum, avg))
|
||||||
|
data["{}_compound_min".format(metric)] = minimum
|
||||||
|
data["{}_compound_max".format(metric)] = maximum
|
||||||
|
data["{}_compound_avg".format(metric)] = avg
|
||||||
|
|
||||||
|
return final_summary
|
||||||
|
|
||||||
|
|
||||||
def analyze(model: nn.Module, min_size=50, max_size=0,
|
def analyze(
|
||||||
alphas: bool = False, lognorms: bool = True, spectralnorms: bool = False,
|
model: nn.Module,
|
||||||
softranks: bool = False, normalize: bool = False, glorot_fix: bool = False):
|
min_size=50,
|
||||||
"""
|
max_size=0,
|
||||||
Analyze the weight matrices of a model.
|
alphas: bool = False,
|
||||||
:param model: A PyTorch model
|
lognorms: bool = True,
|
||||||
:param min_size: The minimum weight matrix size to analyze.
|
spectralnorms: bool = False,
|
||||||
:param max_size: The maximum weight matrix size to analyze (0 = no limit).
|
softranks: bool = False,
|
||||||
:param alphas: Compute the power laws (alpha) of the weight matrices.
|
normalize: bool = False,
|
||||||
Time consuming so disabled by default (use lognorm if you want speed)
|
glorot_fix: bool = False,
|
||||||
:param lognorms: Compute the log norms of the weight matrices.
|
):
|
||||||
:param spectralnorms: Compute the spectral norm (max eigenvalue) of the weight matrices.
|
"""
|
||||||
:param softranks: Compute the soft norm (i.e. StableRank) of the weight matrices.
|
Analyze the weight matrices of a model.
|
||||||
:param normalize: Normalize or not.
|
:param model: A PyTorch model
|
||||||
:param glorot_fix:
|
:param min_size: The minimum weight matrix size to analyze.
|
||||||
:return: (a dict of all layers' results, a dict of the summarized info)
|
:param max_size: The maximum weight matrix size to analyze (0 = no limit).
|
||||||
"""
|
:param alphas: Compute the power laws (alpha) of the weight matrices.
|
||||||
names, modules = [], []
|
Time consuming so disabled by default (use lognorm if you want speed)
|
||||||
for name, module in model.named_modules():
|
:param lognorms: Compute the log norms of the weight matrices.
|
||||||
if isinstance(module, available_module_types()):
|
:param spectralnorms: Compute the spectral norm (max eigenvalue) of the weight matrices.
|
||||||
names.append(name)
|
:param softranks: Compute the soft norm (i.e. StableRank) of the weight matrices.
|
||||||
modules.append(module)
|
:param normalize: Normalize or not.
|
||||||
# print('There are {:} layers to be analyzed in this model.'.format(len(modules)))
|
:param glorot_fix:
|
||||||
all_results = OrderedDict()
|
:return: (a dict of all layers' results, a dict of the summarized info)
|
||||||
for index, module in enumerate(modules):
|
"""
|
||||||
if isinstance(module, nn.Linear):
|
names, modules = [], []
|
||||||
weights = [module.weight.cpu().detach().numpy()]
|
for name, module in model.named_modules():
|
||||||
else:
|
if isinstance(module, available_module_types()):
|
||||||
weights = get_conv2D_Wmats(module.weight.cpu().detach().numpy())
|
names.append(name)
|
||||||
results = analyze_weights(weights, min_size, max_size, alphas, lognorms, spectralnorms, softranks, normalize, glorot_fix)
|
modules.append(module)
|
||||||
results['id'] = index
|
# print('There are {:} layers to be analyzed in this model.'.format(len(modules)))
|
||||||
results['type'] = type(module)
|
all_results = OrderedDict()
|
||||||
all_results[index] = results
|
for index, module in enumerate(modules):
|
||||||
summary = compute_details(all_results)
|
if isinstance(module, nn.Linear):
|
||||||
return all_results, summary
|
weights = [module.weight.cpu().detach().numpy()]
|
||||||
|
else:
|
||||||
|
weights = get_conv2D_Wmats(module.weight.cpu().detach().numpy())
|
||||||
|
results = analyze_weights(
|
||||||
|
weights, min_size, max_size, alphas, lognorms, spectralnorms, softranks, normalize, glorot_fix
|
||||||
|
)
|
||||||
|
results["id"] = index
|
||||||
|
results["type"] = type(module)
|
||||||
|
all_results[index] = results
|
||||||
|
summary = compute_details(all_results)
|
||||||
|
return all_results, summary
|
||||||
|
Loading…
Reference in New Issue
Block a user