Алгоритм потери триплетов в MXNet/Gluon

искусственный интеллект TensorFlow глубокое обучение алгоритм
Алгоритм потери триплетов в MXNet/Gluon

Triplet Loss, то есть потеря триплетов, используется для обучения наборов данных с небольшими различиями, с большим количеством меток в наборе данных и меньшим количеством выборок меток. Входные данные включаютПример якоря⚓️,Положительный примериОтрицательный пример, оптимизировав модель таким образом, чтобы расстояние между примером привязки и положительным примером быломеньше, чемРасстояние между якорным примером и отрицательным примером реализовано вычисление подобия образца. Якорный пример — это случайно выбранный образец в наборе образцов, положительный пример и якорный пример принадлежат к одному и тому же классу, а отрицательный пример и якорный пример принадлежат к разным классам.

Triplet Loss

При обучении модели Triplet Loss вам нужно вводить только образцы, а не метки, чтобы избежать проблемы слишком большого количества меток и слишком малого количества образцов с одной и той же меткой Модель заботится только о кодировании образца, а не о категории образца. Triplet Loss имеет хороший эффект при вычислении и поиске сходства.Он может изучать связь между образцами и преобразованными образцами, а также извлекать другие образцы, наиболее похожие на текущий образец.

Тройная потеря обычно применяется на индивидуальном уровне.мелкозернистыйРаспознавание, такое как классификация кошек и собак, представляет собой распознавание больших категорий, но некоторые требования должны быть точными на индивидуальном уровне, например, распознавание кошек разных типов и окрасов и т. д., поэтому основным применением Triplet Loss также является в области мелкозернистого поиска.

Сравнение потерь триплетов:

  • Если в качестве категорий для обучения классификации используются разные люди, размерность Softmax может быть намного больше, чем характеристика, и точность не может быть гарантирована.
  • Triplet Loss обычно может лучше изучать признаки, чем классификация, и имеет лучший эффект при измерении расстояния выборки;
  • Triplet Loss поддерживает настройку порогового значения Margin для управления расстоянием между положительными и отрицательными образцами.После нормализации признаков достоверность можно повысить, отрегулировав порог.

Тройная потеряформула:

\sum_{i}^n \Bigl[ \ \Vert \ f(x_i^a) - f(x_i^p) \ \Vert_2^2 - \Vert \ f(x_i^a) - f(x_i^n) \ \Vert_2^2 + \alpha \ \Bigl]_+

Для других, пожалуйста, обратитесь к алгоритму Triplet Loss.бумага.

В этой статье используетсяMXNet/GluonФреймворк глубокого обучения, выбор набора данныхMNIST, который реализует алгоритм Triplet Loss.

этой статьиисходный код: https://GitHub.com/spike king/triplet-loss-gluon


набор данных

Установите библиотеку MXNet:

pip install mxnet

Рекомендуемая загрузка исходного кода Douban, быстрая скорость, -i https://pypi.douban.com/simple

MNISTЭто известная библиотека распознавания рукописных цифр, которая содержит рукописные символы из 10 цифр, например от 0 до 9, а размер изображения представляет собой изображение в градациях серого 28 * 28. Цель состоит в том, чтобы определить правильные цифры в соответствии с изображением.

Загрузите набор данных с классом MNIST, получите обучающий наборmnist_trainи тестовый наборmnist_testданные и метки.

mnist_train = MNIST(train=True)  # 加载训练
tr_data = mnist_train._data.reshape((-1, 28 * 28))  # 数据
tr_label = mnist_train._label  # 标签

mnist_test = MNIST(train=False)  # 加载测试
te_data = mnist_test._data.reshape((-1, 28 * 28))  # 数据
te_label = mnist_test._label  # 标签

