diff --git a/Create_lists.py b/Code/Create_lists.py similarity index 100% rename from Create_lists.py rename to Code/Create_lists.py diff --git a/Code/Janet.py b/Code/Janet.py new file mode 100644 index 0000000..476407a --- /dev/null +++ b/Code/Janet.py @@ -0,0 +1,55 @@ +import jsgf + +from Modules.NLG_module import NLG +from Modules.DP_module import DP +from Modules.DST_module import DST +from Modules.Book_NLU_module import Book_NLU +from Modules.ML_NLU_module import ML_NLU + +import random +import torch +random.seed(42) +torch.manual_seed(42) + +if torch.cuda.is_available(): + torch.cuda.manual_seed(0) + torch.cuda.manual_seed_all(0) + torch.backends.cudnn.enabled = False + torch.backends.cudnn.benchmark = False + torch.backends.cudnn.deterministic = True + +class Janet: + def __init__(self): + self.acts={ + 0: "greetings", + 1: "request", + } + self.arguments={ + 0: "name" + } + self.nlg = NLG(self.acts, self.arguments) + self.dp = DP(self.acts, self.arguments) + self.dst = DST(self.acts, self.arguments) + self.nlu = Book_NLU(self.acts, self.arguments, jsgf.parse_grammar_file('book.jsgf')) + self.nlu_v2 = ML_NLU(self.acts, self.arguments) + + def test(self, command): + out = self.nlu_v2.test_nlu(command) + return out + + def process(self, command): + act = self.nlu.analyze(command) + self.dst.store(act) + dest_act = self.dp.choose_tactic(self.dst.transfer()) + return self.nlg.change_to_text(dest_act) + + +def main(): + janet = Janet() + while(1): + print('\n') + text = input("Wpisz tekst: ") + print(janet.test(text)) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Code/Modules/Book_NLU_module.py b/Code/Modules/Book_NLU_module.py new file mode 100644 index 0000000..c1b7db2 --- /dev/null +++ b/Code/Modules/Book_NLU_module.py @@ -0,0 +1,53 @@ +import jsgf + +class Book_NLU: #Natural Language Understanding + """ + Moduł odpowiedzialny za analizę tekstu. W wyniku jego działania tekstowa reprezentacja wypowiedzi użytkownika zostaje zamieniona na jej reprezentację semantyczną, najczęściej w postaci ramy. + + Wejście: Tekst + + Wyjście: Akt użytkownika (rama) + """ + def __init__(self, acts, arguments, book_grammar): + self.acts = acts + self.arguments = arguments + self.book_grammar = book_grammar + + def get_dialog_act(self, rule): + slots = [] + self.get_slots(rule.expansion, slots) + return {'act': rule.grammar.name, 'slots': slots} + + def get_slots(self, expansion, slots): + if expansion.tag != '': + slots.append((expansion.tag, expansion.current_match)) + return + + for child in expansion.children: + self.get_slots(child, slots) + + if not expansion.children and isinstance(expansion, jsgf.NamedRuleRef): + self.get_slots(expansion.referenced_rule.expansion, slots) + + def analyze(self, text): + """ + Analiza Tekstu wprowadzonego przez użytkownika i zamiana na akt (rama) + """ + print("Analiza Tekstu: " + text) + act = "(greetings()&request(name))" + print("Akt to: " + act) + #przerobienie na wektor + act_vector = [[0],[1,0]] #1 wektor to greetings, a 2 wektor to request z argumentem "name" + print("Zamiana na: ") + print(act_vector) + return act_vector + + def test_nlu(self, utterance): + matched = self.book_grammar.find_matching_rules(utterance) + print(matched) + + if matched: + return self.get_dialog_act(matched[0]) + + else: + return {'act': 'null', 'slots': []} \ No newline at end of file diff --git a/Code/Modules/DP_module.py b/Code/Modules/DP_module.py new file mode 100644 index 0000000..27e171e --- /dev/null +++ b/Code/Modules/DP_module.py @@ -0,0 +1,19 @@ +class DP: + """ + Moduł decydujący o wyborze kolejnego aktu, który ma podjąć system prowadząc rozmowę. + + Wejście: Reprezentacja stanu dialogu (rama) + + Wyjście: Akt systemu (rama) + """ + def __init__(self, acts, arguments): + self.acts = acts + self.arguments = arguments + + + def choose_tactic(self, frame_list): + """ + Obieranie taktyki na podstawie aktów usera. Bardzo ważna jest kolejność dodawanych do frame_list wartości. + """ + act_vector = [0, 0] + return act_vector \ No newline at end of file diff --git a/Code/Modules/DST_module.py b/Code/Modules/DST_module.py new file mode 100644 index 0000000..b30f89a --- /dev/null +++ b/Code/Modules/DST_module.py @@ -0,0 +1,27 @@ +class DST: #Dialogue State Tracker + """ + Moduł odpowiedzialny za śledzenie stanu dialogu. Przechowuje informacje o tym jakie dane zostały uzyskane od użytkownika w toku prowadzonej konwersacji. + + Wejście: Akt użytkownika (rama) + + Wyjście: Reprezentacja stanu dialogu (rama) + """ + def __init__(self, acts, arguments): + self.acts = acts + self.arguments = arguments + self.frame_list= [] + + + def store(self, rama): + """ + Dodanie nowego aktu do listy + """ + print("\nDodanie do listy nowej ramy: ") + print(rama) + self.frame_list.append(rama) + + + def transfer(self): + print("Przekazanie dalej listy ram: ") + print(self.frame_list) + return self.frame_list \ No newline at end of file diff --git a/Code/Modules/ML_NLU_module.py b/Code/Modules/ML_NLU_module.py new file mode 100644 index 0000000..4e3139d --- /dev/null +++ b/Code/Modules/ML_NLU_module.py @@ -0,0 +1,69 @@ +import jsgf +from tabulate import tabulate +from flair.data import Sentence, Token +from flair.datasets import SentenceDataset +from flair.models import SequenceTagger + +class ML_NLU: + def __init__(self, acts, arguments): + self.acts = acts + self.arguments = arguments + self.slot_model, self.frame_model = self.setup() + + def nolabel2o(self, line, i): + return 'O' if line[i] == 'NoLabel' else line[i] + + def conllu2flair(self, sentences, label=None): + fsentences = [] + for sentence in sentences: + fsentence = Sentence() + for token in sentence: + ftoken = Token(token['form']) + if label: + ftoken.add_tag(label, token[label]) + fsentence.add_token(ftoken) + fsentences.append(fsentence) + return SentenceDataset(fsentences) + + + def predict(self, sentence): + csentence = [{'form': word} for word in sentence] + fsentence = self.conllu2flair([csentence])[0] + self.slot_model.predict(fsentence) + self.frame_model.predict(fsentence) + possible_intents = {} + for token in fsentence: + for intent in token.annotation_layers["frame"]: + if(intent.value in possible_intents): + possible_intents[intent.value] += intent.score + else: + possible_intents[intent.value] = intent.score + return [(token, ftoken.get_tag('slot').value) for token, ftoken in zip(sentence, fsentence)], max(possible_intents) + + def setup(self): + slot_model = SequenceTagger.load('slot-model/final-model.pt') + frame_model = SequenceTagger.load('frame-model/final-model.pt') + return slot_model, frame_model + + def test_nlu(self, utterance): + if utterance: + slots, act = self.predict(utterance.split()) + slots = [x for x in slots if x[1] != 'O'] + arguments = self.convert_slot_to_argument(slots) + return {'act': act, 'slots': arguments} + else: + return 'Critical Error' + + def convert_slot_to_argument(self, slots): + arguments = [] + candidate = None + for slot in slots: + if slot[1].startswith("B-"): + if(candidate != None): + arguments.append(candidate) + candidate = [slot[1].replace("B-", ""), slot[0]] + if slot[1].startswith("I-") and candidate != None and slot[1].endswith(candidate[0]): + candidate[1] += " " + slot[0] + if(candidate != None): + arguments.append(candidate) + return [(x[0], x[1]) for x in arguments] diff --git a/Code/Modules/NLG_module.py b/Code/Modules/NLG_module.py new file mode 100644 index 0000000..10f673e --- /dev/null +++ b/Code/Modules/NLG_module.py @@ -0,0 +1,20 @@ +class NLG: + """ + Moduł, który tworzy reprezentację tekstową aktu systemowego wybranego przez taktykę dialogu. + + Wejście: Akt systemu (rama) + + Wyjście: Tekst + """ + def __init__(self, acts, arguments): + self.acts = acts + self.arguments = arguments + + + def change_to_text(self, act_vector): + """ + Funkcja zamieniająca akt systemu na tekst rozumiany przez użytkownika. + """ + if(act_vector == [0, 0]): + return "Cześć, mam na imię Janet" + return "Nie rozumiem" \ No newline at end of file diff --git a/Test_nlu.py b/Code/Test_nlu.py similarity index 100% rename from Test_nlu.py rename to Code/Test_nlu.py diff --git a/eval.py b/Code/eval.py similarity index 100% rename from eval.py rename to Code/eval.py diff --git a/train.py b/Code/train.py similarity index 100% rename from train.py rename to Code/train.py diff --git a/Makiety.py b/Makiety.py deleted file mode 100644 index a5ab9fd..0000000 --- a/Makiety.py +++ /dev/null @@ -1,235 +0,0 @@ -import jsgf -from tabulate import tabulate -from flair.data import Sentence, Token -from flair.datasets import SentenceDataset -from flair.models import SequenceTagger - -import random -import torch -random.seed(42) -torch.manual_seed(42) - -if torch.cuda.is_available(): - torch.cuda.manual_seed(0) - torch.cuda.manual_seed_all(0) - torch.backends.cudnn.enabled = False - torch.backends.cudnn.benchmark = False - torch.backends.cudnn.deterministic = True - -class ML_NLU: - def __init__(self, acts, arguments): - self.acts = acts - self.arguments = arguments - self.slot_model, self.frame_model = self.setup() - - def nolabel2o(self, line, i): - return 'O' if line[i] == 'NoLabel' else line[i] - - def conllu2flair(self, sentences, label=None): - fsentences = [] - for sentence in sentences: - fsentence = Sentence() - for token in sentence: - ftoken = Token(token['form']) - if label: - ftoken.add_tag(label, token[label]) - fsentence.add_token(ftoken) - fsentences.append(fsentence) - return SentenceDataset(fsentences) - - - def predict(self, sentence): - csentence = [{'form': word} for word in sentence] - fsentence = self.conllu2flair([csentence])[0] - self.slot_model.predict(fsentence) - self.frame_model.predict(fsentence) - possible_intents = {} - for token in fsentence: - for intent in token.annotation_layers["frame"]: - if(intent.value in possible_intents): - possible_intents[intent.value] += intent.score - else: - possible_intents[intent.value] = intent.score - return [(token, ftoken.get_tag('slot').value) for token, ftoken in zip(sentence, fsentence)], max(possible_intents) - - def setup(self): - slot_model = SequenceTagger.load('slot-model/final-model.pt') - frame_model = SequenceTagger.load('frame-model/final-model.pt') - return slot_model, frame_model - - def test_nlu(self, utterance): - if utterance: - slots, act = self.predict(utterance.split()) - slots = [x for x in slots if x[1] != 'O'] - arguments = self.convert_slot_to_argument(slots) - return {'act': act, 'slots': arguments} - else: - return 'Critical Error' - - def convert_slot_to_argument(self, slots): - arguments = [] - candidate = None - for slot in slots: - if slot[1].startswith("B-"): - if(candidate != None): - arguments.append(candidate) - candidate = [slot[1].replace("B-", ""), slot[0]] - if slot[1].startswith("I-") and candidate != None and slot[1].endswith(candidate[0]): - candidate[1] += " " + slot[0] - if(candidate != None): - arguments.append(candidate) - return [(x[0], x[1]) for x in arguments] - -class Book_NLU: #Natural Language Understanding - """ - Moduł odpowiedzialny za analizę tekstu. W wyniku jego działania tekstowa reprezentacja wypowiedzi użytkownika zostaje zamieniona na jej reprezentację semantyczną, najczęściej w postaci ramy. - - Wejście: Tekst - - Wyjście: Akt użytkownika (rama) - """ - def __init__(self, acts, arguments, book_grammar): - self.acts = acts - self.arguments = arguments - self.book_grammar = book_grammar - - def get_dialog_act(self, rule): - slots = [] - self.get_slots(rule.expansion, slots) - return {'act': rule.grammar.name, 'slots': slots} - - def get_slots(self, expansion, slots): - if expansion.tag != '': - slots.append((expansion.tag, expansion.current_match)) - return - - for child in expansion.children: - self.get_slots(child, slots) - - if not expansion.children and isinstance(expansion, jsgf.NamedRuleRef): - self.get_slots(expansion.referenced_rule.expansion, slots) - - def analyze(self, text): - """ - Analiza Tekstu wprowadzonego przez użytkownika i zamiana na akt (rama) - """ - print("Analiza Tekstu: " + text) - act = "(greetings()&request(name))" - print("Akt to: " + act) - #przerobienie na wektor - act_vector = [[0],[1,0]] #1 wektor to greetings, a 2 wektor to request z argumentem "name" - print("Zamiana na: ") - print(act_vector) - return act_vector - - def test_nlu(self, utterance): - matched = self.book_grammar.find_matching_rules(utterance) - print(matched) - - if matched: - return self.get_dialog_act(matched[0]) - - else: - return {'act': 'null', 'slots': []} - -class DST: #Dialogue State Tracker - """ - Moduł odpowiedzialny za śledzenie stanu dialogu. Przechowuje informacje o tym jakie dane zostały uzyskane od użytkownika w toku prowadzonej konwersacji. - - Wejście: Akt użytkownika (rama) - - Wyjście: Reprezentacja stanu dialogu (rama) - """ - def __init__(self, acts, arguments): - self.acts = acts - self.arguments = arguments - self.frame_list= [] - - - def store(self, rama): - """ - Dodanie nowego aktu do listy - """ - print("\nDodanie do listy nowej ramy: ") - print(rama) - self.frame_list.append(rama) - - - def transfer(self): - print("Przekazanie dalej listy ram: ") - print(self.frame_list) - return self.frame_list - -class DP: - """ - Moduł decydujący o wyborze kolejnego aktu, który ma podjąć system prowadząc rozmowę. - - Wejście: Reprezentacja stanu dialogu (rama) - - Wyjście: Akt systemu (rama) - """ - def __init__(self, acts, arguments): - self.acts = acts - self.arguments = arguments - - - def choose_tactic(self, frame_list): - """ - Obieranie taktyki na podstawie aktów usera. Bardzo ważna jest kolejność dodawanych do frame_list wartości. - """ - act_vector = [0, 0] - return act_vector - -class NLG: - """ - Moduł, który tworzy reprezentację tekstową aktu systemowego wybranego przez taktykę dialogu. - - Wejście: Akt systemu (rama) - - Wyjście: Tekst - """ - def __init__(self, acts, arguments): - self.acts = acts - self.arguments = arguments - - - def change_to_text(self, act_vector): - """ - Funkcja zamieniająca akt systemu na tekst rozumiany przez użytkownika. - """ - if(act_vector == [0, 0]): - return "Cześć, mam na imię Janet" - return "Nie rozumiem" - - -class Janet: - def __init__(self): - self.acts={ - 0: "greetings", - 1: "request", - } - self.arguments={ - 0: "name" - } - self.nlg = NLG(self.acts, self.arguments) - self.dp = DP(self.acts, self.arguments) - self.dst = DST(self.acts, self.arguments) - self.nlu = Book_NLU(self.acts, self.arguments, jsgf.parse_grammar_file('book.jsgf')) - self.nlu_v2 = ML_NLU(self.acts, self.arguments) - - def test(self, command): - out = self.nlu_v2.test_nlu(command) - return out - - def process(self, command): - act = self.nlu.analyze(command) - self.dst.store(act) - dest_act = self.dp.choose_tactic(self.dst.transfer()) - return self.nlg.change_to_text(dest_act) - -janet = Janet() -print(janet.test('chciałbym się umówić na wizytę do Piotra Pająka na jutro')) #Testowy print na start -while(1): - print('\n') - text = input("Wpisz tekst: ") - print(janet.test(text)) \ No newline at end of file