Skip to content

Commit 2e0ed8f

Browse files
authored
Add wrapper for FactorizationMachiones algorithm. (aws#38)
* Add FactorizationMachines class with unit tests. * Add integration test for FactorizationMachines. * Add CHANGELOG file.
1 parent f43b9c3 commit 2e0ed8f

File tree

10 files changed

+412
-7
lines changed

10 files changed

+412
-7
lines changed

CHANGELOG.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=========
2+
CHANGELOG
3+
=========
4+
5+
1.0.2
6+
=====
7+
8+
* feature: Estimators: add support for Amazon FactorizationMachines algorithm
9+
* feature: Session: Correctly handle TooManyBuckets error_code in default_bucket method
10+
* feature: Tests: add training failure tests for TF and MXNet
11+
* feature: Documentation: show how to make predictions against existing endpoint
12+
* feature: Estimators: implement write_spmatrix_to_sparse_tensor to support any scipy.sparse matrix
13+
14+
15+
1.0.1
16+
=====
17+
18+
* api-change: Model: Remove support for 'supplemental_containers' when creating Model
19+
* feature: Documentation: multiple updates
20+
* feature: Tests: ignore tests data in tox.ini, increase timeout for endpoint creation, capture exceptions during endpoint deletion, tests for input-output functions
21+
* feature: Logging: change to describe job every 30s when showing logs
22+
* feature: Session: use custom user agent at all times
23+
* feature: Setup: add travis file
24+
25+
26+
1.0.0
27+
=====
28+
29+
* Initial commit
30+

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def read(fname):
1010

1111

1212
setup(name="sagemaker",
13-
version="1.0.1",
13+
version="1.0.2",
1414
description="Open source library for training and deploying models on Amazon SageMaker.",
1515
packages=find_packages('src'),
1616
package_dir={'': 'src'},

src/sagemaker/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from sagemaker.amazon.kmeans import KMeans, KMeansModel, KMeansPredictor
1717
from sagemaker.amazon.pca import PCA, PCAModel, PCAPredictor
1818
from sagemaker.amazon.linear_learner import LinearLearner, LinearLearnerModel, LinearLearnerPredictor
19+
from sagemaker.amazon.factorization_machines import FactorizationMachines, FactorizationMachinesModel
20+
from sagemaker.amazon.factorization_machines import FactorizationMachinesPredictor
1921

2022
from sagemaker.model import Model
2123
from sagemaker.predictor import RealTimePredictor
@@ -27,5 +29,7 @@
2729

2830

2931
__all__ = [estimator, KMeans, KMeansModel, KMeansPredictor, PCA, PCAModel, PCAPredictor, LinearLearner,
30-
LinearLearnerModel, LinearLearnerPredictor, Model, RealTimePredictor, Session,
32+
LinearLearnerModel, LinearLearnerPredictor,
33+
FactorizationMachines, FactorizationMachinesModel, FactorizationMachinesPredictor,
34+
Model, RealTimePredictor, Session,
3135
container_def, s3_input, production_variant, get_execution_role]

src/sagemaker/amazon/amazon_estimator.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ class AmazonAlgorithmEstimatorBase(EstimatorBase):
2828
"""Base class for Amazon first-party Estimator implementations. This class isn't intended
2929
to be instantiated directly."""
3030

31-
MAX_DEFAULT_BATCH_SIZE = 500
32-
3331
feature_dim = hp('feature_dim', (validation.isint, validation.gt(0)))
3432
mini_batch_size = hp('mini_batch_size', (validation.isint, validation.gt(0)))
3533

@@ -87,10 +85,9 @@ def fit(self, records, mini_batch_size=None, **kwargs):
8785
mini_batch_size (int or None): The size of each mini-batch to use when training. If None, a
8886
default value will be used.
8987
"""
90-
default_mini_batch_size = min(self.MAX_DEFAULT_BATCH_SIZE,
91-
max(1, int(records.num_records / self.train_instance_count)))
92-
self.mini_batch_size = mini_batch_size or default_mini_batch_size
9388
self.feature_dim = records.feature_dim
89+
self.mini_batch_size = mini_batch_size
90+
9491
data = {records.channel: s3_input(records.s3_data, distribution='ShardedByS3Key',
9592
s3_data_type=records.s3_data_type)}
9693
super(AmazonAlgorithmEstimatorBase, self).fit(data, **kwargs)
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You
4+
# may not use this file except in compliance with the License. A copy of
5+
# the License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is
10+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11+
# ANY KIND, either express or implied. See the License for the specific
12+
# language governing permissions and limitations under the License.
13+
from sagemaker.amazon.amazon_estimator import AmazonAlgorithmEstimatorBase, registry
14+
from sagemaker.amazon.common import numpy_to_record_serializer, record_deserializer
15+
from sagemaker.amazon.hyperparameter import Hyperparameter as hp # noqa
16+
from sagemaker.amazon.validation import gt, isin, isint, ge, isnumber
17+
from sagemaker.predictor import RealTimePredictor
18+
from sagemaker.model import Model
19+
from sagemaker.session import Session
20+
21+
22+
class FactorizationMachines(AmazonAlgorithmEstimatorBase):
23+
24+
repo = 'factorization-machines:1'
25+
26+
num_factors = hp('num_factors', (gt(0), isint), 'An integer greater than zero')
27+
predictor_type = hp('predictor_type', isin('binary_classifier', 'regressor'),
28+
'Value "binary_classifier" or "regressor"')
29+
epochs = hp('epochs', (gt(0), isint), "An integer greater than 0")
30+
clip_gradient = hp('clip_gradient', isnumber, "A float value")
31+
eps = hp('eps', isnumber, "A float value")
32+
rescale_grad = hp('rescale_grad', isnumber, "A float value")
33+
bias_lr = hp('bias_lr', (ge(0), isnumber), "A non-negative float")
34+
linear_lr = hp('linear_lr', (ge(0), isnumber), "A non-negative float")
35+
factors_lr = hp('factors_lr', (ge(0), isnumber), "A non-negative float")
36+
bias_wd = hp('bias_wd', (ge(0), isnumber), "A non-negative float")
37+
linear_wd = hp('linear_wd', (ge(0), isnumber), "A non-negative float")
38+
factors_wd = hp('factors_wd', (ge(0), isnumber), "A non-negative float")
39+
bias_init_method = hp('bias_init_method', isin('normal', 'uniform', 'constant'),
40+
'Value "normal", "uniform" or "constant"')
41+
bias_init_scale = hp('bias_init_scale', (ge(0), isnumber), "A non-negative float")
42+
bias_init_sigma = hp('bias_init_sigma', (ge(0), isnumber), "A non-negative float")
43+
bias_init_value = hp('bias_init_value', isnumber, "A float value")
44+
linear_init_method = hp('linear_init_method', isin('normal', 'uniform', 'constant'),
45+
'Value "normal", "uniform" or "constant"')
46+
linear_init_scale = hp('linear_init_scale', (ge(0), isnumber), "A non-negative float")
47+
linear_init_sigma = hp('linear_init_sigma', (ge(0), isnumber), "A non-negative float")
48+
linear_init_value = hp('linear_init_value', isnumber, "A float value")
49+
factors_init_method = hp('factors_init_method', isin('normal', 'uniform', 'constant'),
50+
'Value "normal", "uniform" or "constant"')
51+
factors_init_scale = hp('factors_init_scale', (ge(0), isnumber), "A non-negative float")
52+
factors_init_sigma = hp('factors_init_sigma', (ge(0), isnumber), "A non-negative float")
53+
factors_init_value = hp('factors_init_value', isnumber, "A float value")
54+
55+
def __init__(self, role, train_instance_count, train_instance_type,
56+
num_factors, predictor_type,
57+
epochs=None, clip_gradient=None, eps=None, rescale_grad=None,
58+
bias_lr=None, linear_lr=None, factors_lr=None,
59+
bias_wd=None, linear_wd=None, factors_wd=None,
60+
bias_init_method=None, bias_init_scale=None, bias_init_sigma=None, bias_init_value=None,
61+
linear_init_method=None, linear_init_scale=None, linear_init_sigma=None, linear_init_value=None,
62+
factors_init_method=None, factors_init_scale=None, factors_init_sigma=None, factors_init_value=None,
63+
**kwargs):
64+
"""Factorization Machines is :class:`Estimator` for general-purpose supervised learning.
65+
66+
Amazon SageMaker Factorization Machines is a general-purpose supervised learning algorithm that you can use
67+
for both classification and regression tasks. It is an extension of a linear model that is designed
68+
to parsimoniously capture interactions between features within high dimensional sparse datasets.
69+
70+
This Estimator may be fit via calls to
71+
:meth:`~sagemaker.amazon.amazon_estimator.AmazonAlgorithmEstimatorBase.fit`. It requires Amazon
72+
:class:`~sagemaker.amazon.record_pb2.Record` protobuf serialized data to be stored in S3.
73+
There is an utility :meth:`~sagemaker.amazon.amazon_estimator.AmazonAlgorithmEstimatorBase.record_set` that
74+
can be used to upload data to S3 and creates :class:`~sagemaker.amazon.amazon_estimator.RecordSet` to be passed
75+
to the `fit` call.
76+
77+
To learn more about the Amazon protobuf Record class and how to prepare bulk data in this format, please
78+
consult AWS technical documentation: https://docs.aws.amazon.com/sagemaker/latest/dg/cdf-training.html
79+
80+
After this Estimator is fit, model data is stored in S3. The model may be deployed to an Amazon SageMaker
81+
Endpoint by invoking :meth:`~sagemaker.amazon.estimator.EstimatorBase.deploy`. As well as deploying an Endpoint,
82+
deploy returns a :class:`~sagemaker.amazon.pca.FactorizationMachinesPredictor` object that can be used
83+
for inference calls using the trained model hosted in the SageMaker Endpoint.
84+
85+
FactorizationMachines Estimators can be configured by setting hyperparameters. The available hyperparameters for
86+
FactorizationMachines are documented below.
87+
88+
For further information on the AWS FactorizationMachines algorithm,
89+
please consult AWS technical documentation: https://docs.aws.amazon.com/sagemaker/latest/dg/fact-machines.html
90+
91+
Args:
92+
role (str): An AWS IAM role (either name or full ARN). The Amazon SageMaker training jobs and
93+
APIs that create Amazon SageMaker endpoints use this role to access
94+
training data and model artifacts. After the endpoint is created,
95+
the inference code might use the IAM role, if accessing AWS resource.
96+
train_instance_count (int): Number of Amazon EC2 instances to use for training.
97+
train_instance_type (str): Type of EC2 instance to use for training, for example, 'ml.c4.xlarge'.
98+
num_factors (int): Dimensionality of factorization.
99+
predictor_type (str): Type of predictor 'binary_classifier' or 'regressor'.
100+
epochs (int): Number of training epochs to run.
101+
clip_gradient (float): Optimizer parameter. Clip the gradient by projecting onto
102+
the box [-clip_gradient, +clip_gradient]
103+
eps (float): Optimizer parameter. Small value to avoid division by 0.
104+
rescale_grad (float): Optimizer parameter. If set, multiplies the gradient with rescale_grad
105+
before updating. Often choose to be 1.0/batch_size.
106+
bias_lr (float): Non-negative learning rate for the bias term.
107+
linear_lr (float): Non-negative learning rate for linear terms.
108+
factors_lr (float): Noon-negative learning rate for factorization terms.
109+
bias_wd (float): Non-negative weight decay for the bias term.
110+
linear_wd (float): Non-negative weight decay for linear terms.
111+
factors_wd (float): Non-negative weight decay for factorization terms.
112+
bias_init_method (string): Initialization method for the bias term: 'normal', 'uniform' or 'constant'.
113+
bias_init_scale (float): Non-negative range for initialization of the bias term that takes
114+
effect when bias_init_method parameter is 'uniform'
115+
bias_init_sigma (float): Non-negative standard deviation for initialization of the bias term that takes
116+
effect when bias_init_method parameter is 'normal'.
117+
bias_init_value (float): Initial value of the bias term that takes effect
118+
when bias_init_method parameter is 'constant'.
119+
linear_init_method (string): Initialization method for linear term: 'normal', 'uniform' or 'constant'.
120+
linear_init_scale (float): Non-negative range for initialization of linear terms that takes
121+
effect when linear_init_method parameter is 'uniform'.
122+
linear_init_sigma (float): Non-negative standard deviation for initialization of linear terms that takes
123+
effect when linear_init_method parameter is 'normal'.
124+
linear_init_value (float): Initial value of linear terms that takes effect
125+
when linear_init_method parameter is 'constant'.
126+
factors_init_method (string): Initialization method for factorization term: 'normal',
127+
'uniform' or 'constant'.
128+
factors_init_scale (float): Non-negative range for initialization of factorization terms that takes
129+
effect when factors_init_method parameter is 'uniform'.
130+
factors_init_sigma (float): Non-negative standard deviation for initialization of factorization terms that
131+
takes effect when factors_init_method parameter is 'normal'.
132+
factors_init_value (float): Initial value of factorization terms that takes
133+
effect when factors_init_method parameter is 'constant'.
134+
**kwargs: base class keyword argument values.
135+
"""
136+
super(FactorizationMachines, self).__init__(role, train_instance_count, train_instance_type, **kwargs)
137+
138+
self.num_factors = num_factors
139+
self.predictor_type = predictor_type
140+
self.epochs = epochs
141+
self.clip_gradient = clip_gradient
142+
self.eps = eps
143+
self.rescale_grad = rescale_grad
144+
self.bias_lr = bias_lr
145+
self.linear_lr = linear_lr
146+
self.factors_lr = factors_lr
147+
self.bias_wd = bias_wd
148+
self.linear_wd = linear_wd
149+
self.factors_wd = factors_wd
150+
self.bias_init_method = bias_init_method
151+
self.bias_init_scale = bias_init_scale
152+
self.bias_init_sigma = bias_init_sigma
153+
self.bias_init_value = bias_init_value
154+
self.linear_init_method = linear_init_method
155+
self.linear_init_scale = linear_init_scale
156+
self.linear_init_sigma = linear_init_sigma
157+
self.linear_init_value = linear_init_value
158+
self.factors_init_method = factors_init_method
159+
self.factors_init_scale = factors_init_scale
160+
self.factors_init_sigma = factors_init_sigma
161+
self.factors_init_value = factors_init_value
162+
163+
def create_model(self):
164+
"""Return a :class:`~sagemaker.amazon.FactorizationMachinesModel` referencing the latest
165+
s3 model data produced by this Estimator."""
166+
167+
return FactorizationMachinesModel(self.model_data, self.role, sagemaker_session=self.sagemaker_session)
168+
169+
170+
class FactorizationMachinesPredictor(RealTimePredictor):
171+
"""Performs binary-classification or regression prediction from input vectors.
172+
173+
The implementation of :meth:`~sagemaker.predictor.RealTimePredictor.predict` in this
174+
`RealTimePredictor` requires a numpy ``ndarray`` as input. The array should contain the
175+
same number of columns as the feature-dimension of the data used to fit the model this
176+
Predictor performs inference on.
177+
178+
:meth:`predict()` returns a list of :class:`~sagemaker.amazon.record_pb2.Record` objects, one
179+
for each row in the input ``ndarray``. The prediction is stored in the ``"score"``
180+
key of the ``Record.label`` field.
181+
Please refer to the formats details described: https://docs.aws.amazon.com/sagemaker/latest/dg/fm-in-formats.html
182+
"""
183+
184+
def __init__(self, endpoint, sagemaker_session=None):
185+
super(FactorizationMachinesPredictor, self).__init__(endpoint,
186+
sagemaker_session,
187+
serializer=numpy_to_record_serializer(),
188+
deserializer=record_deserializer())
189+
190+
191+
class FactorizationMachinesModel(Model):
192+
"""Reference S3 model data created by FactorizationMachines estimator. Calling :meth:`~sagemaker.model.Model.deploy`
193+
creates an Endpoint and returns :class:`FactorizationMachinesPredictor`."""
194+
195+
def __init__(self, model_data, role, sagemaker_session=None):
196+
sagemaker_session = sagemaker_session or Session()
197+
image = registry(sagemaker_session.boto_session.region_name) + "/" + FactorizationMachines.repo
198+
super(FactorizationMachinesModel, self).__init__(model_data,
199+
image,
200+
role,
201+
predictor_cls=FactorizationMachinesPredictor,
202+
sagemaker_session=sagemaker_session)

src/sagemaker/amazon/kmeans.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ def create_model(self):
9999
s3 model data produced by this Estimator."""
100100
return KMeansModel(self.model_data, self.role, self.sagemaker_session)
101101

102+
def fit(self, records, mini_batch_size=5000, **kwargs):
103+
super(KMeans, self).fit(records, mini_batch_size, **kwargs)
104+
102105
def hyperparameters(self):
103106
"""Return the SageMaker hyperparameters for training this KMeans Estimator"""
104107
hp = dict(force_dense='True') # KMeans requires this hp to fit on Record objects

src/sagemaker/amazon/linear_learner.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ class LinearLearner(AmazonAlgorithmEstimatorBase):
2323

2424
repo = 'linear-learner:1'
2525

26+
DEFAULT_MINI_BATCH_SIZE = 1000
27+
2628
binary_classifier_model_selection_criteria = hp('binary_classifier_model_selection_criteria',
2729
isin('accuracy', 'f1', 'precision_at_target_recall',
2830
'recall_at_target_precision', 'cross_entropy_loss'))
@@ -191,6 +193,13 @@ def create_model(self):
191193

192194
return LinearLearnerModel(self, self.model_data, self.role, self.sagemaker_session)
193195

196+
def fit(self, records, mini_batch_size=None, **kwargs):
197+
# mini_batch_size can't be greater than number of records or training job fails
198+
default_mini_batch_size = min(self.DEFAULT_MINI_BATCH_SIZE,
199+
max(1, int(records.num_records / self.train_instance_count)))
200+
use_mini_batch_size = mini_batch_size or default_mini_batch_size
201+
super(LinearLearner, self).fit(records, use_mini_batch_size, **kwargs)
202+
194203

195204
class LinearLearnerPredictor(RealTimePredictor):
196205
"""Performs binary-classification or regression prediction from input vectors.

src/sagemaker/amazon/pca.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class PCA(AmazonAlgorithmEstimatorBase):
2222

2323
repo = 'pca:1'
2424

25+
DEFAULT_MINI_BATCH_SIZE = 500
26+
2527
num_components = hp(name='num_components', validate=lambda x: x > 0 and isinstance(x, int),
2628
validation_message='Value must be an integer greater than zero')
2729
algorithm_mode = hp(name='algorithm_mode', validate=lambda x: x in ['regular', 'stable', 'randomized'],
@@ -86,6 +88,13 @@ def create_model(self):
8688

8789
return PCAModel(self.model_data, self.role, sagemaker_session=self.sagemaker_session)
8890

91+
def fit(self, records, mini_batch_size=None, **kwargs):
92+
# mini_batch_size is a required parameter
93+
default_mini_batch_size = min(self.DEFAULT_MINI_BATCH_SIZE,
94+
max(1, int(records.num_records / self.train_instance_count)))
95+
use_mini_batch_size = mini_batch_size or default_mini_batch_size
96+
super(PCA, self).fit(records, use_mini_batch_size, **kwargs)
97+
8998

9099
class PCAPredictor(RealTimePredictor):
91100
"""Transforms input vectors to lower-dimesional representations.

0 commit comments

Comments
 (0)