Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
d4100eb063 | |||
![]() |
777e6fa497 | ||
![]() |
de703d016c | ||
![]() |
22b61e0dd7 | ||
![]() |
d63f926b49 | ||
![]() |
5bf25d0b2b | ||
![]() |
cdd9ce17e9 | ||
![]() |
18858dddc8 | ||
![]() |
283f26a164 | ||
![]() |
be761cfb54 | ||
6cd5dcad5c | |||
e9e8eae277 | |||
60dbfcd478 | |||
258d8b3be0 | |||
b0e4a5bb2e | |||
1cce41a8c6 | |||
0595bba4d8 | |||
2f0d3350e9 | |||
e3cfaf2573 | |||
00c4ee5c5f | |||
240ba12397 | |||
b6411ae7d0 | |||
c11e6ea284 | |||
1b2f1f6dc3 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.idea/
|
||||||
|
__pycache__/
|
18
SystemActType.py
Normal file
18
SystemActType.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
SystemActType = dict(
|
||||||
|
affirm='affirm',
|
||||||
|
bye='bye',
|
||||||
|
canthear='canthear',
|
||||||
|
confirm_domain='confirm-domain',
|
||||||
|
negate='negate',
|
||||||
|
repeat='repeat',
|
||||||
|
reqmore='reqmore',
|
||||||
|
welcomemsg='welcomemsg',
|
||||||
|
canthelp='canthelp',
|
||||||
|
canthelp_missing_slot_value='canthelp.missing_slot_value',
|
||||||
|
expl_conf='expl-conf',
|
||||||
|
impl_conf='inform-conf',
|
||||||
|
inform='infomr',
|
||||||
|
offer='offer',
|
||||||
|
request='request',
|
||||||
|
select='select'
|
||||||
|
)
|
23
UserActType.py
Normal file
23
UserActType.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
UserActType = dict(
|
||||||
|
ack='ack',
|
||||||
|
affirm='affirm',
|
||||||
|
bye='bye',
|
||||||
|
hello='hello',
|
||||||
|
help='help',
|
||||||
|
negate='negate',
|
||||||
|
null='null',
|
||||||
|
repeat='repeat',
|
||||||
|
requalts='requalts',
|
||||||
|
reqmore='reqmore',
|
||||||
|
restart='restart',
|
||||||
|
silence='silence',
|
||||||
|
thankyou='thankyou',
|
||||||
|
confirm='confirm',
|
||||||
|
deny='deny',
|
||||||
|
inform='inform',
|
||||||
|
request='request',
|
||||||
|
order='order',
|
||||||
|
delivery='delivery',
|
||||||
|
payment='payment',
|
||||||
|
price='price'
|
||||||
|
)
|
27
dp.py
Normal file
27
dp.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from SystemActType import SystemActType
|
||||||
|
from UserActType import UserActType
|
||||||
|
|
||||||
|
|
||||||
|
class DP:
|
||||||
|
def update_system_action(self, state, last_user_act, last_system_act, slots):
|
||||||
|
if state == UserActType['order']:
|
||||||
|
if 'kind' not in slots[state]:
|
||||||
|
return {'act': SystemActType['request'], 'slot': 'kind'}
|
||||||
|
elif 'size' not in slots[state]:
|
||||||
|
return {'act': SystemActType['request'], 'slot': 'size'}
|
||||||
|
elif 'plates' not in slots[state]:
|
||||||
|
return {'act': SystemActType['request'], 'slot': 'plates'}
|
||||||
|
else:
|
||||||
|
return {'act': SystemActType['confirm_domain']}
|
||||||
|
|
||||||
|
else:
|
||||||
|
if last_user_act == UserActType['hello']:
|
||||||
|
return {'act': SystemActType['welcomemsg']}
|
||||||
|
elif last_user_act == UserActType['bye']:
|
||||||
|
return {'act': SystemActType['bye']}
|
||||||
|
else:
|
||||||
|
return {'act': SystemActType['canthelp']}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
51
dst.py
Normal file
51
dst.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
from UserActType import UserActType
|
||||||
|
|
||||||
|
class DST:
|
||||||
|
def __init__(self):
|
||||||
|
self.state = None
|
||||||
|
self.last_user_act = None
|
||||||
|
self.last_system_act = None
|
||||||
|
self.slots = self.init_slots()
|
||||||
|
|
||||||
|
def update(self, user_act=None):
|
||||||
|
act = user_act['act']
|
||||||
|
self.last_user_act = act
|
||||||
|
if not self.state:
|
||||||
|
if act in [UserActType['order'],
|
||||||
|
UserActType['delivery'],
|
||||||
|
UserActType['payment'],
|
||||||
|
UserActType['price']]:
|
||||||
|
self.state = act
|
||||||
|
|
||||||
|
for slot, value in user_act['slots']:
|
||||||
|
slot = slot.lower()
|
||||||
|
value = value.lower()
|
||||||
|
|
||||||
|
self.slots[act][slot] = value
|
||||||
|
|
||||||
|
return self.state
|
||||||
|
|
||||||
|
def get_dialogue_state_tracker_state(self):
|
||||||
|
return self.state, self.last_user_act, self.last_system_act, self.slots
|
||||||
|
|
||||||
|
def get_state(self):
|
||||||
|
return self.state
|
||||||
|
|
||||||
|
def get_last_user_act(self):
|
||||||
|
return self.last_user_act
|
||||||
|
|
||||||
|
def get_last_system_act(self):
|
||||||
|
return self.last_system_act
|
||||||
|
|
||||||
|
def get_slots(self):
|
||||||
|
return self.slots
|
||||||
|
|
||||||
|
def update_last_user_act(self, new_user_act):
|
||||||
|
self.last_user_act = new_user_act
|
||||||
|
|
||||||
|
def update_last_system_act(self, new_system_act):
|
||||||
|
self.last_system_act = new_system_act
|
||||||
|
|
||||||
|
def init_slots(self):
|
||||||
|
return dict(order={}, delivery={}, payment={}, price={})
|
||||||
|
|
66
evaluate.py
Normal file
66
evaluate.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import jsgf
|
||||||
|
from os import listdir
|
||||||
|
from os.path import isfile, join
|
||||||
|
|
||||||
|
|
||||||
|
gram_dir = './gramatyki/'
|
||||||
|
grammar_files = [file for file in listdir(gram_dir) if isfile(join(gram_dir, file))]
|
||||||
|
|
||||||
|
grammars = []
|
||||||
|
|
||||||
|
for grammarFile in grammar_files:
|
||||||
|
grammar = jsgf.parse_grammar_file(gram_dir + grammarFile)
|
||||||
|
grammars.append(grammar)
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
data_dir = './data/'
|
||||||
|
data_files = [file for file in listdir(data_dir) if isfile(join(data_dir, file))]
|
||||||
|
|
||||||
|
detected = 0
|
||||||
|
|
||||||
|
for file in data_files:
|
||||||
|
f = open(f'{data_dir}{file}', "r")
|
||||||
|
for line in f:
|
||||||
|
sep = line.split('\t')
|
||||||
|
if sep[0] == 'user':
|
||||||
|
lines.append([sep[1], sep[2]])
|
||||||
|
|
||||||
|
|
||||||
|
def get_dialog_act(rule):
|
||||||
|
slots = []
|
||||||
|
get_slots(rule.expansion, slots)
|
||||||
|
global detected
|
||||||
|
detected = detected + 1
|
||||||
|
return {'act': rule.grammar.name, 'slots': slots}
|
||||||
|
|
||||||
|
|
||||||
|
def get_slots(expansion, slots):
|
||||||
|
if expansion.tag != '':
|
||||||
|
slots.append((expansion.tag, expansion.current_match))
|
||||||
|
return
|
||||||
|
|
||||||
|
for child in expansion.children:
|
||||||
|
get_slots(child, slots)
|
||||||
|
|
||||||
|
if not expansion.children and isinstance(expansion, jsgf.NamedRuleRef):
|
||||||
|
get_slots(expansion.referenced_rule.expansion, slots)
|
||||||
|
|
||||||
|
|
||||||
|
def nlu(utterance):
|
||||||
|
matched = None
|
||||||
|
for _grammar in grammars:
|
||||||
|
matched = _grammar.find_matching_rules(utterance)
|
||||||
|
if matched:
|
||||||
|
break
|
||||||
|
|
||||||
|
if matched:
|
||||||
|
return get_dialog_act(matched[0])
|
||||||
|
else:
|
||||||
|
return {'act': 'null', 'slots': []}
|
||||||
|
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
nlu(line[0])
|
||||||
|
|
||||||
|
print(f'{round((detected/len(lines)*100),2)}%')
|
8
evaluation.md
Normal file
8
evaluation.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
## WNIOSKI
|
||||||
|
|
||||||
|
- System był zrozumiały dla większości użytkowników
|
||||||
|
- Tempo reakcji było bardziej niż zadowalające według większości badanych
|
||||||
|
- Trzeba rozszerzyć system gramatyk, gdyż nie rozpoznaje on dostatecznie często wypowiedzi użytkowników
|
||||||
|
- Wśród badanych panuje duża niezgoda, czy system zachowuje się tak, jak oczekiwano, warto przeredagować odpowiedzi systemu by były bardziej zrozumiałe dla użytkownika
|
||||||
|
- Część użytkowników nie wiedziała co powiedzieć na każdym etapie dialogu, ponownie należy rozwinąć system gramatyk oraz przeredagować wypowiedzi systemu by bardziej nakierowywały użytkowników.
|
||||||
|
- Wśród badanych zauważalny był odzew negatywny, należy wprowadzić wspomniane poprawki do systemu i ponownie przeprowadzić ewaluację by zobaczyć nowy stan odczuć użytkowników w stosunku do chęci korzystania z systemu.
|
8
gramatyki/delivery.jsgf
Normal file
8
gramatyki/delivery.jsgf
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#JSGF V1.0 UTF-8 pl;
|
||||||
|
|
||||||
|
grammar delivery;
|
||||||
|
|
||||||
|
|
||||||
|
<deliverys> = <droga> ;
|
||||||
|
|
||||||
|
public <droga> = (na | <NULL>) (dowoz | miejscu | wynos);
|
8
gramatyki/hello.jsgf
Normal file
8
gramatyki/hello.jsgf
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#JSGF V1.0 UTF-8 pl;
|
||||||
|
|
||||||
|
grammar hello;
|
||||||
|
|
||||||
|
|
||||||
|
<hello> = <greeting> ;
|
||||||
|
|
||||||
|
public <greeting> = (dzień dobry | cześć | siema | witam);
|
13
gramatyki/order.jsgf
Normal file
13
gramatyki/order.jsgf
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#JSGF V1.0 UTF-8 pl;
|
||||||
|
|
||||||
|
grammar order;
|
||||||
|
|
||||||
|
public <zamow> = chciałbym zamowic <rodzaj> <rozmiar> <liczba_osob> ;
|
||||||
|
|
||||||
|
<rodzaj> = (pizze | <NULL>) (vesuvio | hawajska | amerykanska | grecka) {kind};
|
||||||
|
|
||||||
|
<rozmiar> = (mala | srednia | duza | XXL) {size};
|
||||||
|
|
||||||
|
<liczba_osob> = (na | dla) <liczba> {plates} (osób | osoby);
|
||||||
|
|
||||||
|
<liczba> = (dwie | dwóch | trzy | trzech | cztery | czterech | pięć | pieciu);
|
8
gramatyki/payment.jsgf
Normal file
8
gramatyki/payment.jsgf
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#JSGF V1.0 UTF-8 pl;
|
||||||
|
|
||||||
|
grammar payment;
|
||||||
|
|
||||||
|
|
||||||
|
<payment> = <platnosc> ;
|
||||||
|
|
||||||
|
public <platnosc> = (płacę | zapłacę | płatność | <NULL>) (kartą | gotówką | online | przy odbiorze);
|
9
gramatyki/price.jsgf
Normal file
9
gramatyki/price.jsgf
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#JSGF V1.0 UTF-8 pl;
|
||||||
|
|
||||||
|
grammar price;
|
||||||
|
|
||||||
|
|
||||||
|
<price> = <cena> ;
|
||||||
|
|
||||||
|
public <cena> = (ile | jaki jest | jaka jest | w jakiej jest | <NULL>) (całkowity | <NULL>)
|
||||||
|
(koszt | kosztuje | cena | cenie) (pizza | pizze | pizzy | zamówienia | <NULL>);
|
38
main.py
Normal file
38
main.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
from UserActType import UserActType
|
||||||
|
from nlu import nlu
|
||||||
|
from dst import DST
|
||||||
|
from dp import DP
|
||||||
|
from nlg import NLG
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
dst = DST()
|
||||||
|
dp = DP()
|
||||||
|
nlg = NLG()
|
||||||
|
|
||||||
|
print("Witamy w restauracji πzza. W czym mogę pomóc?")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
user_input = input("$")
|
||||||
|
# get user act frame from user input with Natural Language Understanding
|
||||||
|
user_act_frame = nlu(user_input)
|
||||||
|
# print('NLU', user_act_frame)
|
||||||
|
# update Dialogue State Tracker with new user act frame
|
||||||
|
dst.update(user_act_frame)
|
||||||
|
state, last_user_act, last_system_act, slots = dst.get_dialogue_state_tracker_state()
|
||||||
|
# print('state', state)
|
||||||
|
# print('last_user_act', last_user_act)
|
||||||
|
# print('last_system_act', last_system_act)
|
||||||
|
# print('slots', slots)
|
||||||
|
|
||||||
|
# get system act frame which decides what's next from Dialogue Policy
|
||||||
|
system_act_frame = dp.update_system_action(state, last_user_act, last_system_act, slots)
|
||||||
|
dst.update_last_system_act(system_act_frame)
|
||||||
|
# print('system_act_frame', system_act_frame)
|
||||||
|
|
||||||
|
# generate response based on system act frame
|
||||||
|
system_response = nlg.generate_response(state, last_user_act, last_system_act, slots, system_act_frame)
|
||||||
|
print('BOT:', system_response)
|
||||||
|
|
||||||
|
if user_act_frame['act'] == UserActType['bye']:
|
||||||
|
break
|
51
mockup.py
Normal file
51
mockup.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
acts_list = {'hello': 'welcomemsg',
|
||||||
|
'null': 'canthear'}
|
||||||
|
|
||||||
|
|
||||||
|
class Act_frame:
|
||||||
|
text = ''
|
||||||
|
act = []
|
||||||
|
|
||||||
|
|
||||||
|
class Dialogue_state_frame:
|
||||||
|
state_frame = []
|
||||||
|
|
||||||
|
|
||||||
|
def NLU(text):
|
||||||
|
user_frame = Act_frame()
|
||||||
|
user_frame.text = text
|
||||||
|
if text == 'Cześć, jak masz na imię?':
|
||||||
|
user_frame.act = 'hello'
|
||||||
|
print(user_frame.act)
|
||||||
|
else:
|
||||||
|
user_frame.act = 'null'
|
||||||
|
print(user_frame.act)
|
||||||
|
return user_frame
|
||||||
|
|
||||||
|
|
||||||
|
def DST(user_frame):
|
||||||
|
dialogue_frame = Dialogue_state_frame()
|
||||||
|
dialogue_frame.state_frame.append((user_frame.text, user_frame.act))
|
||||||
|
print(dialogue_frame.state_frame)
|
||||||
|
return dialogue_frame
|
||||||
|
|
||||||
|
|
||||||
|
def DP(dialogue_frame):
|
||||||
|
system_frame = Act_frame()
|
||||||
|
system_frame.act = acts_list[dialogue_frame.state_frame[-1][1]]
|
||||||
|
print(system_frame.act)
|
||||||
|
return system_frame
|
||||||
|
|
||||||
|
|
||||||
|
def NLG(system_frame):
|
||||||
|
answer = ''
|
||||||
|
if system_frame.act == 'welcomemsg':
|
||||||
|
answer = 'Witaj, nazywam się Igrek Iksiński.'
|
||||||
|
elif system_frame.act == 'canthear':
|
||||||
|
answer = 'Nie zrozumiałem.'
|
||||||
|
return answer
|
||||||
|
|
||||||
|
|
||||||
|
text = 'Cześć, jak masz na imię?'
|
||||||
|
#text = 'Niezrozumiałe'
|
||||||
|
print(NLG(DP(DST(NLU(text)))))
|
21
nlg.py
Normal file
21
nlg.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from SystemActType import SystemActType
|
||||||
|
from UserActType import UserActType
|
||||||
|
|
||||||
|
|
||||||
|
class NLG:
|
||||||
|
def generate_response(self, state, last_user_act, last_system_act, slots, system_act):
|
||||||
|
if state == UserActType['order']:
|
||||||
|
if system_act['act'] == SystemActType['request']:
|
||||||
|
if system_act['slot'] == 'kind':
|
||||||
|
return 'Jaką pizzę chcesz zamówić?'
|
||||||
|
elif system_act['slot'] == 'size':
|
||||||
|
return 'Jakiego rozmiaru chcesz pizzę?'
|
||||||
|
elif system_act['slot'] == 'plates':
|
||||||
|
return 'Dla ilu osób ma to być?'
|
||||||
|
elif system_act['act'] == SystemActType['confirm_domain']:
|
||||||
|
return 'Czy mam dodać tę pizzę do zamówienia?\nPizza: {}\nRozmiar: {}\nIlość osób: {}'.\
|
||||||
|
format(slots['order']['kind'], slots['order']['size'], slots['order']['plates'])
|
||||||
|
elif last_user_act == UserActType['hello']:
|
||||||
|
return 'Dzień dobry, w czym mogę pomóc?'
|
||||||
|
else:
|
||||||
|
return 'Przepraszam. Zdanie nie jest mi zrozumiałe. Spróbuj je sformułować w inny sposób.'
|
49
nlu.py
Normal file
49
nlu.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import jsgf
|
||||||
|
from os import listdir
|
||||||
|
from os.path import isfile, join
|
||||||
|
|
||||||
|
|
||||||
|
gram_dir = './gramatyki/'
|
||||||
|
grammar_files = [file for file in listdir(gram_dir) if isfile(join(gram_dir, file))]
|
||||||
|
|
||||||
|
grammars = []
|
||||||
|
|
||||||
|
for grammarFile in grammar_files:
|
||||||
|
grammar = jsgf.parse_grammar_file(gram_dir + grammarFile)
|
||||||
|
grammars.append(grammar)
|
||||||
|
|
||||||
|
|
||||||
|
def get_dialog_act(rule):
|
||||||
|
slots = []
|
||||||
|
get_slots(rule.expansion, slots)
|
||||||
|
return {'act': rule.grammar.name, 'slots': slots}
|
||||||
|
|
||||||
|
|
||||||
|
def get_slots(expansion, slots):
|
||||||
|
if expansion.tag != '':
|
||||||
|
slots.append((expansion.tag, expansion.current_match))
|
||||||
|
return
|
||||||
|
|
||||||
|
for child in expansion.children:
|
||||||
|
get_slots(child, slots)
|
||||||
|
|
||||||
|
if not expansion.children and isinstance(expansion, jsgf.NamedRuleRef):
|
||||||
|
get_slots(expansion.referenced_rule.expansion, slots)
|
||||||
|
|
||||||
|
|
||||||
|
def nlu(utterance):
|
||||||
|
matched = None
|
||||||
|
for _grammar in grammars:
|
||||||
|
matched = _grammar.find_matching_rules(utterance)
|
||||||
|
if matched:
|
||||||
|
break
|
||||||
|
|
||||||
|
if matched:
|
||||||
|
return get_dialog_act(matched[0])
|
||||||
|
else:
|
||||||
|
return {'act': 'null', 'slots': []}
|
||||||
|
|
||||||
|
|
||||||
|
# print(nlu('chciałbym zamowic pizze vesuvio XXL na dwie osoby'))
|
||||||
|
# print(nlu('na dowoz'))
|
||||||
|
# print(nlu('dowoz'))
|
Loading…
Reference in New Issue
Block a user