Python 是一种动态_606_1">动态脚本语言。它不仅具有动态类型系统(可以先将变量分配给一种类型并稍后更改),而且它的对象模型也是动态的。这允许我们在运行时修改其行为。这样做的结果是猴子修补的可能性。这是一个我们可以在不修改更高层代码的情况下修改程序底层的想法。想象一下,您可以使用该print()
函数将某些内容打印到屏幕上,并且我们可以修改该函数的定义以将其打印到文件中,而无需修改任何代码行。

猴子修补 Python 代码
这是可能的,因为Python是一种解释性语言,所以我们可以在程序运行时进行更改。我们可以在Python中利用这个属性来修改类或模块的接口。如果我们正在处理遗留代码或其他人的代码,我们不想对其进行广泛修改,但仍希望使其在不同版本的库或环境中运行,那么它非常有用。在323ai导航网的本教程中,我们将了解如何将此技术应用于一些 Keras 和 TensorFlow 代码。
完成本教程后,您将学到:
- 什么是猴子补丁
- 如何在运行时更改 Python 中的对象或模块
教程概述
本教程分为三个部分;他们是:
- 一种型号,两种接口
- 通过猴子修补来扩展对象
- 猴子修补以恢复遗留代码
一种型号,两种接口
TensorFlow 是一个巨大的库。它提供了一个高级 Keras API 来分层描述深度学习模型。它还带有许多用于训练的功能,例如不同的优化器和数据生成器。仅仅因为我们需要运行经过训练的模型,安装 TensorFlow 就让人不知所措。因此,TensorFlow 为我们提供了一个名为TensorFlow Lite 的对应版本 ,它的尺寸要小得多,适合在移动或嵌入式设备等小型设备中运行。
我们想要展示原始 TensorFlow Keras 模型和 TensorFlow Lite 模型的使用方式有何不同。那么我们来做一个大小适中的模型,比如LeNet-5模型。以下是我们加载 MNIST 数据集并训练分类模型的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
import numpy as np
import tensorflow as tf
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, Dense, AveragePooling2D, Dropout, Flatten
from tensorflow.keras.callbacks import EarlyStopping
# Load MNIST data
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# Reshape data to shape of (n_sample, height, width, n_channel)
X_train = np.expand_dims(X_train, axis=3).astype(‘float32’)
X_test = np.expand_dims(X_test, axis=3).astype(‘float32’)
# LeNet5 model: ReLU can be used intead of tanh
model = Sequential([
Conv2D(6, (5,5), input_shape=(28,28,1), padding=“same”, activation=“tanh”),
AveragePooling2D((2,2), strides=2),
Conv2D(16, (5,5), activation=“tanh”),
AveragePooling2D((2,2), strides=2),
Conv2D(120, (5,5), activation=“tanh”),
Flatten(),
Dense(84, activation=“tanh”),
Dense(10, activation=“softmax”)
])
# Training
model.compile(loss=“sparse_categorical_crossentropy”, optimizer=“adam”, metrics=[“sparse_categorical_accuracy”])
earlystopping = EarlyStopping(monitor=“val_loss”, patience=4, restore_best_weights=True)
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=100, batch_size=32, callbacks=[earlystopping]
|
运行上述代码将使用 TensorFlow 的数据集 API 下载 MNIST 数据集并训练模型。之后,我们可以保存模型:
1
|
model.save(“lenet5-mnist.h5”)
|
或者我们可以用我们的测试集评估模型:
1
2
|
print(np.argmax(model.predict(X_test), axis=1))
print(y_test)
|
然后我们应该看到:
1
2
|
[7 2 1 … 4 5 6]
[7 2 1 … 4 5 6]
|
但如果我们打算将其与 TensorFlow Lite 一起使用,我们需要将其转换为 TensorFlow Lite 格式,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# tflite conversion with dynamic range optimization
import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
# Optional: Save the data for testing
import numpy as np
np.savez(‘mnist-test.npz’, X=X_test, y=y_test)
# Save the model.
with open(‘lenet5-mnist.tflite’, ‘wb’) as f:
f.write(tflite_model
|
我们可以向转换器添加更多选项,例如减少模型以使用 16 位浮点。但在所有情况下,转换的输出都是二进制字符串。转换不仅会将模型减小到更小的尺寸(与从 Keras 保存的 HDF5 文件的大小相比),而且还允许我们将其与轻量级库一起使用。有适用于 Android 和 iOS 移动设备的库。如果您使用嵌入式 Linux,则可以从 PyPI 存储库找到该tflite-runtime
模块(或者可以从 TensorFlow 源代码编译一个模块)。以下是我们如何tflite-runtime
运行转换后的模型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import numpy as np
import tflite_runtime.interpreter as tflite
loaded = np.load(‘mnist-test.npz’)
X_test = loaded[“X”]
y_test = loaded[“y”]
interpreter = tflite.Interpreter(model_path=“lenet5-mnist.tflite”)
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
print(input_details[0][‘shape’])
rows = []
for n in range(len(X_test)):
# this model has single input and single output
interpreter.set_tensor(input_details[0][‘index’], X_test[n:n+1])
interpreter.invoke()
row = interpreter.get_tensor(output_details[0][‘index’])
rows.append(row)
rows = np.vstack(rows)
accuracy = np.sum(np.argmax(rows, axis=1) == y_test) / len(y_test)
print(accuracy
|
事实上,更大的 TensorFlow 库也可以以非常相似的语法运行转换后的模型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import numpy as np
import tensorflow as tf
interpreter = tf.lite.Interpreter(model_path=“lenet5-mnist.tflite”)
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
rows = []
for n in range(len(X_test)):
# this model has single input and single output
interpreter.set_tensor(input_details[0][‘index’], X_test[n:n+1])
interpreter.invoke()
row = interpreter.get_tensor(output_details[0][‘index’])
rows.append(row)
rows = np.vstack(rows)
accuracy = np.sum(np.argmax(rows, axis=1) == y_test) / len(y_test)
print(accuracy)
|
请注意使用模型的不同方式:在 Keras 模型中,我们有一个predict()
函数将批次作为输入并返回结果。然而,在 TensorFlow Lite 模型中,我们必须一次向“解释器”注入一个输入张量并调用它,然后检索结果。
将所有内容放在一起,下面的代码是我们如何构建 Keras 模型、训练它、将其转换为 TensorFlow Lite 格式并使用转换后的模型进行测试: