Skip to content

Commit cd25808

Browse files
Add num_partitions arg to the SDCA optimizer for distribution and deprecate container argument.
Change: 130165115
1 parent ccac1bf commit cd25808

3 files changed

Lines changed: 104 additions & 50 deletions

File tree

tensorflow/contrib/linear_optimizer/python/kernel_tests/sdca_ops_test.py

Lines changed: 82 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
from __future__ import division
1919
from __future__ import print_function
2020

21-
import uuid
22-
21+
from threading import Thread
2322
import tensorflow as tf
2423

2524
from tensorflow.contrib.linear_optimizer.python.ops.sdca_ops import _sdca_ops
@@ -31,7 +30,7 @@
3130

3231
_MAX_ITERATIONS = 100
3332
_SHARD_NUMBERS = [None, 1, 3, 10]
34-
33+
_NUM_PARTITIONS = [2, 4]
3534

3635
def make_example_proto(feature_dict, target, value=1.0):
3736
e = tf.train.Example()
@@ -146,11 +145,6 @@ def get_binary_predictions_for_hinge(predictions):
146145
dtype=tf.int32)
147146

148147

149-
# Setup the single container shared across all tests. This is testing proper
150-
# isolation across optimizers instantiated in each of the tests below.
151-
CONTAINER = uuid.uuid4().hex
152-
153-
154148
# TODO(sibyl-Mooth6ku): Add tests that exercise L1 and Shrinking.
155149
# TODO(sibyl-vie3Poto): Refactor tests to avoid repetition of boilerplate code.
156150
class SdcaModelTest(TensorFlowTestCase):
@@ -184,8 +178,8 @@ def testSimple(self):
184178
symmetric_l1_regularization=0,
185179
loss_type='logistic_loss')
186180

187-
lr = SdcaModel(CONTAINER, examples, variables, options,
188-
num_table_shards=num_shards)
181+
lr = SdcaModel(
182+
examples, variables, options, num_table_shards=num_shards)
189183
tf.initialize_all_variables().run()
190184
unregularized_loss = lr.unregularized_loss(examples)
191185
loss = lr.regularized_loss(examples)
@@ -211,6 +205,62 @@ def testSimple(self):
211205
rtol=1e-2,
212206
atol=1e-2)
213207

208+
def testDistributedSimple(self):
209+
# Setup test data
210+
example_protos = [
211+
make_example_proto({'age': [0],
212+
'gender': [0]}, 0),
213+
make_example_proto({'age': [1],
214+
'gender': [1]}, 1),
215+
]
216+
example_weights = [1.0, 1.0]
217+
for num_shards in _SHARD_NUMBERS:
218+
for num_partitions in _NUM_PARTITIONS:
219+
with self._single_threaded_test_session():
220+
examples = make_example_dict(example_protos, example_weights)
221+
variables = make_variable_dict(1, 1)
222+
options = dict(
223+
symmetric_l2_regularization=1,
224+
symmetric_l1_regularization=0,
225+
loss_type='logistic_loss',
226+
num_partitions=num_partitions)
227+
228+
lr = SdcaModel(
229+
examples, variables, options, num_table_shards=num_shards)
230+
tf.initialize_all_variables().run()
231+
unregularized_loss = lr.unregularized_loss(examples)
232+
loss = lr.regularized_loss(examples)
233+
predictions = lr.predictions(examples)
234+
self.assertAllClose(0.693147, unregularized_loss.eval())
235+
self.assertAllClose(0.693147, loss.eval())
236+
237+
train_op = lr.minimize()
238+
239+
def Minimize():
240+
with self._single_threaded_test_session():
241+
for _ in range(_MAX_ITERATIONS):
242+
train_op.run()
243+
244+
threads = []
245+
for _ in range(num_partitions):
246+
threads.append(Thread(target=Minimize))
247+
threads[-1].start()
248+
249+
for t in threads:
250+
t.join()
251+
252+
# The high tolerance in unregularized_loss comparisons is due to the
253+
# fact that it's possible to trade off unregularized_loss vs.
254+
# regularization and still have a sum that is quite close to the
255+
# optimal regularized_loss value. SDCA's duality gap only ensures
256+
# that the regularized_loss is within 0.01 of optimal.
257+
# 0.525457 is the optimal regularized_loss.
258+
# 0.411608 is the unregularized_loss at that optimum.
259+
self.assertAllClose(0.411608, unregularized_loss.eval(), atol=0.05)
260+
self.assertAllClose(0.525457, loss.eval(), atol=0.01)
261+
predicted_labels = get_binary_predictions_for_logistic(predictions)
262+
self.assertAllEqual([0, 1], predicted_labels.eval())
263+
self.assertTrue(lr.approximate_duality_gap().eval() < 0.02)
214264

