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

API распознавания изображений

В то время как первый столкнется с профессиональным вызовом в разработке собственного решения для глубокого обучения с помощью таких инструментов, как CNTK, Tensorflow, Keras и остальных, второй предпочел бы поискать на полках готовое решение. С помощью этих инструментов даже аналитики данных могут сэкономить пару дней или часов. К счастью, существует множество вариантов, предоставляемых такими громкими именами, как Amazon Web Services, Google, Microsoft, а также более мелкими специализированными разработчиками. Вы можете использовать теги/понятия/ярлыки из этих сервисов для рекомендаций, организации контента или выявления неисправных продуктов. Ваш вариант использования будет определять, какой поставщик вы выберете: некоторые лучше подходят для чистых изображений, некоторые могут справиться с плотными сценами, другие предоставляют предварительно обученные модели для конкретных отраслей.

Давайте бросим вызов визуальному восприятию искусственного интеллекта Google, Microsoft и Clarafai с некоторыми изображениями, далекими от наиболее распространенных вариантов использования.

Заброшенный V6 на аккумуляторе для баффов

Это изображение я сделал, когда бежал по тропам вокруг моста Золотые Ворота. Мы ставим некоторые препятствия перед ИИ:

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

В таблице ниже показано, какие концепции мы получаем от API трех сервисов. Уровни уверенности также пройдены.

Видно, что все три сервиса довольно хорошо поняли изображение на высоком уровне: есть какие-то отходы, ржавчина на улице. Вы также можете заметить, что ИИ Clarifai даже немного не «храбрее» и детальнее, чем другие: не только уровни достоверности выше, но и он предоставляет много абстрактных понятий: бедствие, снос, ущерб говорят нам, что служба Clarifai распознала, что основной объект не должен выглядеть так, как на фото. Azure Computer Vision также видел сцены из «Безумного Макса»: это заброшенный (среда? сцена?), но не пытался угадать причины.

Собака ждет сонного смотрителя

Бейгли стремится бежать даже довольно рано утром, но не очень любит ждать, пока я допью свой кофе.

Изображение не самого лучшего качества, но сцена четкая. Неудивительно, что все три услуги работали хорошо: есть собака, в помещении, на полу.
Как и в случае с «пламенем» на предыдущем изображении, с Clarify проще получить ошибку типа I: например. там написано, что на изображении есть девушка (0.85). В статистике мы обычно игнорируем эту гипотезу, но при маркировке изображений мы склонны принимать гораздо более низкие уровни достоверности. Но имейте в виду, чем больше догадок и чем меньше доверия вы принимаете, тем больше ошибочных тегов вы получаете. Именно ваша сфера определяет важность таких ложных отождествлений или упущенных концепций.

Кстати, по данным Google мы видим лабрадора-ретривера (0,60). Azure думает, что мы видим бигля (0,46) или лабрадора-ретривера (0,19), может быть, золотистого ретривера (0,15). Первая догадка верна, на картинке бигль-полукровка.

Битва в гостиной

Пока мы довольны результатами. Объединение тем первых двух изображений приводит нас к «Битве в гостиной»: что происходит, когда метеорит падает прямо в контейнер с игрушками и тренировочными инструментами Бейгли. Ладно, это я постирала вещи и положила их сушиться. В любом случае, изображение плотное, даже трудноперевариваемое человеческим глазом.

Google API отказался от этого полностью, он даже не может распознать ни один элемент сцены, он говорит автомобиль, автозапчасть, транспортное средство, соответствующие показатели достоверности 0,56, 0,55, 0,53.

Azure Computer Vision возвращает 5 тегов, но только «внутри» и «пол» имеют высокую достоверность. «Загроможденный» — идеальное слово здесь, но если мы полагаемся на показатель достоверности (0,30), мы не будем его использовать. Azure также видит на картинке кошку и собаку, но не уверен в результатах.

Мы не можем описать Clarifai таким же образом, он предлагает 20 концепций, все с показателем достоверности выше 83%. Помимо забавных идей, вроде «музыки» (может, игрушки на сушилке напоминают партитуру ИИ?) есть и такие, которые хорошо описывают ситуацию: собака, игрушка, праздник или моя любимая, битва.

Вывод

Google Cloud Vision API играет осторожно: он дает несколько меток и делает это с низкой оценкой достоверности. Конечно, если вы не работаете, вы не можете ошибаться. По крайней мере, уровни достоверности являются четкими индикаторами.

Microsoft Azure Computer Vision неплох в этикетках. К сожалению, уровни достоверности подразумевают, что далеко не факт, что объект, распознанный на одном изображении, будет распознан на другом.

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

