{"id":5646,"date":"2022-05-25T06:28:58","date_gmt":"2022-05-25T06:28:58","guid":{"rendered":"https:\/\/www.aiproblog.com\/index.php\/2022\/05\/25\/monkey-patching-python-code\/"},"modified":"2022-05-25T06:28:58","modified_gmt":"2022-05-25T06:28:58","slug":"monkey-patching-python-code","status":"publish","type":"post","link":"https:\/\/www.aiproblog.com\/index.php\/2022\/05\/25\/monkey-patching-python-code\/","title":{"rendered":"Monkey Patching Python Code"},"content":{"rendered":"<p>Author: Adrian Tam<\/p>\n<div>\n<p>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 <code>print()<\/code> 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.<\/p>\n<p>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\u2019s 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.<\/p>\n<p>After finishing this tutorial, you will learn:<\/p>\n<ul>\n<li>What is monkey patching<\/li>\n<li>How to change an object or a module in Python at runtime<\/li>\n<\/ul>\n<p>Let\u2019s get started.<\/p>\n<div id=\"attachment_13187\" style=\"width: 810px\" class=\"wp-caption aligncenter\">\n<img decoding=\"async\" aria-describedby=\"caption-attachment-13187\" class=\"size-full wp-image-13187\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2022\/05\/juan-rumimpunu-nLXOatvTaLo-unsplash-scaled.jpg\" alt=\"\" width=\"800\"><\/p>\n<p id=\"caption-attachment-13187\" class=\"wp-caption-text\">Monkey Patching Python Code. Photo by <a href=\"https:\/\/unsplash.com\/photos\/nLXOatvTaLo\">Juan Rumimpunu<\/a>. Some rights reserved.<\/p>\n<\/div>\n<h2>Tutorial Overview<\/h2>\n<p>This tutorial is in three parts; they are:<\/p>\n<ul>\n<li>One model, two interfaces<\/li>\n<li>Extending an object with monkey patching<\/li>\n<li>Monkey patching to revive legacy code<\/li>\n<\/ul>\n<h2>One Model, Two Interfaces<\/h2>\n<p>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 <strong>trained model<\/strong>. Therefore, TensorFlow provides us with a counterpart called <strong>TensorFlow Lite<\/strong>\u00a0that is much smaller in size and suitable to run in small devices such as mobile or embedded devices.<\/p>\n<p>We want to show how the original TensorFlow Keras model and the TensorFlow Lite model are used differently. So let\u2019s 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:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">import numpy as np\r\nimport tensorflow as tf\r\nfrom tensorflow.keras.datasets import mnist\r\nfrom tensorflow.keras.models import Sequential\r\nfrom tensorflow.keras.layers import Conv2D, Dense, AveragePooling2D, Dropout, Flatten\r\nfrom tensorflow.keras.callbacks import EarlyStopping\r\n\r\n# Load MNIST data\r\n(X_train, y_train), (X_test, y_test) = mnist.load_data()\r\n\r\n# Reshape data to shape of (n_sample, height, width, n_channel)\r\nX_train = np.expand_dims(X_train, axis=3).astype('float32')\r\nX_test = np.expand_dims(X_test, axis=3).astype('float32')\r\n\r\n# LeNet5 model: ReLU can be used intead of tanh\r\nmodel = Sequential([\r\n    Conv2D(6, (5,5), input_shape=(28,28,1), padding=\"same\", activation=\"tanh\"),\r\n    AveragePooling2D((2,2), strides=2),\r\n    Conv2D(16, (5,5), activation=\"tanh\"),\r\n    AveragePooling2D((2,2), strides=2),\r\n    Conv2D(120, (5,5), activation=\"tanh\"),\r\n    Flatten(),\r\n    Dense(84, activation=\"tanh\"),\r\n    Dense(10, activation=\"softmax\")\r\n])\r\n\r\n# Training\r\nmodel.compile(loss=\"sparse_categorical_crossentropy\", optimizer=\"adam\", metrics=[\"sparse_categorical_accuracy\"])\r\nearlystopping = EarlyStopping(monitor=\"val_loss\", patience=4, restore_best_weights=True)\r\nmodel.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=100, batch_size=32, callbacks=[earlystopping])<\/pre>\n<p>Running the above code will download the MNIST dataset using the TensorFlow\u2019s dataset API and train the model. Afterward, we can save the model:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">model.save(\"lenet5-mnist.h5\")<\/pre>\n<p>Or we can evaluate the model with our test set:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">print(np.argmax(model.predict(X_test), axis=1))\r\nprint(y_test)<\/pre>\n<p>Then we should see:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">[7 2 1 ... 4 5 6]\r\n[7 2 1 ... 4 5 6]<\/pre>\n<p>But if we intend to use it with TensorFlow Lite, we want to convert it to the TensorFlow Lite format as follows:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># tflite conversion with dynamic range optimization\r\nimport tensorflow as tf\r\nconverter = tf.lite.TFLiteConverter.from_keras_model(model)\r\nconverter.optimizations = [tf.lite.Optimize.DEFAULT]\r\ntflite_model = converter.convert()\r\n\r\n# Optional: Save the data for testing\r\nimport numpy as np\r\nnp.savez('mnist-test.npz', X=X_test, y=y_test)\r\n\r\n# Save the model.\r\nwith open('lenet5-mnist.tflite', 'wb') as f:\r\n    f.write(tflite_model)<\/pre>\n<p>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\u2019re using embedded Linux, you may find the <code>tflite-runtime<\/code> module from the PyPI repository (or you may compile one from TensorFlow source code). Below is how we can use <code>tflite-runtime<\/code> to run the converted model:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">import numpy as np\r\nimport tflite_runtime.interpreter as tflite\r\n\r\nloaded = np.load('mnist-test.npz')\r\nX_test = loaded[\"X\"]\r\ny_test = loaded[\"y\"]\r\ninterpreter = tflite.Interpreter(model_path=\"lenet5-mnist.tflite\")\r\ninterpreter.allocate_tensors()\r\ninput_details = interpreter.get_input_details()\r\noutput_details = interpreter.get_output_details()\r\nprint(input_details[0]['shape'])\r\n\r\nrows = []\r\nfor n in range(len(X_test)):\r\n    # this model has single input and single output\r\n    interpreter.set_tensor(input_details[0]['index'], X_test[n:n+1])\r\n    interpreter.invoke()\r\n    row = interpreter.get_tensor(output_details[0]['index'])\r\n    rows.append(row)\r\nrows = np.vstack(rows)\r\n\r\naccuracy = np.sum(np.argmax(rows, axis=1) == y_test) \/ len(y_test)\r\nprint(accuracy)<\/pre>\n<p>In fact, the larger TensorFlow library can also run the converted model in a very similar syntax:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">import numpy as np\r\nimport tensorflow as tf\r\n\r\ninterpreter = tf.lite.Interpreter(model_path=\"lenet5-mnist.tflite\")\r\ninterpreter.allocate_tensors()\r\ninput_details = interpreter.get_input_details()\r\noutput_details = interpreter.get_output_details()\r\n\r\nrows = []\r\nfor n in range(len(X_test)):\r\n    # this model has single input and single output\r\n    interpreter.set_tensor(input_details[0]['index'], X_test[n:n+1])\r\n    interpreter.invoke()\r\n    row = interpreter.get_tensor(output_details[0]['index'])\r\n    rows.append(row)\r\nrows = np.vstack(rows)\r\n\r\naccuracy = np.sum(np.argmax(rows, axis=1) == y_test) \/ len(y_test)\r\nprint(accuracy)<\/pre>\n<p>Note the different ways of using the models: In the Keras model, we have the <code>predict()<\/code> 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 \u201cinterpreter\u201d and invoke it, then retrieve the result.<\/p>\n<p>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:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">import numpy as np\r\nimport tensorflow as tf\r\nfrom tensorflow.keras.datasets import mnist\r\nfrom tensorflow.keras.models import Sequential\r\nfrom tensorflow.keras.layers import Conv2D, Dense, AveragePooling2D, Dropout, Flatten\r\nfrom tensorflow.keras.callbacks import EarlyStopping\r\n\r\n# Load MNIST data\r\n(X_train, y_train), (X_test, y_test) = mnist.load_data()\r\n\r\n# Reshape data to shape of (n_sample, height, width, n_channel)\r\nX_train = np.expand_dims(X_train, axis=3).astype('float32')\r\nX_test = np.expand_dims(X_test, axis=3).astype('float32')\r\n\r\n# LeNet5 model: ReLU can be used intead of tanh\r\nmodel = Sequential([\r\n    Conv2D(6, (5,5), input_shape=(28,28,1), padding=\"same\", activation=\"tanh\"),\r\n    AveragePooling2D((2,2), strides=2),\r\n    Conv2D(16, (5,5), activation=\"tanh\"),\r\n    AveragePooling2D((2,2), strides=2),\r\n    Conv2D(120, (5,5), activation=\"tanh\"),\r\n    Flatten(),\r\n    Dense(84, activation=\"tanh\"),\r\n    Dense(10, activation=\"softmax\")\r\n])\r\n\r\n# Training\r\nmodel.compile(loss=\"sparse_categorical_crossentropy\", optimizer=\"adam\", metrics=[\"sparse_categorical_accuracy\"])\r\nearlystopping = EarlyStopping(monitor=\"val_loss\", patience=4, restore_best_weights=True)\r\nmodel.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=100, batch_size=32, callbacks=[earlystopping])\r\n\r\n# Save model\r\nmodel.save(\"lenet5-mnist.h5\")\r\n\r\n# Compare the prediction vs test data\r\nprint(np.argmax(model.predict(X_test), axis=1))\r\nprint(y_test)\r\n\r\n# tflite conversion with dynamic range optimization\r\nimport tensorflow as tf\r\nconverter = tf.lite.TFLiteConverter.from_keras_model(model)\r\nconverter.optimizations = [tf.lite.Optimize.DEFAULT]\r\ntflite_model = converter.convert()\r\n\r\n# Optional: Save the data for testing\r\nimport numpy as np\r\nnp.savez('mnist-test.npz', X=X_test, y=y_test)\r\n\r\n# Save the tflite model.\r\nwith open('lenet5-mnist.tflite', 'wb') as f:\r\n    f.write(tflite_model)\r\n\r\n# Load the tflite model and run test\r\ninterpreter = tf.lite.Interpreter(model_path=\"lenet5-mnist.tflite\")\r\ninterpreter.allocate_tensors()\r\ninput_details = interpreter.get_input_details()\r\noutput_details = interpreter.get_output_details()\r\n\r\nrows = []\r\nfor n in range(len(X_test)):\r\n    # this model has single input and single output\r\n    interpreter.set_tensor(input_details[0]['index'], X_test[n:n+1])\r\n    interpreter.invoke()\r\n    row = interpreter.get_tensor(output_details[0]['index'])\r\n    rows.append(row)\r\nrows = np.vstack(rows)\r\n\r\naccuracy = np.sum(np.argmax(rows, axis=1) == y_test) \/ len(y_test)\r\nprint(accuracy)<\/pre>\n<\/p>\n<h2>Extending an Object with Monkey Patching<\/h2>\n<p>Can we use\u00a0<code>predict()<\/code>\u00a0in the TensorFlow Lite interpreter?<\/p>\n<p>The interpreter object does not have such a function. But since we\u2019re using Python, it is possible for us to add it using the <strong>monkey patching<\/strong> technique. To understand what we are doing, first, we have to note that the <code>interpreter<\/code>\u00a0object we defined in the previous code may contain many attributes and functions. When we call\u00a0<code>interpreter.predict()<\/code> 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 <code>AttributeError<\/code> exception:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\ninterpreter.predict()<\/pre>\n<p>That gives:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">Traceback (most recent call last):\r\n  File \"\/Users\/MLM\/pred_error.py\", line 13, in &lt;module&gt;\r\n    interpreter.predict()\r\nAttributeError: 'Interpreter' object has no attribute 'predict'<\/pre>\n<p>To make this work, we need to add a function to the <code>interpreter<\/code>\u00a0object with the name\u00a0<code>predict<\/code>, 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 <code>predict()<\/code> function that behaves like the one from the Keras model, but using the TensorFlow Lite interpreter:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n\r\n# Monkey patching the tflite model\r\ndef predict(self, input_batch):\r\n    batch_size = len(input_batch)\r\n    output = []\r\n\r\n    input_details = self.get_input_details()\r\n    output_details = self.get_output_details()\r\n    # Run each sample from the batch\r\n    for sample in range(batch_size):\r\n        self.set_tensor(input_details[0][\"index\"], input_batch[sample:sample+1])\r\n        self.invoke()\r\n        sample_output = self.get_tensor(output_details[0][\"index\"])\r\n        output.append(sample_output)\r\n\r\n    # vstack the output of each sample\r\n    return np.vstack(output)\r\n\r\ninterpreter.predict = predict.__get__(interpreter)<\/pre>\n<p>The last line above assigns the function we created to the <code>interpreter<\/code>\u00a0object, with the name\u00a0<code>predict<\/code>. The\u00a0<code>__get__(interpreter)<\/code>\u00a0part is required to make a function we defined to become a member function of the object\u00a0<code>interpreter<\/code>.<\/p>\n<p>With these, we can now run a batch:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\nout_proba = interpreter.predict(X_test)\r\nout = np.argmax(out_proba, axis=1)\r\nprint(out)\r\n\r\naccuracy = np.sum(out == y_test) \/ len(y_test)\r\nprint(accuracy)<\/pre>\n<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">[7 2 1 ... 4 5 6]\r\n0.9879<\/pre>\n<p>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 <code>model.compile()<\/code>\u00a0before we can run\u00a0<code>model.fit()<\/code>. One effect of the compile function is to add the attribute\u00a0<code>loss<\/code>\u00a0to the model to hold the loss function. This is accomplished at runtime.<\/p>\n<p>With the\u00a0<code>predict()<\/code>\u00a0function added to the\u00a0<code>interpreter<\/code>\u00a0object, we can pass around the\u00a0<code>interpreter<\/code> 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.<\/p>\n<p>Below is the complete code to load our saved TensorFlow Lite model, then monkey patch the <code>predict()<\/code> function to it to make it look like a Keras model:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">import numpy as np\r\nimport tensorflow as tf\r\nfrom tensorflow.keras.datasets import mnist\r\n\r\n# Load MNIST data and reshape\r\n(X_train, y_train), (X_test, y_test) = mnist.load_data()\r\nX_train = np.expand_dims(X_train, axis=3).astype('float32')\r\nX_test = np.expand_dims(X_test, axis=3).astype('float32')\r\n\r\n# Monkey patching the tflite model\r\ndef predict(self, input_batch):\r\n    batch_size = len(input_batch)\r\n    output = []\r\n\r\n    input_details = self.get_input_details()\r\n    output_details = self.get_output_details()\r\n    # Run each sample from the batch\r\n    for sample in range(batch_size):\r\n        self.set_tensor(input_details[0][\"index\"], input_batch[sample:sample+1])\r\n        self.invoke()\r\n        sample_output = self.get_tensor(output_details[0][\"index\"])\r\n        output.append(sample_output)\r\n\r\n    # vstack the output of each sample\r\n    return np.vstack(output)\r\n\r\n# Load and monkey patch\r\ninterpreter = tf.lite.Interpreter(model_path=\"lenet5-mnist.tflite\")\r\ninterpreter.predict = predict.__get__(interpreter)\r\ninterpreter.allocate_tensors()\r\n\r\n# test output\r\nout_proba = interpreter.predict(X_test)\r\nout = np.argmax(out_proba, axis=1)\r\nprint(out)\r\naccuracy = np.sum(out == y_test) \/ len(y_test)\r\nprint(accuracy)<\/pre>\n<\/p>\n<h2>Monkey Patching to Revive Legacy Code<\/h2>\n<p>We can give one more example of monkey patching in Python. Consider the following code:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># https:\/\/machinelearningmastery.com\/dropout-regularization-deep-learning-models-keras\/\r\n# Example of Dropout on the Sonar Dataset: Hidden Layer\r\nfrom pandas import read_csv\r\nfrom keras.models import Sequential\r\nfrom keras.layers import Dense\r\nfrom keras.layers import Dropout\r\nfrom keras.wrappers.scikit_learn import KerasClassifier\r\nfrom keras.constraints import maxnorm\r\nfrom keras.optimizers import SGD\r\nfrom sklearn.model_selection import cross_val_score\r\nfrom sklearn.preprocessing import LabelEncoder\r\nfrom sklearn.model_selection import StratifiedKFold\r\nfrom sklearn.preprocessing import StandardScaler\r\nfrom sklearn.pipeline import Pipeline\r\n# load dataset\r\ndataframe = read_csv(\"sonar.csv\", header=None)\r\ndataset = dataframe.values\r\n# split into input (X) and output (Y) variables\r\nX = dataset[:,0:60].astype(float)\r\nY = dataset[:,60]\r\n# encode class values as integers\r\nencoder = LabelEncoder()\r\nencoder.fit(Y)\r\nencoded_Y = encoder.transform(Y)\r\n\r\n# dropout in hidden layers with weight constraint\r\ndef create_model():\r\n\t# create model\r\n\tmodel = Sequential()\r\n\tmodel.add(Dense(60, input_dim=60, activation='relu', kernel_constraint=maxnorm(3)))\r\n\tmodel.add(Dropout(0.2))\r\n\tmodel.add(Dense(30, activation='relu', kernel_constraint=maxnorm(3)))\r\n\tmodel.add(Dropout(0.2))\r\n\tmodel.add(Dense(1, activation='sigmoid'))\r\n\t# Compile model\r\n\tsgd = SGD(lr=0.1, momentum=0.9)\r\n\tmodel.compile(loss='binary_crossentropy', optimizer=sgd, metrics=['accuracy'])\r\n\treturn model\r\n\r\nestimators = []\r\nestimators.append(('standardize', StandardScaler()))\r\nestimators.append(('mlp', KerasClassifier(build_fn=create_model, epochs=300, batch_size=16, verbose=0)))\r\npipeline = Pipeline(estimators)\r\nkfold = StratifiedKFold(n_splits=10, shuffle=True)\r\nresults = cross_val_score(pipeline, X, encoded_Y, cv=kfold)\r\nprint(\"Hidden: %.2f%% (%.2f%%)\" % (results.mean()*100, results.std()*100))<\/pre>\n<p>This code was written a few years back and assumes an older version of Keras with TensorFlow 1.x. The data file <code>sonar.csv<\/code> can be found in <a href=\"https:\/\/machinelearningmastery.com\/dropout-regularization-deep-learning-models-keras\/\">the other post<\/a>. If we run this code with TensorFlow 2.5, we will see the issue of an <code>ImportError<\/code> on the line of <code>SGD<\/code>. We need to make two changes at a minimum in the above code in order to make it run:<\/p>\n<ol>\n<li>Functions and classes should be imported from\u00a0<code>tensorflow.keras<\/code>\u00a0instead of\u00a0<code>keras<\/code>\n<\/li>\n<li>The constraint class\u00a0<code>maxnorm<\/code>\u00a0should be in camel case,\u00a0<code>MaxNorm<\/code>\n<\/li>\n<li style=\"list-style-type: none;\">\n<\/ol>\n<p>The following is the updated code, in which we modified only the import statements:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># Example of Dropout on the Sonar Dataset: Hidden Layer\r\nfrom pandas import read_csv\r\nfrom tensorflow.keras.models import Sequential\r\nfrom tensorflow.keras.layers import Dense\r\nfrom tensorflow.keras.layers import Dropout\r\nfrom tensorflow.keras.wrappers.scikit_learn import KerasClassifier\r\nfrom tensorflow.keras.constraints import MaxNorm as maxnorm\r\nfrom tensorflow.keras.optimizers import SGD\r\nfrom sklearn.model_selection import cross_val_score\r\nfrom sklearn.preprocessing import LabelEncoder\r\nfrom sklearn.model_selection import StratifiedKFold\r\nfrom sklearn.preprocessing import StandardScaler\r\nfrom sklearn.pipeline import Pipeline\r\n# load dataset\r\ndataframe = read_csv(\"sonar.csv\", header=None)\r\ndataset = dataframe.values\r\n# split into input (X) and output (Y) variables\r\nX = dataset[:,0:60].astype(float)\r\nY = dataset[:,60]\r\n# encode class values as integers\r\nencoder = LabelEncoder()\r\nencoder.fit(Y)\r\nencoded_Y = encoder.transform(Y)\r\n\r\n# dropout in hidden layers with weight constraint\r\ndef create_model():\r\n\t# create model\r\n\tmodel = Sequential()\r\n\tmodel.add(Dense(60, input_dim=60, activation='relu', kernel_constraint=maxnorm(3)))\r\n\tmodel.add(Dropout(0.2))\r\n\tmodel.add(Dense(30, activation='relu', kernel_constraint=maxnorm(3)))\r\n\tmodel.add(Dropout(0.2))\r\n\tmodel.add(Dense(1, activation='sigmoid'))\r\n\t# Compile model\r\n\tsgd = SGD(lr=0.1, momentum=0.9)\r\n\tmodel.compile(loss='binary_crossentropy', optimizer=sgd, metrics=['accuracy'])\r\n\treturn model\r\n\r\nestimators = []\r\nestimators.append(('standardize', StandardScaler()))\r\nestimators.append(('mlp', KerasClassifier(build_fn=create_model, epochs=300, batch_size=16, verbose=0)))\r\npipeline = Pipeline(estimators)\r\nkfold = StratifiedKFold(n_splits=10, shuffle=True)\r\nresults = cross_val_score(pipeline, X, encoded_Y, cv=kfold)\r\nprint(\"Hidden: %.2f%% (%.2f%%)\" % (results.mean()*100, results.std()*100))<\/pre>\n<p>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\u2019s module system is just a dictionary at\u00a0<code>sys.modules<\/code>. 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\u2019t need this patching in the latest version of libraries):<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># monkey patching\r\nimport sys\r\nimport tensorflow.keras\r\ntensorflow.keras.constraints.maxnorm = tensorflow.keras.constraints.MaxNorm\r\nfor x in sys.modules.keys():\r\n    if x.startswith(\"tensorflow.keras\"):\r\n        sys.modules[x[len(\"tensorflow.\"):]] = sys.modules[x]\r\n\r\n# Old code below:\r\n\r\n# Example of Dropout on the Sonar Dataset: Hidden Layer\r\nfrom pandas import read_csv\r\nfrom keras.models import Sequential\r\nfrom keras.layers import Dense\r\nfrom keras.layers import Dropout\r\nfrom keras.wrappers.scikit_learn import KerasClassifier\r\nfrom keras.constraints import maxnorm\r\nfrom keras.optimizers import SGD\r\nfrom sklearn.model_selection import cross_val_score\r\nfrom sklearn.preprocessing import LabelEncoder\r\nfrom sklearn.model_selection import StratifiedKFold\r\nfrom sklearn.preprocessing import StandardScaler\r\nfrom sklearn.pipeline import Pipeline\r\n# load dataset\r\ndataframe = read_csv(\"sonar.csv\", header=None)\r\ndataset = dataframe.values\r\n# split into input (X) and output (Y) variables\r\nX = dataset[:,0:60].astype(float)\r\nY = dataset[:,60]\r\n# encode class values as integers\r\nencoder = LabelEncoder()\r\nencoder.fit(Y)\r\nencoded_Y = encoder.transform(Y)\r\n\r\n# dropout in hidden layers with weight constraint\r\ndef create_model():\r\n\t# create model\r\n\tmodel = Sequential()\r\n\tmodel.add(Dense(60, input_dim=60, activation='relu', kernel_constraint=maxnorm(3)))\r\n\tmodel.add(Dropout(0.2))\r\n\tmodel.add(Dense(30, activation='relu', kernel_constraint=maxnorm(3)))\r\n\tmodel.add(Dropout(0.2))\r\n\tmodel.add(Dense(1, activation='sigmoid'))\r\n\t# Compile model\r\n\tsgd = SGD(lr=0.1, momentum=0.9)\r\n\tmodel.compile(loss='binary_crossentropy', optimizer=sgd, metrics=['accuracy'])\r\n\treturn model\r\n\r\nestimators = []\r\nestimators.append(('standardize', StandardScaler()))\r\nestimators.append(('mlp', KerasClassifier(build_fn=create_model, epochs=300, batch_size=16, verbose=0)))\r\npipeline = Pipeline(estimators)\r\nkfold = StratifiedKFold(n_splits=10, shuffle=True)\r\nresults = cross_val_score(pipeline, X, encoded_Y, cv=kfold)\r\nprint(\"Hidden: %.2f%% (%.2f%%)\" % (results.mean()*100, results.std()*100))<\/pre>\n<p>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.<\/p>\n<h2>Further Readings<\/h2>\n<div class=\"page\" title=\"Page 273\">\n<div class=\"layoutArea\">\n<div class=\"column\">\n<p>This section provides more resources on the topic if you are looking to go deeper.<\/p>\n<h4>Articles<\/h4>\n<\/div>\n<\/div>\n<\/div>\n<ul>\n<li>StackOverflow Question \u201c<a href=\"https:\/\/stackoverflow.com\/questions\/5626193\/what-is-monkey-patching\">What is monkey patching?<\/a>\u201c<\/li>\n<li>\n<a href=\"https:\/\/www.tensorflow.org\/lite\/guide\/python\">Python quickstart<\/a>, TensorFlow Lite Guide<\/li>\n<li>\n<a href=\"https:\/\/docs.python.org\/3\/reference\/import.html\">The import system<\/a>, Python Language Reference<\/li>\n<\/ul>\n<h2>Summary<\/h2>\n<p>In this tutorial, we learned what monkey patching is and how to do it. Specifically,<\/p>\n<ul>\n<li>We learned how to add a member function to an existing object<\/li>\n<li>How to modify the Python module cache at <code>sys.modules<\/code> to deceive the <code>import<\/code> statements<\/li>\n<\/ul>\n<p>The post <a rel=\"nofollow\" href=\"https:\/\/machinelearningmastery.com\/monkey-patching-python-code\/\">Monkey Patching Python Code<\/a> appeared first on <a rel=\"nofollow\" href=\"https:\/\/machinelearningmastery.com\/\">Machine Learning Mastery<\/a>.<\/p>\n<\/div>\n<p><a href=\"https:\/\/machinelearningmastery.com\/monkey-patching-python-code\/\">Go to Source<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Author: Adrian Tam Python is a dynamic scripting language. Not only does it have a dynamic type system where a variable can be assigned to [&hellip;] <span class=\"read-more-link\"><a class=\"read-more\" href=\"https:\/\/www.aiproblog.com\/index.php\/2022\/05\/25\/monkey-patching-python-code\/\">Read More<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":5647,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"footnotes":""},"categories":[24],"tags":[],"_links":{"self":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/posts\/5646"}],"collection":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/comments?post=5646"}],"version-history":[{"count":0,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/posts\/5646\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/media\/5647"}],"wp:attachment":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/media?parent=5646"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/categories?post=5646"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/tags?post=5646"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}