####################################################################### # Copyright (C) 2017 Shangtong Zhang(zhangshangtong.cpp@gmail.com) # # Permission given to modify the code as long as you keep this # # declaration at the top # ####################################################################### import torch import numpy as np class Normalizer: def __init__(self, x_size): self.m = np.zeros(x_size) self.v = np.zeros(x_size) self.n = 1.0 def __call__(self, x): if np.isscalar(x) or len(x.shape) == 1: return self.nomalize_single(x) elif len(x.shape) == 2: new_x = np.zeros(x.shape) for i in range(x.shape[0]): new_x[i] = self.nomalize_single(x[i]) return new_x else: assert 'Unsupported Shape' def nomalize_single(self, x): is_scalar = np.isscalar(x) if is_scalar: x = np.asarray([x]) new_m = self.m * (self.n / (self.n + 1)) + x / (self.n + 1) self.v = self.v * (self.n / (self.n + 1)) + (x - self.m) * (x - new_m) / (self.n + 1) self.m = new_m self.n += 1 std = (self.v + 1e-6) ** .5 x = (x - self.m) / std if is_scalar: x = np.asscalar(x) return x class StaticNormalizer: def __init__(self, o_size): self.offline_stats = SharedStats(o_size) self.online_stats = SharedStats(o_size) def __call__(self, o_): if np.isscalar(o_): o = torch.FloatTensor([o_]) else: o = torch.FloatTensor(o_) self.online_stats.feed(o) if self.offline_stats.n[0] == 0: return o_ std = (self.offline_stats.v + 1e-6) ** .5 o = (o - self.offline_stats.m) / std o = o.numpy() if np.isscalar(o_): o = np.asscalar(o) else: o = o.reshape(o_.shape) return o def state_dict(self): return self.offline_stats.state_dict() def load_state_dict(self, saved): self.offline_stats.load_state_dict(saved) class SharedStats: def __init__(self, o_size): self.m = torch.zeros(o_size) self.v = torch.zeros(o_size) self.n = torch.zeros(1) self.m.share_memory_() self.v.share_memory_() self.n.share_memory_() def feed(self, o): n = self.n[0] new_m = self.m * (n / (n + 1)) + o / (n + 1) self.v.copy_(self.v * (n / (n + 1)) + (o - self.m) * (o - new_m) / (n + 1)) self.m.copy_(new_m) self.n.add_(1) def zero(self): self.m.zero_() self.v.zero_() self.n.zero_() def load(self, stats): self.m.copy_(stats.m) self.v.copy_(stats.v) self.n.copy_(stats.n) def merge(self, B): A = self n_A = self.n[0] n_B = B.n[0] n = n_A + n_B delta = B.m - A.m m = A.m + delta * n_B / n v = A.v * n_A + B.v * n_B + delta * delta * n_A * n_B / n v /= n self.m.copy_(m) self.v.copy_(v) self.n.add_(B.n) def state_dict(self): return {'m': self.m.numpy(), 'v': self.v.numpy(), 'n': self.n.numpy()} def load_state_dict(self, saved): self.m = torch.FloatTensor(saved['m']) self.v = torch.FloatTensor(saved['v']) self.n = torch.FloatTensor(saved['n'])