Всем доброго дня. Совсем недавно я закончила продвинутый курс от Deep Learning School. Курс объемный, много свежей информации. Мне, как закончившей прикладную математику (нейросетки, генетика, fuzzy logics) было не сильно сложно, но мега-увлекательно за счет того, что ребята очень заинтересованные и рассказывали про свежие интересные модели, еще и на русском языке. Приятно видеть, что ИИ-сфера в нашей стране тоже не стоит на месте.

В качестве проекта на курсе мне хотелось сделать своими руками что-нибудь эдакое и полезное, и одновременно мега-современное, и вот что я придумала. У нас есть частный дом, там есть дворовые коты, которых надо кормить и в мое отсутствие. А так же есть еще птицы, кроты, чужие вездесущие собаки и другая живность, которых не стоит кормить, если не хотим, чтобы они у нас все поселились. Так вот, а что, если прикрутить модель детекции изображений к умной кормушке? Далее было бы здорово научиться использовать голосовые команды, например, на закрытие кормушки. Интересненько :-)

Сразу замечу, это учебный проект, увлекательный но поверхностный процесс, о котором собираюсь рассказать в этой статье. Акцент сделаю именно на то, как можно пользоваться моделями детекции изображений на практике и что из этого всего может выйти. И чтобы не писать никаких специальных программ, воспользуемся очень популярным сервисом телеграмм-ботов, который есть почти в каждом смартфоне.

Логика предполагалась следующая:

  1. Кот приходит к кормушке, его снимает рядом стоящая IP камера и отправляет снимок в телеграмм-бот.

  2. Бот обрабатывает сообщение, если это фото, то отправляет его в модель детекции.

  3. Модель детекции распознает кота или не распознает.

  4. Если это кот, бот посылает сигнал умной кормушке открыться и уведомляет об этом владельца.

  5. Умная кормушка открывается (и потом закрывается по таймеру, например).

  6. Владелец в любой момент времени может подать голосовую команду, например, на закрытие кормушки через телеграмм-бот.

  7. Команда из голосовой преобразуется в текст.

  8. Если будет обнаружена команда "закрыть кормушку", бот отправит сигнал на закрытие кормушки и сообщение владельцу, что принял команду.

Моделей детекции в современном мире тьма, в целях данной работы я выбрала Single Shot MultiBox Detector model for object detection (далее SSD-модель), разработанную компанией NVIDIA. Эта модель архитектуры глубоких нейронных сетей на основе модели ResNet-50 (победившей на соревнованиях ImageNet 2015 года), специально заточенная под детекцию изображений. Статья у нас практическая, не будем вдаваться в сложности архитектуры, только подчеркнем, что данная архитектура нейронных сетей стала практически стандартом качества и часто используется в задачах обнаружения объектов и сегментации изображений. SSD-модель заточена под быструю обработку и детекцию объектов даже в реальном времени, она обеспечивает высокую точность и быстроту обработки изображений.

1 часть: подготовка к работе

Писать будем на Python 3.11

Создаем новый проект, новое виртуальное окружение, в которое устанавливаем необходимые библиотеки

pip install numpy scipy scikit-image matplotlib
pip install pyTelegramBotAPI
pip install SpeechRecognition
pip install pydub
pip install pydub[ffmpeg]
pip install pydub opencv-python-headless opuslib

Скачиваем и настраиваем выбранную модель: переводим ее в "боевой" режим. Расчеты следует производить на GPU или на CUDA, т.к. CPU будет считать очень долго и медленно. Я делала проект в Google Colab GPU

import torch
precision = 'fp32'
ssd_model = torch.hub.load('NVIDIA/DeepLearningExamples:torchhub', 
                           'nvidia_ssd', model_math=precision)
utils = torch.hub.load('NVIDIA/DeepLearningExamples:torchhub', 
                       'nvidia_ssd_processing_utils')
ssd_model.to('cuda')
ssd_model.eval()

Монтируем google disk, на котором будем хранить все материалы

from google.colab import drive
drive.mount('/content/drive')
torch.save(ssd_model.state_dict(), 'ssd_model.pth')

Подгружаем необходимые библиотеки

import telebot
import cv2
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np

from matplotlib import pyplot as plt
import matplotlib.patches as patches

import os
import speech_recognition as sr

from pydub import AudioSegment
import subprocess

import time

2 часть: реализация логики

