Finishing up NLU module

This commit is contained in:
s495727 2024-05-22 23:45:33 +02:00
parent f0c7b481d1
commit 6ca7b66fb4
17 changed files with 284 additions and 171 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
__pycache__
slot-model*
frame-model*
nlu_cache

View File

@ -35,3 +35,12 @@ Agent powinien wykazywać elastyczność, adaptując się do potrzeb klienta, np
| offer | rekomendacja (restauracji) | | offer | rekomendacja (restauracji) |
| request | pytanie użytkownika o wartość slotu | | request | pytanie użytkownika o wartość slotu |
| select | prośba o dokonanie wyboru spośród przedstawionych opcji | | select | prośba o dokonanie wyboru spośród przedstawionych opcji |
# Obsługa projektu
- Python 3.10.12
- Instalacja dependencies `pip3 install -r requirements.txt`
- Centralna część systemu - uruchamiamy `python3 src/main.py`
- NLU:
- uczenie modeli od zera `python3 nlu_train.py`
- Ewaluacja `python3 evaluate.py`

View File

@ -2,11 +2,10 @@ import re
import os import os
import pandas as pd import pandas as pd
import numpy as np import numpy as np
from nlu_utils import predict_multiple
from flair.models import SequenceTagger from flair.models import SequenceTagger
from conllu import parse_incr from conllu import parse_incr
from flair.data import Corpus from flair.data import Corpus
from nlu_utils import conllu2flair, nolabel2o from src.utils.nlu_utils import conllu2flair, nolabel2o, predict_multiple
# Frame model evaluation # Frame model evaluation
frame_model = SequenceTagger.load('frame-model-prod/best-model.pt') frame_model = SequenceTagger.load('frame-model-prod/best-model.pt')

View File

@ -6,7 +6,7 @@ from flair.embeddings import CharacterEmbeddings
from flair.embeddings import FlairEmbeddings from flair.embeddings import FlairEmbeddings
from flair.models import SequenceTagger from flair.models import SequenceTagger
from flair.trainers import ModelTrainer from flair.trainers import ModelTrainer
from nlu_utils import conllu2flair, nolabel2o from src.utils.nlu_utils import conllu2flair, nolabel2o
import torch import torch
if torch.cuda.is_available(): if torch.cuda.is_available():

5
requirements.txt Normal file
View File

@ -0,0 +1,5 @@
flair==0.13.1
conllu==4.5.3
pandas==1.5.3
numpy==1.26.4
torch==2.3.0

0
src/__init__.py Normal file
View File

View File

@ -1,26 +1,38 @@
from model.frame import Frame
from service.dialog_state_monitor import DialogStateMonitor from service.dialog_state_monitor import DialogStateMonitor
from service.dialog_policy import DialogPolicy from service.dialog_policy import DialogPolicy
from service.natural_languag_understanding import NaturalLanguageUnderstanding from service.natural_languag_understanding import NaturalLanguageUnderstanding
from service.natural_language_generation import NaturalLanguageGeneration from service.natural_language_generation import NaturalLanguageGeneration
print("Natural language understanding, example:") # initialize classes
naturalLanguageUnderstanding = NaturalLanguageUnderstanding() nlu = NaturalLanguageUnderstanding() # NLU
print(naturalLanguageUnderstanding.convert_text_to_frame("Cześć, jak masz na imię?")) monitor = DialogStateMonitor() # DSM
dialog_policy = DialogPolicy() # DP
language_generation = NaturalLanguageGeneration() # NLG
# Main loop
user_input = input("Możesz zacząć pisać.\n")
while True:
# NLU
frame = nlu.process_input(user_input)
print(frame)
# DSM
monitor.append(frame)
# DP
print(dialog_policy.next_dialogue_act(monitor.get_all()).act)
# NLG
response = language_generation.respond_to_name_query("Jak masz na imię?")
print(response)
if frame.act == "bye":
break
user_input = input(">\n")
# Example
print("Dialog state monitor, examples:")
monitor = DialogStateMonitor()
monitor.append(Frame('system', 'hello', []))
monitor.append(Frame('user', 'some_text', []))
print(monitor.get_all()[0].act)
print(monitor.get_last().act)
print("Dialog policy, next dialogue act:")
dialog_policy = DialogPolicy(monitor.get_all())
print(dialog_policy.next_dialogue_act().act)
print("Natural Language Generation example:")
agent = NaturalLanguageGeneration()
response = agent.respond_to_name_query("Jak masz na imię?")
print(response)

0
src/model/__init__.py Normal file
View File

View File

@ -1,7 +1,14 @@
from model.slot import Slot from .slot import Slot
class Frame: class Frame:
def __init__(self, source: str, act: str, slots: list[Slot]): def __init__(self, source: str, act: str, slots: list[Slot]):
self.source = source self.source = source
self.slots = slots self.slots = slots
self.act = act self.act = act
def __str__(self):
msg = f"Act: {self.act}, Slots: ["
for slot in self.slots:
msg += f"({slot}), "
msg += "]"
return msg

View File

@ -2,3 +2,6 @@ class Slot:
def __init__(self, name, value=None): def __init__(self, name, value=None):
self.name = name self.name = name
self.value = value self.value = value
def __str__(self) -> str:
return f"Name: {self.name}, Value: {self.value}"

View File

