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

дедупликация
/ diːˌdjuːplɪˈkeɪʃ (ə) n /

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

дедупликация удаляет повторяющуюся информацию перед ее сохранением

Как сказано в определении, задача, которую мы пытаемся сделать, - удалить повторяющиеся тексты / предложения и так далее. Это не что иное, как проверка того, насколько похож текст друг на друга. Они могут быть абсолютно идентичными, например:
Глубокое обучение - это круто! и Глубокое обучение - это здорово!.
Или они могут быть очень похожи друг на друга с точки зрения того, что пытается передать предложение, например:
Глубокое обучение - это круто! и Глубокое обучение - это так здорово.
Мы знаем, что эти два предложения передают одно и то же, и это то, что мы хотим, чтобы наши машины уловили.

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

Проблема

Эта проблема. Да, это наша главная цель. Решить проблему. Позвольте привести пример. Скажем, вам нужно отправить действительно смешные шутки (ваша шутка может быть предложением или связкой предложений) группе людей по электронной почте (LOL!), и ваш начальник просит вас сделать уверен, что люди не получают таких же шуток. Поэтому вы должны убедиться, что все ваши шутки уникальны и людям не надоедает их содержание.
Что за работа, серьезно?

Вы, как крутой программист, решаете автоматизировать эту задачу. У вас есть этот волшебный API, который дает вам множество бесплатных шуток, и вы пишете сценарий, чтобы разослать шутки группе людей, которых любит ваш босс. Но ведь мы не можем доверять этому волшебному API, не так ли? Это волшебство. Что делать, если API дает вам похожие шутки? Вы не можете рискнуть расстроить своего начальника.
Здесь вы можете использовать механизм дедупликации, который гарантирует, что ни одна отправленная шутка не будет похожа на отправленную в прошлом.

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

В пространстве СТС…

Давайте попробуем разбить это на то, как определяется это измерение сходства и между какими двумя объектами мы пытаемся найти сходство (буквально текст как есть или что-то еще?).

Во-первых, говоря об измерении сходства, можно использовать довольно много разных вариантов. Для полноты картины перечислим несколько:
1. Сходство Жаккара
2. Косинусное сходство
3 . Расстояние земного движителя
4. Расстояние Дженсена-Шеннона

Но чтобы перейти к делу, мы будем использовать Косинусное подобие. Математически косинусное сходство - это мера сходства между двумя векторами (отличными от нуля) внутреннего пространства продукта, которая измеряет косинус угла между ними.

Если два документа похожи и если они находятся далеко друг от друга в евклидовом пространстве, они все равно могут быть довольно близки друг к другу. Это фиксируется косинусным расстоянием и, следовательно, выгодно.

Во-вторых, где мы используем это косинусное расстояние? Между парами строк предложений? Неа! Здесь мы использовали возможности обработки естественного языка и глубокого обучения. Мы используем векторы.

Вектор слова / предложения - это строка действительных чисел (в отличие от фиктивных чисел), где каждая точка отражает размерность слова / предложения значение и , когда семантически похожие слова / предложения имеют похожие векторы.

И, опять же, есть масса способов получить эти векторы. Вот некоторые из них:
Встраивание слов: встраивание слов в word2vec, GloVe, BERT, ELMo и т. Д.
Встраивание предложений: встраивание предложений BERT, Универсальный кодировщик предложений и т. Д.

Я сразу перейду к методам, с которыми я лично экспериментировал и которые прекрасно сработали для меня.

               word2vec + Universal Sentence Encoder

Чтобы эта статья не была чисто ориентированной на реализацию (а это и было задумано), я постараюсь очень кратко объяснить, что это за модели.

word2vec

word2vec поставляется в двух вариантах: Skip-Gram и Модель непрерывного мешка слов (CBOW). Если вам нужно подробное объяснение, по обоим этим вариантам есть много материала. Я буду здесь очень чёткой. Модель skip-gram немного медленнее, но обычно лучше справляется с нечастыми словами. Следовательно, это то, что часто используется. Поговорим об этом вкратце.

Вы найдете эту диаграмму почти в каждом блоге и руководстве по word2vec (модель Skip-Gram). В этой архитектуре модель использует текущее слово для прогнозирования окружающего окна контекстных слов. Он более взвешивает близлежащие контекстные слова, чем отдаленные контекстные слова.

Здесь мы смотрим на окно контекстных слов (в данном случае по 2 слова с каждой стороны) и пытаемся предугадать центральное слово.

Считайте, что w (t) - это входное слово, обычное скалярное произведение между весовой матрицей и входным вектором w (t) выполняется единственным скрытым слоем. Мы применяем функцию softmax к скалярному произведению между выходным вектором скрытого слоя и весовой матрицей. Это дает нам вероятности слов, которые появляются в контексте w (t) в текущем местоположении слова.

Именно векторы, присутствующие в скрытых слоях, становятся векторным представлением этого слова. Но это встраивание «слов», и нам нужно найти похожие «предложения». Итак, как получить векторное представление предложения вместо простого встраивания слов?

Один простой и тривиальный способ (тот, который я покажу сегодня) - это просто усреднить вложение всех слов этого предложения. Просто, не правда ли?

Теперь что касается основной части, давайте запишем это в код.

