Junglee Games - один из лидеров онлайн-игр в Индии, ежедневно обрабатывающий более 2 миллионов игр. Ежедневно платформу посещают сотни тысяч активных игроков, и ее масштабы будут продолжать расти по мере того, как Индия быстро открывает арену онлайн-игр. Как вы понимаете, с расширяющейся пользовательской базой Junglee система обслуживания клиентов должна быть хорошо оснащена для быстрого и эффективного решения вопросов клиентов и проблемных моментов. Быстрое решение проблем клиентов важно не только с точки зрения взаимодействия, но и для создания доверия в игровом сообществе и лояльности к бренду.

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

Заявление о проблеме

Наши клиенты могут подавать запросы по различным каналам. Жалобы, полученные через приложение и электронные письма, составляют большую часть жалоб, которые получает команда CX. Наши агенты CX могут видеть эти жалобы в пользовательском интерфейсе Salesforce, который является нашим основным инструментом CRM. Впоследствии агент вручную помечает эти запросы и направляет их соответствующим группам. Пометка включает определение категории и подкатегории, что, в свою очередь, также помогает нам расставить приоритеты для этих запросов. Ниже приведены некоторые примеры категорий и подкатегорий:

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

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

  • Моделирование: исследование данных, архитектура модели (векторизация слов, CNN, гибридные нейронные сети), правила для большого пальца.
  • Развертывание: Simple Salesforce API, Salesforce Workbench, запрос данных и обновление через Salesforce API

Моделирование

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

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

Исследование данных

На этапе исследования данных мы столкнулись с некоторыми интересными проблемами.

  • Большое количество ярлыков: существует более 40 подкатегорий, к которым можно пометить обращение. Кроме того, эти метки могут иметь перекрывающиеся значения, что может привести к неоднозначности при обучении. Мы решили эту проблему, объединив ярлыки с похожими значениями. . Устранение двусмысленности входных данных моделирования было решающим для повышения точности.
  • HTML-теги, восстановление фрагментов в текстах сообщений электронной почты. Была проведена обширная очистка данных для удаления специальных символов и нежелательных фрагментов текста.
  • Неправильное написание, смешанный язык: это побудило нас принять подход, который использует контекстное моделирование вместо моделирования на основе ключевых слов. Мы рассмотрим это более подробно позже.

После идентификации целевой переменной и исследования данных мы перешли к этапу построения модели.

Модельная архитектура

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

Моделирование текстовых данных

Текстовые данные передаются в алгоритм Word2Vec, который изучает значения слов. Полученные значения затем передаются в сверточную нейронную сеть (CNN) для выявления закономерностей в текстовых данных. Подробнее об этом ниже.

Word2Vec: Оригинальный алгоритм word2vec, разработанный Mikolov et al, имеет множество приложений и почти повсеместно признан современным. Это помогло нам преобразовать наши текстовые данные в вектор признаков. Учитывая огромный корпус слов, который у нас был, в том числе орфографические ошибки, мы сгенерировали вложения слов, используя word2vec для уменьшения размерности и вычисления сходства слов. В результате такие слова, как турнир и поворот, или любое такое слово с ошибками в преобразованном векторном пространстве будут похожи, даже если частота поворота в наборе данных невысока. Модель обучалась независимо с использованием пакета gensim на Python. Мы использовали CBOW (Непрерывный набор слов) в качестве опции для прогнозирования вероятности слова с учетом контекста. Окончательный размер встраивания составлял 100 измерений, что дало нам наилучшие результаты при настройке модели.

Сходство слов, выведенное из Word2Vec, можно визуализировать на Tensorboard следующим образом:

Обучение модели Word2Vec:

from gensim.models.word2vec import Word2Vec
from gensim.models.doc2vec import TaggedDocument
from sklearn import utils# Create unique tags for each document
def labelize(text,label):
    result = []
    prefix = label
    for i, t in zip(text.index, text):
        result.append(TaggedDocument(t.split(), 
                      [prefix + '_%s' % i]))
    return resultdef CBOW_model(textseries, epochs=30, size=100):
    all_x = textseries
    all_x_w2v = labelize(all_x, 'all')# Define model parameters
    model_cbow = Word2Vec(sg=0, size=100, 
                             negative=5, window=2, 
                             min_count=2)
    model_cbow.build_vocab([x.words for x in all_x_w2v])
    for epoch in range(epochs):
        model_cbow.train(utils.shuffle([x.words for x 
                            in all_x_w2v]),
                            total_examples=len(all_x_w2v), epochs=1)
    return model_cbow
