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

277 lines
8.4 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"id": "82a52d23",
"metadata": {},
"source": [
"Parsing semantyczny z wykorzystaniem gramatyk\n",
"=============================================\n",
"\n",
"Wartości slotów możemy wydobywać z wypowiedzi użytkownika korzystając z takich technik, jak:\n",
"\n",
" - wyszukiwanie słów kluczowych w tekście,\n",
"\n",
" - dopasowywanie wzorców zbudowanych przy użyciu wyrażeń regularnych,\n",
"\n",
" - parsery regułowe (temat dzisiejszych zajęć),\n",
"\n",
" - uczenie maszynowe (temat kolejnych zajęć)."
]
},
{
"cell_type": "markdown",
"id": "4e864fb4",
"metadata": {},
"source": [
"Przykłady parserów regułowych\n",
"-----------------------------\n",
"\n",
" - [Phoenix](http://wiki.speech.cs.cmu.edu/olympus/index.php/Phoenix_Server) — parser gramatyk\n",
" bezkontekstowych whodzący w skład systemu dialogowego [Olympus](http://wiki.speech.cs.cmu.edu/olympus/index.php/Olympus)\n",
"\n",
" - Parsery [DCG](https://www.swi-prolog.org/pldoc/man?section=DCG) (Definite Clause Grammars) języka [Prolog](https://www.swi-prolog.org/)\n",
"\n",
" - [JSpeech Grammar Format](https://www.w3.org/TR/jsgf/) (JSGF)\n",
"\n",
"Przykład\n",
"--------\n",
"Zapiszmy w JSGF gramatykę semantyczną dla aktu dialogowego reprezentującego zamiar rezerwacji\n",
"stolika w restauracji."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "4c6b17fc",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Writing book.jsgf\n"
]
}
],
"source": [
"%%writefile book.jsgf\n",
"#JSGF V1.0 UTF-8 pl;\n",
"\n",
"grammar book;\n",
"\n",
"public <rezerwuj> = chciałbym zarezerwować stolik <dzien_rezerwacji> <godzina_rezerwacji> <liczba_osob> ;\n",
"\n",
"<dzien_rezerwacji> = na <dzien> {day};\n",
"\n",
"<dzien> = dzisiaj | jutro | poniedziałek | wtorek | środę | czwartek | piątek | sobotę | niedzielę;\n",
"\n",
"<godzina_rezerwacji> = na [godzinę] <godzina_z_minutami> {hour};\n",
"\n",
"<godzina_z_minutami> = <godzina> [<minuty>];\n",
"\n",
"<godzina> = dziewiątą | dziesiątą | jedenastą | dwunastą;\n",
"\n",
"<minuty> = pietnaście | trzydzieści;\n",
"\n",
"<liczba_osob> = (na | dla) <liczba> {size} osób;\n",
"\n",
"<liczba> = dwie | dwóch | trzy | trzech | cztery | czterech | pięć | pieciu;\n"
]
},
{
"cell_type": "markdown",
"id": "7442f5b9",
"metadata": {},
"source": [
"Parser akceptujący powyższą gramatykę utworzymy korzystając z biblioteki [pyjsgf](https://github.com/Danesprite/pyjsgf)."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "927f7cac",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Grammar(version=1.0, charset=UTF-8, language=pl, name=book)"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import jsgf\n",
"\n",
"book_grammar = jsgf.parse_grammar_file('book.jsgf')\n",
"book_grammar"
]
},
{
"cell_type": "markdown",
"id": "7fbf718f",
"metadata": {},
"source": [
"Wykorzystajmy gramatykę `book.jsgf` do analizy następującej wypowiedzi"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "fe70432a",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[]"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"utterance = 'chcialbym zarezerwowac stolik na jutro na godzine dwunasta trzydziesci na piec osob'\n",
"matched = book_grammar.find_matching_rules(utterance)\n",
"matched"
]
},
{
"cell_type": "markdown",
"id": "4dd1ac3d",
"metadata": {},
"source": [
"Reprezentację znaczenia można wydobyć ze sparsowanej wypowiedzi na wiele sposobów. My do\n",
"wydobywania slotów wykorzystamy mechanizm tagów JSGF a za nazwę aktu dialogowego przyjmiemy nazwę\n",
"gramatyki. Wzorując się na [DSTC2](https://github.com/matthen/dstc) wynikową ramę zapiszemy korzystając ze słownika o polach `act` i `slots`."
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "2b2bd6b7",
"metadata": {},
"outputs": [
{
"ename": "IndexError",
"evalue": "list index out of range",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mIndexError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[1;32mIn[16], line 17\u001b[0m\n\u001b[0;32m 14\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m expansion\u001b[39m.\u001b[39mchildren \u001b[39mand\u001b[39;00m \u001b[39misinstance\u001b[39m(expansion, jsgf\u001b[39m.\u001b[39mNamedRuleRef):\n\u001b[0;32m 15\u001b[0m get_slots(expansion\u001b[39m.\u001b[39mreferenced_rule\u001b[39m.\u001b[39mexpansion, slots)\n\u001b[1;32m---> 17\u001b[0m get_dialog_act(matched[\u001b[39m0\u001b[39;49m])\n",
"\u001b[1;31mIndexError\u001b[0m: list index out of range"
]
}
],
"source": [
"def get_dialog_act(rule):\n",
" slots = []\n",
" get_slots(rule.expansion, slots)\n",
" return {'act': rule.grammar.name, 'slots': slots}\n",
"\n",
"def get_slots(expansion, slots):\n",
" if expansion.tag != '':\n",
" slots.append((expansion.tag, expansion.current_match))\n",
" return\n",
"\n",
" for child in expansion.children:\n",
" get_slots(child, slots)\n",
"\n",
" if not expansion.children and isinstance(expansion, jsgf.NamedRuleRef):\n",
" get_slots(expansion.referenced_rule.expansion, slots)\n",
"\n",
"get_dialog_act(matched[0])"
]
},
{
"cell_type": "markdown",
"id": "2fca2fc3",
"metadata": {},
"source": [
"Łącząc powyższe funkcje możemy zbudować prosty moduł NLU."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "87a917a7",
"metadata": {},
"outputs": [],
"source": [
"def nlu(utterance):\n",
" matched = book_grammar.find_matching_rules(utterance)\n",
"\n",
" if matched:\n",
" return get_dialog_act(matched[0])\n",
" else:\n",
" return {'act': 'null', 'slots': []}\n",
"\n",
"nlu('chciałbym zarezerwować stolik na jutro na godzinę dziesiątą dla trzech osób')"
]
},
{
"cell_type": "markdown",
"id": "9e801dde",
"metadata": {},
"source": [
"Problemy\n",
"--------\n",
"\n",
" - Co z normalizacją wyrażeń liczbowych takich, jak godziny, daty czy numery telefonów?\n",
"\n",
" - Co w przypadku gdy więcej niż jedna reguła zostanie dopasowana?"
]
},
{
"cell_type": "markdown",
"id": "c8289023",
"metadata": {},
"source": [
"Zadanie\n",
"-------\n",
"Zaimplementować analizator języka naturalnego (NLU) na potrzeby realizowanego agenta dialogowego.\n",
"\n",
"Moduł powinien być zbudowany z wykorzystaniem parsingu regułowego i/lub technik uczenia maszynowego.\n",
"\n",
"Przygotować skrypt `evaluate.py` wyznaczający *dokładność* (ang. accuracy) analizatora względem zgromadzonego korpusu eksperymentalnego,\n",
"tj. stosunek liczby wypowiedzi użytkownika, w których akty dialogowe zostały rozpoznane prawidłowo do liczby wszystkich wypowiedzi użytkownika w korpusie.\n",
"\n",
"Analizator języka naturalnego umieścić w gałęzi `master` repozytorium projektowego. Skrypt `evaluate.py` umieścić w katalogu głównym tej gałęzi."
]
}
],
"metadata": {
"jupytext": {
"cell_metadata_filter": "-all",
"main_language": "python",
"notebook_metadata_filter": "-all"
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}