Цель этой статьи — пометить конфиденциальные комментарии, постоянно присутствующие на нескольких платформах социальных сетей, таких как Youtube, Linkedin и т. д., с помощью BERT и преобразователей предложений.

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

Мой подход к этой проблеме заключается в том, что мы можем сохранить наши кодировки в файле pickle, размер которого зависит от набора данных (для этой статьи размер составлял ~50 МБ для 16 000 предложений), а затем выполнить проверку на сходство. Без дальнейших ожиданий, давайте погрузимся в раздел кода.

Загрузка и настройка данных

Чтобы продолжить, набор данных доступен здесь (данные не принадлежат мне, и это чисто собственность kaggle)

После распаковки основное внимание уделяется файлу с именем «train.csv».

# !pip install scipy
# !pip install pandas
# !pip install sentence-transformers
import scipy
import pandas as pd 
import pickle 
import re 
import string
from sentence_transformers import SentenceTransformer
#some of the packages we will be utilizing

Давайте прочитаем и интерпретируем, как данные представлены в настоящее время.

df = pd.read_csv('./train.csv')
df.sample(5)

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

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

Предварительная обработка данных

Во-первых, нам нужны только строки, содержащие токсичные тексты, поэтому мы пишем

df = (df[(df['toxic']==1) | (df['severe_toxic']==1) | (df['obscene']==1) | (df['threat']==1) | \
       (df['insult']==1) | (df['identity_hate']==1)])
df.shape #(16225, 8)

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

После этого наша следующая цель — очистить данные, которые содержат МНОГО ошибок и мусорных символов.

import re
import string
def  clean_text(text):
    text =  text.lower()
    text = re.sub(r"i'm", "i am", text)
    text = re.sub(r"\r", "", text)
    text = re.sub(r"he's", "he is", text)
    text = re.sub(r"she's", "she is", text)
    text = re.sub(r"it's", "it is", text)
    text = re.sub(r"that's", "that is", text)
    text = re.sub(r"what's", "that is", text)
    text = re.sub(r"where's", "where is", text)
    text = re.sub(r"how's", "how is", text)
    text = re.sub(r"\'ll", " will", text)
    text = re.sub(r"\'ve", " have", text)
    text = re.sub(r"\'re", " are", text)
    text = re.sub(r"\'d", " would", text)
    text = re.sub(r"\'re", " are", text)
    text = re.sub(r"won't", "will not", text)
    text = re.sub(r"can't", "cannot", text)
    text = re.sub(r"n't", " not", text)
    text = re.sub(r"n'", "ng", text)
    text = re.sub(r"'bout", "about", text)
    text = re.sub(r"'til", "until", text)
    text = re.sub(r"[-()\"#/@;:<>{}`+=~|.!?,]", "", text)
    text = text.translate(str.maketrans('', '', string.punctuation)) 
    text = re.sub("(\\W)"," ",text) 
    text = re.sub('\S*\d\S*\s*','', text)
    
    return text
df['comment_text'] = df['comment_text'].apply(clean_text)

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

Теперь нам нужно создать один столбец со значением 1 для всех таких комментариев и удалить все эти другие ненужные столбцы («токсичные», «серьезные_токсичные» и т. д.). анализ на него в будущем, они имеют привилегию сделать это. Для этого достаточно одной строки кода.

df['flag_toxic'] = 1
df.drop(['toxic', 'severe_toxic', 'obscene', 'threat', 'insult', 'identity_hate'], axis = 1, inplace=True)

Окончательно !! Наша предварительная обработка завершена, и теперь нам нужно создать экземпляр нашего преобразователя предложений BERT.

Кодирование и сохранение вложений

Используя преобразователи предложений, мы вызываем BERT, а затем кодируем наши предложения-запросы.

bert_based_embedder = SentenceTransformer('bert-base-nli-mean-tokens')
input_sentences = df['comment_text'].tolist()
input_sentence_embeddings = bert_based_embedder.encode(input_sentences)

Следующий код будет выполняться примерно 15–20 минут в зависимости от вычислительной мощности вашей машины. Как только это будет сделано, вы можете сохранить свои вложения, используя pickle в python, чтобы вы могли напрямую загружать их для прогнозов в будущем, например

with open('input_sentence_clean_embeddings.pickle', 'wb') as file:
    pickle.dump(query_embeddings, file)
    file.close()

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

with open('query_embeddings_clean.pickle', 'rb') as file:
    
    input_sentence_embeddings = pickle.load(file)

Вычисление подобия с использованием косинусного расстояния

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

def check(comment):
    comment_embeddings = bert_based_embedder.encode(comment)
    distances = scipy.spatial.distance.cdist(input_sentence_embeddings, [comment_embeddings], "cosine")[0]
    print(1-distances)
    if(1-distances>=0.5):
        x = 'The above statement needs to be flagged'
        return x
    
    else:
        x = 'Nothing wrong here'
        return x

Я вычел значение расстояния из 1,0, чтобы получить четкую оценку оценки, поскольку косинусное сходство работает таким образом, что близость приводит к меньшему значению. Кроме того, мы можем настроить 0,5 в соответствии с нашими потребностями, более высокое значение означает, что утверждение/комментарий должны быть действительно токсичными или плохими для срабатывания, и наоборот.

Посмотрите здесь, если хотите узнать больше о косинусном подобии

value = check('It was a good day !')
'''
Output : 
'Nothing wrong here'
[0.05024188]
'''
value = check('Shut up you imbecile !')
'''
Output : 
'The above statement needs to be flagged'
[0.62924553]
'''
value = check('take your dumb @$$ away from here')
Output : 
'The above statement needs to be flagged'
[0.55886211]
'''

Плюсы: время получения результатов ближе к 200 мс (быстрее, чем у других моделей без поддержки графического процессора).

Минусы: может потребоваться больше данных для повышения точности

Спасибо !! за чтение моего первого блога, надеюсь, вам понравилось. Не стесняйтесь оставлять отзывы, и весь код для этого блога приведен ниже.