model_cbow = CBOW_model(df['text'], epochs=30, size=100)

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

Построение матрицы встраивания слова в вектор:

# NUM_WORDS: number of words retained in corpus
def create_embedding_matrix(model_cbow,text_column, NUM_WORDS):
    embeddings_index = {}
    
    # Create dictionary: Word to Vector Mapping
          # key = vocabulary words. # value = word vector
    for w in model_cbow.wv.vocab.keys():
        embeddings_index[w] = model_cbow.wv[w]
    # List containing no of words in each case
    length = []
    for x in text_column:
        length.append(len(x.split()))
    
    # Word to Vector Mapping from dictionary to matrix
    tokenizer = build_tokenizer(text_column, NUM_WORDS)    
    embedding_matrix = np.zeros((NUM_WORDS, 100))
    for word, i in tokenizer.word_index.items():
        if i >= num_words:
            continue
        embedding_vector = embeddings_index.get(word)
        if embedding_vector is not None:
            embedding_matrix[i] = embedding_vector
            
    return embedding_matrix
embedding_matrix = create_embedding_matrix(model_cbow,
                               df['text'], NUM_WORDS)

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

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

Мы рассмотрим упрощенный пример, чтобы понять одномерную свертку:

Слова были преобразованы в 4-мерное векторное пространство. Свертка применяется к тексту «Загрузить документы KYC» с 2 фильтрами и размером ядра 2. Размер ядра дает количество строк в каждой подматрице, которое нужно умножить на фильтры. В приведенном выше примере будет 2 подматрицы: [[0,5, -1,2,3,6,4,5], [2,4,4,5, -0,6,0,0]] и [[2,4,4,5, -0,6,0,0], [ -3.2,5.0,1.0, -4.2]], который будет скалярно умножен с каждым фильтром f1 и f2. Для последовательности из n слов, свернутых через размер ядра m и количество фильтров f, выходной размер = [(n-m + 1), f]; [(3–2 + 1), 2] в нашем случае.

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

Моделирование нетекстовых данных:

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

Объединяя две модели, мы получаем структуру гибридной нейронной сети, как показано ниже. Модели CNN и MLP обучаются одновременно, а выходные узлы из двух моделей подключаются к новому скрытому слою. Затем он подключается к выходному слою, который выводит вероятности для каждой подкатегории. Подкатегория с наибольшей вероятностью выбирается в качестве подкатегории для случая.

Данные были разделены на 3 набора - обучение (81%), проверка (9%) и тестирование (10%). Было выполнено несколько итераций по количеству нейронов, отфильтруйте размеры в слое свертки, чтобы получить лучшую модель. Была выбрана модель с максимальной точностью на наборах для тестирования и валидации, в то же время гарантируя, что модель не переоснащается.

from keras.models import Sequential
from keras.layers import Conv1D, GlobalAveragePooling1D, Embedding
from keras.layers import concatenate, Model, layers
# NUM_WORDS: number of words retained in corpus
# EMBEDDING_DIM: dimensions of embedding matrix
# MAXLEN: length for a sequence:
#           if length > MAXLEN, sequence is trimmed
#           if length < MAXLEN, sequence is padded with 0s
# Define MLP network
def create_mlp():
    model = Sequential()
    model.add(Dense(100, 
               input_dim=X_train_mlp.shape[1], activation="relu"))
               model.add(Dense(50, activation="relu"))
    return model
# Define CNN network
def create_cnn():
    model = Sequential()
    model.add(layers.Embedding(NUM_WORDS, EMBEDDING_DIM, 
              weights=[embedding_matrix],
              input_length=MAXLEN, trainable=True))
    model.add(Conv1D(filters=128, kernel_size=2, 
              padding='valid', activation='relu', strides=1))
    model.add(GlobalAveragePooling1D())
    model.add(Dense(100, activation='relu'))
    return modelmlp = create_mlp()