Ниже пример самой простой логики обработки изображения нашей моделью. В целях нашей задачи мы обнаруживаем только кота, но возможности модели очень широкие - она может опознавать большое количество различных объектов и даже в онлайн режиме.

def filter(uri):
    # Инициализация флага обнаружения объекта "cat"
    is_detected_cat = 0
    
    # Подготовка входных данных
    inputs = [utils.prepare_input(uri)]
    
    # Подготовка тензора для модели с учетом точности 'fp16'
    tensor = utils.prepare_tensor(inputs, precision == 'fp16')

    # Выполнение обнаружения объектов с использованием SSD-модели
    with torch.no_grad():
        detections_batch = ssd_model(tensor)

    # Декодирование результатов обнаружения
    results_per_input = utils.decode_results(detections_batch)
    
    # Выбор лучших результатов с пороговой уверенностью 0.20
    best_results_per_input = [utils.pick_best(results, 0.20) for results in results_per_input]
    
    # Получение словаря с соответствием классов меткам
    classes_to_labels = utils.get_coco_object_dictionary()

    # Итерация по изображениям с лучшими результатами
    for image_idx in range(len(best_results_per_input)):
        # Создание графика для визуализации результатов
        fig, ax = plt.subplots(1)
        
        # Отображение оригинального изображения
        image = inputs[image_idx] / 2 + 0.5
        ax.imshow(image)
        
        # Отображение прямоугольников вокруг обнаруженных объектов и меток с уверенностью
        bboxes, classes, confidences = best_results_per_input[image_idx]
        for idx in range(len(bboxes)):
            left, bot, right, top = bboxes[idx]
            x, y, w, h = [val * 300 for val in [left, bot, right - left, top - bot]]
            rect = patches.Rectangle((x, y), w, h, linewidth=1, edgecolor='r', facecolor='none')
            ax.add_patch(rect)
            
            # Проверка, является ли обнаруженный объект классом "cat"
            class_cat = classes_to_labels[classes[idx] - 1]
            if (class_cat == 'cat'):
                is_detected_cat = 1
            
            # Вывод метки класса и уверенности
            ax.text(x, y, "{} {:.0f}%".format(classes_to_labels[classes[idx] - 1], confidences[idx]*100), bbox=dict(facecolor='white', alpha=0.5))
   
    # Сохранение отфильтрованного изображения
    plt.savefig("filtered_image.jpg")
    
    # Возвращение флага обнаружения объекта "cat"
    return is_detected_cat

3 часть: создание телеграмм-бота

Бота создаем через @BotFather, в целях безопасности отдельно в файле сохраняем токен.

Пишем логику бота, наш бот будет уметь:

Здороваться

@bot.message_handler(commands=['start'])
def start_message(message):
    bot.send_message(message.chat.id, 'Привет! Пожалуйста, загрузи картинку.')

В рамках учебного проекта я не делала полноценную интеграцию с камерой, поэтому бот и модель детекции будет работать на примере загруженных в него фотографий. Справедливости ради стоит заметить, что реагировать данная модель будет на любого кота. Чтобы модель реагировала только на какого-то одного определенного кота, надо собрать отдельный дата-сет с фотографиями этого кота и дообучить скачанную и сохраненную модель на него.

Посылать сигнал на открытие кормушки при обнаружении кота

@bot.message_handler(content_types=['photo'])
def process_image(message):

    # Получаем информацию о картинке
    file_id = message.photo[-1].file_id
    file_info = bot.get_file(file_id)
    file_path = file_info.file_path

    # Скачиваем картинку
    downloaded_file = bot.download_file(file_path)

    # Сохраняем картинку на диск
    with open('image.jpg', 'wb') as f:
        f.write(downloaded_file)

    is_detected_cat = filter("image.jpg")

    # Отправляем пользователю преобразованное изображение
    with open('filtered_image.jpg', 'rb') as f:
        bot.send_photo(message.chat.id, f)

    if is_detected_cat == True:
        bot.reply_to(message, "Пришел твой кот. Кормушка открыта")

Обрабатывать команды пользователя в виде голосовых сообщений, в нашем примере будет реагировать только на одну команду "закрой кормушку". Код ниже наверняка можно оптимизировать.