Ключевым шагом в обучении Triplet Loss является подготовка обучающих данных. Этот пример наследует класс набора данных для создания класса набора данных Triplet.TripletDataset:

  1. В конструкторе:
    • Передайте исходные данные rd, исходную метку rl;
    • _dataи_labelстандартные данные и переменные метки;
    • _transformстандартная переменная преобразования;
    • перечислить_get_data(),Заканчивать_dataи_labelназначение;
  2. __getitem__Является интерфейсом обработки данных, возвращает данные по индексу idx, поддерживает вызов_transformвыполнить преобразование данных;
  3. __len__общее количество данных;
  4. _get_data()является основным методом присвоения данных:
    • Разделите индекс и получите список значений индекса тех же данных с той же меткойdigit_indices;
    • Создайте тройки, т. е. индексную комбинированную матрицу якорных примеров, положительных примеров и отрицательных примеров;
    • Данные — это триплет, а метка — матрица единиц, потому что метка не имеет практического значения в Triplet Loss;

Выполнение:

class TripletDataset(dataset.Dataset):
    def __init__(self, rd, rl, transform=None):
        self.__rd = rd  # 原始数据
        self.__rl = rl  # 原始标签
        self._data = None
        self._label = None
        self._transform = transform
        self._get_data()

    def __getitem__(self, idx):
        if self._transform is not None:
            return self._transform(self._data[idx], self._label[idx])
        return self._data[idx], self._label[idx]

    def __len__(self):
        return len(self._label)

    def _get_data(self):
        label_list = np.unique(self.__rl)
        digit_indices = [np.where(self.__rl == i)[0] for i in label_list]
        tl_pairs = create_pairs(self.__rd, digit_indices, len(label_list))
        self._data = tl_pairs
        self._label = mx.nd.ones(tl_pairs.shape[0])

create_pairs()это основная логика для создания троек:

  1. Определите количество выбранных образцов для разных этикеток и выберите минимальное количество образцов этикеток;
  2. Перемешайте значения индекса метки d случайным образом и выберите образцы i и i+1 в качестве якорей и положительных примеров;
  3. Произвольно выбрать (Randrange) образцы i на других этикетках dn в качестве отрицательных примеров;
  4. Переберите все метки и все образцы, чтобы сгенерировать случайные комбинации якорей, положительных и отрицательных примеров.

Созданная таким образом комбинационная матрица обеспечивает равномерное распределение выборок, что не только позволяет избежать слишком больших комбинаций (по сравнению с полной перестановкой), но и вносит достаточную случайность (двойную случайность). Примечание. Поскольку скользящее окно равно 2, т. е. i и i+1, 19 сэмплов создают 18 групп сэмплов.

Конкретная реализация выглядит следующим образом:

@staticmethod
def create_pairs(x, digit_indices, num_classes):
    x = x.asnumpy()  # 转换数据格式
    pairs = []
    n = min([len(digit_indices[d]) for d in range(num_classes)]) - 1  # 最小类别数
    for d in range(num_classes):
        for i in range(n):
            np.random.shuffle(digit_indices[d])
            z1, z2 = digit_indices[d][i], digit_indices[d][i + 1]
            inc = random.randrange(1, num_classes)
            dn = (d + inc) % num_classes
            z3 = digit_indices[dn][i]
            pairs += [[x[z1], x[z2], x[z3]]]
    return np.asarray(pairs))

Используйте DataLoader для переноса TripletDataset в качестве итератораtrain_dataиtest_data, который поддерживает пакетный вывод сэмплов.train_dataдля обучения сети,test_dataИспользуется для аутентификации в сети.

def transform(data_, label_):
    return data_.astype(np.float32) / 255., label_.astype(np.float32)

train_data = DataLoader(
    TripletDataset(rd=tr_data, rl=tr_label, transform=transform),
    batch_size, shuffle=True)

test_data = DataLoader(
    TripletDataset(rd=te_data, rl=te_label, transform=transform),
    batch_size, shuffle=True)

сеть и обучение

Базовая сеть Triplet Loss использует очень простой многослойный персептрон, в основном для проверкиTriplet LossЭффект.