cnn = create_cnn()
# Combine input from MLP and CNN and connect to a new dense layer
combinedInput = concatenate([mlp.output, cnn.output])
x = Dense(50, activation="relu")(combinedInput)
x = Dense(Y_train_enc.shape[1], activation='softmax')(x)
model = Model(inputs=[mlp.input, cnn.input], outputs=x)
model.compile(loss='categorical_crossentropy', 
              optimizer='adam', metrics=['accuracy'])
model.fit([np.array(X_train_mlp), X_train_cnn_seq],Y_train_enc, 
          validation_data=([np.array(X_val_mlp), X_val_cnn_seq],
                           Y_val_enc),
           epochs=10, batch_size=32)

Помимо вывода на основе текстовых и нетекстовых данных из жалоб, были некоторые правила большого пальца, которые были закодированы так, чтобы работать поверх выходных данных модели.

Правила большого пальца

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

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

Развертывание

После завершения построения модели следующим шагом было ее развертывание на платформе Salesforce. Поскольку Salesforce является широко используемым инструментом, доступны различные API-интерфейсы для разных целей. Мы использовали API SimpleSalesforce (SF) для Python (читайте документацию здесь). Это мощный API, который позволяет запрашивать данные из серверной части Salesforce и выполнять операции обновления для записей.

Извлечение данных: жалобы без тегов извлекаются из SF API с помощью команды sf.query_all (query). Ввод осуществляется в форме запросов SOQL, которые похожи на запросы SQL. Архитектуру имен таблиц и имен столбцов в Salesforce можно просмотреть в SalesForce WorkBench

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

Обновить категории: Категория и подкатегория для каждой жалобы обновляются с помощью команды sf.bulk.Case.update (output_dictionary). Это выполняет массовое обновление вместо последовательных обновлений и, следовательно, быстрее, когда количество жалоб, которые нужно обновлять каждый раз, превышает 10.

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

Тестирование: Salesforce предлагает тестовый сервер под названием Salesforce Sandbox для экспериментов с новыми функциями. Тестирование системной интеграции (SIT) было выполнено на этом сервере перед развертыванием на основном производственном сервере.

Ведение журнала: регистрируются все жалобы, обновленные моделью. У сотрудника есть возможность перезаписать прогнозы модели на платформе Salesforce, если модель делает неверный прогноз. Это помогает нам отслеживать производительность модели.

Среда развертывания: сценарии Python просто развертывались в планировщике задач Windows на виртуальной машине. Для определения идеальной частоты обновления сервера Salesforce был проведен анализ объема жалоб по времени.

from simple_salesforce import Salesforce
# Create Salesforce connection
sf = Salesforce(
username= USERNAME, 
password= PASSWORD, security_token=TOKEN)
# Query data
sf_data = sf.query_all(query)
df = pd.DataFrame(sf_data['records']).drop(columns='attributes')
# Get model predictions in the desired output format
output_dicts = get_model_predictions()
# Update Salesforce data tables
sf.bulk.Case.update(output_dicts)

Результаты:

  • Точность после развертывания была аналогична точности тестирования и проверки: 82% на уровне категории и 74% на уровне подкатегории.
  • Взвешенная точность и отзыв для обученной модели составили 74% и 76% соответственно. Это довольно неплохо для текстового набора данных с более чем 40 различными ярлыками и электронными письмами, которые могут включать в себя разговорный язык.
  • Для наборов данных с меньшим количеством меток такие методы, как моделирование коррелированных тем и скрытое распределение Дирихле, также могут дать приличную производительность.

В то время как мы могли решить несколько миллионов ежедневных игр, мы готовимся к десяткам миллионов. Модель НЛП может быть расширена от чистой категоризации для отправки автоматических ответов клиентам в тех случаях, когда мы достаточно уверены в прогнозах модели . Достигнутая здесь значительная экономия времени, в свою очередь, позволит нашему отделу обслуживания клиентов (CX) обрабатывать большее количество жалоб по мере того, как наши масштабы с каждым днем ​​растут.

В Junglee Games мы любим решать сложные задачи. Если вы заинтересованы в решении невероятных проблем машинного обучения в быстрорастущей среде, напишите нам по адресу [email protected]

-Невин Николас, Вивек Капур, Авишек Дас, Аакаш Дхондиял