Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
common.py 7.20 KiB
import os
import sys
import pdb
import shutil
from collections import OrderedDict
import numpy as np
import sklearn.decomposition

import torch
import torch.nn.functional as F

try:
    import torch
    import torch.nn as nn
except ImportError:
    pass


def typename(x):
    return type(x).__module__


def tonumpy(x):
    if typename(x) == torch.__name__:
        return x.cpu().numpy()
    else:
        return x


def matmul(A, B):
    if typename(A) == np.__name__:
        B = tonumpy(B)
        scores = np.dot(A, B.T)
    elif typename(B) == torch.__name__:
        scores = torch.matmul(A, B.t()).cpu().numpy()
    else:
        raise TypeError("matrices must be either numpy or torch type")
    return scores


def pool(x, pooling='mean', gemp=3):
    if len(x) == 1:
        return x[0]
    x = torch.stack(x, dim=0)
    if pooling == 'mean':
        return torch.mean(x, dim=0)
    elif pooling == 'gem':
        def sympow(x, p, eps=1e-6):
            s = torch.sign(x)
            return (x*s).clamp(min=eps).pow(p) * s
        x = sympow(x, gemp)
        x = torch.mean(x, dim=0)
        return sympow(x, 1/gemp)
    else:
        raise ValueError("Bad pooling mode: "+str(pooling))


def torch_set_gpu(gpus, seed=None, randomize=True):
    if type(gpus) is int:
            gpus = [gpus]

    assert gpus, 'error: empty gpu list, use --gpu N N ...'

    cuda = all(gpu >= 0 for gpu in gpus)

    if cuda:
        if any(gpu >= 1000 for gpu in gpus):
            visible_gpus = [int(gpu) for gpu in os.environ['CUDA_VISIBLE_DEVICES'].split(',')]
            os.environ['CUDA_VISIBLE_DEVICES'] = ','.join([str(visible_gpus[gpu-1000]) for gpu in gpus])
        else:
            os.environ['CUDA_VISIBLE_DEVICES'] = ','.join([str(gpu) for gpu in gpus])
        assert cuda and torch.cuda.is_available(), "%s has GPUs %s unavailable" % (
            os.environ['HOSTNAME'], os.environ['CUDA_VISIBLE_DEVICES'])
        torch.backends.cudnn.benchmark = True
        torch.backends.cudnn.fastest = True
        print('Launching on GPUs ' + os.environ['CUDA_VISIBLE_DEVICES'])
    else:
        print('Launching on >> CPU <<')

    torch_set_seed(seed, cuda, randomize=randomize)
    return cuda


def torch_set_seed(seed, cuda, randomize=True):
    if seed:
        # this makes it 3x SLOWER but deterministic
        torch.backends.cudnn.enabled = False

    if randomize and not seed:
        import time
        seed = int(np.uint32(hash(time.time())))

    if seed:
        np.random.seed(seed)
        torch.manual_seed(seed)
        if cuda:
            torch.cuda.manual_seed(seed)


def save_checkpoint(state, is_best, filename):
    try:
        dirs = os.path.split(filename)[0]
        if not os.path.isdir(dirs):
            os.makedirs(dirs)
        torch.save(state, filename)
        if is_best:
            filenamebest = filename+'.best'
            shutil.copyfile(filename, filenamebest)
            filename = filenamebest
        print("saving to "+filename)
    except:
        print("Error: Could not save checkpoint at %s, skipping" % filename)


def load_checkpoint(filename, iscuda=False):
    if not filename:
        return None
    assert os.path.isfile(filename), "=> no checkpoint found at '%s'" % filename
    checkpoint = torch.load(filename, map_location=lambda storage, loc: storage)
    print("=> loading checkpoint '%s'" % filename, end='')
    for key in ['epoch', 'iter', 'current_iter']:
        if key in checkpoint:
            print(" (%s %d)" % (key, checkpoint[key]), end='')
    print()

    new_dict = OrderedDict()
    for k, v in list(checkpoint['state_dict'].items()):
        if k.startswith('module.'):
            k = k[7:]
        new_dict[k] = v
    checkpoint['state_dict'] = new_dict

    if iscuda and 'optimizer' in checkpoint:
        try:
            for state in checkpoint['optimizer']['state'].values():
                for k, v in state.items():
                    if iscuda and torch.is_tensor(v):
                        state[k] = v.cuda()
        except RuntimeError as e:
            print("RuntimeError:", e, "(machine %s, GPU %s)" % (
                os.environ['HOSTNAME'], os.environ['CUDA_VISIBLE_DEVICES']),
                file=sys.stderr)
            sys.exit(1)

    return checkpoint


def switch_model_to_cuda(model, iscuda=True, checkpoint=None):
    if iscuda:
        if checkpoint:
            checkpoint['state_dict'] = {'module.' + k: v for k, v in checkpoint['state_dict'].items()}
        try:
            model = torch.nn.DataParallel(model)

            # copy attributes automatically
            for var in dir(model.module):
                if var.startswith('_'):
                    continue
                val = getattr(model.module, var)
                if isinstance(val, (bool, int, float, str, dict)) or \
                   (callable(val) and var.startswith('get_')):
                    setattr(model, var, val)

            model.cuda()
            model.isasync = True
        except RuntimeError as e:
            print("RuntimeError:", e, "(machine %s, GPU %s)" % (
                os.environ['HOSTNAME'], os.environ['CUDA_VISIBLE_DEVICES']),
                file=sys.stderr)
            sys.exit(1)

    model.iscuda = iscuda
    return model


def model_size(model):
    ''' Computes the number of parameters of the model
    '''
    size = 0
    for weights in model.state_dict().values():
        size += np.prod(weights.shape)
    return size


def freeze_batch_norm(model, freeze=True, only_running=False):
    model.freeze_bn = bool(freeze)
    if not freeze:
        return

    for m in model.modules():
        if isinstance(m, nn.BatchNorm2d):
            # Eval mode freezes the running mean and std
            m.eval()
            for param in m.named_parameters():
                if only_running:
                    # Weight and bias can be updated
                    param[1].requires_grad = True
                else:
                    # Freeze the weight and bias
                    param[1].requires_grad = False


def variables(inputs, iscuda, not_on_gpu=[]):
    ''' convert several Tensors to cuda.Variables
    Tensor whose index are in not_on_gpu stays on cpu.
    '''
    inputs_var = []

    for i, x in enumerate(inputs):
        if i not in not_on_gpu and not isinstance(x, (tuple, list)):
            if iscuda:
                x = x.cuda(non_blocking=True)
            x = torch.autograd.Variable(x)
        inputs_var.append(x)

    return inputs_var


def transform(pca, X, whitenp=0.5, whitenv=None, whitenm=1.0, use_sklearn=True):
    if use_sklearn:
        # https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/decomposition/base.py#L99
        if pca.mean_ is not None:
            X = X - pca.mean_
        X_transformed = np.dot(X, pca.components_[:whitenv].T)
        if pca.whiten:
            X_transformed /= whitenm * np.power(pca.explained_variance_[:whitenv], whitenp)
    else:
        X = X - pca['means']
        X_transformed = np.dot(X, pca['W'])
    return X_transformed


def whiten_features(X, pca, l2norm=True, whitenp=0.5, whitenv=None, whitenm=1.0, use_sklearn=True):
    res = transform(pca, X, whitenp=whitenp, whitenv=whitenv, whitenm=whitenm, use_sklearn=use_sklearn)
    if l2norm:
        res = res / np.expand_dims(np.linalg.norm(res, axis=1), axis=1)
    return res