11import unittest
22
33import numpy as np
4+ import tempfile
45import pytest
6+ import shutil
7+ import os
58
69import coremltools
710from coremltools ._deps import HAS_KERAS_TF
1518 from keras .layers import Dense , LSTM
1619 from coremltools .converters import keras as keras_converter
1720
18- if HAS_TF :
21+ if HAS_TF :
1922 import tensorflow as tf
2023 from tensorflow .python .platform import gfile
2124 from tensorflow .python .tools import freeze_graph
2225 import tfcoreml as tf_converter
23- tf .disable_eager_execution ()
26+
27+ tf .compat .v1 .disable_eager_execution ()
28+
2429
2530@unittest .skipIf (not HAS_KERAS_TF , 'Missing keras. Skipping tests.' )
2631@pytest .mark .keras1
2732class KerasBasicNumericCorrectnessTest (unittest .TestCase ):
28-
33+
2934 def test_classifier (self ):
3035 np .random .seed (1988 )
31-
36+
3237 print ('running test classifier' )
33-
38+
3439 input_dim = 5
3540 num_hidden = 12
3641 num_classes = 6
3742 input_length = 3
38-
43+
3944 model = Sequential ()
4045 model .add (LSTM (num_hidden , input_dim = input_dim , input_length = input_length , return_sequences = False ))
4146 model .add (Dense (num_classes , activation = 'softmax' ))
42-
47+
4348 model .set_weights ([np .random .rand (* w .shape ) for w in model .get_weights ()])
44-
49+
4550 input_names = ['input' ]
4651 output_names = ['zzzz' ]
4752 class_labels = ['a' , 'b' , 'c' , 'd' , 'e' , 'f' ]
4853 predicted_feature_name = 'pf'
4954 coremlmodel = keras_converter .convert (model , input_names , output_names , class_labels = class_labels , predicted_feature_name = predicted_feature_name , predicted_probabilities_output = output_names [0 ])
50-
55+
5156 if is_macos () and macos_version () >= (10 , 13 ):
5257 inputs = np .random .rand (input_dim )
5358 outputs = coremlmodel .predict ({'input' : inputs })
5459 # this checks that the dictionary got the right name and type
5560 self .assertEquals (type (outputs [output_names [0 ]]), type ({'a' : 0.5 }))
56-
61+
5762 def test_classifier_no_name (self ):
5863 np .random .seed (1988 )
59-
64+
6065 input_dim = 5
6166 num_hidden = 12
6267 num_classes = 6
6368 input_length = 3
64-
69+
6570 model = Sequential ()
6671 model .add (LSTM (num_hidden , input_dim = input_dim , input_length = input_length , return_sequences = False ))
6772 model .add (Dense (num_classes , activation = 'softmax' ))
68-
73+
6974 model .set_weights ([np .random .rand (* w .shape ) for w in model .get_weights ()])
70-
75+
7176 input_names = ['input' ]
7277 output_names = ['zzzz' ]
7378 class_labels = ['a' , 'b' , 'c' , 'd' , 'e' , 'f' ]
7479 predicted_feature_name = 'pf'
7580 coremlmodel = keras_converter .convert (model , input_names , output_names , class_labels = class_labels , predicted_feature_name = predicted_feature_name )
76-
81+
7782 if is_macos () and macos_version () >= (10 , 13 ):
7883 inputs = np .random .rand (input_dim )
7984 outputs = coremlmodel .predict ({'input' : inputs })
8085 # this checks that the dictionary got the right name and type
8186 self .assertEquals (type (outputs [output_names [0 ]]), type ({'a' : 0.5 }))
82-
87+
8388 def test_internal_layer (self ):
84-
89+
8590 np .random .seed (1988 )
86-
91+
8792 input_dim = 5
8893 num_channels1 = 10
8994 num_channels2 = 7
9095 num_channels3 = 5
91-
92- w1 = (np .random .rand (input_dim , num_channels1 )- 0.5 )/ 5.0 ;
93- w2 = (np .random .rand (num_channels1 , num_channels2 )- 0.5 )/ 5.0 ;
94- w3 = (np .random .rand (num_channels2 , num_channels3 )- 0.5 )/ 5.0 ;
95-
96- b1 = (np .random .rand (num_channels1 ,) - 0.5 )/ 5.0 ;
97- b2 = (np .random .rand (num_channels2 ,) - 0.5 )/ 5.0 ;
98- b3 = (np .random .rand (num_channels3 ,) - 0.5 )/ 5.0 ;
99-
96+
97+ w1 = (np .random .rand (input_dim , num_channels1 ) - 0.5 ) / 5.0 ;
98+ w2 = (np .random .rand (num_channels1 , num_channels2 ) - 0.5 ) / 5.0 ;
99+ w3 = (np .random .rand (num_channels2 , num_channels3 ) - 0.5 ) / 5.0 ;
100+
101+ b1 = (np .random .rand (num_channels1 , ) - 0.5 ) / 5.0 ;
102+ b2 = (np .random .rand (num_channels2 , ) - 0.5 ) / 5.0 ;
103+ b3 = (np .random .rand (num_channels3 , ) - 0.5 ) / 5.0 ;
104+
100105 model = Sequential ()
101- model .add (Dense (num_channels1 , input_dim = input_dim ))
106+ model .add (Dense (num_channels1 , input_dim = input_dim ))
102107 model .add (Dense (num_channels2 , name = 'middle_layer' ))
103108 model .add (Dense (num_channels3 ))
104-
109+
105110 model .set_weights ([w1 , b1 , w2 , b2 , w3 , b3 ])
106-
111+
107112 input_names = ['input' ]
108113 output_names = ['output' ]
109114 coreml1 = keras_converter .convert (model , input_names , output_names )
110-
115+
111116 # adjust the output parameters of coreml1 to include the intermediate layer
112117 spec = coreml1 .get_spec ()
113118 coremlNewOutputs = spec .description .output .add ()
114119 coremlNewOutputs .name = 'middle_layer_output'
115120 coremlNewParams = coremlNewOutputs .type .multiArrayType
116121 coremlNewParams .dataType = coremltools .proto .FeatureTypes_pb2 .ArrayFeatureType .ArrayDataType .Value ('DOUBLE' )
117122 coremlNewParams .shape .extend ([num_channels2 ])
118-
123+
119124 coremlfinal = coremltools .models .MLModel (spec )
120-
125+
121126 # generate a second model which
122127 model2 = Sequential ()
123- model2 .add (Dense (num_channels1 , input_dim = input_dim ))
128+ model2 .add (Dense (num_channels1 , input_dim = input_dim ))
124129 model2 .add (Dense (num_channels2 ))
125130 model2 .set_weights ([w1 , b1 , w2 , b2 ])
126-
131+
127132 coreml2 = keras_converter .convert (model2 , input_names , ['output2' ])
128-
133+
129134 if is_macos () and macos_version () >= (10 , 13 ):
130135 # generate input data
131136 inputs = np .random .rand (input_dim )
132137
133138 fullOutputs = coremlfinal .predict ({'input' : inputs })
134-
139+
135140 partialOutput = coreml2 .predict ({'input' : inputs })
136-
141+
137142 for i in range (0 , num_channels2 ):
138143 self .assertAlmostEquals (fullOutputs ['middle_layer_output' ][i ], partialOutput ['output2' ][i ], 2 )
139144
145+
140146@unittest .skipIf (not HAS_TF , 'Missing TF. Skipping tests.' )
141147class TFBasicConversionTest (unittest .TestCase ):
142148
143- frozen_graph_file = '/tmp/my_frozen_classifier_graph.pb'
144- converted_coreml_file = '/tmp/my_frozen_classifier_graph.mlmodel'
149+ @classmethod
150+ def setUpClass (self ):
151+ self .tmp_dir = tempfile .mkdtemp ()
152+ _ , self .graph_file = tempfile .mkstemp (suffix = '.pb' , prefix = self .tmp_dir )
153+ _ , self .checkpoint_file = tempfile .mkstemp (suffix = '.ckpt' , prefix = self .tmp_dir )
154+ _ , self .class_label_file = tempfile .mkstemp (suffix = '.txt' , prefix = self .tmp_dir )
155+ _ , self .frozen_graph_file = tempfile .mkstemp (suffix = '.pb' , prefix = self .tmp_dir )
156+ _ , self .converted_coreml_file = tempfile .mkstemp (suffix = '.mlmodel' , prefix = self .tmp_dir )
157+
158+ @classmethod
159+ def tearDownClass (self ):
160+ if os .path .exists (self .tmp_dir ):
161+ shutil .rmtree (self .tmp_dir )
145162
146163 def my_conv_2d (self , input , weight_shape , num_filters , strides , name , activation_fn = tf .nn .relu , with_bias_add = True ):
147- my_weights = tf .get_variable (name = name + " weights" , shape = weight_shape )
164+ my_weights = tf .get_variable (name = name + ' weights' , shape = weight_shape )
148165 if with_bias_add :
149- my_bias = tf .get_variable (name = name + " bias" , shape = num_filters )
166+ my_bias = tf .get_variable (name = name + ' bias' , shape = num_filters )
150167 my_conv = tf .nn .conv2d (input , my_weights , strides = strides , padding = 'SAME' , name = name )
151168 if with_bias_add :
152169 my_conv = tf .nn .bias_add (my_conv , my_bias )
@@ -157,9 +174,9 @@ def my_conv_2d(self, input, weight_shape, num_filters, strides, name, activation
157174 return conv_layer_out
158175
159176 def test_1 (self ):
160- with open ('/tmp/labels.txt' , 'w+' ) as labels_file :
177+ with open (self . class_label_file , 'w+' ) as labels_file :
161178 for a in range (10 ):
162- labels_file .write (str (a + 1 )+ "\n " )
179+ labels_file .write (str (a + 1 ) + "\n " )
163180
164181 image_size = 224
165182
@@ -171,28 +188,25 @@ def test_1(self):
171188 i_placeholder = tf .placeholder (name = 'input' , dtype = tf .float32 , shape = [1 , image_size , image_size , 3 ])
172189 net = self .my_conv_2d (i_placeholder , [1 , 3 , 3 , 1 ], 1 , 1 , 'first' )
173190 net = tf .nn .avg_pool2d (net , 224 , strides = 1 , padding = 'VALID' , name = 'AvgPool_1a' )
174- net = self .my_conv_2d (net ,[1 , 1 , 1 , 10 ], 10 , 1 , 'fc' , activation_fn = None , with_bias_add = False )
191+ net = self .my_conv_2d (net , [1 , 1 , 1 , 10 ], 10 , 1 , 'fc' , activation_fn = None , with_bias_add = False )
175192 net = tf .squeeze (net , [1 , 2 ], name = 'SpatialSqueeze' )
176193 probabilities = tf .nn .softmax (net , name = 'Softmax' )
177194
178195 saver = tf .train .Saver ()
179196 init_op = tf .global_variables_initializer ()
180197
181- checkpoint_file = '/tmp/my_classifier_model.ckpt'
182- graph_file = '/tmp/my_classifier_graph.pb'
183-
184198 with tf .Session () as sess :
185199 sess .run (init_op )
186200 probabilities = sess .run (probabilities , {i_placeholder : images .eval ()})
187- save_path = saver .save (sess , checkpoint_file )
201+ save_path = saver .save (sess , self . checkpoint_file )
188202
189- with gfile .GFile (graph_file , 'wb' ) as f :
203+ with gfile .GFile (self . graph_file , 'wb' ) as f :
190204 f .write (sess .graph_def .SerializeToString ())
191205
192- freeze_graph .freeze_graph (graph_file ,
206+ freeze_graph .freeze_graph (self . graph_file ,
193207 '' ,
194208 True ,
195- checkpoint_file ,
209+ self . checkpoint_file ,
196210 'Softmax' ,
197211 '' ,
198212 '' ,
@@ -202,39 +216,39 @@ def test_1(self):
202216
203217 def test_cassifier_with_label_file (self ):
204218 tf_converter .convert (self .frozen_graph_file ,
205- mlmodel_path = self .converted_coreml_file ,
206- input_name_shape_dict = {'input' : [1 , 224 , 224 , 3 ]},
207- image_input_names = ['input' ],
208- output_feature_names = ['Softmax' ],
209- predicted_feature_name = 'classLabel' ,
210- class_labels = '/tmp/labels.txt' ,
219+ mlmodel_path = self .converted_coreml_file ,
220+ input_name_shape_dict = {'input' : [1 , 224 , 224 , 3 ]},
221+ image_input_names = ['input' ],
222+ output_feature_names = ['Softmax' ],
223+ predicted_feature_name = 'classLabel' ,
224+ class_labels = self . class_label_file ,
211225 minimum_ios_deployment_target = '13' )
212226
213227 def test_cassifier_with_int_label_list (self ):
214228 tf_converter .convert (self .frozen_graph_file ,
215- mlmodel_path = self .converted_coreml_file ,
216- input_name_shape_dict = {'input' : [1 , 224 , 224 , 3 ]},
217- image_input_names = ['input' ],
218- output_feature_names = ['Softmax' ],
219- predicted_feature_name = 'classLabel' ,
220- class_labels = [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ],
229+ mlmodel_path = self .converted_coreml_file ,
230+ input_name_shape_dict = {'input' : [1 , 224 , 224 , 3 ]},
231+ image_input_names = ['input' ],
232+ output_feature_names = ['Softmax' ],
233+ predicted_feature_name = 'classLabel' ,
234+ class_labels = [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ],
221235 minimum_ios_deployment_target = '13' )
222236
223237 def test_cassifier_with_string_label_list (self ):
224238 tf_converter .convert (self .frozen_graph_file ,
225- mlmodel_path = self .converted_coreml_file ,
226- input_name_shape_dict = {'input' : [1 , 224 , 224 , 3 ]},
227- image_input_names = ['input' ],
228- output_feature_names = ['Softmax' ],
229- predicted_feature_name = 'classLabel' ,
230- class_labels = ['1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , '10' ],
239+ mlmodel_path = self .converted_coreml_file ,
240+ input_name_shape_dict = {'input' : [1 , 224 , 224 , 3 ]},
241+ image_input_names = ['input' ],
242+ output_feature_names = ['Softmax' ],
243+ predicted_feature_name = 'classLabel' ,
244+ class_labels = ['1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , '10' ],
231245 minimum_ios_deployment_target = '13' )
232246
247+
233248class CustomLayerUtilsTest (unittest .TestCase ):
234249
235250 @classmethod
236251 def setUpClass (self ):
237-
238252 spec = Model_pb2 .Model ()
239253 spec .specificationVersion = coremltools .SPECIFICATION_VERSION
240254
@@ -270,14 +284,12 @@ def setUpClass(self):
270284 self .spec = spec
271285
272286 def test_get_custom_names (self ):
273-
274287 names = get_custom_layer_names (self .spec )
275288 self .assertEqual (names , {'name1' , 'name2' })
276289
277290 def test_change_custom_name (self ):
278-
279291 replace_custom_layer_name (self .spec , 'name1' , 'notname1' )
280292 names = get_custom_layer_names (self .spec )
281293 self .assertEqual (names , {'notname1' , 'name2' })
282- #set it back for future tests
294+ # set it back for future tests
283295 replace_custom_layer_name (self .spec , 'notname1' , 'name1' )
0 commit comments