215265
def testSimpleNoL2(self):
216266
# Same as test above (so comments from above apply) but without an L2.
@@ -233,8 +283,8 @@ def testSimpleNoL2(self):
233283
symmetric_l1_regularization=0,
234284
loss_type='logistic_loss')
235285

236-
lr = SdcaModel(CONTAINER, examples, variables, options,
237-
num_table_shards=num_shards)
286+
lr = SdcaModel(
287+
examples, variables, options, num_table_shards=num_shards)
238288
tf.initialize_all_variables().run()
239289
unregularized_loss = lr.unregularized_loss(examples)
240290
loss = lr.regularized_loss(examples)
@@ -287,8 +337,8 @@ def testSomeUnweightedExamples(self):
287337
symmetric_l1_regularization=0,
288338
loss_type='logistic_loss')
289339

290-
lr = SdcaModel(CONTAINER, examples, variables, options,
291-
num_table_shards=num_shards)
340+
lr = SdcaModel(
341+
examples, variables, options, num_table_shards=num_shards)
292342
tf.initialize_all_variables().run()
293343
unregularized_loss = lr.unregularized_loss(examples)
294344
loss = lr.regularized_loss(examples)
@@ -325,8 +375,8 @@ def testFractionalExampleLabel(self):
325375
symmetric_l1_regularization=0,
326376
loss_type='logistic_loss')
327377

328-
lr = SdcaModel(CONTAINER, examples, variables, options,
329-
num_table_shards=num_shards)
378+
lr = SdcaModel(
379+
examples, variables, options, num_table_shards=num_shards)
330380
tf.initialize_all_variables().run()
331381
with self.assertRaisesOpError(
332382
'Only labels of 0.0 or 1.0 are supported right now.'):
@@ -357,8 +407,8 @@ def testImbalanced(self):
357407
symmetric_l1_regularization=0,
358408
loss_type='logistic_loss')
359409

360-
lr = SdcaModel(CONTAINER, examples, variables, options,
361-
num_table_shards=num_shards)
410+
lr = SdcaModel(
411+
examples, variables, options, num_table_shards=num_shards)
362412
tf.initialize_all_variables().run()
363413
unregularized_loss = lr.unregularized_loss(examples)
364414
loss = lr.regularized_loss(examples)
@@ -397,8 +447,8 @@ def testImbalancedWithExampleWeights(self):
397447
symmetric_l1_regularization=0,
398448
loss_type='logistic_loss')
399449

400-
lr = SdcaModel(CONTAINER, examples, variables, options,
401-
num_table_shards=num_shards)
450+
lr = SdcaModel(
451+
examples, variables, options, num_table_shards=num_shards)
402452
tf.initialize_all_variables().run()
403453
unregularized_loss = lr.unregularized_loss(examples)
404454
loss = lr.regularized_loss(examples)
@@ -435,8 +485,8 @@ def testInstancesOfOneClassOnly(self):
435485
symmetric_l1_regularization=0,
436486
loss_type='logistic_loss')
437487

438-
lr = SdcaModel(CONTAINER, examples, variables, options,
439-
num_table_shards=num_shards)
488+
lr = SdcaModel(
489+
examples, variables, options, num_table_shards=num_shards)
440490
tf.initialize_all_variables().run()
441491
unregularized_loss = lr.unregularized_loss(examples)
442492
loss = lr.regularized_loss(examples)
@@ -478,7 +528,7 @@ def testSimple(self):
478528
symmetric_l1_regularization=0,
479529
loss_type='squared_loss')
480530

481-
lr = SdcaModel(CONTAINER, examples, variables, options)
531+
lr = SdcaModel(examples, variables, options)
482532
tf.initialize_all_variables().run()
483533
predictions = lr.predictions(examples)
484534
train_op = lr.minimize()
@@ -523,7 +573,7 @@ def testL2Regularization(self):
523573
symmetric_l1_regularization=0,
524574
loss_type='squared_loss')
525575

526-
lr = SdcaModel(CONTAINER, examples, variables, options)
576+
lr = SdcaModel(examples, variables, options)
527577
tf.initialize_all_variables().run()
528578
predictions = lr.predictions(examples)
529579

@@ -557,7 +607,7 @@ def testL1Regularization(self):
557607
options = dict(symmetric_l2_regularization=1.0,
558608
symmetric_l1_regularization=4.0,
559609
loss_type='squared_loss')
560-
lr = SdcaModel(CONTAINER, examples, variables, options)
610+
lr = SdcaModel(examples, variables, options)
561611
tf.initialize_all_variables().run()
562612
prediction = lr.predictions(examples)
563613
loss = lr.regularized_loss(examples)
@@ -593,7 +643,7 @@ def testFeatureValues(self):
593643
symmetric_l1_regularization=0,
594644
loss_type='squared_loss')
595645

