systemy_dialogowe/notebooks/07-parsing-semantyczny-gramatyki.ipynb

8.4 KiB
Raw Blame History

Parsing semantyczny z wykorzystaniem gramatyk

Wartości slotów możemy wydobywać z wypowiedzi użytkownika korzystając z takich technik, jak:

  • wyszukiwanie słów kluczowych w tekście,

  • dopasowywanie wzorców zbudowanych przy użyciu wyrażeń regularnych,

  • parsery regułowe (temat dzisiejszych zajęć),

  • uczenie maszynowe (temat kolejnych zajęć).

Przykłady parserów regułowych

Przykład

Zapiszmy w JSGF gramatykę semantyczną dla aktu dialogowego reprezentującego zamiar rezerwacji stolika w restauracji.

%%writefile book.jsgf
#JSGF V1.0 UTF-8 pl;

grammar book;

public <rezerwuj> = chciałbym zarezerwować stolik <dzien_rezerwacji> <godzina_rezerwacji> <liczba_osob> ;

<dzien_rezerwacji> = na <dzien> {day};

<dzien> = dzisiaj | jutro | poniedziałek | wtorek | środę | czwartek | piątek | sobotę | niedzielę;

<godzina_rezerwacji> = na [godzinę] <godzina_z_minutami> {hour};

<godzina_z_minutami> = <godzina> [<minuty>];

<godzina> = dziewiątą | dziesiątą | jedenastą | dwunastą;

<minuty> = pietnaście | trzydzieści;

<liczba_osob> = (na | dla) <liczba> {size} osób;

<liczba> = dwie | dwóch | trzy | trzech | cztery | czterech | pięć | pieciu;
Writing book.jsgf

Parser akceptujący powyższą gramatykę utworzymy korzystając z biblioteki pyjsgf.

import jsgf

book_grammar = jsgf.parse_grammar_file('book.jsgf')
book_grammar
Grammar(version=1.0, charset=UTF-8, language=pl, name=book)

Wykorzystajmy gramatykę book.jsgf do analizy następującej wypowiedzi

utterance = 'chcialbym zarezerwowac stolik na jutro na godzine dwunasta trzydziesci na piec osob'
matched = book_grammar.find_matching_rules(utterance)
matched
[]

Reprezentację znaczenia można wydobyć ze sparsowanej wypowiedzi na wiele sposobów. My do wydobywania slotów wykorzystamy mechanizm tagów JSGF a za nazwę aktu dialogowego przyjmiemy nazwę gramatyki. Wzorując się na DSTC2 wynikową ramę zapiszemy korzystając ze słownika o polach act i slots.

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)

get_dialog_act(matched[0])
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[16], line 17
     14     if not expansion.children and isinstance(expansion, jsgf.NamedRuleRef):
     15         get_slots(expansion.referenced_rule.expansion, slots)
---> 17 get_dialog_act(matched[0])

IndexError: list index out of range

Łącząc powyższe funkcje możemy zbudować prosty moduł NLU.

def nlu(utterance):
    matched = book_grammar.find_matching_rules(utterance)

    if matched:
        return get_dialog_act(matched[0])
    else:
        return {'act': 'null', 'slots': []}

nlu('chciałbym zarezerwować stolik na jutro na godzinę dziesiątą dla trzech osób')

Problemy

  • Co z normalizacją wyrażeń liczbowych takich, jak godziny, daty czy numery telefonów?

  • Co w przypadku gdy więcej niż jedna reguła zostanie dopasowana?

Zadanie

Zaimplementować analizator języka naturalnego (NLU) na potrzeby realizowanego agenta dialogowego.

Moduł powinien być zbudowany z wykorzystaniem parsingu regułowego i/lub technik uczenia maszynowego.

Przygotować skrypt evaluate.py wyznaczający _dokładność (ang. accuracy) analizatora względem zgromadzonego korpusu eksperymentalnego, tj. stosunek liczby wypowiedzi użytkownika, w których akty dialogowe zostały rozpoznane prawidłowo do liczby wszystkich wypowiedzi użytkownika w korpusie.

Analizator języka naturalnego umieścić w gałęzi master repozytorium projektowego. Skrypt evaluate.py umieścić w katalogu głównym tej gałęzi.