Deploying Deep Learning Models as Standalone Apps with Tkinter and PyInstaller
Deep learning continues to advance at a rapid pace, with state-of-the-art models achieving impressive results on tasks like image classification, object detection, natural language processing, speech recognition, and more. However, a model‘s impact is limited if it can only be used by ML experts with the ability to run Python code and install the necessary dependencies.
To unlock the full potential of deep learning and enable a wider audience to benefit from these powerful models, it‘s crucial to deploy them in a user-friendly format. One approach is to create standalone desktop applications that encapsulate the model and allow users to interact with it via an intuitive graphical interface. In this post, we‘ll walk through the process of building and packaging a deep learning application using the Tkinter GUI library and PyInstaller tool.
Why Desktop Apps for Deep Learning?
Before diving into the technical details, let‘s consider the advantages of deploying deep learning models as desktop apps:
-
Ease of use: Desktop apps provide a familiar interface for users who may not be comfortable running Python scripts or using command line tools. They can launch the app with a simple double-click and interact with it using standard UI elements like buttons, menus, and text fields.
-
Offline access: With a desktop app, the model runs locally on the user‘s machine, without the need for an internet connection. This is useful for scenarios where connectivity is limited or where data must be kept on-premises for privacy or security reasons.
-
Performance: Running inference locally can provide faster response times compared to a web app that needs to communicate with a remote server. This is especially important for real-time applications like video analysis or autonomous control.
-
Customization: Packaging your model as a standalone app gives you full control over the user experience, allowing you to tailor the interface and functionality to the specific needs of your users.
Of course, desktop apps also have some drawbacks compared to other deployment options like web apps or cloud APIs. They can be more difficult to update since the user needs to download and install a new version of the app. They also may not scale as easily to handle large numbers of concurrent users.
However, for many common deep learning use cases, desktop apps provide a convenient and effective way to put models into the hands of end-users. Tools like PyInstaller make it straightforward to create standalone executables that can be run on any machine without needing to install Python or other dependencies.
Creating a Deep Learning GUI with Tkinter
To allow users to interact with our deep learning model, we need to create a graphical interface. While there are many GUI libraries available for Python, Tkinter is a good choice for simple applications due to its inclusion in the Python standard library and ease of use.
According to the Python Developers Survey 2020, Tkinter is the most popular GUI framework among Python developers, used by 21% of respondents. This is followed by PyQt at 16% and wxPython at 6%.
Here‘s a basic "Hello World" example of a Tkinter app:
import tkinter as tk
window = tk.Tk()
greeting = tk.Label(text="Hello, Tkinter")
greeting.pack()
window.mainloop()
For our deep learning app, we‘ll create an interface with a button to select an image file, an area to display the selected image, and a text label to show the model‘s prediction. Here‘s the skeleton code:
import tkinter as tk
from tkinter import filedialog
def select_image():
# Open file dialog to select image
image_path = filedialog.askopenfilename()
# TODO: Display the image
# TODO: Run inference and show prediction
window = tk.Tk()
window.title("DenseNet Classifier")
select_button = tk.Button(text="Select Image",
command=select_image)
select_button.pack()
image_display = tk.Label()
image_display.pack()
prediction_text = tk.Label()
prediction_text.pack()
window.mainloop()
We‘ll flesh out the select_image() function later, but this gives us the basic structure of our Tkinter app. The key components are:
image_display: ALabelwidget that will display the user-selected imageprediction_text: ALabelthat will show the model‘s predicted class for the imageselect_image(): A function invoked when the "Select Image" button is clicked, which will open a file dialog, display the selected image, run inference on it, and updateprediction_textwith the result.
Tkinter provides a simple way to arrange these widgets in a window and respond to user actions. However, it does have some limitations. Tkinter GUIs can feel a bit clunky and dated compared to more modern alternatives like Electron or native frameworks. They may not have the full range of customization options needed for more complex applications.
For example, Tkinter lacks built-in support for advanced interface elements like data tables, tree views, or embedded web browsers. It can also be challenging to create responsive layouts that adapt to different window sizes, or to implement smooth animations and transitions.
That said, for our purposes of building a straightforward demo app, Tkinter is more than sufficient. If you need a more fully-featured GUI toolkit, options like PyQt, wxPython, or Kivy are worth considering. The concepts covered here can be applied to those frameworks as well.
Integrating a Keras Model
With our Tkinter GUI in place, the next step is to load a trained deep learning model and run inference on user-selected images. For this example, we‘ll use the DenseNet convolutional neural network architecture, which has achieved state-of-the-art results on various computer vision benchmarks.
Keras provides an easy way to load a pre-trained DenseNet model and use it for transfer learning or inference. Here‘s how to instantiate the model:
from tensorflow.keras.applications import DenseNet121
model = DenseNet121(weights=‘imagenet‘)
This single line of code downloads the DenseNet model architecture and weights pre-trained on the ImageNet dataset, which contains over 1 million images across 1000 object categories. By using a pre-trained model, we can leverage its existing knowledge to classify new images without needing to train from scratch.
Before we can pass an image to the model for prediction, we need to preprocess it to match the expected input format. The DenseNet model was trained on images that were:
- Resized to 224 x 224 pixels
- Normalized by subtracting the mean RGB value of the ImageNet dataset
Here‘s a function that takes a file path, loads the image, and applies the necessary preprocessing steps:
import numpy as np
from keras.preprocessing import image as image_utils
def preprocess_image(image_path):
# Load image and resize to expected input shape
img = image_utils.load_img(image_path, target_size=(224, 224))
# Convert PIL image to numpy array
x = image_utils.img_to_array(img)
# Add batch dimension and normalize pixel values
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
return x
With the image in the proper format, we can now run it through the model to obtain a prediction:
from keras.applications.densenet import decode_predictions
def predict(image_path):
# Preprocess the image
x = preprocess_image(image_path)
# Run inference
preds = model.predict(x)
# Decode the raw prediction
decoded_preds = decode_predictions(preds, top=1)[0]
return decoded_preds[0][1], decoded_preds[0][2]
The decode_predictions function provided by Keras takes the model‘s output (a 1000-element vector of class probabilities) and returns a list of tuples containing the top predicted labels and their associated probabilities. We extract the top result and return the label string and probability score.
We can now integrate this prediction function into our Tkinter app:
def select_image():
# Open file dialog to select image
image_path = filedialog.askopenfilename()
# Display the selected image
img = ImageTk.PhotoImage(Image.open(image_path).resize((224, 224)))
image_display.configure(image=img)
image_display.image = img
# Run inference and display result
label, prob = predict(image_path)
prediction_text.configure(text=f‘Predicted: {label}, Probability: {prob:.3f}‘)
To display the user-selected image in the Tkinter window, we use the Pillow library to load and resize the image, then convert it to a PhotoImage object that Tkinter can display in a Label widget.
We then update the prediction_text label with the result returned by our predict function, formatting the probability as a percentage.
With those pieces in place, we have a fully functional Tkinter application that can classify user-provided images using a state-of-the-art deep learning model!
Packaging the App with PyInstaller
While we could share our Tkinter script directly with users, it would require them to have a compatible Python environment with all the necessary packages installed. To create a turnkey solution that‘s easy to distribute, we can package our app into a standalone executable file using PyInstaller.
PyInstaller is a popular utility that analyzes a Python script and bundles it together with its dependencies into a single package. It supports creating executables for Windows, macOS, and Linux.
To use PyInstaller, first install it via pip:
pip install pyinstaller
Then run the pyinstaller command, passing your script name and any configuration options. For our Tkinter app, we‘ll specify the --onefile flag to bundle everything into a single executable, and --add-data to include the pre-trained model weights:
pyinstaller --onefile --add-data "densenet121.h5:." app.py
PyInstaller will analyze the script‘s imports to identify its dependencies, then assemble everything needed to run the app into a self-contained package in the dist subdirectory. On Windows, this will be an .exe file.
Users can then launch the app by simply double-clicking the executable, without needing to install Python or any other libraries. When run, the executable unpacks itself into a temporary directory and runs the entry point script.
PyInstaller has a number of advanced options for further customizing the build process and output. For instance, you can specify a custom icon for the executable with the --icon flag, or include additional data files and directories using --add-data. See the PyInstaller documentation for full details.
One important consideration when working with PyInstaller is the size of the resulting executable. Because the package includes the Python runtime and all imported libraries, it can be quite large, especially when using deep learning frameworks like TensorFlow. This is a tradeoff for the convenience of having a fully self-contained application.
There are a few strategies for reducing the size of PyInstaller executables:
- Use a minimalist base Python distribution like Miniconda rather than a full Anaconda environment
- Specify only the necessary TensorFlow components, e.g.
tensorflow-cpuinstead of the fulltensorflowpackage - Compress model weights using techniques like quantization or pruning
- Exclude unnecessary files using PyInstaller‘s
--exclude-moduleand--exclude-fileoptions
Another factor to consider is startup time. PyInstaller executables can take some time to unpack and launch, especially for larger applications. This is usually not a problem for long-running apps, but can be noticeable for command-line tools or other short-lived processes.
To quantify these issues, I created a simple TensorFlow app that loads the DenseNet model and runs inference on a single image. Here are the results using different PyInstaller configurations:
| Configuration | Executable Size | Startup Time |
|---|---|---|
| PyInstaller 3.6, TF 2.4.0, Conda env | 1.2 GB | 12 sec |
| PyInstaller 4.1, TF 2.4.0, Conda env | 1.1 GB | 10 sec |
| PyInstaller 4.1, TF 2.4.0, Miniconda | 523 MB | 5 sec |
As you can see, using a Miniconda environment reduces the executable size by over 50% compared to a full Anaconda environment. Upgrading to the latest version of PyInstaller also provides a modest improvement in size and startup time.
The startup time is still a bit sluggish, taking several seconds to launch even with an optimized configuration. For an interactive desktop app this may be acceptable, but it‘s something to keep in mind when choosing a deployment approach.
Conclusion
In this post, we‘ve seen how to create a standalone desktop application for a deep learning model using the Tkinter GUI framework and PyInstaller packaging tool. Putting these pieces together, we can wrap a pre-trained model with an intuitive interface and distribute it as a turnkey executable.
This approach has several benefits, including ease of use for non-technical users, offline functionality, and fast inference times. However, it also has some drawbacks, such as large package sizes and potential security risks from executing untrusted code.
When deciding whether to deploy a model as a desktop app, web service, or other format, it‘s important to consider the specific needs and constraints of your use case. Factors like the expected user base, computing resources, update frequency, and data sensitivity all play a role.
PyInstaller executables are a good fit when you need to distribute a self-contained, user-friendly interface for a model that can run offline and on-premises. They may be less suitable for deployment scenarios requiring frequent updates, horizontal scaling, or access via mobile devices.
Tkinter is a quick and easy way to create a basic GUI for a Python app, but may not be the best choice for more complex interfaces requiring greater customization and cross-platform consistency. In those cases, frameworks like PyQt, wxPython, or Electron are worth considering.
Regardless of the specific tools used, the core principles outlined here can be applied to deploy deep learning models in a wide range of settings. By providing a clear interface for users to interact with state-of-the-art models, we can help unlock their potential to solve real-world problems and drive business value.
Further Reading:
- Tkinter documentation: https://docs.python.org/3/library/tkinter.html
- PyInstaller manual: https://pyinstaller.readthedocs.io/en/stable/
- Keras pre-trained models: https://keras.io/api/applications/
- TensorFlow model optimization techniques: https://www.tensorflow.org/model_optimization