Skip to content

Instantly share code, notes, and snippets.

@Koziev
Created April 22, 2019 11:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Koziev/cd7ee3c0b9a264d147e787126eb7059a to your computer and use it in GitHub Desktop.
Save Koziev/cd7ee3c0b9a264d147e787126eb7059a to your computer and use it in GitHub Desktop.
Использование ruword2tags.RuFlexer (а также rulemma, rupostagger, rutokenizer) для полуавтоматической генерации NLP датасета
# -*- 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)))
@egoriyaa
Copy link

где можно найти файлы invalid_syntax_dataset.txt и ['../../data/paraphrases.txt', '../../data/pqa_all.dat'] и что в них находится?

@Koziev
Copy link
Author

Koziev commented Jul 16, 2021

Привет,

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