1# Copyright 2018 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Tests specific to Feature Columns integration."""
16
17from __future__ import absolute_import
18from __future__ import division
19from __future__ import print_function
20
21import numpy as np
22
23from tensorflow.python import keras
24from tensorflow.python.data.ops import dataset_ops
25from tensorflow.python.feature_column import feature_column_lib as fc
26from tensorflow.python.keras import keras_parameterized
27from tensorflow.python.keras import metrics as metrics_module
28from tensorflow.python.keras import testing_utils
29from tensorflow.python.platform import test
30
31
32class TestDNNModel(keras.models.Model):
33
34  def __init__(self, feature_columns, units, name=None, **kwargs):
35    super(TestDNNModel, self).__init__(name=name, **kwargs)
36    self._input_layer = fc.DenseFeatures(feature_columns, name='input_layer')
37    self._dense_layer = keras.layers.Dense(units, name='dense_layer')
38
39  def call(self, features):
40    net = self._input_layer(features)
41    net = self._dense_layer(net)
42    return net
43
44
45class FeatureColumnsIntegrationTest(keras_parameterized.TestCase):
46  """Most Sequential model API tests are covered in `training_test.py`.
47
48  """
49
50  @keras_parameterized.run_all_keras_modes
51  def test_sequential_model(self):
52    columns = [fc.numeric_column('a')]
53    model = keras.models.Sequential([
54        fc.DenseFeatures(columns),
55        keras.layers.Dense(64, activation='relu'),
56        keras.layers.Dense(20, activation='softmax')
57    ])
58    model.compile(
59        optimizer='rmsprop',
60        loss='categorical_crossentropy',
61        metrics=['accuracy'],
62        run_eagerly=testing_utils.should_run_eagerly())
63
64    x = {'a': np.random.random((10, 1))}
65    y = np.random.randint(20, size=(10, 1))
66    y = keras.utils.to_categorical(y, num_classes=20)
67    model.fit(x, y, epochs=1, batch_size=5)
68    model.fit(x, y, epochs=1, batch_size=5)
69    model.evaluate(x, y, batch_size=5)
70    model.predict(x, batch_size=5)
71
72  @keras_parameterized.run_all_keras_modes
73  def test_sequential_model_with_ds_input(self):
74    columns = [fc.numeric_column('a')]
75    model = keras.models.Sequential([
76        fc.DenseFeatures(columns),
77        keras.layers.Dense(64, activation='relu'),
78        keras.layers.Dense(20, activation='softmax')
79    ])
80    model.compile(
81        optimizer='rmsprop',
82        loss='categorical_crossentropy',
83        metrics=['accuracy'],
84        run_eagerly=testing_utils.should_run_eagerly())
85
86    y = np.random.randint(20, size=(100, 1))
87    y = keras.utils.to_categorical(y, num_classes=20)
88    x = {'a': np.random.random((100, 1))}
89    ds1 = dataset_ops.Dataset.from_tensor_slices(x)
90    ds2 = dataset_ops.Dataset.from_tensor_slices(y)
91    ds = dataset_ops.Dataset.zip((ds1, ds2)).batch(5)
92    model.fit(ds, steps_per_epoch=1)
93    model.fit(ds, steps_per_epoch=1)
94    model.evaluate(ds, steps=1)
95    model.predict(ds, steps=1)
96
97  @keras_parameterized.run_all_keras_modes
98  def test_subclassed_model_with_feature_columns(self):
99    col_a = fc.numeric_column('a')
100    col_b = fc.numeric_column('b')
101
102    dnn_model = TestDNNModel([col_a, col_b], 20)
103
104    dnn_model.compile(
105        optimizer='rmsprop',
106        loss='categorical_crossentropy',
107        metrics=['accuracy'],
108        run_eagerly=testing_utils.should_run_eagerly())
109
110    x = {'a': np.random.random((10, 1)), 'b': np.random.random((10, 1))}
111    y = np.random.randint(20, size=(10, 1))
112    y = keras.utils.to_categorical(y, num_classes=20)
113    dnn_model.fit(x=x, y=y, epochs=1, batch_size=5)
114    dnn_model.fit(x=x, y=y, epochs=1, batch_size=5)
115    dnn_model.evaluate(x=x, y=y, batch_size=5)
116    dnn_model.predict(x=x, batch_size=5)
117
118  @keras_parameterized.run_all_keras_modes
119  def test_subclassed_model_with_feature_columns_with_ds_input(self):
120    col_a = fc.numeric_column('a')
121    col_b = fc.numeric_column('b')
122
123    dnn_model = TestDNNModel([col_a, col_b], 20)
124
125    dnn_model.compile(
126        optimizer='rmsprop',
127        loss='categorical_crossentropy',
128        metrics=['accuracy'],
129        run_eagerly=testing_utils.should_run_eagerly())
130
131    y = np.random.randint(20, size=(100, 1))
132    y = keras.utils.to_categorical(y, num_classes=20)
133    x = {'a': np.random.random((100, 1)), 'b': np.random.random((100, 1))}
134    ds1 = dataset_ops.Dataset.from_tensor_slices(x)
135    ds2 = dataset_ops.Dataset.from_tensor_slices(y)
136    ds = dataset_ops.Dataset.zip((ds1, ds2)).batch(5)
137    dnn_model.fit(ds, steps_per_epoch=1)
138    dnn_model.fit(ds, steps_per_epoch=1)
139    dnn_model.evaluate(ds, steps=1)
140    dnn_model.predict(ds, steps=1)
141
142  # TODO(kaftan) seems to throw an error when enabled.
143  @keras_parameterized.run_all_keras_modes
144  def DISABLED_test_function_model_feature_layer_input(self):
145    col_a = fc.numeric_column('a')
146    col_b = fc.numeric_column('b')
147
148    feature_layer = fc.DenseFeatures([col_a, col_b], name='fc')
149    dense = keras.layers.Dense(4)
150
151    # This seems problematic.... We probably need something for DenseFeatures
152    # the way Input is for InputLayer.
153    output = dense(feature_layer)
154
155    model = keras.models.Model([feature_layer], [output])
156
157    optimizer = 'rmsprop'
158    loss = 'mse'
159    loss_weights = [1., 0.5]
160    model.compile(
161        optimizer,
162        loss,
163        metrics=[metrics_module.CategoricalAccuracy(), 'mae'],
164        loss_weights=loss_weights)
165
166    data = ({'a': np.arange(10), 'b': np.arange(10)}, np.arange(10, 20))
167    print(model.fit(*data, epochs=1))
168
169  # TODO(kaftan) seems to throw an error when enabled.
170  @keras_parameterized.run_all_keras_modes
171  def DISABLED_test_function_model_multiple_feature_layer_inputs(self):
172    col_a = fc.numeric_column('a')
173    col_b = fc.numeric_column('b')
174    col_c = fc.numeric_column('c')
175
176    fc1 = fc.DenseFeatures([col_a, col_b], name='fc1')
177    fc2 = fc.DenseFeatures([col_b, col_c], name='fc2')
178    dense = keras.layers.Dense(4)
179
180    # This seems problematic.... We probably need something for DenseFeatures
181    # the way Input is for InputLayer.
182    output = dense(fc1) + dense(fc2)
183
184    model = keras.models.Model([fc1, fc2], [output])
185
186    optimizer = 'rmsprop'
187    loss = 'mse'
188    loss_weights = [1., 0.5]
189    model.compile(
190        optimizer,
191        loss,
192        metrics=[metrics_module.CategoricalAccuracy(), 'mae'],
193        loss_weights=loss_weights)
194
195    data_list = ([{
196        'a': np.arange(10),
197        'b': np.arange(10)
198    }, {
199        'b': np.arange(10),
200        'c': np.arange(10)
201    }], np.arange(10, 100))
202    print(model.fit(*data_list, epochs=1))
203
204    data_bloated_list = ([{
205        'a': np.arange(10),
206        'b': np.arange(10),
207        'c': np.arange(10)
208    }, {
209        'a': np.arange(10),
210        'b': np.arange(10),
211        'c': np.arange(10)
212    }], np.arange(10, 100))
213    print(model.fit(*data_bloated_list, epochs=1))
214
215    data_dict = ({
216        'fc1': {
217            'a': np.arange(10),
218            'b': np.arange(10)
219        },
220        'fc2': {
221            'b': np.arange(10),
222            'c': np.arange(10)
223        }
224    }, np.arange(10, 100))
225    print(model.fit(*data_dict, epochs=1))
226
227    data_bloated_dict = ({
228        'fc1': {
229            'a': np.arange(10),
230            'b': np.arange(10),
231            'c': np.arange(10)
232        },
233        'fc2': {
234            'a': np.arange(10),
235            'b': np.arange(10),
236            'c': np.arange(10)
237        }
238    }, np.arange(10, 100))
239    print(model.fit(*data_bloated_dict, epochs=1))
240
241
242if __name__ == '__main__':
243  test.main()
244