@ -1,8 +1,8 @@
from flair.models import SequenceTagger from flair.models import SequenceTagger
from nlu_utils import predict_single, predict_multiple, predict_and_annotate from utils.nlu_utils import predict_single, predict_multiple, predict_and_annotate
# Exploratory tests # Exploratory tests
frame_model = SequenceTagger.load('frame-model/best-model.pt') frame_model = SequenceTagger.load('frame-model-prod/best-model.pt')
tests = [ tests = [
'chciałbym zamówić pizzę', 'chciałbym zamówić pizzę',
'na godzinę 12', 'na godzinę 12',
@ -16,7 +16,7 @@ tests = [
'pizzę barcelona bez cebuli', 'pizzę barcelona bez cebuli',
] ]
# print("=== Exploratory tests - frame model ===") print("=== Exploratory tests - frame model ===")
for test in tests: for test in tests:
print(f"Sentence: {test}") print(f"Sentence: {test}")
print(f"Single prediction: {predict_single(frame_model, test.split(), 'frame')}") print(f"Single prediction: {predict_single(frame_model, test.split(), 'frame')}")
@ -24,7 +24,7 @@ for test in tests:
print(f"Annotated sentence: {predict_and_annotate(frame_model, test.split(), 'frame')}") print(f"Annotated sentence: {predict_and_annotate(frame_model, test.split(), 'frame')}")
print("=== Exploratory tests - slot model ===") print("=== Exploratory tests - slot model ===")
slot_model = SequenceTagger.load('slot-model/final-model.pt') slot_model = SequenceTagger.load('slot-model-prod/best-model.pt')
for test in tests: for test in tests:
print(f"Sentence: {test}") print(f"Sentence: {test}")
print(f"Prediction: {predict_and_annotate(slot_model, test.split(), 'slot')}") print(f"Prediction: {predict_and_annotate(slot_model, test.split(), 'slot')}")

0
src/service/__init__.py Normal file
View File

View File

@ -1,11 +1,8 @@
from model.frame import Frame from model.frame import Frame
class DialogPolicy: class DialogPolicy:
def __init__(self, frames: list[Frame]) -> None: def next_dialogue_act(self, frames: list[Frame]) -> Frame:
self.frames = frames if frames[-1].act == "welcomemsg":
def next_dialogue_act(self) -> Frame:
if self.frames[-1].act == "welcomemsg":
return Frame("system", "welcomemsg", []) return Frame("system", "welcomemsg", [])
else: else:
return Frame("system", "canthelp", []) return Frame("system", "canthelp", [])

View File

@ -1,14 +1,92 @@
from flair.models import SequenceTagger
from utils.nlu_utils import predict_single, predict_and_annotate
from model.frame import Frame, Slot
"""
ACTS:
inform/order
request/menu
inform/address
request/price
request/ingredients
request/sauce
inform/phone
inform/order-complete
request/time
request/size
welcomemsg
affirm
inform/delivery
inform/payment
request/delivery-price
bye
inform/time
request/drinks
inform/name
negate
SLOTS:
food
pizza
size
address
quantity
ingredient
payment-method
delivery
drink
ingredient/neg
name
phone
sauce
"""
class NaturalLanguageUnderstanding: class NaturalLanguageUnderstanding:
def __init__(self):
print("\n========================================================")
print("Models are loading, it may take a moment, please wait...")
print("========================================================\n")
dictionary = { self.frame_model = SequenceTagger.load('frame-model-prod/best-model.pt')
"Cześć," : "welcomemsg()", self.slot_model = SequenceTagger.load('slot-model-prod/best-model.pt')
"imię?" : "request(name)"
}
def convert_text_to_frame(self, text: str): print("\n========================================================")
frame = "" print("Models loaded. NLU system is ready.")
text = text.split(" ") print("========================================================\n")
for word in text:
if(word in self.dictionary): def __predict_intention(self, text: str):
frame+=self.dictionary[word]+"&" return predict_single(self.frame_model, text.split(), 'frame')
return frame[0:-1]
def __predict_slot(self, text: str):
anootations = predict_and_annotate(self.slot_model, text.split(), 'slot')
current_slot = None
current_slot_value = ""
slots = []
for annotation in anootations:
form = annotation["form"]
slot = annotation["slot"]
if slot[0:2] == "B-":
if current_slot != None:
slots.append(Slot(name=current_slot, value=current_slot_value))
current_slot = slot[2:]
current_slot_value = form
elif slot[0:2] == "I-":
current_slot_value = current_slot_value + " " + form
elif slot == "O":
if current_slot != None:
slots.append(Slot(name=current_slot, value=current_slot_value))
current_slot = None
current_slot_value = ""
if current_slot != None:
slots.append(Slot(name=current_slot, value=current_slot_value))
return slots
def process_input(self, text: str):
act = self.__predict_intention(text)
slots = self.__predict_slot(text)
frame = Frame(source = 'user', act = act, slots = slots)
return frame

View File

@ -1,5 +1,5 @@
class NaturalLanguageGeneration: class NaturalLanguageGeneration:
def __init__(self, name): def __init__(self):
self.name = ["Michał"] self.name = ["Michał"]
def respond_to_name_query(self, question): def respond_to_name_query(self, question):

0
src/utils/__init__.py Normal file
View File

View File

@ -69,7 +69,6 @@ def predict_single(model, sentence, label_type):
csentence = __csentence(sentence, label_type) csentence = __csentence(sentence, label_type)
fsentence = __predict(model, csentence) fsentence = __predict(model, csentence)
intent = {} intent = {}
for span in fsentence.get_spans(label_type): for span in fsentence.get_spans(label_type):
tag = span.get_label(label_type).value tag = span.get_label(label_type).value
if tag in intent: if tag in intent: