From 048825648a24b5110de9eaa52d6b52bc82430fb6 Mon Sep 17 00:00:00 2001 From: Luka Date: Fri, 27 Apr 2018 11:15:37 +0200 Subject: [PATCH] Added some runnable applications of this model --- .idea/accetuation.iml | 11 - .idea/dictionaries/luka.xml | 9 - .idea/encodings.xml | 6 - .idea/misc.xml | 22 - .idea/modules.xml | 8 - .idea/vcs.xml | 6 - .idea/workspace.xml | 1326 ----------------------------- accentuate.py | 71 ++ accentuate_connected_text.py | 79 ++ learn_location_weights.py | 74 ++ prepare_data.py | 92 +- preprocessed_data/environment.pkl | Bin 0 -> 73968 bytes test_data/accented_connected_text | 1 + test_data/accented_data | 6 + test_data/original_connected_text | 1 + test_data/unaccented_dictionary | 6 + 16 files changed, 329 insertions(+), 1389 deletions(-) delete mode 100644 .idea/accetuation.iml delete mode 100644 .idea/dictionaries/luka.xml delete mode 100644 .idea/encodings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml delete mode 100644 .idea/workspace.xml create mode 100644 accentuate.py create mode 100644 accentuate_connected_text.py create mode 100644 learn_location_weights.py create mode 100644 preprocessed_data/environment.pkl create mode 100644 test_data/accented_connected_text create mode 100644 test_data/accented_data create mode 100644 test_data/original_connected_text create mode 100644 test_data/unaccented_dictionary diff --git a/.idea/accetuation.iml b/.idea/accetuation.iml deleted file mode 100644 index 4b7c000..0000000 --- a/.idea/accetuation.iml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/dictionaries/luka.xml b/.idea/dictionaries/luka.xml deleted file mode 100644 index 1a1714b..0000000 --- a/.idea/dictionaries/luka.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - accentuations - nonresonant - overfitting - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 97626ba..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 23db218..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - $USER_HOME$/.subversion - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index f3d2052..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index c56c4eb..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,1326 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - count_vowels - count - sylla - # word - accented_word - get_ensemble_type_predictions - rever - accentuate_wo - content - transla - feature_dic - _create_slovene_feature_dictionary - feature_dictionary - morp - convert_multext - _convert_multext - _convert_to_multext_east_v4 - decode_x - assign_word_accentuation_type - accented_vowels - test_ - à - _get_accented_vowels - ô - ó - accent_class - i - for i - _syllable_generator - _generator_instance - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1486720239842 - - - 1492074623429 - - - 1523366019388 - - - 1523368401204 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - file://$PROJECT_DIR$/cnn/word_accetuation/cnn_dictionary/TEST/workbench.py - 36 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/accentuate.py b/accentuate.py new file mode 100644 index 0000000..c66d955 --- /dev/null +++ b/accentuate.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import pickle +import numpy as np +from keras.models import load_model +import sys + +from prepare_data import * + +# obtain data from parameters +if len(sys.argv) < 3: + print('Please provide arguments for this script to work. First argument should be location of file with unaccented words and morphological data ' + 'and second the name of file where you would like to save file to. Example: python accentuate.py \'test_data/unaccented_dictionary\' ' + '\'test_data/accented_data\'') + raise Exception +read_location = sys.argv[1] +write_location = sys.argv[2] + +# get environment variables necessary for calculations +pickle_input = open('preprocessed_data/environment.pkl', 'rb') +environment = pickle.load(pickle_input) +dictionary = environment['dictionary'] +max_word = environment['max_word'] +max_num_vowels = environment['max_num_vowels'] +vowels = environment['vowels'] +accented_vowels = environment['accented_vowels'] +feature_dictionary = environment['feature_dictionary'] +syllable_dictionary = environment['syllable_dictionary'] + +# load models +data = Data('l', shuffle_all_inputs=False) +letter_location_model, syllable_location_model, syllabled_letters_location_model = data.load_location_models( + 'cnn/word_accetuation/cnn_dictionary/v5_3/20_final_epoch.h5', + 'cnn/word_accetuation/syllables/v3_3/20_final_epoch.h5', + 'cnn/word_accetuation/syllabled_letters/v3_3/20_final_epoch.h5') + +letter_location_co_model, syllable_location_co_model, syllabled_letters_location_co_model = data.load_location_models( + 'cnn/word_accetuation/cnn_dictionary/v5_2/20_final_epoch.h5', + 'cnn/word_accetuation/syllables/v3_2/20_final_epoch.h5', + 'cnn/word_accetuation/syllabled_letters/v3_2/20_final_epoch.h5') + +letter_type_model, syllable_type_model, syllabled_letter_type_model = data.load_type_models( + 'cnn/accent_classification/letters/v3_1/20_final_epoch.h5', + 'cnn/accent_classification/syllables/v2_1/20_final_epoch.h5', + 'cnn/accent_classification/syllabled_letters/v2_1/20_final_epoch.h5') + +letter_type_co_model, syllable_type_co_model, syllabled_letter_type_co_model = data.load_type_models( + 'cnn/accent_classification/letters/v3_0/20_final_epoch.h5', + 'cnn/accent_classification/syllables/v2_0/20_final_epoch.h5', + 'cnn/accent_classification/syllabled_letters/v2_0/20_final_epoch.h5') + +# read from data +content = data._read_content(read_location) + +# format data for accentuate_word function it has to be like [['besedišči', '', 'Ncnpi', 'besedišči'], ] +content = [[el[0], '', el[1][:-1], el[0]] for el in content[:-1]] + +# use environment variables and models to accentuate words +data = Data('l', shuffle_all_inputs=False) +location_accented_words, accented_words = data.accentuate_word(content, letter_location_model, syllable_location_model, syllabled_letters_location_model, + letter_location_co_model, syllable_location_co_model, syllabled_letters_location_co_model, + letter_type_model, syllable_type_model, syllabled_letter_type_model, + letter_type_co_model, syllable_type_co_model, syllabled_letter_type_co_model, + dictionary, max_word, max_num_vowels, vowels, accented_vowels, feature_dictionary, syllable_dictionary) + +# save accentuated words +with open(write_location, 'w') as f: + for i in range(len(location_accented_words)): + f.write(location_accented_words[i] + ' ' + accented_words[i] + '\n') + f.write('\n') \ No newline at end of file diff --git a/accentuate_connected_text.py b/accentuate_connected_text.py new file mode 100644 index 0000000..cbf7e95 --- /dev/null +++ b/accentuate_connected_text.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import sys + +sys.path.insert(0, '../../../') +from prepare_data import * + +import pickle + +# from keras import backend as Input +np.random.seed(7) + +# obtain data from parameters +if len(sys.argv) < 3: + print('Please provide arguments for this script to work. First argument should be location of file with unaccented words and morphological data, ' + 'second the name of file where you would like to save results to and third location of ReLDI tagger. Example: python accentuate.py ' + '\'test_data/original_connected_text\' \'test_data/accented_connected_text\' \'../reldi_tagger\'') + raise Exception +read_location = sys.argv[1] +write_location = sys.argv[2] +reldi_location = sys.argv[3] + +# get environment variables necessary for calculations +pickle_input = open('preprocessed_data/environment.pkl', 'rb') +environment = pickle.load(pickle_input) +dictionary = environment['dictionary'] +max_word = environment['max_word'] +max_num_vowels = environment['max_num_vowels'] +vowels = environment['vowels'] +accented_vowels = environment['accented_vowels'] +feature_dictionary = environment['feature_dictionary'] +syllable_dictionary = environment['syllable_dictionary'] + +# get models +data = Data('l', shuffle_all_inputs=False) +letter_location_model, syllable_location_model, syllabled_letters_location_model = data.load_location_models( + 'cnn/word_accetuation/cnn_dictionary/v5_3/20_final_epoch.h5', + 'cnn/word_accetuation/syllables/v3_3/20_final_epoch.h5', + 'cnn/word_accetuation/syllabled_letters/v3_3/20_final_epoch.h5') + +letter_location_co_model, syllable_location_co_model, syllabled_letters_location_co_model = data.load_location_models( + 'cnn/word_accetuation/cnn_dictionary/v5_2/20_final_epoch.h5', + 'cnn/word_accetuation/syllables/v3_2/20_final_epoch.h5', + 'cnn/word_accetuation/syllabled_letters/v3_2/20_final_epoch.h5') + +letter_type_model, syllable_type_model, syllabled_letter_type_model = data.load_type_models( + 'cnn/accent_classification/letters/v3_1/20_final_epoch.h5', + 'cnn/accent_classification/syllables/v2_1/20_final_epoch.h5', + 'cnn/accent_classification/syllabled_letters/v2_1/20_final_epoch.h5') + +letter_type_co_model, syllable_type_co_model, syllabled_letter_type_co_model = data.load_type_models( + 'cnn/accent_classification/letters/v3_0/20_final_epoch.h5', + 'cnn/accent_classification/syllables/v2_0/20_final_epoch.h5', + 'cnn/accent_classification/syllabled_letters/v2_0/20_final_epoch.h5') + +# get word tags +tagged_words, original_text = data.tag_words(reldi_location, read_location) + +# find accentuation locations +predictions = data.get_ensemble_location_predictions(tagged_words, letter_location_model, syllable_location_model, syllabled_letters_location_model, + letter_location_co_model, syllable_location_co_model, syllabled_letters_location_co_model, + dictionary, max_word, max_num_vowels, vowels, accented_vowels, feature_dictionary, + syllable_dictionary) + +location_accented_text = data.create_connected_text_locations(tagged_words, original_text, predictions, vowels) + +# accentuate text +location_y = np.around(predictions) +type_predictions = data.get_ensemble_type_predictions(tagged_words, location_y, letter_type_model, syllable_type_model, syllabled_letter_type_model, + letter_type_co_model, syllable_type_co_model, syllabled_letter_type_co_model, + dictionary, max_word, max_num_vowels, vowels, accented_vowels, feature_dictionary, + syllable_dictionary) + +accented_text = data.create_connected_text_accented(tagged_words, original_text, type_predictions, location_y, vowels, accented_vowels) + +# save accentuated text +with open(write_location, 'w') as f: + f.write(accented_text) diff --git a/learn_location_weights.py b/learn_location_weights.py new file mode 100644 index 0000000..f2581d9 --- /dev/null +++ b/learn_location_weights.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +# text in Western (Windows 1252) + +import pickle +import numpy as np +np.random.seed(7) + +import sys +from prepare_data import * + +# preprocess data +# data = Data('l', allow_shuffle_vector_generation=True, save_generated_data=False, shuffle_all_inputs=True) +data = Data('l', save_generated_data=False, shuffle_all_inputs=True) +data.generate_data('../../internal_representations/inputs/letters_word_accentuation_train', + '../../internal_representations/inputs/letters_word_accentuation_test', + '../../internal_representations/inputs/letters_word_accentuation_validate', + content_location='../accetuation/data/', + content_name='SlovarIJS_BESEDE_utf8.lex', + inputs_location='../accetuation/cnn/internal_representations/inputs/', + content_shuffle_vector='content_shuffle_vector', + shuffle_vector='shuffle_vector') + +# combine all data (if it is unwanted comment code below) +data.x_train = np.concatenate((data.x_train, data.x_test, data.x_validate), axis=0) +data.x_other_features_train = np.concatenate((data.x_other_features_train, data.x_other_features_test, data.x_other_features_validate), axis=0) +data.y_train = np.concatenate((data.y_train, data.y_test, data.y_validate), axis=0) + +# build neural network architecture +nn_output_dim = 10 +batch_size = 16 +actual_epoch = 20 +num_fake_epoch = 20 + +conv_input_shape=(23, 36) +othr_input = (140, ) + +conv_input = Input(shape=conv_input_shape, name='conv_input') +x_conv = Conv1D(115, (3), padding='same', activation='relu')(conv_input) +x_conv = Conv1D(46, (3), padding='same', activation='relu')(x_conv) +x_conv = MaxPooling1D(pool_size=2)(x_conv) +x_conv = Flatten()(x_conv) + +othr_input = Input(shape=othr_input, name='othr_input') + +x = concatenate([x_conv, othr_input]) +x = Dense(256, activation='relu')(x) +x = Dropout(0.3)(x) +x = Dense(256, activation='relu')(x) +x = Dropout(0.3)(x) +x = Dense(256, activation='relu')(x) +x = Dropout(0.3)(x) +x = Dense(nn_output_dim, activation='sigmoid')(x) + +model = Model(inputs=[conv_input, othr_input], outputs=x) +opt = optimizers.Adam(lr=1E-3, beta_1=0.9, beta_2=0.999, epsilon=1e-08) +model.compile(loss='mean_squared_error', optimizer=opt, metrics=[actual_accuracy,]) +# model.compile(loss='mean_squared_error', optimizer='adam', metrics=['accuracy']) + + +# start learning +history = model.fit_generator(data.generator('train', batch_size, content_name='SlovarIJS_BESEDE_utf8.lex', content_location='../accetuation/data/'), + data.x_train.shape[0]/(batch_size * num_fake_epoch), + epochs=actual_epoch*num_fake_epoch, + validation_data=data.generator('test', batch_size), + validation_steps=data.x_test.shape[0]/(batch_size * num_fake_epoch)) + + +# save generated data +name = 'test_data/20_epoch' +model.save(name + '.h5') +output = open(name + '_history.pkl', 'wb') +pickle.dump(history.history, output) +output.close() diff --git a/prepare_data.py b/prepare_data.py index 250807e..75eefcb 100644 --- a/prepare_data.py +++ b/prepare_data.py @@ -7,6 +7,7 @@ import h5py import math import keras.backend as K import os.path +from os import remove import codecs from copy import copy @@ -666,7 +667,7 @@ class Data: loc += batch_size # generator for inputs for tracking of data fitting - def _syllable_generator(self, orig_x, orig_x_additional, orig_y, batch_size, translator, accented_vowels, oversampling): + def _syllable_generator(self, orig_x, orig_x_additional, orig_y, batch_size, translator, accented_vowels, oversampling=np.ones(13)): size = orig_x.shape[0] while 1: loc = 0 @@ -1655,6 +1656,95 @@ class Data: return location_accented_words, accented_words + def tag_words(self, reldi_location, original_location): + # generates text with every word in new line + with open(original_location) as f: + original_text = f.readlines() + original_text = ''.join(original_text) + # print(original_text) + text_with_whitespaces = original_text.replace(',', ' ,').replace('.', ' .').replace('\n', ' ').replace("\"", " \" ").replace(":", + " :").replace( + "ć", "č").replace('–', '-') + # print('-------------------------------------------------') + text_with_whitespaces = '\n'.join(text_with_whitespaces.split()) + text_with_whitespaces += '\n\n' + # print(text_with_whitespaces) + with open('.words_with_whitespaces', "w") as text_file: + text_file.write(text_with_whitespaces) + + # generates text with PoS tags + import subprocess + + myinput = open('.words_with_whitespaces', 'r') + myoutput = open('.word_tags', 'w') + # print(myinput.readlines()) + python3_command = reldi_location + "/tagger.py sl" # launch your python2 script using bash + + process = subprocess.run(python3_command.split(), stdin=myinput, stdout=myoutput) + + # generates interesting words + pointless_words = ['.', ',', '\"', ':', '-'] + with open('.word_tags', "r") as text_file: + tagged_input_words = [] + for x in text_file.readlines()[:-1]: + splited_line = x[:-1].split('\t') + if splited_line[0] not in pointless_words and not any(char.isdigit() for char in splited_line[0]): + tagged_input_words.append([splited_line[0].lower(), '', splited_line[1], splited_line[0].lower()]) + + remove(".words_with_whitespaces") + remove(".word_tags") + return tagged_input_words, original_text + + def create_connected_text_locations(self, tagged_input_words, original_text, predictions, vowels): + if 'A' not in vowels: + vowels.extend(['A', 'E', 'I', 'O', 'U']) + accented_words = [self.assign_location_stress(tagged_input_words[i][0][::-1], self.decode_y(predictions[i]), vowels)[::-1] for i in + range(len(tagged_input_words))] + + # print(accented_words[:20]) + # print(tagged_input_words[:20]) + + words_and_accetuation_loc = [[tagged_input_words[i][0], self.decode_y(predictions[i])] for i in range(len(tagged_input_words))] + + original_text_list = list(original_text) + original_text_lowercase = original_text.lower() + end_pos = 0 + for word in words_and_accetuation_loc: + posit = original_text_lowercase.find(word[0], end_pos) + if posit != -1: + start_pos = posit + end_pos = start_pos + len(word[0]) + + original_text_list[start_pos:end_pos] = list( + self.assign_location_stress(''.join(original_text_list[start_pos:end_pos][::-1]), word[1], vowels)[::-1]) + + return ''.join(original_text_list) + + def create_connected_text_accented(self, tagged_input_words, original_text, type_predictions, location_y, vowels, accented_vowels): + + input_words = [el[0] for el in tagged_input_words] + words = self.assign_stress_types(type_predictions, input_words, location_y, vowels, accented_vowels) + + # print(original_text) + + original_text_list = list(original_text) + original_text_lowercase = original_text.lower() + end_pos = 0 + for i in range(len(words)): + posit = original_text_lowercase.find(input_words[i], end_pos) + if posit != -1: + start_pos = posit + end_pos = start_pos + len(words[i]) + + orig_word = original_text_list[start_pos:end_pos] + new_word = list(words[i]) + for j in range(len(orig_word)): + if orig_word[j].isupper(): + new_word[j] = new_word[j].upper() + + original_text_list[start_pos:end_pos] = new_word + + return ''.join(original_text_list) # def count_vowels(content, vowels): # num_all_vowels = 0 # for el in content: diff --git a/preprocessed_data/environment.pkl b/preprocessed_data/environment.pkl new file mode 100644 index 0000000000000000000000000000000000000000..7912fd37e838e582112d78608b2f8d8c3aa2e8df GIT binary patch literal 73968 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 dPT=!Ct1z2ufUcl;)SA_y;_zu&b&YCn^dHCN