/** * @license * Copyright 2018 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================= */ import * as tf from '../index'; import { ALL_ENVS, describeWithFlags } from '../jasmine_util'; import { expectArraysClose } from '../test_util'; describeWithFlags('MomentumOptimizer', ALL_ENVS, () => { it('basic', async () => { const learningRate = .1; const momentum = .5; const optimizer = tf.train.momentum(learningRate, momentum); const x = tf.tensor1d([1, 2]).variable(); const f = () => x.square().sum(); let numTensors = tf.memory().numTensors; let cost = optimizer.minimize(f, /* returnCost */ true); // Cost & velocity should be the only additional arrays. expect(tf.memory().numTensors).toBe(numTensors + 2); // newAccumulation = momentum * accumulation + gradient // newVariable += -learningRate * newAccumulation + variable // // de/dx = [2, 4] // newAccumulation = [2, 4] // x = [.8, 1.6] expectArraysClose(await x.data(), [.8, 1.6]); cost.dispose(); numTensors = tf.memory().numTensors; cost = optimizer.minimize(f, /* returnCost */ false); // de/dx = [1.6, 3.2] // accumulation = [2, 4] // newAccumulation = [2.6, 5.2] // x = [0.54, 1.08] expectArraysClose(await x.data(), [0.54, 1.08]); // There should be no new additional Tensors. expect(tf.memory().numTensors).toBe(numTensors); expect(cost).toBe(null); x.dispose(); numTensors = tf.memory().numTensors; optimizer.dispose(); // The optimizer.dispose() call should have disposed the m variable and the // momentum variable for x. expect(tf.memory().numTensors).toBe(numTensors - 2); }); it('basic - with Nesterov', async () => { const learningRate = .1; const momentum = .5; const useNesterov = true; const optimizer = tf.train.momentum(learningRate, momentum, useNesterov); const x = tf.tensor1d([1, 2]).variable(); const f = () => x.square().sum(); let numTensors = tf.memory().numTensors; let cost = optimizer.minimize(f, /* returnCost */ true); // Cost and velocity should be the only additional arrays. expect(tf.memory().numTensors).toBe(numTensors + 2); // newAccumulation = momentum * accumulation + gradient // newVariable = -learningRate * (newAccumulation * momentum + gradient) + // variable // // de/dx = [2, 4] // newAccumulation = [2, 4] // newVariable = -0.1 * ([2, 4] * 0.5 + [2, 4]) + [1, 2] // x = [.7, 1.4] expectArraysClose(await x.data(), [.7, 1.4]); cost.dispose(); numTensors = tf.memory().numTensors; cost = optimizer.minimize(f, /* returnCost */ false); // de/dx = [1.4, 2.8] // accumulation = [2, 4] // newAccumulation = [0.5 * 2 + 1.4, 0.5 * 4 + 2.8] = [2.4, 4.8] // newVariable = -0.1 * ([2.4, 4.8] * 0.5 + [1.4, 2.8]) + [0.7, 1.4] // x = [0.44, 0.88] expectArraysClose(await x.data(), [0.44, 0.88]); // There should be no new additional Tensors. expect(tf.memory().numTensors).toBe(numTensors); expect(cost).toBe(null); x.dispose(); numTensors = tf.memory().numTensors; optimizer.dispose(); // The optimizer.dispose() call should have disposed the m variable and the // momentum variable for x. expect(tf.memory().numTensors).toBe(numTensors - 2); }); it('Save, load weights and conntinue training', async () => { const learningRate = .1; const momentum = .5; const useNesterov = true; const optimizer1 = tf.train.momentum(learningRate, momentum, useNesterov); const x = tf.tensor1d([1, 2]).variable(); const f = () => x.square().sum(); let cost = optimizer1.minimize(f, /* returnCost */ true); // The iterations counter and the accumulation for the variable x. const optimizer2 = tf.train.momentum(learningRate, momentum, useNesterov); await optimizer2.setWeights(await optimizer1.getWeights()); cost = optimizer2.minimize(f, /* returnCost */ true); expectArraysClose(await cost.data(), 2.45); expectArraysClose(await x.data(), [0.44, 0.88]); expect(optimizer2.iterations).toEqual(2); }); it('serialization round-trip', () => { const originalOpt = tf.train.momentum(0.1, 0.2, true); const reserialized = tf.MomentumOptimizer.fromConfig(tf.MomentumOptimizer, originalOpt.getConfig()); expect(reserialized.getConfig()).toEqual(originalOpt.getConfig()); }); }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9tZW50dW1fb3B0aW1pemVyX3Rlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi90ZmpzLWNvcmUvc3JjL29wdGltaXplcnMvbW9tZW50dW1fb3B0aW1pemVyX3Rlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUgsT0FBTyxLQUFLLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDL0IsT0FBTyxFQUFDLFFBQVEsRUFBRSxpQkFBaUIsRUFBQyxNQUFNLGlCQUFpQixDQUFDO0FBQzVELE9BQU8sRUFBQyxpQkFBaUIsRUFBQyxNQUFNLGNBQWMsQ0FBQztBQUUvQyxpQkFBaUIsQ0FBQyxtQkFBbUIsRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFO0lBQ3BELEVBQUUsQ0FBQyxPQUFPLEVBQUUsS0FBSyxJQUFJLEVBQUU7UUFDckIsTUFBTSxZQUFZLEdBQUcsRUFBRSxDQUFDO1FBQ3hCLE1BQU0sUUFBUSxHQUFHLEVBQUUsQ0FBQztRQUNwQixNQUFNLFNBQVMsR0FBRyxFQUFFLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxZQUFZLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFFNUQsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBRXpDLE1BQU0sQ0FBQyxHQUFvQixHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBRyxFQUFFLENBQUM7UUFFbEQsSUFBSSxVQUFVLEdBQUcsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDLFVBQVUsQ0FBQztRQUV4QyxJQUFJLElBQUksR0FBRyxTQUFTLENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUV4RCx3REFBd0Q7UUFDeEQsTUFBTSxDQUFDLEVBQUUsQ0FBQyxN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TSxDQUFDLFlBQVksQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FBQztJQUNwRSxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMTggR29vZ2xlIExMQy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuXG5pbXBvcnQgKiBhcyB0ZiBmcm9tICcuLi9pbmRleCc7XG5pbXBvcnQge0FMTF9FTlZTLCBkZXNjcmliZVdpdGhGbGFnc30gZnJvbSAnLi4vamFzbWluZV91dGlsJztcbmltcG9ydCB7ZXhwZWN0QXJyYXlzQ2xvc2V9IGZyb20gJy4uL3Rlc3RfdXRpbCc7XG5cbmRlc2NyaWJlV2l0aEZsYWdzKCdNb21lbnR1bU9wdGltaXplcicsIEFMTF9FTlZTLCAoKSA9PiB7XG4gIGl0KCdiYXNpYycsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBsZWFybmluZ1JhdGUgPSAuMTtcbiAgICBjb25zdCBtb21lbnR1bSA9IC41O1xuICAgIGNvbnN0IG9wdGltaXplciA9IHRmLnRyYWluLm1vbWVudHVtKGxlYXJuaW5nUmF0ZSwgbW9tZW50dW0pO1xuXG4gICAgY29uc3QgeCA9IHRmLnRlbnNvcjFkKFsxLCAyXSkudmFyaWFibGUoKTtcblxuICAgIGNvbnN0IGY6ICgpID0+IHRmLlNjYWxhciA9ICgpID0+IHguc3F1YXJlKCkuc3VtKCk7XG5cbiAgICBsZXQgbnVtVGVuc29ycyA9IHRmLm1lbW9yeSgpLm51bVRlbnNvcnM7XG5cbiAgICBsZXQgY29zdCA9IG9wdGltaXplci5taW5pbWl6ZShmLCAvKiByZXR1cm5Db3N0ICovIHRydWUpO1xuXG4gICAgLy8gQ29zdCAmIHZlbG9jaXR5IHNob3VsZCBiZSB0aGUgb25seSBhZGRpdGlvbmFsIGFycmF5cy5cbiAgICBleHBlY3QodGYubWVtb3J5KCkubnVtVGVuc29ycykudG9CZShudW1UZW5zb3JzICsgMik7XG5cbiAgICAvLyBuZXdBY2N1bXVsYXRpb24gPSBtb21lbnR1bSAqIGFjY3VtdWxhdGlvbiArIGdyYWRpZW50XG4gICAgLy8gbmV3VmFyaWFibGUgKz0gLWxlYXJuaW5nUmF0ZSAqIG5ld0FjY3VtdWxhdGlvbiArIHZhcmlhYmxlXG4gICAgLy9cbiAgICAvLyBkZS9keCA9IFsyLCA0XVxuICAgIC8vIG5ld0FjY3VtdWxhdGlvbiA9IFsyLCA0XVxuICAgIC8vIHggPSBbLjgsIDEuNl1cbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCB4LmRhdGEoKSwgWy44LCAxLjZdKTtcblxuICAgIGNvc3QuZGlzcG9zZSgpO1xuICAgIG51bVRlbnNvcnMgPSB0Zi5tZW1vcnkoKS5udW1UZW5zb3JzO1xuXG4gICAgY29zdCA9IG9wdGltaXplci5taW5pbWl6ZShmLCAvKiByZXR1cm5Db3N0ICovIGZhbHNlKTtcblxuICAgIC8vIGRlL2R4ID0gWzEuNiwgMy4yXVxuICAgIC8vIGFjY3VtdWxhdGlvbiA9IFsyLCA0XVxuICAgIC8vIG5ld0FjY3VtdWxhdGlvbiA9IFsyLjYsIDUuMl1cbiAgICAvLyB4ID0gWzAuNTQsIDEuMDhdXG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgeC5kYXRhKCksIFswLjU0LCAxLjA4XSk7XG5cbiAgICAvLyBUaGVyZSBzaG91bGQgYmUgbm8gbmV3IGFkZGl0aW9uYWwgVGVuc29ycy5cbiAgICBleHBlY3QodGYubWVtb3J5KCkubnVtVGVuc29ycykudG9CZShudW1UZW5zb3JzKTtcblxuICAgIGV4cGVjdChjb3N0KS50b0JlKG51bGwpO1xuXG4gICAgeC5kaXNwb3NlKCk7XG4gICAgbnVtVGVuc29ycyA9IHRmLm1lbW9yeSgpLm51bVRlbnNvcnM7XG4gICAgb3B0aW1pemVyLmRpc3Bvc2UoKTtcblxuICAgIC8vIFRoZSBvcHRpbWl6ZXIuZGlzcG9zZSgpIGNhbGwgc2hvdWxkIGhhdmUgZGlzcG9zZWQgdGhlIG0gdmFyaWFibGUgYW5kIHRoZVxuICAgIC8vIG1vbWVudHVtIHZhcmlhYmxlIGZvciB4LlxuICAgIGV4cGVjdCh0Zi5tZW1vcnkoKS5udW1UZW5zb3JzKS50b0JlKG51bVRlbnNvcnMgLSAyKTtcbiAgfSk7XG5cbiAgaXQoJ2Jhc2ljIC0gd2l0aCBOZXN0ZXJvdicsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBsZWFybmluZ1JhdGUgPSAuMTtcbiAgICBjb25zdCBtb21lbnR1bSA9IC41O1xuICAgIGNvbnN0IHVzZU5lc3Rlcm92ID0gdHJ1ZTtcbiAgICBjb25zdCBvcHRpbWl6ZXIgPSB0Zi50cmFpbi5tb21lbnR1bShsZWFybmluZ1JhdGUsIG1vbWVudHVtLCB1c2VOZXN0ZXJvdik7XG5cbiAgICBjb25zdCB4ID0gdGYudGVuc29yMWQoWzEsIDJdKS52YXJpYWJsZSgpO1xuXG4gICAgY29uc3QgZjogKCkgPT4gdGYuU2NhbGFyID0gKCkgPT4geC5zcXVhcmUoKS5zdW0oKTtcblxuICAgIGxldCBudW1UZW5zb3JzID0gdGYubWVtb3J5KCkubnVtVGVuc29ycztcblxuICAgIGxldCBjb3N0ID0gb3B0aW1pemVyLm1pbmltaXplKGYsIC8qIHJldHVybkNvc3QgKi8gdHJ1ZSk7XG5cbiAgICAvLyBDb3N0IGFuZCB2ZWxvY2l0eSBzaG91bGQgYmUgdGhlIG9ubHkgYWRkaXRpb25hbCBhcnJheXMuXG4gICAgZXhwZWN0KHRmLm1lbW9yeSgpLm51bVRlbnNvcnMpLnRvQmUobnVtVGVuc29ycyArIDIpO1xuXG4gICAgLy8gbmV3QWNjdW11bGF0aW9uID0gbW9tZW50dW0gKiBhY2N1bXVsYXRpb24gKyBncmFkaWVudFxuICAgIC8vIG5ld1ZhcmlhYmxlID0gLWxlYXJuaW5nUmF0ZSAqIChuZXdBY2N1bXVsYXRpb24gKiBtb21lbnR1bSArIGdyYWRpZW50KSArXG4gICAgLy8gICAgICAgICAgICAgICAgdmFyaWFibGVcbiAgICAvL1xuICAgIC8vIGRlL2R4ID0gWzIsIDRdXG4gICAgLy8gbmV3QWNjdW11bGF0aW9uID0gWzIsIDRdXG4gICAgLy8gbmV3VmFyaWFibGUgPSAtMC4xICogKFsyLCA0XSAqIDAuNSArIFsyLCA0XSkgKyBbMSwgMl1cbiAgICAvLyB4ID0gWy43LCAxLjRdXG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgeC5kYXRhKCksIFsuNywgMS40XSk7XG5cbiAgICBjb3N0LmRpc3Bvc2UoKTtcbiAgICBudW1UZW5zb3JzID0gdGYubWVtb3J5KCkubnVtVGVuc29ycztcblxuICAgIGNvc3QgPSBvcHRpbWl6ZXIubWluaW1pemUoZiwgLyogcmV0dXJuQ29zdCAqLyBmYWxzZSk7XG5cbiAgICAvLyBkZS9keCA9IFsxLjQsIDIuOF1cbiAgICAvLyBhY2N1bXVsYXRpb24gPSBbMiwgNF1cbiAgICAvLyBuZXdBY2N1bXVsYXRpb24gPSBbMC41ICogMiArIDEuNCwgMC41ICogNCArIDIuOF0gPSBbMi40LCA0LjhdXG4gICAgLy8gbmV3VmFyaWFibGUgPSAtMC4xICogKFsyLjQsIDQuOF0gKiAwLjUgKyBbMS40LCAyLjhdKSArIFswLjcsIDEuNF1cbiAgICAvLyB4ID0gWzAuNDQsIDAuODhdXG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgeC5kYXRhKCksIFswLjQ0LCAwLjg4XSk7XG5cbiAgICAvLyBUaGVyZSBzaG91bGQgYmUgbm8gbmV3IGFkZGl0aW9uYWwgVGVuc29ycy5cbiAgICBleHBlY3QodGYubWVtb3J5KCkubnVtVGVuc29ycykudG9CZShudW1UZW5zb3JzKTtcblxuICAgIGV4cGVjdChjb3N0KS50b0JlKG51bGwpO1xuXG4gICAgeC5kaXNwb3NlKCk7XG4gICAgbnVtVGVuc29ycyA9IHRmLm1lbW9yeSgpLm51bVRlbnNvcnM7XG4gICAgb3B0aW1pemVyLmRpc3Bvc2UoKTtcblxuICAgIC8vIFRoZSBvcHRpbWl6ZXIuZGlzcG9zZSgpIGNhbGwgc2hvdWxkIGhhdmUgZGlzcG9zZWQgdGhlIG0gdmFyaWFibGUgYW5kIHRoZVxuICAgIC8vIG1vbWVudHVtIHZhcmlhYmxlIGZvciB4LlxuICAgIGV4cGVjdCh0Zi5tZW1vcnkoKS5udW1UZW5zb3JzKS50b0JlKG51bVRlbnNvcnMgLSAyKTtcbiAgfSk7XG5cbiAgaXQoJ1NhdmUsIGxvYWQgd2VpZ2h0cyBhbmQgY29ubnRpbnVlIHRyYWluaW5nJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGxlYXJuaW5nUmF0ZSA9IC4xO1xuICAgIGNvbnN0IG1vbWVudHVtID0gLjU7XG4gICAgY29uc3QgdXNlTmVzdGVyb3YgPSB0cnVlO1xuICAgIGNvbnN0IG9wdGltaXplcjEgPSB0Zi50cmFpbi5tb21lbnR1bShsZWFybmluZ1JhdGUsIG1vbWVudHVtLCB1c2VOZXN0ZXJvdik7XG5cbiAgICBjb25zdCB4ID0gdGYudGVuc29yMWQoWzEsIDJdKS52YXJpYWJsZSgpO1xuICAgIGNvbnN0IGY6ICgpID0+IHRmLlNjYWxhciA9ICgpID0+IHguc3F1YXJlKCkuc3VtKCk7XG5cbiAgICBsZXQgY29zdCA9IG9wdGltaXplcjEubWluaW1pemUoZiwgLyogcmV0dXJuQ29zdCAqLyB0cnVlKTtcblxuICAgIC8vIFRoZSBpdGVyYXRpb25zIGNvdW50ZXIgYW5kIHRoZSBhY2N1bXVsYXRpb24gZm9yIHRoZSB2YXJpYWJsZSB4LlxuICAgIGNvbnN0IG9wdGltaXplcjIgPSB0Zi50cmFpbi5tb21lbnR1bShsZWFybmluZ1JhdGUsIG1vbWVudHVtLCB1c2VOZXN0ZXJvdik7XG4gICAgYXdhaXQgb3B0aW1pemVyMi5zZXRXZWlnaHRzKGF3YWl0IG9wdGltaXplcjEuZ2V0V2VpZ2h0cygpKTtcbiAgICBjb3N0ID0gb3B0aW1pemVyMi5taW5pbWl6ZShmLCAvKiByZXR1cm5Db3N0ICovIHRydWUpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IGNvc3QuZGF0YSgpLCAyLjQ1KTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCB4LmRhdGEoKSwgWzAuNDQsIDAuODhdKTtcbiAgICBleHBlY3Qob3B0aW1pemVyMi5pdGVyYXRpb25zKS50b0VxdWFsKDIpO1xuICB9KTtcblxuICBpdCgnc2VyaWFsaXphdGlvbiByb3VuZC10cmlwJywgKCkgPT4ge1xuICAgIGNvbnN0IG9yaWdpbmFsT3B0ID0gdGYudHJhaW4ubW9tZW50dW0oMC4xLCAwLjIsIHRydWUpO1xuICAgIGNvbnN0IHJlc2VyaWFsaXplZCA9IHRmLk1vbWVudHVtT3B0aW1pemVyLmZyb21Db25maWcoXG4gICAgICAgIHRmLk1vbWVudHVtT3B0aW1pemVyLCBvcmlnaW5hbE9wdC5nZXRDb25maWcoKSk7XG4gICAgZXhwZWN0KHJlc2VyaWFsaXplZC5nZXRDb25maWcoKSkudG9FcXVhbChvcmlnaW5hbE9wdC5nZXRDb25maWcoKSk7XG4gIH0pO1xufSk7XG4iXX0=