Skip to content

Commit ba399c8

Browse files
tonybove-appleabove3
andauthored
Docs-Guides - Update Examples in Batch 2 (#2097)
Co-authored-by: above3 <[email protected]>
1 parent f392a70 commit ba399c8

File tree

4 files changed

+126
-168
lines changed

4 files changed

+126
-168
lines changed

docs-guides/source/convert-a-tensorflow-1-image-classifier.md

Lines changed: 69 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,28 @@
88

99
# Converting a TensorFlow 1 Image Classifier
1010

11-
The following example converts the TensorFlow Inception V1 image classifier to a Core ML neural network classifier model that directly predicts the class label of the input image. It demonstrates the importance of setting the image preprocessing parameters correctly to get the right results.
11+
The following example converts the TensorFlow Inception V1 image classifier to a Core ML classifier model that directly predicts the class label of the input image. It demonstrates the importance of setting the image preprocessing parameters correctly to get the right results.
1212

13-
## Prerequisites
13+
## Requirements
1414

15-
To use TensorFlow 1 (version 1.15) for this example, you may need to change your version of Python to one that works with TensorFlow 1. For details, see the following:
16-
17-
- For virtual environments, see [How to manage multiple Python versions](https://www.freecodecamp.org/news/manage-multiple-python-versions-and-virtual-environments-venv-pyenv-pyvenv-a29fb00c296f/).
18-
- For Miniconda and Anaconda environments, see [Managing Python](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-python.html).
19-
20-
You may also need to install [Pillow](https://pillow.readthedocs.io/en/stable/), [Requests](https://pypi.org/project/requests/), and [matplotlib](https://pypi.org/project/matplotlib/). The following commands work for a Miniconda environment:
15+
This model requires TensorFlow 1, which is deprecated and difficult to install directly with pip. You can use the appropriate [Miniconda installer](https://docs.conda.io/en/latest/miniconda.html) for your operating system and create a Miniconda environment specifically for Python 3.7, and then use conda to install TensorFlow 1.15:
2116

2217
```shell
23-
conda install python=3.7.9
18+
conda create -n tensorflow1-env python=3.7
19+
conda activate tensorflow1-env
2420
conda install tensorflow==1.15
21+
```
22+
23+
```{note}
24+
For alternatives, see [How to pip install old version of library(tensorflow)](https://stackoverflow.com/questions/41937915/how-to-pip-install-old-version-of-librarytensorflow) on StackOverflow.
25+
```
26+
27+
In addition, you need to install the following for this environment:
28+
29+
```
30+
pip install -U coremltools
2531
pip install pillow
26-
conda install requests
32+
conda install requests
2733
conda install matplotlib
2834
```
2935

@@ -72,7 +78,6 @@ The following code loads the TensorFlow graph to find the input and output tenso
7278
# Load the TF graph definition
7379
import tensorflow as tf # 1.x
7480

75-
7681
tf_model_path = './inception_v1_2016_08_28_frozen.pb'
7782
with open(tf_model_path, 'rb') as f:
7883
serialized = f.read()
@@ -95,68 +100,20 @@ with tf.Graph().as_default() as g:
95100
print("name = {}, shape: {},".format(x.name, x.get_shape())),
96101
```
97102

98-
As shown in the following results, the output of the `Placeholder` op is the input (`input:0`), and the output of the `Softmax` op (near the end of the graph) is the output (`InceptionV1/Logits/Predictions/Softmax:0`).
99-
100-
```text Result
101-
op id 0 : op type: "Placeholder"
102-
input(s):
103-
104-
output(s):
105-
name = input:0, shape: (1, 224, 224, 3),
106-
107-
108-
op id 1 : op type: "Const"
109-
input(s):
110-
111-
output(s):
112-
name = InceptionV1/Conv2d_1a_7x7/weights:0, shape: (7, 7, 3, 64),
113-
114-
115-
op id 2 : op type: "Identity"
116-
input(s):
117-
name = InceptionV1/Conv2d_1a_7x7/weights:0, shape: (7, 7, 3, 64),
118-
119-
output(s):
120-
name = InceptionV1/Conv2d_1a_7x7/weights/read:0, shape: (7, 7, 3, 64),
121-
122-
123-
op id 1012 : op type: "Softmax"
124-
input(s):
125-
name = InceptionV1/Logits/Predictions/Reshape:0, shape: (1, 1001),
126-
127-
output(s):
128-
name = InceptionV1/Logits/Predictions/Softmax:0, shape: (1, 1001),
129-
130-
131-
op id 1013 : op type: "Const"
132-
input(s):
133-
134-
output(s):
135-
name = InceptionV1/Logits/Predictions/Shape:0, shape: (2,),
136-
137-
138-
op id 1014 : op type: "Reshape"
139-
input(s):
140-
name = InceptionV1/Logits/Predictions/Softmax:0, shape: (1, 1001),
141-
name = InceptionV1/Logits/Predictions/Shape:0, shape: (2,),
142-
143-
output(s):
144-
name = InceptionV1/Logits/Predictions/Reshape_1:0, shape: (1, 1001),
145-
```
103+
If you run the code at this point, you can see that the output of the `Placeholder` op is the input (`input:0`), and the output of the `Softmax` op (near the end of the graph) is the output (`InceptionV1/Logits/Predictions/Softmax:0`).
146104

147105
## Convert to Core ML
148106

149-
The following code sets the `image_inputs` for `inputs` and the output name (`'InceptionV1/Logits/Predictions/Softmax'`) for `outputs` in order to use them with the [`convert()`](https://apple.github.io/coremltools/source/coremltools.converters.convert.html#module-coremltools.converters._converters_entry) method. The `convert()` method produces a neural network by default:
107+
The following code sets the `image_inputs` for `inputs` and the output name (`'InceptionV1/Logits/Predictions/Softmax'`) for `outputs` in order to use them with the [`convert()`](https://apple.github.io/coremltools/source/coremltools.converters.convert.html#module-coremltools.converters._converters_entry) method. The `convert()` method produces an ML program by default:
150108

151109
```python
152110
import coremltools as ct
153111

154112
image_inputs = ct.ImageType(shape=(1, 224, 224, 3))
155113
classifier_config = ct.ClassifierConfig('imagenet_slim_labels.txt')
156-
coreml_model_file = './inception_v1.mlmodel'
114+
coreml_model_file = './inception_v1.mlpackage'
157115
output = ['InceptionV1/Logits/Predictions/Softmax']
158116

159-
160117
coreml_model = ct.convert(tf_model_path,
161118
inputs=[image_inputs],
162119
classifier_config=classifier_config,
@@ -165,56 +122,49 @@ coreml_model = ct.convert(tf_model_path,
165122
coreml_model.save(coreml_model_file)
166123
```
167124

168-
The result shows the progress of the conversion:
125+
The result shows the progress of the conversion, but also includes the following warning:
169126

170-
```text Result
171-
Running TensorFlow Graph Passes: 100%|██████████| 7/7 [00:02<00:00, 3.24 passes/s]
172-
Converting Frontend ==> MIL Ops: 100%|██████████| 441/441 [00:00<00:00, 926.87 ops/s]
173-
Running MIL optimization passes: 100%|██████████| 17/17 [00:00<00:00, 20.06 passes/s]
174-
Translating MIL ==> MLModel Ops: 100%|██████████| 839/839 [00:00<00:00, 1085.04 ops/s]
127+
```
128+
UserWarning: Output, 'InceptionV1/Logits/Predictions/Softmax', of the source model, has been renamed to 'InceptionV1_Logits_Predictions_Softmax' in the Core ML model.
175129
```
176130

177-
## Load a Test Image
178-
179-
To make predictions on the same image using both the original model and the converted model, the following sample code snippet loads a test image. It uses [NumPy](https://numpy.org), [Pillow](https://pillow.readthedocs.io/en/stable/), [Requests](https://pypi.org/project/requests/), and [matplotlib](https://pypi.org/project/matplotlib/):
131+
You will use the new name when making a prediction.
180132

181-
```python
182-
# Load an image
183-
import numpy as np
184-
import PIL
185-
import requests
186-
from io import BytesIO
187-
from matplotlib.pyplot import imshow
188-
# This is an image of a golden retriever from Wikipedia
189-
img_url = 'https://upload.wikimedia.org/wikipedia/commons/9/93/Golden_Retriever_Carlos_%2810581910556%29.jpg'
190-
response = requests.get(img_url)
191-
%matplotlib inline
192-
img = PIL.Image.open(BytesIO(response.content))
193-
imshow(np.asarray(img))
194-
```
133+
## Load a Test Image
195134

196-
The code shows the following image:
135+
To make predictions on the same image using both the original model and the converted model, right-click the following image and save it as `Golden_Retriever_Carlos.jpg` in the same folder as your Python project:
197136

198-
```{figure} images/Golden_Retriever_Carlos.png
199-
:alt: Golden Retriever image
137+
```{figure} images/Golden_Retriever_Carlos_full.jpg
138+
:alt: Golden_Retriever_Carlos.jpg
200139
:align: center
201140
:class: imgnoborder
202141
203-
This image of a golden retriever is from Wikipedia.
142+
This image of a golden retriever is from [Wikipedia](https://en.m.wikipedia.org/wiki/File:Golden_Retriever_Carlos_%2810581910556%29.jpg).
204143
```
205144

145+
The following code loads the image:
146+
147+
```python
148+
# Load an image
149+
import numpy as np
150+
from PIL import Image
151+
img = Image.open("Golden_Retriever_Carlos.jpg")
152+
```
206153

207154
## Input the Image and Make a Prediction
208155

209-
The following code passes the PIL image into the Core ML model after resizing it, and uses a NumPy array of the image to make a prediction:
156+
The following code passes the PIL image into the Core ML model after resizing it, and uses a NumPy array of the image to make a prediction. It also fixes the output name to use the renamed output (`'InceptionV1_Logits_Predictions_Softmax'`) in the Core ML model:
210157

211158
```python
212-
# To get CoreML predictions directly pass in the PIL image after resizing
213-
import coremltools
214-
img = img.resize([224,224], PIL.Image.ANTIALIAS)
159+
img = img.resize([224,224], Image.LANCZOS)
215160
coreml_inputs = {'input': img}
161+
162+
# Fix output name
163+
output = ['InceptionV1_Logits_Predictions_Softmax']
164+
216165
coreml_output = coreml_model.predict(coreml_inputs)
217166
coreml_pred_dict = coreml_output[output[0]]
167+
218168
coreml_predicted_class_label = coreml_output['classLabel']
219169

220170
#for getting TF prediction we get the numpy array of the image
@@ -226,40 +176,35 @@ img_tf = np.expand_dims(img_np, axis = 0) #now shape is [1,224,224,3] as require
226176
# Evaluate TF and get the highest label
227177
tf_input_name = 'input:0'
228178
tf_output_name = 'InceptionV1/Logits/Predictions/Softmax:0'
179+
# tf_output_name = 'InceptionV1_Logits_Predictions_Softmax:0'
180+
181+
img_tf = (2.0/255.0) * img_tf - 1
229182
with tf.Session(graph = g) as sess:
230183
tf_out = sess.run(tf_output_name,
231184
feed_dict={tf_input_name: img_tf})
232-
tf_out = tf_out.flatten()
185+
186+
tf_out = tf_out.flatten()
233187
idx = np.argmax(tf_out)
234188
label_file = 'imagenet_slim_labels.txt'
235189
with open(label_file) as f:
236-
labels = f.readlines()
237-
238-
#print predictions
239-
print('\n')
240-
print("CoreML prediction class = {}, probability = {}".format(coreml_predicted_class_label,
241-
str(coreml_pred_dict[coreml_predicted_class_label])))
190+
labels = f.readlines()
191+
192+
# print TF prediction
242193
print("TF prediction class = {}, probability = {}".format(labels[idx],
243194
str(tf_out[idx])))
244-
```
245-
246-
The result shows that both predictions match, which ensures that the conversion is correct. However, the class labels are incorrect:
247-
248-
```text Result
249-
image shape: (224, 224, 3)
250-
first few values: [39. 33. 18. 42.] max value: 255.0
251195

196+
#print Core ML prediction
197+
print('\n')
252198

253-
CoreML prediction class = thatch, probability = 0.5372982025146484
254-
TF prediction class = thatch
255-
, probability = 0.5372873
199+
print("CoreML prediction class = {}, probability = {}".format(coreml_predicted_class_label,
200+
str(coreml_pred_dict[0])))
256201
```
257202

258-
The class labels are incorrect because the image was not preprocessed correctly before it was passed to the neural network.
203+
The result shows that both predictions match, which ensures that the conversion is correct. However, for better results, ensure that the image is preprocessed correctly before passing it to the ML program.
259204

260205
## Preprocess the Image Before Converting
261206

262-
Preprocessing is always a crucial step when using neural networks on images. The best approach is to find the source of the pre-trained model and check for the preprocessing that the model's author used during training and evaluation.
207+
Preprocessing is always a crucial step when using ML programs and neural networks on images. The best approach is to find the source of the pre-trained model and check for the preprocessing that the model's author used during training and evaluation.
263208

264209
In this case, the TensorFlow model comes from the
265210
[SLIM library](https://github.com/tensorflow/models/tree/edb6ed22a801665946c63d650ab9a0b23d98e1b1/research/slim "tensorflow/models/slim/"),
@@ -278,54 +223,47 @@ print("TF prediction class = {}, probability = {}".format(labels[idx],
278223
str(tf_out[idx])))
279224
```
280225

281-
The TensorFlow model now predicts a dog as the highest class:
226+
The TensorFlow model predicts an English Setter as the highest class, (with a probability of 0.301507):
282227

283228
```text Result
284229
TF prediction class = English setter
285230
, probability = 0.301507
286231
```
287232

288-
Core ML automatically handles the image preprocessing when the input is of type image. However, the image biases and scale are not correct. The channel scale should be multiplied first before adding the bias. The following code converts the model again with this correction, and saves the newly converted model:
233+
Core ML automatically handles the image preprocessing when the input is of type image. However, the image biases and scale are not correct. The channel scale should be multiplied first before adding the bias. The following code converts the model again with this correction, and saves the newly converted model. It also makes the prediction again with the newly converted Core ML model:
234+
289235

290236
```python
291237
image_inputs = ct.ImageType(shape=(1, 224, 224, 3), bias=[-1,-1,-1], scale=2.0/255)
292238
classifier_config = ct.ClassifierConfig('imagenet_slim_labels.txt')
293-
coreml_model_file = './inception_v1.mlmodel'
239+
coreml_model_file = './inception_v1.mlpackage'
294240
output = ['InceptionV1/Logits/Predictions/Softmax']
295241

296-
297242
coreml_model = ct.convert(tf_model_path,
298243
inputs=[image_inputs],
299244
classifier_config=classifier_config,
300245
outputs=output)
301246

302247
coreml_model.save(coreml_model_file)
303-
```
304248

305-
The result shows the progress of the conversion:
306-
307-
```text
308-
Running TensorFlow Graph Passes: 100%|██████████| 7/7 [00:02<00:00, 3.10 passes/s]
309-
Converting Frontend ==> MIL Ops: 100%|██████████| 441/441 [00:00<00:00, 998.08 ops/s]
310-
Running MIL optimization passes: 100%|██████████| 17/17 [00:00<00:00, 20.62 passes/s]
311-
Translating MIL ==> MLModel Ops: 100%|██████████| 839/839 [00:00<00:00, 1125.96 ops/s]
312-
```
249+
# Call CoreML predict again
313250

314-
The following code makes the prediction again with the newly converted Core ML model:
251+
# Fix output name
252+
output = ['InceptionV1_Logits_Predictions_Softmax']
315253

316-
```python
317-
# Call CoreML predict again
318254
coreml_output = coreml_model.predict(coreml_inputs)
319255
coreml_pred_dict = coreml_output[output[0]]
320256
coreml_predicted_class_label = coreml_output['classLabel']
321257
print("CoreML prediction class = {}, probability = {}".format(coreml_predicted_class_label,
322-
str(coreml_pred_dict[coreml_predicted_class_label])))
258+
str(coreml_pred_dict[0])))
323259

324260
```
325-
The output now correctly matches the TensorFlow output:
261+
262+
The output predicts the English Setter with higher probability (1.68707207e-04):
326263

327264
```text Result
328-
CoreML prediction class = English setter, probability = 0.3015042543411255
265+
CoreML prediction class = English setter, probability = [1.68707207e-04 4.01963953e-05 2.33356332e-04 ... 1.15576135e-04
266+
3.79885838e-04 2.21910377e-04]
329267
```
330268

331269
```{admonition} Predictions Can Vary Slightly

0 commit comments

Comments
 (0)