В прошлом году, во вселенной, где иметь штаны все еще имело смысл, я решил нанять личного стилиста.
Во время нашей первой встречи стилист пришел ко мне в квартиру и сфотографировал каждую вещь, которая у меня была.
Во время нашей второй встречи она встретила меня в Nordstrom’s, где попросила примерить повседневное платье за 400 долларов, блейзер за 700 долларов и кроссовки за 300 долларов. (Я никогда не думал спросить, работала ли она на комиссионных.)
Но только после нашей третьей и последней встречи, когда она, наконец, прислала мне папку, полную кураторских «образов», сделанных из моих новых и старых предметов одежды, наконец-то щелкнуло: я только что взорвался. много денег.
У меня возникло подозрение, что мы находимся на разных страницах, когда, когда мы проходили через секцию обуви в Nordstrom, стилист сказал: «Проблема с вами, людьми, занимающимися технологиями, заключается в том, что вы всегда ищете какую-то теорию, стратегию или формулу для мода. Но формулы нет – речь идет о вкус.”
Пфффф. мы увидим о что!
Я вернул дорогую одежду и решил создать собственного (более дешевого!) стилиста с искусственным интеллектом. В этом посте я покажу вам, как вы тоже можете.
Хотите увидеть видеоверсию этого поста? Проверить:
Мой ИИ-стилист был наполовину основан на этом умном шкафу из фильма. невежественный:
и наполовину на идее, что один из способов модно одеваться – это копировать модных людей. В частности, модные люди в Instagram.
Приложение извлекает из Instagram ленты нескольких модных «авторитетов» в Instagram и объединяет их с фотографиями одежды, которая у вас уже есть, чтобы порекомендовать вам наряды. Вот как это выглядит:
(Вы также можете проверить живое приложение здесь.)
На левой панели — экране шкафа — вы можете увидеть все предметы одежды, которые у меня уже есть. На правой панели вы увидите список учетных записей Instagram, на которые я подписан для вдохновения. В средней панели (главный экран) вы можете увидеть фактические рекомендации по экипировке, которые ИИ сделал для меня. Фотография для вдохновения в Instagram находится вверху, а вещи для моего гардероба показаны ниже:
Здесь моя муза стиля — Лаура Медалиа, вдохновляющий разработчик программного обеспечения, @codergirl_ в Instagram (не забудьте подписаться на нее, чтобы узнать о моде и советах по работе в сфере технологий!).
На создание всего приложения у меня ушло около месяца, и оно обошлось примерно в 7 долларов в кредитах Google Cloud (подробнее о ценах позже). Давайте погрузимся.
Архитектура
Я создал это приложение, используя комбинацию Облачное хранилище GoogleFirebase и Cloud Functions для серверной части, Реагировать для фронтенда и API Google Cloud Vision для бит ML. Я разделил архитектуру на два бита.
Во-первых, есть пакетный процесскоторый запускается каждый час (или как угодно часто) в облаке:
«Пакетный процесс» — это просто причудливый способ сказать, что я написал скрипт Python, который запускается с заданным интервалом (подробнее об этом позже). Процесс:
- Вытягивает фотографии из соцсетей
- Использует Vision API Поиск продукта функция поиска похожих вещей в моем шкафу
- Подсчет совпадений (т. е. из всех фотографий в социальных сетях, какую одежду я могу наиболее точно воссоздать в моем шкафу?)
- Записывает совпадения в пожарный магазин
Это действительно мощная часть приложения, где происходит вся магия машинного обучения. Процесс дает рекомендации по выбору одежды и записывает их в Firestore — мою любимую легковесную базу данных для разработки приложений (я использую ее почти во всех своих проектах).
Настоящий приложение (в данном случае просто отзывчивое веб-приложение) просто: оно просто читает рекомендации по одежде из Firestore и отображает их в красивом интерфейсе:
Давайте взглянем!
В идеале я хотел, чтобы мое приложение автоматически извлекало изображения из Instagram в зависимости от того, на какие учетные записи я указал ему подписаться. К сожалению, у Instagram нет API (и использование скребок нарушит их TOS). Поэтому я специально попросил у Лауры разрешения на использование ее фотографий. Я загрузил их на свой компьютер, а затем загрузил на Сегмент облачного хранилища Google:
# Create a cloud storage bucket
gsutil mb gs://inspo-pics-bucket
# Upload inspiration pics
gsutil cp path/to/inspo/pics/*.jpg gs://inspo-pics-bucket
Фильтрация модных фото
Мне нравится аккаунт Лауры за вдохновение, потому что она обычно публикует свои фотографии в одежде с ног до головы (включая туфли). Но некоторые фото в ее аккаунте больше похожи на это:
Восхитительно, да, но лично я не могу выглядеть одетым только в воротник. Поэтому мне нужен был какой-то способ узнать, на каких фотографиях есть наряды (которые носят люди), а на каких нет.
Для этого я обратился к своему верному коню, API Google Cloud Vision (который я использую по-разному для этого проекта). Сначала я использовал его классификация функция, которая присваивает метки изображению. Вот ярлыки, которые он дает мне для моей фотографии, пытающейся изобразить из себя инфлюенсера:
Ярлыки ранжируются по тому, насколько модель уверена в том, что они соответствуют изображению. Обратите внимание, что есть один лейбл под названием «Мода» (достоверность 90%). Чтобы отфильтровать изображения Лауры, я пометил их все с помощью Vision API и удалил все изображения, не отмеченные ярлыком «Мода». Вот код:
from google.cloud import vision
from google.cloud.vision import types
# Path to all my inspo pics in the cloud
uris = (
"gs://inspo-pics-bucket/pic1.jpg",
"gs://inspo-pics-bucket/pic2.jpg",
...
)
# Create a Vision API Client
client = vision.ImageAnnotatorClient()
# Keep track of all the fashion pics
fashionPics = ()
for uri in uris:
image_source = vision.types.ImageSource(image_uri="gcs/path/to/file")
labels = client.label_detection(image=image).label_annotations
# Only save images that have the label "Fashion"
if any((x.description == "Fashion" for x in labels)):
fashionPics.append(uri)
Если вам нужен полный код, проверьте его здесь.
Оцифровка моего шкафа
Теперь цель состоит в том, чтобы мое приложение просматривало модные фотографии Лауры и рекомендовало мне вещи в моем шкафу, которые я могу использовать для их воссоздания. Для этого мне пришлось сфотографировать предмет одежды, который у меня был, что было бы проблемой, если бы у меня не было очень тесного шкафа.
Я повесил каждый предмет на свой манекен и сфотографировал.
Использование API поиска продуктов Vision
Как только у меня были все мои фотографии вдохновения моды и мои фотографии гардероба, я была готова начать давать рекомендации по одежде, используя API поиска продуктов Google Vision.
Этот API предназначен для поддержки таких функций, как «поиск похожих товаров». Вот пример из приложения Pinterest:
IKEA также создала прекрасную демонстрацию, которая позволяет покупателям искать свои товары по изображениям с помощью такой технологии:
Я собираюсь использовать Product Search API аналогичным образом, но вместо того, чтобы подключать каталог товаров, я буду использовать свой собственный гардероб и вместо того, чтобы рекомендовать похожие индивидуальные предметыя буду рекомендовать целые наряды.
Чтобы использовать этот API, вам необходимо:
- Загрузка фотографий из шкафа в облачное хранилище
- Создайте новую группу товаров с помощью API поиска товаров.
- Создайте новый продукт для каждого предмета в вашем шкафу
- Загрузить (несколько) изображений этих продуктов
Сначала я попытался это сделать с помощью официальная клиентская библиотека Google Pythonно это было немного неуклюже, поэтому я написал свою собственную библиотеку-оболочку Python Product Search, которую вы можете найти здесь (на ПиПи). Вот как это выглядит в коде:
from visionproductsearch.ProductSearch import ProductSearch, ProductCategories
# Initialize ProductSearch with your credentials
# Pass a path to the storage bucket where you'd like to save image files
ps = ProductSearch(`my_gcp_project_id`, 'us-west1', 'path/to/creds.json', 'my_gcp_bucket_name' )
# Create a new product set
productSet = ps.createProductSet('my_test_set')
# Create a new product
product = ps.createProduct('my_fancy_shirt', ProductCategories.APPAREL)
# Add a reference image to a product
product.addReferenceImage('./skirt_pic.jpg')
# List all reference images for a product
for img in product.listReferenceImages():
print(img)
# Add a product to a product set
product.addProduct(product)
# List all products in a product set
for p in productSet.listProducts():
print(p)
# Search for similar products by image
productSet.search(ProductCategories.APPAREL, file_path='img/to/search.jpg')
Обратите внимание на эту библиотеку-оболочку автоматически обрабатывает загрузку фотографий в корзину облачного хранилищапоэтому вы можете загрузить новый предмет одежды в свой набор продуктов из локального файла изображения:
# Create a new product
product = ps.createProduct('my_fancy_shirt', ProductCategories.APPAREL)
# Add a reference image to a product
product.addReferenceImage('./skirt_pic.jpg')
Если вы, дорогой читатель, хотите сделать свой собственный набор продуктов из своих фотографий в шкафу, я написал Скрипт Python чтобы помочь вам создать набор продуктов из папки на рабочем столе. Только:
- Загрузите код с GitHub и перейдите в папку instafashion/scripts:
# Download the code
git clone git@github.com:google/making_with_ml.git
# CD into the right folder
cd making_with_ml/instafashion/scripts
- Создайте новую папку на своем компьютере для хранения всей вашей одежды (моя называется
my_closet
):
mkdir my_closet
cd my_closet
- Создайте новую папку для каждого предмета одежды и поместите все свои фотографии этого предмета в папку:
Итак, на гифке выше все мои фотографии с черным бомбардировщиком находятся в папке с именем black_bomber_jacket
.
Чтобы использовать мой сценарий, вам нужно будет назвать папки вашего продукта, используя следующее соглашение: name_of_your_item_shoe
где shoe
может быть любой из (skirt, dress, jacket, top, shoe, shorts, scarf, pants)
.
- После создания каталога с фотографиями продуктов вам нужно будет настроить некоторую конфигурацию, отредактировав файл `.env_template`:
cp .env_template .env
# In the .env file, fill out these fields:
export PROJECTID="YOUR_GCP_PROJECT_ID"
export BUCKET="YOUR_CLOSET_STORAGE_BUCKET"
export CREDS="path/to/key.json"
export CLOSET_DIR="./my_closet"
export PRODUCT_SET="PRODUCT_SET_NAME"
(О, кстати: вам нужна учетная запись Google Cloud, чтобы использовать этот API! Как только вы это сделаете, вы можете создать новый проект и загрузить файл учетных данных.)
- Затем установите соответствующие библиотеки Python и запустите скрипт.
product_set_from_dir.py
:
> pip install -r requirements.txt
> python product_set_from_dir.py
"Added 200 products to set"
Уф, это было больше шагов, чем я думал!
Когда вы запускаете этот скрипт Python, product_set_from_dir.py
, ваши фотографии одежды загружаются в облако, а затем обрабатываются или «индексируются» с помощью API поиска товаров. Процесс индексации может занять до 30 минут, так что запускайте воздушного змея или что-то в этом роде.
Поиск похожих товаров
Как только ваш набор продуктов будет проиндексирован, вы можете начать использовать его для поиска похожих товаров. Ууууу! 🎊
В коде просто запустите:
# Create a Product Search client
ps = ProductSearch("YOUR_GCP_PROJECTID", "path/to/creds.json", "YOUR_CLOSET_BUCKET")
# Grab the product set you just created
productSet = ps.getProductSet("YOUR_PRODUCT_SET_NAME")
# Call "search" with a path to an inspiration picture
results = ProductSet.search(ProductCategories.APPAREL, image_uri="gs://path/to/inspo.jpg")
''' Returns:
{'score': 0.7648860812187195,
'label': 'Shoe',
'matches': ({'product': <pyvisionproductsearch.ProductSearch.ProductSearch.Product at 0x14992d2e0>,
'score': 0.35719582438468933,
'image': 'projects/yourprojectid/locations/us-west1/products/high_rise_white_jeans_pants/referenceImages/6550f579-6b26-433a-8fa6-56e5bbca95c1'},
{'product': <pyvisionproductsearch.ProductSearch.ProductSearch.Product at 0x14992d5b0>,
'score': 0.32596680521965027,
'image': 'projects/yourprojectid/locations/us-west1/products/white_boot_shoe/referenceImages/56248bb2-9d5e-4004-b397-6c3b2fb0edc3'},
{'product': <pyvisionproductsearch.ProductSearch.ProductSearch.Product at 0x14a423850>,
'score': 0.26240724325180054,
'image': 'projects/yourprojectid/locations/us-west1/products/tan_strap_sandal_shoe/referenceImages/f970af65-c51e-42e8-873c-d18080f00430'}),
'boundingBox': (x: 0.6475263833999634
y: 0.8726409077644348
, x: 0.7815263271331787
y: 0.8726409077644348
, x: 0.7815263271331787
y: 0.9934644103050232
, x: 0.6475263833999634
y: 0.9934644103050232
)},
{'score': 0.8066604733467102,
'label': 'Shorts',
'matches': ({'product': <pyvisionproductsearch.ProductSearch.ProductSearch.Product at 0x106a4fa60>,
'score': 0.27552375197410583,
'image': 'projects/yourprojectid/locations/us-west1/products/white_sneaker_shoe_*/referenceImages/a109b530-56ff-42bc-ac73-d60578b7f363'},
{'product': <pyvisionproductsearch.ProductSearch.ProductSearch.Product at 0x106a4f400>,
'score': 0.2667400538921356,
'image': 'projects/yourprojectid/locations/us-west1/products/grey_vneck_tee_top_*/referenceImages/cc6f873c-328e-481a-86fb-a2116614ce80'},
{'product': <pyvisionproductsearch.ProductSearch.ProductSearch.Product at 0x106a4f8e0>,
'score': 0.2606571912765503,
'image': 'projects/yourprojectid/locations/us-west1/products/high_rise_white_jeans_pants_*/referenceImages/360b26d8-a844-4a83-bf97-ef80f2243fdb'},
{'product': <pyvisionproductsearch.ProductSearch.ProductSearch.Product at 0x106a4fb80>),
'boundingBox': (x: 0.4181176424026489
y: 0.40305882692337036
, x: 0.6837647557258606
y: 0.40305882692337036
, x: 0.6837647557258606
y: 0.64000004529953
, x: 0.4181176424026489
y: 0.64000004529953
)})
'''
Ответ содержит множество данных, в том числе, какие элементы были распознаны на исходной фотографии (например, «юбка», «верх») и какие элементы в вашем наборе проектов им соответствовали. API также возвращает поле «Оценка» для каждого совпадения, которое говорит вам, насколько уверен API в том, что элемент в вашем наборе продуктов соответствует изображению.
От одинаковых вещей к одинаковым нарядам
API поиска товаров просматривает вдохновляющую картинку (в данном случае фото Лауры) и находит похожие вещи в моем гардеробе. Но то, что я действительно хочу сделать, это собрать воедино наряды, которые состоят из одного топа, одной пары брюк, одного комплекта обуви и т. д. Иногда Product Search API не возвращает логичный наряд. Например, если Лора одета в длинную рубашку, которая выглядит так, почти быть платьем, API может вернуть в мой гардероб похожую рубашку и платье. Чтобы обойти это, мне пришлось написать свой собственный алгоритм логики наряда для создания наряда на основе результатов API поиска:
# This code snippet lets you avoid pairing items that don't
# make sense together in an outfit (i.e. a top AND a dress
def canAddItem(existingArray, newType):
bottoms = {"pants", "skirt", "shorts", "dress"}
newType = newType.lower()
if newType in existingArray:
return False
if newType == "shoe":
return True
if newType in bottoms and len(bottoms.intersection(existingArray)):
return False
if newType == "top" and "dress" in existingArray:
return False
return True
Костюмы для подсчета очков
Естественно, я не мог воссоздать все наряды Лауры, используя только вещи из своего ограниченного гардероба. Поэтому я решил, что мой подход будет заключаться в том, чтобы посмотреть на наряды, которые я могу наиболее точно воссоздать (используя оценки достоверности, возвращаемые API поиска товаров), и создать «оценку» для сортировки рекомендуемых нарядов.
Выяснение того, как «забить» наряд, — это творческая задача, на которую нет однозначного ответа! Вот несколько функций оценки, которые я написал. Они дают наряды, содержащие предметы, которые с высокой степенью достоверности соответствуют большему весу, и дают бонус к нарядам, которые соответствуют большему количеству предметов в моем шкафу:
# Option 1: sum up the confidence scores for each closet item matched to the inspo photo
def scoreOutfit1(matches):
if not matches:
return 0
return sum((match('score') for match in matches)) / len(matches)
# Option 2: Sum up the confidence scores only of items that matched with the inspo photo
# with confidence > 0.3. Also, because shoes will match most images _twice_
# (because people have two feet), only count the shoe confidence score once
def scoreOutfit2(matches):
if not len(matches):
return 0
noShoeSum = sum((x('score') for x in matches if (x('score') > 0.3 and not isTypeMatch("shoe", x("label")))))
shoeScore = 0
try:
shoeScore = max((x('score') for x in matches if isTypeMatch("shoe", x("label"))))
except:
pass
return noShoeSum + shoeScore * 0.5 # half the weight for shoes
Если вы хотите увидеть, как весь этот код вместе работает в действии, ознакомьтесь с этот блокнот Jupyter.
Собираем все вместе
После того, как я написал всю логику создания нарядов в скрипте Python, я запустил скрипт и записал все результаты в Firestore. Firestore — это бессерверная база данных, предназначенная для простого использования в приложениях, поэтому, как только я записал туда все совпадения с моими нарядами, мне было легко написать внешний интерфейс вокруг нее, благодаря которому все выглядело красиво. Я решил создать веб-приложение React, но вы можете просто отобразить эти данные в приложении Flutter, iOS или Android!
И это почти все! Возьми это, дорогой стилист.