base_net = Sequential()
with base_net.name_scope():
    base_net.add(Dense(256, activation='relu'))
    base_net.add(Dense(128, activation='relu'))
    
base_net.collect_params().initialize(mx.init.Uniform(scale=0.1), ctx=ctx)

Параметры инициализации, использующие равномерное равномерное распределение, диапазон[-0.1, 0.1], эффект аналогичен следующему:

Uniform

Gluon поставляется с функцией потери TripletLoss, что очень хорошо?, сочетание промышленности и научных кругов очень хорошо! Инициализировать функцию потерьtriplet_lossи тренерtrainer_triplet.

triplet_loss = gluon.loss.TripletLoss()  # TripletLoss损失函数
trainer_triplet = gluon.Trainer(base_net.collect_params(), 'sgd', {'learning_rate': 0.05})

Тренировочный процесс Triplet Loss:

  1. Выполнить эпоху циклически, всего 10 раундов;
  2. train_dataИтеративно выводить данные обучающих данных каждой партии;
  3. Укажите среду выполнения для обученияas_in_context()среда данных MXNet является средой обучения;
  4. Данные поступают из TripletDataset и могут быть непосредственно разделены на три примера;
  5. Три примера общих моделейbase_net,рассчитатьtriplet_lossфункция потерь;
  6. Вызов loss.backward(), обратное распространение для деривации;
  7. Настроить трейнерtrainer_tripletШагbatch_size;
  8. Вычислить среднее значение функции потерьcurr_loss;
  9. Использовать тестовые данныеtest_dataОценить сетьbase_net;

Выполнение:

for epoch in range(10):
    curr_loss = 0.0
    for i, (data, _) in enumerate(train_data):
        data = data.as_in_context(ctx)
        anc_ins, pos_ins, neg_ins = data[:, 0], data[:, 1], data[:, 2]
        with autograd.record():
            inter1 = base_net(anc_ins)
            inter2 = base_net(pos_ins)
            inter3 = base_net(neg_ins)
            loss = triplet_loss(inter1, inter2, inter3)  # Triplet Loss
        loss.backward()
        trainer_triplet.step(batch_size)
        curr_loss = mx.nd.mean(loss).asscalar()
        # print('Epoch: %s, Batch: %s, Triplet Loss: %s' % (epoch, i, curr_loss))
    print('Epoch: %s, Triplet Loss: %s' % (epoch, curr_loss))
    evaluate_net(base_net, test_data, ctx=ctx)  # 评估网络

Оценка сети также является важным процессом для проверки способности сети к обобщению:

  1. настраиватьtriplet_lossФункция убытка, маржа установлена ​​на 0;
  2. test_dataИтеративно выводить данные проверки данных каждой партии;
  3. Укажите среду для данных проверки, которые должны быть связаны с обучением.последовательный, потому что проверяется в процессе обучения;
  4. С помощью модели прогнозируются троичные данные и рассчитывается функция потерь;
  5. Поскольку запас TripletLoss равен 0, только 0 является правильным прогнозом, а все остальные неверны;
  6. Подсчитайте общее количество образцов и количество правильных образцов в целом и рассчитайте правильную скорость всех тестовых данных;

Выполнение:

def evaluate_net(model, test_data, ctx):
    triplet_loss = gluon.loss.TripletLoss(margin=0)
    sum_correct = 0
    sum_all = 0
    rate = 0.0
    for i, (data, _) in enumerate(test_data):
        data = data.as_in_context(ctx)

        anc_ins, pos_ins, neg_ins = data[:, 0], data[:, 1], data[:, 2]
        inter1 = model(anc_ins)  # 训练的时候组合
        inter2 = model(pos_ins)
        inter3 = model(neg_ins)
        loss = triplet_loss(inter1, inter2, inter3)  

        loss = loss.asnumpy()
        n_all = loss.shape[0]
        n_correct = np.sum(np.where(loss == 0, 1, 0))

        sum_correct += n_correct
        sum_all += n_all
        rate = safe_div(sum_correct, sum_all)

    print('准确率: %.4f (%s / %s)' % (rate, sum_correct, sum_all))
    return rate

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

