{ "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 = chciałbym zarezerwować stolik ;\n", "\n", " = na {day};\n", "\n", " = dzisiaj | jutro | poniedziałek | wtorek | środę | czwartek | piątek | sobotę | niedzielę;\n", "\n", " = na [godzinę] {hour};\n", "\n", " = [];\n", "\n", " = dziewiątą | dziesiątą | jedenastą | dwunastą;\n", "\n", " = pietnaście | trzydzieści;\n", "\n", " = (na | dla) {size} osób;\n", "\n", " = 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 }