[New Book] Click to get The Beginner's Guide to Data Science!
Use the offer code 20offearlybird to get 20% off. Hurry, sale ends soon!

Monkey Patching Python Code

Python is a dynamic scripting language. Not only does it have a dynamic type system where a variable can be assigned to one type first and changed later, but its object model is also dynamic. This allows us to modify its behavior at run time. A consequence of this is the possibility of monkey patching. This is an idea that we can modify the base layer of a program without modifying the higher-level code. Imagine you can use the print() function to print something to the screen, and we can modify the definition of this function to print it to a file without modifying any single line of your code.

It is possible because Python is an interpreted language, so we can make changes while the program is running. We can make use of this property in Python to modify the interface of a class or a module. It’s useful if we are dealing with legacy code or code from other people in which we do not want to modify it extensively but still want to make it run with different versions of libraries or environments. In this tutorial, we are going to see how we can apply this technique to some Keras and TensorFlow code.

After finishing this tutorial, you will learn:

  • What is monkey patching
  • How to change an object or a module in Python at runtime

Kick-start your project with my new book Python for Machine Learning, including step-by-step tutorials and the Python source code files for all examples.


Let’s get started.

Monkey Patching Python Code. Photo by Juan Rumimpunu. Some rights reserved.

Tutorial Overview

This tutorial is in three parts; they are:

  • One model, two interfaces
  • Extending an object with monkey patching
  • Monkey patching to revive legacy code

One Model, Two Interfaces

TensorFlow is a huge library. It provides a high-level Keras API to describe deep learning models in layers. It also comes with a lot of functions for training, such as different optimizers and data generators. It is overwhelming to install TensorFlow just because we need to run our trained model. Therefore, TensorFlow provides us with a counterpart called TensorFlow Lite that is much smaller in size and suitable to run in small devices such as mobile or embedded devices.

We want to show how the original TensorFlow Keras model and the TensorFlow Lite model are used differently. So let’s make a model of moderate size, such as the LeNet-5 model. Below is how we load the MNIST dataset and train a model for classification:

Running the above code will download the MNIST dataset using the TensorFlow’s dataset API and train the model. Afterward, we can save the model:

Or we can evaluate the model with our test set:

Then we should see:

But if we intend to use it with TensorFlow Lite, we want to convert it to the TensorFlow Lite format as follows:

We can add more options to the converter, such as reducing the model to use a 16-bit floating point. But in all cases, the output of the conversion is a binary string. Not only will the conversion reduce the model to a much smaller size (compared to the size of the HDF5 file saved from Keras), but it will also allow us to use it with a lightweight library. There are libraries for Android and iOS mobile devices. If you’re using embedded Linux, you may find the tflite-runtime module from the PyPI repository (or you may compile one from TensorFlow source code). Below is how we can use tflite-runtime to run the converted model:

In fact, the larger TensorFlow library can also run the converted model in a very similar syntax:

Note the different ways of using the models: In the Keras model, we have the predict() function that takes a batch as input and returns a result. In the TensorFlow Lite model, however, we have to inject one input tensor at a time to the “interpreter” and invoke it, then retrieve the result.

Putting everything together, the code below is how we build a Keras model, train it, convert it to TensorFlow Lite format, and test with the converted model:

Want to Get Started With Python for Machine Learning?

Take my free 7-day email crash course now (with sample code).

Click to sign-up and also get a free PDF Ebook version of the course.

Extending an Object with Monkey Patching

Can we use predict() in the TensorFlow Lite interpreter?

The interpreter object does not have such a function. But since we’re using Python, it is possible for us to add it using the monkey patching technique. To understand what we are doing, first, we have to note that the interpreter object we defined in the previous code may contain many attributes and functions. When we call interpreter.predict() like a function, Python will look for the one with such a name inside the object, then execute it. If no such name is found, Python will raise the AttributeError exception:

That gives:

To make this work, we need to add a function to the interpreter object with the name predict, and that should behave like one when it is invoked. To make things simple, we notice that our model is a sequential one with an array as input and returns an array of softmax results as output. So we can write a predict() function that behaves like the one from the Keras model, but using the TensorFlow Lite interpreter:

The last line above assigns the function we created to the interpreter object, with the name predict. The __get__(interpreter) part is required to make a function we defined to become a member function of the object interpreter.

With these, we can now run a batch:

This is possible because Python has a dynamic object model. We can modify attributes or member functions of an object at runtime. In fact, this should not surprise us. A Keras model needs to run model.compile() before we can run model.fit(). One effect of the compile function is to add the attribute loss to the model to hold the loss function. This is accomplished at runtime.

With the predict() function added to the interpreter object, we can pass around the interpreter object just like a trained Keras model for prediction. While they are different behind the scenes, they share the same interface so other functions can use it without modifying any line of code.

Below is the complete code to load our saved TensorFlow Lite model, then monkey patch the predict() function to it to make it look like a Keras model:

Monkey Patching to Revive Legacy Code

We can give one more example of monkey patching in Python. Consider the following code:

This code was written a few years back and assumes an older version of Keras with TensorFlow 1.x. The data file sonar.csv can be found in the other post. If we run this code with TensorFlow 2.5, we will see the issue of an ImportError on the line of SGD. We need to make two changes at a minimum in the above code in order to make it run:

  1. Functions and classes should be imported from tensorflow.keras instead of keras
  2. The constraint class maxnorm should be in camel case, MaxNorm

The following is the updated code, in which we modified only the import statements:

If we have a much bigger project with a lot of scripts, it would be tedious to modify every single line of import. But Python’s module system is just a dictionary at sys.modules. Therefore we can monkey patch it to make the old code fit with the new library. The following is how we do it. This works for TensorFlow 2.5 installations (this backward compatibility issue of Keras code was fixed in TensorFlow 2.9; hence you don’t need this patching in the latest version of libraries):

This is definitely not a clean and tidy code, and it will be a problem for future maintenance. Therefore, monkey patching is unwelcomed in production code. However, this would be a quick technique that exploited the inner mechanism of Python language to get something to work easily.

Further Readings

This section provides more resources on the topic if you are looking to go deeper.

Articles

Summary

In this tutorial, we learned what monkey patching is and how to do it. Specifically,

  • We learned how to add a member function to an existing object
  • How to modify the Python module cache at sys.modules to deceive the import statements

Get a Handle on Python for Machine Learning!

Python For Machine Learning

Be More Confident to Code in Python

...from learning the practical Python tricks

Discover how in my new Ebook:
Python for Machine Learning

It provides self-study tutorials with hundreds of working code to equip you with skills including:
debugging, profiling, duck typing, decorators, deployment, and much more...

Showing You the Python Toolbox at a High Level for
Your Projects


See What's Inside

No comments yet.

Leave a Reply