|
17 | 17 | 'BatchNorm2d',
|
18 | 18 | 'BatchNorm3d',
|
19 | 19 | 'InstanceNorm',
|
| 20 | + 'InstanceNorm1d', |
| 21 | + 'InstanceNorm2d', |
| 22 | + 'InstanceNorm3d', |
20 | 23 | 'LayerNorm',
|
21 | 24 | 'GroupNorm',
|
22 | 25 | 'SwitchNorm',
|
@@ -259,7 +262,7 @@ def build(self, inputs_shape):
|
259 | 262 | self.moving_var = self._get_weights("moving_var", shape=params_shape, init=self.moving_var_init)
|
260 | 263 |
|
261 | 264 | def forward(self, inputs):
|
262 |
| - mean, var = tf.nn.moments(inputs, self.axes) |
| 265 | + mean, var = tf.nn.moments(inputs, self.axes, keepdims=True) |
263 | 266 | if self.is_train:
|
264 | 267 | # update moving_mean and moving_var
|
265 | 268 | self.moving_mean = moving_averages.assign_moving_average(
|
@@ -388,81 +391,225 @@ def _get_param_shape(self, inputs_shape):
|
388 | 391 |
|
389 | 392 |
|
390 | 393 | class InstanceNorm(Layer):
|
391 |
| - """The :class:`InstanceNorm` class is a for instance normalization. |
| 394 | + """ |
| 395 | + The :class:`InstanceNorm` is an instance normalization layer for both fully-connected and convolution outputs. |
| 396 | + See ``tf.nn.batch_normalization`` and ``tf.nn.moments``. |
392 | 397 |
|
393 | 398 | Parameters
|
394 | 399 | -----------
|
395 | 400 | act : activation function.
|
396 | 401 | The activation function of this layer.
|
397 | 402 | epsilon : float
|
398 | 403 | Eplison.
|
| 404 | + beta_init : initializer or None |
| 405 | + The initializer for initializing beta, if None, skip beta. |
| 406 | + Usually you should not skip beta unless you know what happened. |
| 407 | + gamma_init : initializer or None |
| 408 | + The initializer for initializing gamma, if None, skip gamma. |
| 409 | + When the instance normalization layer is use instead of 'biases', or the next layer is linear, this can be |
| 410 | + disabled since the scaling can be done by the next layer. see `Inception-ResNet-v2 <https://github.com/tensorflow/models/blob/master/research/slim/nets/inception_resnet_v2.py>`__ |
| 411 | + num_features: int |
| 412 | + Number of features for input tensor. Useful to build layer if using InstanceNorm1d, InstanceNorm2d or InstanceNorm3d, |
| 413 | + but should be left as None if using InstanceNorm. Default None. |
| 414 | + data_format : str |
| 415 | + channels_last 'channel_last' (default) or channels_first. |
399 | 416 | name : None or str
|
400 |
| - A unique layer name |
| 417 | + A unique layer name. |
| 418 | +
|
| 419 | +
|
| 420 | + Examples |
| 421 | + --------- |
| 422 | + With TensorLayer |
| 423 | +
|
| 424 | + >>> net = tl.layers.Input([None, 50, 50, 32], name='input') |
| 425 | + >>> net = tl.layers.InstanceNorm()(net) |
401 | 426 |
|
| 427 | + Notes |
| 428 | + ----- |
| 429 | + The :class:`InstanceNorm` is universally suitable for 3D/4D/5D input in static model, but should not be used |
| 430 | + in dynamic model where layer is built upon class initialization. So the argument 'num_features' should only be used |
| 431 | + for subclasses :class:`InstanceNorm1d`, :class:`InstanceNorm2d` and :class:`InstanceNorm3d`. All the three subclasses are |
| 432 | + suitable under all kinds of conditions. |
402 | 433 | """
|
403 | 434 |
|
404 | 435 | def __init__(
|
405 |
| - self, |
406 |
| - act=None, |
407 |
| - epsilon=1e-5, |
408 |
| - name=None, #'instan_norm', |
| 436 | + self, act=None, epsilon=0.00001, beta_init=tl.initializers.zeros(), |
| 437 | + gamma_init=tl.initializers.random_normal(mean=1.0, stddev=0.002), num_features=None, |
| 438 | + data_format='channels_last', name=None |
409 | 439 | ):
|
410 |
| - # super(InstanceNorm, self).__init__(prev_layer=prev_layer, act=act, name=name) |
411 |
| - super().__init__(name) |
| 440 | + super(InstanceNorm, self).__init__(name=name) |
412 | 441 | self.act = act
|
413 | 442 | self.epsilon = epsilon
|
| 443 | + self.beta_init = beta_init |
| 444 | + self.gamma_init = gamma_init |
| 445 | + self.num_features = num_features |
| 446 | + self.data_format = data_format |
| 447 | + |
| 448 | + if num_features is not None: |
| 449 | + if not isinstance(self, InstanceNorm1d) and not isinstance(self, InstanceNorm2d) and not isinstance( |
| 450 | + self, InstanceNorm3d): |
| 451 | + raise ValueError( |
| 452 | + "Please use InstanceNorm1d or InstanceNorm2d or InstanceNorm3d instead of InstanceNorm " |
| 453 | + "if you want to specify 'num_features'." |
| 454 | + ) |
| 455 | + self.build(None) |
| 456 | + self._built = True |
414 | 457 |
|
415 | 458 | logging.info(
|
416 |
| - "InstanceNorm %s: epsilon: %f act: %s" % |
| 459 | + "InstanceNorm %s: epsilon: %f act: %s " % |
417 | 460 | (self.name, epsilon, self.act.__name__ if self.act is not None else 'No Activation')
|
418 | 461 | )
|
419 | 462 |
|
| 463 | + def __repr__(self): |
| 464 | + actstr = self.act.__name__ if self.act is not None else 'No Activation' |
| 465 | + s = '{classname}(num_features=num_features, epsilon={epsilon}' + actstr |
| 466 | + if self.name is not None: |
| 467 | + s += ', name="{name}"' |
| 468 | + s += ')' |
| 469 | + return s.format(classname=self.__class__.__name__, **self.__dict__) |
| 470 | + |
| 471 | + def _get_param_shape(self, inputs_shape): |
| 472 | + if self.data_format == 'channels_last': |
| 473 | + axis = len(inputs_shape) - 1 |
| 474 | + elif self.data_format == 'channels_first': |
| 475 | + axis = 1 |
| 476 | + else: |
| 477 | + raise ValueError('data_format should be either %s or %s' % ('channels_last', 'channels_first')) |
| 478 | + |
| 479 | + channels = inputs_shape[axis] |
| 480 | + params_shape = [1] * len(inputs_shape) |
| 481 | + params_shape[axis] = channels |
| 482 | + |
| 483 | + axes = [i for i in range(len(inputs_shape)) if i != 0 and i != axis] |
| 484 | + return params_shape, axes |
| 485 | + |
420 | 486 | def build(self, inputs_shape):
|
421 |
| - # self.scale = tf.compat.v1.get_variable( |
422 |
| - # self.name + '\scale', [inputs.get_shape()[-1]], |
423 |
| - # initializer=tf.compat.v1.initializers.truncated_normal(mean=1.0, stddev=0.02), dtype=LayersConfig.tf_dtype |
424 |
| - # ) |
425 |
| - self.scale = self._get_weights( |
426 |
| - "scale", shape=[inputs_shape[-1]], init=tf.compat.v1.initializers.truncated_normal(mean=1.0, stddev=0.02) |
427 |
| - ) |
428 |
| - # self.offset = tf.compat.v1.get_variable( |
429 |
| - # self.name + '\offset', [inputs.get_shape()[-1]], initializer=tf.compat.v1.initializers.constant(0.0), |
430 |
| - # dtype=LayersConfig.tf_dtype |
431 |
| - # ) |
432 |
| - self.offset = self._get_weights( |
433 |
| - "offset", shape=[inputs_shape[-1]], init=tf.compat.v1.initializers.constant(0.0) |
434 |
| - ) |
435 |
| - # self.add_weights([self.scale, self.offset]) |
| 487 | + params_shape, self.axes = self._get_param_shape(inputs_shape) |
| 488 | + |
| 489 | + self.beta, self.gamma = None, None |
| 490 | + if self.beta_init: |
| 491 | + self.beta = self._get_weights("beta", shape=params_shape, init=self.beta_init) |
| 492 | + |
| 493 | + if self.gamma_init: |
| 494 | + self.gamma = self._get_weights("gamma", shape=params_shape, init=self.gamma_init) |
436 | 495 |
|
437 | 496 | def forward(self, inputs):
|
| 497 | + mean, var = tf.nn.moments(inputs, self.axes, keepdims=True) |
| 498 | + outputs = batch_normalization(inputs, mean, var, self.beta, self.gamma, self.epsilon, self.data_format) |
| 499 | + if self.act: |
| 500 | + outputs = self.act(outputs) |
| 501 | + return outputs |
438 | 502 |
|
439 |
| - mean, var = tf.nn.moments(x=inputs, axes=[1, 2], keepdims=True) |
440 | 503 |
|
441 |
| - outputs = self.scale * tf.compat.v1.div(inputs - mean, tf.sqrt(var + self.epsilon)) + self.offset |
442 |
| - outputs = self.act(outputs) |
| 504 | +class InstanceNorm1d(InstanceNorm): |
| 505 | + """The :class:`InstanceNorm1d` applies Instance Normalization over 3D input (a mini-instance of 1D |
| 506 | + inputs with additional channel dimension), of shape (N, L, C) or (N, C, L). |
| 507 | + See more details in :class:`InstanceNorm`. |
| 508 | +
|
| 509 | + Examples |
| 510 | + --------- |
| 511 | + With TensorLayer |
| 512 | +
|
| 513 | + >>> # in static model, no need to specify num_features |
| 514 | + >>> net = tl.layers.Input([None, 50, 32], name='input') |
| 515 | + >>> net = tl.layers.InstanceNorm1d()(net) |
| 516 | + >>> # in dynamic model, build by specifying num_features |
| 517 | + >>> conv = tl.layers.Conv1d(32, 5, 1, in_channels=3) |
| 518 | + >>> bn = tl.layers.InstanceNorm1d(num_features=32) |
| 519 | +
|
| 520 | + """ |
| 521 | + |
| 522 | + def _get_param_shape(self, inputs_shape): |
| 523 | + if self.data_format == 'channels_last': |
| 524 | + axis = 2 |
| 525 | + elif self.data_format == 'channels_first': |
| 526 | + axis = 1 |
| 527 | + else: |
| 528 | + raise ValueError('data_format should be either %s or %s' % ('channels_last', 'channels_first')) |
| 529 | + |
| 530 | + if self.num_features is None: |
| 531 | + channels = inputs_shape[axis] |
| 532 | + else: |
| 533 | + channels = self.num_features |
| 534 | + params_shape = [1] * 3 |
| 535 | + params_shape[axis] = channels |
| 536 | + |
| 537 | + axes = [i for i in range(3) if i != 0 and i != axis] |
| 538 | + return params_shape, axes |
443 | 539 |
|
444 |
| - return outputs |
445 | 540 |
|
446 |
| - # with tf.variable_scope(name) as vs: |
447 |
| - # mean, var = tf.nn.moments(self.inputs, [1, 2], keep_dims=True) |
448 |
| - # |
449 |
| - # scale = tf.get_variable( |
450 |
| - # 'scale', [self.inputs.get_shape()[-1]], |
451 |
| - # initializer=tf.truncated_normal_initializer(mean=1.0, stddev=0.02), dtype=LayersConfig.tf_dtype |
452 |
| - # ) |
453 |
| - # |
454 |
| - # offset = tf.get_variable( |
455 |
| - # 'offset', [self.inputs.get_shape()[-1]], initializer=tf.constant_initializer(0.0), |
456 |
| - # dtype=LayersConfig.tf_dtype |
457 |
| - # ) |
458 |
| - # |
459 |
| - # self.outputs = scale * tf.div(self.inputs - mean, tf.sqrt(var + epsilon)) + offset |
460 |
| - # self.outputs = self._apply_activation(self.outputs) |
461 |
| - # |
462 |
| - # variables = tf.get_collection(TF_GRAPHKEYS_VARIABLES, scope=vs.name) |
463 |
| - # |
464 |
| - # self._add_layers(self.outputs) |
465 |
| - # self._add_params(variables) |
| 541 | +class InstanceNorm2d(InstanceNorm): |
| 542 | + """The :class:`InstanceNorm2d` applies Instance Normalization over 4D input (a mini-instance of 2D |
| 543 | + inputs with additional channel dimension) of shape (N, H, W, C) or (N, C, H, W). |
| 544 | + See more details in :class:`InstanceNorm`. |
| 545 | +
|
| 546 | + Examples |
| 547 | + --------- |
| 548 | + With TensorLayer |
| 549 | +
|
| 550 | + >>> # in static model, no need to specify num_features |
| 551 | + >>> net = tl.layers.Input([None, 50, 50, 32], name='input') |
| 552 | + >>> net = tl.layers.InstanceNorm2d()(net) |
| 553 | + >>> # in dynamic model, build by specifying num_features |
| 554 | + >>> conv = tl.layers.Conv2d(32, (5, 5), (1, 1), in_channels=3) |
| 555 | + >>> bn = tl.layers.InstanceNorm2d(num_features=32) |
| 556 | +
|
| 557 | + """ |
| 558 | + |
| 559 | + def _get_param_shape(self, inputs_shape): |
| 560 | + if self.data_format == 'channels_last': |
| 561 | + axis = 3 |
| 562 | + elif self.data_format == 'channels_first': |
| 563 | + axis = 1 |
| 564 | + else: |
| 565 | + raise ValueError('data_format should be either %s or %s' % ('channels_last', 'channels_first')) |
| 566 | + |
| 567 | + if self.num_features is None: |
| 568 | + channels = inputs_shape[axis] |
| 569 | + else: |
| 570 | + channels = self.num_features |
| 571 | + params_shape = [1] * 4 |
| 572 | + params_shape[axis] = channels |
| 573 | + |
| 574 | + axes = [i for i in range(4) if i != 0 and i != axis] |
| 575 | + return params_shape, axes |
| 576 | + |
| 577 | + |
| 578 | +class InstanceNorm3d(InstanceNorm): |
| 579 | + """The :class:`InstanceNorm3d` applies Instance Normalization over 5D input (a mini-instance of 3D |
| 580 | + inputs with additional channel dimension) with shape (N, D, H, W, C) or (N, C, D, H, W). |
| 581 | + See more details in :class:`InstanceNorm`. |
| 582 | +
|
| 583 | + Examples |
| 584 | + --------- |
| 585 | + With TensorLayer |
| 586 | +
|
| 587 | + >>> # in static model, no need to specify num_features |
| 588 | + >>> net = tl.layers.Input([None, 50, 50, 50, 32], name='input') |
| 589 | + >>> net = tl.layers.InstanceNorm3d()(net) |
| 590 | + >>> # in dynamic model, build by specifying num_features |
| 591 | + >>> conv = tl.layers.Conv3d(32, (5, 5, 5), (1, 1), in_channels=3) |
| 592 | + >>> bn = tl.layers.InstanceNorm3d(num_features=32) |
| 593 | +
|
| 594 | + """ |
| 595 | + |
| 596 | + def _get_param_shape(self, inputs_shape): |
| 597 | + if self.data_format == 'channels_last': |
| 598 | + axis = 4 |
| 599 | + elif self.data_format == 'channels_first': |
| 600 | + axis = 1 |
| 601 | + else: |
| 602 | + raise ValueError('data_format should be either %s or %s' % ('channels_last', 'channels_first')) |
| 603 | + |
| 604 | + if self.num_features is None: |
| 605 | + channels = inputs_shape[axis] |
| 606 | + else: |
| 607 | + channels = self.num_features |
| 608 | + params_shape = [1] * 5 |
| 609 | + params_shape[axis] = channels |
| 610 | + |
| 611 | + axes = [i for i in range(5) if i != 0 and i != axis] |
| 612 | + return params_shape, axes |
466 | 613 |
|
467 | 614 |
|
468 | 615 | # FIXME : not sure about the correctness, need testing
|
|
0 commit comments