gx
chenyc
2025-06-12 7b72ac13a83764a662159d4a49b7fffb90476ecb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
 * @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('SGDOptimizer', ALL_ENVS, () => {
    it('basic', async () => {
        const initialTensors = tf.memory().numTensors;
        const learningRate = .1;
        const optimizer = tf.train.sgd(learningRate);
        const x = tf.scalar(4).variable();
        let numTensors = tf.memory().numTensors;
        let cost = optimizer.minimize(() => x.square(), /* returnCost */ true);
        // Cost should be the only additional arrays.
        expect(tf.memory().numTensors).toBe(numTensors + 1);
        // de/dx = 2x
        const expectedValue1 = -2 * 4 * learningRate + 4;
        expectArraysClose(await x.data(), [expectedValue1]);
        expectArraysClose(await cost.data(), [Math.pow(4, 2)]);
        cost.dispose();
        numTensors = tf.memory().numTensors;
        cost = optimizer.minimize(() => x.square(), /* returnCost */ false);
        // There should be no new additional Tensors.
        expect(tf.memory().numTensors).toBe(numTensors);
        const expectedValue2 = -2 * expectedValue1 * learningRate + expectedValue1;
        expectArraysClose(await x.data(), [expectedValue2]);
        expect(cost).toBe(null);
        optimizer.dispose();
        x.dispose();
        // The only additional tensor remaining is the argument to variable().
        expect(tf.memory().numTensors).toBe(initialTensors + 1);
    });
    it('Set and get weights: empty', async () => {
        const x = tf.scalar(4).variable();
        const learningRate = .1;
        const optimizer1 = tf.train.sgd(learningRate);
        let weights = await optimizer1.getWeights();
        expect(optimizer1.iterations).toEqual(0);
        optimizer1.minimize(() => x.square());
        weights = await optimizer1.getWeights();
        expect(optimizer1.iterations).toEqual(1);
        expect(weights.length).toEqual(1);
        expect(weights[0].name).toEqual('iter');
        expectArraysClose(await weights[0].tensor.data(), 1);
        const optimizer2 = tf.train.sgd(learningRate);
        await optimizer2.setWeights(weights);
        optimizer2.minimize(() => x.square());
        expectArraysClose(await x.data(), 2.56);
        expect(optimizer2.iterations).toEqual(2);
        const optimizer3 = tf.train.sgd(learningRate);
        await optimizer3.setWeights(await optimizer2.getWeights());
        optimizer3.minimize(() => x.square());
        expectArraysClose(await x.data(), 2.048);
        expect(optimizer3.iterations).toEqual(3);
    });
    it('serialization round-trip', () => {
        const learningRate = .1;
        const originalOpt = tf.train.sgd(learningRate);
        const reserialized = tf.SGDOptimizer.fromConfig(tf.SGDOptimizer, originalOpt.getConfig());
        expect(reserialized.getConfig()).toEqual(originalOpt.getConfig());
    });
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2dkX29wdGltaXplcl90ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vdGZqcy1jb3JlL3NyYy9vcHRpbWl6ZXJzL3NnZF9vcHRpbWl6ZXJfdGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFFSCxPQUFPLEtBQUssRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUMvQixPQUFPLEVBQUMsUUFBUSxFQUFFLGlCQUFpQixFQUFDLE1BQU0saUJBQWlCLENBQUM7QUFDNUQsT0FBTyxFQUFDLGlCQUFpQixFQUFDLE1BQU0sY0FBYyxDQUFDO0FBRS9DLGlCQUFpQixDQUFDLGNBQWMsRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFO0lBQy9DLEVBQUUsQ0FBQyxPQUFPLEVBQUUsS0FBSyxJQUFJLEVBQUU7UUFDckIsTUFBTSxjQUFjLEdBQUcsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDLFVBQVUsQ0FBQztRQUM5QyxNQUFNLFlBQVksR0FBRyxFQUFFLENBQUM7UUFDeEIsTUFBTSxTQUFTLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsWUFBWSxDQUFDLENBQUM7UUFFN0MsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUVsQyxJQUFJLFVBQVUsR0FBRyxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUMsVUFBVSxDQUFDO1FBRXhDLElBQUksSUFBSSxHQUFHLFNBQVMsQ0FBQyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDO1FBRXZFLDZDQUE2QztRQUM3QyxNQUFNLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDLFVBQVUsQ0FBQyxDQUFDLElBQUksQ0FBQyxVQUFVLEdBQUcsQ0FBQyxDQUFDLENBQUM7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dGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgMjAxOCBHb29nbGUgTExDLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICovXG5cbmltcG9ydCAqIGFzIHRmIGZyb20gJy4uL2luZGV4JztcbmltcG9ydCB7QUxMX0VOVlMsIGRlc2NyaWJlV2l0aEZsYWdzfSBmcm9tICcuLi9qYXNtaW5lX3V0aWwnO1xuaW1wb3J0IHtleHBlY3RBcnJheXNDbG9zZX0gZnJvbSAnLi4vdGVzdF91dGlsJztcblxuZGVzY3JpYmVXaXRoRmxhZ3MoJ1NHRE9wdGltaXplcicsIEFMTF9FTlZTLCAoKSA9PiB7XG4gIGl0KCdiYXNpYycsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBpbml0aWFsVGVuc29ycyA9IHRmLm1lbW9yeSgpLm51bVRlbnNvcnM7XG4gICAgY29uc3QgbGVhcm5pbmdSYXRlID0gLjE7XG4gICAgY29uc3Qgb3B0aW1pemVyID0gdGYudHJhaW4uc2dkKGxlYXJuaW5nUmF0ZSk7XG5cbiAgICBjb25zdCB4ID0gdGYuc2NhbGFyKDQpLnZhcmlhYmxlKCk7XG5cbiAgICBsZXQgbnVtVGVuc29ycyA9IHRmLm1lbW9yeSgpLm51bVRlbnNvcnM7XG5cbiAgICBsZXQgY29zdCA9IG9wdGltaXplci5taW5pbWl6ZSgoKSA9PiB4LnNxdWFyZSgpLCAvKiByZXR1cm5Db3N0ICovIHRydWUpO1xuXG4gICAgLy8gQ29zdCBzaG91bGQgYmUgdGhlIG9ubHkgYWRkaXRpb25hbCBhcnJheXMuXG4gICAgZXhwZWN0KHRmLm1lbW9yeSgpLm51bVRlbnNvcnMpLnRvQmUobnVtVGVuc29ycyArIDEpO1xuXG4gICAgLy8gZGUvZHggPSAyeFxuICAgIGNvbnN0IGV4cGVjdGVkVmFsdWUxID0gLTIgKiA0ICogbGVhcm5pbmdSYXRlICsgNDtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCB4LmRhdGEoKSwgW2V4cGVjdGVkVmFsdWUxXSk7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgY29zdC5kYXRhKCksIFtNYXRoLnBvdyg0LCAyKV0pO1xuXG4gICAgY29zdC5kaXNwb3NlKCk7XG4gICAgbnVtVGVuc29ycyA9IHRmLm1lbW9yeSgpLm51bVRlbnNvcnM7XG5cbiAgICBjb3N0ID0gb3B0aW1pemVyLm1pbmltaXplKCgpID0+IHguc3F1YXJlKCksIC8qIHJldHVybkNvc3QgKi8gZmFsc2UpO1xuICAgIC8vIFRoZXJlIHNob3VsZCBiZSBubyBuZXcgYWRkaXRpb25hbCBUZW5zb3JzLlxuICAgIGV4cGVjdCh0Zi5tZW1vcnkoKS5udW1UZW5zb3JzKS50b0JlKG51bVRlbnNvcnMpO1xuXG4gICAgY29uc3QgZXhwZWN0ZWRWYWx1ZTIgPSAtMiAqIGV4cGVjdGVkVmFsdWUxICogbGVhcm5pbmdSYXRlICsgZXhwZWN0ZWRWYWx1ZTE7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgeC5kYXRhKCksIFtleHBlY3RlZFZhbHVlMl0pO1xuICAgIGV4cGVjdChjb3N0KS50b0JlKG51bGwpO1xuXG4gICAgb3B0aW1pemVyLmRpc3Bvc2UoKTtcbiAgICB4LmRpc3Bvc2UoKTtcbiAgICAvLyBUaGUgb25seSBhZGRpdGlvbmFsIHRlbnNvciByZW1haW5pbmcgaXMgdGhlIGFyZ3VtZW50IHRvIHZhcmlhYmxlKCkuXG4gICAgZXhwZWN0KHRmLm1lbW9yeSgpLm51bVRlbnNvcnMpLnRvQmUoaW5pdGlhbFRlbnNvcnMgKyAxKTtcbiAgfSk7XG5cbiAgaXQoJ1NldCBhbmQgZ2V0IHdlaWdodHM6IGVtcHR5JywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IHggPSB0Zi5zY2FsYXIoNCkudmFyaWFibGUoKTtcblxuICAgIGNvbnN0IGxlYXJuaW5nUmF0ZSA9IC4xO1xuICAgIGNvbnN0IG9wdGltaXplcjEgPSB0Zi50cmFpbi5zZ2QobGVhcm5pbmdSYXRlKTtcblxuICAgIGxldCB3ZWlnaHRzID0gYXdhaXQgb3B0aW1pemVyMS5nZXRXZWlnaHRzKCk7XG4gICAgZXhwZWN0KG9wdGltaXplcjEuaXRlcmF0aW9ucykudG9FcXVhbCgwKTtcblxuICAgIG9wdGltaXplcjEubWluaW1pemUoKCkgPT4geC5zcXVhcmUoKSk7XG5cbiAgICB3ZWlnaHRzID0gYXdhaXQgb3B0aW1pemVyMS5nZXRXZWlnaHRzKCk7XG4gICAgZXhwZWN0KG9wdGltaXplcjEuaXRlcmF0aW9ucykudG9FcXVhbCgxKTtcbiAgICBleHBlY3Qod2VpZ2h0cy5sZW5ndGgpLnRvRXF1YWwoMSk7XG4gICAgZXhwZWN0KHdlaWdodHNbMF0ubmFtZSkudG9FcXVhbCgnaXRlcicpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IHdlaWdodHNbMF0udGVuc29yLmRhdGEoKSwgMSk7XG5cbiAgICBjb25zdCBvcHRpbWl6ZXIyID0gdGYudHJhaW4uc2dkKGxlYXJuaW5nUmF0ZSk7XG4gICAgYXdhaXQgb3B0aW1pemVyMi5zZXRXZWlnaHRzKHdlaWdodHMpO1xuICAgIG9wdGltaXplcjIubWluaW1pemUoKCkgPT4geC5zcXVhcmUoKSk7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgeC5kYXRhKCksIDIuNTYpO1xuICAgIGV4cGVjdChvcHRpbWl6ZXIyLml0ZXJhdGlvbnMpLnRvRXF1YWwoMik7XG5cbiAgICBjb25zdCBvcHRpbWl6ZXIzID0gdGYudHJhaW4uc2dkKGxlYXJuaW5nUmF0ZSk7XG4gICAgYXdhaXQgb3B0aW1pemVyMy5zZXRXZWlnaHRzKGF3YWl0IG9wdGltaXplcjIuZ2V0V2VpZ2h0cygpKTtcbiAgICBvcHRpbWl6ZXIzLm1pbmltaXplKCgpID0+IHguc3F1YXJlKCkpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IHguZGF0YSgpLCAyLjA0OCk7XG4gICAgZXhwZWN0KG9wdGltaXplcjMuaXRlcmF0aW9ucykudG9FcXVhbCgzKTtcbiAgfSk7XG5cbiAgaXQoJ3NlcmlhbGl6YXRpb24gcm91bmQtdHJpcCcsICgpID0+IHtcbiAgICBjb25zdCBsZWFybmluZ1JhdGUgPSAuMTtcbiAgICBjb25zdCBvcmlnaW5hbE9wdCA9IHRmLnRyYWluLnNnZChsZWFybmluZ1JhdGUpO1xuICAgIGNvbnN0IHJlc2VyaWFsaXplZCA9XG4gICAgICAgIHRmLlNHRE9wdGltaXplci5mcm9tQ29uZmlnKHRmLlNHRE9wdGltaXplciwgb3JpZ2luYWxPcHQuZ2V0Q29uZmlnKCkpO1xuICAgIGV4cGVjdChyZXNlcmlhbGl6ZWQuZ2V0Q29uZmlnKCkpLnRvRXF1YWwob3JpZ2luYWxPcHQuZ2V0Q29uZmlnKCkpO1xuICB9KTtcbn0pO1xuIl19