обзор
Нейронная сеть, обученная распознавать изображения, содержащие огонь или пламя, может сделать системы обнаружения пожара более надежными и экономически эффективными. В этом руководстве показано, как использовать недавно выпущенные API-интерфейсы Python для механизма вывода Arm NN, чтобы классифицировать изображения как «Огонь» по сравнению с «Не-Огонь».
Что такое Arm NN и PyArmNN?
Arm NN — это механизм логического вывода для процессоров, графических процессоров и процессоров. Он выполняет модели ML на устройстве, чтобы делать прогнозы на основе входных данных. Arm NN обеспечивает эффективную трансляцию существующих сред нейронных сетей, таких как TensorFlow Lite, TensorFlow, ONNX и Caffe, что позволяет им работать эффективно и без изменений в процессорах Arm Cortex-A, GPU Arm Mali и NPU Arm Ethos.
PyArmNN — это недавно разработанное расширение Python для Arm NN SDK. В этом руководстве мы собираемся использовать API-интерфейсы PyArmNN для запуска модели классификации изображений для обнаружения пожара fire_detection.tflite и сравнивать производительность вывода с TensorFlow Lite на Raspberry Pi.
Arm NN предоставляет анализатор TFLite armnnTfLiteParser, который представляет собой библиотеку для загрузки нейронных сетей, определенных файлами TensorFlow Lite FlatBuffers, во время выполнения Arm NN. Мы собираемся использовать анализатор TFLite для анализа нашей модели обнаружения пожара для классификации изображений «Пожар» и «Не огонь».
Вам также может понравиться:
Пример Python REST API (с микросервисами) — Часть 1
Что нам нужно?
- Малиновый пи. Я тестирую с Raspberry Pi 4 с ОС Raspbian 10. Устройство Pi питается от процессора Arm Cortex-A72, который может использовать мощность Arm NN SDK для ускоренной работы ML.
- Прежде чем приступить к настройке проекта, вам нужно проверить и собрать Arm NN версии 19.11 или новее для вашего Raspberry Pi. Инструкции здесь .
- Пакет PyArmNN
- fire_detection.tflite, сгенерированный из этого руководства и преобразованный в модель TensorFlow Lite.
Выполнить вывод ML с PyArmNN
Чтобы запустить модель ML на устройстве, наши основные шаги:
- Импортировать модуль pyarmnn
- Загрузить входное изображение
- Создайте парсер и загрузите сеть
- Выберите бэкэнды, создайте среду выполнения и оптимизируйте модель
- Выполнить вывод
- Интерпретировать и сообщить результат
Импорт модуля pyarmnn
питон
xxxxxxxxxx
1
import PIL
2
from PIL import Image
3
import pyarmnn as ann
4
import numpy as np
5
import cv2
6
7
print(f"Working with ARMNN {ann.ARMNN_VERSION}")
Наша модель — это модель с плавающей точкой. Мы должны масштабировать значения входного изображения в диапазоне от -1 до 1.
питон
xxxxxxxxxx
1
parser = argparse.ArgumentParser(
2
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
3
parser.add_argument(
4
'--image', help='File path of image file', required=True)
5
args = parser.parse_args()
6
7
# Load an image.
8
image = cv2.imread(args.image)
9
image = cv2.resize(image, (128, 128))
10
image = np.array(image, dtype=np.float32) / 255.0
11
print(image.size)
Создайте парсер и загрузите сеть
Следующим шагом при работе с Armn NN является создание объекта парсера, который будет использоваться для загрузки сетевого файла. В Arm NN есть парсеры для различных типов файлов моделей, включая TFLite, ONNX, Caffe и т. Д. Парсеры обрабатывают создание основного графа Arm NN, поэтому вам не нужно создавать график модели вручную.
В этом примере мы создадим анализатор TfLite для загрузки нашей модели TensorFlow Lite по указанному пути:
питон
xxxxxxxxxx
1
parser = ann.ITfLiteParser()
2
network = parser.CreateNetworkFromBinaryFile('./fire_detection.tflite')
Получить информацию о привязке ввода
После создания анализатор используется для извлечения входной информации для сети.
Мы можем извлечь все входные имена, вызвав GetSubgraphInputTensorNames (), а затем использовать их для получения информации о привязке ввода. В этом примере, поскольку наша модель имеет только один входной слой, мы используем input_names [0] для получения входного тензора, а затем используем эту строку для получения информации о входной привязке.
Информация связывания ввода содержит всю необходимую информацию о входе, это кортеж, состоящий из целочисленных идентификаторов для привязываемых слоев (входы, выходы) и информация тензора (тип данных, информация квантования, количество измерений, общее количество элементов).
питон
xxxxxxxxxx
1
graph_id = 0
2
input_names = parser.GetSubgraphInputTensorNames(graph_id)
3
input_binding_info = parser.GetNetworkInputBindingInfo(graph_id, input_names[0])
4
input_tensor_id = input_binding_info[0]
5
input_tensor_info = input_binding_info[1]
6
print(f"""
7
tensor id: {input_tensor_id},
8
tensor info: {input_tensor_info}
9
""")
Выберите Backends, создайте среду выполнения и оптимизируйте модель
Укажите бэкэнд-список, который вы можете оптимизировать в сети.
питон
xxxxxxxxxx
1
options = ann.CreationOptions()
2
runtime = ann.IRuntime(options)
3
4
# Backend choices earlier in the list have higher preference.
5
preferredBackends = [ann.BackendId('CpuAcc'), ann.BackendId('CpuRef')]
6
opt_network, messages = ann.Optimize(network, preferredBackends, runtime.GetDeviceSpec(), ann.OptimizerOptions())
Загрузите оптимизированную сеть в среду выполнения
Загрузите оптимизированную сеть в контексте времени выполнения. LoadNetwork () создает специфичные для бэкэнда рабочие нагрузки для слоев.
питон
xxxxxxxxxx
1
# Load the optimized network into the runtime.
2
net_id, _ = runtime.LoadNetwork(opt_network)
3
print(f"Loaded network, id={net_id}")
4
input_tensors = ann.make_input_tensors([input_binding_info], [image])
Получить выходные данные привязки и сделать выходной тензор
Подобно информации о входной привязке, мы можем извлечь из синтаксического анализатора имена выходных тензоров и получить информацию о привязке.
В нашем примере считается, что модель классификации изображений имеет только один выход, следовательно, она использует только первое имя из возвращенного списка, и ее можно легко расширить до нескольких выходных циклов на output_names.
питон
xxxxxxxxxx
1
# Get output binding information for an output layer by using the layer name.
2
output_names = parser.GetSubgraphOutputTensorNames(graph_id)
3
output_binding_info = parser.GetNetworkOutputBindingInfo(0, output_names[0])
4
output_tensors = ann.make_output_tensors([output_binding_info])
Выполнить вывод
Производительный вывод Функция EnqueueWorkload () контекста времени выполнения выполняет вывод для загруженной сети.
питон
xxxxxxxxxx
1
runtime.EnqueueWorkload(0, input_tensors, output_tensors)
2
output, output_tensor_info = ann.from_output_tensor(output_tensors[0][1])
3
print(f"Output tensor info: {output_tensor_info}")
Вот наш полный код Python предиката_pyarmnn.py:
питон
xxxxxxxxxx
1
import PIL
2
from PIL import Image
3
import pyarmnn as ann
4
import numpy as np
5
import cv2
6
import argparse
7
8
print(f"Working with ARMNN {ann.ARMNN_VERSION}")
9
10
#Load an image
11
parser = argparse.ArgumentParser(
12
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
13
14
parser.add_argument(
15
16
'--image', help='File path of image file', required=True)
17
18
args = parser.parse_args()
19
20
image = cv2.imread(args.image)
21
image = cv2.resize(image, (128, 128))
22
image = np.array(image, dtype=np.float32) / 255.0
23
print(image.shape)
24
25
# ONNX, Caffe and TF parsers also exist.
26
parser = ann.ITfLiteParser()
27
network = parser.CreateNetworkFromBinaryFile('./fire_detection.tflite')
28
29
graph_id = 0
30
input_names = parser.GetSubgraphInputTensorNames(graph_id)
31
input_binding_info = parser.GetNetworkInputBindingInfo(graph_id, input_names[0])
32
input_tensor_id = input_binding_info[0]
33
input_tensor_info = input_binding_info[1]
34
35
print(f"""
36
tensor id: {input_tensor_id},
37
tensor info: {input_tensor_info}
38
""")
39
40
# Create a runtime object that will perform inference.
41
options = ann.CreationOptions()
42
runtime = ann.IRuntime(options)
43
44
# Backend choices earlier in the list have higher preference.
45
preferredBackends = [ann.BackendId('CpuAcc'), ann.BackendId('CpuRef')]
46
47
opt_network, messages = ann.Optimize(network, preferredBackends, runtime.GetDeviceSpec(), ann.OptimizerOptions())
48
49
# Load the optimized network into the runtime.
50
net_id, _ = runtime.LoadNetwork(opt_network)
51
print(f"Loaded network, id={net_id}")
52
53
# Create an inputTensor for inference.
54
input_tensors = ann.make_input_tensors([input_binding_info], [image])
55
56
# Get output binding information for an output layer by using the layer name.
57
output_names = parser.GetSubgraphOutputTensorNames(graph_id)
58
output_binding_info = parser.GetNetworkOutputBindingInfo(0, output_names[0])
59
output_tensors = ann.make_output_tensors([output_binding_info])
60
61
runtime.EnqueueWorkload(0, input_tensors, output_tensors)
62
output, output_tensor_info = ann.from_output_tensor(output_tensors[0][1])
63
print(f"Output tensor info: {output_tensor_info}")
64
print(output)
65
j = np.argmax(output)
66
if j == 0:
67
print("Non-Fire")
68
else:
69
print("Fire")
Запустите скрипт Python из командной строки:
Оболочка
xxxxxxxxxx
1
$ python3 predict_pyarmnn.py --image ./images/opencountry_land663.jpg
2
Working with ARMNN 20190800
3
4
tensor id: 15616,
5
tensor info: TensorInfo{DataType: 1, IsQuantized: 0, QuantizationScale: 0.000000, QuantizationOffset: 0, NumDimensions: 4, NumElements: 49152}
6
7
(128, 128, 3)
8
Output tensor info: TensorInfo{DataType: 1, IsQuantized: 0, QuantizationScale: 0.000000, QuantizationOffset: 0, NumDimensions: 2, NumElements: 2}
9
[0.9967675, 0.00323252]
10
Non-Fire
В нашем примере вероятность класса 0 равна 0,9967675, тогда как вероятность класса 1 равна 0,00323252, на изображении не обнаружен пожар.
Сравнительный анализ производительности PyArmNN и TensorFlow Lite
В качестве следующего шага мы оцениваем производительность PyArmNN и API-интерфейса TensorFlow Lite Python на Raspberry Pi.
TensorFlow Lite использует интерпретатор для выполнения вывода. Интерпретатор использует статический порядок графа и пользовательский (менее динамичный) распределитель памяти. Чтобы понять, как загрузить и запустить модель с помощью Python API, обратитесь к документации TensorFlow Lite .
Для оценки производительности был сделан вывод с нашей моделью обнаружения пожара. В нашем примере мы запускаем вывод только один раз. Мы также можем запустить модель несколько раз и взять среднее время вывода. Вот наш код Foret_tflite.py:
питон
xxxxxxxxxx
1
import tensorflow as tf
2
import argparse
3
import io
4
import time
5
import cv2
6
import numpy as np
7
from PIL import Image
8
from timeit import default_timer as timer
9
10
# Load TFLite model and allocate tensors.
11
interpreter = tf.lite.Interpreter(model_path="./fire_detection.tflite")
12
interpreter.allocate_tensors()
13
14
# Get input and output tensors.
15
input_details = interpreter.get_input_details()
16
output_details = interpreter.get_output_details()
17
_,height,width,_ = input_details[0]['shape']
18
floating_model = False
19
if input_details[0]['dtype'] == np.float32:
20
floating_model = True
21
parser = argparse.ArgumentParser(
22
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
23
parser.add_argument(
24
'--image', help='File path of image file', required=True)
25
args = parser.parse_args()
26
image = cv2.imread(args.image)
27
image = cv2.resize(image, (width, height))
28
image = np.expand_dims(image, axis=0)
29
if floating_model:
30
image = np.array(image, dtype=np.float32) / 255.0
31
32
# Test model on image.
33
interpreter.set_tensor(input_details[0]['index'], image)
34
start = timer()
35
interpreter.invoke()
36
end = timer()
37
print('Elapsed time is ', (end-start)*1000, 'ms')
38
39
# The function `get_tensor()` returns a copy of the tensor data.
40
# Use `tensor()` in order to get a pointer to the tensor.
41
output_data = interpreter.get_tensor(output_details[0]['index'])
42
print(output_data)
43
j = np.argmax(output_data)
44
if j == 0:
45
print("Non-Fire")
46
else:
47
print("Fire")
Оболочка
xxxxxxxxxx
1
$ python3 predict_tflite.py --image ./images/746.jpg
2
2020-01-01 11:32:33.609188: E
3
Elapsed time is 38.02500700112432 ms
4
[[9.9076563e-04 9.9900925e-01]]
5
Fire
Мы расширяем файлgnast_pyarmmnn.py тем же кодом для тестирования производительности.
питон
xxxxxxxxxx
1
start = timer()
2
runtime.EnqueueWorkload(0, input_tensors, output_tensors)
3
end = timer()
4
print('Elapsed time is ', (end - start) * 1000, 'ms')
Запустите скрипт Python снова:
Оболочка
xxxxxxxxxx
1
$ python3 predict_pyarmnn.py --image ./images/746.jpg
2
Working with ARMNN 20190800
3
(128, 128, 3)
4
5
tensor id: 15616,
6
tensor info: TensorInfo{DataType: 1, IsQuantized: 0, QuantizationScale: 0.000000, QuantizationOffset: 0, NumDimensions: 4, NumElements: 49152}
7
Loaded network, id=0
8
Elapsed time is 21.224445023108274 ms
9
Output tensor info: TensorInfo{DataType: 1, IsQuantized: 0, QuantizationScale: 0.000000, QuantizationOffset: 0, NumDimensions: 2, NumElements: 2}
10
[0.0009907632, 0.99900925]
11
Fire
В результате вы можете наблюдать повышение производительности логического вывода с помощью API PyArmNN.
Следующие шаги и полезные ресурсы
Из этого туториала Вы узнаете, как использовать API-интерфейсы Arm NN Python для классификации изображений как «Огонь» и «Не огонь». Вы также можете использовать его в качестве отправной точки для обработки других типов нейронных сетей.
Чтобы узнать больше о Arm NN, ознакомьтесь со следующими ресурсами:
Дальнейшее чтение
Как использовать Python с данными в реальном времени и REST API
Raspberry Pi, OpenCV, глубокие нейронные сети, и, конечно же, немного сомнений