*This article is based on PaddlePaddle 0.11.0 and Python 2.7
Foreword¶
If readers have used some image recognition interfaces from Baidu, such as Baidu’s Fine-grained Image Recognition interface, they should understand the process, ignoring other security considerations. The general process of this interface is: we upload images to Baidu’s server, the server converts these images into vector data, and finally this data is passed to a deep learning prediction interface (e.g., PaddlePaddle’s prediction interface) to obtain the prediction result, which is then returned to the client. This is just a simple process; the actual complexity is far more involved, but we only need to understand these basics to build our own image recognition interface.
Environment¶
- System: 64-bit Ubuntu 16.04
- Development language: Python 2.7
- Web framework: Flask
- Prediction interface: Image recognition
Familiarity with Flask¶
Install Flask¶
Installing Flask is straightforward with a single command:
pip install flask
We also use flask_cors for cross-origin resource sharing, so we need to install this library as well:
pip install flask_cors
These are the main libraries. If any other libraries are missing, use the pip command with * as a placeholder for the missing library:
pip install *
Test the Flask Framework¶
Let’s write a simple program to test the installed framework. Using @app.route('/') specifies the access path:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Welcome to PaddlePaddle'
if __name__ == '__main__':
app.run()
Then enter the following address in your browser:
http://127.0.0.1:5000
The browser will return the string we wrote earlier:
Welcome to PaddlePaddle
File Upload¶
Let’s write a program to handle file uploads. This is slightly more complex, so we need to pay attention to the following:
- secure_filename ensures we can correctly retrieve the uploaded file’s name.
- flask_cors enables cross-origin access.
- methods=['POST'] restricts this route to POST requests only.
- f = request.files['img'] reads the file with the form name img.
- f.save(img_path) saves the file to the specified path.
from werkzeug.utils import secure_filename
from flask import Flask, request
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route('/upload', methods=['POST'])
def upload_file():
f = request.files['img']
img_path = './data/' + secure_filename(f.filename)
print img_path
f.save(img_path)
return 'success'
We also create an HTML webpage index.html to test the interface:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Image Prediction</title>
</head>
<body>
<form action="http://127.0.0.1:5000/upload" enctype="multipart/form-data" method="post">
Select an image to predict: <input type="file" name="img"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
Open this webpage in your browser, select a file, and click “Submit”. If the response is success, the file was uploaded successfully. You can check the saved location to confirm the file exists.
Using PaddlePaddle for Prediction¶
Obtain the Prediction Model¶
We use the MNIST handwritten digit recognition example from Chapter 2, as it trains quickly and provides the necessary prediction model. The code is similar to Chapter 2 but adds functionality to generate the inference topology:
# Save the inference topology graph
inference_topology = paddle.topology.Topology(layers=out)
with open("../models/inference_topology.pkl", 'wb') as f:
inference_topology.serialize_for_inference(f)
We also remove the testing part to speed up training:
result = trainer.test(reader=paddle.batch(paddle.dataset.mnist.test(), batch_size=128))
print "\nTest with Pass %d, Cost %f, %s\n" % (event.pass_id, result.cost, result.metrics)
lists.append((event.pass_id, result.cost, result.metrics['classification_error_evaluator']))
This will generate two files:
1. param.tar (model parameters file)
2. inference_topology.pkl (prediction topology file)
Deploy PaddlePaddle to the Server¶
First, we create a queue to handle PaddlePaddle predictions:
app = Flask(__name__)
CORS(app)
# Create the main queue
sendQ = Queue()
Next, we write the file upload interface for inference:
@app.route('/infer', methods=['POST'])
def infer():
# Get the uploaded image
f = request.files['img']
img_path = './data/' + secure_filename(f.filename)
print img_path
# Save the uploaded image
f.save(img_path)
# Convert the uploaded image to vector data
data = []
data.append((load_image(img_path),))
# print 'Prediction data:', data
# Create a sub-queue
recv_queue = Queue()
# Send data and sub-queue to the main queue
sendQ.put((data, recv_queue))
# Retrieve the prediction result from the sub-queue
success, resp = recv_queue.get()
if success:
# Return prediction result if successful
return successResp(resp)
else:
# Return error message if failed
return errorResp(resp)
The code for uploading and saving files was explained earlier. The key part is converting the image to vector data:
data = []
data.append((load_image(img_path),))
The load_image() function remains the same as before:
def load_image(img_path):
im = Image.open(img_path).convert('L')
im = im.resize((28, 28), Image.ANTIALIAS)
im = np.array(im).astype(np.float32).flatten()
im = im / 255.0
return im
Now, we use a thread for PaddlePaddle inference:
- PaddlePaddle initialization and model loading should be done once, outside the loop.
- In the loop, we fetch image data and sub-queues from the main queue.
- Perform prediction with the image data and return results via the sub-queue.
# Create a PaddlePaddle prediction thread
def worker():
# Initialize PaddlePaddle
paddle.init(use_gpu=False, trainer_count=2)
# Load model parameters and prediction topology to create a predictor
with open('../models/param.tar', 'r') as param_f:
params = paddle.parameters.Parameters.from_tar(param_f)
# Get the classifier
out = convolutional_neural_network()
while True:
# Fetch data and sub-queue from the main queue
data, recv_queue = sendQ.get()
try:
# Perform prediction
result = paddle.infer(output_layer=out,
parameters=params,
input=data)
# Process the prediction result
lab = np.argsort(-result)
print lab
# Return the predicted label and probability
result_str = '{"result":%d,"possibility":%f}' % (lab[0][0], result[0][(lab[0][0])])
print result_str
recv_queue.put((True, result_str))
except:
# Send error message via sub-queue
trace = traceback.format_exc()
print trace
recv_queue.put((False, trace))
continue
Back in the infer() function, we retrieve the prediction result from the sub-queue and return it:
# Get the result from the sub-queue
success, resp = recv_queue.get()
if success:
# Return prediction result if successful
return successResp(resp)
else:
# Return error message if failed
return errorResp(resp)
Finally, define functions to format the response data (JSON format):
# Error response
def errorResp(msg):
return jsonify(code=-1, message=msg)
# Success response
def successResp(data):
return jsonify(code=0, message="success", data=data)
Start the prediction thread and the server:
if __name__ == '__main__':
t = threading.Thread(target=worker)
t.daemon = True
t.start()
# Change the port to 80 for accessibility
app.run(host='0.0.0.0', port=80, threaded=True)
Open the index.html file in your browser, update the form’s action to http://127.0.0.1/infer, select an image, and click “Submit”. The prediction result will be returned as JSON:
{
"code": 0,
"data": "{\"result\":3,\"possibility\":1.000000}",
"message": "success"
}
Previous chapter: 《My PaddlePaddle Learning Journey》Note Twelve——Using VisualDL Visualization Tool¶
Next chapter: 《My PaddlePaddle Learning Journey》Note Fourteen——Migrate PaddlePaddle to Android Devices¶
Project Code¶
GitHub address: https://github.com/yeyupiaoling/LearnPaddle
References¶
- http://paddlepaddle.org/
- http://blog.csdn.net/u011054333/article/details/70151857