{ "cells": [ { "cell_type": "markdown", "metadata": { "collapsed": false }, "source": [ "![Logo 1](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech1.jpg)\n", "
\n", "

Systemy Dialogowe

\n", "

9. Zarz\u0105dzanie dialogiem z wykorzystaniem regu\u0142 [laboratoria]

\n", "

Marek Kubis (2021)

\n", "
\n", "\n", "![Logo 2](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech2.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zarz\u0105dzanie dialogiem z wykorzystaniem regu\u0142\n", "============================================\n", "\n", "Agent dialogowy wykorzystuje do zarz\u0105dzanie dialogiem dwa modu\u0142y:\n", "\n", " - monitor stanu dialogu (dialogue state tracker, DST) \u2014 modu\u0142 odpowiedzialny za \u015bledzenie stanu dialogu.\n", "\n", " - taktyk\u0119 prowadzenia dialogu (dialogue policy) \u2014 modu\u0142, kt\u00f3ry na podstawie stanu dialogu\n", " podejmuje decyzj\u0119 o tym jak\u0105 akcj\u0119 (akt systemu) agent ma podj\u0105\u0107 w kolejnej turze.\n", "\n", "Oba modu\u0142y mog\u0105 by\u0107 realizowane zar\u00f3wno z wykorzystaniem regu\u0142 jak i uczenia maszynowego.\n", "Mog\u0105 one zosta\u0107 r\u00f3wnie\u017c po\u0142\u0105czone w pojedynczy modu\u0142 zwany w\u00f3wczas *mened\u017cerem dialogu*.\n", "\n", "Przyk\u0142ad\n", "--------\n", "\n", "Zaimplementujemy regu\u0142owe modu\u0142y monitora stanu dialogu oraz taktyki dialogowej a nast\u0119pnie\n", "osadzimy je w \u015brodowisku *[ConvLab-2](https://github.com/thu-coai/ConvLab-2)*,\n", "kt\u00f3re s\u0142u\u017cy do ewaluacji system\u00f3w dialogowych.\n", "\n", "**Uwaga:** Niekt\u00f3re modu\u0142y \u015brodowiska *ConvLab-2* nie s\u0105 zgodne z najnowszymi wersjami Pythona,\n", "dlatego przed uruchomieniem poni\u017cszych przyk\u0142ad\u00f3w nale\u017cy si\u0119 upewni\u0107, \u017ce maj\u0105 Pa\u0144stwo interpreter\n", "Pythona w wersji 3.7. W przypadku nowszych wersji Ubuntu Pythona 3.7 mo\u017cna zainstalowa\u0107 z\n", "repozytorium `deadsnakes`, wykonuj\u0105c polecenia przedstawione poni\u017cej.\n", "\n", "```\n", "sudo add-apt-repository ppa:deadsnakes/ppa\n", "sudo apt update\n", "sudo apt install python3.7 python3.7-dev python3.7-venv\n", "```\n", "\n", "W przypadku innych system\u00f3w mo\u017cna skorzysta\u0107 np. z narz\u0119dzia [pyenv](https://github.com/pyenv/pyenv) lub \u015brodowiska [conda](https://conda.io).\n", "\n", "Ze wzgl\u0119du na to, \u017ce *ConvLab-2* ma wiele zale\u017cno\u015bci zach\u0119cam r\u00f3wnie\u017c do skorzystania ze \u015brodowiska\n", "wirtualnego `venv`, w kt\u00f3rym modu\u0142y zale\u017cne mog\u0105 zosta\u0107 zainstalowane.\n", "W tym celu nale\u017cy wykona\u0107 nast\u0119puj\u0105ce polecenia\n", "\n", "```\n", "python3.7 -m venv convenv # utworzenie nowego \u015brodowiska o nazwie convenv\n", "source convenv/bin/activate # aktywacja \u015brodowiska w bie\u017c\u0105cej pow\u0142oce\n", "pip install --ignore-installed jupyter # instalacja jupytera w \u015brodowisku convenv\n", "```\n", "\n", "Po skonfigurowaniu \u015brodowiska mo\u017cna przyst\u0105pi\u0107 do instalacji *ConvLab-2*, korzystaj\u0105c z\n", "nast\u0119puj\u0105cych polece\u0144\n", "\n", "```\n", "mkdir -p l08\n", "cd l08\n", "git clone https://github.com/thu-coai/ConvLab-2.git\n", "cd ConvLab-2\n", "pip install -e .\n", "python -m spacy download en_core_web_sm\n", "cd ../..\n", "```\n", "\n", "Po zako\u0144czeniu instalacji nale\u017cy ponownie uruchomi\u0107 notatnik w pow\u0142oce, w kt\u00f3rej aktywne jest\n", "\u015brodowisko wirtualne *convenv*.\n", "\n", "```\n", "jupyter notebook 08-zarzadzanie-dialogiem-reguly.ipynb\n", "```\n", "\n", "Dzia\u0142anie zaimplementowanych modu\u0142\u00f3w zilustrujemy, korzystaj\u0105c ze zbioru danych\n", "[MultiWOZ](https://github.com/budzianowski/multiwoz) (Budzianowski i in., 2018), kt\u00f3ry zawiera\n", "wypowiedzi dotycz\u0105ce m.in. rezerwacji pokoi hotelowych, zamawiania bilet\u00f3w kolejowych oraz\n", "rezerwacji stolik\u00f3w w restauracji.\n", "\n", "### Monitor Stanu Dialogu\n", "\n", "Do reprezentowania stanu dialogu u\u017cyjemy struktury danych wykorzystywanej w *ConvLab-2*." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from convlab2.util.multiwoz.state import default_state\n", "default_state()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Metoda `update` naszego monitora stanu dialogu b\u0119dzie przyjmowa\u0107 akty u\u017cytkownika i odpowiednio\n", "modyfikowa\u0107 stan dialogu.\n", "W przypadku akt\u00f3w typu `inform` warto\u015bci slot\u00f3w zostan\u0105 zapami\u0119tane w s\u0142ownikach odpowiadaj\u0105cych\n", "poszczeg\u00f3lnym dziedzinom pod kluczem `belief_state`.\n", "W przypadku akt\u00f3w typu `request` sloty, o kt\u00f3re pyta u\u017cytkownik zostan\u0105 zapisane pod kluczem\n", "`request_state`.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import json\n", "import os\n", "from convlab2.dst.dst import DST\n", "from convlab2.dst.rule.multiwoz.dst_util import normalize_value\n", "from convlab2.util.multiwoz.multiwoz_slot_trans import REF_SYS_DA\n", "\n", "\n", "class SimpleRuleDST(DST):\n", " def __init__(self):\n", " DST.__init__(self)\n", " self.state = default_state()\n", " self.value_dict = json.load(open('l08/ConvLab-2/data/multiwoz/value_dict.json'))\n", "\n", " def update(self, user_act=None):\n", " for intent, domain, slot, value in user_act:\n", " domain = domain.lower()\n", " intent = intent.lower()\n", "\n", " if domain in ['unk', 'general', 'booking']:\n", " continue\n", "\n", " if intent == 'inform':\n", " k = REF_SYS_DA[domain.capitalize()].get(slot, slot)\n", "\n", " if k is None:\n", " continue\n", "\n", " domain_dic = self.state['belief_state'][domain]\n", "\n", " if k in domain_dic['semi']:\n", " nvalue = normalize_value(self.value_dict, domain, k, value)\n", " self.state['belief_state'][domain]['semi'][k] = nvalue\n", " elif k in domain_dic['book']:\n", " self.state['belief_state'][domain]['book'][k] = value\n", " elif k.lower() in domain_dic['book']:\n", " self.state['belief_state'][domain]['book'][k.lower()] = value\n", " elif intent == 'request':\n", " k = REF_SYS_DA[domain.capitalize()].get(slot, slot)\n", "\n", " if domain not in self.state['request_state']:\n", " self.state['request_state'][domain] = {}\n", " if k not in self.state['request_state'][domain]:\n", " self.state['request_state'][domain][k] = 0\n", "\n", " return self.state\n", "\n", " def init_session(self):\n", " self.state = default_state()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "W definicji metody `update` zak\u0142adamy, \u017ce akty dialogowe przekazywane do monitora stanu dialogu z\n", "modu\u0142u NLU s\u0105 czteroelementowymi listami z\u0142o\u017conymi z:\n", "\n", " - nazwy aktu u\u017cytkownika,\n", " - nazwy dziedziny, kt\u00f3rej dotyczy wypowied\u017a,\n", " - nazwy slotu,\n", " - warto\u015bci slotu.\n", "\n", "Zobaczmy na kilku prostych przyk\u0142adach jak stan dialogu zmienia si\u0119 pod wp\u0142ywem przekazanych akt\u00f3w\n", "u\u017cytkownika." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "lines_to_next_cell": 0 }, "outputs": [], "source": [ "dst = SimpleRuleDST()\n", "dst.state" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "lines_to_next_cell": 0 }, "outputs": [], "source": [ "dst.update([['Inform', 'Hotel', 'Price', 'cheap'], ['Inform', 'Hotel', 'Parking', 'yes']])\n", "dst.state['belief_state']['hotel']" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "lines_to_next_cell": 0 }, "outputs": [], "source": [ "dst.update([['Inform', 'Hotel', 'Area', 'north']])\n", "dst.state['belief_state']['hotel']" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "lines_to_next_cell": 0 }, "outputs": [], "source": [ "dst.update([['Request', 'Hotel', 'Area', '?']])\n", "dst.state['request_state']" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "lines_to_next_cell": 0 }, "outputs": [], "source": [ "dst.update([['Inform', 'Hotel', 'Day', 'tuesday'], ['Inform', 'Hotel', 'People', '2'], ['Inform', 'Hotel', 'Stay', '4']])\n", "dst.state['belief_state']['hotel']" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dst.state" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Taktyka Prowadzenia Dialogu\n", "\n", "Prosta taktyka prowadzenia dialogu dla systemu rezerwacji pokoi hotelowych mo\u017ce sk\u0142ada\u0107 si\u0119 z nast\u0119puj\u0105cych regu\u0142:\n", "\n", " 1. Je\u017celi u\u017cytkownik przekaza\u0142 w ostatniej turze akt typu `Request`, to udziel odpowiedzi na jego\n", " pytanie.\n", "\n", " 2. Je\u017celi u\u017cytkownik przekaza\u0142 w ostatniej turze akt typu `Inform`, to zaproponuj mu hotel\n", " spe\u0142niaj\u0105cy zdefiniowane przez niego kryteria.\n", "\n", " 3. Je\u017celi u\u017cytkownik przekaza\u0142 w ostatniej turze akt typu `Inform` zawieraj\u0105cy szczeg\u00f3\u0142y\n", " rezerwacji, to zarezerwuj pok\u00f3j.\n", "\n", "Metoda `predict` taktyki `SimpleRulePolicy` realizuje regu\u0142y przedstawione powy\u017cej." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from collections import defaultdict\n", "import copy\n", "import json\n", "from copy import deepcopy\n", "\n", "from convlab2.policy.policy import Policy\n", "from convlab2.util.multiwoz.dbquery import Database\n", "from convlab2.util.multiwoz.multiwoz_slot_trans import REF_SYS_DA, REF_USR_DA\n", "\n", "\n", "class SimpleRulePolicy(Policy):\n", " def __init__(self):\n", " Policy.__init__(self)\n", " self.db = Database()\n", "\n", " def predict(self, state):\n", " self.results = []\n", " system_action = defaultdict(list)\n", " user_action = defaultdict(list)\n", "\n", " for intent, domain, slot, value in state['user_action']:\n", " user_action[(domain, intent)].append((slot, value))\n", "\n", " for user_act in user_action:\n", " self.update_system_action(user_act, user_action, state, system_action)\n", "\n", " # Regu\u0142a 3\n", " if any(True for slots in user_action.values() for (slot, _) in slots if slot in ['Stay', 'Day', 'People']):\n", " if self.results:\n", " system_action = {('Booking', 'Book'): [[\"Ref\", self.results[0].get('Ref', 'N/A')]]}\n", "\n", " system_acts = [[intent, domain, slot, value] for (domain, intent), slots in system_action.items() for slot, value in slots]\n", " state['system_action'] = system_acts\n", " return system_acts\n", "\n", " def update_system_action(self, user_act, user_action, state, system_action):\n", " domain, intent = user_act\n", " constraints = [(slot, value) for slot, value in state['belief_state'][domain.lower()]['semi'].items() if value != '']\n", " self.results = deepcopy(self.db.query(domain.lower(), constraints))\n", "\n", " # Regu\u0142a 1\n", " if intent == 'Request':\n", " if len(self.results) == 0:\n", " system_action[(domain, 'NoOffer')] = []\n", " else:\n", " for slot in user_action[user_act]:\n", " kb_slot_name = REF_SYS_DA[domain].get(slot[0], slot[0])\n", "\n", " if kb_slot_name in self.results[0]:\n", " system_action[(domain, 'Inform')].append([slot[0], self.results[0].get(kb_slot_name, 'unknown')])\n", "\n", " # Regu\u0142a 2\n", " elif intent == 'Inform':\n", " if len(self.results) == 0:\n", " system_action[(domain, 'NoOffer')] = []\n", " else:\n", " system_action[(domain, 'Inform')].append(['Choice', str(len(self.results))])\n", " choice = self.results[0]\n", "\n", " if domain in [\"Hotel\", \"Attraction\", \"Police\", \"Restaurant\"]:\n", " system_action[(domain, 'Recommend')].append(['Name', choice['name']])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podobnie jak w przypadku akt\u00f3w u\u017cytkownika akty systemowe przekazywane do modu\u0142u NLG s\u0105 czteroelementowymi listami z\u0142o\u017conymi z:\n", "\n", " - nazwy aktu systemowe,\n", " - nazwy dziedziny, kt\u00f3rej dotyczy wypowied\u017a,\n", " - nazwy slotu,\n", " - warto\u015bci slotu.\n", "\n", "Sprawd\u017amy jakie akty systemowe zwraca taktyka `SimpleRulePolicy` w odpowiedzi na zmieniaj\u0105cy si\u0119 stan dialogu." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "lines_to_next_cell": 0 }, "outputs": [], "source": [ "from convlab2.dialog_agent import PipelineAgent\n", "dst.init_session()\n", "policy = SimpleRulePolicy()\n", "agent = PipelineAgent(nlu=None, dst=dst, policy=policy, nlg=None, name='sys')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "lines_to_next_cell": 0 }, "outputs": [], "source": [ "agent.response([['Inform', 'Hotel', 'Price', 'cheap'], ['Inform', 'Hotel', 'Parking', 'yes']])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "lines_to_next_cell": 0 }, "outputs": [], "source": [ "agent.response([['Inform', 'Hotel', 'Area', 'north']])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "lines_to_next_cell": 0 }, "outputs": [], "source": [ "agent.response([['Request', 'Hotel', 'Area', '?']])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "agent.response([['Inform', 'Hotel', 'Day', 'tuesday'], ['Inform', 'Hotel', 'People', '2'], ['Inform', 'Hotel', 'Stay', '4']])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Testy End-to-End\n", "\n", "Na koniec przeprowad\u017amy dialog \u0142\u0105cz\u0105c w potok nasze modu\u0142y\n", "z modu\u0142ami NLU i NLG dost\u0119pnymi dla MultiWOZ w \u015brodowisku `ConvLab-2`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from convlab2.nlu.svm.multiwoz import SVMNLU\n", "from convlab2.nlg.template.multiwoz import TemplateNLG\n", "\n", "nlu = SVMNLU()\n", "nlg = TemplateNLG(is_user=False)\n", "agent = PipelineAgent(nlu=nlu, dst=dst, policy=policy, nlg=nlg, name='sys')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "lines_to_next_cell": 0 }, "outputs": [], "source": [ "agent.response(\"I need a cheap hotel with free parking .\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "lines_to_next_cell": 0 }, "outputs": [], "source": [ "agent.response(\"Where it is located ?\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "lines_to_next_cell": 0 }, "outputs": [], "source": [ "agent.response(\"I would prefer the hotel be in the north part of town .\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "agent.response(\"Yeah , could you book me a room for 2 people for 4 nights starting Tuesday ?\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zauwa\u017cmy, ze nasza prosta taktyka dialogowa zawiera wiele luk, do kt\u00f3rych nale\u017c\u0105 m.in.:\n", "\n", " 1. Niezdolno\u015b\u0107 do udzielenia odpowiedzi na przywitanie, pro\u015bb\u0119 o pomoc lub restart.\n", "\n", " 2. Brak regu\u0142 dopytuj\u0105cych u\u017cytkownika o szczeg\u00f3\u0142y niezb\u0119dne do dokonania rezerwacji takie, jak d\u0142ugo\u015b\u0107 pobytu czy liczba os\u00f3b.\n", "\n", "Bardziej zaawansowane modu\u0142y zarz\u0105dzania dialogiem zbudowane z wykorzystaniem regu\u0142 mo\u017cna znale\u017a\u0107 w\n", "\u015brodowisku `ConvLab-2`. Nale\u017c\u0105 do nich m.in. monitor [RuleDST](https://github.com/thu-coai/ConvLab-2/blob/master/convlab2/dst/rule/multiwoz/dst.py) oraz taktyka [RuleBasedMultiwozBot](https://github.com/thu-coai/ConvLab-2/blob/master/convlab2/policy/rule/multiwoz/rule_based_multiwoz_bot.py).\n", "\n", "Zadania\n", "-------\n", " 1. Zaimplementowa\u0107 w projekcie monitor stanu dialogu.\n", "\n", " 2. Zaimplementowa\u0107 w projekcie taktyk\u0119 prowadzenia dialogu.\n", "\n", "Termin: 24.05.2021, godz. 23:59.\n", "\n", "Literatura\n", "----------\n", " 1. Pawel Budzianowski, Tsung-Hsien Wen, Bo-Hsiang Tseng, I\u00f1igo 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\n", " 2. Cathy Pearl, Basic principles for designing voice user interfaces, https://www.oreilly.com/content/basic-principles-for-designing-voice-user-interfaces/ data dost\u0119pu: 21 marca 2021\n", " 3. 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\u0119pu: 21 marca 2021" ] } ], "metadata": { "jupytext": { "cell_metadata_filter": "-all", "main_language": "python", "notebook_metadata_filter": "-all" }, "author": "Marek Kubis", "email": "mkubis@amu.edu.pl", "lang": "pl", "subtitle": "9.Zarz\u0105dzanie dialogiem z wykorzystaniem regu\u0142[laboratoria]", "title": "Systemy Dialogowe", "year": "2021" }, "nbformat": 4, "nbformat_minor": 4 }