Epoch: 0, Triplet Loss: 0.26367417
准确率: 0.9052 (8065 / 8910)
Epoch: 1, Triplet Loss: 0.18126598
准确率: 0.9297 (8284 / 8910)
Epoch: 2, Triplet Loss: 0.15365836
准确率: 0.9391 (8367 / 8910)
Epoch: 3, Triplet Loss: 0.13773362
准确率: 0.9448 (8418 / 8910)
Epoch: 4, Triplet Loss: 0.12188278
准确率: 0.9495 (8460 / 8910)
Epoch: 5, Triplet Loss: 0.115614936
准确率: 0.9520 (8482 / 8910)
Epoch: 6, Triplet Loss: 0.10390957
准确率: 0.9544 (8504 / 8910)
Epoch: 7, Triplet Loss: 0.087059245
准确率: 0.9569 (8526 / 8910)
Epoch: 8, Triplet Loss: 0.10168926
准确率: 0.9588 (8543 / 8910)
Epoch: 9, Triplet Loss: 0.06260935
准确率: 0.9606 (8559 / 8910)

визуализация

Основная функция Triplet Loss заключается в кодировании данных в виде различимых признаков. использоватьPCAУменьшение размерности преобразует признаки выборки в визуальное двумерное распределение.Путем наблюдения можно увидеть, что признаки выборки имеют определенное различие. Эффект следующий:

PCA-Triplet

Исходное распределение данных менее эффективно:

PCA-Origin

В конце обучения выполните для визуализации данных:

  • необработанные данные и метки
  • Данные и метки выводятся сетью Triplet Loss

Выполнение:

te_data, te_label = transform(te_data, te_label)
tb_projector(te_data, te_label, os.path.join(ROOT_DIR, 'logs', 'origin'))
te_res = base_net(te_data)
tb_projector(te_res.asnumpy(), te_label, os.path.join(ROOT_DIR, 'logs', 'triplet'))

Инструмент визуализации основан на tensorboard и реализует визуализацию распределения данных через интерфейс визуализации встраивания вектора. существуетtb_projector()В методе введите данные, метки и пути для создания визуального формата данных.

Выполнение:

def tb_projector(X_test, y_test, log_dir):
    metadata = os.path.join(log_dir, 'metadata.tsv')
    images = tf.Variable(X_test)
    with open(metadata, 'w') as metadata_file: # 把标签写入metadata
        for row in y_test:
            metadata_file.write('%d\n' % row)
    with tf.Session() as sess:
        saver = tf.train.Saver([images])  # 把数据存储为矩阵
        sess.run(images.initializer)  # 图像初始化
        saver.save(sess, os.path.join(log_dir, 'images.ckpt'))  # 图像存储
        config = projector.ProjectorConfig()  # 配置
        embedding = config.embeddings.add()  # 嵌入向量添加
        embedding.tensor_name = images.name  # Tensor名称
        embedding.metadata_path = metadata  # Metadata的路径
        projector.visualize_embeddings(tf.summary.FileWriter(log_dir), config)  # 可视化嵌入向量

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

TensorBoard требует дополнительной установки TensorFlow:

pip install tensorflow

Triplet Loss играет важную роль в области кодирования данных, и алгоритм также очень умный.Он подходит для рекомендаций по сходству и других потребностей.Это одна из важных промышленных потребностей, таких как рекомендуемые рецепты, рекомендуемая музыка, рекомендуемые видео , и т.д. Модель Triplet Loss может изучать сходство разных выборок в наборе данных. В дополнение к традиционному методу расчета потерь Triplet Loss есть несколько интересных оптимизаций, таких какLossless Triplet LossЖдать.

OK, that's all! Enjoy it!