w2vmodel = gensim.models.KeyedVectors.load_word2vec_format(
'models/GoogleNews-vectors-negative300.bin.gz'), binary=True)

def sent2vec(s):                               
'''
Finding word2vec vector representation of sentences                               @param s  : sentence                                
'''                               
    words = str(s).lower()                               
    words = word_tokenize(words)                               
    words = [w for w in words if not w in stop_words]
    words = [w for w in words if w.isalpha()]
    
    featureVec = np.zeros((300,), dtype="float32")
    nwords = 0
                               
    for w in words:                                   
        try:                                       
            nwords = nwords + 1                                       
            featureVec = np.add(featureVec, w2vmodel[w])
        except:                                       
            continue                               
        # averaging                               
        if nwords > 0:                                   
            featureVec = np.divide(featureVec, nwords)
    return featureVec

def get_w2v_vectors(list_text1, list_text2): 
‘’’
Computing the word2vec vector representation of list of sentences
@param list_text1 : first list of sentences
@param list_text2 : second list of sentences 
‘’’ 
    print(“Computing first vectors…”)
    text1_vectors = np.zeros((len(list_text1), 300))
    for i, q in tqdm(enumerate(list_text1)):
        text1_vectors[i, :] = sent2vec(q)
    text2_vectors = np.zeros((len(list_text2), 300))
    for i, q in tqdm(enumerate(list_text2)):
        text2_vectors[i, :] = sent2vec(q)
    return text1_vectors, text2_vectors

Вот и все! 🤷‍♂ У вас есть вложение предложения с помощью word2vec.

Универсальный кодировщик предложений

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

Поставляется в двух вариантах:
1. Один с кодировщиком Transformer
2. Один с Сетью глубокого усреднения

У каждого из них разные цели проектирования:
1. Обеспечивает высокую точность за счет большей сложности модели и увеличения потребления ресурсов.
2. Обеспечивает эффективный вывод при немного сниженной точности.

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

usemodel = hub.Module('models/sentence_encoder')
def get_use_vectors(list_text1, list_text2):
'''
Computing the USE vector representation of list of sentences
@param  list_text1  :   first list of sentences
@param  list_text2  :   second list of sentences 
'''
    print("Computing second vectors...")
    messages1 = list_text1
    messages2 = list_text2
    num_batches = math.ceil(len(messages1) / BATCH_SIZE)
    # Reduce logging output.
    tf.logging.set_verbosity(tf.logging.ERROR)
    message_embeddings1 = []
    message_embeddings2 = []
    with tf.Session() as session:
        session.run([tf.global_variables_initializer(),
             tf.tables_initializer()])
    for batch in range(num_batches):
        print(batch * BATCH_SIZE, batch *
              BATCH_SIZE + BATCH_SIZE)
        batch_msgs1 = messages1[batch * BATCH_SIZE: batch *
                    BATCH_SIZE + BATCH_SIZE]
        batch_msgs2 = messages2[batch * BATCH_SIZE: batch *
                    BATCH_SIZE + BATCH_SIZE]
        message_embeddings1_temp, message_embeddings2_temp =  session.run([usemodel(batch_msgs1), usemodel(batch_msgs2)])
        
        message_embeddings1.append(message_embeddings1_temp)
        message_embeddings2.append(message_embeddings2_temp)
    all_embedding1 = np.concatenate(tuple(message_embeddings1))
    all_embedding2 = np.concatenate(tuple(message_embeddings2))
    return all_embedding1, all_embedding2

Опять все! 🤷‍♂

Теперь у нас есть вложение предложений для ваших шуток из двух разных моделей.

Теперь нам нужны косинусные сходства!

def cosine_similarity(list_vec1, list_vec2):
'''
Computing the cosine similarity between two vector representation
@param  list_text1  :   first list of sentences
@param  list_text2  :   second list of sentences 
'''
    cosine_dist = [cosine(x, y) for (x, y) in zip(np.nan_to_num(list_vec1), np.nan_to_num(list_vec2))]
    cosine_sim = [(1 - dist) for dist in cosine_dist]
    return cosine_sim

Когда я выполнял эту работу до вас, у меня была куча анекдотов, которые не были дубликатами, и несколько анекдотов. В частности, у меня было 385K неповторяющихся пар и 10K повторяющихся пар. Я построил AUC-ROC для этой задачи, используя только модель word2vec.

Отлично! Кривая выглядит очень красиво. (Я специально опускаю матрицы путаницы).

TPR / отзыв / чувствительность: 77%
FPR: 2,2%

Посмотрим, как работает универсальный кодировщик предложений.

Площадь под кривой немного лучше, не так ли?

TPR / отзыв / чувствительность: 77%
FPR: 2,2%

Посмотрим, что получится, когда мы их объединим. Под объединением я подразумеваю усреднение косинусных сходств обоих подходов и проверку показателей. «Усредненный ансамбль моделей».

Лучший пока что!

TPR / отзыв / чувствительность: 78,2%
FPR: 1,5%

Wohooo! 🎉🎉 У нас явный победитель!

Это один из быстрых способов поиска дубликатов. Никакого обучения, просто загрузите существующие блестящие модели и используйте их для своей задачи STS!

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

И таким образом сделайте счастливыми друзей своего босса, босса счастливым, получайте хорошую зарплату и будьте счастливы :)