16 KiB
Zarządzanie dialogiem z wykorzystaniem reguł
Agent dialogowy wykorzystuje do zarządzanie dialogiem dwa moduły:
monitor stanu dialogu (dialogue state tracker, DST) — moduł odpowiedzialny za śledzenie stanu dialogu.
taktykę prowadzenia dialogu (dialogue policy) — moduł, który na podstawie stanu dialogu podejmuje decyzję o tym jaką akcję (akt systemu) agent ma podjąć w kolejnej turze.
Oba moduły mogą być realizowane zarówno z wykorzystaniem reguł jak i uczenia maszynowego. Mogą one zostać również połączone w pojedynczy moduł zwany wówczas _menedżerem dialogu.
Przykład
Zaimplementujemy regułowe moduły monitora stanu dialogu oraz taktyki dialogowej a następnie osadzimy je w środowisku _ConvLab, które służy do ewaluacji systemów dialogowych.
Uwaga: Niektóre moduły środowiska _ConvLab nie są zgodne z najnowszymi wersjami Pythona, dlatego przed uruchomieniem poniższych przykładów należy się upewnić, że mają Państwo interpreter Pythona w wersji 3.8. Odpowiednią wersję Pythona można zainstalować korzystając m.in. z narzędzia pyenv oraz środowiska conda.
Środowisko _ConvLab można zainstalować korzystając z poniższych poleceń.
!mkdir -p l09
%cd l09
!git clone --depth 1 https://github.com/ConvLab/ConvLab-3
%cd ConvLab-3
!pip install -e .
%cd ../..
Po zainstalowaniu środowiska ConvLab
należy zrestartować interpreter Pythona (opcja _Kernel -> Restart w Jupyter).
Działanie zaimplementowanych modułów zilustrujemy, korzystając ze zbioru danych MultiWOZ (Budzianowski i in., 2018), który zawiera wypowiedzi dotyczące m.in. rezerwacji pokoi hotelowych, zamawiania biletów kolejowych oraz rezerwacji stolików w restauracji.
Monitor Stanu Dialogu
Do reprezentowania stanu dialogu użyjemy struktury danych wykorzystywanej w _ConvLab.
from convlab.util.multiwoz.state import default_state
default_state()
Metoda update
naszego monitora stanu dialogu będzie przyjmować akty użytkownika i odpowiednio
modyfikować stan dialogu.
W przypadku aktów typu inform
wartości slotów zostaną zapamiętane w słownikach odpowiadających
poszczególnym dziedzinom pod kluczem belief_state
.
W przypadku aktów typu request
sloty, o które pyta użytkownik zostaną zapisane pod kluczem
request_state
.
import json
import os
from convlab.dst.dst import DST
from convlab.dst.rule.multiwoz.dst_util import normalize_value
class SimpleRuleDST(DST):
def __init__(self):
DST.__init__(self)
self.state = default_state()
self.value_dict = json.load(open('l09/ConvLab-3/data/multiwoz/value_dict.json'))
def update(self, user_act=None):
for intent, domain, slot, value in user_act:
domain = domain.lower()
intent = intent.lower()
slot = slot.lower()
if domain not in self.state['belief_state']:
continue
if intent == 'inform':
if slot == 'none' or slot == '':
continue
domain_dic = self.state['belief_state'][domain]
if slot in domain_dic:
nvalue = normalize_value(self.value_dict, domain, slot, value)
self.state['belief_state'][domain][slot] = nvalue
elif intent == 'request':
if domain not in self.state['request_state']:
self.state['request_state'][domain] = {}
if slot not in self.state['request_state'][domain]:
self.state['request_state'][domain][slot] = 0
return self.state
def init_session(self):
self.state = default_state()
W definicji metody update
zakładamy, że akty dialogowe przekazywane do monitora stanu dialogu z
modułu NLU są czteroelementowymi listami złożonymi z:
- nazwy aktu użytkownika,
- nazwy dziedziny, której dotyczy wypowiedź,
- nazwy slotu,
- wartości slotu.
Zobaczmy na kilku prostych przykładach jak stan dialogu zmienia się pod wpływem przekazanych aktów użytkownika.
dst = SimpleRuleDST()
dst.state
dst.update([['Inform', 'Hotel', 'Price Range', 'cheap'], ['Inform', 'Hotel', 'Parking', 'yes']])
dst.state['belief_state']['hotel']
dst.update([['Inform', 'Hotel', 'Area', 'north']])
dst.state['belief_state']['hotel']
dst.update([['Request', 'Hotel', 'Area', '?']])
dst.state['request_state']
dst.update([['Inform', 'Hotel', 'Book Day', 'tuesday'], ['Inform', 'Hotel', 'Book People', '2'], ['Inform', 'Hotel', 'Book Stay', '4']])
dst.state['belief_state']['hotel']
dst.state
Taktyka Prowadzenia Dialogu
Prosta taktyka prowadzenia dialogu dla systemu rezerwacji pokoi hotelowych może składać się z następujących reguł:
Jeżeli użytkownik przekazał w ostatniej turze akt typu
Request
, to udziel odpowiedzi na jego pytanie.Jeżeli użytkownik przekazał w ostatniej turze akt typu
Inform
, to zaproponuj mu hotel spełniający zdefiniowane przez niego kryteria.Jeżeli użytkownik przekazał w ostatniej turze akt typu
Inform
zawierający szczegóły rezerwacji, to zarezerwuj pokój.
Metoda predict
taktyki SimpleRulePolicy
realizuje reguły przedstawione powyżej.
from collections import defaultdict
import copy
import json
from copy import deepcopy
from convlab.policy.policy import Policy
from convlab.util.multiwoz.dbquery import Database
class SimpleRulePolicy(Policy):
def __init__(self):
Policy.__init__(self)
self.db = Database()
def predict(self, state):
self.results = []
system_action = defaultdict(list)
user_action = defaultdict(list)
for intent, domain, slot, value in state['user_action']:
user_action[(domain.lower(), intent.lower())].append((slot.lower(), value))
for user_act in user_action:
self.update_system_action(user_act, user_action, state, system_action)
# Reguła 3
if any(True for slots in user_action.values() for (slot, _) in slots if slot in ['book stay', 'book day', 'book people']):
if self.results:
system_action = {('Booking', 'Book'): [["Ref", self.results[0].get('Ref', 'N/A')]]}
system_acts = [[intent, domain, slot, value] for (domain, intent), slots in system_action.items() for slot, value in slots]
state['system_action'] = system_acts
return system_acts
def update_system_action(self, user_act, user_action, state, system_action):
domain, intent = user_act
constraints = [(slot, value) for slot, value in state['belief_state'][domain.lower()].items() if value != '']
self.results = deepcopy(self.db.query(domain.lower(), constraints))
# Reguła 1
if intent == 'request':
if len(self.results) == 0:
system_action[(domain, 'NoOffer')] = []
else:
for slot in user_action[user_act]:
if slot[0] in self.results[0]:
system_action[(domain, 'Inform')].append([slot[0], self.results[0].get(slot[0], 'unknown')])
# Reguła 2
elif intent == 'inform':
if len(self.results) == 0:
system_action[(domain, 'NoOffer')] = []
else:
system_action[(domain, 'Inform')].append(['Choice', str(len(self.results))])
choice = self.results[0]
if domain in ["hotel", "attraction", "police", "restaurant"]:
system_action[(domain, 'Recommend')].append(['Name', choice['name']])
Podobnie jak w przypadku aktów użytkownika akty systemowe przekazywane do modułu NLG są czteroelementowymi listami złożonymi z:
- nazwy aktu systemowe,
- nazwy dziedziny, której dotyczy wypowiedź,
- nazwy slotu,
- wartości slotu.
Sprawdźmy jakie akty systemowe zwraca taktyka SimpleRulePolicy
w odpowiedzi na zmieniający się stan dialogu.
from convlab.dialog_agent import PipelineAgent
dst.init_session()
policy = SimpleRulePolicy()
agent = PipelineAgent(nlu=None, dst=dst, policy=policy, nlg=None, name='sys')
agent.response([['Inform', 'Hotel', 'Price Range', 'cheap'], ['Inform', 'Hotel', 'Parking', 'yes']])
agent.response([['Request', 'Hotel', 'Area', '?']])
agent.response([['Inform', 'Hotel', 'Area', 'centre']])
agent.response([['Inform', 'Hotel', 'Book Day', 'tuesday'], ['Inform', 'Hotel', 'Book People', '2'], ['Inform', 'Hotel', 'Book Stay', '4']])
Testy End-to-End
Na koniec przeprowadźmy dialog łącząc w potok nasze moduły
z modułami NLU i NLG dostępnymi dla MultiWOZ w środowisku ConvLab
.
from convlab.base_models.t5.nlu import T5NLU
from convlab.nlg.template.multiwoz import TemplateNLG
nlu = T5NLU(speaker='user', context_window_size=0, model_name_or_path='ConvLab/t5-small-nlu-multiwoz21')
nlg = TemplateNLG(is_user=False)
agent = PipelineAgent(nlu=nlu, dst=dst, policy=policy, nlg=nlg, name='sys')
agent.response("I need a cheap hotel with free parking .")
agent.response("Yeah , could you book me a room for 2 people for 4 nights starting Tuesday ?")
agent.response("what is the hotel phone number ?")
Zauważmy, ze nasza prosta taktyka dialogowa zawiera wiele luk, do których należą m.in.:
Niezdolność do udzielenia odpowiedzi na przywitanie, prośbę o pomoc lub restart.
Brak reguł dopytujących użytkownika o szczegóły niezbędne do dokonania rezerwacji takie, jak długość pobytu czy liczba osób.
Bardziej zaawansowane moduły zarządzania dialogiem zbudowane z wykorzystaniem reguł można znaleźć w
środowisku ConvLab
. Należą do nich m.in. monitor RuleDST oraz taktyka RuleBasedMultiwozBot.
Zadania
Zaimplementować w projekcie monitor stanu dialogu.
Zaimplementować w projekcie taktykę prowadzenia dialogu.
Literatura
- Pawel Budzianowski, Tsung-Hsien Wen, Bo-Hsiang Tseng, Iñigo Casanueva, Stefan Ultes, Osman Ramadan, Milica Gasic, MultiWOZ - A Large-Scale Multi-Domain Wizard-of-Oz Dataset for Task-Oriented Dialogue Modelling. EMNLP 2018, pp. 5016-5026
- Cathy Pearl, Basic principles for designing voice user interfaces, https://www.oreilly.com/content/basic-principles-for-designing-voice-user-interfaces/ data dostępu: 21 marca 2021
- Cathy Pearl, Designing Voice User Interfaces, Excerpts from Chapter 5: Advanced Voice User Interface Design, https://www.uxmatters.com/mt/archives/2018/01/designing-voice-user-interfaces.php data dostępu: 21 marca 2021