def perform_training(self, num_epochs=None, message=None): starttime = datetime.now().replace(microsecond=0) if num_epochs is None: num_epochs = self.ps.num_epochs self.logger( 'Started Training at %s for %d epochs' % (datetime.strftime(starttime, '%Y-%m-%d_%H:%M:%S'), num_epochs)) if message is not None: self.logger(expr_message) prev_lr = np.inf scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(self.optimizer, 'min', patience=7) early_stopping = EarlyStopping(patience=20) for epoch_num in range(1, num_epochs + 1): train_loss_dict = self.train() eval_loss_dict = self.evaluate() scheduler.step(eval_loss_dict['loss_total']) cur_lr = self.optimizer.param_groups[0]['lr'] if cur_lr != prev_lr: self.logger('--- Optimizer learning rate changed from %.2e to %.2e ---' % (prev_lr, cur_lr)) prev_lr = cur_lr self.epochs_completed += 1 with torch.no_grad(): eval_msg = CCPCM_Trainer.creat_loss_message(eval_loss_dict, expr_code=self.ps.expr_code, epoch_num=self.epochs_completed, it=len(self.ds_val), try_num=self.try_num, mode='evald') if eval_loss_dict['loss_total'] < self.best_loss_total: self.ps.best_model_fname = makepath( os.path.join(self.ps.work_dir, 'snapshots', 'TR%02d_E%03d.pt' % ( self.try_num, self.epochs_completed)), isfile=True) self.logger(eval_msg + ' ** ') self.best_loss_total = eval_loss_dict['loss_total'] torch.save(self.ccpcm_model.module.state_dict() if isinstance(self.ccpcm_model, torch.nn.DataParallel) else self.ccpcm_model.state_dict(), self.ps.best_model_fname) else: self.logger(eval_msg) self.swriter.add_scalars('total_loss/scalars', {'train_loss_total': train_loss_dict['loss_total'], 'evald_loss_total': eval_loss_dict['loss_total'], }, self.epochs_completed) if early_stopping(eval_loss_dict['loss_total']): self.logger("Early stopping") break endtime = datetime.now().replace(microsecond=0) self.logger(expr_message) self.logger('Finished Training at %s\n' % (datetime.strftime(endtime, '%Y-%m-%d_%H:%M:%S'))) self.logger( 'Training done in %s! Best val total loss achieved: %.2e\n' % (endtime - starttime, self.best_loss_total)) self.logger('Best model path: %s\n' % self.ps.best_model_fname)
def evaluate_error(dataset_dir, ccpcm_model, ccpcm_ps, splitname, batch_size=1): ccpcm_model.eval() n_class = 40 ds_name = dataset_dir.split('/')[-2] comp_device = torch.device( "cuda:0" if torch.cuda.is_available() else "cpu") ccpcm_model = ccpcm_model.to(comp_device) BCELoss = torch.nn.BCELoss() ds = CCPCM_DS(dataset_dir=os.path.join(dataset_dir, splitname)) print('%s dataset size: %s' % (splitname, len(ds))) ds = DataLoader( ds, batch_size=batch_size, shuffle=False, drop_last=False) #batchsize for bm is fixed so drop the last one all_auc = {} loss_mean = [] with torch.no_grad(): for dorig in ds: dorig = {k: dorig[k].to(comp_device) for k in dorig.keys()} drec = ccpcm_model(Ditp1=dorig['Ditp1'], Bit=dorig['Bit'], Binf=dorig['Binf']) loss_mean.append(BCELoss(drec['Bit_p1'], dorig['Bit_p1'])) y_test = c2c(dorig['Bit_p1']) y_score = c2c(drec['Bit_p1']) if splitname == 'test': y_test = np.int32(y_test > 0.5) # Compute ROC curve and ROC area for each class for j in range(n_class): fpr, tpr, _ = roc_curve(y_test[:, j], y_score[:, j]) auc = compute_auc(fpr, tpr) c = all_auc.get(j, []) c.append(auc.mean()) all_auc[j] = c.copy() valid_ids = {k: ~np.isnan(v) for k, v in all_auc.items()} final_results = { 'BCE': float(c2c(torch.stack(loss_mean).mean())), 'auc': {k: np.mean(np.stack(v)[valid_ids[k]]) for k, v in all_auc.items()}, } outpath = makepath(os.path.join( ccpcm_ps.work_dir, 'evaluations', 'ds_%s' % ds_name, os.path.basename(ccpcm_ps.best_model_fname).replace( '.pt', '_%s.json' % splitname)), isfile=True) with open(outpath, 'w') as f: json.dump(final_results, f) return final_results
# Implementation: Nima Ghorbani: nghorbani.github.io # # If you use this code please consider citing: # Cross-Category Product Choice: A Scalable Deep-Learning Model (Sebastian Gabel and Artem Timoshenko) # # # 2019.09.01 import pandas import numpy as np from ccpcm.tools.model_loader import load_ccpcm from ccpcm.tools.omni_tools import makepath from ccpcm.tools.omni_tools import copy2cpu as c2c import os import torch B_path = makepath('../data/dataset/train/B.npz', isfile=True) Bit = np.load(B_path)['data'] Binf = Bit.mean(1) # product purchasing frequencies window_size = 10 n_prod = 40 expr_code = '10' expr_dir = os.path.join('../experiments/', expr_code) ccpcm_model, ccpcm_ps = load_ccpcm(expr_dir) # Test will be applied on week 50th # Note: testset specifies discount on product 24 however, it has never been discounted before in the trainset prediction_example = pandas.read_csv( "../data/dataset/prediction_example.csv") # .sort_values(['i','j']) promotion_schedule = pandas.read_csv( "../data/dataset/promotion_schedule.csv") # .sort_values(['j'])
def __init__(self, work_dir, ps): from tensorboardX import SummaryWriter torch.manual_seed(ps.seed) starttime = datetime.now().replace(microsecond=0) ps.work_dir = makepath(work_dir, isfile=False) logger = log2file(makepath(os.path.join(work_dir, '%s.log' % (expr_code)), isfile=True)) summary_logdir = os.path.join(work_dir, 'summaries') self.swriter = SummaryWriter(log_dir=summary_logdir) logger('[%s] - Started training ccpcm experiment code %s' % (expr_code, starttime)) logger('tensorboard --logdir=%s' % summary_logdir) logger('Torch Version: %s\n' % torch.__version__) logger('Base dataset_dir is %s' % ps.dataset_dir) shutil.copy2(os.path.basename(sys.argv[0]), work_dir) use_cuda = torch.cuda.is_available() if use_cuda: torch.cuda.empty_cache() self.comp_device = torch.device("cuda:%d" % ps.cuda_id if torch.cuda.is_available() else "cpu") gpu_count = torch.cuda.device_count() logger('%d CUDAs available!' % gpu_count) gpu_brand = torch.cuda.get_device_name(ps.cuda_id) if use_cuda else None logger('Training with %s [%s]' % (self.comp_device, gpu_brand) if use_cuda else 'Training on CPU!!!') logger('Base dataset_dir is %s' % ps.dataset_dir) kwargs = {'num_workers': ps.n_workers} # kwargs = {'num_workers': ps.n_workers, 'pin_memory': True} if use_cuda else {'num_workers': ps.n_workers} ds_train = CCPCM_DS(dataset_dir=os.path.join(ps.dataset_dir, 'train')) self.ds_train = DataLoader(ds_train, batch_size=ps.batch_size, shuffle=True, drop_last=True, **kwargs) ds_val = CCPCM_DS(dataset_dir=os.path.join(ps.dataset_dir, 'vald')) self.ds_val = DataLoader(ds_val, batch_size=ps.batch_size, shuffle=True, drop_last=True, **kwargs) ds_test = CCPCM_DS(dataset_dir=os.path.join(ps.dataset_dir, 'test')) self.ds_test = DataLoader(ds_test, batch_size=ps.batch_size, shuffle=True, drop_last=False) logger('Dataset Train, Vald, Test size respectively: %.2f M, %.2f K, %.2f' % (len(self.ds_train.dataset) * 1e-6, len(self.ds_val.dataset) * 1e-3, len(self.ds_test.dataset))) self.ccpcm_model = CCPCM(window_size=ps.window_size, n_class=ps.n_class).to(self.comp_device) if ps.use_multigpu: self.ccpcm_model = nn.DataParallel(self.ccpcm_model) logger("Training on Multiple GPU's") varlist = [var[1] for var in self.ccpcm_model.named_parameters()] params_count = sum(p.numel() for p in varlist if p.requires_grad) logger('Total Trainable Parameters Count is %2.2f M.' % ((params_count) * 1e-6)) self.optimizer = optim.Adam(varlist, lr=ps.base_lr, weight_decay=ps.reg_coef) self.logger = logger self.best_loss_total = np.inf self.try_num = ps.try_num self.epochs_completed = 0 self.ps = ps if ps.best_model_fname is not None: self._get_model().load_state_dict(torch.load(ps.best_model_fname, map_location=self.comp_device), strict=False) logger('Restored model from %s' % ps.best_model_fname) self.BCELoss = nn.BCELoss()
import os import torch data = pandas.read_csv(f"dataset/train.csv").sort_values(['i', 't']) customers = data['i'].to_numpy(dtype=np.int32) times = data['t'].to_numpy(dtype=np.int32) prods = data['j'].to_numpy(dtype=np.int32) prices = data['price'].to_numpy(dtype=np.float32) advertised = data['advertised'].to_numpy(dtype=np.bool) n_cust = data.i.max() + 1 n_prod = data.j.max() + 1 T = data.t.max() + 1 B_path = makepath('dataset/train/B.npz', isfile=True) if not os.path.exists(B_path): Bit = np.zeros( [n_cust, T, n_prod], dtype=np.bool ) # customer purchasing history: customer i purchased product j at time t for i in tqdm(range(n_cust)): for t in range(T): for j in range(n_prod): Bit[i, t, j] = np.sum(prods[(customers == i) & (times == t)] == j) > 0 np.savez(B_path, data=Bit) else: Bit = np.load(B_path)['data']