Как на Raspberry Pi запустить модель ML и сэкономить пространство одноплатника
- суббота, 10 июля 2021 г. в 00:36:57
Представьте ситуацию: впереди выходные, а у вас есть достаточно нагруженная малинка и вы — ради эксперимента — хотите посмотреть, что ML умеет на мощностях RPi, но не хотите слишком перегружать машину, даже всей облегчённой версией TF. Что можно сделать? Мы уже писали о классификации мусора с помощью RPi, а сегодня, к старту курса о глубоком и машинном обучении, делимся переводом руководства, автор которого приводит простейший пример работы с необходимым минимумом TFLite. Выводы делаются моделью менее чем за секунду, при этом не нужно устанавливать весь пакет TensorFlow; используется только tflite_runtime, поддерживающий класс Interpreter.
Есть несколько способов доступа к Raspberry Pi. Вне зависимости от способа цель — получить доступ к терминалу RPi для ввода команд, помогающих подготовить компьютер к работе с TFLite.
Можно работать с RPi как с обычным ПК, подключив его к монитору через порт HDMI, а также подсоединив мышь и клавиатуру. После успешного запуска RPi вы сможете получить доступ к его графическому интерфейсу и открыть терминал. К сожалению, этот метод может не сработать при покупке новой RPi: чтобы разрешить использование порта HDMI, нужно изменить некоторые настройки.
Доступ к RPi через специальный монитор и периферийные устройства обойдётся дорого, если у вас нет собственного ПК/ноутбука. Для управления RPi через свой ПК сначала нужно подключить порт Ethernet RPi к порту коммутатора. Коммутатор должен поддерживать протокол DHCP, чтобы IP-адрес для RPi присваивался автоматически.
Назначив IP, при помощи IP-сканера вы найдёте IP Ethernet-интерфейса RPi с подключённого через тот же коммутатор ПК, а имея IP-адрес RPi, с вашего ПК вы сможете открыть сеанс SSH, где получите доступ к терминалу RPi. Подробности читайте в этом учебном руководстве.
При таком доступе RPi нужно каждый раз подключать к коммутатору. Чтобы упростить работу, можно воспользоваться беспроводным интерфейсом. Подключив RPi к порту коммутатора и настроив его беспроводной интерфейс на беспроводную сеть, вы облегчите доступ к малинке в будущем. Эта сеть может быть создана со смартфона, он будет точкой доступа.
Возможно, после настройки сети коммутатор не понадобится. Всё, что нужно сделать, — подключить ПК к той же беспроводной сети. IP-сканер сообщит IP беспроводного интерфейса в RPi. После этого можно открыть сеанс SSH и достучаться до терминала одноплатника.
Независимо от метода доступа к RPi вы должны иметь возможность получить доступ к терминалу, как на рисунке ниже. На этом этапе можно подготовить TFLite командами терминала, их обсудим ниже.
Здесь предполагается, что у вас уже есть модель TensorFlow, преобразованная в модель TensorFlow Lite. Если это не так, есть множество моделей TensorFlow Lite, которые можно загрузить; воспользуемся Lite-версией MobileNet.
TensorFlow Lite — это часть TensorFlow, то есть установив библиотеку TensorFlow, вы установите и версию Lite. Перед установкой TensorFlow задумайтесь о необходимых проекту модулях. Здесь мы просто запускаем модель TFLite, чтобы классифицировать изображение, и ничего больше, поэтому не нужно устанавливать все модули TensorFlow.
Единственный класс TensorFlow, необходимый для прогнозирования в режиме TFLite — это Interpreter, доступ к нему можно получить так:
import tensorflow.lite.python.interpreter.Interpreter
То есть вместо установки всего TensorFlow можно установить только этот класс, сэкономив пространство на RPi. Но как именно установить только этот класс?
Пакет tflite_runtime содержит только класс Interpreter. Доступ к нему можно получить по адресу tflite_runtime.interpreter.Interpreter. Чтобы установить tflite_runtime, просто скачайте подходящее версии Python wheel, например Python 3.5 или Python 3.7.
В моей RPi файл .whl расположен здесь:
/home/pi/Downloads/tflite_runtime-1.14.0-cp35-cp35m-linux_armv7l.whl
Чтобы установить скачанный пакет, я запустил pip3 install. Обратите внимание: нужно использовать pip3: просто pip будет работать с Python 2.
pip3 install /home/pi/Downloads/tflite_runtime-1.14.0-cp35-cp35m-linux_armv7l.whl
Установив tflite_runtime, вы можете проверить, что всё работает правильно, импортировав класс Interpreter:
from tflite_runtime.interpreter import Interpreter
И вот результат:
Установка tflite_runtime не означает, что установлена вся TFLite. Доступен только класс Interpreter, который делает прогнозы на основе TFLite. Если нужны другие возможности TFLite, установите TensorFlow.
После установки tflite_runtime и подготовки RPi к прогнозированию переходим к следующему шагу.
MobileNet для TFLite можно скачать здесь. Это сжатый файл, он содержит не только модель TFLite, но и метки классов, которые прогнозирует модель. Распакованный архив выглядит так:
1) mobilenet_v1_1.0_224_quant.tflite;
2) labels_mobilenet_quant_v1_224.txt.
У MobileNet две версии, от версии зависит разрешение входного изображения. Здесь воспользуемся первой версией, она принимает изображения с разрешением 224x224. Модель подвергается квантованию, то есть она уменьшается, снижается задержка прогнозов. Для двух изображений на RPi я создал новую папку TFLite_MobileNet.
/home/pi/TFLite_MobileNetmobilenet_v1_1.0_224_quant.tflitelabels_mobilenet_quant_v1_224.txttest.jpg
В следующем разделе я покажу, как подать изображение в модель и спрогнозировать метку класса.
Код загрузки модели TFLite и классификации приведён ниже. Пути к модели и файла меток классов содержатся в model_path и labels. Пути к модели для её загрузки передаются конструктору класса Interpreter. Загруженная модель возвращается в переменной interpreter.
from tflite_runtime.interpreter import Interpreter
from PIL import Image
import numpy as np
import time
data_folder = "/home/pi/TFLite_MobileNet/"
model_path = data_folder + "mobilenet_v1_1.0_224_quant.tflite"
label_path = data_folder + "labels_mobilenet_quant_v1_224.txt"
interpreter = Interpreter(model_path)
print("Model Loaded Successfully.")
interpreter.allocate_tensors()
_, height, width, _ = interpreter.get_input_details()[0]['shape']
print("Image Shape (", width, ",", height, ")")
# Load an image to be classified.
image = Image.open(data_folder + "test.jpg").convert('RGB').resize((width, height))
# Classify the image.
time1 = time.time()
label_id, prob = classify_image(interpreter, image)
time2 = time.time()
classification_time = np.round(time2-time1, 3)
print("Classificaiton Time =", classification_time, "seconds.")
# Read class labels.
labels = load_labels(label_path)
# Return the classification label of the image.
classification_label = labels[label_id]
print("Image Label is :", classification_label, ", with Accuracy :", np.round(prob*100, 2), "%.")
После загрузки модели для выделения памяти под входные и выходные тензоры вызывается метод allocate_tensors(), затем, чтобы вернуть информацию о входном тензоре, вызывается get_input_details(). В возвращаемую информацию входят ширина и высота входного изображения. Для чего они?
Вспомните, что загруженная модель принимает изображения 224x224. Если подать изображение другого размера, мы получим ошибку. Зная ширину и высоту принимаемого моделью изображения, можно изменить размер входных данных так, чтобы модель могла работать с ними. Тестовое изображение считывается с помощью PIL, и возвращается изображение с подходящим модели разрешением.
Теперь с помощью функции classify_image(), реализация которой показана ниже, выполним классификацию. В ней функцией set_input_tensor() входной тензор модели устанавливается равным тензору тестового изображения.
В оригинальной статье показанный ниже код не содержит вызова set_input_tensor(), при этом вызов set_input_tensor() есть чуть ниже, в полном коде программы, откуда и была взята правильная по тексту статьи реализация.
Далее для запуска модели и передачи входных данных вызывается invoke(). Выходные данные — индекс класса и его вероятность.
def classify_image(interpreter, image, top_k=1):
set_input_tensor(interpreter, image)
interpreter.invoke()
output_details = interpreter.get_output_details()[0]
output = np.squeeze(interpreter.get_tensor(output_details['index']))
scale, zero_point = output_details['quantization']
output = scale * (output - zero_point)
ordered = np.argpartition(-output, 1)
return [(i, output[i]) for i in ordered[:top_k]][0]
Далее метки классов загружаются из текстового файла с помощью функции load_labels(), её реализация показана ниже. Она принимает путь к текстовому файлу и возвращает список с метками классов. Индекс класса, к которому отнесено изображение, используется для возврата соответствующей метки класса. Наконец, метка выводится на консоль.
def load_labels(path): # Read the labels from the text file as a Python list.
with open(path, 'r') as f:
return [line.strip() for i, line in enumerate(f.readlines())]
Весь код приведён ниже.
from tflite_runtime.interpreter import Interpreter
from PIL import Image
import numpy as np
import time
def load_labels(path): # Read the labels from the text file as a Python list.
with open(path, 'r') as f:
return [line.strip() for i, line in enumerate(f.readlines())]
def set_input_tensor(interpreter, image):
tensor_index = interpreter.get_input_details()[0]['index']
input_tensor = interpreter.tensor(tensor_index)()[0]
input_tensor[:, :] = image
def classify_image(interpreter, image, top_k=1):
set_input_tensor(interpreter, image)
interpreter.invoke()
output_details = interpreter.get_output_details()[0]
output = np.squeeze(interpreter.get_tensor(output_details['index']))
scale, zero_point = output_details['quantization']
output = scale * (output - zero_point)
ordered = np.argpartition(-output, 1)
return [(i, output[i]) for i in ordered[:top_k]][0]
data_folder = "/home/pi/TFLite_MobileNet/"
model_path = data_folder + "mobilenet_v1_1.0_224_quant.tflite"
label_path = data_folder + "labels_mobilenet_quant_v1_224.txt"
interpreter = Interpreter(model_path)
print("Model Loaded Successfully.")
interpreter.allocate_tensors()
_, height, width, _ = interpreter.get_input_details()[0]['shape']
print("Image Shape (", width, ",", height, ")")
# Load an image to be classified.
image = Image.open(data_folder + "test.jpg").convert('RGB').resize((width, height))
# Classify the image.
time1 = time.time()
label_id, prob = classify_image(interpreter, image)
time2 = time.time()
classification_time = np.round(time2-time1, 3)
print("Classificaiton Time =", classification_time, "seconds.")
# Read class labels.
labels = load_labels(label_path)
# Return the classification label of the image.
classification_label = labels[label_id]
print("Image Label is :", classification_label, ", with Accuracy :", np.round(prob*100, 2), "%.")
Вывод программы:
Model Loaded Successfully.
Image Shape ( 224 , 224 )
Classificaiton Time = 0.345 seconds.
Image Label is : Egyptian cat , with Accuracy : 53.12 %.
Raspberry Pi — относительно недорогой компьютер, на котором уже сегодня можно запускать простые модели — ML идёт к тому, чтобы стать таким же естественным, как вода в вашем доме — и если вы не хотите оставаться в стороне от сферы ИИ, то можете обратить внимание на наш курс «Machine Learning и Deep Learning», а если предпочитаете полагаться не на машины, но на себя, то можете присмотреться к нашему флагманскому курсу о Data Science.
Узнайте, как прокачаться и в других специальностях или освоить их с нуля:
ПРОФЕССИИ
КУРСЫ