Classifier — это обученная модель, которая предсказывает, к какому из известных классов относится текстовый запрос пользователя.
Классификация может быть бинарной или многоклассовой.
Использование моделей классификации упрощает процесс прохождения бизнес-сценария в смартапе и делает его более гибким.
Пример. В сценарии есть слот «тип банковской карты». Он должен быть заполнен одним из значений: «виртуальная карта», «золотая», «платиновая», «классическая», «моментум» и др.
Предобученная модель классификации (нейросетевая модель на основе правил или любая другая) поможет определить, к какому конкретно классу, т. е. типу банковской карты, можно отнести ответ пользователя на вопрос про тип карты. Эту модель классификации можно вызвать в сущности для слот-филлинга (Filler с типом classifier), и слот будет заполнен наиболее вероятным вариантом ответа, который выдаст модель.
Где применяются классификаторы
Классификаторы применяются для работы сущностей Filler и Requirement с типом classifier
.
Пример использования классификатора для слот-филлинга можно увидеть в форме сценария hello.
Конфигурация классификатора и хранение моделей
Все классификаторы должны быть описаны в файле classifiers.json, который находится в директории static/references/classifiers.
Вызывать классификатор нужно по его названию с помощью external classifier
.
Пример конфигурации:
{
"binary_classifier_name": {
"type": "scikit",
"threshold": 0.6,
"path": "pretrained_binary_classifier_model.pkl",
"intents": ["да", "нет"]
},
"hello_scenario_classifier": {
"type": "external",
"timeout": 0.2,
"classifier": "binary_classifier_name"
}
}
Чекпоинты предобученных моделей в pkl-файлах должны храниться в директории static/references/classifiers_data. Там же должны находиться сохраненные pkl-файлы с кастомными слоями модели, если они есть.
Если ваша предобученная модель должна принимать на вход векторное представление реплики (embedding), и вы использовали плагин SAF Vectorizers во время обучения модели, добавьте поле vectorizer
в конфигурацию классификатора, в котором укажите название модели-векторизатора, например:
{
"type": "scikit",
"threshold": 0.7,
"path": "pretrained_model.pkl",
"intents": ["intent_1", "intent_2" ... "intent_n"],
"vectorizer": "sbert"
}
Типы классификаторов
Параметр | Описание |
---|---|
scikit | Использование обученного классификатора, который лежит в директории static/references/classifiers_data. Возвращает Порядок выдачи метода predict_proba должен соответствовать порядку
Значения:
|
external | Вызов описанного в classifiers.json классификатора по имени.
Значения:
|
skip | Применяется, когда необходимо по формату указать классификатор, но использовать конкретное значение-результат (список intents). Возвращает список
|
Формат ответа
Любой классификатор должен возвращать список наиболее вероятных вариантов ответов или классов (intents
) из заданного множества, преодолевших определенный порог уверенности (threshold
). Этот список должен быть отсортирован в порядке уменьшения уверенности (score
).
Каждый вариант из списка должен соответствовать общему шаблону, в котором поле answer
равно классу или интенту, score
— величине уверенности в ответе, а other
указывает на принадлежность класса к other
.
[
{
"answer": "intent_name_1",
"score": 0.6,
"other": false
},
{
"answer": "intent_name_2",
"score": 0.3,
"other": false
}
]
Имплементация классификатора
Фреймворк работает с классификаторами, созданными при помощи основных Python-библиотек: scikit-learn, Tensorflow, Keras, Torch.
Любой классификатор на вход получает объект tokenized_elements_list
(один из атрибутов сущности text_preprocessing_result
) — список токенов с информацией о них: оригинальная и начальная формы слова, род, число, зависимые слова, тип связи между словами и т. д. Список токенов — это по сути словарь.
import pickle
pkl_loader = pickle.loads
def predict_proba(tokenized_elements_list):
list_of_tokens = pkl_loader(tokenized_elements_list)
- Трансформируйте список словарей в строку, чтобы получить на выходе аналоги тех строк, на которых обучен классификатор.
- Векторизируйте строку, полученную после предобработки. Если вы пользовались своим векторизатором, используйте его при инференсе точно так же, как при обучении.
Пример функции, которая берет леммы всех токенов, кроме пунктуации:
def tokenized_elements_list_to_str(tokenized_elements_list):
return ' '.join([token['lemma'] for token in tokenized_elements_list if 'dependency_type' in token and token['dependency_type']!='punct'])
Сохранение чекпоинта готового классификатора
Для любого классификатора должен быть реализован метод predict_proba. Если у дефолтного класса классификатора его нет, создайте обертку, в которой он будет эксплицитно прописан.
Этот метод должен принимать на вход tokenized_elements_list
, сериализованный в pkl-объект, полученный с помощью метода pickle.dumps():
class ReminderClassifier:
def __init__(self):
self.pkl_loader = pickle.loads
self.white_list_verbs = set(["оповестить", "оповещать", "пингануть", "ткнуть", "напомнить", "напоминать", "сообщить", "сообщать"])
def predict_proba(self, tokenized_elements_list_pkl):
list_of_tokens = self.pkl_loader(tokenized_elements_list_pkl)
to_return = numpy.array([[1, 0]])
for token in list_of_tokens:
lemma = token.get('lemma', '')
if lemma in self.white_list_verbs:
to_return = numpy.array([[0, 1]])
break
return to_return
Метод должен возвращать numpy array со списком вероятностей принадлежности каждому классу.
Нужно извлечь из дампа этот классификатор с помощью библиотеки dill.
Если модель была построена без кастомных слоев, дамп делается следующим образом:
import dill
with open("pretrained_binary_classifier_model.pkl", "wb") as out:
dill.dump(MyBinaryClassifier(), out)
Если в модели есть кастомные слои Keras, которые нельзя напрямую импортировать из Keras, их нужно извлечь из дампа с помощью dill отдельно:
import dill
from keras.engine.topology import Layer
class CustomLayer(Layer):
pass
dill.settings['recurse'] = True
dill.dump(CustomLayer, open("CustomLayer.pkl", "wb"))
Если кастомных слоев несколько, процедуру нужно повторить для каждого из них. В итоге на выходе должно быть n+1 pkl-файлов, где n – число кастомных слоев.
После это нужно извлечь из дампа сам классификатор — конкретный инстанс. Обращение к кастомным слоям происходит при помощи CustomObjectScope. В файле с расширением h5 должен лежать выход функции keras.save, примененный к основному классификатору.
import pickle
import dill
from keras.models import load_model
from keras.utils import CustomObjectScope
class MyModel:
def __init__(self):
from CustomLayer import CustomLayer
self.pkl_loader = pickle.loads
with CustomObjectScope({'CustomLayer': CustomLayer}):
self.model = load_model('my_model_cpu.h5', compile=False)
def fit(self):
pass
def predict_proba(self, tokenized_elements_list_pkl):
list_tokens = self.pkl_loader(tokenized_elements_list_pkl)
return self.model.predict(list_tokens)
dill.settings['recurse'] = True
dill.dump(MyModel(), open("my_model.pkl", "wb"))
Ваш векторайзер также можно поместить вместе с классификатором в один pkl-файл.
Пример конфигурации классификатора с кастомными слоями
{
"type": "scikit",
"threshold": 0.2,
"is_gpu": false,
"path": "pretrained_classifier_model.pkl",
"intents": ["class_1", "class_2", ... , "class_n"],
"custom_layers": [
{
"layer_name": "CustomLayer",
"path": "CustomLayer.pkl"
}
]
}