Пожалуйста, имейте в виду, что это всего лишь примеры, а не репрезентативное исследование. Если ваша организация использует Google и/или входные данные вашего приложения, как правило, понятны. Бывают ситуации, когда ошибки первого рода лучше считать ошибками второго рода: обычно лучше заподозрить болезнь, чем пропустить ее. В других случаях такой подход неприемлем из-за финансовых соображений.

Кроме того, другие аспекты этих API, такие как распознавание текста, могут привести к другому порядку служб.

Небольшое приложение для обработки изображений на Python

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

Инструменты, которые мы используем

В этом примере приложения мы будем

  1. хранить изображения с локального компьютера в хранилище BLOB-объектов Azure,
  2. анализировать изображения, хранящиеся с помощью Clarifai API,
  3. сохранять результаты в базе данных документов MongoDB.

Пакеты, которые мы должны установить:

import os, uuid, sys, pprint as pp
import azure.storage.blob
from azure.storage.blob import BlockBlobService, PublicAccess, baseblobservice
from clarifai.rest import ClarifaiApp
from pymongo import MongoClient
import configparser

Если вы храните свои учетные данные в config.ini, ваши следующие строки кода должны быть следующими.

config = configparser.ConfigParser()
config.read('config.ini')
azure_account_name = config['Azure Blob Storage']['account name']
azure_account_key = config['Azure Blob Storage']['account key']
clarifai_api_key = config['Clarifai']['api key']
mongoServer = config['MongoDB']['server']
mongo_port = int(config['MongoDB']['port'])
mongo_database = config['MongoDB']['database']
mongo_user = config['MongoDB']['admin user']
mongoPassword = config['MongoDB']['admin password']

Сохранение в хранилище BLOB-объектов

Создание службы и контейнера

После того, как мы создали учетную запись хранения в Azure, мы можем создать службу. С помощью этого сервиса мы создаем контейнер, пространство для хранения наших двоичных данных и устанавливаем к нему публичный доступ. Это последнее необходимо, чтобы позволить Clarify прочитать BLOB-объекты.

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

global block_blob_service
block_blob_service = BlockBlobService(account_name = azure_account_name, account_key = azure_account_key)
global container_name
container_name ='workshopblobs'
        
block_blob_service.create_container(container_name) 
block_blob_service.set_container_acl(container_name, public_access=PublicAccess.Container)

Загрузка локального файла

В Python достаточно указать путь и имя файла в методе create_blob_from_path класса block_blob_service :

global full_path_to_file
full_path_to_file = os.path.join(local_path, local_file_name)
block_blob_service.create_blob_from_path(container_name, local_file_name, full_path_to_file)

Общий URL-адрес BLOB-объекта

Нам нужен еще один класс из пакета больших двоичных объектов: BaseBlobService для создания URL-адреса, который будет передан Clarifai.

block_blob_service.create_blob_from_path(container_name, local_file_name, full_path_to_file)
        
base_blob_service = baseblobservice.BaseBlobService(account_name = azure_account_name, account_key = azure_account_key)
global blob_url
blob_url = base_blob_service.make_blob_url(container_name, local_file_name, protocol=None, sas_token=None, snapshot=None)
process_blob(blob_url)

Предсказание концепций по URL-адресу

Используемая выше функция очень проста с методом predict_by_url(): нам нужно передать только URL-адрес, который мы сгенерировали на предыдущем шаге. app.public_models.general_model относится к общей модели, есть и другие общедоступные модели, но вы также можете обучить свою собственную. Если вы не планируете хранить большие двоичные объекты, следует использовать predict_by_filename().

def process_blob(input_blob_url):
    try:
        app = ClarifaiApp(api_key = clarifai_api_key)
        model = app.public_models.general_model
        response = model.predict_by_url(url=input_blob_url)
        
        save_results(response)
except Exception as e:
        print(e)

Сохранение результатов в MongoDB

MongoDB Atlas требует TLS и может использоваться со строкой подключения. Мы сохраняем результаты в ссылках коллекции blobResults как результаты.

mongodb_connection_string = "mongodb+srv://" + mongo_user + ":" + mongoPassword + "@" + mongoServer + "/" + mongo_database + "?retryWrites=true"
client = MongoClient(mongodb_connection_string)
db = client[mongo_database]
results = db["blobResults"]

В process_blob() мы использовали функцию save_results(). Это наша функция, которая вставляет ответ от API Clarifai, вызывая метод insert_one() из нашей коллекции.

def save_results(result):
    try:
        results.insert_one(result)
except Exception as e:
        print(e)

Просто передайте имя файла для обработки в переменную local_file_name для работы скрипта.

local_file_name = "battle.jpg"

Запрос результатов

Для поиска определенных понятий и списка URL-адресов больших двоичных объектов, помеченных ими, вы можете использовать следующую функцию. Query – это словарь в формате, который может интерпретировать метод find(). Нам нужны концепции из выходных данных в виде запроса и URL из входного вложенного документа в качестве поля для извлечения.

