Skip to content
This repository was archived by the owner on Nov 17, 2023. It is now read-only.

Commit dfddae1

Browse files
committed
Implemented a python SVRGModule for performing SVRG Optimization Logic. This version supports single machine SVRG with single cpu, gpu and multi-gpus.
1 parent e290623 commit dfddae1

16 files changed

Lines changed: 1745 additions & 2 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# SVRG Optimization in Python Module API
2+
3+
## Overview
4+
SVRG which stands for Stochastic Variance Reduced Gradients, is an optimization technique that was first introduced in
5+
paper _Accelerating Stochastic Gradient Descent using Predictive Variance Reduction_ in 2013. It is complement to SGD
6+
(Stochastic Gradient Descent), which is known for large scale optimization but suffers from slow convergence
7+
asymptotically due to its inherent variance. SGD approximates the full gradients using a small batch of data or
8+
a single data sample, which will introduce variance and thus requires to start with a small learning rate in order to
9+
ensure convergence. SVRG remedies the problem by keeping track of a version of estimated weights that close to the
10+
optimal parameter values and maintaining an average of full gradients over a full pass of data. The average of full
11+
gradients is calculated with respect to the weights from the last m-th epochs in the training. SVRG uses a different
12+
update rule: gradients w.r.t current parameter values minus gradients w.r.t to parameters from the last m-th epochs
13+
plus the average of full gradients over all data.
14+
15+
Key Characteristics of SVRG:
16+
* Employs explicit variance reduction by using a different update rule compared to SGD.
17+
* Ability to use relatively large learning rate, which leads to faster convergence compared to SGD.
18+
* Guarantees for fast convergence for smooth and strongly convex functions.
19+
20+
SVRG optimization is implemented as a SVRGModule in `mxnet.contrib.svrg_optimization`, which is an extension of the
21+
existing `mxnet.module.Module` APIs and encapsulates SVRG optimization logic within several new functions. SVRGModule
22+
API changes compared to Module API to end users are minimal.
23+
24+
In distributed training, each worker gets the same special weights from the last m-th epoch and calculates the full
25+
gradients with respect to its own shard of data. The standard SVRG optimization requires building a global full
26+
gradients, which is calculated by aggregating the full gradients from each worker and averaging over the number of
27+
workers. The workaround is to keep an additional set of keys in the KVStore that maps to full gradients.
28+
The `_SVRGOptimizer` is designed to wrap two optimizers, an `_AssignmentOptimizer` which is used for full gradients
29+
accumulation in the KVStore and a regular optimizer that performs actual update rule to the parameters.
30+
The `_SVRGOptimizer` and `_AssignmentOptimizer` are designed to be used in `SVRGModule` only.
31+
32+
```eval_rst
33+
.. warning:: This package contains experimental APIs and may change in the near future.
34+
```
35+
36+
This document lists the SVRGModule APIs in MXNet/Contrib package:
37+
38+
```eval_rst
39+
.. autosummary::
40+
:nosignatures:
41+
42+
mxnet.contrib.svrg_optimization.svrg_module
43+
```
44+
45+
### Intermediate Level API for SVRGModule
46+
47+
The only extra step to use a SVRGModule compared to use a Module is to check if the current epoch should update the
48+
full gradients over all data. Code snippets below demonstrate the suggested usage of SVRGModule using intermediate
49+
level APIs.
50+
51+
```python
52+
>>> mod = SVRGModule(symbol=model, update_freq=2, data_names=['data'], label_names=['lin_reg_label'])
53+
>>> mod.bind(data_shapes=di.provide_data, label_shapes=di.provide_label)
54+
>>> mod.init_params()
55+
>>> mod.init_optimizer(optimizer='sgd', optimizer_params=(('learning_rate', 0.01), ), kvstore='local')
56+
>>> for epoch in range(num_epochs):
57+
... if epoch % mod.update_freq == 0:
58+
... mod.update_full_grads(di)
59+
... di.reset()
60+
... for batch in di:
61+
... mod.forward_backward(data_batch=batch)
62+
... mod.update()
63+
```
64+
65+
### High Level API for SVRGModule
66+
67+
The high level API usage of SVRGModule remains exactly the same as Module API. Code snippets below gives an example of
68+
suggested usage of high level API.
69+
70+
```python
71+
>>> mod = SVRGModule(symbol=model, update_freq=2, data_names=['data'], label_names=['lin_reg_label'])
72+
>>> mod.fit(di, num_epochs=100, optimizer='sgd', optimizer_params=(('learning_rate', 0.01), ))
73+
```
74+
75+
## API reference
76+
77+
<script type="text/javascript" src='../../../_static/js/auto_module_index.js'></script>
78+
79+
```eval_rst
80+
81+
.. automodule:: mxnet.contrib.svrg_optimization.svrg_module
82+
.. autoclass:: mxnet.contrib.svrg_optimization.svrg_module.SVRGModule
83+
:members: init_optimizer, bind, forward, backward, reshape, update, update_full_grads, fit, prepare
84+
85+
```
86+
<script>auto_index("api-reference");</script>

