Найдите продукты, похожие на определенный продукт, используя изображение и название продуктов.
Описание проблемы:
Shopee — это ведущая онлайн-платформа электронной коммерции, которая позволяет пользователям покупать и продавать товары в Интернете. Он специализируется на электронной коммерции и ведет свой бизнес в основном в странах Южной Азии. Клиенты ценят простой, безопасный и быстрый процесс онлайн-покупок, адаптированный к их региону. Компания также обеспечивает надежную оплату и материально-техническую поддержку, а также функцию Гарантия самой низкой цены на тысячи перечисленных товаров Shopee. В этом конкурсе они открывают изображения продуктов с названиями/описаниями и ожидают, что специалисты по машинному обучению создадут модели, которые идентифицируют похожие продукты на основе изображений и описаний.
Исследование данных:
Posting_id: уникальный идентификатор для каждой публикации/продукта.
Изображение: имя файла изображения.
Image_phash: воспринимаемый хэш изображения.
Заголовок: описание этого изображения.
Label_group: код группы, к которой принадлежит продукт.
В трейн 34250 постов и некоторые посты имеют похожие файлы изображений, фаш и заголовки. Существует 11014 групп меток. Товары, принадлежащие группе label_group, похожи. Например,
Эти три разных продукта, которые принадлежат к группе этикеток, похожи, и это видно по изображениям этих трех продуктов.
Может ли label_group рассматриваться как наземная реальность или наземная истина? Нет
Рассмотрим Image_phash. Мы можем заметить, что приведенные ниже точки данных имеют один и тот же phash изображения, но они принадлежат к разным группам меток.
Когда я проверил изображения, они оказались похожими продуктами.
Мы можем ясно видеть, что вышеуказанные 3 продукта похожи и принадлежат к разным группам этикеток. Так можно ли считать фальшивые изображения истинной правдой? Нет
Из этого обсуждения на kaggle видно, что у непохожих товаров может быть один и тот же фэш изображения. Кроме того, есть публикации с одинаковыми/похожими изображениями или описаниями, но принадлежащие к разным группам ярлыков или имеющие разный фэш изображения.
Переходя к заголовку сообщений, давайте посмотрим, какие слова наиболее часто встречаются в заголовках данных о поездах с использованием облака слов.
Мы можем ясно видеть, что многие слова здесь не английские слова. Пройдите эту дискуссию на Kaggle, где с помощью Google Translate было изучено и замечено, что английский, индонезийский, малайский и немецкий языки являются наиболее используемыми языками, и это очевидно, потому что Shopee ведет свой бизнес в основном в странах Южной Азии. Также мы можем наблюдать числа в наиболее часто встречающихся словах. Номера не должны удаляться из заголовков во время предварительной обработки, потому что числа могут значительно описывать и различать продукты.
Сколько слов в заголовках?
Сколько уникальных слов присутствует в заголовках?
С помощью гистограмм количества слов и количества уникальных слов мы можем наблюдать, что в заголовках нет повторяющихся слов. Логично, что в названии продуктов обычно нет ни часто повторяющихся слов, ни стоп-слов. Я не заметил каких-либо стоп-слов в Word Cloud.
Переходя к изображениям,
from tensorflow.keras.preprocessing.image import load_img,img_to_array | |
# dimension of squared images | |
dims=[] | |
for img in tqdm(train.image.unique()): | |
path='Data/train_images/'+img | |
img_arr=img_to_array(load_img(path)) | |
if img_arr.shape[0]==img_arr.shape[1]: | |
dims.append(img_arr.shape[0]) | |
print('Number of unique Images: {}'.format(train.image.unique().shape[0])) | |
print('Number of squared Images: {}'.format(len(dims))) |
Было замечено, что около 96% изображений в поезде являются квадратными изображениями, а более 56% изображений имеют размеры 640x640 и 1024x1024.
Формулировка проблемы машинного обучения:
Эта проблема не имеет оснований, и ее следует решать с помощью неконтролируемого подхода. Используя заголовки/описания, мы можем векторизовать их, используя базовые подходы, такие как TFIDF, Word2Vec/Glove, а также можем использовать BERT и различные варианты BERT (DistilBERT, SentenceBERT и т. д.) для получения вложений. После получения вложений мы можем сделать векторы единичными векторами и вычислить произведение DOT (Косинусное сходство) с другими векторами/вложениями. Приведение векторов/вложений к единичной длине гарантирует, что произведение DOT (косинусное сходство) находится между +1 и -1, поскольку теперь оно зависит только от угла между векторами/вложениями. Если косинусное сходство равно +1, то угол разделения между векторами равен 0 градусов (Cos(0)=+1), что, в свою очередь, означает, что они идентичны, а если косинусное сходство равно -1, то угол разделения между векторами составляет 180 градусов. (Cos(180)=-1), что, в свою очередь, означает, что они являются самыми непохожими векторами.
Точно так же встраивание изображений также можно выполнять с помощью сверточных нейронных сетей, и процесс вычисления сходства такой же, как описано выше. Мы также можем использовать Евклидово расстояние в качестве меры сходства, но, поскольку я уже создаю векторы/эмбеддинги как единичные векторы, евклидово расстояние пропорционально косинусному расстоянию. Нам нужно установить порог решения для продукта Косинусного сходства/DOT, чтобы получить совпадения для продукта, и этот оптимальный порог можно определить во время работы над проблемой.
Показатель эффективности:
Показателем производительности этой задачи является средний балл F1 по публикациям. Это означает, что оценка F1 вычисляется для каждого продукта с использованием его соответствий, и берется среднее значение всех продуктов.
Продукт ArcFace Loss/Arc Margin:
Справочный документ: https://arxiv.org/pdf/1801.07698.pdf
- ArcFace является модификацией Softmax, чтобы потери зависели только от косинусного подобия (точечный продукт) между вектором признаков и вектором весов. Возьмем, к примеру, сигмоид. Мы знаем, что сигмовидная функция равна 1/(1+exp-(W.X+b)), где X — вектор признаков, W — вес, а b — член смещения. Давайте рассмотрим, что плоскость проходит через начало координат, т.е. для простоты смещение = 0. Теперь Sigmoid=1/(1+exp-(W.X)).
- Мы знаем, что точка(W,X)=||W||.||X||.Cos(угол между ними). Мы вычисляем это и рассматриваем это как оценку вероятности и рассчитываем потери с этим. Что, если мы сделаем W и X единичными векторами? Потери зависят только от угла между этими векторами. То есть, если угол меньше (более похож на косинус), потери меньше, и во время обучения модель обновляет веса таким образом, чтобы уменьшить угол между W и X для конкретной метки класса.
- В дополнение к этому, что, если мы добавим некоторый угол к реальному? Даже если угол меньше, поскольку мы добавляем определенный угол и делаем вектор менее косинусным, модель пытается уменьшить добавленный угол во время обучения, что, в свою очередь, обновляет веса посредством обратного распространения таким образом, что фактический угол уменьшается. Мы просто усложняем обучение, что приводит к лучшему обучению, чем на самом деле.
- Этот процесс зависимости потерь от угла (подобие косинуса) и добавления к нему дополнительного угла для повышения эффективности обучения называется ArcFace-Additive Angular Margin Loss.
class ArcFace(tf.keras.layers.Layer): | |
# Implementation reference from https://github.com/lyakaap/Landmark2019-1st-and-3rd-Place-Solution/blob/master/src/modeling/metric_learning.py | |
def __init__(self, n_classes, scale, margin, **kwargs): | |
super(ArcFace, self).__init__(**kwargs) | |
self.n_classes = n_classes | |
self.scale = scale | |
self.margin = margin | |
self.cos_m = tf.math.cos(margin) | |
self.sin_m = tf.math.sin(margin) | |
def get_config(self): | |
config = super().get_config().copy() | |
config.update({'n_classes': self.n_classes,'scale': self.scale,'margin': self.margin}) | |
return config | |
def build(self, input_shape): | |
super(ArcFace, self).build(input_shape[0]) | |
self.W = self.add_weight( | |
name='W', | |
shape=(int(input_shape[0][-1]), self.n_classes), | |
initializer='glorot_uniform', | |
dtype='float32', | |
trainable=True, | |
regularizer=None) | |
def call(self, inputs): | |
X, y = inputs | |
y = tf.cast(y, dtype=tf.int32) | |
# Normalizing vectors( Unit Vectors ) to make dot product depend only on angle between vectors. | |
cosine = tf.matmul( | |
tf.math.l2_normalize(X, axis=1), | |
tf.math.l2_normalize(self.W, axis=0) | |
) | |
# Sin(angle)^2 + Cos(angle)^2 = 1 | |
sine = tf.math.sqrt(1.0 - tf.math.pow(cosine, 2)) | |
# Cos(angle+margin)=Cos(angle)*Cos(margin)-Sin(angle)*Sin(margin) | |
phi = cosine * self.cos_m - sine * self.sin_m | |
one_hot = tf.cast(tf.one_hot(y, depth=self.n_classes),dtype=cosine.dtype) | |
output = (one_hot * phi) + ((1.0 - one_hot) * cosine) | |
output *= self.scale | |
return output |
Лучшее решение:
IDF +BERT(ArcFace) + EfficientNet B3(ArcFace): Отправка
Наилучшая частная оценка 0,72 — это сочетание текстовых и графических моделей/встраивания. EfficientNet B3 (на основе изображений) был обучен с использованием ArcFace с переменным запасом. Здесь маржа добавляется в каждом сценарии, но варьируется в зависимости от угла между характеристикой и весами. Маржа больше, когда угол мал, и наоборот.
class ArcFace(tf.keras.layers.Layer): | |
# Implementation reference from https://github.com/lyakaap/Landmark2019-1st-and-3rd-Place-Solution/blob/master/src/modeling/metric_learning.py | |
def __init__(self, n_classes, scale, margin, **kwargs): | |
super(ArcFace, self).__init__(**kwargs) | |
self.n_classes = n_classes | |
self.scale = scale | |
self.margin = margin | |
self.cos_m = tf.math.cos(margin) | |
self.sin_m = tf.math.sin(margin) | |
# Cos(180-margin) | |
self.threshold = tf.math.cos(math.pi - margin) | |
# let margin=30 degress =0.52 radians, then minimum_margin= sin(180-30)*0.52 = 0.26 | |
self.minimum_margin = tf.math.sin(math.pi - margin) * margin | |
def get_config(self): | |
config = super().get_config().copy() | |
config.update({'n_classes': self.n_classes,'scale': self.scale,'margin': self.margin}) | |
return config | |
def build(self, input_shape): | |
super(ArcFace, self).build(input_shape[0]) | |
self.W = self.add_weight( | |
name='W', | |
shape=(int(input_shape[0][-1]), self.n_classes), | |
initializer='glorot_uniform', | |
dtype='float32', | |
trainable=True, | |
regularizer=None) | |
def call(self, inputs): | |
X, y = inputs | |
y = tf.cast(y, dtype=tf.int32) | |
# Normalizing vectors( Unit Vectors ) to make dot product depend only on angle between vectors. | |
cosine = tf.matmul( | |
tf.math.l2_normalize(X, axis=1), | |
tf.math.l2_normalize(self.W, axis=0) | |
) | |
# Sin(angle)^2 + Cos(angle)^2 = 1 | |
sine = tf.math.sqrt(1.0 - tf.math.pow(cosine, 2)) | |
# Cos(angle+margin)=Cos(angle)*Cos(margin)-Sin(angle)*Sin(margin) | |
phi = cosine * self.cos_m - sine * self.sin_m | |
# Smaller the angle between feature vector and weight vector, larger the margin and vice versa | |
# let theta be angle between feature vector and weight vector | |
# let theta=20 degrees, phi= Cos(30)-minimum_margin = 0.606. angle=inv_cos(0.606)= 52 degrees --> margin_added= 22 deg (approx) | |
# let theta=40 degrees, phi= Cos(40)-minimum_margin = 0.506. angle=inv_cos(0.506)= 59 degrees --> margin_added= 19 deg (approx) | |
# let theta=70 degrees, phi= Cos(70)-minimum_margin = 0.082. angle=inv_cos(0.082)= 85 degrees --> margin_added= 15 deg (approx) | |
phi = tf.where(cosine > self.threshold, phi, cosine - self.minimum_margin) | |
one_hot = tf.cast(tf.one_hot(y, depth=self.n_classes),dtype=cosine.dtype) | |
output = (one_hot * phi) + ((1.0 - one_hot) * cosine) | |
output *= self.scale | |
return output |
EfficientNet B3 (на основе изображения) обучен на этом достигнутом частном счете 0,69 с использованием порога Косинусного сходства как 0,6, и я добавил совпадения продуктов, предсказанные с использованием текстовых вложений с более высокими порогами косинусного сходства (IDF и ArcFace обучили BERT с CS порог 0,75 и 0,7), следя за тем, чтобы в матчи EfficientNet B3 добавлялись только точные прогнозы, и эта комбинация привела к частной оценке 0,72.
Будущая работа:
- Поскольку мы заметили, что заголовок находится на нескольких языках, мы можем перевести его с помощью таких ресурсов, как Google Translate, и векторизовать текст.
- Также мы можем использовать специализированный языковой или многоязычный обученный BERT, такой как индонезийский BERT и т. д.,
- Что касается изображения, обучение в сиамском стиле можно использовать для оптимизации с тройной или контрастной потерей. Кроме того, SBERT, который предварительно обучен сиамскому подходу, может быть дополнительно обучен на этих данных соревнований и использован.
- Можно попробовать другие метрики подобия, использующие fiass, и попытаться найти более эффективный способ объединения совпадений, найденных разными моделями, вместо того, чтобы просто объединять их.
Использованная литература:
- https://appliedaicourse.com/
- https://arxiv.org/pdf/1801.07698.pdf
- https://github.com/lyakaap/Landmark2019-1st-and-3rd-Place-Solution/blob/master/src/modeling/metric_learning.py
- https://kaggle.com/ragnar123/unsupervised-baseline-arcface
Репозиторий Github: https://github.com/Chaitanya-Boyalla/Shopee-Product-Matching
LinkedIn: www.linkedin.com/in/chaitanya-boyalla-23051998
электронная почта: chaitanya.boyalla@gmail.com