596-
lr = SdcaModel(CONTAINER, examples, variables, options)
646+
lr = SdcaModel(examples, variables, options)
597647
tf.initialize_all_variables().run()
598648
predictions = lr.predictions(examples)
599649

@@ -626,7 +676,7 @@ def testDenseFeaturesWithDefaultWeights(self):
626676
options = dict(symmetric_l2_regularization=1.0,
627677
symmetric_l1_regularization=0,
628678
loss_type='squared_loss')
629-
lr = SdcaModel(CONTAINER, examples, variables, options)
679+
lr = SdcaModel(examples, variables, options)
630680
tf.initialize_all_variables().run()
631681
predictions = lr.predictions(examples)
632682

@@ -656,7 +706,7 @@ def testDenseFeaturesWithArbitraryWeights(self):
656706
options = dict(symmetric_l2_regularization=5.0,
657707
symmetric_l1_regularization=0,
658708
loss_type='squared_loss')
659-
lr = SdcaModel(CONTAINER, examples, variables, options)
709+
lr = SdcaModel(examples, variables, options)
660710
tf.initialize_all_variables().run()
661711
predictions = lr.predictions(examples)
662712

@@ -700,7 +750,7 @@ def testSimple(self):
700750
options = dict(symmetric_l2_regularization=1.0,
701751
symmetric_l1_regularization=0,
702752
loss_type='hinge_loss')
703-
model = SdcaModel(CONTAINER, examples, variables, options)
753+
model = SdcaModel(examples, variables, options)
704754
tf.initialize_all_variables().run()
705755

706756
# Before minimization, the weights default to zero. There is no loss due
@@ -737,7 +787,7 @@ def testDenseFeaturesPerfectlySeparable(self):
737787
symmetric_l2_regularization=1.0,
738788
symmetric_l1_regularization=0,
739789
loss_type='hinge_loss')
740-
model = SdcaModel(CONTAINER, examples, variables, options)
790+
model = SdcaModel(examples, variables, options)
741791
tf.initialize_all_variables().run()
742792
predictions = model.predictions(examples)
743793
binary_predictions = get_binary_predictions_for_hinge(predictions)
@@ -767,7 +817,7 @@ def testDenseFeaturesSeparableWithinMargins(self):
767817
options = dict(symmetric_l2_regularization=1.0,
768818
symmetric_l1_regularization=0,
769819
loss_type='hinge_loss')
770-
model = SdcaModel(CONTAINER, examples, variables, options)
820+
model = SdcaModel(examples, variables, options)
771821
tf.initialize_all_variables().run()
772822
predictions = model.predictions(examples)
773823
binary_predictions = get_binary_predictions_for_hinge(predictions)
@@ -796,7 +846,7 @@ def testDenseFeaturesWeightedExamples(self):
796846
options = dict(symmetric_l2_regularization=1.0,
797847
symmetric_l1_regularization=0,
798848
loss_type='hinge_loss')
799-
model = SdcaModel(CONTAINER, examples, variables, options)
849+
model = SdcaModel(examples, variables, options)
800850
tf.initialize_all_variables().run()
801851
predictions = model.predictions(examples)
802852
binary_predictions = get_binary_predictions_for_hinge(predictions)

