def trial_ids_from_dirs(*paths): dir_trial_ids = tuple( set(utils.getUniqueIds(path, prefix='trial=', to_array=True)) for path in paths) trial_ids = np.array(list(sorted(set.intersection(*dir_trial_ids)))) for dir_name, t_ids in zip(paths, dir_trial_ids): logger.info(f"{len(t_ids)} trial ids from {dir_name}:") logger.info(f" {t_ids}") logger.info(f"{len(trial_ids)} trials in intersection: {trial_ids}") return trial_ids
def main(out_dir=None, data_dir=None, feature_fn_format='feature-seq.pkl', label_fn_format='label_seq.pkl', cv_params={}): out_dir = os.path.expanduser(out_dir) data_dir = os.path.expanduser(data_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) # Load data trial_ids = utils.getUniqueIds(data_dir, prefix='trial=', suffix=feature_fn_format, to_array=True) dataset = utils.CvDataset(trial_ids, data_dir, feature_fn_format=feature_fn_format, label_fn_format=label_fn_format, vocab=[]) utils.saveMetadata(dataset.metadata, out_data_dir) # Make folds dataset_size = len(trial_ids) cv_folds = utils.makeDataSplits(dataset_size, metadata=dataset.metadata, **cv_params) save_cv_folds(cv_folds, os.path.join(out_data_dir, 'cv-folds.json')) # Check folds for cv_index, cv_fold in enumerate(cv_folds): train_data, val_data, test_data = dataset.getFold(cv_fold) train_feats, train_labels, train_ids = train_data test_feats, test_labels, test_ids = test_data val_feats, val_labels, val_ids = val_data logger.info( f'CV fold {cv_index + 1} / {len(cv_folds)}: {len(trial_ids)} total ' f'({len(train_ids)} train, {len(val_ids)} val, {len(test_ids)} test)' )
def __init__(self, data_dirs, scores_dirs, prefix='seq='): self.prefix = prefix self.data_dirs = data_dirs self.scores_dirs = scores_dirs self.seq_ids = utils.getUniqueIds(self.data_dirs['event'], prefix=prefix, suffix='labels.*', to_array=True) self.vocabs = { name: utils.loadVariable('vocab', dir_) for name, dir_ in self.data_dirs.items() }
def main(out_dir=None, data_dir=None): data_dir = os.path.expanduser(data_dir) out_dir = os.path.expanduser(out_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def saveVariable(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f'{var_name}.pkl')) def loadVariable(var_name): fn = os.path.join(data_dir, f'{var_name}.pkl') return joblib.load(fn) # Load data trial_ids = utils.getUniqueIds(data_dir, prefix='trial-') cv_folds = loadVariable(f'cv-folds') assembly_seqs = tuple( loadVariable(f"trial-{seq_id}_true-state-seq-orig") for seq_id in trial_ids ) unique_assemblies = [] label_seqs = tuple( np.array(list(labels.gen_eq_classes(seq, unique_assemblies, equivalent=None))) for seq in assembly_seqs ) saveVariable(unique_assemblies, f'unique-assemblies') for cv_index, (train_idxs, test_idxs) in enumerate(cv_folds): logger.info( f'CV fold {cv_index + 1}: {len(trial_ids)} total ' f'({len(train_idxs)} train, {len(test_idxs)} test)' ) # train_ids = trial_ids[np.array(train_idxs)] train_label_seqs = tuple(label_seqs[i] for i in train_idxs) unique_train_labels = np.unique(np.hstack(train_label_seqs)) unique_train_assemblies = tuple(unique_assemblies[i] for i in unique_train_labels) signatures = make_signatures(unique_train_assemblies) test_ids = trial_ids[np.array(test_idxs)] for trial_id in test_ids: saveVariable(signatures, f'trial={trial_id}_signatures') saveVariable(unique_train_labels, f'trial={trial_id}_unique-train-labels')
def main(out_dir=None, data_dir=None): data_dir = os.path.expanduser(data_dir) out_dir = os.path.expanduser(out_dir) if not os.path.exists(data_dir): raise AssertionError(f"{data_dir} does not exist") logger.info(f"Reading from: {data_dir}") logger.info(f"Writing to: {out_dir}") fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def loadFromDataDir(var_name): return joblib.load(os.path.join(data_dir, f'{var_name}.pkl')) def saveVariable(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f'{var_name}.pkl')) trial_ids = utils.getUniqueIds(data_dir) if not trial_ids: logger.warning(f"No recognizable data in {data_dir}") for i, trial_id in enumerate(trial_ids): trial_str = f"trial-{trial_id}" logger.info( f"Processing video {i + 1} / {len(trial_ids)} (trial {trial_id})") action_seq = loadFromDataDir(f"{trial_str}_action-seq") rgb_frame_seq = loadFromDataDir(f"{trial_str}_rgb-frame-seq") num_frames = len(rgb_frame_seq) # TODO: MAKE LABELS HERE label_seq = makeBasketLabels(action_seq, num_frames) saveVariable(label_seq, f'{trial_str}_label-seq')
def __init__(self, actions_dir, parts_dir, events_dir, edges_dir, vocab=None, modalities=('actions', 'parts', 'edges'), labels='edges', prefix='seq=', feature_fn_format='score-seq', label_fn_format='true-label-seq'): self.modalities = modalities self.labels = labels self.actions_dir = actions_dir self.parts_dir = parts_dir self.events_dir = events_dir self.edges_dir = edges_dir self.data_dirs = { 'actions': self.actions_dir, 'parts': self.parts_dir, 'events': self.events_dir, 'edges': self.edges_dir } labels_dir = self.data_dirs[self.labels] self.trial_ids = utils.getUniqueIds( labels_dir, prefix='trial=' if self.labels == 'edges' else prefix, suffix=f'{label_fn_format}.*', to_array=True) if vocab is None: vocab = utils.loadVariable('vocab', labels_dir) self.vocab = vocab self.metadata = utils.loadMetadata(labels_dir, rows=self.trial_ids) self.prefix = prefix self.feature_fn_format = feature_fn_format self.label_fn_format = label_fn_format
def loadMasks(masks_dir=None, trial_ids=None, num_per_video=10): if masks_dir is None: return None def loadMasks(video_id): masks = joblib.load( os.path.join(masks_dir, f'trial={video_id}_person-mask-seq.pkl')) any_detections = masks.any(axis=-1).any(axis=-1) masks = masks[any_detections] masks = utils.sampleWithoutReplacement(masks, num_samples=num_per_video) return masks masks_dir = os.path.expanduser(masks_dir) if trial_ids is None: trial_ids = utils.getUniqueIds(masks_dir, prefix='trial=', to_array=True) masks = np.vstack(tuple(map(loadMasks, trial_ids))) return masks
def main( out_dir=None, preds_dir=None, data_dir=None, metric_names=None, plot_output=None, results_file=None, sweep_param_name=None): if metric_names is None: metric_names = ('accuracy', 'edit_score', 'overlap_score') preds_dir = os.path.expanduser(preds_dir) out_dir = os.path.expanduser(out_dir) if not os.path.exists(out_dir): os.makedirs(out_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) logger.info(f"Writing to: {out_dir}") if results_file is None: results_file = os.path.join(out_dir, 'results.csv') if os.path.exists(results_file): os.remove(results_file) else: results_file = os.path.expanduser(results_file) def saveVariable(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f'{var_name}.pkl')) def loadAll(seq_ids, var_name, data_dir): def loadOne(seq_id): fn = os.path.join(data_dir, f'trial={seq_id}_{var_name}') return joblib.load(fn) return tuple(map(loadOne, seq_ids)) assembly_vocab = joblib.load(os.path.join(data_dir, 'assembly-vocab.pkl')) trial_ids = utils.getUniqueIds(preds_dir, prefix='trial=') pred_seqs = loadAll(trial_ids, 'pred-label-seq.pkl', preds_dir) true_seqs = loadAll(trial_ids, 'true-label-seq.pkl', preds_dir) input_seqs = loadAll(trial_ids, 'score-seq.pkl', preds_dir) action_vocab = [] action_batch = [] assembly_batch = [] for i, trial_id in enumerate(trial_ids): logger.info(f"VIDEO {trial_id}:") pred_assembly_index_seq = pred_seqs[i] true_assembly_index_seq = true_seqs[i] pred_action_segs, pred_action_index_seq = actionsFromAssemblies( pred_assembly_index_seq, assembly_vocab, action_vocab ) lib_assembly.writeAssemblies( os.path.join(out_data_dir, f'trial={trial_id}_pred-actions.txt'), pred_action_segs ) true_action_segs, true_action_index_seq = actionsFromAssemblies( true_assembly_index_seq, assembly_vocab, action_vocab ) lib_assembly.writeAssemblies( os.path.join(out_data_dir, f'trial={trial_id}_true-actions.txt'), true_action_segs ) metric_dict = {} for name in metric_names: key = f"{name}_action" value = getattr(LCTM.metrics, name)(pred_action_index_seq, true_action_index_seq) / 100 metric_dict[key] = value logger.info(f" {key}: {value * 100:.1f}%") key = f"{name}_assembly" value = getattr(LCTM.metrics, name)( pred_assembly_index_seq, true_assembly_index_seq ) / 100 metric_dict[key] = value logger.info(f" {key}: {value * 100:.1f}%") utils.writeResults(results_file, metric_dict, sweep_param_name, {}) input_seq = input_seqs[i] action_batch.append((pred_action_index_seq, input_seq, true_action_index_seq, trial_id)) assembly_batch.append( (pred_assembly_index_seq, input_seq, true_assembly_index_seq, trial_id) ) lib_assembly.writeAssemblies(os.path.join(out_data_dir, 'action-vocab.txt'), action_vocab) label_names = ('true', 'pred') for preds, inputs, gt_labels, seq_id in action_batch: fn = os.path.join(fig_dir, f"trial={seq_id}_model-io_action.png") utils.plot_array(None, (gt_labels, preds), label_names, fn=fn, labels_together=True) label_names = ('true', 'pred') for preds, inputs, gt_labels, seq_id in assembly_batch: fn = os.path.join(fig_dir, f"trial={seq_id}_model-io_assembly.png") utils.plot_array(None, (gt_labels, preds), label_names, fn=fn, labels_together=True)
def main(out_dir=None, data_dir=None, model_name=None, pretrained_model_dir=None, gpu_dev_id=None, batch_size=None, learning_rate=None, independent_signals=None, active_only=None, model_params={}, cv_params={}, train_params={}, viz_params={}, plot_predictions=None, results_file=None, sweep_param_name=None, label_mapping=None, eval_label_mapping=None): data_dir = os.path.expanduser(data_dir) out_dir = os.path.expanduser(out_dir) if not os.path.exists(out_dir): os.makedirs(out_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) if results_file is None: results_file = os.path.join(out_dir, 'results.csv') else: results_file = os.path.expanduser(results_file) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def saveVariable(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f'{var_name}.pkl')) def loadAll(seq_ids, var_name, data_dir): def loadOne(seq_id): fn = os.path.join(data_dir, f'trial={seq_id}_{var_name}') return joblib.load(fn) return tuple(map(loadOne, seq_ids)) # Load data trial_ids = utils.getUniqueIds(data_dir, prefix='trial=', to_array=True) feature_seqs = loadAll(trial_ids, 'feature-seq.pkl', data_dir) label_seqs = loadAll(trial_ids, 'label-seq.pkl', data_dir) device = torchutils.selectDevice(gpu_dev_id) if label_mapping is not None: def map_labels(labels): for i, j in label_mapping.items(): labels[labels == i] = j return labels label_seqs = tuple(map(map_labels, label_seqs)) # Define cross-validation folds dataset_size = len(trial_ids) cv_folds = utils.makeDataSplits(dataset_size, **cv_params) def getSplit(split_idxs): split_data = tuple( tuple(s[i] for i in split_idxs) for s in (feature_seqs, label_seqs, trial_ids)) return split_data for cv_index, cv_splits in enumerate(cv_folds): if pretrained_model_dir is not None: def loadFromPretrain(fn): return joblib.load( os.path.join(pretrained_model_dir, f"{fn}.pkl")) model = loadFromPretrain(f'cvfold={cv_index}_{model_name}-best') train_ids = loadFromPretrain(f'cvfold={cv_index}_train-ids') val_ids = loadFromPretrain(f'cvfold={cv_index}_val-ids') test_ids = tuple(i for i in trial_ids if i not in (train_ids + val_ids)) test_idxs = tuple(trial_ids.tolist().index(i) for i in test_ids) test_data = getSplit(test_idxs) if independent_signals: criterion = torch.nn.CrossEntropyLoss() labels_dtype = torch.long test_data = splitSeqs(*test_data, active_only=False) else: # FIXME # criterion = torch.nn.BCEWithLogitsLoss() # labels_dtype = torch.float criterion = torch.nn.CrossEntropyLoss() labels_dtype = torch.long test_feats, test_labels, test_ids = test_data test_set = torchutils.SequenceDataset(test_feats, test_labels, device=device, labels_dtype=labels_dtype, seq_ids=test_ids, transpose_data=True) test_loader = torch.utils.data.DataLoader(test_set, batch_size=batch_size, shuffle=False) # Test model metric_dict = { 'Avg Loss': metrics.AverageLoss(), 'Accuracy': metrics.Accuracy(), 'Precision': metrics.Precision(), 'Recall': metrics.Recall(), 'F1': metrics.Fmeasure() } test_io_history = torchutils.predictSamples( model.to(device=device), test_loader, criterion=criterion, device=device, metrics=metric_dict, data_labeled=True, update_model=False, seq_as_batch=train_params['seq_as_batch'], return_io_history=True) if independent_signals: test_io_history = tuple(joinSeqs(test_io_history)) metric_str = ' '.join(str(m) for m in metric_dict.values()) logger.info('[TST] ' + metric_str) d = {k: v.value for k, v in metric_dict.items()} utils.writeResults(results_file, d, sweep_param_name, model_params) if plot_predictions: imu.plot_prediction_eg(test_io_history, fig_dir, **viz_params) def saveTrialData(pred_seq, score_seq, feat_seq, label_seq, trial_id): if label_mapping is not None: def dup_score_cols(scores): num_cols = scores.shape[-1] + len(label_mapping) col_idxs = torch.arange(num_cols) for i, j in label_mapping.items(): col_idxs[i] = j return scores[..., col_idxs] score_seq = dup_score_cols(score_seq) saveVariable(pred_seq.cpu().numpy(), f'trial={trial_id}_pred-label-seq') saveVariable(score_seq.cpu().numpy(), f'trial={trial_id}_score-seq') saveVariable(label_seq.cpu().numpy(), f'trial={trial_id}_true-label-seq') for io in test_io_history: saveTrialData(*io) continue train_data, val_data, test_data = tuple(map(getSplit, cv_splits)) if independent_signals: criterion = torch.nn.CrossEntropyLoss() labels_dtype = torch.long split_ = functools.partial(splitSeqs, active_only=active_only) train_data = split_(*train_data) val_data = split_(*val_data) test_data = splitSeqs(*test_data, active_only=False) else: # FIXME # criterion = torch.nn.BCEWithLogitsLoss() # labels_dtype = torch.float criterion = torch.nn.CrossEntropyLoss() labels_dtype = torch.long train_feats, train_labels, train_ids = train_data train_set = torchutils.SequenceDataset(train_feats, train_labels, device=device, labels_dtype=labels_dtype, seq_ids=train_ids, transpose_data=True) train_loader = torch.utils.data.DataLoader(train_set, batch_size=batch_size, shuffle=True) test_feats, test_labels, test_ids = test_data test_set = torchutils.SequenceDataset(test_feats, test_labels, device=device, labels_dtype=labels_dtype, seq_ids=test_ids, transpose_data=True) test_loader = torch.utils.data.DataLoader(test_set, batch_size=batch_size, shuffle=False) val_feats, val_labels, val_ids = val_data val_set = torchutils.SequenceDataset(val_feats, val_labels, device=device, labels_dtype=labels_dtype, seq_ids=val_ids, transpose_data=True) val_loader = torch.utils.data.DataLoader(val_set, batch_size=batch_size, shuffle=True) logger.info( f'CV fold {cv_index + 1} / {len(cv_folds)}: {len(trial_ids)} total ' f'({len(train_ids)} train, {len(val_ids)} val, {len(test_ids)} test)' ) input_dim = train_set.num_obsv_dims output_dim = train_set.num_label_types if model_name == 'linear': model = torchutils.LinearClassifier( input_dim, output_dim, **model_params).to(device=device) elif model_name == 'conv': model = ConvClassifier(input_dim, output_dim, **model_params).to(device=device) elif model_name == 'TCN': model = TcnClassifier(input_dim, output_dim, **model_params) else: raise AssertionError() train_epoch_log = collections.defaultdict(list) val_epoch_log = collections.defaultdict(list) metric_dict = { 'Avg Loss': metrics.AverageLoss(), 'Accuracy': metrics.Accuracy(), 'Precision': metrics.Precision(), 'Recall': metrics.Recall(), 'F1': metrics.Fmeasure() } optimizer_ft = torch.optim.Adam(model.parameters(), lr=learning_rate, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False) lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer_ft, step_size=1, gamma=1.00) model, last_model_wts = torchutils.trainModel( model, criterion, optimizer_ft, lr_scheduler, train_loader, val_loader, device=device, metrics=metric_dict, train_epoch_log=train_epoch_log, val_epoch_log=val_epoch_log, **train_params) # Test model metric_dict = { 'Avg Loss': metrics.AverageLoss(), 'Accuracy': metrics.Accuracy(), 'Precision': metrics.Precision(), 'Recall': metrics.Recall(), 'F1': metrics.Fmeasure() } test_io_history = torchutils.predictSamples( model.to(device=device), test_loader, criterion=criterion, device=device, metrics=metric_dict, data_labeled=True, update_model=False, seq_as_batch=train_params['seq_as_batch'], return_io_history=True) if independent_signals: test_io_history = tuple(joinSeqs(test_io_history)) metric_str = ' '.join(str(m) for m in metric_dict.values()) logger.info('[TST] ' + metric_str) d = {k: v.value for k, v in metric_dict.items()} utils.writeResults(results_file, d, sweep_param_name, model_params) if plot_predictions: # imu.plot_prediction_eg(test_io_history, fig_dir, fig_type=fig_type, **viz_params) imu.plot_prediction_eg(test_io_history, fig_dir, **viz_params) def saveTrialData(pred_seq, score_seq, feat_seq, label_seq, trial_id): if label_mapping is not None: def dup_score_cols(scores): num_cols = scores.shape[-1] + len(label_mapping) col_idxs = torch.arange(num_cols) for i, j in label_mapping.items(): col_idxs[i] = j return scores[..., col_idxs] score_seq = dup_score_cols(score_seq) saveVariable(pred_seq.cpu().numpy(), f'trial={trial_id}_pred-label-seq') saveVariable(score_seq.cpu().numpy(), f'trial={trial_id}_score-seq') saveVariable(label_seq.cpu().numpy(), f'trial={trial_id}_true-label-seq') for io in test_io_history: saveTrialData(*io) saveVariable(train_ids, f'cvfold={cv_index}_train-ids') saveVariable(test_ids, f'cvfold={cv_index}_test-ids') saveVariable(val_ids, f'cvfold={cv_index}_val-ids') saveVariable(train_epoch_log, f'cvfold={cv_index}_{model_name}-train-epoch-log') saveVariable(val_epoch_log, f'cvfold={cv_index}_{model_name}-val-epoch-log') saveVariable(metric_dict, f'cvfold={cv_index}_{model_name}-metric-dict') saveVariable(model, f'cvfold={cv_index}_{model_name}-best') model.load_state_dict(last_model_wts) saveVariable(model, f'cvfold={cv_index}_{model_name}-last') torchutils.plotEpochLog(train_epoch_log, subfig_size=(10, 2.5), title='Training performance', fn=os.path.join( fig_dir, f'cvfold={cv_index}_train-plot.png')) if val_epoch_log: torchutils.plotEpochLog(val_epoch_log, subfig_size=(10, 2.5), title='Heldout performance', fn=os.path.join( fig_dir, f'cvfold={cv_index}_val-plot.png')) if eval_label_mapping is not None: metric_dict = { 'Avg Loss': metrics.AverageLoss(), 'Accuracy': metrics.Accuracy(), 'Precision': metrics.Precision(), 'Recall': metrics.Recall(), 'F1': metrics.Fmeasure() } test_io_history = torchutils.predictSamples( model.to(device=device), test_loader, criterion=criterion, device=device, metrics=metric_dict, data_labeled=True, update_model=False, seq_as_batch=train_params['seq_as_batch'], return_io_history=True, label_mapping=eval_label_mapping) if independent_signals: test_io_history = joinSeqs(test_io_history) metric_str = ' '.join(str(m) for m in metric_dict.values()) logger.info('[TST] ' + metric_str)
def main(out_dir=None, data_dir=None, attr_dir=None, model_name=None, gpu_dev_id=None, batch_size=None, learning_rate=None, model_params={}, cv_params={}, train_params={}, viz_params={}, plot_predictions=None, results_file=None, sweep_param_name=None): data_dir = os.path.expanduser(data_dir) out_dir = os.path.expanduser(out_dir) attr_dir = os.path.expanduser(attr_dir) if not os.path.exists(out_dir): os.makedirs(out_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) if results_file is None: results_file = os.path.join(out_dir, f'results.csv') else: results_file = os.path.expanduser(results_file) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def loadData(seq_id): var_name = f"trial-{seq_id}_rgb-frame-seq" data = joblib.load(os.path.join(data_dir, f'{var_name}.pkl')) return data.swapaxes(1, 3) def loadLabels(seq_id): var_name = f"trial-{seq_id}_label-seq" return joblib.load(os.path.join(attr_dir, f'{var_name}.pkl')) def saveVariable(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f'{var_name}.pkl')) # Load data trial_ids = utils.getUniqueIds(data_dir) label_seqs = tuple(map(loadLabels, trial_ids)) device = torchutils.selectDevice(gpu_dev_id) # Define cross-validation folds dataset_size = len(trial_ids) cv_folds = utils.makeDataSplits(dataset_size, **cv_params) def getSplit(split_idxs): split_data = tuple( tuple(s[i] for i in split_idxs) for s in (label_seqs, trial_ids)) return split_data for cv_index, cv_splits in enumerate(cv_folds): train_data, val_data, test_data = tuple(map(getSplit, cv_splits)) criterion = torch.nn.BCEWithLogitsLoss() labels_dtype = torch.float train_labels, train_ids = train_data train_set = torchutils.PickledVideoDataset(loadData, train_labels, device=device, labels_dtype=labels_dtype, seq_ids=train_ids, batch_size=batch_size) train_loader = torch.utils.data.DataLoader(train_set, batch_size=1, shuffle=True) test_labels, test_ids = test_data test_set = torchutils.PickledVideoDataset(loadData, test_labels, device=device, labels_dtype=labels_dtype, seq_ids=test_ids, batch_size=batch_size) test_loader = torch.utils.data.DataLoader(test_set, batch_size=1, shuffle=False) val_labels, val_ids = val_data val_set = torchutils.PickledVideoDataset(loadData, val_labels, device=device, labels_dtype=labels_dtype, seq_ids=val_ids, batch_size=batch_size) val_loader = torch.utils.data.DataLoader(val_set, batch_size=1, shuffle=True) logger.info( f'CV fold {cv_index + 1} / {len(cv_folds)}: {len(trial_ids)} total ' f'({len(train_ids)} train, {len(val_ids)} val, {len(test_ids)} test)' ) if model_name == 'resnet': # input_dim = train_set.num_obsv_dims output_dim = train_set.num_label_types model = ImageClassifier(output_dim, **model_params).to(device=device) else: raise AssertionError() train_epoch_log = collections.defaultdict(list) val_epoch_log = collections.defaultdict(list) metric_dict = { 'Avg Loss': metrics.AverageLoss(), 'Accuracy': metrics.Accuracy(), 'Precision': metrics.Precision(), 'Recall': metrics.Recall(), 'F1': metrics.Fmeasure() } optimizer_ft = torch.optim.Adam(model.parameters(), lr=learning_rate, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False) lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer_ft, step_size=1, gamma=1.00) model, last_model_wts = torchutils.trainModel( model, criterion, optimizer_ft, lr_scheduler, train_loader, val_loader, device=device, metrics=metric_dict, train_epoch_log=train_epoch_log, val_epoch_log=val_epoch_log, **train_params) # Test model metric_dict = { 'Avg Loss': metrics.AverageLoss(), 'Accuracy': metrics.Accuracy(), 'Precision': metrics.Precision(), 'Recall': metrics.Recall(), 'F1': metrics.Fmeasure() } test_io_history = torchutils.predictSamples( model.to(device=device), test_loader, criterion=criterion, device=device, metrics=metric_dict, data_labeled=True, update_model=False, seq_as_batch=train_params['seq_as_batch'], return_io_history=True) metric_str = ' '.join(str(m) for m in metric_dict.values()) logger.info('[TST] ' + metric_str) utils.writeResults(results_file, metric_dict, sweep_param_name, model_params) if plot_predictions: # imu.plot_prediction_eg(test_io_history, fig_dir, fig_type=fig_type, **viz_params) imu.plot_prediction_eg(test_io_history, fig_dir, **viz_params) def saveTrialData(pred_seq, score_seq, feat_seq, label_seq, trial_id): saveVariable(pred_seq.cpu().numpy(), f'trial={trial_id}_pred-label-seq') saveVariable(score_seq.cpu().numpy(), f'trial={trial_id}_score-seq') saveVariable(label_seq.cpu().numpy(), f'trial={trial_id}_true-label-seq') for io in test_io_history: saveTrialData(*io) saveVariable(train_ids, f'cvfold={cv_index}_train-ids') saveVariable(test_ids, f'cvfold={cv_index}_test-ids') saveVariable(val_ids, f'cvfold={cv_index}_val-ids') saveVariable(train_epoch_log, f'cvfold={cv_index}_{model_name}-train-epoch-log') saveVariable(val_epoch_log, f'cvfold={cv_index}_{model_name}-val-epoch-log') saveVariable(metric_dict, f'cvfold={cv_index}_{model_name}-metric-dict') saveVariable(model, f'cvfold={cv_index}_{model_name}-best') model.load_state_dict(last_model_wts) saveVariable(model, f'cvfold={cv_index}_{model_name}-last') torchutils.plotEpochLog(train_epoch_log, subfig_size=(10, 2.5), title='Training performance', fn=os.path.join( fig_dir, f'cvfold={cv_index}_train-plot.png')) if val_epoch_log: torchutils.plotEpochLog(val_epoch_log, subfig_size=(10, 2.5), title='Heldout performance', fn=os.path.join( fig_dir, f'cvfold={cv_index}_val-plot.png'))
def main( out_dir=None, data_dir=None, model_name=None, model_params={}, cv_params={}, train_params={}, viz_params={}, plot_predictions=None, results_file=None, sweep_param_name=None): data_dir = os.path.expanduser(data_dir) out_dir = os.path.expanduser(out_dir) if not os.path.exists(out_dir): os.makedirs(out_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) if results_file is None: results_file = os.path.join(out_dir, f'results.csv') write_mode = 'w' else: results_file = os.path.expanduser(results_file) write_mode = 'a' fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def saveVariable(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f'{var_name}.pkl')) def loadAll(seq_ids, var_name, data_dir): def loadOne(seq_id): fn = os.path.join(data_dir, f'trial={seq_id}_{var_name}') return joblib.load(fn) return tuple(map(loadOne, seq_ids)) # Load data trial_ids = utils.getUniqueIds(data_dir, prefix='trial=') feature_seqs = loadAll(trial_ids, 'feature-seq.pkl', data_dir) label_seqs = loadAll(trial_ids, 'label-seq.pkl', data_dir) # Define cross-validation folds dataset_size = len(trial_ids) cv_folds = utils.makeDataSplits(dataset_size, **cv_params) def getSplit(split_idxs): split_data = tuple( tuple(s[i] for i in split_idxs) for s in (feature_seqs, label_seqs, trial_ids) ) return split_data for cv_index, cv_splits in enumerate(cv_folds): train_data, val_data, test_data = tuple(map(getSplit, cv_splits)) train_feats, train_labels, train_ids = train_data test_feats, test_labels, test_ids = test_data val_feats, val_labels, val_ids = val_data logger.info( f'CV fold {cv_index + 1} / {len(cv_folds)}: {len(trial_ids)} total ' f'({len(train_ids)} train, {len(val_ids)} val, {len(test_ids)} test)' ) input_dim = train_set.num_obsv_dims output_dim = train_set.num_label_types elif model_name == 'dummy': # FIXME model = None else: raise AssertionError() metric_str = ' '.join(str(m) for m in metric_dict.values()) logger.info('[TST] ' + metric_str) d = {k: v.value for k, v in metric_dict.items()} utils.writeResults( results_file, d, sweep_param_name, model_params, write_mode=write_mode ) if plot_predictions: io_fig_dir = os.path.join(fig_dir, 'model-io') if not os.path.exists(io_fig_dir): os.makedirs(io_fig_dir) label_names = ('gt', 'pred') preds, scores, inputs, gt_labels, ids = zip(*test_io_history) for batch in test_io_history: batch = map(lambda x: x.cpu().numpy(), batch) for preds, _, inputs, gt_labels, seq_id in zip(*batch): fn = os.path.join(io_fig_dir, f"trial={seq_id}_model-io.png") utils.plot_array(inputs, (gt_labels, preds), label_names, fn=fn) def saveTrialData(pred_seq, score_seq, feat_seq, label_seq, trial_id): saveVariable(pred_seq, f'trial={trial_id}_pred-label-seq') saveVariable(score_seq, f'trial={trial_id}_score-seq') saveVariable(label_seq, f'trial={trial_id}_true-label-seq') for batch in test_io_history: batch = map(lambda x: x.cpu().numpy(), batch) for io in zip(*batch): saveTrialData(*io) saveVariable(train_ids, f'cvfold={cv_index}_train-ids') saveVariable(test_ids, f'cvfold={cv_index}_test-ids') saveVariable(val_ids, f'cvfold={cv_index}_val-ids') saveVariable(train_epoch_log, f'cvfold={cv_index}_{model_name}-train-epoch-log') saveVariable(val_epoch_log, f'cvfold={cv_index}_{model_name}-val-epoch-log') train_fig_dir = os.path.join(fig_dir, 'train-plots') if not os.path.exists(train_fig_dir): os.makedirs(train_fig_dir)
def main(out_dir=None, data_dir=None, person_masks_dir=None, bg_masks_dir=None, sat_thresh=1, start_from=None, stop_at=None, num_disp_imgs=None): out_dir = os.path.expanduser(out_dir) data_dir = os.path.expanduser(data_dir) person_masks_dir = os.path.expanduser(person_masks_dir) bg_masks_dir = os.path.expanduser(bg_masks_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def loadFromDir(var_name, dir_name): return joblib.load(os.path.join(dir_name, f"{var_name}.pkl")) def saveToWorkingDir(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f"{var_name}.pkl")) trial_ids = utils.getUniqueIds(data_dir, prefix='trial=', to_array=True) for seq_idx, trial_id in enumerate(trial_ids): if start_from is not None and seq_idx < start_from: continue if stop_at is not None and seq_idx > stop_at: break trial_str = f"trial={trial_id}" logger.info( f"Processing video {seq_idx + 1} / {len(trial_ids)} (trial {trial_id})" ) logger.info(" Loading data...") rgb_frame_seq = loadFromDir(f'{trial_str}_rgb-frame-seq', data_dir) person_mask_seq = loadFromDir(f'{trial_str}_person-mask-seq', person_masks_dir) bg_mask_seq_depth = loadFromDir(f'{trial_str}_bg-mask-seq-depth', bg_masks_dir) # bg_mask_seq_rgb = loadFromDir(f'{trial_str}_bg-mask-seq-rgb', bg_masks_dir) logger.info(" Making segment labels...") fg_mask_seq = ~bg_mask_seq_depth seg_labels_seq = np.stack(tuple( map(makeCoarseSegmentLabels, fg_mask_seq)), axis=0) hsv_frame_seq = np.stack(tuple(map(makeHsvFrame, rgb_frame_seq)), axis=0) sat_frame_seq = hsv_frame_seq[..., 1] bg_mask_seq_sat = sat_frame_seq < sat_thresh seg_labels_seq[person_mask_seq] = 0 seg_labels_seq = np.stack(tuple( makeFineSegmentLabels(segs, sat) for segs, sat in zip(seg_labels_seq, bg_mask_seq_sat)), axis=0) logger.info(" Saving output...") saveToWorkingDir(seg_labels_seq.astype(np.uint8), f'{trial_str}_seg-labels-seq') plotHsvHist(hsv_frame_seq, seg_labels_seq, file_path=os.path.join(fig_dir, f'{trial_str}_hsv-hists.png')) if num_disp_imgs is not None: if rgb_frame_seq.shape[0] > num_disp_imgs: idxs = np.arange(rgb_frame_seq.shape[0]) np.random.shuffle(idxs) idxs = idxs[:num_disp_imgs] else: idxs = slice(None, None, None) imageprocessing.displayImages(*(rgb_frame_seq[idxs]), *(bg_mask_seq_sat[idxs]), *(bg_mask_seq_depth[idxs]), *(person_mask_seq[idxs]), *(seg_labels_seq[idxs]), num_rows=5, file_path=os.path.join( fig_dir, f'{trial_str}_best-frames.png'))
def main(out_dir=None, data_dir=None, cv_data_dir=None, score_dirs=[], fusion_method='sum', prune_imu=None, standardize=None, decode=None, plot_predictions=None, results_file=None, sweep_param_name=None, gpu_dev_id=None, model_params={}, cv_params={}, train_params={}, viz_params={}): data_dir = os.path.expanduser(data_dir) out_dir = os.path.expanduser(out_dir) score_dirs = tuple(map(os.path.expanduser, score_dirs)) if cv_data_dir is not None: cv_data_dir = os.path.expanduser(cv_data_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) if results_file is None: results_file = os.path.join(out_dir, 'results.csv') else: results_file = os.path.expanduser(results_file) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def saveVariable(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f'{var_name}.pkl')) def loadAll(seq_ids, var_name, data_dir): def loadOne(seq_id): fn = os.path.join(data_dir, f'trial={seq_id}_{var_name}') return joblib.load(fn) return tuple(map(loadOne, seq_ids)) device = torchutils.selectDevice(gpu_dev_id) # Load data dir_trial_ids = tuple( set(utils.getUniqueIds(d, prefix='trial=', to_array=True)) for d in score_dirs) dir_trial_ids += (set( utils.getUniqueIds(data_dir, prefix='trial=', to_array=True)), ) trial_ids = np.array(list(sorted(set.intersection(*dir_trial_ids)))) for dir_name, t_ids in zip(score_dirs + (data_dir, ), dir_trial_ids): logger.info(f"{len(t_ids)} trial ids from {dir_name}:") logger.info(f" {t_ids}") logger.info(f"{len(trial_ids)} trials in intersection: {trial_ids}") assembly_seqs = loadAll(trial_ids, 'assembly-seq.pkl', data_dir) feature_seqs = tuple( loadAll(trial_ids, 'data-scores.pkl', d) for d in score_dirs) feature_seqs = tuple(zip(*feature_seqs)) # Combine feature seqs include_indices = [] for i, seq_feats in enumerate(feature_seqs): feat_shapes = tuple(f.shape for f in seq_feats) include_seq = all(f == feat_shapes[0] for f in feat_shapes) if include_seq: include_indices.append(i) else: warn_str = ( f'Excluding trial {trial_ids[i]} with mismatched feature shapes: ' f'{feat_shapes}') logger.warning(warn_str) trial_ids = trial_ids[include_indices] assembly_seqs = tuple(assembly_seqs[i] for i in include_indices) feature_seqs = tuple(feature_seqs[i] for i in include_indices) feature_seqs = tuple(np.stack(f) for f in feature_seqs) # Define cross-validation folds if cv_data_dir is None: dataset_size = len(trial_ids) cv_folds = utils.makeDataSplits(dataset_size, **cv_params) cv_fold_trial_ids = tuple( tuple(map(lambda x: trial_ids[x], splits)) for splits in cv_folds) else: fn = os.path.join(cv_data_dir, 'cv-fold-trial-ids.pkl') cv_fold_trial_ids = joblib.load(fn) def getSplit(split_idxs): split_data = tuple( tuple(s[i] for i in split_idxs) for s in (feature_seqs, assembly_seqs, trial_ids)) return split_data gt_scores = [] all_scores = [] num_keyframes_total = 0 num_rgb_errors_total = 0 num_correctable_errors_total = 0 num_oov_total = 0 num_changed_total = 0 for cv_index, (train_ids, test_ids) in enumerate(cv_fold_trial_ids): try: test_idxs = np.array( [trial_ids.tolist().index(i) for i in test_ids]) include_indices.append(cv_index) except ValueError: logger.info( f" Skipping fold {cv_index}: missing test data {test_ids}") logger.info(f'CV fold {cv_index + 1}: {len(trial_ids)} total ' f'({len(train_ids)} train, {len(test_ids)} test)') # TRAIN PHASE if cv_data_dir is None: train_idxs = np.array([trial_ids.index(i) for i in train_ids]) train_assembly_seqs = tuple(assembly_seqs[i] for i in train_idxs) train_assemblies = [] for seq in train_assembly_seqs: list( labels.gen_eq_classes(seq, train_assemblies, equivalent=None)) model = None else: fn = f'cvfold={cv_index}_train-assemblies.pkl' train_assemblies = joblib.load(os.path.join(cv_data_dir, fn)) train_idxs = [ i for i in range(len(trial_ids)) if i not in test_idxs ] fn = f'cvfold={cv_index}_model.pkl' model = joblib.load(os.path.join(cv_data_dir, fn)) train_features, train_assembly_seqs, train_ids = getSplit(train_idxs) if False: train_labels = tuple( np.array( list( labels.gen_eq_classes(assembly_seq, train_assemblies, equivalent=None)), ) for assembly_seq in train_assembly_seqs) train_set = torchutils.SequenceDataset(train_features, train_labels, seq_ids=train_ids, device=device) train_loader = torch.utils.data.DataLoader(train_set, batch_size=1, shuffle=True) train_epoch_log = collections.defaultdict(list) # val_epoch_log = collections.defaultdict(list) metric_dict = { 'Avg Loss': metrics.AverageLoss(), 'Accuracy': metrics.Accuracy() } criterion = torch.nn.CrossEntropyLoss() optimizer_ft = torch.optim.Adam(model.parameters(), lr=1e-3, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False) lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer_ft, step_size=1, gamma=1.00) model = FusionClassifier(num_sources=train_features[0].shape[0]) model, last_model_wts = torchutils.trainModel( model, criterion, optimizer_ft, lr_scheduler, train_loader, # val_loader, device=device, metrics=metric_dict, train_epoch_log=train_epoch_log, # val_epoch_log=val_epoch_log, **train_params) torchutils.plotEpochLog(train_epoch_log, subfig_size=(10, 2.5), title='Training performance', fn=os.path.join( fig_dir, f'cvfold={cv_index}_train-plot.png')) test_assemblies = train_assemblies.copy() for feature_seq, gt_assembly_seq, trial_id in zip( *getSplit(test_idxs)): gt_seq = np.array( list( labels.gen_eq_classes(gt_assembly_seq, test_assemblies, equivalent=None))) if plot_predictions: assembly_fig_dir = os.path.join(fig_dir, 'assembly-imgs') if not os.path.exists(assembly_fig_dir): os.makedirs(assembly_fig_dir) for i, assembly in enumerate(test_assemblies): assembly.draw(assembly_fig_dir, i) # TEST PHASE accuracies = [] for feature_seq, gt_assembly_seq, trial_id in zip( *getSplit(test_idxs)): gt_seq = np.array( list( labels.gen_eq_classes(gt_assembly_seq, test_assemblies, equivalent=None))) num_labels = gt_seq.shape[0] num_features = feature_seq.shape[-1] if num_labels != num_features: err_str = (f"Skipping trial {trial_id}: " f"{num_labels} labels != {num_features} features") logger.info(err_str) continue # Ignore OOV states in ground-truth sample_idxs = np.arange(feature_seq.shape[-1]) score_idxs = gt_seq[gt_seq < feature_seq.shape[1]] sample_idxs = sample_idxs[gt_seq < feature_seq.shape[1]] gt_scores.append(feature_seq[:, score_idxs, sample_idxs]) all_scores.append(feature_seq.reshape(feature_seq.shape[0], -1)) if fusion_method == 'sum': score_seq = feature_seq.sum(axis=0) elif fusion_method == 'rgb_only': score_seq = feature_seq[1] elif fusion_method == 'imu_only': score_seq = feature_seq[0] else: raise NotImplementedError() if not decode: model = None if model is None: pred_seq = score_seq.argmax(axis=0) elif isinstance(model, torch.nn.Module): inputs = torch.tensor(feature_seq[None, ...], dtype=torch.float, device=device) outputs = model.forward(inputs) pred_seq = model.predict(outputs)[0].cpu().numpy() else: dummy_samples = np.arange(score_seq.shape[1]) pred_seq, _, _, _ = model.viterbi(dummy_samples, log_likelihoods=score_seq, ml_decode=(not decode)) pred_assemblies = [train_assemblies[i] for i in pred_seq] gt_assemblies = [test_assemblies[i] for i in gt_seq] acc = metrics.accuracy_upto(pred_assemblies, gt_assemblies, equivalence=None) accuracies.append(acc) rgb_pred_seq = feature_seq[1].argmax(axis=0) num_changed = np.sum(rgb_pred_seq != pred_seq) rgb_is_wrong = rgb_pred_seq != gt_seq num_rgb_errors = np.sum(rgb_is_wrong) imu_scores = feature_seq[0] imu_scores_gt = np.array([ imu_scores[s_idx, t] if s_idx < imu_scores.shape[0] else -np.inf for t, s_idx in enumerate(gt_seq) ]) imu_scores_rgb = np.array([ imu_scores[s_idx, t] if s_idx < imu_scores.shape[0] else -np.inf for t, s_idx in enumerate(rgb_pred_seq) ]) # imu_scores_gt = imu_scores[gt_seq, range(len(rgb_pred_seq))] best_imu_scores = imu_scores.max(axis=0) imu_is_right = imu_scores_gt >= best_imu_scores rgb_pred_score_is_lower = imu_scores_gt > imu_scores_rgb is_correctable_error = rgb_is_wrong & imu_is_right & rgb_pred_score_is_lower num_correctable_errors = np.sum(is_correctable_error) prop_correctable = num_correctable_errors / num_rgb_errors num_oov = np.sum(gt_seq >= len(train_assemblies)) num_states = len(gt_seq) num_keyframes_total += num_states num_rgb_errors_total += num_rgb_errors num_correctable_errors_total += num_correctable_errors num_oov_total += num_oov num_changed_total += num_changed logger.info(f" trial {trial_id}: {num_states} keyframes") logger.info(f" accuracy (fused): {acc * 100:.1f}%") logger.info( f" {num_oov} OOV states ({num_oov / num_states * 100:.1f}%)" ) logger.info( f" {num_rgb_errors} RGB errors; " f"{num_correctable_errors} correctable from IMU ({prop_correctable * 100:.1f}%)" ) saveVariable(score_seq, f'trial={trial_id}_data-scores') saveVariable(pred_assemblies, f'trial={trial_id}_pred-assembly-seq') saveVariable(gt_assemblies, f'trial={trial_id}_gt-assembly-seq') if plot_predictions: io_figs_dir = os.path.join(fig_dir, 'system-io') if not os.path.exists(io_figs_dir): os.makedirs(io_figs_dir) fn = os.path.join(io_figs_dir, f'trial={trial_id:03}.png') utils.plot_array(feature_seq, (gt_seq, pred_seq, score_seq), ('gt', 'pred', 'scores'), fn=fn) score_figs_dir = os.path.join(fig_dir, 'modality-scores') if not os.path.exists(score_figs_dir): os.makedirs(score_figs_dir) plot_scores(feature_seq, k=25, fn=os.path.join(score_figs_dir, f"trial={trial_id:03}.png")) paths_dir = os.path.join(fig_dir, 'path-imgs') if not os.path.exists(paths_dir): os.makedirs(paths_dir) assemblystats.drawPath(pred_seq, trial_id, f"trial={trial_id}_pred-seq", paths_dir, assembly_fig_dir) assemblystats.drawPath(gt_seq, trial_id, f"trial={trial_id}_gt-seq", paths_dir, assembly_fig_dir) label_seqs = (gt_seq, ) + tuple( scores.argmax(axis=0) for scores in feature_seq) label_seqs = np.row_stack(label_seqs) k = 10 for i, scores in enumerate(feature_seq): label_score_seqs = tuple( np.array([ scores[s_idx, t] if s_idx < scores.shape[0] else -np.inf for t, s_idx in enumerate(label_seq) ]) for label_seq in label_seqs) label_score_seqs = np.row_stack(label_score_seqs) drawPaths(label_seqs, f"trial={trial_id}_pred-scores_modality={i}", paths_dir, assembly_fig_dir, path_scores=label_score_seqs) topk_seq = (-scores).argsort(axis=0)[:k, :] path_scores = np.column_stack( tuple(scores[idxs, i] for i, idxs in enumerate(topk_seq.T))) drawPaths(topk_seq, f"trial={trial_id}_topk_modality={i}", paths_dir, assembly_fig_dir, path_scores=path_scores) label_score_seqs = tuple( np.array([ score_seq[s_idx, t] if s_idx < score_seq.shape[0] else -np.inf for t, s_idx in enumerate(label_seq) ]) for label_seq in label_seqs) label_score_seqs = np.row_stack(label_score_seqs) drawPaths(label_seqs, f"trial={trial_id}_pred-scores_fused", paths_dir, assembly_fig_dir, path_scores=label_score_seqs) topk_seq = (-score_seq).argsort(axis=0)[:k, :] path_scores = np.column_stack( tuple(score_seq[idxs, i] for i, idxs in enumerate(topk_seq.T))) drawPaths(topk_seq, f"trial={trial_id}_topk_fused", paths_dir, assembly_fig_dir, path_scores=path_scores) if accuracies: fold_accuracy = float(np.array(accuracies).mean()) # logger.info(f' acc: {fold_accuracy * 100:.1f}%') metric_dict = {'Accuracy': fold_accuracy} utils.writeResults(results_file, metric_dict, sweep_param_name, model_params) num_unexplained_errors = num_rgb_errors_total - ( num_oov_total + num_correctable_errors_total) prop_correctable = num_correctable_errors_total / num_rgb_errors_total prop_oov = num_oov_total / num_rgb_errors_total prop_unexplained = num_unexplained_errors / num_rgb_errors_total prop_changed = num_changed_total / num_keyframes_total logger.info("PERFORMANCE ANALYSIS") logger.info( f" {num_rgb_errors_total} / {num_keyframes_total} " f"RGB errors ({num_rgb_errors_total / num_keyframes_total * 100:.1f}%)" ) logger.info(f" {num_oov_total} / {num_rgb_errors_total} " f"RGB errors are OOV ({prop_oov * 100:.1f}%)") logger.info(f" {num_correctable_errors_total} / {num_rgb_errors_total} " f"RGB errors are correctable ({prop_correctable * 100:.1f}%)") logger.info(f" {num_unexplained_errors} / {num_rgb_errors_total} " f"RGB errors are unexplained ({prop_unexplained * 100:.1f}%)") logger.info( f" {num_changed_total} / {num_keyframes_total} " f"Predictions changed after fusion ({prop_changed * 100:.1f}%)") gt_scores = np.hstack(tuple(gt_scores)) plot_hists(np.exp(gt_scores), fn=os.path.join(fig_dir, "score-hists_gt.png")) all_scores = np.hstack(tuple(all_scores)) plot_hists(np.exp(all_scores), fn=os.path.join(fig_dir, "score-hists_all.png"))
def main(out_dir=None, data_dir=None, model_name=None, predict_mode='classify', gpu_dev_id=None, batch_size=None, learning_rate=None, independent_signals=None, active_only=None, output_dim_from_vocab=False, prefix='trial=', feature_fn_format='feature-seq.pkl', label_fn_format='label_seq.pkl', dataset_params={}, model_params={}, cv_params={}, train_params={}, viz_params={}, metric_names=['Loss', 'Accuracy', 'Precision', 'Recall', 'F1'], plot_predictions=None, results_file=None, sweep_param_name=None): data_dir = os.path.expanduser(data_dir) out_dir = os.path.expanduser(out_dir) if not os.path.exists(out_dir): os.makedirs(out_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) if results_file is None: results_file = os.path.join(out_dir, 'results.csv') else: results_file = os.path.expanduser(results_file) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def saveVariable(var, var_name, to_dir=out_data_dir): return utils.saveVariable(var, var_name, to_dir) # Load data device = torchutils.selectDevice(gpu_dev_id) trial_ids = utils.getUniqueIds(data_dir, prefix=prefix, suffix=feature_fn_format, to_array=True) dataset = utils.CvDataset( trial_ids, data_dir, prefix=prefix, feature_fn_format=feature_fn_format, label_fn_format=label_fn_format, ) utils.saveMetadata(dataset.metadata, out_data_dir) utils.saveVariable(dataset.vocab, 'vocab', out_data_dir) # Define cross-validation folds cv_folds = utils.makeDataSplits(len(trial_ids), **cv_params) utils.saveVariable(cv_folds, 'cv-folds', out_data_dir) if predict_mode == 'binary multiclass': # criterion = torchutils.BootstrappedCriterion( # 0.25, base_criterion=torch.nn.functional.binary_cross_entropy_with_logits, # ) criterion = torch.nn.BCEWithLogitsLoss() labels_dtype = torch.float elif predict_mode == 'multiclass': criterion = torch.nn.CrossEntropyLoss() labels_dtype = torch.long elif predict_mode == 'classify': criterion = torch.nn.CrossEntropyLoss() labels_dtype = torch.long else: raise AssertionError() def make_dataset(feats, labels, ids, shuffle=True): dataset = torchutils.SequenceDataset(feats, labels, device=device, labels_dtype=labels_dtype, seq_ids=ids, **dataset_params) loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True) return dataset, loader for cv_index, cv_fold in enumerate(cv_folds): train_data, val_data, test_data = dataset.getFold(cv_fold) if independent_signals: train_data = splitSeqs(*train_data, active_only=active_only) val_data = splitSeqs(*val_data, active_only=active_only) test_data = splitSeqs(*test_data, active_only=False) train_set, train_loader = make_dataset(*train_data, shuffle=True) test_set, test_loader = make_dataset(*test_data, shuffle=False) val_set, val_loader = make_dataset(*val_data, shuffle=True) logger.info( f'CV fold {cv_index + 1} / {len(cv_folds)}: {len(dataset.trial_ids)} total ' f'({len(train_set)} train, {len(val_set)} val, {len(test_set)} test)' ) logger.info(f'{train_set.num_label_types} unique labels in train set; ' f'vocab size is {len(dataset.vocab)}') input_dim = train_set.num_obsv_dims output_dim = train_set.num_label_types if output_dim_from_vocab: output_dim = len(dataset.vocab) if model_name == 'linear': model = torchutils.LinearClassifier( input_dim, output_dim, **model_params).to(device=device) elif model_name == 'conv': model = ConvClassifier(input_dim, output_dim, **model_params).to(device=device) elif model_name == 'TCN': if predict_mode == 'multiclass': num_multiclass = train_set[0][1].shape[-1] output_dim = max([ train_set.num_label_types, test_set.num_label_types, val_set.num_label_types ]) else: num_multiclass = None model = TcnClassifier(input_dim, output_dim, num_multiclass=num_multiclass, **model_params).to(device=device) elif model_name == 'LSTM': if predict_mode == 'multiclass': num_multiclass = train_set[0][1].shape[-1] output_dim = max([ train_set.num_label_types, test_set.num_label_types, val_set.num_label_types ]) else: num_multiclass = None model = LstmClassifier(input_dim, output_dim, num_multiclass=num_multiclass, **model_params).to(device=device) else: raise AssertionError() optimizer_ft = torch.optim.Adam(model.parameters(), lr=learning_rate, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False) lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer_ft, step_size=1, gamma=1.00) train_epoch_log = collections.defaultdict(list) val_epoch_log = collections.defaultdict(list) metric_dict = {name: metrics.makeMetric(name) for name in metric_names} model, last_model_wts = torchutils.trainModel( model, criterion, optimizer_ft, lr_scheduler, train_loader, val_loader, device=device, metrics=metric_dict, train_epoch_log=train_epoch_log, val_epoch_log=val_epoch_log, **train_params) # Test model metric_dict = {name: metrics.makeMetric(name) for name in metric_names} test_io_history = torchutils.predictSamples( model.to(device=device), test_loader, criterion=criterion, device=device, metrics=metric_dict, data_labeled=True, update_model=False, seq_as_batch=train_params['seq_as_batch'], return_io_history=True) if independent_signals: test_io_history = tuple(joinSeqs(test_io_history)) logger.info('[TST] ' + ' '.join(str(m) for m in metric_dict.values())) utils.writeResults(results_file, {k: v.value for k, v in metric_dict.items()}, sweep_param_name, model_params) if plot_predictions: io_fig_dir = os.path.join(fig_dir, 'model-io') if not os.path.exists(io_fig_dir): os.makedirs(io_fig_dir) label_names = ('gt', 'pred') preds, scores, inputs, gt_labels, ids = zip(*test_io_history) for batch in test_io_history: batch = tuple( x.cpu().numpy() if isinstance(x, torch.Tensor) else x for x in batch) for preds, _, inputs, gt_labels, seq_id in zip(*batch): fn = os.path.join(io_fig_dir, f"{prefix}{seq_id}_model-io.png") utils.plot_array(inputs, (gt_labels.T, preds.T), label_names, fn=fn) for batch in test_io_history: batch = tuple(x.cpu().numpy() if isinstance(x, torch.Tensor) else x for x in batch) for pred_seq, score_seq, feat_seq, label_seq, trial_id in zip( *batch): saveVariable(pred_seq, f'{prefix}{trial_id}_pred-label-seq') saveVariable(score_seq, f'{prefix}{trial_id}_score-seq') saveVariable(label_seq, f'{prefix}{trial_id}_true-label-seq') saveVariable(model, f'cvfold={cv_index}_{model_name}-best') train_fig_dir = os.path.join(fig_dir, 'train-plots') if not os.path.exists(train_fig_dir): os.makedirs(train_fig_dir) if train_epoch_log: torchutils.plotEpochLog(train_epoch_log, subfig_size=(10, 2.5), title='Training performance', fn=os.path.join( train_fig_dir, f'cvfold={cv_index}_train-plot.png')) if val_epoch_log: torchutils.plotEpochLog(val_epoch_log, subfig_size=(10, 2.5), title='Heldout performance', fn=os.path.join( train_fig_dir, f'cvfold={cv_index}_val-plot.png'))
def main(out_dir=None, video_data_dir=None, features_dir=None, activity_labels_dir=None, gt_keyframes_dir=None, use_gt_activity_labels=False): out_dir = os.path.expanduser(out_dir) video_data_dir = os.path.expanduser(video_data_dir) features_dir = os.path.expanduser(features_dir) activity_labels_dir = os.path.expanduser(activity_labels_dir) if gt_keyframes_dir is not None: gt_keyframes_dir = os.path.expanduser(gt_keyframes_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) def loadFromDir(var_name, dir_name): return joblib.load(os.path.join(dir_name, f"{var_name}.pkl")) def saveToWorkingDir(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f"{var_name}.pkl")) trial_ids = utils.getUniqueIds(features_dir, prefix='trial=', suffix='.pkl') seq_lens = [] for seq_idx, trial_id in enumerate(trial_ids): logger.info( f"Processing video {seq_idx + 1} / {len(trial_ids)} (trial {trial_id})" ) logger.info(f" Loading data...") feature_seq = loadFromDir(f"trial={trial_id}_feature-seq", features_dir) raw_labels = loadFromDir(f"trial-{trial_id}_action-seq", video_data_dir) timestamp_seq = loadFromDir( f"trial-{trial_id}_rgb-frame-timestamp-seq", video_data_dir) if use_gt_activity_labels: activity_label_fn = f"trial={trial_id}_true-label-seq" else: activity_label_fn = f"trial={trial_id}_pred-label-seq" activity_labels = loadFromDir(activity_label_fn, activity_labels_dir) action_labels = makeActionLabels(raw_labels, seq_len=feature_seq.shape[0]) if timestamp_seq.shape[0] != feature_seq.shape[0]: logger.warning( f"Video dimensions don't match: " f"{feature_seq.shape} scores, {timestamp_seq.shape} timestamps" ) continue # trim sequences is_activity = activity_labels == 1 if not is_activity.any(): logger.warning(f"No activity detected: skipping trial") continue action_labels = action_labels[is_activity, ...] timestamp_seq = timestamp_seq[is_activity, ...] feature_seq = feature_seq[is_activity, ...] if feature_seq.shape[0] != action_labels.shape[0]: err_str = ( "Data dimensions don't match: " f"{feature_seq.shape} features, {action_labels.shape} labels") raise AssertionError(err_str) seq_lens.append(feature_seq.shape[0]) logger.info(f" Saving output...") trial_str = f"trial={trial_id}" saveToWorkingDir(timestamp_seq, f'{trial_str}_timestamp-seq') saveToWorkingDir(feature_seq, f'{trial_str}_feature-seq') saveToWorkingDir(action_labels, f'{trial_str}_label-seq') fn = os.path.join(fig_dir, f"trial={trial_id}.png") utils.plot_array(feature_seq.T, (action_labels, ), ('action', ), fn=fn) logger.info(f"min seq len: {min(seq_lens)} max seq len: {max(seq_lens)}")
def main(out_dir=None, data_dir=None, scores_dir=None, start_from=None, stop_at=None, results_file=None, sweep_param_name=None, cv_params={}): data_dir = os.path.expanduser(data_dir) scores_dir = os.path.expanduser(scores_dir) out_dir = os.path.expanduser(out_dir) if not os.path.exists(out_dir): os.makedirs(out_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) if results_file is None: results_file = os.path.join(out_dir, 'results.csv') else: results_file = os.path.expanduser(results_file) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) io_dir = os.path.join(fig_dir, 'model-io') if not os.path.exists(io_dir): os.makedirs(io_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def loadVariable(var_name, from_dir=scores_dir): var = utils.loadVariable(var_name, from_dir) return var def saveVariable(var, var_name, to_dir=out_data_dir): utils.saveVariable(var, var_name, out_data_dir) def makeSeqBatches(unflatten, seq_ids): d = collections.defaultdict(list) for batch_index, (seq_index, win_index) in enumerate(unflatten): seq_id = seq_ids[seq_index] d[seq_id].append(batch_index) return d def loadBatchData(cv_index, batch_index): prefix = f"cvfold={cv_index}_batch={batch_index}" batch_score = loadVariable(f"{prefix}_score-seq") batch_pred = loadVariable(f"{prefix}_pred-label-seq").astype(int) batch_true = loadVariable(f"{prefix}_true-label-seq").astype(int) return batch_score, batch_pred, batch_true vocab = loadVariable('vocab') parts_vocab = loadVariable('parts-vocab') edge_labels = loadVariable('part-labels') saveVariable(vocab, 'vocab') saveVariable(parts_vocab, 'parts-vocab') saveVariable(edge_labels, 'part-labels') trial_ids = utils.getUniqueIds(data_dir, prefix='trial=', suffix='', to_array=True) cv_folds = utils.makeDataSplits(len(trial_ids), **cv_params) for cv_index, (__, __, test_indices) in enumerate(cv_folds): logger.info(f"CV FOLD {cv_index + 1} / {len(cv_folds)}") test_ids = trial_ids[test_indices] unflatten = loadVariable(f"cvfold={cv_index}_test-set-unflatten") flatten = makeSeqBatches(unflatten, test_ids) for seq_id in test_ids: logger.info(f" Processing sequence {seq_id}...") batch_idxs = flatten[seq_id] score_seq, pred_seq, true_seq = map( np.vstack, zip(*tuple(loadBatchData(cv_index, i) for i in batch_idxs))) trial_prefix = f"trial={seq_id}" rgb_seq = loadVariable(f"{trial_prefix}_rgb-frame-seq.", from_dir=data_dir) if score_seq.shape[0] != rgb_seq.shape[0]: err_str = f"scores shape {score_seq.shape} != data shape {rgb_seq.shape}" raise AssertionError(err_str) saveVariable(score_seq, f"{trial_prefix}_score-seq") saveVariable(pred_seq, f"{trial_prefix}_pred-label-seq") saveVariable(true_seq, f"{trial_prefix}_true-label-seq")
def main(out_dir=None, data_dir=None, model_name=None, gpu_dev_id=None, batch_size=None, learning_rate=None, model_params={}, cv_params={}, train_params={}, viz_params={}, load_masks_params={}, kornia_tfs={}, only_edge=None, num_disp_imgs=None, results_file=None, sweep_param_name=None): data_dir = os.path.expanduser(data_dir) out_dir = os.path.expanduser(out_dir) if not os.path.exists(out_dir): os.makedirs(out_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) if results_file is None: results_file = os.path.join(out_dir, 'results.csv') else: results_file = os.path.expanduser(results_file) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) io_dir = os.path.join(fig_dir, 'model-io') if not os.path.exists(io_dir): os.makedirs(io_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def saveVariable(var, var_name, to_dir=out_data_dir): return utils.saveVariable(var, var_name, to_dir) trial_ids = utils.getUniqueIds(data_dir, prefix='trial=', to_array=True) vocab = [BlockAssembly() ] + [make_single_block_state(i) for i in range(len(defn.blocks))] for seq_id in trial_ids: assembly_seq = utils.loadVariable(f"trial={seq_id}_assembly-seq", data_dir) for assembly in assembly_seq: utils.getIndex(assembly, vocab) parts_vocab, part_labels = labels_lib.make_parts_vocab( vocab, lower_tri_only=True, append_to_vocab=True) if only_edge is not None: part_labels = part_labels[:, only_edge:only_edge + 1] logger.info( f"Loaded {len(trial_ids)} sequences; {len(vocab)} unique assemblies") saveVariable(vocab, 'vocab') saveVariable(parts_vocab, 'parts-vocab') saveVariable(part_labels, 'part-labels') device = torchutils.selectDevice(gpu_dev_id) if model_name == 'AAE': Dataset = sim2real.DenoisingDataset elif model_name == 'Resnet': Dataset = sim2real.RenderDataset elif model_name == 'Connections': Dataset = sim2real.ConnectionDataset elif model_name == 'Labeled Connections': Dataset = sim2real.LabeledConnectionDataset occlusion_masks = loadMasks(**load_masks_params) if occlusion_masks is not None: logger.info(f"Loaded {occlusion_masks.shape[0]} occlusion masks") def make_data(shuffle=True): dataset = Dataset( parts_vocab, part_labels, vocab, device=device, occlusion_masks=occlusion_masks, kornia_tfs=kornia_tfs, ) data_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=shuffle) return dataset, data_loader for cv_index, cv_splits in enumerate(range(1)): cv_str = f"cvfold={cv_index}" train_set, train_loader = make_data(shuffle=True) test_set, test_loader = make_data(shuffle=False) val_set, val_loader = make_data(shuffle=True) if model_name == 'AAE': model = sim2real.AugmentedAutoEncoder(train_set.data_shape, train_set.num_classes) criterion = torchutils.BootstrappedCriterion( 0.25, base_criterion=torch.nn.functional.mse_loss, ) metric_names = ('Reciprocal Loss', ) elif model_name == 'Resnet': model = sim2real.ImageClassifier(train_set.num_classes, **model_params) criterion = torch.nn.CrossEntropyLoss() metric_names = ('Loss', 'Accuracy') elif model_name == 'Connections': model = sim2real.ConnectionClassifier(train_set.label_shape[0], **model_params) criterion = torch.nn.BCEWithLogitsLoss() metric_names = ('Loss', 'Accuracy', 'Precision', 'Recall', 'F1') elif model_name == 'Labeled Connections': out_dim = int(part_labels.max()) + 1 num_vertices = len(defn.blocks) edges = np.column_stack(np.tril_indices(num_vertices, k=-1)) if only_edge is not None: edges = edges[only_edge:only_edge + 1] model = sim2real.LabeledConnectionClassifier( out_dim, num_vertices, edges, **model_params) if only_edge is not None: logger.info(f"Class freqs: {train_set.class_freqs}") # criterion = torch.nn.CrossEntropyLoss(weight=1 / train_set.class_freqs[:, 0]) criterion = torch.nn.CrossEntropyLoss() else: criterion = torch.nn.CrossEntropyLoss() # criterion = torchutils.BootstrappedCriterion( # 0.25, base_criterion=torch.nn.functional.cross_entropy, # ) metric_names = ('Loss', 'Accuracy', 'Precision', 'Recall', 'F1') model = model.to(device=device) optimizer_ft = torch.optim.Adam(model.parameters(), lr=learning_rate, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False) lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer_ft, step_size=1, gamma=1.00) train_epoch_log = collections.defaultdict(list) val_epoch_log = collections.defaultdict(list) metric_dict = {name: metrics.makeMetric(name) for name in metric_names} model, last_model_wts = torchutils.trainModel( model, criterion, optimizer_ft, lr_scheduler, train_loader, val_loader, device=device, metrics=metric_dict, train_epoch_log=train_epoch_log, val_epoch_log=val_epoch_log, **train_params) # Test model metric_dict = {name: metrics.makeMetric(name) for name in metric_names} test_io_batches = torchutils.predictSamples( model.to(device=device), test_loader, criterion=criterion, device=device, metrics=metric_dict, data_labeled=True, update_model=False, seq_as_batch=train_params['seq_as_batch'], return_io_history=True) metric_str = ' '.join(str(m) for m in metric_dict.values()) logger.info('[TST] ' + metric_str) utils.writeResults(results_file, metric_dict, sweep_param_name, model_params) for pred_seq, score_seq, feat_seq, label_seq, trial_id in test_io_batches: trial_str = f"trial={trial_id}" saveVariable(pred_seq.cpu().numpy(), f'{trial_str}_pred-label-seq') saveVariable(score_seq.cpu().numpy(), f'{trial_str}_score-seq') saveVariable(label_seq.cpu().numpy(), f'{trial_str}_true-label-seq') saveVariable(model, f'{cv_str}_model-best') if train_epoch_log: torchutils.plotEpochLog(train_epoch_log, subfig_size=(10, 2.5), title='Training performance', fn=os.path.join( fig_dir, f'{cv_str}_train-plot.png')) if val_epoch_log: torchutils.plotEpochLog(val_epoch_log, subfig_size=(10, 2.5), title='Heldout performance', fn=os.path.join(fig_dir, f'{cv_str}_val-plot.png')) if num_disp_imgs is not None: model.plotBatches(test_io_batches, io_dir, dataset=test_set)
def main(out_dir=None, data_dir=None, preprocess_dir=None, segments_dir=None, keyframe_model_fn=None, max_seqs=None, subsample_period=None, frame_scoring_options={}, frame_selection_options={}): out_dir = os.path.expanduser(out_dir) data_dir = os.path.expanduser(data_dir) preprocess_dir = os.path.expanduser(preprocess_dir) keyframe_model_fn = os.path.expanduser(keyframe_model_fn) if segments_dir is not None: segments_dir = os.path.expanduser(segments_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) def loadFromDir(var_name, dir_name): return joblib.load(os.path.join(dir_name, f"{var_name}.pkl")) def loadFromPreprocessDir(var_name): return joblib.load(os.path.join(preprocess_dir, f"{var_name}.pkl")) def saveToWorkingDir(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f"{var_name}.pkl")) trial_ids = utils.getUniqueIds(preprocess_dir, prefix='trial-', suffix='.pkl') keyframe_model = joblib.load(keyframe_model_fn) models.visualizeKeyframeModel(keyframe_model, fn=os.path.join(fig_dir, 'keyframe-model.png')) if max_seqs is not None: trial_ids = trial_ids[:max_seqs] for seq_idx, trial_id in enumerate(trial_ids): logger.info( f"Processing video {seq_idx + 1} / {len(trial_ids)} (trial {trial_id})" ) logger.info(f" Loading data...") trial_str = f"trial-{trial_id}" rgb_frame_seq = loadFromDir(f'{trial_str}_rgb-frame-seq', data_dir) segment_frame_seq = loadFromDir(f'{trial_str}_segment-frame-seq', preprocess_dir) if segments_dir is not None: fn = f'trial={trial_id}_pred-segment-seq-rgb' try: segments_seq = loadFromDir(fn, segments_dir) except FileNotFoundError: logger.info(f" File not found: {fn}") continue else: segments_seq = None logger.info(f" Scoring frames...") frame_scores = videoprocessing.scoreFrames( keyframe_model, rgb_frame_seq, segment_frame_seq, score_kwargs=frame_scoring_options) segment_keyframe_idxs = videoprocessing.selectSegmentKeyframes( frame_scores, segment_labels=segments_seq, **frame_selection_options) logger.info(f" Saving output...") fn = os.path.join(fig_dir, f'{trial_str}_scores-plot.png') plotScores(frame_scores, segment_keyframe_idxs, fn) def saveFrames(indices, label): best_rgb = rgb_frame_seq[indices] best_seg = segment_frame_seq[indices] rgb_quantized = np.stack( tuple( videoprocessing.quantizeImage(keyframe_model, rgb_img, segment_img) for rgb_img, segment_img in zip(best_rgb, best_seg))) imageprocessing.displayImages( *best_rgb, *best_seg, *rgb_quantized, num_rows=3, file_path=os.path.join(fig_dir, f'{trial_str}_best-frames-{label}.png')) saveFrames(segment_keyframe_idxs, 'segment') # Save intermediate results saveToWorkingDir(frame_scores, f'{trial_str}_frame-scores') saveToWorkingDir(segment_keyframe_idxs, f'{trial_str}_keyframe-idxs')
def main(out_dir=None, video_data_dir=None, imu_data_dir=None, video_seg_scores_dir=None, imu_seg_scores_dir=None, gt_keyframes_dir=None, label_kwargs={}): out_dir = os.path.expanduser(out_dir) video_data_dir = os.path.expanduser(video_data_dir) imu_data_dir = os.path.expanduser(imu_data_dir) video_seg_scores_dir = os.path.expanduser(video_seg_scores_dir) if imu_seg_scores_dir is not None: imu_seg_scores_dir = os.path.expanduser(imu_seg_scores_dir) if gt_keyframes_dir is not None: gt_keyframes_dir = os.path.expanduser(gt_keyframes_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) def loadFromDir(var_name, dir_name): return joblib.load(os.path.join(dir_name, f"{var_name}.pkl")) def saveToWorkingDir(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f"{var_name}.pkl")) trial_ids = utils.getUniqueIds(video_seg_scores_dir, prefix='trial-', suffix='.pkl') all_score_seqs = [] all_action_labels = [] for seq_idx, trial_id in enumerate(trial_ids): logger.info( f"Processing video {seq_idx + 1} / {len(trial_ids)} (trial {trial_id})" ) logger.info(" Loading data...") score_seq = loadFromDir(f"trial-{trial_id}_frame-scores", video_seg_scores_dir) raw_labels = loadFromDir(f"trial-{trial_id}_action-seq", video_data_dir) action_labels = makeActivityLabels(raw_labels, seq_len=score_seq.shape[0], **label_kwargs) timestamp_seq = loadFromDir( f"trial-{trial_id}_rgb-frame-timestamp-seq", video_data_dir) if timestamp_seq.shape != score_seq.shape: logger.warning( f"Video dimensions don't match: " f"{score_seq.shape} scores, {timestamp_seq.shape} timestamps") continue if imu_seg_scores_dir is not None: try: imu_score_seq = loadFromDir(f'trial={trial_id}_score-seq', imu_seg_scores_dir) imu_score_seq = make_imu_feats(imu_score_seq) except FileNotFoundError: logger.info(f" IMU scores not found: trial {trial_id}") continue imu_timestamp_seq = loadFromDir(f"trial={trial_id}_timestamp-seq", imu_data_dir) if imu_timestamp_seq.shape != imu_score_seq.shape: logger.warning( f"IMU dimensions don't match: " f"{imu_score_seq.shape} scores, {imu_timestamp_seq.shape} timestamps" ) continue # Downsample imu scores to match rgb scores imu_score_seq = utils.resampleSeq(imu_score_seq, imu_timestamp_seq, timestamp_seq) imu_timestamp_seq = timestamp_seq else: imu_score_seq = None imu_timestamp_seq = None logger.info(" Saving output...") gt_keyframe_fn = os.path.join(gt_keyframes_dir, f"trial-{trial_id}_gt-keyframe-seq.pkl") if os.path.exists(gt_keyframe_fn): gt_keyframes = joblib.load(gt_keyframe_fn) else: gt_keyframes = None trial_str = f"trial={trial_id}" fn = os.path.join(fig_dir, f'{trial_str}_scores-plot.png') plotScores(timestamp_seq, score_seq, action_labels=action_labels, raw_labels=raw_labels, imu_timestamp_seq=imu_timestamp_seq, imu_score_seq=imu_score_seq, keyframe_idxs=gt_keyframes, fn=fn) all_score_seqs.append(score_seq) all_action_labels.append(action_labels) # Save intermediate results score_seq -= np.nanmean(score_seq) score_is_nan = np.isnan(score_seq) score_seq[score_is_nan] = 0 features = (score_seq, score_is_nan.astype(float)) if imu_score_seq is not None: features += (imu_score_seq, ) feature_seq = np.column_stack(features) saveToWorkingDir(feature_seq, f'{trial_str}_feature-seq') saveToWorkingDir(action_labels, f'{trial_str}_label-seq') all_score_seqs = np.hstack(tuple(all_score_seqs)) all_action_labels = np.hstack(tuple(all_action_labels)) fn = os.path.join(fig_dir, 'score-hists.png') plotScoreHists(all_score_seqs, all_action_labels, fn=fn)
def main(out_dir=None, rgb_data_dir=None, rgb_attributes_dir=None, rgb_vocab_dir=None, imu_data_dir=None, imu_attributes_dir=None, modalities=['rgb', 'imu'], gpu_dev_id=None, plot_predictions=None, results_file=None, sweep_param_name=None, model_params={}, cv_params={}, train_params={}, viz_params={}): out_dir = os.path.expanduser(out_dir) rgb_data_dir = os.path.expanduser(rgb_data_dir) rgb_attributes_dir = os.path.expanduser(rgb_attributes_dir) rgb_vocab_dir = os.path.expanduser(rgb_vocab_dir) imu_data_dir = os.path.expanduser(imu_data_dir) imu_attributes_dir = os.path.expanduser(imu_attributes_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) if results_file is None: results_file = os.path.join(out_dir, 'results.csv') else: results_file = os.path.expanduser(results_file) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def saveVariable(var, var_name, to_dir=out_data_dir): utils.saveVariable(var, var_name, to_dir) # Load data if modalities == ['rgb']: trial_ids = utils.getUniqueIds(rgb_data_dir, prefix='trial=', to_array=True) logger.info(f"Processing {len(trial_ids)} videos") else: rgb_trial_ids = utils.getUniqueIds(rgb_data_dir, prefix='trial=', to_array=True) imu_trial_ids = utils.getUniqueIds(imu_data_dir, prefix='trial=', to_array=True) trial_ids = np.array( sorted(set(rgb_trial_ids.tolist()) & set(imu_trial_ids.tolist()))) logger.info( f"Processing {len(trial_ids)} videos common to " f"RGB ({len(rgb_trial_ids)} total) and IMU ({len(imu_trial_ids)} total)" ) device = torchutils.selectDevice(gpu_dev_id) dataset = FusionDataset(trial_ids, rgb_attributes_dir, rgb_data_dir, imu_attributes_dir, imu_data_dir, device=device, modalities=modalities) utils.saveMetadata(dataset.metadata, out_data_dir) saveVariable(dataset.vocab, 'vocab') # parts_vocab = loadVariable('parts-vocab') edge_labels = { 'rgb': utils.loadVariable('part-labels', rgb_vocab_dir), 'imu': np.stack([ labels.inSameComponent(a, lower_tri_only=True) for a in dataset.vocab ]) } # edge_labels = revise_edge_labels(edge_labels, input_seqs) attribute_labels = tuple(edge_labels[name] for name in modalities) logger.info('Making transition probs...') transition_probs = make_transition_scores(dataset.vocab) saveVariable(transition_probs, 'transition-probs') model = AttributeModel(*attribute_labels, device=device) if plot_predictions: figsize = (12, 3) fig, axis = plt.subplots(1, figsize=figsize) axis.imshow(edge_labels['rgb'].T, interpolation='none', aspect='auto') plt.savefig(os.path.join(fig_dir, "edge-labels.png")) plt.close() for i, trial_id in enumerate(trial_ids): logger.info(f"Processing sequence {trial_id}...") trial_prefix = f"trial={trial_id}" true_label_seq = dataset.loadTargets(trial_id) attribute_feats = dataset.loadInputs(trial_id) score_seq = model(attribute_feats) pred_label_seq = model.predict(score_seq) attribute_feats = attribute_feats.cpu().numpy() score_seq = score_seq.cpu().numpy() true_label_seq = true_label_seq.cpu().numpy() pred_label_seq = pred_label_seq.cpu().numpy() saveVariable(score_seq.T, f'{trial_prefix}_score-seq') saveVariable(true_label_seq.T, f'{trial_prefix}_label-seq') if plot_predictions: fn = os.path.join(fig_dir, f'{trial_prefix}.png') utils.plot_array(attribute_feats.T, (true_label_seq, pred_label_seq, score_seq), ('gt', 'pred', 'scores'), fn=fn) metric_dict = eval_metrics(pred_label_seq, true_label_seq) for name, value in metric_dict.items(): logger.info(f" {name}: {value * 100:.2f}%") utils.writeResults(results_file, metric_dict, sweep_param_name, model_params)
def main( out_dir=None, data_dir=None, use_vid_ids_from=None, output_data=None, magnitude_centering=None, resting_from_gt=None, remove_before_first_touch=None, include_signals=None, fig_type=None): data_dir = os.path.expanduser(data_dir) out_dir = os.path.expanduser(out_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) logger.info(f"Reading from: {data_dir}") logger.info(f"Writing to: {out_dir}") fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def loadAll(seq_ids, var_name, from_dir=data_dir, prefix='trial='): all_data = tuple( utils.loadVariable(f"{prefix}{seq_id}_{var_name}", from_dir) for seq_id in seq_ids ) return all_data def saveVariable(var, var_name, to_dir=out_data_dir): utils.saveVariable(var, var_name, to_dir) if fig_type is None: fig_type = 'multi' # Load data if use_vid_ids_from is None: trial_ids = utils.getUniqueIds(data_dir, prefix='trial=', to_array=True) else: use_vid_ids_from = os.path.expanduser(use_vid_ids_from) trial_ids = utils.getUniqueIds(use_vid_ids_from, prefix='trial-', to_array=True) accel_seqs = loadAll(trial_ids, 'accel-samples.pkl') gyro_seqs = loadAll(trial_ids, 'gyro-samples.pkl') action_seqs = loadAll(trial_ids, 'action-seq.pkl') rgb_timestamp_seqs = loadAll(trial_ids, 'rgb-frame-timestamp-seq.pkl') def validate_imu(seqs): def is_valid(d): return not any(np.isnan(x).any() for x in d.values()) return np.array([is_valid(d) for d in seqs]) imu_is_valid = validate_imu(accel_seqs) & validate_imu(gyro_seqs) logger.info( f"Ignoring {(~imu_is_valid).sum()} IMU sequences with NaN-valued samples " f"(of {len(imu_is_valid)} total)" ) def chooseValid(seq): return tuple(x for x, is_valid in zip(seq, imu_is_valid) if is_valid) trial_ids = np.array(list(chooseValid(trial_ids))) accel_seqs = chooseValid(accel_seqs) gyro_seqs = chooseValid(gyro_seqs) action_seqs = chooseValid(action_seqs) rgb_timestamp_seqs = chooseValid(rgb_timestamp_seqs) vocab = [] metadata = utils.loadMetadata(data_dir, rows=trial_ids) utils.saveMetadata(metadata, out_data_dir) utils.saveVariable(vocab, 'vocab', out_data_dir) def norm(x): norm = np.linalg.norm(imu.getImuSamples(x), axis=1)[:, None] return norm accel_mag_seqs = tuple(map(lambda x: dictToArray(x, transform=norm), accel_seqs)) gyro_mag_seqs = tuple(map(lambda x: dictToArray(x, transform=norm), gyro_seqs)) imu_timestamp_seqs = utils.batchProcess(makeTimestamps, accel_seqs, gyro_seqs) if remove_before_first_touch: before_first_touch_seqs = utils.batchProcess( beforeFirstTouch, action_seqs, rgb_timestamp_seqs, imu_timestamp_seqs ) num_ignored = sum(b is None for b in before_first_touch_seqs) logger.info( f"Ignoring {num_ignored} sequences without first-touch annotations " f"(of {len(before_first_touch_seqs)} total)" ) trials_missing_first_touch = [ i for b, i in zip(before_first_touch_seqs, trial_ids) if b is None ] logger.info(f"Trials without first touch: {trials_missing_first_touch}") def clip(signal, bool_array): return signal[~bool_array, ...] accel_mag_seqs = tuple( clip(signal, b) for signal, b in zip(accel_mag_seqs, before_first_touch_seqs) if b is not None ) gyro_mag_seqs = tuple( clip(signal, b) for signal, b in zip(gyro_mag_seqs, before_first_touch_seqs) if b is not None ) imu_timestamp_seqs = tuple( clip(signal, b) for signal, b in zip(imu_timestamp_seqs, before_first_touch_seqs) if b is not None ) trial_ids = tuple( x for x, b in zip(trial_ids, before_first_touch_seqs) if b is not None ) action_seqs = tuple( x for x, b in zip(action_seqs, before_first_touch_seqs) if b is not None ) rgb_timestamp_seqs = tuple( x for x, b in zip(rgb_timestamp_seqs, before_first_touch_seqs) if b is not None ) assembly_seqs = utils.batchProcess( parseActions, action_seqs, rgb_timestamp_seqs, imu_timestamp_seqs ) if output_data == 'components': accel_feat_seqs = accel_mag_seqs gyro_feat_seqs = gyro_mag_seqs unique_components = {frozenset(): 0} imu_label_seqs = zip( *tuple( labels.componentLabels(*args, unique_components) for args in zip(action_seqs, rgb_timestamp_seqs, imu_timestamp_seqs) ) ) saveVariable(unique_components, 'unique_components') elif output_data == 'pairwise components': imu_label_seqs = utils.batchProcess( labels.pairwiseComponentLabels, assembly_seqs, static_kwargs={'lower_tri_only': True, 'include_action_labels': False} ) accel_feat_seqs = tuple(map(imu.pairwiseFeats, accel_mag_seqs)) gyro_feat_seqs = tuple(map(imu.pairwiseFeats, gyro_mag_seqs)) else: raise AssertionError() signals = {'accel': accel_feat_seqs, 'gyro': gyro_feat_seqs} if include_signals is None: include_signals = tuple(signals.keys()) signals = tuple(signals[key] for key in include_signals) imu_feature_seqs = tuple(np.stack(x, axis=-1).squeeze(axis=-1) for x in zip(*signals)) video_seqs = tuple(zip(imu_feature_seqs, imu_label_seqs, trial_ids)) imu.plot_prediction_eg(video_seqs, fig_dir, fig_type=fig_type, output_data=output_data) video_seqs = tuple( zip(assembly_seqs, imu_feature_seqs, imu_timestamp_seqs, imu_label_seqs, trial_ids) ) for assembly_seq, feature_seq, timestamp_seq, label_seq, trial_id in video_seqs: id_string = f"trial={trial_id}" saveVariable(assembly_seq, f'{id_string}_assembly-seq') saveVariable(feature_seq, f'{id_string}_feature-seq') saveVariable(timestamp_seq, f'{id_string}_timestamp-seq') saveVariable(label_seq, f'{id_string}_label-seq')
def main(out_dir=None, data_dir=None, scores_dir=None, frames_dir=None, vocab_from_scores_dir=None, only_fold=None, plot_io=None, prefix='seq=', results_file=None, sweep_param_name=None, model_params={}, cv_params={}): data_dir = os.path.expanduser(data_dir) scores_dir = os.path.expanduser(scores_dir) frames_dir = os.path.expanduser(frames_dir) out_dir = os.path.expanduser(out_dir) if not os.path.exists(out_dir): os.makedirs(out_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) if results_file is None: results_file = os.path.join(out_dir, 'results.csv') else: results_file = os.path.expanduser(results_file) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) io_dir_images = os.path.join(fig_dir, 'model-io_images') if not os.path.exists(io_dir_images): os.makedirs(io_dir_images) io_dir_plots = os.path.join(fig_dir, 'model-io_plots') if not os.path.exists(io_dir_plots): os.makedirs(io_dir_plots) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) seq_ids = utils.getUniqueIds(scores_dir, prefix=prefix, suffix='pred-label-seq.*', to_array=True) logger.info( f"Loaded scores for {len(seq_ids)} sequences from {scores_dir}") if vocab_from_scores_dir: vocab = utils.loadVariable('vocab', scores_dir) else: vocab = utils.loadVariable('vocab', data_dir) all_metrics = collections.defaultdict(list) # Define cross-validation folds cv_folds = utils.makeDataSplits(len(seq_ids), **cv_params) utils.saveVariable(cv_folds, 'cv-folds', out_data_dir) all_pred_seqs = [] all_true_seqs = [] for cv_index, cv_fold in enumerate(cv_folds): if only_fold is not None and cv_index != only_fold: continue train_indices, val_indices, test_indices = cv_fold logger.info( f"CV FOLD {cv_index + 1} / {len(cv_folds)}: " f"{len(train_indices)} train, {len(val_indices)} val, {len(test_indices)} test" ) for i in test_indices: seq_id = seq_ids[i] logger.info(f" Processing sequence {seq_id}...") trial_prefix = f"{prefix}{seq_id}" score_seq = utils.loadVariable(f"{trial_prefix}_score-seq", scores_dir) pred_seq = utils.loadVariable(f"{trial_prefix}_pred-label-seq", scores_dir) true_seq = utils.loadVariable(f"{trial_prefix}_true-label-seq", scores_dir) metric_dict = eval_metrics(pred_seq, true_seq) for name, value in metric_dict.items(): logger.info(f" {name}: {value * 100:.2f}%") all_metrics[name].append(value) utils.writeResults(results_file, metric_dict, sweep_param_name, model_params) all_pred_seqs.append(pred_seq) all_true_seqs.append(true_seq) if plot_io: utils.plot_array(score_seq.T, (true_seq, pred_seq), ('true', 'pred'), fn=os.path.join(io_dir_plots, f"seq={seq_id:03d}.png")) if False: confusions = metrics.confusionMatrix(all_pred_seqs, all_true_seqs, len(vocab)) utils.saveVariable(confusions, "confusions", out_data_dir) per_class_acc, class_counts = metrics.perClassAcc(confusions, return_counts=True) class_preds = confusions.sum(axis=1) logger.info(f"MACRO ACC: {np.nanmean(per_class_acc) * 100:.2f}%") metrics.plotConfusions(os.path.join(fig_dir, 'confusions.png'), confusions, vocab) metrics.plotPerClassAcc(os.path.join(fig_dir, 'per-class-results.png'), vocab, per_class_acc, class_preds, class_counts)
def main( out_dir=None, data_dir=None, prefix='trial=', feature_fn_format='feature-seq.pkl', label_fn_format='label_seq.pkl', slowfast_labels_path=None, cv_params={}, slowfast_csv_params={}): out_dir = os.path.expanduser(out_dir) data_dir = os.path.expanduser(data_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) # Load data trial_ids = utils.getUniqueIds( data_dir, prefix=prefix, suffix=feature_fn_format, to_array=True ) dataset = utils.CvDataset( trial_ids, data_dir, feature_fn_format=feature_fn_format, label_fn_format=label_fn_format, vocab=[], prefix=prefix ) utils.saveMetadata(dataset.metadata, out_data_dir) # Make folds dataset_size = len(trial_ids) cv_folds = utils.makeDataSplits(dataset_size, metadata=dataset.metadata, **cv_params) save_cv_folds(cv_folds, os.path.join(out_data_dir, 'cv-folds.json')) split_names = ('train', 'val', 'test') # Check folds for cv_index, cv_fold in enumerate(cv_folds): train_data, val_data, test_data = dataset.getFold(cv_fold) train_feats, train_labels, train_ids = train_data test_feats, test_labels, test_ids = test_data val_feats, val_labels, val_ids = val_data logger.info( f'CV fold {cv_index + 1} / {len(cv_folds)}: {len(trial_ids)} total ' f'({len(train_ids)} train, {len(val_ids)} val, {len(test_ids)} test)' ) slowfast_labels_pattern = os.path.join(data_dir, 'slowfast-labels*.csv') for slowfast_labels_path in glob.glob(slowfast_labels_pattern): cv_str = f"cvfold={cv_index}" fn = os.path.basename(slowfast_labels_path) slowfast_labels = pd.read_csv( slowfast_labels_path, index_col=0, keep_default_na=False, **slowfast_csv_params ) for split_indices, split_name in zip(cv_fold, split_names): matches = tuple( slowfast_labels.loc[slowfast_labels['video_name'] == vid_id] for vid_id in dataset.metadata.iloc[split_indices]['dir_name'].to_list() ) if matches: split = pd.concat(matches, axis=0) split.to_csv( os.path.join(out_data_dir, f"{cv_str}_{split_name}_{fn}"), **slowfast_csv_params ) else: logger.info(f' Skipping empty slowfast split: {split_name}')
def main(out_dir=None, data_dir=None, segs_dir=None, scores_dir=None, vocab_dir=None, label_type='edges', gpu_dev_id=None, start_from=None, stop_at=None, num_disp_imgs=None, results_file=None, sweep_param_name=None, model_params={}, cv_params={}): data_dir = os.path.expanduser(data_dir) segs_dir = os.path.expanduser(segs_dir) scores_dir = os.path.expanduser(scores_dir) vocab_dir = os.path.expanduser(vocab_dir) out_dir = os.path.expanduser(out_dir) if not os.path.exists(out_dir): os.makedirs(out_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) if results_file is None: results_file = os.path.join(out_dir, 'results.csv') else: results_file = os.path.expanduser(results_file) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) io_dir_images = os.path.join(fig_dir, 'model-io_images') if not os.path.exists(io_dir_images): os.makedirs(io_dir_images) io_dir_plots = os.path.join(fig_dir, 'model-io_plots') if not os.path.exists(io_dir_plots): os.makedirs(io_dir_plots) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) seq_ids = utils.getUniqueIds(scores_dir, prefix='trial=', suffix='score-seq.*', to_array=True) logger.info( f"Loaded scores for {len(seq_ids)} sequences from {scores_dir}") link_vocab = {} joint_vocab = {} joint_type_vocab = {} vocab, parts_vocab, part_labels = load_vocab(link_vocab, joint_vocab, joint_type_vocab, vocab_dir) pred_vocab = [] # FIXME if label_type == 'assembly': logger.info("Converting assemblies -> edges") state_pred_seqs = tuple( utils.loadVariable(f"trial={seq_id}_pred-label-seq", scores_dir) for seq_id in seq_ids) state_true_seqs = tuple( utils.loadVariable(f"trial={seq_id}_true-label-seq", scores_dir) for seq_id in seq_ids) edge_pred_seqs = tuple(part_labels[seq] for seq in state_pred_seqs) edge_true_seqs = tuple(part_labels[seq] for seq in state_true_seqs) elif label_type == 'edge': logger.info("Converting edges -> assemblies (will take a few minutes)") edge_pred_seqs = tuple( utils.loadVariable(f"trial={seq_id}_pred-label-seq", scores_dir) for seq_id in seq_ids) edge_true_seqs = tuple( utils.loadVariable(f"trial={seq_id}_true-label-seq", scores_dir) for seq_id in seq_ids) state_pred_seqs = tuple( edges_to_assemblies(seq, pred_vocab, parts_vocab, part_labels) for seq in edge_pred_seqs) state_true_seqs = tuple( edges_to_assemblies(seq, vocab, parts_vocab, part_labels) for seq in edge_true_seqs) device = torchutils.selectDevice(gpu_dev_id) dataset = sim2real.LabeledConnectionDataset( utils.loadVariable('parts-vocab', vocab_dir), utils.loadVariable('part-labels', vocab_dir), utils.loadVariable('vocab', vocab_dir), device=device) all_metrics = collections.defaultdict(list) # Define cross-validation folds cv_folds = utils.makeDataSplits(len(seq_ids), **cv_params) utils.saveVariable(cv_folds, 'cv-folds', out_data_dir) for cv_index, cv_fold in enumerate(cv_folds): train_indices, val_indices, test_indices = cv_fold logger.info( f"CV FOLD {cv_index + 1} / {len(cv_folds)}: " f"{len(train_indices)} train, {len(val_indices)} val, {len(test_indices)} test" ) train_states = np.hstack( tuple(state_true_seqs[i] for i in (train_indices))) train_edges = part_labels[train_states] # state_train_vocab = np.unique(train_states) # edge_train_vocab = part_labels[state_train_vocab] train_freq_bigram, train_freq_unigram = edge_joint_freqs(train_edges) # state_probs = utils.makeHistogram(len(vocab), train_states, normalize=True) test_states = np.hstack( tuple(state_true_seqs[i] for i in (test_indices))) test_edges = part_labels[test_states] # state_test_vocab = np.unique(test_states) # edge_test_vocab = part_labels[state_test_vocab] test_freq_bigram, test_freq_unigram = edge_joint_freqs(test_edges) f, axes = plt.subplots(1, 2) axes[0].matshow(train_freq_bigram) axes[0].set_title('Train') axes[1].matshow(test_freq_bigram) axes[1].set_title('Test') plt.tight_layout() plt.savefig( os.path.join(fig_dir, f"edge-freqs-bigram_cvfold={cv_index}.png")) f, axis = plt.subplots(1) axis.stem(train_freq_unigram, label='Train', linefmt='C0-', markerfmt='C0o') axis.stem(test_freq_unigram, label='Test', linefmt='C1--', markerfmt='C1o') plt.legend() plt.tight_layout() plt.savefig( os.path.join(fig_dir, f"edge-freqs-unigram_cvfold={cv_index}.png")) for i in test_indices: seq_id = seq_ids[i] logger.info(f" Processing sequence {seq_id}...") trial_prefix = f"trial={seq_id}" # I include the '.' to differentiate between 'rgb-frame-seq' and # 'rgb-frame-seq-before-first-touch' # rgb_seq = utils.loadVariable(f"{trial_prefix}_rgb-frame-seq.", data_dir) # seg_seq = utils.loadVariable(f"{trial_prefix}_seg-labels-seq", segs_dir) score_seq = utils.loadVariable(f"{trial_prefix}_score-seq", scores_dir) # if score_seq.shape[0] != rgb_seq.shape[0]: # err_str = f"scores shape {score_seq.shape} != data shape {rgb_seq.shape}" # raise AssertionError(err_str) edge_pred_seq = edge_pred_seqs[i] edge_true_seq = edge_true_seqs[i] state_pred_seq = state_pred_seqs[i] state_true_seq = state_true_seqs[i] num_types = np.unique(state_pred_seq).shape[0] num_samples = state_pred_seq.shape[0] num_total = len(pred_vocab) logger.info( f" {num_types} assemblies predicted ({num_total} total); " f"{num_samples} samples") # edge_freq_bigram, edge_freq_unigram = edge_joint_freqs(edge_true_seq) # dist_shift = np.linalg.norm(train_freq_unigram - edge_freq_unigram) metric_dict = { # 'State OOV rate': oov_rate_state(state_true_seq, state_train_vocab), # 'Edge OOV rate': oov_rate_edges(edge_true_seq, edge_train_vocab), # 'State avg prob, true': state_probs[state_true_seq].mean(), # 'State avg prob, pred': state_probs[state_pred_seq].mean(), # 'Edge distribution shift': dist_shift } metric_dict = eval_edge_metrics(edge_pred_seq, edge_true_seq, append_to=metric_dict) metric_dict = eval_state_metrics(state_pred_seq, state_true_seq, append_to=metric_dict) for name, value in metric_dict.items(): logger.info(f" {name}: {value * 100:.2f}%") all_metrics[name].append(value) utils.writeResults(results_file, metric_dict, sweep_param_name, model_params) if num_disp_imgs is not None: pred_images = tuple( render(dataset, vocab[seg_label]) for seg_label in utils.computeSegments(state_pred_seq)[0]) imageprocessing.displayImages( *pred_images, file_path=os.path.join( io_dir_images, f"seq={seq_id:03d}_pred-assemblies.png"), num_rows=None, num_cols=5) true_images = tuple( render(dataset, vocab[seg_label]) for seg_label in utils.computeSegments(state_true_seq)[0]) imageprocessing.displayImages( *true_images, file_path=os.path.join( io_dir_images, f"seq={seq_id:03d}_true-assemblies.png"), num_rows=None, num_cols=5) utils.plot_array(score_seq.T, (edge_true_seq.T, edge_pred_seq.T), ('true', 'pred'), fn=os.path.join(io_dir_plots, f"seq={seq_id:03d}.png"))
def main(out_dir=None, predictions_dir=None, imu_data_dir=None, video_data_dir=None, use_gt_segments=None, model_name=None, model_params={}, results_file=None, sweep_param_name=None, cv_params={}, viz_params={}, plot_predictions=None): predictions_dir = os.path.expanduser(predictions_dir) out_dir = os.path.expanduser(out_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) if results_file is None: results_file = os.path.join(out_dir, f'results.csv') else: results_file = os.path.expanduser(results_file) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def loadFromDir(var_name, dir_name): return joblib.load(os.path.join(dir_name, f"{var_name}.pkl")) def saveVariable(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f'{var_name}.pkl')) # Load data trial_ids = utils.getUniqueIds(predictions_dir, prefix='trial=', suffix='.pkl') for seq_idx, trial_id in enumerate(trial_ids): logger.info( f"Processing video {seq_idx + 1} / {len(trial_ids)} (trial {trial_id})" ) logger.info(f" Loading data...") feature_seq = loadFromDir(f"trial={trial_id}_score-seq", predictions_dir) label_seq = loadFromDir(f"trial={trial_id}_true-label-seq", predictions_dir) feature_seq = feature_seq[..., 2].swapaxes(0, 1) label_seq = (label_seq.swapaxes(0, 1) == 2).any(axis=1).astype(int) trial_str = f"trial={trial_id}" fn = os.path.join(fig_dir, f'{trial_str}_scores-plot.png') plotScores(feature_seq.max(axis=1), action_labels=label_seq, fn=fn) # all_score_seqs.append(score_seq) # all_action_labels.append(label_seq) feature_seq -= feature_seq.mean() saveVariable(feature_seq, f'{trial_str}_feature-seq') saveVariable(label_seq, f'{trial_str}_label-seq')
def main(out_dir=None, data_dir=None, corpus_name=None, start_from=None, stop_at=None, display_summary_img=None, background_removal_options={}, segment_image_options={}): out_dir = os.path.expanduser(out_dir) data_dir = os.path.expanduser(data_dir) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def loadFromWorkingDir(var_name): return joblib.load(os.path.join(data_dir, f"{var_name}.pkl")) def saveToWorkingDir(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f"{var_name}.pkl")) # corpus = duplocorpus.DuploCorpus(corpus_name) trial_ids = utils.getUniqueIds(data_dir, prefix='trial=', to_array=True) camera_pose = render.camera_pose camera_params = render.intrinsic_matrix for seq_idx, trial_id in enumerate(trial_ids): if start_from is not None and seq_idx < start_from: continue if stop_at is not None and seq_idx > stop_at: break trial_str = f"trial={trial_id}" logger.info( f"Processing video {seq_idx + 1} / {len(trial_ids)} (trial {trial_id})" ) # task_id = corpus.getTaskIndex(trial_id) # goal_state = labels.constructGoalState(task_id) goal_state = None logger.info(f" Loading data...") rgb_frame_seq = loadFromWorkingDir(f"{trial_str}_rgb-frame-seq") depth_frame_seq = loadFromWorkingDir(f"{trial_str}_depth-frame-seq") logger.info(f" Removing background...") foreground_mask_seq, background_plane_seq = utils.batchProcess( videoprocessing.foregroundPixels, depth_frame_seq, static_args=(camera_params, camera_pose), static_kwargs=background_removal_options, unzip=True) foreground_mask_seq = np.stack(foreground_mask_seq) logger.info(f" Segmenting foreground...") segment_frame_seq = utils.batchProcess( videoprocessing.segmentImage, rgb_frame_seq, depth_frame_seq, foreground_mask_seq, static_args=(goal_state, ), static_kwargs=segment_image_options) segment_frame_seq = np.stack(segment_frame_seq) foreground_mask_seq_no_ref_model = segment_frame_seq != 0 logger.info(f" Saving output...") saveToWorkingDir(background_plane_seq, f'{trial_str}_background-plane-seq') saveToWorkingDir(foreground_mask_seq, f'{trial_str}_foreground-mask-seq') saveToWorkingDir(segment_frame_seq, f'{trial_str}_segment-frame-seq') saveToWorkingDir(foreground_mask_seq_no_ref_model, f'{trial_str}_foreground-mask-seq_no-ref-model') if display_summary_img: if utils.in_ipython_console(): file_path = None else: trial_str = f"trial={trial_id}" file_path = os.path.join(fig_dir, f'{trial_str}_best-frames.png') imageprocessing.displayImages(*rgb_frame_seq, *depth_frame_seq, *segment_frame_seq, num_rows=3, file_path=file_path)
def main(out_dir=None, data_dir=None, attributes_dir=None, use_gt_segments=None, segments_dir=None, cv_data_dir=None, ignore_trial_ids=None, gpu_dev_id=None, plot_predictions=None, results_file=None, sweep_param_name=None, model_params={}, cv_params={}, train_params={}, viz_params={}): data_dir = os.path.expanduser(data_dir) out_dir = os.path.expanduser(out_dir) attributes_dir = os.path.expanduser(attributes_dir) if segments_dir is not None: segments_dir = os.path.expanduser(segments_dir) if cv_data_dir is not None: cv_data_dir = os.path.expanduser(cv_data_dir) if results_file is None: results_file = os.path.join(out_dir, 'results.csv') else: results_file = os.path.expanduser(results_file) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def saveVariable(var, var_name): joblib.dump(var, os.path.join(out_data_dir, f'{var_name}.pkl')) def loadAll(seq_ids, var_name, data_dir, prefix='trial='): def loadOne(seq_id): fn = os.path.join(data_dir, f'{prefix}{seq_id}_{var_name}') return joblib.load(fn) return tuple(map(loadOne, seq_ids)) # Load data trial_ids = utils.getUniqueIds(data_dir, prefix='trial=', to_array=True) assembly_seqs = loadAll(trial_ids, 'assembly-seq.pkl', data_dir, prefix='trial=') # Define cross-validation folds if cv_data_dir is None: dataset_size = len(trial_ids) cv_folds = utils.makeDataSplits(dataset_size, **cv_params) cv_fold_trial_ids = tuple( tuple(map(lambda x: trial_ids[x], splits)) for splits in cv_folds) else: fn = os.path.join(cv_data_dir, 'cv-fold-trial-ids.pkl') cv_fold_trial_ids = joblib.load(fn) test_ids = set().union(*tuple(set(fold[-1]) for fold in cv_fold_trial_ids)) logger.info(f"{len(test_ids)} final test ids: {test_ids}") def getSplit(split_idxs): split_data = tuple( tuple(s[i] for i in split_idxs) for s in (assembly_seqs, trial_ids)) return split_data for cv_index, (train_ids, test_ids) in enumerate(cv_fold_trial_ids): logger.info(f'CV fold {cv_index + 1}: {len(trial_ids)} total ' f'({len(train_ids)} train, {len(test_ids)} test)') try: test_idxs = torch.tensor( [trial_ids.tolist().index(i) for i in test_ids]) except ValueError: logger.info(" Skipping fold: missing test data") continue # TRAIN PHASE if cv_data_dir is None: train_idxs = torch.tensor([trial_ids.index(i) for i in train_ids]) train_assembly_seqs = tuple(assembly_seqs[i] for i in train_idxs) train_assemblies = [] for seq in train_assembly_seqs: list( labels.gen_eq_classes(seq, train_assemblies, equivalent=None)) else: fn = os.path.join(cv_data_dir, f'cvfold={cv_index}_train-assemblies.pkl') train_assemblies = joblib.load(fn) signatures = make_signatures(train_assemblies) if plot_predictions: figsize = (12, 3) fig, axis = plt.subplots(1, figsize=figsize) axis.imshow(signatures.numpy().T, interpolation='none', aspect='auto') plt.savefig( os.path.join(fig_dir, f"cvfold={cv_index}_signatures.png")) plt.close() # TEST PHASE accuracies = {'assembly': [], 'assembly_upto_eq': []} for gt_assembly_seq, trial_id in zip(*getSplit(test_idxs)): # if ignore_trial_ids is not None and trial_id in ignore_trial_ids: if False: logger.info(f" Ignoring trial {trial_id} in test fold") continue # FIXME: implement consistent data dimensions during serialization # (ie samples along rows) # feature_seq shape is (pairs, samples, classes) # should be (samples, pairs, classes) try: fn = os.path.join(attributes_dir, f'trial={trial_id}_score-seq.pkl') feature_seq = joblib.load(fn) except FileNotFoundError: logger.info(f" File not found: {fn}") continue test_assemblies = train_assemblies.copy() gt_assembly_id_seq = list( labels.gen_eq_classes(gt_assembly_seq, test_assemblies, equivalent=None)) gt_seq = makeAssemblyLabels(gt_assembly_id_seq, gt_assembly_seq) if use_gt_segments: segments = utils.makeSegmentLabels(gt_seq) elif segments_dir is not None: var_name = 'segment-seq-imu.pkl' fn = os.path.join(segments_dir, f'trial={trial_id}_{var_name}') try: segments = joblib.load(fn) except FileNotFoundError: logger.info(f" File not found: {fn}") continue else: segments = None feature_seq = torch.tensor(feature_seq, dtype=torch.float) if segments is not None: feature_seq = feature_seq.transpose(0, 1) feature_seq, _ = utils.reduce_over_segments( feature_seq.numpy(), segments, reduction=lambda x: x.mean(axis=0)) feature_seq = torch.tensor(feature_seq.swapaxes(0, 1), dtype=torch.float) gt_seq, _ = utils.reduce_over_segments(gt_seq.numpy(), segments) gt_seq = torch.tensor(gt_seq, dtype=torch.long) feature_seq = make_features(feature_seq) score_seq = score(feature_seq, signatures) pred_seq = predict(score_seq) pred_assemblies = [train_assemblies[i] for i in pred_seq] gt_assemblies = [test_assemblies[i] for i in gt_seq] acc = metrics.accuracy_upto(pred_assemblies, gt_assemblies, equivalence=components_equivalent) accuracies['assembly_upto_eq'].append(acc) acc = metrics.accuracy_upto(pred_assemblies, gt_assemblies) accuracies['assembly'].append(acc) # FIXME: Improve data naming convention in decode_keyframes.py saveVariable(score_seq, f'trial={trial_id}_data-scores') if plot_predictions: fn = os.path.join(fig_dir, f'trial={trial_id:03}.png') utils.plot_array( feature_seq, (gt_seq.numpy(), pred_seq.numpy(), score_seq.numpy()), ('gt', 'pred', 'scores'), fn=fn) if not any(v for v in accuracies.values()): continue metric_dict = {} for name, fold_accuracies in accuracies.items(): fold_accuracy = float(torch.tensor(fold_accuracies).mean()) logger.info(f' {name}: {fold_accuracy * 100:.1f}%') metric_dict[name] = fold_accuracy utils.writeResults(results_file, metric_dict, sweep_param_name, model_params)
def main(out_dir=None, data_dir=None, assembly_data_dir=None, scores_dir=None, event_attr_fn=None, connection_attr_fn=None, assembly_attr_fn=None, only_fold=None, plot_io=None, prefix='seq=', stop_after=None, background_action='', model_params={}, cv_params={}, stride=None, results_file=None, sweep_param_name=None): data_dir = os.path.expanduser(data_dir) assembly_data_dir = os.path.expanduser(assembly_data_dir) scores_dir = os.path.expanduser(scores_dir) event_attr_fn = os.path.expanduser(event_attr_fn) connection_attr_fn = os.path.expanduser(connection_attr_fn) assembly_attr_fn = os.path.expanduser(assembly_attr_fn) out_dir = os.path.expanduser(out_dir) if not os.path.exists(out_dir): os.makedirs(out_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) if results_file is None: results_file = os.path.join(out_dir, 'results.csv') else: results_file = os.path.expanduser(results_file) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) misc_dir = os.path.join(out_dir, 'misc') if not os.path.exists(misc_dir): os.makedirs(misc_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) seq_ids = utils.getUniqueIds(data_dir, prefix=prefix, suffix='labels.*', to_array=True) dataset = utils.FeaturelessCvDataset(seq_ids, data_dir, prefix=prefix, label_fn_format='labels') logger.info( f"Loaded scores for {len(seq_ids)} sequences from {scores_dir}") # Define cross-validation folds cv_folds = utils.makeDataSplits(len(seq_ids), **cv_params) utils.saveVariable(cv_folds, 'cv-folds', out_data_dir) # Load event, connection attributes assembly_vocab = tuple( tuple(sorted(tuple(sorted(joint)) for joint in a)) for a in utils.loadVariable('vocab', assembly_data_dir)) (*probs, assembly_transition_probs), vocabs = loadPartInfo( event_attr_fn, connection_attr_fn, assembly_attr_fn, background_action=background_action, assembly_vocab=assembly_vocab) event_assembly_scores = event_to_assembly_scores(*probs, vocabs) assembly_transition_scores = np.log(assembly_transition_probs) viz_transition_probs(os.path.join(fig_dir, 'action-transitions'), np.exp(event_assembly_scores), vocabs['event_vocab']) write_transition_probs(os.path.join(misc_dir, 'action-transitions'), np.exp(event_assembly_scores), vocabs['event_vocab'], vocabs['assembly_vocab']) for cv_index, cv_fold in enumerate(cv_folds): if only_fold is not None and cv_index != only_fold: continue train_indices, val_indices, test_indices = cv_fold logger.info( f"CV FOLD {cv_index + 1} / {len(cv_folds)}: " f"{len(train_indices)} train, {len(val_indices)} val, {len(test_indices)} test" ) train_data, val_data, test_data = dataset.getFold(cv_fold) cv_str = f'cvfold={cv_index}' class_priors, event_dur_probs = count_priors(train_data[0], len(dataset.vocab), stride=stride, approx_upto=0.95, support_only=True) event_dur_scores = np.log(event_dur_probs) event_dur_scores = np.zeros_like(event_dur_scores) scores = (event_dur_scores, event_assembly_scores, assembly_transition_scores) model = decode.AssemblyActionRecognizer(scores, vocabs, model_params) viz_priors(os.path.join(fig_dir, f'{cv_str}_priors'), class_priors, event_dur_probs) model.write_fsts(os.path.join(misc_dir, f'{cv_str}_fsts')) model.save_vocabs(os.path.join(out_data_dir, f'{cv_str}_model-vocabs')) for i, (_, seq_id) in enumerate(zip(*test_data)): if stop_after is not None and i >= stop_after: break trial_prefix = f"{prefix}{seq_id}" if model_params['return_label'] == 'input': true_seq = utils.loadVariable(f"{trial_prefix}_true-label-seq", scores_dir) elif model_params['return_label'] == 'output': try: true_seq = utils.loadVariable(f"{trial_prefix}_label-seq", assembly_data_dir) true_seq = true_seq[::stride] except AssertionError: # logger.info(f' Skipping sequence {seq_id}: {e}') continue logger.info(f" Processing sequence {seq_id}...") event_score_seq = utils.loadVariable(f"{trial_prefix}_score-seq", scores_dir) if event_score_seq.shape[0] != true_seq.shape[0]: err_str = (f'Event scores shape {event_score_seq.shape} ' f'!= labels shape {true_seq.shape}') raise AssertionError(err_str) # FIXME: the serialized variables are probs, not log-probs # event_score_seq = suppress_nonmax(event_score_seq) # event_score_seq = np.log(event_score_seq) decode_score_seq = model.forward(event_score_seq) pred_seq = model.predict(decode_score_seq) metric_dict = eval_metrics(pred_seq, true_seq) for name, value in metric_dict.items(): logger.info(f" {name}: {value * 100:.2f}%") utils.writeResults(results_file, metric_dict, sweep_param_name, model_params) utils.saveVariable(decode_score_seq, f'{trial_prefix}_score-seq', out_data_dir) utils.saveVariable(pred_seq, f'{trial_prefix}_pred-label-seq', out_data_dir) utils.saveVariable(true_seq, f'{trial_prefix}_true-label-seq', out_data_dir) if plot_io: utils.plot_array(event_score_seq.T, (pred_seq.T, true_seq.T), ('pred', 'true'), fn=os.path.join(fig_dir, f"seq={seq_id:03d}.png")) write_labels( os.path.join(misc_dir, f"seq={seq_id:03d}_pred-seq.txt"), pred_seq, model.output_vocab.as_raw()) write_labels( os.path.join(misc_dir, f"seq={seq_id:03d}_true-seq.txt"), true_seq, model.output_vocab.as_raw())
def main(out_dir=None, data_dir=None, metadata_file=None, metric_names=None, ignore_initial_state=None, draw_paths=None, plot_predictions=None, results_file=None, sweep_param_name=None): if metric_names is None: metric_names = ('accuracy', 'edit_score', 'overlap_score') logger.info(f"Reading from: {data_dir}") logger.info(f"Writing to: {out_dir}") data_dir = os.path.expanduser(data_dir) out_dir = os.path.expanduser(out_dir) if results_file is None: results_file = os.path.join(out_dir, 'results.csv') else: results_file = os.path.expanduser(results_file) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) out_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_data_dir): os.makedirs(out_data_dir) def loadVariable(var_name): return joblib.load(os.path.join(data_dir, f'{var_name}.pkl')) def loadAll(seq_ids, var_name): def loadOne(seq_id): fn = os.path.join(data_dir, f'trial={seq_id}_{var_name}') return joblib.load(fn) return tuple(map(loadOne, seq_ids)) trial_ids = utils.getUniqueIds(data_dir, prefix='trial=', to_array=True) pred_assembly_seqs = loadAll(trial_ids, "pred-assembly-seq.pkl") gt_assembly_seqs = loadAll(trial_ids, "gt-assembly-seq.pkl") all_assemblies = [] gt_assembly_label_seqs = tuple( np.array(list(labels.gen_eq_classes(gt_assembly_seq, all_assemblies))) for gt_assembly_seq in gt_assembly_seqs) pred_assembly_label_seqs = tuple( np.array(list(labels.gen_eq_classes(pred_assembly_seq, all_assemblies))) for pred_assembly_seq in pred_assembly_seqs) def checkPredictions(pred_assembly_label_seqs, gt_assembly_label_seqs, trial_ids): for i, trial_id in enumerate(trial_ids): pred_seq = pred_assembly_label_seqs[i] true_seq = gt_assembly_label_seqs[i] train_seqs = gt_assembly_label_seqs[:i] + gt_assembly_label_seqs[ i + 1:] train_vocab = np.unique(np.hstack(train_seqs)) pred_is_correct = pred_seq == true_seq pred_in_test_vocab = np.array( [np.any(train_vocab == i) for i in pred_seq]) pred_is_oov_and_correct = ~pred_in_test_vocab * pred_is_correct if not pred_in_test_vocab.all(): warn_str = ( f"trial {trial_id}: {np.sum(~pred_in_test_vocab)} / " f"{len(pred_in_test_vocab)} preds not in test; " f"{np.sum(pred_is_oov_and_correct)} oov preds are correct") logger.warning(warn_str) checkPredictions(pred_assembly_label_seqs, gt_assembly_label_seqs, trial_ids) def estimate_oov_rate(label_seqs): num_oov = 0 num_items = 0 for i in range(len(label_seqs)): test_seq = label_seqs[i] train_seqs = label_seqs[:i] + label_seqs[i + 1:] train_vocab = np.unique(np.hstack(train_seqs)) num_oov += sum( float(not np.any(train_vocab == i)) for i in test_seq) num_items += len(test_seq) return num_oov / num_items oov_rate = estimate_oov_rate(gt_assembly_label_seqs) logger.info(f"OOV rate: {oov_rate * 100:.2f}%") pred_action_seqs = tuple(map(actionsFromAssemblies, pred_assembly_seqs)) gt_action_seqs = tuple(map(actionsFromAssemblies, gt_assembly_seqs)) all_actions = [] gt_action_label_seqs = tuple( np.array(list(labels.gen_eq_classes(gt_action_seq, all_actions))) for gt_action_seq in gt_action_seqs) pred_action_label_seqs = tuple( np.array(list(labels.gen_eq_classes(pred_action_seq, all_actions))) for pred_action_seq in pred_action_seqs) if draw_paths: assembly_fig_dir = os.path.join(fig_dir, 'assembly-imgs') if not os.path.exists(assembly_fig_dir): os.makedirs(assembly_fig_dir) for i, assembly in enumerate(all_assemblies): assembly.draw(assembly_fig_dir, i) action_fig_dir = os.path.join(fig_dir, 'action-imgs') if not os.path.exists(action_fig_dir): os.makedirs(action_fig_dir) for i, action in enumerate(all_actions): action.draw(action_fig_dir, i) logger.info(f"Evaluating {len(trial_ids)} sequence predictions") batch = [] for i, trial_id in enumerate(trial_ids): logger.info(f"VIDEO {trial_id}:") pred_assembly_index_seq = pred_assembly_label_seqs[i] true_assembly_index_seq = gt_assembly_label_seqs[i] if ignore_initial_state: pred_assembly_index_seq = pred_assembly_index_seq[1:] true_assembly_index_seq = true_assembly_index_seq[1:] pred_action_index_seq = pred_action_label_seqs[i] true_action_index_seq = gt_action_label_seqs[i] if draw_paths: drawPaths([pred_assembly_index_seq, true_assembly_index_seq], f"trial={trial_id}_assembly-paths", fig_dir, assembly_fig_dir, path_labels=None, img_ext='png') drawPaths([pred_action_index_seq, true_action_index_seq], f"trial={trial_id}_action-paths", fig_dir, action_fig_dir, path_labels=None, img_ext='png') metric_dict = {} for name in metric_names: key = f"{name}_action" value = getattr(LCTM.metrics, name)(pred_action_index_seq, true_action_index_seq) / 100 metric_dict[key] = value logger.info(f" {key}: {value * 100:.1f}%") key = f"{name}_assembly" value = getattr(LCTM.metrics, name)(pred_assembly_index_seq, true_assembly_index_seq) / 100 metric_dict[key] = value logger.info(f" {key}: {value * 100:.1f}%") utils.writeResults(results_file, metric_dict, sweep_param_name, {}) batch.append( (pred_action_index_seq, None, true_action_index_seq, trial_id)) if plot_predictions: label_names = ('gt', 'pred') for preds, inputs, gt_labels, seq_id in batch: fn = os.path.join(fig_dir, f"trial={seq_id}_model-io.png") utils.plot_array(inputs, (gt_labels, preds), label_names, fn=fn, labels_together=True)
def main(out_dir=None, video_data_dir=None, keyframe_scores_dir=None, activity_labels_dir=None, action_labels_dir=None, max_seqs=None, use_gt_activity=False, use_gt_actions=False, frame_selection_options={}): out_dir = os.path.expanduser(out_dir) video_data_dir = os.path.expanduser(video_data_dir) keyframe_scores_dir = os.path.expanduser(keyframe_scores_dir) activity_labels_dir = os.path.expanduser(activity_labels_dir) action_labels_dir = os.path.expanduser(action_labels_dir) logger = utils.setupRootLogger(filename=os.path.join(out_dir, 'log.txt')) out_video_data_dir = os.path.join(out_dir, 'data') if not os.path.exists(out_video_data_dir): os.makedirs(out_video_data_dir) fig_dir = os.path.join(out_dir, 'figures') if not os.path.exists(fig_dir): os.makedirs(fig_dir) def loadFromDir(var_name, dir_name): return joblib.load(os.path.join(dir_name, f"{var_name}.pkl")) def saveToWorkingDir(var, var_name): joblib.dump(var, os.path.join(out_video_data_dir, f"{var_name}.pkl")) trial_ids = utils.getUniqueIds(action_labels_dir, prefix='trial=', suffix='.pkl') if max_seqs is not None: trial_ids = trial_ids[:max_seqs] all_costs = [] all_precisions = [] all_recalls = [] for seq_idx, trial_id in enumerate(trial_ids): logger.info( f"Processing video {seq_idx + 1} / {len(trial_ids)} (trial {trial_id})" ) logger.info(f" Loading data...") trial_str = f"trial-{trial_id}" rgb_frame_seq = loadFromDir(f'{trial_str}_rgb-frame-seq', video_data_dir) frame_scores = loadFromDir(f"{trial_str}_frame-scores", keyframe_scores_dir) if use_gt_activity: fn = f'trial={trial_id}_true-label-seq' else: fn = f'trial={trial_id}_pred-label-seq' activity_label_seq = loadFromDir(fn, activity_labels_dir) if use_gt_actions: fn = f'trial={trial_id}_true-label-seq' else: fn = f'trial={trial_id}_pred-label-seq' action_label_seq = loadFromDir(fn, action_labels_dir) segments_seq = makeSegments(action_label_seq, activity_label_seq) pred_keyframe_idxs = videoprocessing.selectSegmentKeyframes( frame_scores, segment_labels=segments_seq, **frame_selection_options) # Measure performance try: fn = f'trial-{trial_id}_gt-keyframe-seq' gt_keyframe_idxs = loadFromDir(fn, video_data_dir) except FileNotFoundError: gt_keyframe_idxs = None # Save and visualize output logger.info(f" Saving output...") fn = os.path.join(fig_dir, f'{trial_str}_scores-plot.png') plotScores(frame_scores, pred_keyframe_idxs, fn, segments_seq=segments_seq, gt_keyframe_idxs=gt_keyframe_idxs) def saveFrames(indices, label): best_rgb = rgb_frame_seq[indices] imageprocessing.displayImages( *best_rgb, num_rows=1, file_path=os.path.join(fig_dir, f'{trial_str}_best-frames-{label}.png')) saveFrames(pred_keyframe_idxs, 'pred') if gt_keyframe_idxs is not None: saveFrames(gt_keyframe_idxs, 'gt') best_costs, false_alarms, misses = evalKeyframes( pred_keyframe_idxs, gt_keyframe_idxs) num_fps = len(false_alarms) num_fns = len(misses) num_tps = best_costs.shape[0] precision = num_tps / (num_tps + num_fps) recall = num_tps / (num_tps + num_fns) logger.info(f" PRC: {precision * 100:03.1f}%") logger.info(f" REC: {recall * 100:03.1f}%") all_costs.append(best_costs) all_precisions.append(precision) all_recalls.append(recall) # Save intermediate results saveToWorkingDir(pred_keyframe_idxs, f'{trial_str}_keyframe-idxs') avg_precision = np.array(all_precisions).mean() avg_recall = np.array(all_recalls).mean() logger.info( f"AVG PRC: {avg_precision * 100:03.1f}% ({len(all_precisions)} seqs)") logger.info( f"AVG REC: {avg_recall * 100:03.1f}% ({len(all_recalls)} seqs)") all_costs = np.hstack(all_costs) fn = os.path.join(fig_dir, "keyframe-hist.png") plotKeyframeMetrics(all_costs, fn)