Created
April 22, 2019 11:20
-
-
Save Koziev/cd7ee3c0b9a264d147e787126eb7059a to your computer and use it in GitHub Desktop.
Использование ruword2tags.RuFlexer (а также rulemma, rupostagger, rutokenizer) для полуавтоматической генерации NLP датасета
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
""" | |
Подбор сырья для формирования датасета для тренировки валидатора синтаксиса. | |
Берем фразы с правильным синтаксисом и заменяем в них предлоги на рандомные, | |
при необходимости пересогласуя подчиненные существительные и прилагательные. | |
""" | |
from __future__ import division # for python2 compatibility | |
from __future__ import print_function | |
import io | |
import random | |
import os | |
import rupostagger | |
import rutokenizer | |
import ruword2tags | |
import rulemma | |
def clean_input(s): | |
s = s.replace('(-)', '').replace('(+)', '').replace('T:', '').replace('Q:', '').replace('A:', '') | |
return s.strip() | |
def clean_line(s): | |
s = s.replace(u' ?', u'?').replace(u' !', u'!').replace(u' ,', u',').replace(u' :', u',')\ | |
.replace(u' .', u'.').replace(u'( ', u'(').replace(u' )', u')') | |
s = s[0].upper()+s[1:] | |
return s | |
def decode_pos_tagging(tags1): | |
pos1 = None | |
stags1 = [] | |
for tag1 in tags1.split(u'|'): | |
if tag1 == u'NOUN': | |
pos1 = u'СУЩЕСТВИТЕЛЬНОЕ' | |
else: | |
if pos1 == u'СУЩЕСТВИТЕЛЬНОЕ': | |
if tag1 == u'Case=Nom': | |
stags1.append((u'ПАДЕЖ', u'ИМ')) | |
elif tag1 == u'Case=Acc': | |
stags1.append((u'ПАДЕЖ', u'ВИН')) | |
elif tag1 == u'Case=Dat': | |
stags1.append((u'ПАДЕЖ', u'ДАТ')) | |
elif tag1 == u'Case=Ins': | |
stags1.append((u'ПАДЕЖ', u'ТВОР')) | |
elif tag1 == u'Case=Prep': | |
stags1.append((u'ПАДЕЖ', u'ПРЕДЛ')) | |
elif tag1 == u'Case=Loc': | |
stags1.append((u'ПАДЕЖ', u'МЕСТ')) | |
elif tag1 == u'Case=Gen': | |
stags1.append((u'ПАДЕЖ', u'РОД')) | |
elif tag1 == u'Number=Sing': | |
stags1.append((u'ЧИСЛО', u'ЕД')) | |
elif tag1 == u'Number=Plur': | |
stags1.append((u'ЧИСЛО', u'МН')) | |
elif tag1 == u'Gender=Masc': | |
stags1.append((u'РОД', u'МУЖ')) | |
elif tag1 == u'Gender=Fem': | |
stags1.append((u'РОД', u'ЖЕН')) | |
elif tag1 == u'Gender=Neut': | |
stags1.append((u'РОД', u'СР')) | |
else: | |
print(u'неизвестный тэг "{}"'.format(tag1)) | |
raise NotImplementedError() | |
return pos1, stags1 | |
def pick_tag_value(tags, needed_tag): | |
filtered_tags = [pair[1] for pair in tags if pair[0] == needed_tag] | |
if len(filtered_tags) > 0: | |
return filtered_tags[0] | |
else: | |
return None | |
print('Loading rutokenizer') | |
tokenizer = rutokenizer.Tokenizer() | |
tokenizer.load() | |
print('Loading rupostagger') | |
tagger = rupostagger.RuPosTagger() | |
tagger.load() | |
print('Loading rulemma...') | |
lemmatizer = rulemma.Lemmatizer() | |
lemmatizer.load() | |
print('Loading flexer...') | |
flexer = ruword2tags.RuFlexer() | |
flexer.load() | |
print('Flexer loaded.') | |
# Загрузим ранее добавленные негативные сэмплы из invalid_syntax_dataset.txt, | |
# чтобы не добавлять их снова. | |
print('Loading invalid_syntax_dataset.txt...') | |
emitted = set() | |
with io.open('../../data/invalid_syntax_dataset.txt', 'r', encoding='utf-8') as rdr: | |
for line in rdr: | |
if not line.startswith('#'): | |
words = tokenizer.tokenize(line.strip()) | |
if len(words) > 1: | |
key = u' '.join(words) | |
emitted.add(key) | |
all_prepositions = list(flexer.all_prepositions()) | |
print('Start generation...') | |
with io.open('../../tmp/syntax_validation_phrases.txt', 'w', encoding='utf-8') as wrt: | |
for inpath in ['../../data/paraphrases.txt', '../../data/pqa_all.dat']: | |
with io.open(inpath, 'r', encoding='utf-8') as rdr: | |
for line in rdr: | |
if not line.startswith('#'): | |
s = clean_input(line) | |
words = tokenizer.tokenize(s.lower()) | |
if (u'тебе' in words or u'тебе' in words) and len(words) > 1: | |
key = u' '.join(words) | |
if key not in emitted: | |
emitted.add(key) | |
tags = tagger.tag(words) | |
word_slots = lemmatizer.lemmatize(tags) | |
# Есть ли в предложении предлоги? | |
nword = len(words) | |
for iword, (word, tag, lemma, decoded_pos, decoded_tags) in enumerate(word_slots): | |
if decoded_pos == u'ПРЕДЛОГ': | |
# Нашли паттерн ПРЕДЛОГ+группа_сущ | |
prep = word | |
pattern_found = False | |
adj = None | |
adj_lemma = None | |
adj_tags = None | |
noun = None | |
noun_lemma = None | |
noun_tags = None | |
if iword < nword - 1 and word_slots[iword + 1][3] == u'СУЩЕСТВИТЕЛЬНОЕ': | |
# предлог+сущ | |
noun = word_slots[iword+1][0] | |
noun_lemma = word_slots[iword+1][2] | |
noun_tags = word_slots[iword+1][4] | |
#pattern_found = True | |
elif iword < nword - 2 and word_slots[iword + 1][3] == u'ПРИЛАГАТЕЛЬНОЕ'\ | |
and word_slots[iword + 2][3] == u'СУЩЕСТВИТЕЛЬНОЕ': | |
adj = word_slots[iword+1][0] | |
adj_lemma = word_slots[iword+1][2] | |
adj_tags = word_slots[iword+1][4] | |
noun = word_slots[iword+2][0] | |
noun_lemma = word_slots[iword+2][2] | |
noun_tags = word_slots[iword+2][4] | |
adj_case = pick_tag_value(adj_tags, u'ПАДЕЖ') | |
noun_case = pick_tag_value(noun_tags, u'ПАДЕЖ') | |
if adj_case == noun_case: | |
adj_num = pick_tag_value(adj_tags, u'ЧИСЛО') | |
noun_num = pick_tag_value(noun_tags, u'ЧИСЛО') | |
if adj_num == noun_num: | |
# Род не проверяем для простоты... | |
pattern_found = True | |
if pattern_found: | |
# Выбираем рандомный предлог | |
# Делаем несколько попыток генерации, так как не все полученные фразы будут невалидные. | |
for _ in range(10): | |
new_prep = random.choice(all_prepositions) | |
if new_prep == prep: | |
continue | |
# НАЧАЛО ОТЛАДКИ | |
#new_prep = u'сквозь' | |
# КОНЕЦ ОТЛАДКИ | |
new_prep_case = None | |
for new_prep_tagset in flexer.get_word_tagsets(new_prep): | |
if (u'ЧАСТЬ_РЕЧИ', u'ПРЕДЛОГ') in new_prep_tagset: | |
# Определяем, какие падежи сочетаются с этим предлогом | |
new_prep_cases = [pair[1] for pair in new_prep_tagset if pair[0] == 'ПАДЕЖ'] | |
# Выбираем один из падежей | |
if len(new_prep_cases) == 1: | |
new_prep_case = new_prep_cases[0] | |
break | |
elif len(new_prep_cases) > 1: | |
new_prep_case = random.choice(new_prep_cases) | |
break | |
if new_prep_case: | |
# Если падеж не совпадает с текущим падежом существительного, то ищем новую форму | |
# и подставляем ее в предложение. | |
noun_case = pick_tag_value(noun_tags, u'ПАДЕЖ') | |
noun_number = pick_tag_value(noun_tags, u'ЧИСЛО') | |
new_noun = noun | |
new_adj = adj | |
if noun_case != new_prep_case: | |
do_generate = True | |
new_tags = [(u'ПАДЕЖ', new_prep_case), (u'ЧИСЛО', noun_number)] | |
new_forms = list(flexer.find_forms_by_tags(noun_lemma, new_tags)) | |
if len(new_forms) > 0: | |
new_noun = new_forms[0] | |
else: | |
new_noun = None | |
do_generate = False | |
if do_generate and adj is not None: | |
# Меняем форму прилагательного | |
new_tags = [(u'ПАДЕЖ', new_prep_case), (u'ЧИСЛО', noun_number), (u'КРАТКИЙ', '0'), (u'СТЕПЕНЬ', u'АТРИБ')] | |
adj_gender = pick_tag_value(adj_tags, u'РОД') | |
if adj_gender: | |
new_tags.append((u'РОД', adj_gender)) | |
if adj_gender == u'МУЖ' and new_prep_case == u'ВИН': | |
adj_anim = pick_tag_value(adj_tags, u'ОДУШ') | |
if adj_anim: | |
new_tags.append((u'ОДУШ', adj_anim)) | |
else: | |
# Тэг одушевленности получим у существительного | |
for noun_tagset in flexer.get_word_tagsets(noun_lemma): | |
adj_anim = pick_tag_value(noun_tagset, u'ОДУШ') | |
if adj_anim: | |
new_tags.append((u'ОДУШ', adj_anim)) | |
break | |
new_forms = list(flexer.find_forms_by_tags(adj_lemma, new_tags)) | |
if len(new_forms) > 0: | |
new_adj = new_forms[0] | |
else: | |
new_adj = None | |
do_generate = False | |
if do_generate: | |
new_words = words.copy() | |
new_words[new_words.index(prep)] = new_prep | |
if new_adj: | |
new_words[new_words.index(adj)] = new_adj | |
new_words[new_words.index(noun)] = new_noun | |
new_phrase = u' '.join(new_words) | |
if new_phrase not in emitted: | |
emitted.add(new_phrase) | |
wrt.write(u'{}\n'.format(clean_line(new_phrase))) |
Привет,
invalid_syntax_dataset.txt
в этом файле собраны примеры предложений с поломанным синтаксисом или неверным употреблением слов: https://github.com/Koziev/chatbot/blob/master/data/invalid_syntax_dataset.txt
'../../data/paraphrases.txt', '../../data/pqa_all.dat
Эти датасеты я пока не выкладывал по юридическим причинам. В первом содержатся перефразировки (ручной датасет, не аналог контента из корпуса перевода субтитров Opus и paraphrase.ru). Во втором - данные для модели entailment'а и генерации ответов на заданные вопросы.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
где можно найти файлы invalid_syntax_dataset.txt и ['../../data/paraphrases.txt', '../../data/pqa_all.dat'] и что в них находится?