concept = "battle"
def find_concept(concept):
    query = {
                "outputs.data.concepts":
                    {
                        "$elemMatch":
                            {
                                "name": concept
                            }
                    }
            }
    
    fields = {
                "outputs.input.data.image.url": 1
            }
try:
        cursor = list(db.blobResults.find(query, fields))
        for doc in cursor:
            pp.pprint(doc)
except Exception as e:
        print(e)

MongoDB не извлекает полный набор данных, соответствующий вашим критериям. Вместо этого find() предоставляет курсор, здесь мы читаем его значения в поле for.

Вы можете увидеть полные примеры сценариев ниже. Функция загрузки большого двоичного объекта в контейнер в основном основана на примере кода Microsoft. Вы можете найти дополнительные примеры Azure Storage здесь и документацию pymongo по этой ссылке.

Полный код

Приложение BLOB-объектов

Анализ и сохранение наших изображений.

import os, uuid, sys, pprint as pp
import azure.storage.blob
from azure.storage.blob import BlockBlobService, PublicAccess, baseblobservice
from clarifai.rest import ClarifaiApp
from pymongo import MongoClient
import configparser
config = configparser.ConfigParser()
config.read('config.ini')
azure_account_name = config['Azure Blob Storage']['account name']
azure_account_key = config['Azure Blob Storage']['account key']
clarifai_api_key = config['Clarifai']['api key']
mongoServer = config['MongoDB']['server']
mongo_port = int(config['MongoDB']['port'])
mongo_database = config['MongoDB']['database']
mongo_user = config['MongoDB']['admin user']
mongoPassword = config['MongoDB']['admin password']
mongodb_connection_string = "mongodb+srv://" + mongo_user + ":" + mongoPassword + "@" + mongoServer + "/" + mongo_database + "?retryWrites=true"
client = MongoClient(mongodb_connection_string)
db = client[mongo_database]
results = db["blobResults"]
# image to process
local_file_name = "monster.jpg"
def store_and_analyze_blob():
    try:
        global block_blob_service
        block_blob_service = BlockBlobService(account_name = azure_account_name, account_key = azure_account_key)
global container_name
        container_name ='workshopblobs'
        
        block_blob_service.create_container(container_name) 
        block_blob_service.set_container_acl(container_name, public_access=PublicAccess.Container)
# our image in images subfolder
        local_path=os.path.join(os.getcwd(), "images")
        
        global full_path_to_file
        full_path_to_file = os.path.join(local_path, local_file_name)
block_blob_service.create_blob_from_path(container_name, local_file_name, full_path_to_file)
        
        base_blob_service = baseblobservice.BaseBlobService(account_name = azure_account_name, account_key = azure_account_key)
global blob_url
        blob_url = base_blob_service.make_blob_url(container_name, local_file_name, protocol=None, sas_token=None, snapshot=None)
process_blob(blob_url)
   
    except Exception as e:
        print(e)
def cleanup_storage():
    try:
        sys.stdout.write("When you hit ENTER, the container and the downloaded file will be deleted.")
        sys.stdout.flush()
        input()
except Exception as e:
        print(e)
def process_blob(input_blob_url):
    try:
        app = ClarifaiApp(api_key = clarifai_api_key)
        model = app.public_models.general_model
        response = model.predict_by_url(url=input_blob_url)
        save_results(response)
except Exception as e:
        print(e)
def save_results(result):
    try:
        results.insert_one(result)
except Exception as e:
        print(e)
if __name__ == '__main__':
    store_and_analyze_blob()

Результаты запроса

Поиск концепций в нашей базе данных и извлечение URL-адресов файлов.

from pymongo import MongoClient
import pprint as pp
# read configuration file
import configparser
config = configparser.ConfigParser()
config.read('config.ini')
mongoServer = config['MongoDB']['server']
mongo_port = int(config['MongoDB']['port'])
mongo_database = config['MongoDB']['database']
mongo_user = config['MongoDB']['admin user']
mongo_password = config['MongoDB']['admin password']
mongo_url = "mongodb+srv://" + mongo_user + ":" + mongo_password + "@" + mongoServer + "/" + mongo_database + "?retryWrites=true"
client = MongoClient(mongo_url)
db = client[mongo_database]
concept_to_find = "bike"
def find_concept(concept):
    query = {
                "outputs.data.concepts":
                    {
                        # we are looking for elements of concepts array
                        "$elemMatch":
                            {
                                "name": concept
                            }
                    }
            }
    
    fields = {
                "outputs.input.data.image.url": 1
            }
try:
        cursor = list(db.blobResults.find(query, fields))
        for doc in cursor:
            pp.pprint(doc)
except Exception as e:
        print(e)
# Main method.
if __name__ == '__main__':
    find_concept(concept_to_find)

Первоначально опубликовано на blog.bitrise.io.