Finishing up NLU module
This commit is contained in:
parent
f0c7b481d1
commit
6ca7b66fb4
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
__pycache__
|
||||||
|
slot-model*
|
||||||
|
frame-model*
|
||||||
|
nlu_cache
|
@ -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`
|
||||||
|
@ -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')
|
||||||
|
@ -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
5
requirements.txt
Normal 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
0
src/__init__.py
Normal file
48
src/main.py
48
src/main.py
@ -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
0
src/model/__init__.py
Normal 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
|
||||||
|
@ -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}"
|
@ -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
0
src/service/__init__.py
Normal 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", [])
|
||||||
|
@ -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
|
@ -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
0
src/utils/__init__.py
Normal 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:
|
Loading…
Reference in New Issue
Block a user