tensorflow/contrib/linear_optimizer/python/ops/sdca_ops.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,7 @@ class SdcaModel(object):
280280
281281
```python
282282
# Create a solver with the desired parameters.
283-
lr = tf.contrib.linear_optimizer.SdcaModel(
284-
container, examples, variables, options)
283+
lr = tf.contrib.linear_optimizer.SdcaModel(examples, variables, options)
285284
opt_op = lr.minimize()
286285
287286
predictions = lr.predictions(examples)
@@ -290,9 +289,6 @@ class SdcaModel(object):
290289
# Primal loss only
291290
unregularized_loss = lr.unregularized_loss(examples)
292291
293-
container: Name of the container (eg a hex-encoded UUID) where internal
294-
state of the optimizer can be stored. The container can be safely shared
295-
across many models.
296292
examples: {
297293
sparse_features: list of SparseFeatureColumn.
298294
dense_features: list of dense tensors of type float32.
@@ -308,6 +304,9 @@ class SdcaModel(object):
308304
symmetric_l1_regularization: 0.0
309305
symmetric_l2_regularization: 1.0
310306
loss_type: "logistic_loss"
307+
num_partitions: 1 (Optional, with default value of 1. Number of
308+
partitions of the global loss function, 1 means single machine solver,
309+
and >=1 when we have more than one optimizer working concurrently.)
311310
}
312311
```
313312
@@ -325,13 +324,11 @@ class SdcaModel(object):
325324
"""
326325

327326
def __init__(self,
328-
container,
329327
examples,
330328
variables,
331329
options,
332330
num_table_shards=None): # pylint: disable=unused-argument
333331
"""Create a new sdca optimizer."""
334-
# TODO(andreasst): get rid of obsolete container parameter
335332

336333
if not examples or not variables or not options:
337334
raise ValueError('examples, variables and options must all be specified.')
@@ -379,6 +376,10 @@ def _symmetric_l2_regularization(self):
379376
# Algorithmic requirement (for now) is to have minimal l2 of 1.0.
380377
return max(self._options['symmetric_l2_regularization'], 1.0)
381378

379+
def _num_partitions(self):
380+
# Number of partitions of the global objective.
381+
return self._options.get('num_partitions', 1)
382+
382383
# TODO(sibyl-Aix6ihai): Use optimizer interface to make use of slot creation logic.
383384
def _create_slots(self):
384385
# Make internal variables which have the updates before applying L1
@@ -520,7 +521,7 @@ def minimize(self, global_step=None, name=None):
520521
loss_type=self._options['loss_type'],
521522
l1=self._options['symmetric_l1_regularization'],
522523
l2=self._symmetric_l2_regularization(),
523-
num_partitions=1,
524+
num_partitions=self._num_partitions(),
524525
# TODO(sibyl-Aix6ihai): Provide empirical evidence for this. It is better
525526
# to run more than one iteration on single mini-batch as we want to
526527
# spend more time in compute. SDCA works better with larger

tensorflow/contrib/linear_optimizer/python/sdca_optimizer.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
from __future__ import division
1717
from __future__ import print_function
1818

19-
import uuid
20-
2119
from tensorflow.contrib import layers
2220
from tensorflow.contrib.linear_optimizer.python.ops import sdca_ops
2321
from tensorflow.python.ops import array_ops
@@ -36,6 +34,7 @@ class SDCAOptimizer(object):
3634
real_feature_column = real_valued_column(...)
3735
sparse_feature_column = sparse_column_with_hash_bucket(...)
3836
sdca_optimizer = linear.SDCAOptimizer(example_id_column='example_id',
37+
num_partitions=1,
3938
symmetric_l2_regularization=2.0)
4039
classifier = tf.contrib.learn.LinearClassifier(
4140
feature_columns=[real_feature_column, sparse_feature_column],
@@ -47,13 +46,17 @@ class SDCAOptimizer(object):
4746
Here the expectation is that the input_fn_* functions passed to train and
4847
evaluate return a pair (dict, label_tensor) where dict has `example_id_column`
4948
as `key` whose value is a `Tensor` of shape [batch_size] and dtype string.
49+
num_paritions defines the number of partitions of the loss function, which
50+
is equivalent to the number of concurrent workers running the train steps.
5051
"""
5152

5253
def __init__(self,
5354
example_id_column,
55+
num_partitions=1,
5456
symmetric_l1_regularization=0.0,
5557
symmetric_l2_regularization=1.0):
5658
self._example_id_column = example_id_column
59+
self._num_partitions = num_partitions
5760
self._symmetric_l1_regularization = symmetric_l1_regularization
5861
self._symmetric_l2_regularization = symmetric_l2_regularization
5962

@@ -157,13 +160,13 @@ def _training_examples_and_variables():
157160
dense_features_weights=dense_feature_weights)
158161
return examples, sdca_variables
159162

160-
options = dict(
161-
symmetric_l1_regularization=self._symmetric_l1_regularization,
162-
symmetric_l2_regularization=self._symmetric_l2_regularization,
163-
loss_type=loss_type)
164163
training_examples, training_variables = _training_examples_and_variables()
165-
sdca_model = sdca_ops.SdcaModel(container=uuid.uuid4().hex,
166-
examples=training_examples,
167-
variables=training_variables,
168-
options=options)
164+
sdca_model = sdca_ops.SdcaModel(
165+
examples=training_examples,
166+
variables=training_variables,
167+
options=dict(
168+
symmetric_l1_regularization=self._symmetric_l1_regularization,
169+
symmetric_l2_regularization=self._symmetric_l2_regularization,
170+
num_partitions=self._num_partitions,
171+
loss_type=loss_type))
169172
return sdca_model.minimize(global_step=global_step)

0 commit comments

Comments
 (0)