You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

BatchNorm.py 14 kB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. from __future__ import absolute_import
  2. from .Node import Op
  3. import numpy as np
  4. from .. import ndarray
  5. from .._base import DNNL_LIB
  6. from ..cpu_links import batch_norm as cpu_batch_norm
  7. from ..cpu_links import batch_norm_inference as cpu_batch_norm_inference
  8. from ..cpu_links import batch_norm_gradient as cpu_batch_norm_gradient
  9. from ..gpu_links import CuDNN_Batch_Normalization
  10. from ..gpu_links import CuDNN_Batch_Normalization_gradient
  11. from ..gpu_links import CuDNN_Batch_Normalization_inference
  12. import numpy as np
  13. class Batch_NormalizationOp(Op):
  14. def __init__(self, node_in, bn_scale, bn_bias, momentum=0.99, eps=0.01, ctx=None):
  15. super().__init__(Batch_NormalizationOp,
  16. [node_in, bn_scale, bn_bias], ctx)
  17. self.momentum = momentum
  18. self.eps = eps
  19. self.save_mean = None
  20. self.save_var = None
  21. self.running_mean = None
  22. self.running_var = None
  23. def compute(self, input_vals, output_val, stream_handle=None, inference=False):
  24. if inference:
  25. if self.on_cpu:
  26. if DNNL_LIB['DnnlBatchNorm_Inference']:
  27. cpu_batch_norm_inference(input_vals[0], input_vals[1], input_vals[2], output_val, self.save_mean,
  28. self.save_var, self.momentum, self.eps)
  29. else:
  30. output_val[:] = batchnorm_inference(input_vals[0].asnumpy(), input_vals[1].asnumpy(),
  31. input_vals[2].asnumpy(
  32. ), self.save_mean, self.save_var,
  33. self.eps)
  34. else:
  35. CuDNN_Batch_Normalization_inference(
  36. input_vals[0], input_vals[1], input_vals[2], output_val, self.save_mean, self.save_var, self.eps, stream_handle)
  37. else:
  38. if self.on_cpu:
  39. if DNNL_LIB['DnnlBatchNorm']:
  40. if self.save_mean is None:
  41. dev_id = input_vals[0].handle.contents.ctx.device_id
  42. C = input_vals[0].shape[1]
  43. self.save_mean = ndarray.array(
  44. np.zeros([C], dtype=np.float32), ctx=ndarray.cpu(dev_id))
  45. self.save_var = ndarray.array(
  46. np.zeros([C], dtype=np.float32), ctx=ndarray.cpu(dev_id))
  47. cpu_batch_norm(input_vals[0], input_vals[1], input_vals[2], output_val,
  48. self.save_mean, self.save_var, self.momentum, self.eps)
  49. else:
  50. output_val[:], self.save_mean, self.save_var = batchnorm_forward(input_vals[0].asnumpy(),
  51. input_vals[1].asnumpy(
  52. ),
  53. input_vals[2].asnumpy(
  54. ),
  55. self.save_mean,
  56. self.save_var, self.momentum,
  57. self.eps)
  58. else:
  59. if self.save_mean == None:
  60. dev_id = input_vals[0].handle.contents.ctx.device_id
  61. C = input_vals[0].shape[1]
  62. self.save_mean = ndarray.array(
  63. np.zeros([1, C, 1, 1]), ctx=ndarray.gpu(dev_id))
  64. self.save_var = ndarray.array(
  65. np.zeros([1, C, 1, 1]), ctx=ndarray.gpu(dev_id))
  66. self.running_mean = ndarray.array(
  67. np.zeros([1, C, 1, 1]), ctx=ndarray.gpu(dev_id))
  68. self.running_var = ndarray.array(
  69. np.zeros([1, C, 1, 1]), ctx=ndarray.gpu(dev_id))
  70. CuDNN_Batch_Normalization(
  71. input_vals[0], input_vals[1], input_vals[2], output_val, self.save_mean, self.save_var,
  72. self.running_mean, self.running_var, self.momentum,
  73. self.eps, stream_handle)
  74. def gradient(self, output_grad):
  75. bn_gradient_node = batch_normalization_gradient_op(
  76. output_grad, self.inputs[0], self.inputs[1], self, self.eps, ctx=self.raw_ctx)
  77. data_gradient = batch_normalization_gradient_of_data_op(
  78. bn_gradient_node, self.inputs[0], ctx=self.raw_ctx)
  79. scale_gradient = batch_normalization_gradient_of_scale_op(
  80. bn_gradient_node, self.inputs[1], ctx=self.raw_ctx)
  81. bias_gradient = batch_normalization_gradient_of_bias_op(
  82. bn_gradient_node, self.inputs[2], ctx=self.raw_ctx)
  83. return [data_gradient, scale_gradient, bias_gradient]
  84. def infer_shape(self, input_shapes):
  85. return input_shapes[0]
  86. class Batch_Normalization_GradientOp(Op):
  87. def __init__(self, out_gradient, in_node, bn_scale, forward_node, eps, ctx=None):
  88. super().__init__(Batch_Normalization_GradientOp,
  89. [out_gradient, in_node, bn_scale], ctx)
  90. self.tmp_gradient_in_arr = None
  91. self.tmp_gradient_bn_bias = None
  92. self.tmp_gradient_bn_scale = None
  93. self.forward_node = forward_node
  94. self.eps = eps
  95. def update_mean_and_var(self, saved_mean, saved_var):
  96. self.saved_mean = saved_mean
  97. self.saved_var = saved_var
  98. def compute(self, input_vals, output_val, stream_handle=None):
  99. if self.on_cpu:
  100. if DNNL_LIB['DnnlBatchNorm_Gradient']:
  101. if self.tmp_gradient_bn_bias is None:
  102. shapebn = input_vals[2].shape
  103. self.tmp_gradient_bn_bias = np.zeros(
  104. shape=shapebn, dtype=np.float32)
  105. self.tmp_gradient_bn_scale = np.zeros(
  106. shape=shapebn, dtype=np.float32)
  107. self.tmp_gradient_in_arr = np.zeros(
  108. shape=input_vals[1].shape, dtype=np.float32)
  109. cpu_batch_norm_gradient(input_vals[0], input_vals[1], input_vals[2], bn_bias, self.tmp_gradient_in_arr,
  110. self.tmp_gradient_bn_scale,
  111. self.tmp_gradient_bn_bias, self.forward_node.running_mean,
  112. self.forward_node.running_var, self.eps)
  113. else:
  114. if self.tmp_gradient_bn_bias is None:
  115. typebn = input_vals[2].asnumpy().dtype
  116. shapebn = input_vals[2].asnumpy().shape
  117. self.tmp_gradient_bn_bias = np.zeros(
  118. shape=shapebn, dtype=typebn)
  119. self.tmp_gradient_bn_scale = np.zeros(
  120. shape=shapebn, dtype=typebn)
  121. self.tmp_gradient_in_arr, self.tmp_gradient_bn_scale, self.tmp_gradient_bn_bias = batchnorm_backward(
  122. input_vals[0].asnumpy(), input_vals[1].asnumpy(
  123. ), input_vals[2].asnumpy(),
  124. self.tmp_gradient_bn_scale, self.tmp_gradient_bn_bias,
  125. self.eps, self.forward_node.save_mean, self.forward_node.save_var)
  126. else:
  127. if self.tmp_gradient_bn_bias == None:
  128. shapebn = input_vals[2].shape
  129. self.tmp_gradient_bn_scale = ndarray.empty(
  130. shape=shapebn, ctx=input_vals[0].ctx)
  131. self.tmp_gradient_bn_bias = ndarray.empty(
  132. shape=shapebn, ctx=input_vals[0].ctx)
  133. self.tmp_gradient_in_arr = ndarray.empty(
  134. shape=input_vals[1].shape, ctx=input_vals[0].ctx)
  135. CuDNN_Batch_Normalization_gradient(input_vals[0], input_vals[1], input_vals[2],
  136. self.tmp_gradient_in_arr, self.tmp_gradient_bn_scale,
  137. self.tmp_gradient_bn_bias, self.forward_node.save_mean,
  138. self.forward_node.save_var, self.eps, stream_handle)
  139. def gradient(self, output_grad):
  140. raise NotImplementedError
  141. def infer_shape(self, input_shapes):
  142. return (1,)
  143. class Batch_Normalization_Gradient_of_DataOp(Op):
  144. def __init__(self, bn_gradient, in_arr, ctx=None):
  145. super().__init__(Batch_Normalization_Gradient_of_DataOp,
  146. [bn_gradient, in_arr], ctx)
  147. def compute(self, input_vals, output_val, stream_handle=None):
  148. if self.on_cpu:
  149. output_val[:] = self.inputs[0].tmp_gradient_in_arr
  150. else:
  151. self.inputs[0].tmp_gradient_in_arr.copyto(output_val)
  152. def gradient(self, output_grad):
  153. raise NotImplementedError
  154. def infer_shape(self, input_shapes):
  155. return input_shapes[1]
  156. class Batch_Normalization_Gradient_of_ScaleOp(Op):
  157. def __init__(self, bn_gradient, in_scale, ctx=None):
  158. super().__init__(Batch_Normalization_Gradient_of_ScaleOp,
  159. [bn_gradient, in_scale], ctx)
  160. def compute(self, input_vals, output_val, stream_handle=None):
  161. if self.on_cpu:
  162. output_val[:] = self.inputs[0].tmp_gradient_bn_scale
  163. else:
  164. self.inputs[0].tmp_gradient_bn_scale.copyto(output_val)
  165. def gradient(self, output_grad):
  166. raise NotImplementedError
  167. def infer_shape(self, input_shapes):
  168. return input_shapes[1]
  169. class Batch_Normalization_Gradient_of_BiasOp(Op):
  170. def __init__(self, bn_gradient, in_bias, ctx=None):
  171. super().__init__(Batch_Normalization_Gradient_of_BiasOp,
  172. [bn_gradient, in_bias], ctx)
  173. def compute(self, input_vals, output_val, stream_handle=None):
  174. if self.on_cpu:
  175. output_val[:] = self.inputs[0].tmp_gradient_bn_bias
  176. else:
  177. self.inputs[0].tmp_gradient_bn_bias.copyto(output_val)
  178. def gradient(self, output_grad):
  179. raise NotImplementedError
  180. def infer_shape(self, input_shapes):
  181. return input_shapes[1]
  182. def batch_normalization_op(node_in, bn_scale, bn_bias, momentum=0.99, eps=0.01, ctx=None):
  183. """Batch normalization layer node.
  184. Parameters:
  185. ----
  186. node_in : Node
  187. Input data.
  188. bn_scale : float
  189. scaling parameter
  190. bn_bias :
  191. learnable bias parameter
  192. momentum : float
  193. Acting on the calculation of mean and variance, the mean and variance values in historical batch are retained.
  194. eps : float
  195. Epsilon value for numerical stability.
  196. Returns:
  197. ----
  198. A new Node instance created by Op.
  199. """
  200. return Batch_NormalizationOp(node_in, bn_scale, bn_bias, momentum, eps, ctx=ctx)
  201. def batch_normalization_gradient_op(out_gradient, in_node, bn_scale, forward_node, eps, ctx=None):
  202. """Gradient node of batch normalization.
  203. Parameters:
  204. ----
  205. out_gradient :
  206. The gradient array.
  207. in_node : Node
  208. Input node of bn layer.
  209. bn_scale :
  210. Scaling parameter.
  211. Returns:
  212. ----
  213. A new Node instance created by Op.
  214. """
  215. return Batch_Normalization_GradientOp(out_gradient, in_node, bn_scale, forward_node, eps, ctx=ctx)
  216. def batch_normalization_gradient_of_data_op(bn_gradient, in_arr, ctx=None):
  217. """Gradient node of data of batch normalization.
  218. Parameters:
  219. ----
  220. bn_gradient :
  221. The gradient array.
  222. in_arr : Node
  223. Input array of bn layer.
  224. Returns:
  225. ----
  226. A new Node instance created by Op.
  227. """
  228. return Batch_Normalization_Gradient_of_DataOp(bn_gradient, in_arr, ctx=ctx)
  229. def batch_normalization_gradient_of_scale_op(bn_gradient, in_scale, ctx=None):
  230. """Gradient node of scale parameter of batch normalization.
  231. Parameters:
  232. ----
  233. bn_gradient :
  234. The gradient array.
  235. in_scale :
  236. Scaling parameter of bn layer.
  237. Returns:
  238. ----
  239. A new Node instance created by Op.
  240. """
  241. return Batch_Normalization_Gradient_of_ScaleOp(bn_gradient, in_scale, ctx=ctx)
  242. def batch_normalization_gradient_of_bias_op(bn_gradient, in_bias, ctx=None):
  243. """Gradient node of bias parameter of batch normalization.
  244. Parameters:
  245. ----
  246. bn_gradient :
  247. The gradient array.
  248. in_bias :
  249. Bias parameter of bn layer.
  250. Returns:
  251. ----
  252. A new Node instance created by Op.
  253. """
  254. return Batch_Normalization_Gradient_of_BiasOp(bn_gradient, in_bias, ctx=ctx)
  255. def batchnorm_forward(x, bn_scale, bn_bias, save_mean, save_var, momentum=0.99, eps=0.01):
  256. D = x.shape[1]
  257. if save_mean is None:
  258. save_mean = np.zeros(D, dtype=x.dtype)
  259. if save_var is None:
  260. save_var = np.ones(D, dtype=x.dtype)
  261. sample_mean = x.mean(axis=(0, 2, 3), dtype=x.dtype)
  262. sample_var = x.var(axis=(0, 2, 3), dtype=x.dtype)
  263. save_mean = momentum * sample_mean + (1.0 - momentum) * save_mean
  264. save_var = momentum * sample_var + (1.0 - momentum) * save_var
  265. std = np.sqrt(sample_var.reshape(1, D, 1, 1) + eps, dtype=x.dtype)
  266. x_centered = x - sample_mean.reshape(1, D, 1, 1)
  267. x_norm = x_centered / std
  268. out = bn_scale.reshape(1, D, 1, 1) * x_norm + bn_bias.reshape(1, D, 1, 1)
  269. return out, save_mean, save_mean
  270. def batchnorm_inference(x, bn_scale, bn_bias, save_mean, save_var, eps=0.01):
  271. D = x.shape[1]
  272. std = np.sqrt(save_var.reshape(1, D, 1, 1) + eps, dtype=x.dtype)
  273. x_centered = x - save_mean.reshape(1, D, 1, 1)
  274. x_norm = x_centered / std
  275. out = bn_scale.reshape(1, D, 1, 1) * x_norm + bn_bias.reshape(1, D, 1, 1)
  276. return out
  277. def batchnorm_backward(gradient_Y, x, bn_scale, dbn_scale, dbn_bias, eps, save_mean, save_var):
  278. D = gradient_Y.shape[1]
  279. sample_mean = save_mean
  280. sample_var = save_var
  281. std = np.sqrt(sample_var.reshape(1, D, 1, 1) + eps)
  282. x_centered = x - sample_mean.reshape(1, D, 1, 1)
  283. x_norm = x_centered / std
  284. dbn_scale = (gradient_Y * x_norm).sum(axis=(0, 2, 3))
  285. dbn_bias = gradient_Y.sum(axis=(0, 2, 3))
  286. dx_norm = gradient_Y * bn_scale.reshape(1, D, 1, 1)
  287. dx_centered = dx_norm / std
  288. dmean = -(dx_centered.sum(axis=(0, 2, 3)) + 2 / D *
  289. x_centered.sum(axis=(0, 2, 3))).reshape(1, D, 1, 1)
  290. dstd = (dx_norm * x_centered * -std ** (-2)
  291. ).sum(axis=(0, 2, 3)).reshape(1, D, 1, 1)
  292. dvar = dstd / 2 / std
  293. dx = dx_centered + (dmean + dvar * 2 * x_centered) / D
  294. return dx, dbn_scale, dbn_bias