@bot.message_handler(content_types=['voice'])
def process_voice_message(message):
    # Получаем информацию о голосовом сообщении
    file_id = message.voice.file_id
    file_info = bot.get_file(file_id)
    file_path = file_info.file_path

    # Скачиваем голосовое сообщение
    downloaded_file = bot.download_file(file_path)

    # Сохраняем голосовое сообщение на диск
    voice_file_path = 'voice_message.ogg'
    with open(voice_file_path, 'wb') as f:
        f.write(downloaded_file)

    # Преобразование голосового сообщения в текст
    input_file = 'voice_message.ogg'
    audio = AudioSegment.from_file(input_file, format='ogg')

    # Конвертация в формат WAV
    output_file = 'audio.wav'
    audio.export(output_file, format='wav')


    recognizer = sr.Recognizer()
    with sr.AudioFile(output_file) as source:
        audio_data = recognizer.record(source)
        text = recognizer.recognize_google(audio_data, language='ru')  # Здесь можно указать другой язык, если нужно
        print(text)

    # Отправляем текст пользователю
    bot.reply_to(message, text)

    # Удаляем временные файлы
    os.remove(output_file)

4 часть: запуск!

Напрямую в Google Colab можно запустить бота вот так:

bot.polling()

А можно выгрузить в облако, тогда бот будет работать круглосуточно и без необходимости держать открытым Google Colab. Например, в яндекс облако.

Вот такое интересное и практичное применение возможно моделям искусственного интеллекта, а не вот эти вот все скучные захваты мира))))

А что же дальше...

Главное - желание, а уж идеи найдутся. Например, можно дообучить модель. Собрать собственный датасет пользователя с его питомцем и оттюнинговать модель в целях повышения качества "узнавания" именно конкретно заданного домашнего питомца. Бескрайний простор работы с расширением функционала протокола взаимодействия с кормушкой и другими "умными" вещами, разнообразными датчиками и усложнение их логики. Подключение онлайн детекции питомца по потоковому видео.

Источники

SSD: Single Shot MultiBox Detector

Школа глубокого обучения ФПМИ МФТИ

Прыжок до небес: запускаем телеграм бота на Python в serverless облаке

Мой исходник на Github

Послесловие

Да, конечно, в обычных ситуациях проще купить систему умного дома, сейчас они получили широкое повсеместное использование. Но, как правило, в промышленное использование поступают наиболее распространенные запросы, наиболее популярные модели. Плюс на это требуется время. А вот с помощью предобученных моделей мы практически не ограничены в своих возможностях их использования.

