python

Как на Raspberry Pi запустить модель ML и сэкономить пространство одноплатника

  • суббота, 10 июля 2021 г. в 00:36:57
https://habr.com/ru/company/skillfactory/blog/566926/
  • Блог компании SkillFactory
  • Python
  • Машинное обучение
  • Разработка на Raspberry Pi
  • TensorFlow


Представьте ситуацию: впереди выходные, а у вас есть достаточно нагруженная малинка и вы — ради эксперимента — хотите посмотреть, что ML умеет на мощностях RPi, но не хотите слишком перегружать машину, даже всей облегчённой версией TF. Что можно сделать? Мы уже писали о классификации мусора с помощью RPi, а сегодня, к старту курса о глубоком и машинном обучении, делимся переводом руководства, автор которого приводит простейший пример работы с необходимым минимумом TFLite. Выводы делаются моделью менее чем за секунду, при этом не нужно устанавливать весь пакет TensorFlow; используется только tflite_runtime, поддерживающий класс Interpreter.


Доступ к Raspberry Pi с компьютера

Есть несколько способов доступа к 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 командами терминала, их обсудим ниже.

Подготовка TFLite в RPi

Здесь предполагается, что у вас уже есть модель 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

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.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы