From 0e8f04f5e3fa43f94755d264dcaa9b680605c0b3 Mon Sep 17 00:00:00 2001 From: koziev ilya Date: Sun, 17 Jan 2021 08:39:48 +0300 Subject: [PATCH] =?UTF-8?q?=D1=87=D0=B5=D1=80=D0=BD=D0=BE=D0=B2=D0=BE?= =?UTF-8?q?=D0=B9=20=D0=B2=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=20=D0=B4?= =?UTF-8?q?=D0=B5=D1=82=D0=B5=D0=BA=D1=82=D0=BE=D1=80=D0=B0=20=D0=B3=D0=B5?= =?UTF-8?q?=D0=BD=D0=B4=D0=B5=D1=80=D0=BD=D0=BE=D0=B9=20=D1=81=D0=B0=D0=BC?= =?UTF-8?q?=D0=BE=D0=B8=D0=B4=D0=B5=D0=BD=D1=82=D0=B8=D1=84=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dockerfile | 2 + ruchatbot/bot/interlocutor_gender_detector.py | 76 +++++++++++++++++++ ruchatbot/bot/simple_answering_machine.py | 10 ++- run-service.sh | 2 +- 4 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 ruchatbot/bot/interlocutor_gender_detector.py diff --git a/dockerfile b/dockerfile index daf83e7..4898f56 100644 --- a/dockerfile +++ b/dockerfile @@ -35,6 +35,8 @@ RUN pip install flask_sqlalchemy RUN pip install flask_wtf RUN pip install python-telegram-bot --upgrade RUN pip install h5py==2.10.0 +RUN pip install pyconll +RUN pip install ufal.udpipe WORKDIR /home ADD ruword2tags.tar.gz /home diff --git a/ruchatbot/bot/interlocutor_gender_detector.py b/ruchatbot/bot/interlocutor_gender_detector.py new file mode 100644 index 0000000..911c4c2 --- /dev/null +++ b/ruchatbot/bot/interlocutor_gender_detector.py @@ -0,0 +1,76 @@ +""" +Реализация модели для выделения признаков гендерной самоидентификации из сообщения собеседника. +TODO: подумать о переходе на полностью нейросетевую модель, чтобы учесть больше разных конструкций. +""" + +import pickle +import io +import os + + +class InterlocutorGenderDetector: + def __init__(self): + self.name2gender = None + + def load(self, models_dir): + with open(os.path.join(models_dir, 'names.pkl'), 'rb') as f: + self.name2gender = pickle.load(f) + + def detect_interlocutor_gender(self, text_str, text_utils): + # Пол собеседника пока неизвестен, будем пытаться определить его из лексического и синтаксического + # содержания фразы. + parsed_data = text_utils.parse_syntax(text_str) + + # Русскоязычные диалоги допускают несколько способов передать гендерную самоидентификацию. + # Проверим самые частотные. + interlocutor_gender = None + + # 1. Если есть глагол в прошедшем времени в роли сказуемого и подлежащее "я", то берем его тэг грамматического + # рода. + up_words = [z.form.lower().replace('ё', 'е') for z in parsed_data] + up_lemmas = [z.lemma.lower().replace('ё', 'е') for z in parsed_data] + edges2 = [] + for pred_token in parsed_data: + if pred_token.head != '0': + edges2.append((pred_token.form.lower(), parsed_data[pred_token.head].form.lower())) + + if pred_token.head == '0' and pred_token.upos == 'VERB': + if text_utils.get_udpipe_attr(pred_token, 'Tense') == 'Past': + interlocutor_gender = text_utils.get_udpipe_attr(pred_token, 'Gender') + if interlocutor_gender: + return interlocutor_gender + + # Конструкция "я должен/должна ... " + if 'ты' in up_words and 'должен' in up_lemmas: + if ('ты', 'должен') in edges2: + return 'Masc' + elif ('ты', 'должна') in edges2: + return 'Fem' + + # Реплики с шаблоном "меня зовут Марина" + if 'тебя' in up_words and 'звать' in up_lemmas: + for word in up_words: + uword = word.lower().replace('ё', 'е') + if uword in self.name2gender: + g = self.name2gender[uword] + if g == 'm': + return 'Masc' + elif g == 'f': + return 'Fem' + + return None + + # Реплики с шаблоном "мое имя Олег" + if 'твое' in up_words and 'имя' in up_lemmas and ('твое', 'имя') in edges2: + for word in up_words: + uword = word.lower().replace('ё', 'е') + if uword in self.name2gender: + g = self.name2gender + if g == 'm': + return 'Masc' + elif g == 'f': + return 'Fem' + + return None + + return None diff --git a/ruchatbot/bot/simple_answering_machine.py b/ruchatbot/bot/simple_answering_machine.py index 3b7b54b..27b7a04 100644 --- a/ruchatbot/bot/simple_answering_machine.py +++ b/ruchatbot/bot/simple_answering_machine.py @@ -8,6 +8,7 @@ import operator import random import requests +import collections from ruchatbot.bot.base_answering_machine import BaseAnsweringMachine from ruchatbot.bot.simple_dialog_session_factory import SimpleDialogSessionFactory @@ -1148,7 +1149,14 @@ def query_chitchat_service(self, bot, session, interlocutor, last_phrase): # Взвешиваем по контексту p_discourse = self.calc_discourse_relevance(rtext, session) - p_line = p_syntax * p_discourse + # Составные предложения (несколько клауз) будем штрафовать + t_chars = collections.defaultdict(int) + for c in rtext[:-1]: + t_chars[c] += 1 + nb_clauses = t_chars['.'] + t_chars['?'] + t_chars['!'] + p_clauses = math.exp(-nb_clauses) + + p_line = p_syntax * p_discourse * p_clauses ranked_lines.append((rtext, p_line)) if ranked_lines: diff --git a/run-service.sh b/run-service.sh index 97f05ed..acb76ea 100755 --- a/run-service.sh +++ b/run-service.sh @@ -1 +1 @@ -docker run -p 9001:9001 -it chatbot bash -c "/chatbot/scripts/flask_bot.sh" +sudo docker run -p 9001:9001 -it chatbot bash -c "/chatbot/scripts/flask_bot.sh"