Комментарии (15)


  1. WondeRu
    18.11.2023 17:20
    +1

    А где видео работы?)


    1. evia Автор
      18.11.2023 17:20

      я по снимкам делала, можно будет поэкспериментировать и с видео, спасибо за идею


  1. Dynasaur
    18.11.2023 17:20

    Несчастной айтишнице даже распознать нечего, кроме своего стола,кнопатуры и мобилы :-)))

    А если серьёзно, пара вопросов:

    1. Почему выбрана столь не свежая модель распознавания? (победительница 2015 года)?

    2. Дайте ссылочку, где у неё предобученный список классов - с ходу не нашёл


    1. evia Автор
      18.11.2023 17:20
      -1

      Не знаю, что вас так зацепило в моей работе, что вы так злы) надеюсь у вас тоже что-нибудь получится)

      Это был прорыв 2015 года, невероятный скачок в детекции изображений, изменивший дальшейший ход работы в этом направлении. Я помню 2011 год, когда не могли и помыслить, что такое возможно)

      И если более внимательно посмотреть репозиторий Nvidia, последний релиз SSD модели детекции - 30 января 2023 года.


      1. Dynasaur
        18.11.2023 17:20
        +1

        Извините, если обидел, вообще не было такого намерения


        1. yrub
          18.11.2023 17:20

          плохо русским языком владеете. слово "несчастный" однозначно уничижительное. ну и смысловое наполнение первого предложения негативное в любом случаи, какие-то непонятные "доколупки". добрее быть надо ;)


          1. sswwssww
            18.11.2023 17:20
            +2

            Нет, это у вас с автором статьи какие-то проблемы с эмоциональным интеллектом :) Для меня ОЧЕВИДНО что автор комментария не имел намерений никого обидеть.
            1. "А если серьёзно" - дал понять что текст выше это шутка;
            2. Шутка, в целом, про всю отрась(суть которой в том что It-шникам кроме "своего стола,кнопатуры и мобилы" ничего и не надо);
            3. Улыбающий смайлик в конце(можно предъявить что так делают и с целью насмешки, но обычно для этого юзают пару скобок максимум. Этот момент сложно объяснить, но он мне понятен на "интуитивном" уровне);
            4.Как у вас в голове умещаются 2 вещи: "комментатор оскорбил автора" > "комментатор вежливо что-то просит"? Не возникает чувства что явно вы что-то не так восприняли? Или вы действительно думаете что люди в одном и том же абзаце могут и оскорбить и вежливо попросить вас о чем то?


            1. evia Автор
              18.11.2023 17:20

              да просто у всех разные базовые настройки восприятия шуток-серьезности) так что лучше и не обсуждать особо) не по теме) вы лучше по теме статьи меня о чем-нибудь спросите)


  1. olegtsss
    18.11.2023 17:20

    Хорошая статья, краткость сестра таланта). Я не разобрался, где в коде модель настроена на распознание кота?

    (вопрос более детально - какие ручки можно подкрутить для настройки этого сличения, как я понимаю, с эталоном кота)? И где обучение модели распознанию кота? Или она уже обучена, тогда интересно, какие характеристики с изображения камеры в нее попадают для принятия вероятностного решения? А что кроме кота еще можно так различать?)


    1. evia Автор
      18.11.2023 17:20

      Хех, спасибо) ну я тут пыталась показать именно практическую сторону использования уже готовой предобученной модели. По пунктам:

      1. Модель уже готова к использованию, я ее скачиваю и перевожу в режим "eval", т.е. не обучение, а использование на новых изображениях

      2. Она предобучена на COCO датасете. У меня вышло 80 объектов, которые она умеет распознавать (добавила список в свой гитхаб)

      3. На вход модели подается изображение - фото, у меня jpg,

      4. А в коде это функция filter, там вроде старалась расписать максимально подробно, получается, что мы наше изображение сначала подготоваливаем в нужный вид (переменная input), а потом получаем готовый результат от модели (переменная results_per_input), ну а дальше я уже отсекаю нужный мне порог вероятности и подключаю словарь, чтобы сопоставить, к каким классам (меткам) модель соотнесла объекты на нашем изображении. Ну а я собственно оставляю только кота. Т.е. логика наоборот - модель опознает все объекты и потом я уже думаю, что с ними делать. А не я задаю ей задачу искать кота.


      1. olegtsss
        18.11.2023 17:20

        classes_to_labels = utils.get_coco_object_dictionary() - теперь понятно, спасибо. И кто там есть, кроме кота? Как на пиктограммах common objects Coco?


        1. evia Автор
          18.11.2023 17:20
          +1

          да да) тут список того, что я смогла вытащить, там: person, bicycle, car, motorcycle, airplane, bus, train, truck, boat, traffic light, fire hydrant, stop sign, parking meter, bench, bird, cat, dog, horse, sheep, cow, elephant, bear, zebra, giraffe, backpack, umbrella, handbag, tie, suitcase, frisbee, skis, snowboard, sports ball, kite, baseball bat, baseball glove, skateboard, surfboard, tennis racket, bottle, wine glass, cup, fork, knife, spoon, bowl, banana, apple, sandwich, orange, broccoli, carrot, hot dog, pizza, donut, cake, chair, couch, potted plant, bed, dining table, toilet, TV, laptop, mouse, remote, keyboard, cell phone, microwave, oven, toaster, sink, refrigerator, book, clock, vase, scissors, teddy bear, hair drier, toothbrush


          1. olegtsss
            18.11.2023 17:20

            wine glass ))) - забавно.


  1. Devastor87
    18.11.2023 17:20

    А на какой ОС делалось распознавание голоса? Винда?

    Немного не в тему, но может кто подскажет хорошую библиотеку распознавания русской речи для C# под MacOS?


    1. evia Автор
      18.11.2023 17:20

      Этот проект я в Google Colab делала, а так да, винда 11.

      Я бы порекомендовала тогда тот же OpenAI подключить себе и не искать специальные неизвестного качества библиотеки. Там у них на сайте есть API ключи и потом через restapi можно запросы делать. Если не ошибаюсь, вроде бы OpenAI и Speech Recognition (Microsoft) сейчас топовые и с поддержкой русского языка. Вот тут например Speech-to-Text. А потом саммари по тексту и не надо никаких ассистентов, шикарная возможность избавиться от бесконечных совещаний)))) Я попозже думала выложить демо-проект такой штуки, правда на питоне. Питон как-то сильно проще в плане всех этих бесконечных библиотек и легче в том, чтобы слепить что-то побыстрому :)