В последнее время я провожу исследование, связанное с диалоговой политикой, и для ее реализации использую колесо расы.Кстати, я прочитал исходный код и написал статью. Уровень ограничен, поправьте меня.
удар первым
Качество кода ядра rasa очень высокое, очень высокое, очень высокое! Я знаю, что в разработке участвует много китайских инженеров, круто!
вся идея
Сначала мы анализируем исходный код трекера с точки зрения исполнения.
- что это? Как инициализировать
- какой ввод
- что отслеживать
- Как обновить статус
- Как выражается состояние
Как инициализировать - init
Ниже приведен весь код инициализации. Он очень простой. Я сделал несколько комментариев для облегчения понимания. На самом деле комментариев много в исходном коде. Исходные комментарии вы можете прочитать непосредственно в исходном коде. Еще одна вещь, исходный код rasa так красиво написан, с подробными комментариями и стандартизированным форматом, что читать его одно удовольствие.
def __init__(self, sender_id, slots,
max_event_history=None):
"""Initialize the tracker.
A set of events can be stored externally, and we will run through all
of them to get the current state. The tracker will represent all the
information we captured while processing messages of the dialogue."""
# 可以跟踪的最长历史,tracker记录状态是以event为单位的
self._max_event_history = max_event_history
# 历史事件列表
self.events = self._create_events([])
# 这个id和rasa的chenel特性有关系
self.sender_id = sender_id
# slot列表
self.slots = {slot.name: copy.deepcopy(slot) for slot in slots}
###
# current state of the tracker - MUST be re-creatable by processing
# all the events. This only defines the attributes, values are set in
# `reset()`
###
# 暂停标志
self._paused = None
# 一些action记录
self.followup_action = ACTION_LISTEN_NAME
self.latest_action_name = None
self.latest_message = None
# bot的上一个返回内容
self.latest_bot_utterance = None
self._reset()
Что мы можем узнать из функции init?
- трекер — объект, фиксирующий состояние разговора пользователя
- трекер отслеживает состояние диалога на основе объекта Event
Event
Поскольку трекер основан на Событии, давайте посмотрим, что такое Событие.
Проще говоря, Event — это абстракция всего поведения бота, и каждый конкретный класс событий наследуется от базового класса Event.
class Event(object):
"""Events describe everything that occurs in
a conversation and tell the :class:`DialogueStateTracker`
how to update its state."""
type_name = "event"
def __init__(self, timestamp=None):
self.timestamp = timestamp if timestamp else time.time()
Этот дизайн очень хорош, так что трекер может отслеживать события, отличные от предопределенных системой, если вы сами реализуете подкласс Event. Следует сказать, что это базовое проектировочное мышление объектно-ориентированного подхода, но на самом деле трудно тщательно продумать код.
rasa-core внутренне реализует следующие события
Вы можете увидеть, что означает имя
Давайте взглянем на методы, лежащие в основе Event.apply_to()
class UserUttered(Event):
def apply_to(self, tracker):
# type: (DialogueStateTracker) -> None
tracker.latest_message = self
tracker.clear_followup_action()
Просто посмотрите на одного, что он делает? Это изменение свойств трекера и обновление некоторого контента, связанного с ним самим.
Почему этот метод? Поскольку атрибуты, которые необходимо изменить для каждого события, разные, поместите эту часть логики в подкласс, чтобы реализовать ее самостоятельно, а логика вызова реализована в трекере, чтобы максимизировать повторное использование кода. Это также должно относиться к базовому мышлению, так что вы сделали это сами?
Обновление статуса - обновление
def update(self, event):
# type: (Event) -> None
"""Modify the state of the tracker according to an ``Event``. """
if not isinstance(event, Event): # pragma: no cover
raise ValueError("event to log must be an instance "
"of a subclass of Event.")
self.events.append(event)
event.apply_to(self)
это так просто
вывод
Контент, упомянутый выше, является основной частью трекера, а абстракция очень красивая. В качестве отступления рекомендую всем ознакомиться с исходным кодом Flask, я прочитал его часть и сказал, что он радует глаз, на строгий и элегантный дизайн архитектуры действительно приятно смотреть.
Трекер записывает весь процесс общения и предоставляет интерфейс для генерации Истории и интерфейс для генерации Диалога.
def export_stories(self):
# type: () -> Text
"""Dump the tracker as a story in the Rasa Core story format.
Returns the dumped tracker as a string."""
from dqn_policy.training.structures import Story
story = Story.from_events(self.applied_events())
return story.as_story_string(flat=True)
def as_dialogue(self):
# type: () -> Dialogue
"""Return a ``Dialogue`` object containing all of the turns.
This can be serialised and later used to recover the state
of this tracker exactly."""
return Dialogue(self.sender_id, list(self.events))
Другие интерфейсы
трекер также реализует множество интерфейсов, задействующих различные части rasa, поэтому я не буду вдаваться в подробности по отдельности. Многие из них являются вспомогательными интерфейсами для featurize, эту часть я не изучал досконально, позже напишу еще одну статью о featurize, это основной компонент rasa core.
подвести итог
Трекер является связующим звеном в ядре rasa, записывает данные, вводимые из внешнего интерфейса, и обеспечивает основу для обучения модели. Начиная с трекера, мы можем в основном выяснить структуру фрейма всего ядра расы. Абстракция ядра rasa очень хороша, качество кода очень высокое, и это должно быть взорвано. Эта часть исходного кода относительно проста, комментарии очень подробные, читать ее очень удобно, рекомендуется к прочтению всем.