Caffe2 - Python API
A deep learning, cross platform ML framework
normalization.py
1 import torch
2 from torch.autograd.function import Function
3 from torch._thnn import type2backend
4 
5 from . import _all_functions
6 
7 
9 
10  def __init__(self, size, alpha=1e-4, beta=0.75, k=1):
11  super(CrossMapLRN2d, self).__init__()
12  self.size = size
13  self.alpha = alpha
14  self.beta = beta
15  self.k = k
16  self._backend = None
17  self.scale = None
18 
19  def forward(self, input):
20  assert input.dim() == 4
21 
22  self.scale = self.scale or input.new()
23  output = input.new()
24 
25  backend = type2backend[input.type()]
26  if backend is not None:
27  try:
28  backend.SpatialCrossMapLRN_updateOutput
29  self._backend = backend
30  except NotImplementedError:
31  pass
32 
33  if self._backend is not None:
34  self._backend.SpatialCrossMapLRN_updateOutput(
35  self._backend.library_state,
36  input,
37  output,
38  self.scale,
39  self.size,
40  self.alpha,
41  self.beta,
42  self.k
43  )
44  else:
45  batch_size = input.size(0)
46  channels = input.size(1)
47  input_height = input.size(2)
48  input_width = input.size(3)
49 
50  output.resize_as_(input)
51  self.scale.resize_as_(input)
52 
53  # use output storage as temporary buffer
54  input_square = output
55  torch.pow(input, 2, out=input_square)
56 
57  pre_pad = int((self.size - 1) / 2 + 1)
58  pre_pad_crop = channels if pre_pad > channels else pre_pad
59 
60  scale_first = self.scale.select(1, 0)
61  scale_first.zero_()
62  # compute first feature map normalization
63  for c in range(pre_pad_crop):
64  scale_first.add_(input_square.select(1, c))
65 
66  # reuse computations for next feature maps normalization
67  # by adding the next feature map and removing the previous
68  for c in range(1, channels):
69  scale_previous = self.scale.select(1, c - 1)
70  scale_current = self.scale.select(1, c)
71  scale_current.copy_(scale_previous)
72  if c < channels - pre_pad + 1:
73  square_next = input_square.select(1, c + pre_pad - 1)
74  scale_current.add_(1, square_next)
75 
76  if c > pre_pad:
77  square_previous = input_square.select(1, c - pre_pad)
78  scale_current.add_(-1, square_previous)
79 
80  self.scale.mul_(self.alpha / self.size).add_(self.k)
81 
82  torch.pow(self.scale, -self.beta, out=output)
83  output.mul_(input)
84 
85  self.save_for_backward(input, output)
86  return output
87 
88  def backward(self, grad_output):
89  input, output = self.saved_tensors
90  grad_input = grad_output.new()
91 
92  if self._backend is not None:
93  self._backend.SpatialCrossMapLRN_updateGradInput(
94  self._backend.library_state,
95  input,
96  grad_output,
97  grad_input,
98  self.scale,
99  output,
100  self.size,
101  self.alpha,
102  self.beta,
103  self.k
104  )
105  else:
106  batch_size = input.size(0)
107  channels = input.size(1)
108  input_height = input.size(2)
109  input_width = input.size(3)
110 
111  paddded_ratio = input.new(channels + self.size - 1, input_height,
112  input_width)
113  accum_ratio = input.new(input_height, input_width)
114 
115  cache_ratio_value = 2 * self.alpha * self.beta / self.size
116  inversePrePad = int(self.size - (self.size - 1) / 2)
117 
118  grad_input.resize_as_(input)
119  torch.pow(self.scale, -self.beta, out=grad_input).mul_(grad_output)
120 
121  paddded_ratio.zero_()
122  padded_ratio_center = paddded_ratio.narrow(0, inversePrePad,
123  channels)
124  for n in range(batch_size):
125  torch.mul(grad_output[n], output[n], out=padded_ratio_center)
126  padded_ratio_center.div_(self.scale[n])
127  torch.sum(
128  paddded_ratio.narrow(0, 0, self.size - 1), 0, keepdim=False, out=accum_ratio)
129  for c in range(channels):
130  accum_ratio.add_(paddded_ratio[c + self.size - 1])
131  grad_input[n][c].addcmul_(-cache_ratio_value, input[n][c],
132  accum_ratio)
133  accum_ratio.add_(-1, paddded_ratio[c])
134 
135  return grad_input
136 
137 
138 _all_functions.append(CrossMapLRN2d)