docs/api/python/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ Code examples are placed throughout the API documentation and these can be run a
5252
contrib/contrib.md
5353
contrib/text.md
5454
contrib/onnx.md
55+
contrib/svrg_optimization.md
5556
```
5657

5758
## Gluon API
@@ -176,4 +177,4 @@ Code examples are placed throughout the API documentation and these can be run a
176177
:maxdepth: 1
177178
178179
symbol_in_pictures/symbol_in_pictures.md
179-
```
180+
```

docs/api/python/module/module.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,4 @@ additional functionality. We summarize them in this section.
207207
:members:
208208
```
209209

210-
<script>auto_index("api-reference");</script>
210+
<script>auto_index("api-reference");</script>

example/svrg_module/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
## SVRGModule Example
2+
SVRGModule is an extension to the Module API that implements SVRG optimization, which stands for Stochastic
3+
Variance Reduced Gradient. SVRG is an optimization technique that complements SGD and has several key
4+
properties:
5+
* Employs explicit variance reduction by using a different update rule compared to SGD.
6+
* Ability to use relatively large learning rate, which leads to faster convergence compared to SGD.
7+
* Guarantees for fast convergence for smooth and strongly convex functions.
8+
9+
#### API Usage Example
10+
SVRGModule provides both high-level and intermediate-level APIs while minimizing the changes with Module API.
11+
example_api_train.py: provides suggested usage of SVRGModule high-level and intermediate-level API.
12+
example_inference.py: provides example usage of SVRGModule inference.
13+
14+
#### Linear Regression
15+
This example trains a linear regression model using SVRGModule on a real dataset, YearPredictionMSD.
16+
Logs of the training results can be found in experiments.log which will automatically generated when running the
17+
training script.
18+
19+
##### Dataset
20+
YearPredictionMSD: contains predictions of the release year of a song from audio features. It has over
21+
400,000 samples with 90 features. Please uncomment data downloading script from data_reader.py to download the data.
22+
23+
#### Benchmarks:
24+
An initial set of benchmarks has been performed on YearPredictionDatasetMSD with linear regression model.
25+
26+
* benchmark1.py: A lr_scheduler returns a new learning rate based on the number of updates that have been performed.
27+
The training loss of SVRG is less than SGD with lr_scheduler over all of the 100 epochs.
28+
29+
* benchmark2.py: One drawback for SGD is that in order to converge faster, the learning rate has to decay to zero,
30+
thus SGD needs to start with a small learning rate. The learning rate does not need to decay to zero for SVRG,
31+
therefore we can use a relatively larger learning rate. SGD with learning rate of (0.001, 0.0025) and SVRG with
32+
learning rate of (0.025) are benchmarked. Even though SVRG starts with a relatively large learning rate, it converges
33+
much faster than SGD in both cases.
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
19+
import mxnet as mx
20+
import numpy as np
21+
from mxnet.contrib.svrg_optimization.svrg_module import SVRGModule
22+
23+
24+
def test_svrg_intermediate_level_api(args):
25+
"""Demonstrates intermediate level SVRGModule API where the training process
26+
need to be explicitly defined. KVstore is not explicitly created.
27+
28+
Parameters
29+
----------
30+
args: args
31+
Command line arguments
32+
"""
33+
num_epoch = args.epochs
34+
batch_size = args.batch_size
35+
update_freq = args.update_freq
36+
37+
di, mod = create_network(batch_size, update_freq)
38+
39+
mod.bind(data_shapes=di.provide_data, label_shapes=di.provide_label)
40+
mod.init_params(initializer=mx.init.Uniform(0.01), allow_missing=False, force_init=False, allow_extra=False)
41+
kv = mx.kv.create("local")
42+
mod.init_optimizer(kvstore=kv, optimizer='sgd', optimizer_params=(('learning_rate', 0.025),))
43+
metrics = mx.metric.create("mse")
44+
for e in range(num_epoch):
45+
metrics.reset()
46+
if e % mod.update_freq == 0:
47+
mod.update_full_grads(di)
48+
di.reset()
49+
for batch in di:
50+
mod.forward_backward(data_batch=batch)
51+
mod.update()
52+
mod.update_metric(metrics, batch.label)
53+
mod.logger.info('Epoch[%d] Train cost=%f', e, metrics.get()[1])
54+
55+
56+
def test_svrg_high_level_api(args):
57+
"""Demonstrates suggested usage of high level SVRGModule API. KVStore is explicitly created.
58+
59+
Parameters
60+
----------
61+
args: args
62+
Command line arguments
63+
"""
64+
num_epoch = args.epochs
65+
batch_size = args.batch_size
66+
update_freq = args.update_freq
67+
68+
di, mod = create_network(batch_size, update_freq)
69+
mod.fit(di, eval_metric='mse', optimizer='sgd', optimizer_params=(('learning_rate', 0.025),), num_epoch=num_epoch,
70+
kvstore='local')
71+
72+
73+
def create_network(batch_size, update_freq):
74+
"""Create a linear regression network for performing SVRG optimization.
75+
Parameters
76+
----------
77+
batch_size: int
78+
Size of data split
79+
update_freq: int
80+
Update Frequency for calculating full gradients
81+
82+
Returns
83+
----------
84+
di: mx.io.NDArrayIter
85+
Data iterator
86+
update_freq: SVRGModule
87+
An instance of SVRGModule for performing SVRG optimization
88+
"""
89+
import logging
90+
head = '%(asctime)-15s %(message)s'
91+
logging.basicConfig(level=logging.INFO, format=head)
92+
93+
train_data = np.random.randint(1, 5, [1000, 2])
94+
weights = np.array([1.0, 2.0])
95+
train_label = train_data.dot(weights)
96+
97+
di = mx.io.NDArrayIter(train_data, train_label, batch_size=batch_size, shuffle=True, label_name='lin_reg_label')
98+
X = mx.sym.Variable('data')
99+
Y = mx.symbol.Variable('lin_reg_label')
100+
fully_connected_layer = mx.sym.FullyConnected(data=X, name='fc1', num_hidden=1)
101+
lro = mx.sym.LinearRegressionOutput(data=fully_connected_layer, label=Y, name="lro")
102+
103+
mod = SVRGModule(
104+
symbol=lro,
105+
data_names=['data'],
106+
label_names=['lin_reg_label'], update_freq=update_freq, logger=logging
107+
)
108+
109+
return di, mod
110+
111+
# run as a script
112+
if __name__ == "__main__":
113+
import argparse
114+
115+
parser = argparse.ArgumentParser()
116+
parser.add_argument('-e', dest='epochs', default=100, type=int)
117+
parser.add_argument('-bs', dest='batch_size', default=32, type=int)
118+
parser.add_argument('-f', dest="update_freq", default=2, type=int)
119+
args = parser.parse_args()
120+
121+
print("========================== Intermediate Level API ==========================")
122+
test_svrg_intermediate_level_api(args)
123+
print("========================== High Level API ==========================")
124+
test_svrg_high_level_api(args)
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
19+
import mxnet as mx
20+
import numpy as np
21+
import logging
22+
from mxnet.contrib.svrg_optimization.svrg_module import SVRGModule
23+
24+
25+
def test_svrg_inference(args):
26+
epoch = args.epochs
27+
batch_size = args.batch_size
28+
update_freq = args.update_freq
29+
30+
train_iter, val_iter, mod = create_network(batch_size, update_freq)
31+
mod.fit(train_iter, eval_data=val_iter, eval_metric='mse', optimizer='sgd',
32+
optimizer_params=(('learning_rate', 0.025),),
33+
num_epoch=epoch)
34+
35+
36+
def get_validation_score(args):
37+
epoch = args.epochs
38+
batch_size = args.batch_size
39+
update_freq = args.update_freq
40+
41+
train_iter, val_iter, mod = create_network(batch_size, update_freq)
42+
mod.bind(data_shapes=train_iter.provide_data, label_shapes=train_iter.provide_label)
43+
mod.init_params(initializer=mx.init.Uniform(0.01), allow_missing=False, force_init=False, allow_extra=False)
44+
mod.init_optimizer(kvstore='local', optimizer='sgd', optimizer_params=(('learning_rate', 0.025),))
45+
metrics = mx.metric.create("mse")
46+
for e in range(epoch):
47+
metrics.reset()
48+
if e % mod.update_freq == 0:
49+
mod.update_full_grads(train_iter)
50+
train_iter.reset()
51+
for batch in train_iter:
52+
mod.forward_backward(data_batch=batch)
53+
mod.update()
54+
mod.update_metric(metrics, batch.label)
55+
56+
y = mod.predict(val_iter)
57+
58+
# test-train data split, 20% test data out of 1000 data samples
59+
assert y.shape == (200, 1)
60+
score = mod.score(val_iter, ['mse'])
61+
print("Training Loss on Validation Set is {}".format(score[0][1]))
62+
63+
64+
def create_network(batch_size, update_freq):
65+
"""Create a linear regression network for performing SVRG optimization.
66+
:return: an instance of mx.io.NDArrayIter
67+
:return: an instance of mx.mod.svrgmodule for performing SVRG optimization
68+
"""
69+
head = '%(asctime)-15s %(message)s'
70+
logging.basicConfig(level=logging.INFO, format=head)
71+
data = np.random.randint(1, 5, [1000, 2])
72+
73+
#Test_Train data split
74+
n_train = int(data.shape[0] * 0.8)
75+
weights = np.array([1.0, 2.0])
76+
label = data.dot(weights)
77+
78+
di = mx.io.NDArrayIter(data[:n_train, :], label[:n_train], batch_size=batch_size, shuffle=True, label_name='lin_reg_label')
79+
val_iter = mx.io.NDArrayIter(data[n_train:, :], label[n_train:], batch_size=batch_size)
80+
81+
X = mx.sym.Variable('data')
82+
Y = mx.symbol.Variable('lin_reg_label')
83+
fully_connected_layer = mx.sym.FullyConnected(data=X, name='fc1', num_hidden=1)
84+
lro = mx.sym.LinearRegressionOutput(data=fully_connected_layer, label=Y, name="lro")
85+
86+
mod = SVRGModule(
87+
symbol=lro,
88+
data_names=['data'],
89+
label_names=['lin_reg_label'], update_freq=update_freq, logger=logging)
90+
91+
return di, val_iter, mod
92+
93+
94+
# run as a script
95+
if __name__ == "__main__":
96+
import argparse
97+
parser = argparse.ArgumentParser()
98+
parser.add_argument('-e', dest='epochs', default=100, type=int)
99+
parser.add_argument('-bs', dest='batch_size', default=32, type=int)
100+
parser.add_argument('-f', dest="update_freq", default=2, type=int)
101+
args = parser.parse_args()
102+
103+
print("========================== SVRG Module Inference ==========================")
104+
test_svrg_inference(args)
105+
print("========================SVRG Module Score ============================")
106+
get_validation_score(args)
266 KB
Loading
347 KB
Loading

0 commit comments

Comments
 (0)