(inputs: Tensor|Tensor[], kwargs: Kwargs)
| 327 | } |
| 328 | |
| 329 | override call(inputs: Tensor|Tensor[], kwargs: Kwargs): Tensor|Tensor[] { |
| 330 | return tidy(() => { |
| 331 | const training = kwargs['training'] == null ? false : kwargs['training']; |
| 332 | const input = getExactlyOneTensor(inputs); |
| 333 | const inputShape = input.shape; |
| 334 | const ndim = inputShape.length; |
| 335 | const reductionAxes = math_utils.range(0, ndim); |
| 336 | const axis = this.axis >= 0 ? this.axis : (this.axis + ndim); |
| 337 | reductionAxes.splice(axis, 1); |
| 338 | const broadcastShape = generic_utils.pyListRepeat(1, ndim); |
| 339 | broadcastShape[axis] = inputShape[axis]; |
| 340 | |
| 341 | const sortedReductionAxes = reductionAxes.slice(); |
| 342 | sortedReductionAxes.sort(); |
| 343 | const needsBroadcasting = !util.arraysEqual( |
| 344 | sortedReductionAxes, math_utils.range(0, ndim).slice(0, ndim - 1)); |
| 345 | |
| 346 | const normalizeInference: () => Tensor = () => { |
| 347 | if (needsBroadcasting) { |
| 348 | const broadcastMovingMean = |
| 349 | reshape(this.movingMean.read(), broadcastShape); |
| 350 | const broadcastMovingVariance = |
| 351 | reshape(this.movingVariance.read(), broadcastShape); |
| 352 | const broadcastBeta = |
| 353 | this.center ? reshape(this.beta.read(), broadcastShape) : null; |
| 354 | const broadcastGamma = |
| 355 | this.scale ? reshape(this.gamma.read(), broadcastShape) : null; |
| 356 | return batchNormalization( |
| 357 | input, broadcastMovingMean, broadcastMovingVariance, |
| 358 | broadcastBeta, broadcastGamma, this.epsilon); |
| 359 | } else { |
| 360 | return batchNormalization( |
| 361 | input, this.movingMean.read(), this.movingVariance.read(), |
| 362 | this.beta == null ? null : this.beta.read(), |
| 363 | this.gamma == null ? null : this.gamma.read(), this.epsilon); |
| 364 | } |
| 365 | }; |
| 366 | |
| 367 | if (!training) { |
| 368 | return normalizeInference(); |
| 369 | } |
| 370 | |
| 371 | const [normedTraining, mean, variance] = normalizeBatchInTraining( |
| 372 | input, this.gamma.read(), this.beta.read(), reductionAxes, |
| 373 | this.epsilon); |
| 374 | |
| 375 | const doMovingAverage = |
| 376 | (variable: LayerVariable, value: Tensor, momentum: number): void => { |
| 377 | tfc.tidy(() => { |
| 378 | const decay = 1 - momentum; |
| 379 | const origValue = variable.read(); |
| 380 | const updateDelta = tfc.mul(tfc.sub(origValue, value), decay); |
| 381 | variable.write(tfc.sub(origValue, updateDelta)); |
| 382 | }); |
| 383 | }; |
| 384 | |
| 385 | // Perform updates to moving mean and moving variance for training. |
| 386 | // Porting Note: In PyKeras, these updates to `movingMean` and |
nothing calls this directly
no test coverage detected