diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..4c91fbf2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Ignore (I)Pyhton and GNU R trash +**/.ipynb_checkpoints/ +**/__pycache__/ +**/.idea/ +**/.Rhistory + +# Ignore trash +**/.DS_Store +**/._* +**/.~lock.* diff --git a/01_ud_preprocessing.py b/01_ud_preprocessing.py new file mode 100644 index 00000000..3486f330 --- /dev/null +++ b/01_ud_preprocessing.py @@ -0,0 +1,556 @@ +# +# Convert gold standard UD corpus to training/experiments format: +# 1) EstNLTK's format: add EstNLTK's automatic morphological +# annotations to CONLL files; +# 2) UD format: clean annotations by removing null nodes, +# deps and misc values (optional), and copy gold standard +# CONLL files to new location; +# + +import re +import os +import os.path +import sys + +from datetime import datetime +from random import Random +import configparser + +import conllu + +from estnltk import Text +from estnltk.taggers import Tagger +from estnltk.taggers import VabamorfTagger +from estnltk.taggers import MorphExtendedTagger +from estnltk.taggers import WhiteSpaceTokensTagger +from estnltk.taggers import PretokenizedTextCompoundTokensTagger + +from estnltk.converters.conll.conll_importer import conll_to_text + +# =============================================================== +# Convert UD corpora to training/experiments format: +# 1) EstNLTK's format: add EstNLTK's morphological annotations; +# 2) UD format: clean annotations by removing null nodes, +# deps and misc values; +# (MAIN) +# =============================================================== + +def convert_to_estnltk_conllu_main( conf_file, verbose=True ): + ''' + Converts gold standard CONLL-U files to training/experiments format. + Settings/parameters of the conversion will be read from the given + `conf_file`. + Executes sections in the configuration starting with prefix 'preannotation_' + (add EstNLTK's morphological annotations) and 'copy_' (clean and copy files + with UD annotations). + See functions `convert_ud_conllu_to_estnltk_conllu(...)` and + `copy_and_clean_ud_conllu(...)` for details about the conversion + and possible parameters. + ''' + # Parse configuration file + config = configparser.ConfigParser() + if conf_file is None or not os.path.exists(conf_file): + raise FileNotFoundError("Config file {} does not exist".format(conf_file)) + if len(config.read(conf_file)) != 1: + raise ValueError("File {} is not accessible or is not in valid INI format".format(conf_file)) + morph_pipeline = [ WhiteSpaceTokensTagger(), + PretokenizedTextCompoundTokensTagger(), + VabamorfTagger(use_reorderer=True), + MorphExtendedTagger() ] + start = datetime.now() + section_found = False + for section in config.sections(): + if section.startswith('preannotation_'): + # Load preannotation configuration from the section + if not config.has_option(section, 'input_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "input_dir" parameter.') + input_dir = config[section]['input_dir'] + if not os.path.isdir(input_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "input_dir" value {input_dir!r} in {section!r}.') + if not config.has_option(section, 'morph_layer'): + raise ValueError(f'Error in {conf_file}: section {section} is missing "morph_layer" parameter.') + morph_layer = config[section]['morph_layer'] + if morph_layer == 'ud_morph_analysis': + # Add UDMorphConverter() to disambiguated morph + from estnltk.taggers import UDMorphConverter # requires estnltk 1.7.2+ + morph_pipeline.append( UDMorphConverter() ) + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_dir = config[section]['output_dir'] + seed = config[section].getint('seed', 43) + dictionarize = config[section].getboolean('dictionarize', True) + remove_empty_nodes = config[section].getboolean('remove_empty_nodes', True) + remove_deps = config[section].getboolean('remove_deps', True) + remove_misc = config[section].getboolean('remove_misc', True) + replace_lemma_by_root = config[section].getboolean('replace_lemma_by_root', False) + remove_metadata = config[section].getboolean('remove_metadata', False) + # Collect input files. Make possible output files and dir + input_files = [] + output_files = [] + for fname in os.listdir(input_dir): + if fname.endswith('.conllu'): + input_files.append(os.path.join(input_dir, fname)) + out_fname = fname.replace('.conllu', f'-{morph_layer}.conllu') + output_files.append(os.path.join(output_dir, out_fname)) + if not input_files: + raise Exception(f'(!) No conllu files found from "input_dir" {input_dir!r}.') + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + # Convert files + for in_file, out_file in zip(input_files, output_files): + if verbose: + print(f'Reannotating {in_file} with layer {morph_layer} ...') + convert_ud_conllu_to_estnltk_conllu( in_file, morph_pipeline, morph_layer, out_file, + dictionarize=dictionarize, + replace_lemma_by_root=replace_lemma_by_root, + remove_empty_nodes=remove_empty_nodes, + remove_metadata=remove_metadata, + remove_deps=remove_deps, + remove_misc=remove_misc, + seed=seed ) + section_found = True + if section.startswith('copy_'): + # Load copying configuration from the section + if not config.has_option(section, 'input_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "input_dir" parameter.') + input_dir = config[section]['input_dir'] + if not os.path.isdir(input_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "input_dir" value {input_dir!r} in {section!r}.') + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_dir = config[section]['output_dir'] + remove_empty_nodes = config[section].getboolean('remove_empty_nodes', True) + remove_deps = config[section].getboolean('remove_deps', True) + remove_misc = config[section].getboolean('remove_misc', True) + remove_metadata = config[section].getboolean('remove_metadata', False) + # Collect input files. Make possible output files and dir + input_files = [] + output_files = [] + for fname in os.listdir(input_dir): + if fname.endswith('.conllu'): + input_files.append(os.path.join(input_dir, fname)) + output_files.append(os.path.join(output_dir, fname)) + if not input_files: + raise Exception(f'(!) No conllu files found from "input_dir" {input_dir!r}.') + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + # Copy & clean files + for in_file, out_file in zip(input_files, output_files): + if verbose: + print(f'Copying & cleaning {in_file} ...') + copy_and_clean_ud_conllu( in_file, out_file, + remove_empty_nodes=remove_empty_nodes, + remove_deps=remove_deps, + remove_metadata=remove_metadata, + remove_misc=remove_misc ) + section_found = True + if section_found: + print(f'Total processing time: {datetime.now()-start}') + else: + print(f'No section starting with "preannotation_" or "copy_" in {conf_file}.') + + +def convert_ud_conllu_to_estnltk_conllu( in_file, morph_pipeline, morph_layer, out_file, + dictionarize=True, replace_lemma_by_root=False, + remove_empty_nodes=True, remove_metadata=False, + remove_deps=True, remove_misc=True, seed=43 ): + ''' + Reannotates `in_file` with given `morph_pipeline` and saves results into `out_file`. + During reannotation, values of `lemma`, `upos`, `xpos` and `feats` will be replaced + with corresponding automatically tagged values from `morph_layer` (either morph_analysis + or morph_extended type of layer). In case of ambiguity, annotation is chosen randomly. + Use `seed` to provide seed value for random choices. + `morph_pipeline` must be a list of taggers that can be applied on the `Text` object + loaded from `in_file`. Both `in_file` and `out_file` are CONLL-U format files. + + Requires EstNLTK version 1.7.2+. + + Parameters + ----------- + in_file + name/path of the CONLL-U format input file. + morph_pipeline + list of `Tagger`-s (in the order of layer dependencies) that can be applied + on the `Text` object loaded from `in_file`. + this pipeline should produce `morph_layer`, which is used to reannotate + the input file. + morph_layer + morphological annotations layer (either morph_analysis or morph_extended + type of layer) produced by `morph_pipeline`. + Values from this layer are used to reannotate values of `upos`, `xpos` + and `feats` in the input file. + out_file + name/path of the CONLL-U format output file. + dictionarize + If True (default), then values of `feats` will be converted to a dictionary of + features. + replace_lemma_by_root + If True, then lemmas will be replaced by root values from morph_analysis layer. + Default: False. + remove_metadata: + If True, then sentence metadata will be removed from the output conllu file. + This might be necessary if you want to create data for legacy parsers such as + MaltParser. + Default: False. + remove_empty_nodes + If True (default), then null / empty nodes (of the enhanced representation) will + be removed from the output. + remove_deps + If True (default), then values of `deps` field will be replaced with `_`. + remove_misc + If True (default), then values of `misc` field will be replaced with `_`. + ''' + # Validate input pipeline + assert isinstance(morph_pipeline, list) + assert isinstance(morph_layer, str) + has_morph_layer = False + for tagger in morph_pipeline: + assert isinstance(tagger, Tagger) + if morph_layer == tagger.output_layer: + has_morph_layer = True + if not has_morph_layer: + raise Exception(('(!) No tagger in the input pipeline {!r} creates '+\ + 'required layer {!r}.'.format(morph_pipeline, morph_layer))) + # Import text from conllu + with open(in_file, 'r', encoding='utf-8') as input_file: + conll_sentences = conllu.parse(input_file.read()) + text_obj = conll_to_text(in_file, remove_empty_nodes=remove_empty_nodes) + assert len(text_obj['sentences']) == len(conll_sentences), \ + ('(!) Mismatching sentence numbers in estnltk import ({})'+\ + ' and conllu import ({}).').format( len(text_obj['sentences']), \ + len(conll_sentences) ) + # Annotate text + for tagger in morph_pipeline: + tagger.tag( text_obj ) + # If required, remove orphans / null nodes + if remove_empty_nodes: + token_count = 0 + for sid, sentence in enumerate(conll_sentences): + removables = [] + for tid, token in enumerate(sentence): + token_id = token['id'] + if isinstance(token_id, tuple) and len(token_id) == 3 and token_id[1] == '.': + removables.append(token) + if removables: + for token in removables: + sentence.remove(token) + token_count += len(sentence) + assert token_count == len(text_obj[morph_layer]), \ + f'(!) Token count mismatch: tokens from CONLL file: {token_count} '+\ + f'vs tokens from EstNLTK annotated text: {len(text_obj[morph_layer])}.' + # Carry annotations over to TokenList-s + word_id = 0 + # In case of an ambiguity, pick random analysis. + # Fix seed for repeatability + rand = Random() + rand.seed( seed ) + for sid, sentence in enumerate(conll_sentences): + for tid, token in enumerate(sentence): + word_span = text_obj[morph_layer][word_id] + assert word_span.text == token["form"] + annotation = rand.choice(word_span.annotations) + if morph_layer in ['morph_analysis', 'morph_extended']: + token['upos'] = annotation['partofspeech'] + token['xpos'] = annotation['partofspeech'] + token['feats'] = annotation['form'] + token['lemma'] = annotation['lemma'] + if replace_lemma_by_root: + if 'root' in annotation: + token['lemma'] = annotation['root'] + else: + # Find the same analysis from morph_analysis layer + # Get lemma from there + word_span2 = text_obj['morph_analysis'][word_id] + for annotation2 in word_span2.annotations: + if annotation2['lemma'] == annotation['lemma'] and \ + annotation2['partofspeech'] == annotation['partofspeech']: + token['lemma'] = annotation2['root'] + break + # ? Override random pos with first pos (seems to be more accurate ?) + #token['upos'] = word_span.annotations[0]['partofspeech'] + #token['xpos'] = word_span.annotations[0]['partofspeech'] + if dictionarize: + # Format form as a dictionary + form_parts = annotation['form'].split() + token['feats'] = {f:f for f in form_parts} + elif morph_layer == 'ud_morph_analysis': + token['upos'] = annotation['upostag'] + token['xpos'] = annotation['xpostag'] + token['feats'] = annotation['feats'] + token['lemma'] = annotation['lemma'] + else: + raise Exception(f'(!) Unexpected morph_layer: {morph_layer!r}') + if len(token['feats']) == 0: + token['feats'] = None + if remove_misc and token['misc'] is not None: + token['misc'] = None + if remove_deps and token['deps'] is not None: + token['deps'] = None + word_id += 1 + #print(sentence.serialize()) + #print() + # Export annotated file + with open(out_file, 'w', encoding='utf-8') as out_file: + for sentence in conll_sentences: + if remove_metadata: + sentence.metadata = None + out_file.write( sentence.serialize() ) + + +def copy_and_clean_ud_conllu( in_file, out_file, remove_empty_nodes=True, + remove_metadata=False, remove_deps=True, remove_misc=True ): + ''' + Cleans `in_file` by removing empty nodes, deps and misc attributes, and + saves result as `out_file`. + Both `in_file` and `out_file` are CONLL-U format files. + Use this function to prepare data for experiments that use gold standard + UD morphological annotations. + + Parameters + ----------- + in_file + name/path of the CONLL-U format input file. + out_file + name/path of the CONLL-U format output file. + remove_empty_nodes + If True (default), then null / empty nodes (of the enhanced representation) will + be removed from the output. + remove_metadata: + If True, then sentence metadata will be removed from the output conllu file. + This might be necessary if you want to create data for legacy parsers such as + MaltParser. + Default: False. + remove_deps + If True (default), then values of `deps` field will be replaced with `_`. + remove_misc + If True (default), then values of `misc` field will be replaced with `_`. + ''' + # Import text from conllu + with open(in_file, 'r', encoding='utf-8') as input_file: + conll_sentences = conllu.parse(input_file.read()) + # If required, remove orphans / null nodes + if remove_empty_nodes: + for sid, sentence in enumerate(conll_sentences): + removables = [] + for tid, token in enumerate(sentence): + token_id = token['id'] + if isinstance(token_id, tuple) and len(token_id) == 3 and token_id[1] == '.': + removables.append(token) + if removables: + for token in removables: + sentence.remove(token) + # Clean annotations + for sid, sentence in enumerate(conll_sentences): + for tid, token in enumerate(sentence): + if remove_misc and token['misc'] is not None: + token['misc'] = None + if remove_deps and token['deps'] is not None: + token['deps'] = None + # Export annotated file + with open(out_file, 'w', encoding='utf-8') as out_file: + for sentence in conll_sentences: + if remove_metadata: + sentence.metadata = None + out_file.write( sentence.serialize() ) + + +# =============================================================== +# DEBUGGING: +# Convert UD corpora to EstNLTK format and +# compare against reference converted corpus +# =============================================================== + +def convert_and_compare_against_reference( in_file, morph_pipeline, morph_layer, ref_file, + dictionarize=True, remove_empty_nodes=False, + remove_misc=True, remove_deps=True, seed=43 ): + ''' + Reannotates `in_file` with given `morph_pipeline` and compares results against `ref_file`. + Outputs numers/percentages of matching `upos`, `lemma` and `feats` values. + For description of parameters, see `convert_ud_conllu_to_estnltk_conllu(...)`. + ''' + # Validate input pipeline + assert isinstance(morph_pipeline, list) + assert isinstance(morph_layer, str) + has_morph_layer = False + for tagger in morph_pipeline: + assert isinstance(tagger, Tagger) + if morph_layer == tagger.output_layer: + has_morph_layer = True + if not has_morph_layer: + raise Exception(('(!) No tagger in the input pipeline {!r} creates '+\ + 'required layer {!r}.'.format(morph_pipeline, morph_layer))) + # Import text from conllu + with open(in_file, 'r', encoding='utf-8') as input_file: + conll_sentences = conllu.parse(input_file.read()) + # Import reference conllu + with open(ref_file, 'r', encoding='utf-8') as input_file: + ref_conll_sentences = conllu.parse(input_file.read()) + text_obj = conll_to_text(in_file, remove_empty_nodes=remove_empty_nodes) + assert len(text_obj['sentences']) == len(conll_sentences), \ + ('(!) Mismatching sentence numbers in estnltk import ({})'+\ + ' and conllu import ({}).').format( len(text_obj['sentences']), \ + len(conll_sentences) ) + # Annotate text + for tagger in morph_pipeline: + tagger.tag( text_obj ) + # If required, remove orphans / null nodes + if remove_empty_nodes: + token_count = 0 + for sid, sentence in enumerate(conll_sentences): + removables = [] + for tid, token in enumerate(sentence): + token_id = token['id'] + if isinstance(token_id, tuple) and len(token_id) == 3 and token_id[1] == '.': + removables.append(token) + if removables: + for token in removables: + sentence.remove(token) + token_count += len(sentence) + assert token_count == len(text_obj[morph_layer]), \ + f'(!) Token count mismatch: tokens from CONLL file: {token_count} '+\ + f'vs tokens from EstNLTK annotated text: {len(text_obj[morph_layer])}.' + token_count = 0 + for sid, sentence in enumerate(ref_conll_sentences): + removables = [] + for tid, token in enumerate(sentence): + token_id = token['id'] + if isinstance(token_id, tuple) and len(token_id) == 3 and token_id[1] == '.': + removables.append(token) + if removables: + for token in removables: + sentence.remove(token) + token_count += len(sentence) + assert token_count == len(text_obj[morph_layer]), \ + f'(!) Token count mismatch: tokens from CONLL file: {token_count} '+\ + f'vs tokens from EstNLTK annotated text: {len(text_obj[morph_layer])}.' + # Carry annotations over to TokenList-s + # In case of an ambiguity, pick random analysis. + # Fix seed for repeatability + rand = Random() + rand.seed(seed) + word_id = 0 + matches_with_ref_pos = 0 + matches_with_ref_feats = 0 + matches_with_ref_lemma = 0 + matches_complete = 0 + for sid, sentence in enumerate(conll_sentences): + ref_sentence = ref_conll_sentences[sid] + for tid, token in enumerate(sentence): + word_span = text_obj[morph_layer][word_id] + assert word_span.text == token["form"], f'{word_span.text!r} vs {token["form"]!r}' + #if isinstance(token['id'], tuple): + # print('orphan:', token['id'], token['upos'], token['feats']) + annotation = rand.choice(word_span.annotations) + ref_token = ref_sentence[tid] + token['upos'] = annotation['partofspeech'] + token['xpos'] = annotation['partofspeech'] + token['feats'] = annotation['form'] + # ? Override random pos with first pos (seems to be more accurate ?) + #token['upos'] = word_span.annotations[0]['partofspeech'] + #token['xpos'] = word_span.annotations[0]['partofspeech'] + if dictionarize: + # Format form as a dictionary + form_parts = annotation['form'].split() + token['feats'] = {f:f for f in form_parts} + if len(token['feats']) == 0: + token['feats'] = None + if remove_misc and token['misc'] is not None: + token['misc'] = None + if remove_deps and token['deps'] is not None: + token['deps'] = None + word_id += 1 + if token['upos'] == ref_token['upos']: + matches_with_ref_pos += 1 + if token['feats'] == ref_token['feats']: + matches_with_ref_feats += 1 + if token['lemma'] == ref_token['lemma']: + matches_with_ref_lemma += 1 + if token['upos'] == ref_token['upos'] and \ + token['feats'] == ref_token['feats'] and \ + token['lemma'] == ref_token['lemma']: + matches_complete += 1 + + per_pos = matches_with_ref_pos*100.0/word_id + per_feats = matches_with_ref_feats*100.0/word_id + per_lemma = matches_with_ref_lemma*100.0/word_id + per_comp = matches_complete*100.0/word_id + print('seed:', seed, f'| upos matches: {matches_with_ref_pos}/{word_id} ({per_pos:.2f}%) |'+\ + f' feats matches: {matches_with_ref_feats}/{word_id} ({per_feats:.2f}%) |'+\ + f' lemma matches: {matches_with_ref_lemma}/{word_id} ({per_lemma:.2f}%) |'+\ + f' complete matches: {matches_complete}/{word_id} ({per_comp:.2f}%) |') + + +def convert_and_compare_against_all_references( in_dir, morph_pipeline, ref_dirs, ref_skip_list=[], seed=43 ): + ''' + Reannotates CONLL files in `in_dir` with given `morph_pipeline` and + compares results against CONLL files in `ref_dirs`. Outputs results of comparison. + + Outputs from the last runs: + + 1) Results if upos is taken from randomly chosen annotation (default setting): + Conversion target: 'et_edt-ud-dev-morph_extended.conllu', morph layer: 'morph_extended' ... + seed: 43 | upos matches: 42801/44686 (95.78%) | feats matches: 44686/44686 (100.00%) | lemma matches: 44686/44686 (100.00%) | complete matches: 42801/44686 (95.78%) | + Conversion target: 'et_edt-ud-test-morph_extended.conllu', morph layer: 'morph_extended' ... + seed: 43 | upos matches: 46742/48532 (96.31%) | feats matches: 48530/48532 (100.00%) | lemma matches: 48532/48532 (100.00%) | complete matches: 46740/48532 (96.31%) | + Conversion target: 'et_edt-ud-train-morph_extended.conllu', morph layer: 'morph_extended' ... + seed: 43 | upos matches: 331461/344953 (96.09%) | feats matches: 343263/344953 (99.51%) | lemma matches: 344953/344953 (100.00%) | complete matches: 330010/344953 (95.67%) | + + Conversion target: 'et_edt-ud-dev-morph_analysis.conllu', morph layer: 'morph_analysis' ... + seed: 43 | upos matches: 39868/44686 (89.22%) | feats matches: 44686/44686 (100.00%) | lemma matches: 44686/44686 (100.00%) | complete matches: 39868/44686 (89.22%) | + Conversion target: 'et_edt-ud-test-morph_analysis.conllu', morph layer: 'morph_analysis' ... + seed: 43 | upos matches: 43338/48532 (89.30%) | feats matches: 48530/48532 (100.00%) | lemma matches: 48532/48532 (100.00%) | complete matches: 43338/48532 (89.30%) | + Conversion target: 'et_edt-ud-train-morph_analysis.conllu', morph layer: 'morph_analysis' ... + seed: 43 | upos matches: 308432/344953 (89.41%) | feats matches: 344920/344953 (99.99%) | lemma matches: 344953/344953 (100.00%) | complete matches: 308425/344953 (89.41%) | + + 2) Results if upos/xpos is always taken from the first annotation: + Conversion target: 'et_edt-ud-dev-morph_extended.conllu', morph layer: 'morph_extended' ... + seed: 43 | upos matches: 43259/44686 (96.81%) | feats matches: 44686/44686 (100.00%) | lemma matches: 44686/44686 (100.00%) | complete matches: 43259/44686 (96.81%) | + Conversion target: 'et_edt-ud-test-morph_extended.conllu', morph layer: 'morph_extended' ... + seed: 43 | upos matches: 47169/48532 (97.19%) | feats matches: 48530/48532 (100.00%) | lemma matches: 48532/48532 (100.00%) | complete matches: 47167/48532 (97.19%) | + Conversion target: 'et_edt-ud-train-morph_extended.conllu', morph layer: 'morph_extended' ... + seed: 43 | upos matches: 335582/344953 (97.28%) | feats matches: 343263/344953 (99.51%) | lemma matches: 344953/344953 (100.00%) | complete matches: 334002/344953 (96.83%) | + + Conversion target: 'et_edt-ud-dev-morph_analysis.conllu', morph layer: 'morph_analysis' ... + seed: 43 | upos matches: 40390/44686 (90.39%) | feats matches: 44686/44686 (100.00%) | lemma matches: 44686/44686 (100.00%) | complete matches: 40390/44686 (90.39%) | + Conversion target: 'et_edt-ud-test-morph_analysis.conllu', morph layer: 'morph_analysis' ... + seed: 43 | upos matches: 43902/48532 (90.46%) | feats matches: 48530/48532 (100.00%) | lemma matches: 48532/48532 (100.00%) | complete matches: 43902/48532 (90.46%) | + Conversion target: 'et_edt-ud-train-morph_analysis.conllu', morph layer: 'morph_analysis' ... + seed: 43 | upos matches: 313489/344953 (90.88%) | feats matches: 344920/344953 (99.99%) | lemma matches: 344953/344953 (100.00%) | complete matches: 313482/344953 (90.88%) | + ''' + start = datetime.now() + ref_conllu_file_name_pat = re.compile('^(\S+)-([^\-]+)\.conllu$') + assert os.path.isdir( in_dir ) + for ref_dir in ref_dirs: + assert os.path.isdir( ref_dir ) + conllu_files_found = 0 + for ref_fname in os.listdir( ref_dir ): + if ref_fname in ref_skip_list: + continue + m = ref_conllu_file_name_pat.match(ref_fname) + if m: + ref_fpath = os.path.join(ref_dir, ref_fname) + input_file_prefix = m.group(1) + morph_layer_name = m.group(2) + # Detect input file + in_fpath = None + for in_fname in os.listdir( in_dir ): + if in_fname.startswith(input_file_prefix) and in_fname.endswith('.conllu'): + in_fpath = os.path.join(in_dir, in_fname) + if in_fpath is None: + raise FileNotFoundError( ('(!) Unable to find file with '+\ + 'prefix {!r} from dir {!r}.').format(input_file_prefix, + in_dir)) + print(f'Conversion target: {ref_fname!r}, morph layer: {morph_layer_name!r} ...') + convert_and_compare_against_reference( in_fpath, morph_pipeline, + morph_layer_name, ref_fpath, + seed=seed) + print(f'Total processing time: {datetime.now()-start}') + + + +if __name__ == '__main__': + if len(sys.argv) < 2: + raise Exception('(!) Missing input argument: name of the configuration INI file.') + # Try to execute all input files as configurations + for conf_file in sys.argv[1:]: + convert_to_estnltk_conllu_main( conf_file ) diff --git a/01b_extract_clauses.py b/01b_extract_clauses.py new file mode 100644 index 00000000..896c82ab --- /dev/null +++ b/01b_extract_clauses.py @@ -0,0 +1,109 @@ +# +# Splits sentences in CONLLU files into clauses (with EstNLTK's ClauseTagger). +# Cleans clauses (removes conjunctions and punctuation at the beginning or at +# the end of the clause), and exports cleaned clauses as CONLLU files. +# +# Requires estnltk 1.7.2+ +# + +import os +import sys +import configparser +from datetime import datetime + +from estnltk.converters.conll.conll_importer import conll_to_text + +from syntax_sketches.syntax_sketch import clean_clause +from syntax_sketches.clause_export import export_cleaned_clause + +def extract_clauses( conf_file ): + ''' + Splits sentences in CONLLU files into clauses with EstNLTK, + cleans clauses and saves as new CONLLU files. + Inputs/outputs and parameters of the processing will be read + from the given `conf_file`. + Executes sections in the configuration starting with prefix + 'extract_clauses_'. + ''' + # Parse configuration file + config = configparser.ConfigParser() + if conf_file is None or not os.path.exists(conf_file): + raise FileNotFoundError("Config file {} does not exist".format(conf_file)) + if len(config.read(conf_file)) != 1: + raise ValueError("File {} is not accessible or is not in valid INI format".format(conf_file)) + section_found = False + start = datetime.now() + for section in config.sections(): + if section.startswith('extract_clauses_'): + section_found = True + print(f'Performing {section} ...') + # Collect clause tagging parameters + if not config.has_option(section, 'input_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "input_dir" parameter.') + input_dir = config[section]['input_dir'] + if not os.path.isdir(input_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "input_dir" value {input_dir!r} in {section!r}.') + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_dir = config[section]['output_dir'] + if input_dir == output_dir: + raise ValueError(f'Error in {conf_file}: section {section!r} "output_dir" cannot be same as "input_dir".') + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + remove_empty_nodes = config[section].getboolean('remove_empty_nodes', True) + skip_list = config[section].get('skip_list', '') + if len(skip_list) > 0: + skip_list = skip_list.split(',') + skip_list = [fname.strip() for fname in skip_list] + for fname in os.listdir(input_dir): + if fname in skip_list: + continue + fpath = os.path.join(input_dir, fname) + if os.path.isfile(fpath) and fname.endswith('.conllu'): + text_obj = conll_to_text( fpath, 'ud_syntax', remove_empty_nodes=remove_empty_nodes ) + print('Tagging clauses in: ', fname) + text_obj.tag_layer('clauses') + expected_layers = { + 'clauses', 'compound_tokens', 'morph_analysis', + 'sentences', 'tokens', 'ud_syntax', 'words' + } + assert text_obj.layers == expected_layers, 'Unexpected layers' + + print('Writing out results to: ', output_dir) + output_fname = os.path.join(output_dir, fname) + valid_clauses = 0 + invalid_clauses = 0 + output_file = open(output_fname, 'wt', encoding='utf-8') + for clause in text_obj.clauses: + cleaned_clause = clean_clause(clause) + + if len(cleaned_clause['root_loc']) != 1: + invalid_clauses += 1 + continue + + if valid_clauses > 0: + output_file.write('\n\n') + + output_file.write(export_cleaned_clause(cleaned_clause)) + valid_clauses += 1 + + # Add final empty line (to avoid UDError: The CoNLL-U file does not end with empty line) + output_file.write('\n\n') + + print('Valid clauses: {}'.format(valid_clauses)) + print('Invalid clauses: {} (missing root)'.format(invalid_clauses)) + output_file.close() + if section_found: + print(f'Total processing time: {datetime.now()-start}') + else: + print(f'No section starting with "extract_clauses_" in {conf_file}.') + + +if __name__ == '__main__': + if len(sys.argv) < 2: + raise Exception('(!) Missing input argument: name of the configuration INI file.') + # Try to execute all input files as configurations + for conf_file in sys.argv[1:]: + extract_clauses( conf_file ) + + diff --git a/01c_analyse_sketches.ipynb b/01c_analyse_sketches.ipynb new file mode 100644 index 00000000..a40781ac --- /dev/null +++ b/01c_analyse_sketches.ipynb @@ -0,0 +1,808 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7d65d63a", + "metadata": {}, + "source": [ + "# Analyse syntax sketches" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "eb614e41", + "metadata": {}, + "outputs": [], + "source": [ + "import os, os.path\n", + "import conllu\n", + "from statistics import median\n", + "from collections import Counter, OrderedDict\n", + "\n", + "from numpy import argmax \n", + "from numpy import arange\n", + "from numpy import array\n", + "from numpy import cumsum\n", + "from numpy import quantile\n", + "from pandas import DataFrame\n", + "\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from syntax_sketches.syntax_sketch import compute_sketches\n", + "from syntax_sketches.deprel_seq import collect_deprel_seqs" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "611f6180", + "metadata": {}, + "outputs": [], + "source": [ + "# Configuration\n", + "# Input directory (must contain conllu files with full sentences)\n", + "input_dir_full_sentences = 'edt_2.6\\\\preannotated\\\\morph_extended'\n", + "assert os.path.isdir(input_dir_full_sentences)\n", + "\n", + "# Input directory (must contain conllu files with sentences split into clauses)\n", + "input_dir = 'edt_2.6\\\\preannotated\\\\morph_extended_clauses'\n", + "assert os.path.isdir(input_dir)\n", + "\n", + "# Save top 50 sketches for knock-out experiments into this file\n", + "knockout_list_file = 'edt_2.6\\\\preannotated\\\\morph_extended_clauses\\\\top_50_sketches.csv'" + ] + }, + { + "cell_type": "markdown", + "id": "ece9c322", + "metadata": {}, + "source": [ + "## [Prelude] A quick study of full sentence deprel sequences" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8e7e2a21", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "et_edt-ud-dev-morph_extended.conllu | #sentences/deprel_sequences: 3125\n", + "et_edt-ud-test-morph_extended.conllu | #sentences/deprel_sequences: 3214\n", + "et_edt-ud-train-morph_extended.conllu | #sentences/deprel_sequences: 24633\n", + "\n", + "Dependency relation sequences occurring only once: 85.96%\n" + ] + } + ], + "source": [ + "all_deprel_sequences = []\n", + "for fname in os.listdir(input_dir_full_sentences):\n", + " if fname.endswith('.conllu') and fname != 'train_full.conllu':\n", + " deprel_seqs = collect_deprel_seqs(os.path.join(input_dir_full_sentences, fname))\n", + " print(fname, '| #sentences/deprel_sequences: ', len(deprel_seqs))\n", + " all_deprel_sequences.extend(deprel_seqs)\n", + "\n", + "print()\n", + "deprel_seq_counter = Counter(all_deprel_sequences)\n", + "singleton_deprel_seqs = sum(array(list(deprel_seq_counter.values())) == 1)\n", + "print('Dependency relation sequences occurring only once: {:.2f}%'.format(singleton_deprel_seqs/len(all_deprel_sequences) * 100))" + ] + }, + { + "cell_type": "markdown", + "id": "b79e236a", + "metadata": {}, + "source": [ + "## I. Compute sketches for the whole corpus" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "0cb83f70", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "et_edt-ud-dev-morph_extended.conllu | #clauses: 5708\n", + "et_edt-ud-test-morph_extended.conllu | #clauses: 6033\n", + "et_edt-ud-train-morph_extended.conllu | #clauses: 43966\n", + "\n", + "#clauses total: 55707\n" + ] + } + ], + "source": [ + "sketches, clauses_count_total = compute_sketches(input_dir)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "daf8e157", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "55707" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(sketches)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "fd843d42", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[V]obl(L)',\n", + " '[V]nsubj(L)obj(P)obl(L)obl(L)',\n", + " '[V]mark(L)nsubj(P)',\n", + " '[S]advmod(L)aux(L)nsubj(L)obj(L)',\n", + " '[V]aux(L)nsubj(L)',\n", + " '[S]amod(L)cop(L)nsubj:cop(L)obl(P)',\n", + " '[V]advmod(L)nsubj(L)obl(P)',\n", + " '[V]nsubj(ÜP)obj(L)',\n", + " '[S]amod(L)cop(L)nmod(L)nsubj:cop(L)',\n", + " '[V]advmod(L)nsubj(P)obl(L)']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# list of sketches for all clauses\n", + "sketches[:10]" + ] + }, + { + "cell_type": "markdown", + "id": "d70ed01d", + "metadata": {}, + "source": [ + "Sketch name encoding:\n", + "* [ clause_root_postag ] -- V (verb), S (substantive, adjective, pronoun, abbreviation or numeral), X (other postag);\n", + " * first_level_child_deprel ( first_level_child_subtree_size* )\n", + " * (L) -- short subtree: up to two nodes;\n", + " * (P) -- long subtree: three to nine nodes;\n", + " * (ÜP) -- extra long subtree: ten or more nodes;\n", + " * \\* subtree size includes the first level child node itself;" + ] + }, + { + "cell_type": "markdown", + "id": "94756829", + "metadata": {}, + "source": [ + "## II. Analyse distribution of syntax sketches" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "67b7a0c2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of valid clauses: 55707\n", + "Number of different sketches: 7925\n", + "Expected sketch support: 7.03\n", + "Median sketch support: 1.00\n", + "Singleton sketches count: 4954\n", + "Singleton sketches: 62.51%\n", + "Non-singleton sketches: 37.49%\n" + ] + } + ], + "source": [ + "sketch_counter = Counter(sketches)\n", + "print('Number of valid clauses: {}'.format(clauses_count_total))\n", + "print('Number of different sketches: {}'.format(len(sketch_counter)))\n", + "print('Expected sketch support: {:.2f}'.format(clauses_count_total/len(sketch_counter)))\n", + "print('Median sketch support: {:.2f}'.format(median(sketch_counter.values())))\n", + "print('Singleton sketches count: {:}'.format(sum(array(list(sketch_counter.values())) == 1)))\n", + "print('Singleton sketches: {:.2f}%'.format(sum(array(list(sketch_counter.values())) == 1)/len(sketch_counter.values()) * 100))\n", + "print('Non-singleton sketches: {:.2f}%'.format(sum(array(list(sketch_counter.values())) > 1)/len(sketch_counter.values()) * 100))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1ddbff0f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjoAAAHFCAYAAAD7ZFORAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy88F64QAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA/xUlEQVR4nO3deVxUZf//8fewjrjgzqKIaIshKYkbGi65V5qt2mLaVytLS7NNs0K77WtZt6aJmnUXd5lmt6V3Wmlmbrda7plxt2i4Q6YmoCXmcP3+8Md8m0BlcJhhDq/n48Gj5jrXnPlwgZ631znXOTZjjBEAAIAFBfi6AAAAgLJC0AEAAJZF0AEAAJZF0AEAAJZF0AEAAJZF0AEAAJZF0AEAAJZF0AEAAJZF0AEAAJZF0EGF9+WXX+rWW29VVFSUQkJCFBUVpdtuu02bNm3ydWkuDh06pHHjxmn79u1Fto0bN042m82lrVOnTurUqZN3ivv/Tp8+raFDhyoqKkqBgYFKTEz06ucXx2azafjw4R7Z1yeffKJx48aV+v0NGzbU9ddf75FarOpixxj4K4IOKrRXX31V7du314EDBzRp0iR9/vnneumll7R//361bdtWs2fP9nWJTocOHdL48eOLDTpDhgzRhg0bvF/UX8ycOVOvvfaaxo4dq//85z965513fF2SR33yyScaP368r8uwNMYYnhbk6wIAX1m3bp1Gjhypa6+9VgsXLlRQ0P/9cejfv79uvPFGPfjgg7rqqqvUqlUrH1Z6YfXr11f9+vV9XYZ27typSpUqeWwGRZJ+//13VapUyWP7Q/n022+/KSwszNdlwIKY0UGFNXHiRNlsNs2cOdMl5EhSUFCQZsyY4exXaNCgQWrYsGGRfRV36igtLU0dOnRQ3bp1VblyZV155ZWaNGmS/vjjD5d+nTp1UkJCgjZt2qSUlBSFhYWpUaNGeuGFF1RQUCBJWrVqlTNs3XPPPbLZbLLZbM4p/uI+vzinT5/WhAkT1KRJE4WGhqpOnTq655579Msvv7j0++KLL9SpUyfVqlVLlSpVUoMGDXTzzTfrt99+O+e+bTab3njjDf3+++/O+tLT0yVJp06d0pgxYxQXF6eQkBDVq1dPw4YN0/Hjx132UXhq58MPP9RVV10lu91+3n/db9u2Tddff73q1q2r0NBQRUdH67rrrtOBAwfO+R5jjJ566ikFBwfr9ddfd7bPnz9fycnJqly5sqpUqaIePXpo27Ztzu2DBg1SWlqa83st/NqzZ48kqaCgQK+++qoSExNVqVIlVa9eXW3bttVHH31UpIalS5eqRYsWqlSpkpo0aaI333zznPX+2cyZM9W8eXNVqVJFVatWVZMmTfTUU085t5/r9yA9Pd2lVun/xnrhwoVq1qyZ7Ha7GjVqpGnTprm8d9WqVbLZbJozZ45GjRqlyMhIVapUSR07dnQZn0IfffSRkpOTFRYWpqpVq6pbt25FZhsL69y6datuueUW1ahRQ40bN77gGP/rX/9SmzZtFB4e7vxz8j//8z8lGjtUXMzooEJyOBxauXKlWrZsec6ZkJiYGCUlJenzzz9XQUGBAgLc+3fB7t27dccddzgP7l9//bWef/55fffdd0UObNnZ2brzzjv16KOPKjU1VQsXLtSYMWMUHR2tu+++Wy1atNBbb72le+65R08//bSuu+46SXJrFqegoEA33HCD1q5dqyeeeELt2rXT3r17lZqaqk6dOmnz5s2qVKmS9uzZo+uuu04pKSl68803Vb16dR08eFBLly7V6dOnz/mv7g0bNuhvf/ubVq5cqS+++EKS1LhxYxlj1LdvX61YsUJjxoxRSkqKduzYodTUVG3YsEEbNmxQaGiocz9bt27Vf//7Xz399NOKi4tT5cqVi/28kydPqlu3boqLi1NaWpoiIiKUnZ2tlStXKi8vr9j35Ofna9CgQfr444+1ePFi9ezZU5L0v//7v3r66aed43v69Gm99NJLSklJ0caNGxUfH69nnnlGJ0+e1IIFC1wO3FFRUZLOBqE5c+Zo8ODBeu655xQSEqKtW7e6hAtJ+vrrr/Xoo49q9OjRioiI0BtvvKHBgwfrkksuUYcOHc7583vvvff04IMP6qGHHtLLL7+sgIAA7dq1SxkZGed8z4Vs375dI0eO1Lhx4xQZGal3331XI0aM0OnTp/XYY4+59H3qqafUokULvfHGG8rJydG4cePUqVMnbdu2TY0aNZIkzZ07V3feeae6d++uefPmKT8/X5MmTVKnTp20YsUKXX311S77vOmmm9S/f38NHTpUJ0+eVEJCwjnHeMOGDerXr5/69euncePGyW63a+/evc7fNeCcDFABZWdnG0mmf//+5+3Xr18/I8n88ssvxhhjBg4caGJjY4v0S01NNef74+RwOMwff/xh3n77bRMYGGiOHTvm3NaxY0cjyXz11Vcu74mPjzc9evRwvt60aZORZN56660SfX7Hjh1Nx44dna/nzZtnJJkPPvjApV/hfmfMmGGMMWbBggVGktm+ffs5v59zGThwoKlcubJL29KlS40kM2nSJJf2+fPnG0lm9uzZzrbY2FgTGBhovv/++wt+1ubNm40ks2jRovP2k2SGDRtmjh49aq6++mpTr149l+9t3759JigoyDz00EMu78vLyzORkZHmtttuc7YNGzas2J/zmjVrjCQzduzY89YSGxtr7Ha72bt3r7Pt999/NzVr1jT333//ed87fPhwU7169fP2Odfv4VtvvWUkmczMTJdabDZbkZ9zt27dTLVq1czJkyeNMcasXLnSSDItWrQwBQUFzn579uwxwcHBZsiQIcaYs7/j0dHR5sorrzQOh8PZLy8vz9StW9e0a9euSJ3PPvtskVrPNcYvv/yykWSOHz9+3jEA/opTV8B5GGMkqUSnhf5q27Zt6tOnj2rVqqXAwEAFBwfr7rvvlsPh0A8//ODSNzIyUq1bt3Zpa9asmfbu3Vv64v9iyZIlql69unr37q0zZ844vxITExUZGalVq1ZJkhITExUSEqL77rtP//znP/XTTz9d1OcW/ot70KBBLu233nqrKleurBUrVri0N2vWTJdddtkF93vJJZeoRo0aevLJJzVr1qzzzmxkZmYqOTlZOTk5+vLLL9W8eXPntmXLlunMmTO6++67XcbFbrerY8eOznE5n08//VSSNGzYsAv2TUxMVIMGDZyv7Xa7Lrvssgv+rFu3bq3jx4/r9ttv17///W8dOXLkgp91IU2bNnUZC0m64447lJubq61btxZp//Ofg9jYWLVr104rV66UJH3//fc6dOiQBgwY4DL7WaVKFd1888368ssvi5z6vPnmm0tca+Gp29tuu03vv/++Dh48WOL3omIj6KBCql27tsLCwpSZmXnefnv27FGlSpVUq1Ytt/a/b98+paSk6ODBg5o6darWrl2rTZs2Oa8/+P333136F7f/0NDQIv0uxs8//6zjx48rJCREwcHBLl/Z2dnOA2fjxo31+eefq27duho2bJgaN26sxo0ba+rUqaX63KNHjyooKEh16tRxabfZbIqMjNTRo0dd2gtPBV1IeHi4Vq9ercTERD311FNq2rSp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LIugAwAALKvCBh2edQUAgPXxrCuedQUAgN/hWVcAAKDCI+gAAADLIugAAADLIugAAADLIugAAADLIugAAADLIugAAADLIugAAADLIugAAADLIugAAADLIugAAADLIugAAADLIugAAADLIugAAADLIugAAADLIugAAADLqrBBJy0tTfHx8WrVqpWvSwEAAGXEZowxvi7Cl3JzcxUeHq6cnBxVq1bN1+UAAIASKOnxu8LO6AAAAOsj6AAAAMsi6AAAAMsi6AAAAMsi6AAAAMsi6AAAAMsi6AAAAMsi6AAAAMsi6AAAAMsi6AAAAMsi6AAAAMsi6AAAAMsi6AAAAMsi6AAAAMtyO+js27dPxpgi7cYY7du3zyNFAQAAeILbQScuLk6//PJLkfZjx44pLi7OI0UBAAB4gttBxxgjm81WpP3EiROy2+0eKQoAAMATgkracdSoUZIkm82mZ555RmFhYc5tDodDX331lRITEz1eIAAAQGmVOOhs27ZN0tkZnW+++UYhISHObSEhIWrevLkee+wxz1cIAABQSiUOOitXrpQkDRo0SK+++qqqVq1aZkUBAAB4glvX6Jw5c0Zz5szR3r17y6oeAAAAj3Er6AQFBSk2NlYOh6Os6gEAAPAYt1ddPf300xozZoyOHTtWFvUAAAB4TImv0Sk0bdo07dq1S9HR0YqNjVXlypVdtm/dutVjxZXE/v37NWDAAB0+fFhBQUF65plndOutt3q1BgAAUD65HXT69u1bBmWUXlBQkF555RUlJibq8OHDatGiha699toiAQwAAFQ8bged1NTUsqij1KKiohQVFSVJqlu3rmrWrKljx44RdAAAQOkf6rllyxbNmTNH7777rvMeO6WxZs0a9e7dW9HR0bLZbFq0aFGRPjNmzFBcXJzsdruSkpK0du3aYve1efNmFRQUKCYmptT1AAAA63A76Bw+fFjXXHONWrVqpYcffljDhw9XUlKSunTpUuwzsC7k5MmTat68uaZPn17s9vnz52vkyJEaO3astm3bppSUFPXq1avIA0SPHj2qu+++W7Nnz3a7BgAAYE02U9yjyM+jX79+2r17t9555x1dccUVkqSMjAwNHDhQl1xyiebNm1f6Ymw2LVy40OU6oDZt2qhFixaaOXOms+2KK65Q3759NXHiRElSfn6+unXrpnvvvVcDBgw472fk5+crPz/f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Y4bi4uJkt9uVlJSktWvXnrf/6tWrlZSUJLvdrkaNGmnWrFleqtS/uTPOH374obp166Y6deqoWrVqSk5O1rJly7xYrf9y9/e50Lp16xQUFKTExMSyLdAi3B3n/Px8jR07VrGxsQoNDVXjxo315ptveqla/+buWL/77rtq3ry5wsLCFBUVpXvuuUdHjx71UrX+ac2aNerdu7eio6Nls9m0aNGiC77H68dCg1J57733THBwsHn99ddNRkaGGTFihKlcubLZu3dvsf1/+uknExYWZkaMGGEyMjLM66+/boKDg82CBQu8XLl/cXecR4wYYV588UWzceNG88MPP5gxY8aY4OBgs3XrVi9X7l/cHedCx48fN40aNTLdu3c3zZs3906xfqw049ynTx/Tpk0bs3z5cpOZmWm++uors27dOi9W7Z/cHeu1a9eagIAAM3XqVPPTTz+ZtWvXmqZNm5q+fft6uXL/8sknn5ixY8eaDz74wEgyCxcuPG9/XxwLCTql1Lp1azN06FCXtiZNmpjRo0cX2/+JJ54wTZo0cWm7//77Tdu2bcusRitwd5yLEx8fb8aPH+/p0iyltOPcr18/8/TTT5vU1FSCTgm4O86ffvqpCQ8PN0ePHvVGeZbi7li/9NJLplGjRi5t06ZNM/Xr1y+zGq2mJEHHF8dCTl2VwunTp7VlyxZ1797dpb179+5av359se/ZsGFDkf49evTQ5s2b9ccff5RZrf6sNOP8VwUFBcrLy1PNmjXLokRLKO04v/XWW9q9e7dSU1PLukRLKM04f/TRR2rZsqUmTZqkevXq6bLLLtNjjz2m33//3Rsl+63SjHW7du104MABffLJJzLG6Oeff9aCBQt03XXXeaPkCsMXx8IK/1DP0jhy5IgcDociIiJc2iMiIpSdnV3se7Kzs4vtf+bMGR05ckRRUVFlVq+/Ks04/9Xf//53nTx5UrfddltZlGgJpRnnH3/8UaNHj9batWsVFMRfIyVRmnH+6aef9J///Ed2u10LFy7UkSNH9OCDD+rYsWNcp3MepRnrdu3a6d1331W/fv106tQpnTlzRn369NGrr77qjZIrDF8cC5nRuQg2m83ltTGmSNuF+hfXDlfujnOhefPmady4cZo/f77q1q1bVuVZRknH2eFw6I477tD48eN12WWXeas8y3Dn97mgoEA2m03vvvuuWrdurWuvvVaTJ09Weno6szol4M5YZ2Rk6OGHH9azzz6rLVu2aOnSpcrMzNTQoUO9UWqF4u1jIf8UK4XatWsrMDCwyL8MDh8+XCSpFoqMjCy2f1BQkGrVqlVmtfqz0oxzofnz52vw4MH617/+pa5du5ZlmX7P3XHOy8vT5s2btW3bNg0fPlzS2QOyMUZBQUH67LPPdM0113ildn9Smt/nqKgo1atXT+Hh4c62K664QsYYHThwQJdeemmZ1uyvSjPWEydOVPv27fX4449Lkpo1a6bKlSsrJSVFEyZMYNbdQ3xxLGRGpxRCQkKUlJSk5cuXu7QvX75c7dq1K/Y9ycnJRfp/9tlnatmypYKDg8usVn9WmnGWzs7kDBo0SHPnzuX8egm4O87VqlXTN998o+3btzu/hg4dqssvv1zbt29XmzZtvFW6XynN73P79u116NAhnThxwtn2ww8/KCAgQPXr1y/Tev1Zacb6t99+U0CA6yExMDBQ0v/NOODi+eRYWGaXOVtc4dLFf/zjHyYjI8OMHDnSVK5c2ezZs8cYY8zo0aPNgAEDnP0Ll9Q98sgjJiMjw/zjH/9geXkJuDvOc+fONUFBQSYtLc1kZWU5v44fP+6rb8EvuDvOf8Wqq5Jxd5zz8vJM/fr1zS233GK+/fZbs3r1anPppZeaIUOG+Opb8BvujvVbb71lgoKCzIwZM8zu3bvNf/7zH9OyZUvTunVrX30LfiEvL89s27bNbNu2zUgykydPNtu2bXMu4y8Px0KCzkVIS0szsbGxJiQkxLRo0cKsXr3auW3gwIGmY8eOLv1XrVplrrrqKhMSEmIaNmxoZs6c6eWK/ZM749yxY0cjqcjXwIEDvV+4n3H39/nPCDol5+44//e//zVdu3Y1lSpVMvXr1zejRo0yv/32m5er9k/ujvW0adNMfHy8qVSpkomKijJ33nmnOXDggJer9i8rV64879+55eFYaDOGOTkAAGBNXKMDAAAsi6ADAAAsi6ADAAAsi6ADAAAsi6ADAAAsi6ADAAAsi6ADAAAsi6ADAAAsi6ADAH+xatUq2Ww2HT9+XJKUnp6u6tWr+7QmAKVD0AHgdfv379fgwYMVHR2tkJAQxcbGasSIETp69KjXa+nUqZNGjhzp0tauXTtlZWW5PDUcgH8i6ADwqp9++kktW7bUDz/8oHnz5mnXrl2aNWuWVqxYoeTkZB07dszXJSokJESRkZGy2Wy+LgXARSLoAPCqYcOGKSQkRJ999pk6duyoBg0aqFevXvr888918OBBjR07VpJks9m0aNEil/dWr15d6enpztdPPvmkLrvsMoWFhalRo0Z65pln9Mcffzi3jxs3TomJiXrnnXfUsGFDhYeHq3///srLy5MkDRo0SKtXr9bUqVNls9lks9m0Z8+eIqeuirN48WIlJSXJbrerUaNGGj9+vM6cOeOxcQLgGQQdAF5z7NgxLVu2TA8++KAqVarksi0yMlJ33nmn5s+fr5I+a7hq1apKT09XRkaGpk6dqtdff11Tpkxx6bN7924tWrRIS5Ys0ZIlS7R69Wq98MILkqSpU6cqOTlZ9957r7KyspSVlaWYmJgLfu6yZct011136eGHH1ZGRoZee+01paen6/nnny/hSADwFoIOAK/58ccfZYzRFVdcUez2K664Qr/++qt++eWXEu3v6aefVrt27dSwYUP17t1bjz76qN5//32XPgUFBUpPT1dCQoJSUlI0YMAArVixQpIUHh6ukJAQhYWFKTIyUpGRkQoMDLzg5z7//PMaPXq0Bg4cqEaNGqlbt27629/+ptdee61EdQPwniBfFwAAhQpnckJCQkrUf8GCBXrllVe0a9cunThxQmfOnFG1atVc+jRs2FBVq1Z1vo6KitLhw4cvqs4tW7Zo06ZNLjM4DodDp06d0m+//aawsLCL2j8Az2FGB4DXXHLJJbLZbMrIyCh2+3fffac6deqoevXqstlsRU5h/fn6my+//FL9+/dXr169tGTJEm3btk1jx47V6dOnXd4THBzs8tpms6mgoOCivo+CggKNHz9e27dvd3598803+vHHH2W32y9q3wA8ixkdAF5Tq1YtdevWTTNmzNAjjzzicp1Odna23n33XQ0bNkySVKdOHWVlZTm3//jjj/rtt9+cr9etW6fY2FjnxcuStHfvXrdrCgkJkcPhcOs9LVq00Pfff69LLrnE7c8D4F0EHQBeNX36dLVr1049evTQhAkTFBcXp2+//VaPP/64LrvsMj377LOSpGuuuUbTp09X27ZtVVBQoCeffNJlduaSSy7Rvn379N5776lVq1b6+OOPtXDhQrfradiwob766ivt2bNHVapUUc2aNS/4nmeffVbXX3+9YmJidOuttyogIEA7duzQN998owkTJrhdA4Cyw6krAF516aWXatOmTWrUqJFuu+02xcbGqlevXrrsssu0bt06ValSRZL097//XTExMerQoYPuuOMOPfbYYy7Xvtxwww165JFHNHz4cCUmJmr9+vV65pln3K7nscceU2BgoOLj41WnTh3t27fvgu/p0aOHlixZouXLl6tVq1Zq27atJk+erNjYWLc/H0DZspmSruMEgDKSmpqqyZMn67PPPlNycrKvywFgIQQdAOXCW2+9pZycHD388MMKCGCyGYBnEHQAAIBl8c8mAABgWQQdAABgWQQdAABgWQQdAABgWQQdAABgWQQdAABgWQQdAABgWQQdAABgWQQdAABgWf8P1JcOWpOETuIAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "qtls = arange(0, 1.1, 0.1)\n", + "plt.scatter(x=qtls, y=quantile(list(sketch_counter.values()),q=qtls))\n", + "plt.title('Quantiles for sketch supports')\n", + "plt.xlabel('Quantile')\n", + "plt.ylabel('Sketch support')\n", + "plt.yscale('log')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "07291848", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Top sketchesSupport
0[V]nsubj(L)1918
1[S]1891
2[V]nsubj(L)obl(L)979
3[V]883
4[S]cop(L)nsubj:cop(L)853
5[V]nsubj(L)obj(L)760
6[V]advmod(L)nsubj(L)663
7[V]obj(L)594
8[V]obl(L)557
9[V]nsubj(L)obj(L)obl(L)534
10[V]obj(L)obl(L)489
11[S]advmod(L)cop(L)nsubj:cop(L)485
12[V]nsubj(L)obl(P)484
13[V]nsubj(P)477
14[V]advmod(L)nsubj(L)obl(L)476
15[S]nmod(L)423
16[V]nsubj(P)obl(L)418
17[V]nsubj(L)obj(P)396
18[V]advmod(L)nsubj(L)obj(L)366
19[S]cop(L)nsubj:cop(P)365
20[V]advmod(L)357
21[V]nsubj(L)obl(L)obl(L)338
22[X]311
23[V]nsubj(L)xcomp(P)305
24[V]obj(P)291
25[V]aux(L)nsubj(L)283
26[S]cop(L)nsubj:cop(L)obl(L)267
27[S]nummod(L)264
28[V]nsubj(L)xcomp(L)258
29[V]nsubj(L)obl(L)obl(P)248
30[V]advmod(L)obj(L)242
31[V]nsubj(P)obj(L)236
32[V]aux(L)nsubj(L)obj(L)234
33[V]xcomp(P)217
34[S]amod(L)cop(L)nsubj:cop(L)213
35[V]nsubj(P)obl(P)212
36[V]obl(P)207
37[V]aux(L)nsubj(L)obl(L)206
38[V]advmod(L)obl(L)203
39[S]amod(L)203
40[V]obj(P)obl(L)195
41[V]nsubj(L)obj(P)obl(L)189
42[S]cop(L)nmod(L)nsubj:cop(L)188
43[V]nsubj(L)obj(L)obl(P)187
44[V]advmod(L)nsubj(L)obj(L)obl(L)185
45[V]advmod(L)nsubj(L)obl(P)184
46[S]advmod(L)177
47[V]obj(L)obl(P)177
48[V]advmod(L)nsubj(P)173
49[V]aux(L)173
\n", + "
" + ], + "text/plain": [ + " Top sketches Support\n", + "0 [V]nsubj(L) 1918\n", + "1 [S] 1891\n", + "2 [V]nsubj(L)obl(L) 979\n", + "3 [V] 883\n", + "4 [S]cop(L)nsubj:cop(L) 853\n", + "5 [V]nsubj(L)obj(L) 760\n", + "6 [V]advmod(L)nsubj(L) 663\n", + "7 [V]obj(L) 594\n", + "8 [V]obl(L) 557\n", + "9 [V]nsubj(L)obj(L)obl(L) 534\n", + "10 [V]obj(L)obl(L) 489\n", + "11 [S]advmod(L)cop(L)nsubj:cop(L) 485\n", + "12 [V]nsubj(L)obl(P) 484\n", + "13 [V]nsubj(P) 477\n", + "14 [V]advmod(L)nsubj(L)obl(L) 476\n", + "15 [S]nmod(L) 423\n", + "16 [V]nsubj(P)obl(L) 418\n", + "17 [V]nsubj(L)obj(P) 396\n", + "18 [V]advmod(L)nsubj(L)obj(L) 366\n", + "19 [S]cop(L)nsubj:cop(P) 365\n", + "20 [V]advmod(L) 357\n", + "21 [V]nsubj(L)obl(L)obl(L) 338\n", + "22 [X] 311\n", + "23 [V]nsubj(L)xcomp(P) 305\n", + "24 [V]obj(P) 291\n", + "25 [V]aux(L)nsubj(L) 283\n", + "26 [S]cop(L)nsubj:cop(L)obl(L) 267\n", + "27 [S]nummod(L) 264\n", + "28 [V]nsubj(L)xcomp(L) 258\n", + "29 [V]nsubj(L)obl(L)obl(P) 248\n", + "30 [V]advmod(L)obj(L) 242\n", + "31 [V]nsubj(P)obj(L) 236\n", + "32 [V]aux(L)nsubj(L)obj(L) 234\n", + "33 [V]xcomp(P) 217\n", + "34 [S]amod(L)cop(L)nsubj:cop(L) 213\n", + "35 [V]nsubj(P)obl(P) 212\n", + "36 [V]obl(P) 207\n", + "37 [V]aux(L)nsubj(L)obl(L) 206\n", + "38 [V]advmod(L)obl(L) 203\n", + "39 [S]amod(L) 203\n", + "40 [V]obj(P)obl(L) 195\n", + "41 [V]nsubj(L)obj(P)obl(L) 189\n", + "42 [S]cop(L)nmod(L)nsubj:cop(L) 188\n", + "43 [V]nsubj(L)obj(L)obl(P) 187\n", + "44 [V]advmod(L)nsubj(L)obj(L)obl(L) 185\n", + "45 [V]advmod(L)nsubj(L)obl(P) 184\n", + "46 [S]advmod(L) 177\n", + "47 [V]obj(L)obl(P) 177\n", + "48 [V]advmod(L)nsubj(P) 173\n", + "49 [V]aux(L) 173" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(DataFrame({\n", + " 'Top sketches': [sketch for sketch, _ in sketch_counter.most_common(50)],\n", + " 'Support': [count for _, count in sketch_counter.most_common(50)] \n", + "}))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "31de9e87", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Top 50 sketches coverage: 37.58%\n" + ] + } + ], + "source": [ + "# Coverage of the 50 most frequent sketches\n", + "print('Top 50 sketches coverage: {:.2f}%'.format(sum([count for _, count in sketch_counter.most_common(50)])/clauses_count_total * 100))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "d052c52b", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjsAAAHFCAYAAAAUpjivAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy88F64QAAAACXBIWXMAAA9hAAAPYQGoP6dpAABfCUlEQVR4nO3deVhUZf8G8HvYd1R2FAEVFcWdXEtEFHMrUzOXFPWt3PctsxJNQa3MyqW0N7U319JMzQ33DPc0d61ExQVxQZB9mef3x/nNwDiADM5hmOH+XNe55syZZ858zzjM3D7nnOcohBACRERERCbKzNAFEBEREcmJYYeIiIhMGsMOERERmTSGHSIiIjJpDDtERERk0hh2iIiIyKQx7BAREZFJY9ghIiIik8awQ0RERCaNYYdKRaFQlGg6ePDgc9cVFRWFLVu2vHA9kZGRL7QOkseGDRtQv3592NraQqFQ4OzZs4Yu6bnu3r2LyMhInWrdt28fgoODYW9vD4VC8cKfaWOjj7/jglatWgWFQoFTp07pZX0vUt/BgwehUCjw888/66UWKnsMO1QqR48e1Zi6dOkCW1tbreVNmzZ97rr0/SVJ5ceDBw8wcOBA1KxZE7t27cLRo0dRu3ZtQ5f1XHfv3sWsWbNKHHaEEOjTpw8sLS2xdetWHD16FCEhIfIWWc6U97/j8l4fycvC0AWQcWrZsqXGfTc3N5iZmWktpxeTnp4OOzs7Q5dRateuXUNOTg7efvttk/7xv3v3Lh4/fow33ngDYWFhxbY19n9TImPEnh2SzePHjzFy5EhUrVoVVlZWqFGjBmbMmIGsrCx1G4VCgbS0NKxevVq966tdu3YApF6BkSNHol69enBwcIC7uzvat2+P33//vdQ1ZWVlYfbs2QgMDISNjQ1cXFwQGhqK2NhYdZvMzExMnz4d/v7+sLKyQtWqVTFq1Cg8efJE3aZHjx7w9fWFUqnUeo0WLVpo9GgJIbB06VI0btwYtra2qFy5Mnr37o3r169rPK9du3YICgrC4cOH0bp1a9jZ2WHo0KEApF1B4eHh8PLygq2tLQIDA/H+++8jLS1N6/VXrFiB2rVrw9raGvXq1cPatWsxePBg+Pn5abTLzs7GnDlzULduXVhbW8PNzQ1DhgzBgwcPSvRebt26Fa1atYKdnR0cHR3RsWNHHD16VP344MGD8fLLLwMA3nrrLY1/28Kodlvs378f7777LlxcXODk5IRBgwYhLS0NCQkJ6NOnDypVqgQvLy9MnjwZOTk5GusoyWcOAH766Se0aNECzs7OsLOzQ40aNdTv9cGDB/HSSy8BAIYMGaL+XBa1mzQyMhLVqlUDAEybNg0KhUL9XkdGRkKhUODPP/9E7969UblyZdSsWRNAyT8XQggsWLAAvr6+sLGxQdOmTbFz5060a9dO4/1UvX83btzQeL5qF8yzu5T37t2LsLAwODk5wc7ODm3atMG+ffu0tk2hUODixYvo168fnJ2d4eHhgaFDhyI5OVndrri/46IsW7YMjRo1goODAxwdHVG3bl188MEHxT7n3r17aNasGQICAvD3338DAFJSUjB58mSNv9fx48dr/G08r747d+7gvffeg4+PD6ysrODt7Y3evXvj/v37Gq+fk5ODGTNmwNvbG05OTujQoQOuXr2qVWdJ3tsHDx6oX1P199emTRvs3bu32PeASkkQ6UFERISwt7dX38/IyBANGzYU9vb24rPPPhN79uwRH330kbCwsBBdunRRtzt69KiwtbUVXbp0EUePHhVHjx4VFy9eFEIIceXKFTFixAixfv16cfDgQbF9+3bxn//8R5iZmYkDBw5ovD4AMXPmzGJrzMnJEaGhocLCwkJMnjxZ7NixQ2zdulV88MEHYt26dUIIIZRKpejUqZOwsLAQH330kdizZ4/47LPPhL29vWjSpInIzMwUQgjx66+/CgAiJiZG4zUuX74sAIivvvpKvezdd98VlpaWYtKkSWLXrl1i7dq1om7dusLDw0MkJCSo24WEhIgqVaoIHx8f8fXXX4sDBw6IQ4cOCSGE+OSTT8QXX3whfvvtN3Hw4EHxzTffCH9/fxEaGqrx+t9++60AIHr16iW2b98u1qxZI2rXri18fX2Fr6+vul1eXp549dVXhb29vZg1a5aIiYkR3333nahataqoV6+eSE9PL/a9XLNmjQAgwsPDxZYtW8SGDRtEs2bNhJWVlfj999+FEEL8888/YsmSJQKAiIqK0vi3LczKlSsFAOHv7y8mTZok9uzZI+bPny/Mzc1Fv379RNOmTcWcOXNETEyMmDZtmgAgPv/8c/XzS/qZi42NFQqFQvTt21fs2LFD7N+/X6xcuVIMHDhQCCFEcnKyupYPP/xQ/bmMj48vtO74+HixefNmAUCM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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "coverage = cumsum(sorted(list(sketch_counter.values()), reverse=True))\n", + "coverage = coverage/coverage[-1] * 100\n", + "\n", + "plt.plot(range(1, len(coverage)+1), coverage, color='blue')\n", + "plt.axhline(y=70, color='r', linestyle='--')\n", + "plt.axvline(x=argmax(coverage >= 70) + 1, color='r', linestyle='--')\n", + "plt.title('Total coverage of most frequent sketches')\n", + "plt.xlabel('Number of sketches')\n", + "plt.ylabel('Total coverage')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "5c75ecff", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAk0AAAHFCAYAAADv8c1wAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy88F64QAAAACXBIWXMAAA9hAAAPYQGoP6dpAABG3klEQVR4nO3dfVwVdf7//+cR5IgIZ0EEJNHMC5JQ27RVtPIaNFHTysqW1c1FN1MzZStrS3I3Na/bdddcUykvwt28yL4koZluJnjBRkqauaWJN0FM4aDEAuL8/ujj/DyiNhh0UB/3221uN87Ma2ZeZzjJs/dcHJthGIYAAABwVXXc3QAAAMD1gNAEAABgAaEJAADAAkITAACABYQmAAAACwhNAAAAFhCaAAAALCA0AQAAWEBoAgAAsIDQBNSgpKQk2Ww2c/L09FTjxo316KOP6tChQzW67/379ysxMVFHjhypUq9W6y+WmJgom82m7777rsrr/tg2r3dV/T1YMWLECN16663Vtj2rbDabEhMTf/b9ArUFoQn4GSxbtkzp6enavHmzxo4dqw0bNuiee+5RQUFBje1z//79euWVV6r1jzWqriZ+Dy+99JLWrVtXbdsDYI2nuxsAbgaRkZHq2LGjJKl79+6qqKjQlClTtH79ev32t791c3e43rRo0cLdLQA3JUaaADe4EKBOnDjhMn/Dhg2KiopS/fr15evrqz59+ig9Pb3S+tu3b1evXr3k6+ur+vXrq0uXLkpJSTGXJyUl6eGHH5Yk9ejRwzw9mJSUVKU+N23apEGDBqlJkyaqV6+eWrZsqdGjR1/xNFxOTo6GDBkiPz8/ORwO/frXv9bJkycr1a1evVpRUVHy8fFRgwYNFBMTo88++6xKvV1s586dGjBggBo2bKh69eqpRYsWmjBhgkvNjx0z6cqnBC936vLWW29VbGysUlNTddddd8nb21u33367li5d6rLe1X4Pn332mWJjYxUUFCS73a7Q0FD1799fx44du+r7vdzpOZvNprFjx2r58uVq06aN6tevr/bt2+v//b//9yNH7weFhYWaNGmSbrvtNtntdgUFBen+++/Xl19+ecV1Tp48qTFjxigiIkINGjRQUFCQevbsqU8++cSlbuvWrbLZbNq6davL/CNHjlT6XH7zzTd69NFHFRoaKrvdruDgYPXq1UtZWVku61r5DFndFmAVoQlwg8OHD0uSWrdubc5btWqVBg0aJD8/P73zzjtasmSJCgoK1L17d23fvt2s27Ztm3r27Cmn06klS5bonXfeka+vrwYMGKDVq1dLkvr3769p06ZJkv72t78pPT1d6enp6t+/f5X6/PrrrxUVFaWFCxcqLS1NL7/8snbu3Kl77rlH5eXlleoHDx6sli1b6t1331ViYqLWr1+vmJgYl9pp06bpscceU0REhP75z39q+fLlOnPmjO69917t37+/Sv1J0ocffqh7771XR48e1dy5c7Vx40b98Y9/dAmkVo7Ztfj88881adIkPfPMM3rvvffUrl07jRw5Uv/+978lXf33UFxcrD59+ujEiRP629/+pk2bNmn+/Plq2rSpzpw5c039pKSkaMGCBZo6darWrFmjgIAADR48WN98881V1ztz5ozuueceLVq0SL/97W/1/vvv64033lDr1q2Vm5t7xfVOnz4tSZoyZYpSUlK0bNky3XbbberevXulgGTV/fffr8zMTM2cOVObNm3SwoUL9ctf/lKFhYVmjdXPkJVtAVViAKgxy5YtMyQZGRkZRnl5uXHmzBkjNTXVCAkJMe677z6jvLzcMAzDqKioMEJDQ422bdsaFRUV5vpnzpwxgoKCjC5dupjzOnfubAQFBRlnzpwx5507d86IjIw0mjRpYpw/f94wDMP417/+ZUgyPv744yr1evjw4csuP3/+vFFeXm58++23hiTjvffeM5dNmTLFkGQ888wzLuusXLnSkGSsWLHCMAzDOHr0qOHp6WmMGzfOpe7MmTNGSEiIMXTo0Erb/DEtWrQwWrRoYZSUlFyxxuoxu9I+L3dsmjVrZtSrV8/49ttvzXklJSVGQECAMXr0aHPelX4Pe/bsMSQZ69ev/9H3eKnhw4cbzZo1c5knyQgODjaKiorMeXl5eUadOnWM6dOnX3V7U6dONSQZmzZtumqdJGPKlClXXH7u3DmjvLzc6NWrlzF48GBz/scff3zZY3D48GFDkrFs2TLDMAzju+++MyQZ8+fPv+I+rH6GrGwLqCpGmoCfQefOnVW3bl35+vqqb9++8vf313vvvSdPzx8uKzx48KCOHz+uuLg41anz//9n2aBBAz344IPKyMjQ999/r+LiYu3cuVMPPfSQGjRoYNZ5eHgoLi5Ox44d08GDB6ut7/z8fP3+979XWFiYPD09VbduXTVr1kySdODAgUr1jz/+uMvroUOHytPTUx9//LGkH0aFzp07p9/85jc6d+6cOdWrV0/dunWr8ujEV199pa+//lojR45UvXr1LltTk8fszjvvVNOmTc3X9erVU+vWrfXtt9/+6LotW7aUv7+/nnvuOb3xxhvXNMp2qR49esjX19d8HRwcrKCgoB/tZ+PGjWrdurV69+5d5X2+8cYbuuuuu1SvXj3zM/LRRx9d9vPxYwICAtSiRQvNmjVLc+fO1Weffabz58+71Fj9DFnZFlBVhCbgZ/D2229r9+7d2rJli0aPHq0DBw7oscceM5efOnVKktS4ceNK64aGhur8+fMqKChQQUGBDMO4Yt3F2/qpzp8/r+joaK1du1bPPvusPvroI+3atUsZGRmSpJKSkkrrhISEuLz29PRUw4YNzZ4unDK7++67VbduXZdp9erVVX5kwYXrpZo0aXLFmpo8Zg0bNqw0z263X/bYXMrhcGjbtm2688479cILL+iOO+5QaGiopkyZctlTnzXZz8mTJ696DK9k7ty5evLJJ9WpUyetWbNGGRkZ2r17t/r27WvpGFzKZrPpo48+UkxMjGbOnKm77rpLjRo10vjx481TllY/Q1a2BVQVd88BP4M2bdqYF3/36NFDFRUVevPNN/Xuu+/qoYceMv/YXe76kePHj6tOnTry9/eXYRiqU6fOFeskKTAwsFp6zs7O1ueff66kpCQNHz7cnP/f//73iuvk5eXplltuMV+fO3dOp06dMt/fhd7effddc8Tqp2jUqJEkXfXCaX9/f8vH7MJoVWlpqex2u1lXnc+fuljbtm2VnJwswzC0d+9eJSUlaerUqfL29tbzzz9fI/u8nEaNGv3oxeeXs2LFCnXv3l0LFy50mX9pKLn4uF7scse1WbNmWrJkiaQfRhL/+c9/KjExUWVlZXrjjTeq9Bn6sW0BVcVIE+AGM2fOlL+/v15++WWdP39e4eHhuuWWW7Rq1SoZhmHWFRcXa82aNeYddT4+PurUqZPWrl3r8n/y58+f14oVK9SkSRPz4vILf/Sv5f/4JZl3kV0cHiRp0aJFV1xn5cqVLq//+c9/6ty5c+revbskKSYmRp6envr666/VsWPHy05V0bp1a7Vo0UJLly6t9Af5gqocswt3pO3du9dlG++//36V+rqYld+DzWZT+/btNW/ePP3iF7/Qf/7zn2ve37Xo16+fvvrqK23ZsqVK69lstkqfj71791a64/NKx3XDhg1X3X7r1q31xz/+UW3btjWPybV+hi63LaCqGGkC3MDf31+TJ0/Ws88+q1WrVunXv/61Zs6cqccff1yxsbEaPXq0SktLNWvWLBUWFmrGjBnmutOnT1efPn3Uo0cPJSQkyMvLS3//+9+VnZ2td955xww7kZGRkqR//OMf8vX1Vb169dS8efPLnsK5nNtvv10tWrTQ888/L8MwFBAQoPfff1+bNm264jpr166Vp6en+vTpoy+++EIvvfSS2rdvr6FDh0r64Y/n1KlT9eKLL+qbb74xr+86ceKEdu3aJR8fH73yyitVOpZ/+9vfNGDAAHXu3FnPPPOMmjZtqqNHj+rDDz80Q5zVY3b//fcrICBAI0eO1NSpU+Xp6amkpCTl5ORUqaeLXen3kJ6err///e964IEHdNttt8kwDK1du1aFhYXq06fPNe/vWkyYMEGrV6/WoEGD9Pzzz+tXv/qVSkpKtG3bNsXGxqpHjx6XXS82NlZ/+tOfNGXKFHXr1k0HDx7U1KlT1bx5c507d86sCwkJUe/evTV9+nT5+/urWbNm+uijj7R27VqX7e3du1djx47Vww8/rFatWsnLy0tbtmzR3r17zZE3q58hK9sCqsyNF6EDN7wLd13t3r270rKSkhKjadOmRqtWrYxz584ZhmEY69evNzp16mTUq1fP8PHxMXr16mV8+umnldb95JNPjJ49exo+Pj6Gt7e30blzZ+P999+vVDd//nyjefPmhoeHh8tdSlfr9eI7xPbv32/06dPH8PX1Nfz9/Y2HH37YOHr0aKW7qC7cdZaZmWkMGDDAaNCggeHr62s89thjxokTJyrta/369UaPHj0MPz8/w263G82aNTMeeughY/PmzZW2aUV6errRr18/w+FwGHa73WjRokWlO/msHrNdu3YZXbp0MXx8fIxbbrnFmDJlivHmm29e9u65/v37V1q/W7duRrdu3VzmXe738OWXXxqPPfaY0aJFC8Pb29twOBzGr371KyMpKelH3++V7p576qmnKtU2a9bMGD58+I9us6CgwHj66aeNpk2bGnXr1jWCgoKM/v37G19++aXLPi7+vZeWlhoJCQnGLbfcYtSrV8+46667jPXr11+2v9zcXOOhhx4yAgICDIfDYfz617827yC8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/v0uXzlSUVGh1NRU3XLLLdXbHQAAQC1hOTTdeeedstlsstlslz0N5+3trb/+9a/V2hwAAEBtYTk0HT58WIZh6LbbbtOuXbvUqFEjc5mXl5eCgoJcnsANAABwI7Ecmpo1ayZJOn/+fI01AwAAUFtd0xf2fvXVV9q6davy8/MrhaiXX365WhoDAACoTaocmhYvXqwnn3xSgYGBCgkJkc1mM5fZbDZCEwAAuCFVOTT9+c9/1quvvqrnnnuuJvoBAAColar8nKaCggI9/PDDNdELAABArVXl0PTwww8rLS2tJnoBAACotap8eq5ly5Z66aWXlJGRobZt26pu3bouy8ePH19tzQEAANQWNsMwjKqs0Lx58ytvzGbTN99885Obul4VFRXJ4XDI6XTKz8+vWrd96/Mp1bo9XH+OzOjv7hYA4IZk9e93lUeaDh8+/JMaAwAAuB5V+ZomAACAm1GVR5qeeOKJqy5funTpNTcDAABQW1U5NBUUFLi8Li8vV3Z2tgoLCy/7Rb4AAAA3giqHpnXr1lWad/78eY0ZM0a33XZbtTQFAABQ21TLNU116tTRM888o3nz5lXH5gAAAGqdarsQ/Ouvv9a5c+eqa3MAAAC1SpVPz02cONHltWEYys3NVUpKioYPH15tjQEAANQmVQ5Nn332mcvrOnXqqFGjRpozZ86P3lkHAABwvapyaPr4449rog8AAIBarcqh6YKTJ0/q4MGDstlsat26tRo1alSdfQEAANQqVb4QvLi4WE888YQaN26s++67T/fee69CQ0M1cuRIff/99zXRIwAAgNtVOTRNnDhR27Zt0/vvv6/CwkIVFhbqvffe07Zt2zRp0qSa6BEAAMDtqnx6bs2aNXr33XfVvXt3c979998vb29vDR06VAsXLqzO/gAAAGqFKo80ff/99woODq40PygoiNNzAADghlXl0BQVFaUpU6bof//7nzmvpKREr7zyiqKioqq1OQAAgNqiyqfnXn/9dfXt21dNmjRR+/btZbPZlJWVpXr16unDDz+siR4BAADcrsqhKTIyUocOHdKKFSv05ZdfyjAMPfroo3r88cfl7e1dEz0CAAC43TU9p8nb21vx8fHV3QsAAECtVeVrmqZPn66lS5dWmr906VK99tpr1dIUAABAbVPl0LRo0SLdfvvtlebfcccdeuONN6q0renTp+vuu++Wr6+vgoKC9MADD+jgwYMuNYZhKDExUaGhofL29lb37t31xRdfuNSUlpZq3LhxCgwMlI+PjwYOHKhjx4651BQUFCguLk4Oh0MOh0NxcXEqLCx0qTl69KgGDBggHx8fBQYGavz48SorK6vSewIAADemKoemvLw8NW7cuNL8Ro0aKTc3t0rb2rZtm5566illZGRo06ZNOnfunKKjo1VcXGzWzJw5U3PnztWCBQu0e/duhYSEqE+fPjpz5oxZM2HCBK1bt07Jycnavn27zp49q9jYWFVUVJg1w4YNU1ZWllJTU5WamqqsrCzFxcWZyysqKtS/f38VFxdr+/btSk5O1po1a3hgJwAAkHQN1zSFhYXp008/VfPmzV3mf/rppwoNDa3StlJTU11eL1u2TEFBQcrMzNR9990nwzA0f/58vfjiixoyZIgk6a233lJwcLBWrVql0aNHy+l0asmSJVq+fLl69+4tSVqxYoXCwsK0efNmxcTE6MCBA0pNTVVGRoY6deokSVq8eLGioqJ08OBBhYeHKy0tTfv371dOTo75PubMmaMRI0bo1VdflZ+fX1UPFQAAuIFUeaTpd7/7nSZMmKBly5bp22+/1bfffqulS5fqmWee+ckXhzudTklSQECAJOnw4cPKy8tTdHS0WWO329WtWzft2LFDkpSZmany8nKXmtDQUEVGRpo16enpcjgcZmCSpM6dO8vhcLjUREZGugS/mJgYlZaWKjMz87L9lpaWqqioyGUCAAA3piqPND377LM6ffq0xowZY17vU69ePT333HOaPHnyNTdiGIYmTpyoe+65R5GRkZJ+OBUoqdITyIODg/Xtt9+aNV5eXvL3969Uc2H9vLw8BQUFVdpnUFCQS82l+/H395eXl5dZc6np06frlVdeqepbBQAA16EqjzTZbDa99tprOnnypDIyMvT555/r9OnTevnll39SI2PHjtXevXv1zjvvXHafFzMMo9K8S11ac7n6a6m52OTJk+V0Os0pJyfnqj0BAIDrV5VD0wUNGjTQ3XffrcjISNnt9p/UxLhx47RhwwZ9/PHHatKkiTk/JCREkiqN9OTn55ujQiEhISorK1NBQcFVa06cOFFpvydPnnSpuXQ/BQUFKi8vv+x37Uk/nCr08/NzmQAAwI3pmkNTdTAMQ2PHjtXatWu1ZcuWSheXN2/eXCEhIdq0aZM5r6ysTNu2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sHbt2qU777zT3a3ddBhpQq3k6emp3/zmN0pKStLFuf5f//qXysrK9Pjjj7uxO9wsGjRooAYNGmj9+vUqLS11dzu4SY0bN07t27fXb37zG40aNUovv/wygclNGGlCrfXll1+qTZs22rJli3r06CFJ6tatm2655RatWrXKzd3hZrFmzRrFx8erpKREd911l7p166ZHH31U7dq1c3druIlc+Pewbdu2+s9//iNPT77Qwx0YaUKtdfvtt6tLly5aunSpJOnrr7/WJ598oieeeMLNneFm8uCDD+r48ePasGGDYmJitHXrVt11111KSkpyd2u4iSxdulT169fX4cOHdezYMXe3c9NipAm12tKlSzV27Fjl5eVp5syZWrlypb755hvZbDZ3t4ab2O9+9ztt2rTpsjcqANUtPT1d9913nzZu3KiZM2eqoqJCmzdv5t9BN2CkCbXa0KFD5eHhoVWrVumtt97Sb3/7W/6hgNtFRESouLjY3W3gJlBSUqLhw4dr9OjR6t27t958803t3r1bixYtcndrNyVGmlDr/e53v9PatWvldDp1+PBhNW3a1N0t4SZx6tQpPfzww3riiSfUrl07+fr6as+ePRo3bpz69++vJUuWuLtF3OCefvpppaSk6PPPP5ePj48kafHixZo4caL27dunW2+91b0N3mQITaj10tPT1aVLF0VHR+vDDz90dzu4iZSWlioxMVFpaWn6+uuvVV5errCwMD388MN64YUX5O3t7e4WcQPbtm2bevXqpa1bt+qee+5xWRYTE6Nz585xmu5nRmgCAACwgGuaAAAALCA0AQAAWEBoAgAAsIDQBAAAYAGhCQAAwAJCEwAAgAWEJgAAAAsITQBQRd27d9eECRMs12/dulU2m02FhYU/ab+33nqr5s+f/5O2AeDaEZoA1HojRoyQzWaTzWaTp6enmjZtqieffFIFBQXVvp8HHnigWrcJ4MZBaAJwXejbt69yc3N15MgRvfnmm3r//fc1ZswYd7cF4CZCaAJwXbDb7QoJCVGTJk0UHR2tRx55RGlpaeby8+fPa+rUqWrSpInsdrvuvPNOpaamumxj37596tmzp7y9vdWwYUONGjVKZ8+elSQlJibqrbfe0nvvvWeOam3dutVSbytWrFDHjh3l6+urkJAQDRs2TPn5+ZXqPv30U7Vv31716tVTp06dtG/fPpflO3bs0H333Sdvb2+FhYVp/PjxKi4uruKRAlBTCE0ArjvffPONUlNTVbduXXPe66+/rjlz5mj27Nnau3evYmJiNHDgQB06dEiS9P3336tv377y9/fX7t279a9//UubN2/W2LFjJUkJCQkaOnSoOaKVm5urLl26WOqnrKxMf/rTn/T5559r/fr1Onz4sEaMGFGp7g9/+INmz56t3bt3KygoSAMHDlR5ebmkHwJdTEyMhgwZor1792r16tXavn272R+AWsAAgFpu+PDhhoeHh+Hj42PUq1fPkGRIMubOnWvWhIaGGq+++qrLenfffbcxZswYwzAM4x//+Ifh7+9vnD171lyekpJi1KlTx8jLyzP3M2jQoB/tp1u3bsbTTz99xeW7du0yJBlnzpwxDMMwPv74Y0OSkZycbNacOnXK8Pb2NlavXm0YhmHExcUZo0aNctnOJ598YtSpU8coKSkxDMMwmjVrZsybN+9H+wNQMxhpAnBd6NGjh7KysrRz506NGzdOMTExGjdunCSpqKhIx48fV9euXV3W6dq1qw4cOCBJOnDggNq3by8fHx+X5efPn9fBgwd/Um+fffaZBg0apGbNmsnX11fdu3eXJB09etSlLioqyvw5ICBA4eHhZn+ZmZlKSkpSgwYNzCkmJkbnz5/X4cOHf1J/AKoHoQnAdcHHx0ctW7ZUu3bt9Je//EWlpaV65ZVXXGpsNpvLa8MwzHkX/3ypK823ori4WNHR0WrQoIFWrFih3bt3a926dZJ+OG33Yy7s+/z58xo9erSysrLM6fPPP9ehQ4fUokWLa+4PQPUhNAG4Lk2ZMkWzZ8/W8ePH5efnp9DQUG3fvt2lZseOHWrTpo0kKSIiQllZWS4XVn/66aeqU6eOWrduLUny8vJSRUVFlfr48ssv9d1332nGjBm69957dfvtt1/2InBJysjIMH8uKCjQV199pdtvv12SdNddd+mLL75Qy5YtK01eXl5V6glAzSA0Abgude/eXXfccYemTZsm6YeLrF977TWtXr1aBw8e1PPPP6+srCw9/fTTkqTHH39c9erV0/Dhw5Wdna2PP/5Y48aNU1xcnIKDgyX98PDIvXv36uDBg/ruu+/Mi7SvpmnTpvLy8tJf//pXffPNN9qwYYP+9Kc/XbZ26tSp+uijj5Sdna0RI0YoMDDQfC7Uc889p/T0dD311FPKysrSoUOHtGHDBvMUJAD3IzQBuG5NnDhRixcvVk5OjsaPH69JkyZp0qRJatu2rVJTU7Vhwwa1atVKklS/fn19+OGHOn36tO6++2499NBD6tWrlxYsWGBuLz4+XuHh4erYsaMaNWqkTz/99Ed7aNSokZKSkvSvf/1LERERmjFjhmbPnn3Z2hkzZujpp59Whw4dlJubqw0bNpijSO3atdO2bdt06NAh3XvvvfrlL3+pl156SY0bN66GIwWgOtgMwzDc3QQAAEBtx0gTAACABYQmAAAACwhNAAAAFhCaAAAALCA0AQAAWEBoAgAAsIDQBAAAYAGhCQAAwAJCEwAAgAWEJgAAAAsITQAAABYQmgAAACz4/wAB2sUKYGbL1QAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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596b48ePG4sXLza8vb2NcePGuRyvo0aNMho1auTynhiGYbzwwguGJGPv3r2GYRjG8ePHDUnGc889V2k7PXv2NK6//nqjsLDwgr2c+32MHj3aZXzGjBmGJCM/P98wjOofb99//70hyZg9e/ZF3wOgKpwhAmqga9eu8vLykr+/v/r27avAwEC999578vT86XK8/fv36+jRo0pOTtZ11/2//7waNWqk++67T7m5ufrxxx91+vRpbdmyRffff78aNWpk1nl4eCg5OVlHjhzR/v37a63vwsJC/fa3v1VkZKQ8PT3l5eWlqKgoSdK+ffsq1T/88MMu00OGDJGnp6c2bNgg6aezOWfPntUjjzyis2fPmq8GDRqoW7duNb7I9l//+pe+/vprpaSkqEGDBlXW1OV71qlTJzVr1sycbtCggdq0aaNvv/32ksu2atVKgYGB+v3vf69XX331ss6Ona9Hjx7y9/c3p0NDQxUSEnLJfn71q19p586dGj16tD766CMVFxdfsPb555/X8OHDNX36dL300ksux+s///lP9ejRQxERES6/3379+kn66azSxfz444/Kzs7WkCFDzI/VLmbAgAEu0zExMZJk7m91j7egoCC1bNlSf/nLXzRz5kx98cUXqqiouOT2AYmPzIAaefPNN7Vt2zZ9/PHHGjVqlPbt26eHHnrInH/ixAlJP32ccL6IiAhVVFSoqKhIRUVFMgzjgnU/X9cvVVFRoT59+ujdd9/Vk08+qfXr12vr1q3Kzc2VJJWWllZaJiwszGXa09NTjRs3Nns69zHWbbfdJi8vL5fXihUranxL97nrk5o2bXrBmrp8zxo3blxpzMfHp8r35nx2u13Z2dnq1KmTnnrqKd18882KiIjQc889V+XHkXXZz+TJk/XCCy8oNzdX/fr1U+PGjZWQkFDlbe1LlizRDTfcoAcffLDSvGPHjun999+v9Lu9+eabJemSv9+ioiKVl5df9Pf5c+fvr4+Pj6T/d2xW93iz2Wxav369EhMTNWPGDN16661q0qSJxo0bd9kfX8I6uMsMqIF27dqZF1L36NFD5eXlev311/WPf/xD999/v/kPe35+fqVljx49quuuu06BgYEyDEPXXXfdBeskKTg4uFZ63rNnj3bu3KmMjAwNGzbMHP/3v/99wWUKCgp0ww03mNNnz57ViRMnzP0719s//vEP80zT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jdb1+OOPa9myZXrvvffk7+9vXs9jt9vl6+urU6dOKS0tTffdd5/Cw8N16NAhPfXUUwoODta9995r1qakpGjChAlq3LixgoKCNHHiRHXo0MG866xdu3bq27evRowYoXnz5kmSRo4cqaSkJO4wAwAANT9DdCG//vWva/w9ZnPnzpXD4VD37t0VHh5uvlasWCFJ8vDw0O7duzVw4EC1adNGw4YNU5s2bZSTkyN/f39zPbNmzdI999yjIUOG6Pbbb1fDhg31/vvvy8PDw6xZunSpOnTooD59+qhPnz6KiYnR4sWLa2fnAQDAVa3WLqrOyclRgwYNarSMYRgXne/r66uPPvrokutp0KCBXnnlFb3yyisXrAkKCtKSJUtq1B8AALCGGgei8+/sMgxD+fn52r59u5555plaawwAAOBKqXEgstvtLtPXXXed2rZtq6lTp7o8HBEAAOBqUeNA9MYbb9RFHwAAAG5z2dcQ7dixQ/v27ZPNZlP79u11yy231GZfAAAAV0yNA1FhYaEefPBBbdy4Uddff70Mw5DD4VCPHj20fPlyNWnSpC76BAAAqDM1vu1+7NixKi4u1t69e/XDDz+oqKhIe/bsUXFxscaNG1cXPQIAANSpGp8hyszM1Lp169SuXTtzrH379vrrX//KRdUAAOCqVOMzRBUVFfLy8qo07uXlpYqKilppCgAA4EqqcSDq2bOnnnjiCR09etQc+89//qPf/e53SkhIqNXmAAAAroQaB6I5c+aopKREzZs3V8uWLdWqVSu1aNFCJSUlF31SNAAAQH1V42uIIiMj9fnnnysrK0tfffWVDMNQ+/btzS9SBQAAuNpc9nOIevfurd69e9dmLwAAAG5R44/Mxo0bp5dffrnS+Jw5c5SamlobPQEAAFxRNQ5E77zzjm6//fZK4/Hx8frHP/5RK00BAABcSTUORCdOnKj0Ba+SFBAQoO+//75WmgIAALiSahyIWrVqpczMzErjH374oW688cZaaQoAAOBKqvFF1ePHj9eYMWN0/Phx9ezZU5K0fv16vfjii5o9e3Zt9wcAAFDnahyIfvOb38jpdOr555/XH//4R0lS8+bNNXfuXD3yyCO13iAAAEBdu6zb7h977DE99thjOn78uHx9fdWoUaPa7gsAAOCKueznEElSkyZNaqsPAAAAt6nxRdUAAADXGgIRAACwPAIRAACw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V0yNzxAlJibqm2++qTT+zjvv6OGHH66VpgAAAK6kGgeixx57TAkJCcrPzzfHVqxYoUceeUQZGRm12RsAAMAVUeOPzJ599lmdOHFCvXr10qeffqrMzEw9+uijWrx4se6777666BEAAKBO1TgQSdJLL72k5ORkde3aVf/5z3/097//XQMHDqzt3gAAAK6IagWi1atXVxq75557lJ2drYceekg2m82sGTBgQO12CAAAUMeqFYgudufYwoULtXDhQkk/PaSxvLy8VhoDAAC4UqoViCoqKuq6DwAAALep8V1mAAAA15pqB6ItW7boww8/dBl788031aJFC4WEhGjkyJFyOp213iAAAEBdq3YgSktL065du8zp3bt3KyUlRb169dKkSZP0/vvvKz09vU6aBAAAqEvVDkR5eXlKSEgwp5cvX67Y2FjNnz9f48eP18svv6y33nqrTpoEAACoS9UOREVFRQoNDTWns7Oz1bdvX3P6tttu0+HDh2u3OwAAgCug2oEoNDRUBw8elCSVlZXp888/V1xcnDm/pKREXl5etd8hAABAHat2IOrbt68mTZqkTz/9VJMnT1bDhg115513mvN37dqlli1b1kmTAAAAdanaX93xpz/9SYMGDVK3bt3UqFEjLVq0SN7e3ub8hQsXqk+fPnXSJAAAQF2qdiBq0qSJPv30UzkcDjVq1EgeHh4u899++201atSo1hsEAACoazX+cle73V7leFBQ0C9uBgAAwB14UjUAALA8AhEAALA8AhEAALA8AhEAALA8twaiTz75RP3791dERIRsNptWrVrlMn/48OGy2Wwur65du7rUOJ1OjR07VsHBwfLz89OAAQN05MgRl5qioiIlJyfLbrfLbrcrOTlZJ0+erOO9AwAAVwu3BqLTp0+rY8eOmjNnzgVr+vbtq/z8fPP1wQcfuMxPTU3VypUrtXz5cm3atEmnTp1SUlKSysvLzZqhQ4cqLy9PmZmZyszMVF5enpKTk+tsvwAAwNWlxrfd16Z+/fqpX79+F63x8fFRWFhYlfMcDocWLFigxYsXq1evXpKkJUuWKDIyUuvWrVNiYqL27dunzMxM5ebmKjY2VpI0f/58xcXFaf/+/Wrbtm3t7hQAALjq1PtriDZu3KiQkBC1adNGI0aMUGFhoTlvx44dOnPmjMsTsiMiIhQdHa3NmzdLknJycmS3280wJEldu3aV3W43a6ridDpVXFzs8gIAANemeh2I+vXrp6VLl+rjjz/Wiy++qG3btqlnz55yOp2SpIKCAnl7eyswMNBludDQUBUUFJg1ISEhldYdEhJi1lQlPT3dvObIbrcrMjKyFvcMAADUJ279yOxSHnjgAfPn6OhodenSRVFRUVqzZo0GDRp0weUMw5DNZjOnf/7zhWrON3nyZI0fP96cLi4uJhQBAHCNqtdniM4XHh6uqKgoHThwQJIUFhamsrIyFRUVudQVFhYqNDTUrDl27FildR0/ftysqYqPj48CAgJcXgAA4Np0VQWiEydO6PDhwwoPD5ckde7cWV5eXsrKyjJr8vPztWfPHsXHx0uS4uLi5HA4tHXrVrNmy5YtcjgcZg0AALA2t35kdurUKf373/82pw8ePKi8vDwFBQUpKChIaWlpuu+++xQeHq5Dhw7pqaeeUnBwsO69915JP33RbEpKiiZMmKDGjRsrKChIEydOVIcOHcy7ztq1a6e+fftqxIgRmjdvniRp5MiRSkpK4g4zAAAgyc2BaPv27erRo4c5fe6anWHDhmnu3LnavXu33nzzTZ08eVLh4eHq0aOHVqxYIX9/f3OZWbNmydPTU0OGDFFpaakSEhKUkZEhDw8Ps2bp0qUaN26ceTfagAEDLvrsIwAAYC02wzAMdzdxNSguLpbdbpfD4aj164maT1pTq+vD1efQ9Lvdun2OQbj7GATqSnX/fl9V1xABAADUBQIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPAIRAACwPLcGok8++UT9+/dXRESEbDabVq1a5TLfMAylpaUpIiJCvr6+6t69u/bu3etS43Q6NXbsWAUHB8vPz08DBgzQkSNHXGqKioqUnJwsu90uu92u5ORknTx5so73DgAAXC3cGohOnz6tjh07as6cOVXOnzFjhmbOnKk5c+Zo27ZtCgsLU+/evVVSUmLWpKamauXKlVq+fLk2bdqkU6dO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gwdr69at6tSpk7tbsxzOEOGK8/T01COPPKKMjAz9PI+//fbbKisr08MPP+zG7mAVjRo1UqNGjbRq1So5nU53twOLGjt2rDp27KhHHnlEI0eO1LPPPksYchPOEMEtvvrqK7Vr104ff/yxevToIUnq1q2bbrjhBi1btszN3cEq3nnnHY0YMUKlpaW69dZb1a1bNz344IOKiYlxd2uwkHP/Hnbo0EGff/65PD0t9SUS9QZniOAWN910k+Lj47Vw4UJJ0tdff61PP/1Uv/nNb9zcGazkvvvu09GjR7V69WolJiZq48aNuvXWW5WRkeHu1mAhCxcuVMOGDXXw4EEdOXLE3e1YFmeI4DYLFy7UmDFjVFBQoBkzZmjp0qX65ptvZLPZ3N0aLOzRRx9VVlZWlRf9A7UtJydHd911lz788EPNmDFD5eXlWrduHf8OugFniOA2Q4YMkYeHh5YtW6ZFixbpf//3f/lHAG7Xvn17nT592t1twAJKS0s1bNgwjRo1Sr169dLrr7+ubdu2ad68ee5uzZI4QwS3evTRR/Xuu+/K4XDo4MGDatasmbtbgkWcOHFCgwcP1m9+8xvFxMTI399f27dv19ixY3X33XdrwYIF7m4R17gnnnhCa9as0c6dO+Xn5ydJmj9/vsaPH6/du3erefPm7m3QYghEcKucnBzFx8erT58++uijj9zdDizE6XQqLS1Na9eu1ddff60zZ84oMjJSgwcP1lNPPSVfX193t4hrWHZ2thISErRx40bdcccdLvMSExN19uxZPjq7wghEAADA8riGCAAAWB6BCAAAWB6BCAAAWB6BCAAAWB6BCAAAWB6BCAAAWB6BCAAAWB6BCAB+pnv37kpNTa12/caNG2Wz2XTy5MlftN3mzZtr9uzZv2gdAC4fgQiAWw0fPlw2m002m02enp5q1qyZHnvsMRUVFdX6du65555aXSeAaweBCIDb9e3bV/n5+Tp06JBef/11vf/++xo9erS72wJgIQQiAG7n4+OjsLAwNW3aVH369NEDDzygtWvXmvMrKio0depUNW3aVD4+PurUqZMyMzNd1rF792717NlTvr6+aty4sUaOHKlTp05JktLS0rRo0SK999575tmojRs3Vqu3JUuWqEuXLvL391dYWJiGDh2qwsLCSnWfffaZOnbsqAYNGig2Nla7d+92mb9582bddddd8vX1VWRkpMaNG6fTp0/X8J0CUFcIRADqlW+++UaZmZny8vIyx1566SW9+OKLeuGFF7Rr1y4lJiZqwIABOnDggCTpxx9/VN++fRUYGKht27bp7bff1rp16zRmzBhJ0sSJEzVkyBDzTFR+fr7i4+Or1U9ZWZn++Mc/aufOnVq1apUOHjyo4cOHV6r7v//7P73wwgvatm2bQkJCNGDAAJ05c0bST2EtMTFRgwYN0q5du7RixQpt2rTJ7A9APWAAgBsNGzbM8PDwMPz8/IwGDRoYkgxJxsyZM82aiIgI4/nnn3dZ7rbbbjNGjx5tGIZhvPbaa0ZgYKBx6tQpc/6aNWuM6667zigoKDC3M3DgwEv2061bN+OJJ5644PytW7cakoySkhLDMAxjw4YNhiRj+fLlZs2JEycMX19fY8WKFYZhGEZycrIxcuRIl/V8+umnxnXXXWeUlpYahmEYUVFRxqxZsy7ZH4C6wRkiAG7Xo0cP5eXlacuWLRo7dqwSExM1duxYSVJxcbGOHj2q22+/3WWZ22+/Xfv27ZMk7du3Tx07dpSfn5/L/IqKCu3fv/8X9fbFF19o4MCBioqKkr+/v7p37y5J+u6771zq4uLizJ+DgoLUtm1bs78dO3YoIyNDjRo1Ml+JiYmqqKjQwYMHf1F/AGoHgQiA2/n5+alVq1aKiYnRyy+/LKfTqSlTprjU2Gw2l2nDMMyxn/98vguNV8fp06fVp08fNWrUSEuWLNG2bdu0cuVKST99lHYp57ZdUVGhUaNGKS8vz3zt3LlTBw4cUMuWLS+7PwC1h0AEoN557rnn9MILL+jo0aMKCAhQRESENm3a5FKzefNmtWvXTpLUvn175eXluVyk/Nlnn+m6665TmzZtJEne3t4qLy+vUR9fffWVvv/+e02fPl133nmnbrrppiovqJak3Nxc8+eioiL961//0k033SRJuvXWW7V37161atWq0svb27tGPQGoGwQiAPVO9+7ddfPNN2vatGmSfrpg+c9//rNWrFih/fv3a9KkScrLy9MTTzwhSXr44YfVoEEDDRs2THv27NGGDRs0duxYJScnKzQ0VNJPDz7ctWuX9u/fr++//9684PlimjVrJm9vb73yyiv65ptvtHr1av3xj3+ssnbq1Klav3699uzZo+HDhys4ONh87tHvf/975eTk6PHHH1deXp4OHDig1atXmx8LAnA/AhGAemn8+PGaP3++Dh8+rHHjxmnChAmaMGGCOnTooMzMTK1evVqtW7eWJDVs2FAfffSRfvjhB9122226//77lZCQoDlz5pjrGzFihNq2basuXbqoSZMm+uyzzy7ZQ5MmTZSRkaG3335b7du31/Tp0/XCCy9UWTt9+nQ98cQT6ty5s/Lz87V69Wrz7E9MTIyys7N14MAB3Xnnnbrlllv0zDPPKDw8vBbeKQC1wWYYhuHuJgAAANyJM0QAAMDyCEQAAMDyCEQAAMDyCEQAAMDyCEQAAMDyCEQAAMDyCEQAAMDyCEQAAMDyCEQAAMDyCEQAAMDyCEQAAMDyCEQAAMDy/j/UyXXP6PKNJAAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "root_counts = dict()\n", + "for sketch, count in sketch_counter.items():\n", + " root_counts[sketch[1]] = root_counts.get(sketch[1], 0) + count\n", + "\n", + "plt.bar(range(len(root_counts)), list(root_counts.values()), align='center')\n", + "plt.xticks(range(len(root_counts)), list(root_counts.keys()))\n", + "plt.title('Root label counts in clauses')\n", + "plt.xlabel('Root label')\n", + "plt.ylabel('Clause count')\n", + "plt.show()\n", + "\n", + "root_counts = dict()\n", + "for sketch, count in sketch_counter.items():\n", + " root_counts[sketch[1]] = root_counts.get(sketch[1], 0) + 1\n", + " \n", + "plt.bar(range(len(root_counts)), list(root_counts.values()), align='center')\n", + "plt.xticks(range(len(root_counts)), list(root_counts.keys()))\n", + "plt.title('Root label counts in sketches')\n", + "plt.xlabel('Root label')\n", + "plt.ylabel('Sketch count')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "1f373fb2", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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CyGgUCikx4//91+pedLMmTOh0+mkJSMj47ljIyIiourJ6ENTUVERBg8ejPT0dCQkJOhdX6RWq1FYWAitVqv3nOzsbGnmSK1W48aNG6X2e/PmTb2aJ2eUtFotioqKSs1APU6pVMLGxkZvISIioprJqEPTo8D0yy+/4MCBA7C3t9fb7u3tDTMzM70LxjMzM5Gamgo/Pz8AgK+vL3Q6HU6cOCHVHD9+HDqdTq8mNTUVmZmZUk18fDyUSiW8vb0NeYhERERUTVTp3XN5eXm4fPmy9Dg9PR0pKSmws7ODRqPBP//5T5w+fRrffPMNiouLpdkgOzs7mJubQ6VSYfTo0Zg8eTLs7e1hZ2eHKVOmwMvLS7qbrnnz5ujVqxfCwsKwfv16AMCYMWMQFBQEDw8PAIC/vz9atGiB0NBQLF26FHfu3MGUKVMQFhbG2SMiIiICUMWh6dSpU+jWrZv0eNKkSQCAESNGIDIyEnv37gUAtGnTRu95hw4dQteuXQEAK1euhKmpKQYPHoz8/Hx0794dW7ZsgYmJiVS/Y8cOhIeHS3fZBQcH6302lImJCfbt24dx48ahY8eOsLS0REhICJYtW2aIwyYiIqJqSCGEEFU9iJoiNzcXKpUKOp2OM1QVwHXGPoP3uLIosEp6P60vERFVPrl/v436miYiIiIiY8HQRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJANDExEREZEMDE1EREREMjA0EREREcnA0EREREQkA0MTERERkQwMTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJANDExEREZEMDE1EREREMjA0EREREcnA0EREREQkA0MTERERkQwMTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJANDExEREZEMDE1EREREMjA0EREREcnA0EREREQkQ5WGph9++AF9+/aFRqOBQqHAnj179LYLIRAZGQmNRgNLS0t07doV58+f16spKCjAxIkT4eDgACsrKwQHB+P69et6NVqtFqGhoVCpVFCpVAgNDUVOTo5ezbVr19C3b19YWVnBwcEB4eHhKCwsNMRhExERUTVUpaHp3r17aN26NaKjo8vcvmTJEqxYsQLR0dE4efIk1Go1evbsibt3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VCqVVFOWgoIC5Obm6i1ERERUM1Wb0CSEwKRJk9CpUyd4enoCALKysgAATk5OerVOTk7StqysLJibm8PW1vaZNY6OjqV6Ojo6SjVliYqKkq6BUqlUcHZ2fvEDJCIiIqNWbULThAkTcPbsWezatavUNoVCofdYCFFq3ZOerCmr/nn7mTlzJnQ6nbRkZGQ87zCIiIiomqoWoWnixInYu3cvDh06hIYNG0rr1Wo1AJSaDcrOzpZmn9RqNQoLC6HVap9Zc+PGjVJ9b968WWoW63FKpRI2NjZ6CxEREdVMRh2ahBCYMGECvvzySxw8eBBubm56293c3KBWq5GQkCCtKywsRGJiIvz8/AAA3t7eMDMz06vJzMxEamqqVOPr6wudTocTJ05INcePH4dOp5NqiIiI6O/NqO+eGz9+PHbu3ImvvvoK1tbW0oySSqWCpaUlFAoFIiIisHDhQri7u8Pd3R0LFy5E7dq1ERISItWOHj0akydPhr29Pezs7DBlyhR4eXlJd9M1b94cvXr1QlhYGNavXw8AGDNmDIKCgnjnHBEREQEw8tC0du1aAEDXrl311m/evBkjR44EAEybNg35+fkYN24ctFotfHx8EB8fD2tra6l+5cqVMDU1xeDBg5Gfn4/u3btjy5YtMDExkWp27NiB8PBw6S674OBgREdHG/YAiYiIqNow6tAkhHhujUKhQGRkJCIjI59aY2FhgdWrV2P16tVPrbGzs8P27dtfZJhERET0N2DU1zQRERERGQuGJiIiIiIZGJqIiIiIZGBoIiIiIpKBoYmIiIhIBoYmIiIiIhkYmoiIiIhkYGgiIiIikoGhiYiIiEgGhiYiIiIiGRiaiIiIiGQw6u+eI6Kaz3XGPoPu/8qiQIPun4j+PjjTRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJANDExEREZEMDE1EREREMjA0EREREcnA0EREREQkA0MTERERkQwMTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJANDExEREZEMDE1EREREMjA0EREREclgWtUDICKqCq4z9hm8x5VFgQbvQUSVhzNNRERERDIwND1hzZo1cHNzg4WFBby9vfHjjz9W9ZCIiIjICDA0PWb37t2IiIjArFmzcObMGbz66qvo3bs3rl27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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkQAAAHFCAYAAAAT5Oa6AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy88F64QAAAACXBIWXMAAA9hAAAPYQGoP6dpAABCFUlEQVR4nO3deViVdf7/8dcJ2QQ5CcpWKGjmvqWFYKUmLrhg2pRmkeZSTZaRbZpTUmNqTqmlk6aZWGpaU9qiobgxmbtFrtPopKUJoYagZqDw+f3Rj/P1CCQoB9T7+biu+7q87/tz7vf7PsdOL+/l3DZjjBEAAICFXVPZDQAAAFQ2AhEAALA8AhEAALA8AhEAALA8AhEAALA8AhEAALA8AhEAALA8AhEAALA8AhEAALA8AhFQjjZt2qTevXurVq1a8vT0VFBQkKKiovTUU09d1PaSkpJks9m0devWC45dsGCBpkyZclF1rG7ZsmVKTEws122Gh4dr4MCB5brNC2nfvr2aNGlSLtvavXu3EhMTdeDAgYt6/cCBA+Xr61suvQAVgUAElJOlS5cqOjpaOTk5mjhxolasWKE33nhDbdu21aJFi1xen0B08ZYtW6aXXnqpXLe5ePFivfDCC+W6zYq0e/duvfTSSxcdiIArTZXKbgC4WkycOFERERFavny5qlT5v/+0+vXrp4kTJ1ZiZ0Xl5+fr7Nmz8vT0rOxWrlotW7as7BYAlAFHiIBycuzYMdWoUcMpDBW65hrn/9RsNluxp2hKOs2SlZWlBx98UP7+/vLx8VHPnj31ww8/ONa3b99eS5cu1Y8//iibzeaYJOnAgQOy2WyaOHGixo4dq4iICHl6emrNmjWSpK1btyouLk7+/v7y8vJSy5Yt9eGHHxbpISMjQw8//LCuv/56eXh4KCIiQi+99JLOnj1bqvdnwYIFioqKkq+vr3x9fdWiRQvNnj3bacy7776r5s2by8vLS/7+/urdu7f27NnjNKZ9+/Zq3759ke0PHDhQ4eHhjvnC/X7ttdc0adIkRUREyNfXV1FRUdq4caPT6/75z39KktN7V3hk5KOPPlJkZKTsdruqVq2qOnXqaNCgQRfc3/M/y7Vr18pms+mDDz7Q6NGjFRoaKj8/P8XExOj777+/4PaOHDmihx56SGFhYfL09FTNmjXVtm1brVy58k9ft3jxYlWtWlVDhgxxfFYX+syTkpJ09913S5I6dOjgeE+SkpIcY5KTk9WxY0fH+9KwYUONHz++SP19+/apW7du8vX1VVhYmJ566inl5uY6jcnLy9PYsWPVoEEDx749+OCDOnLkiNO41atXq3379goICJC3t7dq1aqlu+66S7/99tsF3z/gQghEQDmJiorSpk2bNHz4cG3atElnzpwpt20PHjxY11xzjeO02ObNm9W+fXsdP35ckvTWW2+pbdu2Cg4O1oYNGxzTud58802tXr1ar732mr788ks1aNBAa9asUdu2bXX8+HHNmDFDn376qVq0aKG+ffs6/c8vIyNDt9xyi5YvX64XX3xRX375pQYPHqzx48dr6NChF+z/xRdf1H333afQ0FAlJSVp8eLFGjBggH788UfHmPHjx2vw4MFq3LixPvnkE73xxhvavn27oqKitHfv3ot+7/75z38qJSVFU6ZM0fz583Xq1Cl169ZN2dnZkqQXXnhBf/nLXyTJ6b0LCQnRhg0b1LdvX9WpU0cLFy7U0qVL9eKLL5Y6BBbn+eef148//qh33nlHM2fO1N69e9WzZ0/l5+f/6evi4+O1ZMkSvfjii1qxYoXeeecdxcTE6NixYyW+ZvLkybr77rv1/PPP65133lGVKlVK9Zl3795d48aNc7x/he9J9+7dJUmzZ89Wt27dVFBQoBkzZujzzz/X8OHDdejQIaf6Z86cUVxcnDp27KhPP/1UgwYN0uTJk/Xqq686xhQUFKhXr16aMGGC+vfvr6VLl2rChAlKSUlR+/btdfr0aUl/BNzu3bvLw8ND7777rpKTkzVhwgT5+PgoLy+vzJ8DUIQBUC6OHj1qbr31ViPJSDLu7u4mOjrajB8/3pw4ccJprCQzZsyYItuoXbu2GTBggGN+zpw5RpLp3bu307ivv/7aSDJjx451LOvevbupXbt2kW3u37/fSDJ169Y1eXl5TusaNGhgWrZsac6cOeO0vEePHiYkJMTk5+cbY4x5+OGHja+vr/nxxx+dxr322mtGktm1a1eJ78sPP/xg3NzczH333VfimKysLOPt7W26devmtPynn34ynp6epn///o5l7dq1M+3atSuyjQEDBjjtf+F+N23a1Jw9e9axfPPmzUaS+eCDDxzLhg0bZor7Oizcv+PHj5fYe0nO/yzXrFljJBXZxw8//NBIMhs2bPjT7fn6+pqEhIQ/HdOuXTvTuHFjk5+fbx577DHj4eFh5s2b5zSmtJ/5Rx99ZCSZNWvWOI07ceKE8fPzM7feeqspKCgosZcBAwYYSebDDz90Wt6tWzdTv359x/wHH3xgJJmPP/7YadyWLVuMJPPWW28ZY4z517/+ZSSZtLS0P30PgIvFESKgnAQEBOirr77Sli1bNGHCBPXq1Uv//e9/NWrUKDVt2lRHjx696G3fd999TvPR0dGqXbu247RXacTFxcnd3d0xv2/fPv3nP/9xbPvs2bOOqVu3bkpPT3ecyvniiy/UoUMHhYaGOo2LjY2VJKWmppZYNyUlRfn5+Ro2bFiJYzZs2KDTp08XOV0YFhamO+64Q6tWrSr1fp6ve/fucnNzc8w3a9ZMkpyOTpXk5ptvliTdc889+vDDD/Xzzz9fdB+F4uLinOZL288tt9yipKQkjR07Vhs3bizxCOTvv/+uO++8U/Pnz9eKFSuc/u6U5TMvyfr165WTk6NHH33UcVq2JDabTT179iyyv+fu6xdffKFrr71WPXv2dOqnRYsWCg4O1tq1ayVJLVq0kIeHhx566CHNnTvX6ZQxUB4IREA5a926tZ577jl99NFHOnz4sJ588kkdOHDgki6sDg4OLnbZn50uOV9ISIjT/C+//CJJevrpp+Xu7u40Pfroo5LkCHG//PKLPv/88yLjGjdu7DSuOIXXgVx//fUljincj/N7lKTQ0NAy7ef5AgICnOYLLyQvPBXzZ26//XYtWbJEZ8+e1QMPPKDrr79eTZo00QcffFDh/SxatEgDBgzQO++8o6ioKPn7++uBBx5QRkaG07jMzEwtX75cUVFRio6OdlpXls+8JKX5PAtVrVpVXl5eTss8PT31+++/O/V0/PhxeXh4FOkpIyPD0U/dunW1cuVKBQYGatiwYapbt67q1q2rN95444J9AKXBXWaAC7m7u2vMmDGaPHmydu7c6Vju6elZ5MJSSSX+j//8/+kVLrvhhhtK3cv5/5qvUaOGJGnUqFHq06dPsa+pX7++Y2yzZs30yiuvFDsuNDS0xLo1a9aUJB06dEhhYWHFjikMCenp6UXWHT582NGrJHl5eTmu/znXpRyB+zO9evVSr169lJubq40bN2r8+PHq37+/wsPDFRUV5ZKaxalRo4amTJmiKVOm6KefftJnn32mkSNHKjMzU8nJyY5xtWrV0qRJk9S7d2/16dNHH330kSOUlOUzL8m5n2d5qFGjhgICApz24VzVqlVz/Pm2227Tbbfdpvz8fG3dulVTp05VQkKCgoKC1K9fv3LpB9ZFIALKSXp6erFHOArvkjo3NISHh2v79u1O41avXq2TJ08Wu+358+frrrvucsyvX79eP/74o4YMGeJY5unpWaqjHoXq16+vevXq6bvvvnNcQFuSHj16aNmyZapbt66qV69e6hqS1LlzZ7m5uWn69OklBoioqCh5e3tr3rx5jrubpD/+p7t69WrHRc/SH+/dRx99pNzcXMfRlWPHjmn9+vXy8/MrU2+Fzj1K4+3tXeKYdu3a6dprr9Xy5cv17bffVmggOletWrX02GOPadWqVfr666+LrO/cubOWL1+u7t27q0ePHvr000/l4+NTps+8pCNX0dHRstvtmjFjhvr163fB02YX0qNHDy1cuFD5+fmKjIws1Wvc3NwUGRmpBg0aaP78+frmm28IRLhkBCKgnHTp0kXXX3+9evbsqQYNGqigoEBpaWl6/fXX5evrqyeeeMIxNj4+Xi+88IJefPFFtWvXTrt379a0adNkt9uL3fbWrVs1ZMgQ3X333Tp48KBGjx6t6667znGaQ5KaNm2qTz75RNOnT1erVq10zTXXqHXr1n/a89tvv63Y2Fh16dJFAwcO1HXXXadff/1Ve/bs0TfffKOPPvpIkvTyyy8rJSVF0dHRGj58uOrXr6/ff/9dBw4c0LJlyzRjxowST6GEh4fr+eef19///nedPn1a9957r+x2u3bv3q2jR4/qpZde0rXXXqsXXnhBzz//vB544AHde++9OnbsmF566SV5eXlpzJgxTu/d22+/rfvvv19Dhw7VsWPHNHHixIsOQ4XvnSS9+uqrio2NlZubm5o1a6axY8fq0KFD6tixo66//nodP35cb7zxhtzd3dWuXbuLrldW2dnZ6tChg/r3768GDRqoWrVq2rJli5KTk0s80nPrrbdq1apV6tq1qzp37qxly5bJbreX+jMv/MXrmTNnqlq1avLy8lJERIQCAgL0+uuva8iQIYqJidHQoUMVFBSkffv26bvvvtO0adPKtG/9+vXT/Pnz1a1bNz3xxBO65ZZb5O7urkOHDmnNmjXq1auXevfurRkzZmj16tXq3r27atWqpd9//13vvvuuJCkmJuYS3l3g/6vsq7qBq8WiRYtM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AACg0pT5GiIAAICrTZmPEHXo0OFPT42tXr36khoCAACoaGUORIXXDxU6c+aM0tLStHPnziIPfQUAALgSlDkQTZ48udjliYmJOnny5CU3BAAAUNHK7Rqi+++/n+eYAQCAK1K5BaINGzbIy8urvDYHAABQYcp8yqxPnz5O88YYpaena+vWrXrhhRfKrTEAAICKUuZAZLfbneavueYa1a9fXy+//LI6d+5cbo0BAABUlDIHojlz5riiDwAAgEpT5kBUaNu2bdqzZ49sNpsaNWqkli1blmdfAAAAFabMgSgzM1P9+vXT2rVrde2118oYo+zsbHXo0EELFy5UzZo1XdEnAACAy5T5LrPHH39cOTk52rVrl3799VdlZWVp586dysnJ0fDhw13RIwAAgEuV+QhRcnKyVq5cqYYNGzqWNWrUSP/85z+5qBoAAFyRynyEqKCgQO7u7kWWu7u7q6CgoFyaAgAAqEhlDkR33HGHnnjiCR0+fNix7Oeff9aTTz6pjh07lmtzAAAAFaHMgWjatGk6ceKEwsPDVbduXd1www2KiIjQiRMnNHXqVFf0CAAA4FJlvoYoLCxM33zzjVJSUvSf//xHxhg1atRIMTExrugPAADA5S76d4g6deqkTp06lWcvAAAAlaLMp8yGDx+uN998s8jyadOmKSEhoTx6AgAAqFBlDkQff/yx2rZtW2R5dHS0/vWvf5VLUwAAABWpzIHo2LFjRR7wKkl+fn46evRouTQFAABQkcociG644QYlJycXWf7ll1+qTp065dIUAABARSrzRdUjRozQY489piNHjuiOO+6QJK1atUqvv/66pkyZUt79AQAAuFyZA9GgQYOUm5urV155RX//+98lSeHh4Zo+fboeeOCBcm8QAADA1S7qtvu//vWv+utf/6ojR47I29tbvr6+5d0XAABAhbno3yGSpJo1a5ZXHwAAAJWmzBdVAwAAXG0IRAAAwPIIRAAAwPIIRAAAwPIu6qLqVatWadWqVcrMzFRBQYHTunfffbdcGgMAAKgoZQ5EL730kl5++WW1bt1aISEhstlsrugLAACgwpQ5EM2YMUNJSUmKj493RT8AAAAVrszXEOXl5Sk6OtoVvQAAAFSKMgeiIUOGaMGCBa7oBQAAoFKU6pTZiBEjHH8uKCjQzJkztXLlSjVr1kzu7u5OYydNmlS+HQIAALhYqQLRt99+6zTfokULSdLOnTudlnOBNQAAuBKVKhCtWbPG1X0AAABUmjJfQ5Sdna1ff/21yPJff/1VOTk55dIUAABARSpzIOrXr58WLlxYZPmHH36ofv36lUtTAAAAFanMgWjTpk3q0KFDkeXt27fXpk2byqUpAACAilTmQJSbm6uzZ88WWX7mzBmdPn26XJoCAACoSGUORDfffLNmzpxZZPmMGTPUqlWrcmkKAACgIpX50R2vvPKKYmJi9N1336ljx46S/njY65YtW7RixYpybxAAAMDVynyEqG3bttqwYYPCwsL04Ycf6vPPP9cNN9yg7du367bbbnNFjwAAAC5V5iNE0h8/zDh//vzy7gUAAKBSlPkIkZubmzIzM4ssP3bsmNzc3MqlKQAAgIpU5kBkjCl2eW5urjw8PC65IQAAgIpW6lNmb775pqQ/nlf2zjvvyNfX17EuPz9f//73v9WgQYPy7xAAAMDFSh2IJk+eLOmPI0QzZsxwOj3m4eGh8PBwzZgxo/w7BAAAcLFSB6L9+/dLkjp06KBPPvlE1atXd1lTAAAAFanM1xCtWbNG1atXV15enr7//vtif7UaAADgSlLmQHT69GkNHjxYVatWVePGjfXTTz9JkoYPH64JEyaUaVv//ve/1bNnT4WGhspms2nJkiVO640xSkxMVGhoqLy9vdW+fXvt2rXLaUxubq4ef/xx1ahRQz4+PoqLi9OhQ4ecxmRlZSk+Pl52u112u13x8fE6fvx4WXcdAABcpcociEaOHKnvvvtOa9eulZeXl2N5TEyMFi1aVKZtnTp1Ss2bN9e0adOKXT9x4kRNmjRJ06ZN05YtWxQcHKxOnTrpxIkTjjEJCQlavHixFi5cqHXr1unkyZPq0aOH8vPzHWP69++vtLQ0JScnKzk5WWlpaYqPjy/jngMAgKtVmX+YccmSJVq0aJHatGkjm83mWN6oUSP973//K9O2YmNjFRsbW+w6Y4ymTJmi0aNHq0+fPpKkuXPnKigoSAsWLNDDDz+s7OxszZ49W++//75iYmIkSfPmzVNYWJhWrlypLl26aM+ePUpOTtbGjRsVGRkpSZo1a5aioqL0/fffq379+mV9CwAAwFWmzEeIjhw5osDAwCLLT5065RSQLtX+/fuVkZGhzp07O5Z5enqqXbt2Wr9+vSRp27ZtOnPmjNOY0NBQNWnSxDFmw4YNstvtjjAkSW3atJHdbneMKU5ubq5ycnKcJgAAcHW6qKfdL1261DFfGIIKj7qUl4yMDElSUFCQ0/KgoCDHuoyMDHl4eBS54+38McUFuMDAQMeY4owfP95xzZHdbldYWNgl7Q8AALh8lfmU2fjx49W1a1ft3r1bZ8+e1RtvvKFdu3Zpw4YNSk1NLfcGzz/qZIy54JGo88cUN/5C2xk1apRGjBjhmM/JySEUAQBwlSrzEaLo6Gh9/fXX+u2331S3bl2tWLFCQUFB2rBhg1q1alVujQUHB0tSkaM4mZmZjqNGwcHBysvLU1ZW1p+O+eWXX4ps/8iRI0WOPp3L09NTfn5+ThMAALg6lTkQbd++XU2bNtXcuXO1c+dO7d69W/PmzVPTpk2L3DZ/KSIiIhQcHKyUlBTHsry8PKWmpio6OlqS1KpVK7m7uzuNSU9P186dOx1joqKilJ2drc2bNzvGbNq0SdnZ2Y4xAADA2sp8yqxLly76+uuvVadOHaflH3/8sR544AGdOnWq1Ns6efKk9u3b55jfv3+/0tLS5O/vr1q1aikhIUHjxo1TvXr1VK9ePY0bN05Vq1ZV//79JUl2u12DBw/WU089pYCAAPn7++vpp59W06ZNHXedNWzYUF27dtXQoUP19ttvS5Ieeugh9ejRgzvMABcLH7n0woMuwYEJ3V26fQDWUeZA9Ne//lUdO3bU+vXrFRISIklatGiRBg0apKSkpDJta+vWrerQoYNjvvCanQEDBigpKUnPPvusTp8+rUcffVRZWVmKjIzUihUrVK1aNcdrJk+erCpVquiee+7R6dOn1bFjRyUlJTk9a23+/PkaPny44260uLi4En/7CAAAWE+ZA9GLL76oY8eOKSYmRl999ZWSk5M1ZMgQvf/++7rrrrvKtK327dvLGFPiepvNpsTERCUmJpY4xsvLS1OnTtXUqVNLHOPv76958+aVqTcAAGAdZQ5EkvTGG28oPj5ebdq00c8//6wPPvhAvXr1Ku/eAAAAKkSpAtFnn31WZNmdd96p1NRU3XvvvbLZbI4xcXFx5dshAACAi5UqEN15550lrnv33Xf17rvvSvrjFNe5zxADAAC4EpQqEBUUFLi6DwAAgEpT5t8hAgAAuNqUOhBt2rRJX375pdOy9957TxEREQoMDNRDDz2k3Nzccm8QAADA1UodiBITE7V9+3bH/I4dOzR48GDFxMRo5MiR+vzzzzV+/HiXNAkAAOBKpQ5EaWlp6tixo2N+4cKFioyM1KxZszRixAi9+eab+vDDD13SJAAAgCuVOhBlZWU5PQw1NTVVXbt2dczffPPNOnjwYPl2BwAAUAFKHYiCgoK0f/9+SX88ZPWbb75RVFSUY/2JEyfk7u5e/h0CAAC4WKkDUdeuXTVy5Eh99dVXGjVqlKpWrarbbrvNsX779u2qW7euS5oEAABwpVI/umPs2LHq06eP2rVrJ19fX82dO1ceHh6O9e+++67j4akAAABXklIHopo1a+qrr75Sdna2fH19nZ4mL0kfffSRfH19y71BAAAAVyvzw13tdnuxy/39/S+5GQAAgMrAL1UDAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLIxABAADLq1LZDcCawkcudXmNAxO6u7wGAODqwBEiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeQQiAABgeZd1IEpMTJTNZnOagoODHeuNMUpMTFRoaKi8vb3Vvn177dq1y2kbubm5evzxx1WjRg35+PgoLi5Ohw4dquhdAQAAl7HLOhBJUuPGjZWenu6YduzY4Vg3ceJETZo0SdOmTdOWLVsUHBysTp066cSJE44xCQkJWrx4sRYuXKh169bp5MmT6tGjh/Lz8ytjdwAAwGWoSmU3cCFVqlRxOipUyBijKVOmaPTo0erTp48kae7cuQoKCtKCBQv08MMPKzs7W7Nnz9b777+vmJgYSdK8efMUFhamlStXqkuXLhW6LwAA4PJ02R8h2rt3r0JDQxUREaF+/frphx9+kCTt379fGRkZ6ty5s2Osp6en2rVrp/Xr10uStm3bpjNnzjiN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xMRENWjQQD4+PqpevbpiYmK0adMml9eVpD179iguLk52u13VqlVTmzZt9NNPP11S3dJ8R7vqc3YFAtEVZNGiRUpISNDo0aP17bff6rbbblNsbOwl/6W+kFOnTql58+aaNm2aS+ucLzU1VcOGDdPGjRuVkpKis2fPqnPnzjp16pTLa19//fWaMGGCtm7dqq1bt+qOO+5Qr169KvQ/5C1btmjmzJlq1qxZhdRr3Lix0tPTHdOOHTsqpG5WVpbatm0rd3d3ffnll9q9e7def/11l/8y/JYtW5z2NyUlRZJ09913u7SuJL366quaMWOGpk2bpj179mjixIn6xz/+oalTp7q89pAhQ5SSkqL3339fO3bsUOfOnRUTE6Off/65XOtc6Htj4sSJmjRpkqZNm6YtW7YoODhYnTp10okTJ1xa99SpU2rbtq0mTJhwSXUupvaNN96oadOmaceOHVq3bp3Cw8PVuXNnHTlyxKV1//e//+nWW29VgwYNtHbtWn333Xd64YUX5OXldUl1S/Md7arP2SXK4+GpqBi33HKLeeSRR5yWNWjQwIwcObLCepBkFi9eXGH1zpWZmWkkmdTU1EqpX716dfPOO+9USK0TJ06YevXqmZSUFNOuXTvzxBNPuLTemDFjTPPmzV1aoyTPPfecufXWWyul9rmeeOIJU7duXVNQUODyWt27dzeDBg1yWtanTx9z//33u7Tub7/9Ztzc3MwXX3zhtLx58+Zm9OjRLqt7/vdGQUGBCQ4ONhMmTHAs+/33343dbjczZsxwWd1z7d+/30gy3377bbnVK23tQtnZ2UaSWblypUvr9u3b1+V/t4wp+h1dUZ9zeeEI0RUiLy9P27ZtU+fOnZ2Wd+7cWevXr6+kripWdna2JMnf379C6+bn52vhwoU6deqUoqKiKqTmsGHD1L17d8XExFRIPUnau3evQkNDFRERoX79+umHH36okLqfffaZWrdurbvvvluBgYFq2bKlZs2aVSG1C+Xl5WnevHkaNGhQhTy8+dZbb9WqVav03//+V5L03Xffad26derWrZtL6549e1b5+flFjgx4e3tr3bp1Lq19rv379ysjI8Pp+8zT01Pt2rWzzPdZXl6eZs6cKbvdrubNm7usTkFBgZYuXaobb7xRXbp0UWBgoCIjI11y6cP539FX2udMILpCHD16VPn5+QoKCnJaHhQU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euihhyq7DQAl4FlmAFzqrrvu0pkzZzR37lzVqVNHv/zyi1atWqVff/21UvrJy8uTh4dHhdetWbNmhdcEUHocIQLgMsePH9e6dev06quvqkOHDqpdu7ZuueUWjRo1yvF0+QMHDshmsyktLc3pdTabrcjDJ7/++ms1b95cXl5eioyM1I4dOyT9cUrtwQcfVHZ2tmw2m2w2mxITEyX9capq7NixGjhwoOx2u4YOHSpJWr9+vW6//XZ5e3srLCxMw4cP16lTpxy18vLy9Oyzz+q6666Tj4+PIiMjL/gwzMTERMeRsNDQUA0fPtyx7txTZklJSY4+z50Ke5akOXPmqGHDhvLy8lKDBg301ltvleGdB1BWBCIALuPr6ytfX18tWbJEubm5l7y9Z555Rq+99pq2bNmiwMBAxcXF6cyZM4qOjtaUKVPk5+en9PR0paen6+mnn3a87h//+IeaNGmibdu26YUXXtCOHTvUpUsX9enTR9u3b9eiRYu0bt06PfbYY47XPPjgg/r666+1cOFCbd++XXfffbe6du2qvXv3Ftvbv/71L02ePFlvv/229u7dqyVLlqhp06bFju3bt6+jz/T0dH3wwQeqUqWK2rZtK0maNWuWRo8erVdeeUV79uzRuHHj9MILL2ju3LmX/B4CKEFlP10WwNXtX//6l6levbrx8vIy0dHRZtSoUea7775zrN+/f7+RZL799lvHsqysLCPJrFmzxhjzf0/zXrhwoWPMsWPHjLe3t1m0aJEx5o+nmNvt9iL1a9eube68806nZfHx8eahhx5yWvbVV1+Za665xpw+fdrs27fP2Gw28/PPPzuN6dixoxk1alSx+/n666+bG2+80eTl5RW7vnbt2mby5MlFlu/bt88EBASYiRMnOpaFhYWZBQsWOI37+9//bqKioordNoBLxxEiAC5111136fDhw/rss8/UpUsXrV27VjfddJOSkpLKvK2oqCjHn/39/VW/fn3t2bPngq9r3bq10/y2bduUlJTkOILl6+urLl26qKCgQPv379c333wjY4xuvPFGpzGpqan63//+V2yNu+++W6dPn1adOnU0dOhQLV68WGfPnv3TvrKzs9WjRw/FxsbqmWeekSQdOXJEBw8e1ODBg51qjx07tsTaAC4dF1UDcDkvLy916tRJnTp10osvvqghQ4ZozJgxGjhwoK655o9/lxljHOPPnDlT6m3bbLYLjvHx8XGaLygo0MMPP+x0jU+hWrVqafv27XJzc9O2bdvk5ubmtN7X17fYGmFhYfr++++VkpKilStX6tFHH9U//vEPpaamyt3dvcj4/Px89e3bV35+fpo1a5ZTb9Ifp80iIyOdXnN+LwDKD4EIQIVr1KiRlixZIun/7r5KT09Xy5YtJcnpAutzbdy4UbVq1ZIkZWVl6b///a8aNGggSfLw8FB+fn6p6t90003atWuXbrjhhmLXt2zZUvn5+crMzNRtt91W2t2St7e34uLiFBcXp2HDhqlBgwbasWOHbrrppiJjn3zySe3YsUNbtmyRl5eXY3lQUJCuu+46/fDDD7rvvvtKXRvApSEQAXCZY8eO6e6779agQYPUrFkzVatWTVu3btXEiRPVq1cvSX+EiDZt2mjChAkKDw/X0aNH9be//a3Y7b388ssKCAhQUFCQRo8erRo1aujOO++U9MddXCdPntSqVavUvHlzVa1aVVWrVi12O88995zatGmjYcOGaejQofLx8dGePXuUkpKiqVOn6sYbb9R9992nBx54QK+//rpatmypo0ePavXq1WratKm6detWZJtJSUnKz89XZGSkqlatqvfff1/e3t6qXbt2kbFz5szRW2+9pcWLF+uaa65RRkaGpP+7CD0xMVHDhw+Xn5+fYmNjlZubq61btyorK0sjRoy4mI8CwIVU9kVMAK5ev//+uxk5cqS56aabjN1uN1WrVjX169c3f/vb38xvv/3mGLd7927Tpk0b4+3tbVq0aGFWrFhR7EXVn3/+uWncuLHx8PAwN998s0lLS3Oq98gjj5iAgAAjyYwZM8YYU/LFzJs3bzadOnUyvr6+xsfHxzRr1sy88sorjvV5eXnmxRdfNOHh4cbd3d0EBweb3r17m+3btxe7r4sXLzaRkZHGz8/P+Pj4mDZt2piVK1c61p/bx4ABA4ykIlNhz8YYM3/+fNOiRQvj4eFhqlevbm6//XbzySeflOHdB1AWNmPOOXEPAABgQdxlBgAALI9ABAAALI9ABAAALI9ABAAALI9ABAAALI9ABAAALI9ABAAALI9ABAAALI9ABAAALI9ABAAALI9ABAAALI9ABAAALO//AfcFG1j7VXQVAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "subtree_counts = dict()\n", + "for sketch, count in sketch_counter.items():\n", + " subtree_count = sketch.count(')')\n", + " subtree_counts[subtree_count] = subtree_counts.get(subtree_count, 0) + count\n", + "subtree_counts = dict(sorted(subtree_counts.items())) \n", + "\n", + "plt.bar(range(len(subtree_counts)), list(subtree_counts.values()), align='center')\n", + "plt.xticks(range(len(subtree_counts)), list(subtree_counts.keys()))\n", + "plt.title('Subtree counts in clauses')\n", + "plt.xlabel('Subtree size')\n", + "plt.ylabel('Clause count')\n", + "plt.show() \n", + "\n", + "subtree_counts = dict()\n", + "for sketch, count in sketch_counter.items():\n", + " subtree_count = sketch.count(')')\n", + " subtree_counts[subtree_count] = subtree_counts.get(subtree_count, 0) + 1\n", + "subtree_counts = dict(sorted(subtree_counts.items())) \n", + "\n", + "\n", + "plt.bar(range(len(subtree_counts)), list(subtree_counts.values()), align='center')\n", + "plt.xticks(range(len(subtree_counts)), list(subtree_counts.keys()))\n", + "plt.title('Subtree counts in sketches')\n", + "plt.xlabel('Subtree size')\n", + "plt.ylabel('Sketch count')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "9db8626a", + "metadata": {}, + "source": [ + "## III. Save top 50 sketches for knock-out experiments" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "f8a44838", + "metadata": {}, + "outputs": [], + "source": [ + "DataFrame({\n", + " 'sketch': [sketch for sketch, _ in sketch_counter.most_common(50)],\n", + " 'support': [count for _, count in sketch_counter.most_common(50)] \n", + "}).to_csv( knockout_list_file )" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/01d_prepare_sketches.py b/01d_prepare_sketches.py new file mode 100644 index 00000000..ba659a84 --- /dev/null +++ b/01d_prepare_sketches.py @@ -0,0 +1,632 @@ +# +# Creates frequency table of syntax sketches, and prepares datasets +# for sketches knockout experiments: removes clauses corresponding +# to sketches systematically from train, dev and test sets. +# + +import os +import sys +import configparser +from datetime import datetime +from collections import Counter + +from typing import List, Tuple, Dict, Any + +import os, os.path +from random import Random + +from pandas import DataFrame +from pandas import read_csv + +from syntax_sketches.syntax_sketch import safe_sketch_name +from syntax_sketches.syntax_sketch import extract_sketches +from syntax_sketches.syntax_sketch import remove_sketches +from syntax_sketches.syntax_sketch import remove_sketches_group +from syntax_sketches.syntax_sketch import rand_group_sketches +from syntax_sketches.syntax_sketch import compute_sketches +from syntax_sketches.clause_import import import_clauses +from syntax_sketches.clause_export import remove_extracted_from_conllu_and_dicts + +def prepare_sketches_main( conf_file ): + ''' + Creates frequency table of syntax sketches for a corpus, and/or + prepares datasets for sketches knockout experiments: removes + clauses corresponding to sketches systematically from train, + dev and test sets. + Inputs/outputs and parameters of the processing will be read + from the given `conf_file`. + Executes sections in the configuration starting with prefix + 'make_sketches_table_' and 'prepare_knockout_'. + ''' + # Parse configuration file + config = configparser.ConfigParser() + if conf_file is None or not os.path.exists(conf_file): + raise FileNotFoundError("Config file {} does not exist".format(conf_file)) + if len(config.read(conf_file)) != 1: + raise ValueError("File {} is not accessible or is not in valid INI format".format(conf_file)) + section_found = False + start = datetime.now() + for section in config.sections(): + if section.startswith('make_sketches_table_'): + section_found = True + print(f'Performing {section} ...') + # Collect sketch computing parameters + if not config.has_option(section, 'input_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "input_dir" parameter.') + input_dir = config[section]['input_dir'] + if not os.path.isdir(input_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "input_dir" value {input_dir!r} in {section!r}.') + if not config.has_option(section, 'output_csv_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_csv_file" parameter.') + output_csv_file = config[section]['output_csv_file'] + top_n = config[section].getint('top_n', 50) + skip_list = config[section].get('skip_list', 'train_full.conllu') + if len(skip_list) > 0: + skip_list = skip_list.split(',') + skip_list = [fname.strip() for fname in skip_list] + # Compute sketches frequency TOP N and save into CSV file + compute_sketches_freq_table( input_dir, top_n, output_csv_file, skip_files=skip_list, verbose=True ) + elif section.startswith('prepare_knockout_'): + section_found = True + print(f'Performing {section} ...') + # Collect knockout preparation parameters + # input_dir -- contains train/dev/test conllu files with clauses + if not config.has_option(section, 'input_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "input_dir" parameter.') + input_dir = config[section]['input_dir'] + if not os.path.isdir(input_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "input_dir" value {input_dir!r} in {section!r}.') + # top_sketches_file -- CSV file containing top_n sketches (created via 'make_sketches_table_') + if not config.has_option(section, 'top_sketches_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "top_sketches_file" parameter.') + top_sketches_file = config[section]['top_sketches_file'] + if not os.path.isfile(top_sketches_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid or missing "top_sketches_file" value '+\ + f'{top_sketches_file!r} in {section!r}.') + # min_support -- the minimun number of clauses from top n sketches in test panel. + # If test set does not contain at least min_support clauses of a sketch, then missing + # number of clauses will be extracted and removed from the train set. + min_support = config[section].getint('min_support', 50) + # random_groups -- distributes top_n_sketches randomly into random_groups bins, + # and prepares knockout splits on groups instead of on all sketches. + # If None (default), then prepares knockout splits on all top_n_sketches separately. + random_groups = config[section].getint('random_groups', None) + grouping_seed = config[section].getint('grouping_seed', 1) + top_sketches_grouped_file = config[section].get('top_sketches_grouped_file', None) + # create_control -- instead of preparing knockout data on top_n_sketches or corresponding groups, + # pick same amounts of clauses randomly for control experiment. + create_control = config[section].getboolean('create_control', False) + create_control_seed = config[section].getint('create_control_seed', 5) + # initial_output_dir -- for saving test panel and pure train data + if not config.has_option(section, 'initial_output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "initial_output_dir" parameter.') + initial_output_dir = config[section]['initial_output_dir'] + initial_train_fname = config[section].get('initial_train_fname', 'train_pure.conllu') + initial_test_fname = config[section].get('initial_test_fname', None) + # final_output_dir -- for saving knockout splits data + if not config.has_option(section, 'final_output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "final_output_dir" parameter.') + final_output_dir = config[section]['final_output_dir'] + skip_list = config[section].get('skip_list', 'train_full.conllu') + if len(skip_list) > 0: + skip_list = skip_list.split(',') + skip_list = [fname.strip() for fname in skip_list] + # Load input data + whole_data_map = load_clauses_datasets(input_dir, skip_files=skip_list) + top_n_sketches = read_csv(top_sketches_file, index_col=0).values.tolist() + # Create output directories and required file names + if not os.path.exists(initial_output_dir): + os.makedirs(initial_output_dir, exist_ok=True) + if not os.path.exists(final_output_dir): + os.makedirs(final_output_dir, exist_ok=True) + if initial_test_fname is None: + initial_test_fname = f'test_{len(top_n_sketches)}x{min_support}.conllu' + initial_test_fname = os.path.join(initial_output_dir, initial_test_fname) + initial_train_fname = os.path.join(initial_output_dir, initial_train_fname) + if random_groups is not None: + # ================================== + # Knockout grouped top sketches + # ================================== + if random_groups is not None and top_sketches_grouped_file is None: + top_sketches_grouped_file = f'top_{len(top_n_sketches)}_sketches_{random_groups}_groups.csv' + # Put top_sketches_grouped_file into same folder as top_sketches_file + head, tail = os.path.split( top_sketches_file ) + if len(head) > 0: + top_sketches_grouped_file = os.path.join( head, top_sketches_grouped_file ) + # Group sketches + grouped_sketches = group_top_sketches_randomly(top_n_sketches, random_groups, top_sketches_grouped_file, + seed=grouping_seed) + # Create test panel and pure train + test_data, test_data_sketches = \ + create_test_panel_and_pure_train(top_n_sketches, whole_data_map, initial_test_fname, + initial_train_fname, min_support=min_support) + # Create knockout data + control_removed = create_knockout_files_from_grouped_sketches(grouped_sketches, test_data_sketches, + test_data, whole_data_map, final_output_dir, + dry_run=create_control) + if create_control: + # Create randomized control data + create_random_control_files_from_grouped_sketches(grouped_sketches, test_data_sketches, test_data, + whole_data_map, control_removed, final_output_dir, + seed=create_control_seed) + else: + # ================================== + # Knockout all top sketches + # ================================== + # Create test panel and pure train + test_data, test_data_sketches = \ + create_test_panel_and_pure_train(top_n_sketches, whole_data_map, initial_test_fname, + initial_train_fname, min_support=min_support) + # Create knockout data + control_removed = create_knockout_files_from_sketches(test_data_sketches, test_data, whole_data_map, + final_output_dir, dry_run=create_control) + if create_control: + # Create randomized control data + create_random_control_files_from_sketches(test_data_sketches, test_data, whole_data_map, + control_removed, final_output_dir, + seed=create_control_seed) + if section_found: + print(f'Total processing time: {datetime.now()-start}') + else: + print(f'No section starting with "make_sketches_table_" or "prepare_knockout_" in {conf_file}.') + +# ============================================= +# Make sketches frequency table +# ============================================= + +def compute_sketches_freq_table(input_dir:str, N:int, output_file:str, skip_files:List[str]=[], verbose:bool=True): + ''' + Computes syntax sketches for conllu files in the input_dir, extracts top N most frequent sketches + and saves into CSV file output_file (must end with '.csv'). + Assumes that all conllu files in the input_dir have been created via script "01b_extract_clauses.py", + that is, they contain clauses instead of sentences. + Optionally, you can skip some of the conllu input files from computations via parameter skip_files. + Returns DataFrame with most frequent sketches and their frequencies. + ''' + assert output_file.endswith('.csv'), f'(!) Unexpected file ending for a csv file {output_file!r}' + sketches, clauses_count_total = compute_sketches( input_dir, skip_files=skip_files, verbose=verbose ) + sketch_counter = Counter(sketches) + df = DataFrame({ + 'sketch': [sketch for sketch, _ in sketch_counter.most_common(N)], + 'support': [count for _, count in sketch_counter.most_common(N)] + }) + if verbose: + print(f'Writing sketches freq table into {output_file}.') + df.to_csv( output_file ) + return df + +# ============================================= +# Prepare sketches data for knockout exp +# ============================================= + +def load_clauses_datasets(input_dir:str, skip_files:List[str]=[], verbose:bool=True): + ''' + Loads train, dev and test datasets from conllu files in the input_dir. + The input_dir must contain exactly one file of each type. + Assumes that all conllu files in the input_dir have been created via + script "01b_extract_clauses.py", that is, they contain clauses instead + of sentences. + Returns dictionary mapping keys {'train', 'dev', 'test'} to lists + [file_name, list_of_clause_conllu_strings, list_of_clause_dicts]. + ''' + # Load data from conllu files + whole_data = [] + for fname in os.listdir(input_dir): + if fname in skip_files: + continue + if fname.endswith('.conllu'): + # import clauses as conllu strings + clauses = import_clauses( os.path.join(input_dir, fname), as_dicts=False ) + # import clauses as dicts (required for sketch creation) + clause_dicts = import_clauses( os.path.join(input_dir, fname), as_dicts=True ) + # sanity check + assert len(clauses) == len(clause_dicts), f'{len(clauses)} vs {len(clause_dicts)}' + if verbose: + print( fname, f'| #clauses: {len(clauses)}' ) + whole_data.append( (fname, clauses, clause_dicts) ) + # Map loaded train, test, dev sets to named shortcuts + whole_data_map = {} + for (fname, clauses, clause_dicts) in whole_data: + if 'train' in fname: + assert 'train' not in whole_data_map.keys(), \ + f'More than 1 train files: {fname} vs {whole_data_map["train"][0]}' + whole_data_map['train'] = [fname, clauses, clause_dicts] + elif 'dev' in fname: + assert 'dev' not in whole_data_map.keys(), \ + f'More than 1 dev files: {fname} vs {whole_data_map["dev"][0]}' + whole_data_map['dev'] = [fname, clauses, clause_dicts] + elif 'test' in fname: + assert 'test' not in whole_data_map.keys(), \ + f'More than 1 test files: {fname} vs {whole_data_map["test"][0]}' + whole_data_map['test'] = [fname, clauses, clause_dicts] + assert whole_data_map.keys() == {'train', 'dev', 'test'} + return whole_data_map + +def write_conllu_file(out_fpath:str, clauses_conllus:List[str]): + ''' + Saves clause conllu strings (clauses_conllus) into file out_fpath. + An existing file will be overwritten. + ''' + assert out_fpath.endswith('.conllu'), \ + f'(!) Unexpected output file ending in {out_fpath}. Expected file ending with .conllu' + with open(out_fpath, 'w', encoding='utf-8') as fout: + fout.write('\n\n'.join( clauses_conllus )) + # Add final empty line (to avoid UDError: The CoNLL-U file does not end with empty line) + fout.write('\n\n') + +def create_test_panel_and_pure_train(top_sketches_list:List[Any], whole_data_map:Dict[str,List[Any]], + test_file:str, train_file:str, min_support:int=50, + verbose:bool=True) -> Tuple[List[str], List[List[str]]]: + ''' + Creates a panel test set, extracting all occurrences of top_sketches_list from test set. + If test set does not contain at least min_support (default: 50) clauses of a sketch, + then extracts and removes missing number of clauses from the train set. + Saves the panel test set into test_fname, and purified train set (that has no overlap + with test panel) into train_file. + Returns tuple (list_of_sketch_names, list_of_corresponding_clause_conllus) + ''' + c = 0 + test_data = [] + test_data_sketches = [] + for sketch, support in sorted(top_sketches_list, key=lambda x:x[1], reverse=True): + # Extract all sketches from the test set + test_conllu = whole_data_map['test'][1] + test_dicts = whole_data_map['test'][2] + extracted_conllu, extracted_dicts, extracted_amount = \ + extract_sketches(test_conllu, test_dicts, sketch) + if verbose: + print(sketch) + print( f' extracted {extracted_amount} clauses from test' ) + test_data.append( extracted_conllu ) + test_data_sketches.append( sketch ) + if extracted_amount < min_support: + # If the test set does not contain enough instances (at least min_support), + # we take them form the training set. There are enough clauses in the training + # set so this should not effect the overall performance. + train_conllu = whole_data_map['train'][1] + train_dicts = whole_data_map['train'][2] + extracted_train_conllu, extracted_train_dicts, extracted_train_amount = \ + extract_sketches(train_conllu, train_dicts, sketch, amount=min_support-extracted_amount) + if verbose: + print( f' extracted {extracted_train_amount} clauses from train' ) + # Extend the last sub list in new test + test_data[-1].extend( extracted_train_conllu ) + # Remove extracted clauses from train + new_train_conllu, new_train_dicts, deletion_counts = \ + remove_extracted_from_conllu_and_dicts(train_conllu, train_dicts, extracted_train_conllu) + assert len(new_train_conllu) == len(train_conllu) - sum(deletion_counts.values()) + assert len(new_train_dicts) == len(train_dicts) - sum(deletion_counts.values()) + # Update train (make it pure from test) + whole_data_map['train'][1] = new_train_conllu + whole_data_map['train'][2] = new_train_dicts + c += 1 + assert len(test_data) == len(test_data_sketches) + # Write out test panel file + flat_test_data = [line for conll_list in test_data for line in conll_list] + write_conllu_file( test_file, flat_test_data ) + if verbose: + print(f' Saved {len(flat_test_data)} test clauses into {test_file}.') + # Write out purified train file (all clauses from test panel have been removed) + write_conllu_file( train_file, whole_data_map['train'][1] ) + if verbose: + print(f' Saved {len(whole_data_map["train"][1])} train clauses into {train_file}.') + return test_data, test_data_sketches + +# ================================== +# Knockout grouped top sketches +# ================================== + +def group_top_sketches_randomly(top_sketches_list:List[Any], N:int, output_csv_file:str, seed:int=1, + flatten_groups:bool=True): + ''' + Distributes items of top_sketches_list randomly into N roughly equal size bins. + Assumes top_sketches_list is a list of lists, where each sub list contains two + items: sketch name (str) and its frequency (int). + Returns list of lists of lists, where there are N sub lists and each sub list + contains two item lists (sketch name and support). + If flatten_groups==True (default), then returns list of lists, where there are + N sub lists and each sub list contains only sketch names. + ''' + grouped = rand_group_sketches( top_sketches_list, N, seed=seed ) + DataFrame({ + 'grouped_sketches': [';'.join([s[0] for s in gr]) for gr in grouped], + 'support': [sum([s[1] for s in gr]) for gr in grouped] + }).to_csv( output_csv_file ) + if flatten_groups: + # Flatten list: remove sketch frequencies, + # keep only sketch names + new_grouped = [] + for group in grouped: + assert all([isinstance(sketch[0], str) for sketch in group]) + sketches_list = [sketch[0] for sketch in group] + new_grouped.append(sketches_list) + grouped = new_grouped + return grouped + +def create_knockout_files_from_grouped_sketches(grouped_sketches:List[Any], test_data_sketches:List[str], + test_data:List[List[str]], whole_data_map:Dict[str,List[Any]], + output_dir:str, dry_run:bool=False, verbose:bool=True) -> List[Tuple[int, int, int]]: + ''' + Creates knockout train, dev and test files based on the grouped_sketches. + For each sketch group, removes all clauses corresponding to sketches of the + group from train and dev, and creates a subset of test set containing only + clauses corresponding to sketches of the group. + Saves resulting files into output_dir, under names 'test_group{GID}.conllu', + 'train_group{GID}.conllu', 'dev_group{GID}.conllu', where GID is the index + of the sketch group. + If dry_run==True, then only imitates saving sketches into files, but does + not write any actual files. Use this option if you want to collect removal + statistics for preparation of control experiments. + Returns tuple: + (sketch group index, no of clauses removed from train, no of clauses removed from dev) + ''' + assert len(test_data_sketches) == len(test_data) + control_removed = [] # (sketch group, no of clauses removed from train, no of clauses removed from dev) + for gid, group in enumerate( grouped_sketches ): + assert isinstance(group, list) and all(isinstance(s, str) for s in group) + if verbose: + print(f'group: {gid}') + # + # Subset for test + # + # Collect all test clauses of given group + group_clauses = [] + for sketch, test_conll in zip(test_data_sketches, test_data): + if sketch in group: + if verbose: + print(sketch) + group_clauses.extend(test_conll) + out_test_fname = f'test_group{gid}.conllu' + if not dry_run: + write_conllu_file( os.path.join(output_dir, out_test_fname), group_clauses ) + if verbose: + print(f' Saved {len(group_clauses)} test clauses into {out_test_fname}.') + # + # Extract subset of dev + # + all_conllu = whole_data_map['dev'][1] + all_dicts = whole_data_map['dev'][2] + # Remove and return remaining clauses + # Note: the removal is virtual, actual lists are not affected + preserved_conllu, preserved_dicts, removed_amount_dev = \ + remove_sketches_group(all_conllu, all_dicts, group) + out_dev_fname = f'dev_group{gid}.conllu' + if not dry_run: + write_conllu_file( os.path.join(output_dir, out_dev_fname), preserved_conllu ) + if verbose: + print(f' Removed {removed_amount_dev} clauses and saved remaining {len(preserved_conllu)} dev clauses into {out_dev_fname}.') + # + # Extract subset of train + # + # Assume that instances appearing in test set + # have already been removed in previous steps + # (we have a "pure train set") + all_conllu = whole_data_map['train'][1] + all_dicts = whole_data_map['train'][2] + # Remove and return remaining clauses + # Note: the removal is virtual, actual lists are not affected + preserved_conllu, preserved_dicts, removed_amount_train = \ + remove_sketches_group(all_conllu, all_dicts, group) + out_train_fname = f'train_group{gid}.conllu' + if not dry_run: + write_conllu_file( os.path.join(output_dir, out_train_fname), preserved_conllu ) + if verbose: + print(f' Removed {removed_amount_train} clauses and saved remaining {len(preserved_conllu)} train clauses into {out_train_fname}.') + # Remember how much we removed (for control experiments) + control_removed.append( (gid, removed_amount_train, removed_amount_dev) ) + return control_removed + + +def create_random_control_files_from_grouped_sketches(grouped_sketches:List[Any], test_data_sketches:List[str], + test_data:List[List[str]], whole_data_map:Dict[str,List[Any]], + control_removed:List[Tuple[int, int, int]], output_dir:str, + seed:int=5, verbose:bool=True) -> List[Tuple[int, int, int]]: + ''' + Creates random control train, dev and test files for grouped knockout experiments. + For each sketch group, removes randomly the same amount of clauses that were removed + in knockout dataset preparation (create_knockout_files_from_grouped_sketches) from + train and dev files. Group's test set will be the same as in knockout dataset + preparation. + Saves resulting files into output_dir, under names 'test_group{GID}.conllu', + 'train_group{GID}.conllu', 'dev_group{GID}.conllu', where GID is the index + of the sketch group. + ''' + assert len(test_data_sketches) == len(test_data) + rnd = Random() + rnd.seed(seed) + for (gid, removed_from_train, removed_from_dev) in control_removed: + group = grouped_sketches[gid] + assert isinstance(group, list) and all(isinstance(s, str) for s in group) + if verbose: + print(f'group: {gid}') + # + # Subset for test + # + # Collect all test clauses of given group + group_clauses = [] + for sketch, test_conll in zip(test_data_sketches, test_data): + if sketch in group: + if verbose: + print(sketch) + group_clauses.extend(test_conll) + out_test_fname = f'test_group{gid}.conllu' + write_conllu_file( os.path.join(output_dir, out_test_fname), group_clauses ) + if verbose: + print(f' Saved {len(group_clauses)} test clauses into {out_test_fname}.') + # + # Pick randomly same amount of clauses from train & dev + # + train_pure = whole_data_map['train'][1] + dev_full = whole_data_map['dev'][1] + train_sample = rnd.sample(range(0, len(train_pure)+1), removed_from_train) + dev_sample = rnd.sample(range(0, len(dev_full)+1), removed_from_dev) + # + # Remove randomly picked clause instances + # + ablation_train = [] + ablation_dev = [] + for i, conllu in enumerate(train_pure): + if i in train_sample: + continue + ablation_train.append(conllu) + for i, conllu in enumerate(dev_full): + if i in dev_sample: + continue + ablation_dev.append(conllu) + # + # Save results + # + out_dev_fname = f'dev_group{gid}.conllu' + write_conllu_file( os.path.join(output_dir, out_dev_fname), ablation_dev ) + if verbose: + print(f' Removed {removed_from_dev} clauses randomly and saved remaining {len(ablation_dev)} dev clauses into {out_dev_fname}.') + out_train_fname = f'train_group{gid}.conllu' + write_conllu_file( os.path.join(output_dir, out_train_fname), ablation_train ) + if verbose: + print(f' Removed {removed_from_train} clauses randomly and saved remaining {len(ablation_train)} train clauses into {out_train_fname}.') + + +# ================================== +# Knockout all top sketches +# ================================== + +def create_knockout_files_from_sketches(test_data_sketches:List[str], test_data:List[List[str]], + whole_data_map:Dict[str,List[Any]], output_dir:str, + dry_run:bool=False, verbose:bool=True) -> List[Tuple[int, int, int]]: + ''' + Creates knockout train, dev and test files based for all test_data_sketches. + For sketch in test_data_sketches, removes its clauses from train and dev, and + creates a subset of test set containing only clauses of this sketch. + Saves resulting files into output_dir, under names 'test_{SKETCH}.conllu', + 'train_{SKETCH}.conllu', 'dev_{SKETCH}.conllu', where SKETCH is the name + of the sketch (made safe via safe_sketch_name(...)). + If dry_run==True, then only imitates saving sketches into files, but does + not write any actual files. Use this option if you want to collect removal + statistics for preparation of control experiments. + Returns tuple: + (sketch name, no of clauses removed from train, no of clauses removed from dev) + ''' + assert len(test_data_sketches) == len(test_data) + control_removed = [] # (sketch name, no of clauses removed from train, no of clauses removed from dev) + for sketch, test_conll in zip(test_data_sketches, test_data): + sketch_id = safe_sketch_name(sketch) + if verbose: + print(sketch) + # + # Subset for test + # + out_test_fname = f'test_{sketch_id}.conllu' + if not dry_run: + write_conllu_file( os.path.join(output_dir, out_test_fname), test_conll ) + if verbose: + print(f' Saved {len(test_conll)} test clauses into {out_test_fname}.') + # + # Extract subset of dev + # + all_conllu = whole_data_map['dev'][1] + all_dicts = whole_data_map['dev'][2] + # Remove sketch and return remaining clauses + # Note: the removal is virtual, actual lists are not affected + preserved_conllu, preserved_dicts, removed_amount_dev = \ + remove_sketches(all_conllu, all_dicts, sketch) + out_dev_fname = f'dev_{sketch_id}.conllu' + if not dry_run: + write_conllu_file( os.path.join(output_dir, out_dev_fname), preserved_conllu ) + if verbose: + print(f' Removed {removed_amount_dev} clauses and saved remaining {len(preserved_conllu)} dev clauses into {out_dev_fname}.') + # + # Extract subset of train + # + # Assume that instances appearing in test set + # have already been removed in previous steps + # (we have a "pure train set") + all_conllu = whole_data_map['train'][1] + all_dicts = whole_data_map['train'][2] + # Remove sketch and return remaining clauses + # Note: the removal is virtual, actual lists are not affected + preserved_conllu, preserved_dicts, removed_amount_train = \ + remove_sketches(all_conllu, all_dicts, sketch) + out_train_fname = f'train_{sketch_id}.conllu' + if not dry_run: + write_conllu_file( os.path.join(output_dir, out_train_fname), preserved_conllu ) + if verbose: + print(f' Removed {removed_amount_train} clauses and saved remaining {len(preserved_conllu)} train clauses into {out_train_fname}.') + # Remember how much we removed (for control experiments) + control_removed.append( (sketch, removed_amount_train, removed_amount_dev) ) + return control_removed + + +def create_random_control_files_from_sketches(test_data_sketches:List[str], test_data:List[List[str]], + whole_data_map:Dict[str,List[Any]], control_removed:List[Tuple[int, int, int]], + output_dir:str, seed:int=5, verbose:bool=True) -> List[Tuple[int, int, int]]: + ''' + Creates random control train, dev and test files for single sketch knockout experiments. + For sketch in test_data_sketches, removes randomly the same amount of clauses that were + removed in knockout dataset preparation (create_knockout_files_from_sketches) from + train and dev files. Group's test set will be the same as in knockout dataset + preparation. + Saves resulting files into output_dir, under names 'test_{SKETCH}.conllu', + 'train_{SKETCH}.conllu', 'dev_{SKETCH}.conllu', where SKETCH is the name + of the sketch (made safe via safe_sketch_name(...)). + ''' + assert len(test_data_sketches) == len(test_data) + rnd = Random() + rnd.seed(seed) + for (sketch, removed_from_train, removed_from_dev) in control_removed: + sketch_id = safe_sketch_name(sketch) + if verbose: + print(sketch) + # + # Subset for test + # + # Collect all test clauses of the sketch + sketch_clauses = [] + for sketch2, test_conll in zip(test_data_sketches, test_data): + if sketch2 == sketch: + sketch_clauses.extend(test_conll) + break + out_test_fname = f'test_{sketch_id}.conllu' + write_conllu_file( os.path.join(output_dir, out_test_fname), sketch_clauses ) + if verbose: + print(f' Saved {len(sketch_clauses)} test clauses into {out_test_fname}.') + # + # Pick randomly same amount of clauses from train & dev + # + train_pure = whole_data_map['train'][1] + dev_full = whole_data_map['dev'][1] + train_sample = rnd.sample(range(0, len(train_pure)+1), removed_from_train) + dev_sample = rnd.sample(range(0, len(dev_full)+1), removed_from_dev) + # + # Remove randomly picked clause instances + # + ablation_train = [] + ablation_dev = [] + for i, conllu in enumerate(train_pure): + if i in train_sample: + continue + ablation_train.append(conllu) + for i, conllu in enumerate(dev_full): + if i in dev_sample: + continue + ablation_dev.append(conllu) + # + # Save results + # + out_dev_fname = f'dev_{sketch_id}.conllu' + write_conllu_file( os.path.join(output_dir, out_dev_fname), ablation_dev ) + if verbose: + print(f' Removed {removed_from_dev} clauses randomly and saved remaining {len(ablation_dev)} dev clauses into {out_dev_fname}.') + out_train_fname = f'train_{sketch_id}.conllu' + write_conllu_file( os.path.join(output_dir, out_train_fname), ablation_train ) + if verbose: + print(f' Removed {removed_from_train} clauses randomly and saved remaining {len(ablation_train)} train clauses into {out_train_fname}.') + +# ============================================= +# ============================================= + +if __name__ == '__main__': + if len(sys.argv) < 2: + raise Exception('(!) Missing input argument: name of the configuration INI file.') + # Try to execute all input files as configurations + for conf_file in sys.argv[1:]: + prepare_sketches_main( conf_file ) diff --git a/02_split_data.py b/02_split_data.py new file mode 100644 index 00000000..24ccd1a1 --- /dev/null +++ b/02_split_data.py @@ -0,0 +1,697 @@ +# +# Creates data splits/joins of the following experiments: +# +# * crossvalidation -- split data into N groups; use each +# unique group only once as a test set; +# * smaller_data -- split data into N groups with increasing +# training set sizes; +# * half_data -- split data into N groups; each group has +# training size half of the available training +# size; +# * single_file -- split single file into train, dev and test. +# (mainly used for debbuging pipeline) +# * full_data -- train and dev sets are joined into one set +# (for final evaluation); +# + +import csv +import os +import os.path +import sys +import random +import warnings +import json + +import conllu +import configparser + +# =============================================================== +# Create data splits/joins required by experiments (MAIN) +# =============================================================== + +def create_train_splits_joins_main( conf_file ): + ''' + Creates data splits (or joins) based on the configuration. + Settings/parameters of the conversion will be read from the given + `conf_file`. + Executes sections in the configuration starting with prefixes + 'split_' and 'join_'. + + For details about the conversion and possible parameters, + see the functions: + * `create_crossvalidation_splits(...)` + * `create_smaller_data_splits(...)` + * `create_half_data_splits(...)` + * `create_single_file_split(...)` + ''' + # Parse configuration file + config = configparser.ConfigParser() + if conf_file is None or not os.path.exists(conf_file): + raise FileNotFoundError("Config file {} does not exist".format(conf_file)) + if len(config.read(conf_file)) != 1: + raise ValueError("File {} is not accessible or is not in valid INI format".format(conf_file)) + section_found = False + for section in config.sections(): + if section.startswith('join_'): + # + # Load joining configuration from the section + # Check validity of the parameters + # + section_found = True + print(f'Performing {section} ...') + if not config.has_option(section, 'input_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "input_dir" parameter.') + input_dir = config[section]['input_dir'] + if not os.path.isdir(input_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "input_dir" value {input_dir!r} in {section!r}.') + concatenate = config[section].get('concatenate', 'train, dev') + target_subsets = concatenate.split(',') + if len(target_subsets) != 2: + raise ValueError(f'Error in {conf_file}: {section}.concatenate must have 2 values, '+\ + f'not {len(target_subsets)}.' ) + train_full_name = config[section].get('train_full', 'train_full.conllu') + # Collect input files + concatenate_files = [] + for subset in target_subsets: + subset = subset.strip() + if subset not in ['train', 'dev', 'test']: + raise ValueError(f'Error in {conf_file}: {section}.concatenate has invalid value {subset}.') + # Find corresponding file from the input dir + for fname in os.listdir(input_dir): + if fname == train_full_name: + continue + if fname.endswith('.conllu') and subset in fname: + concatenate_files.append(os.path.join(input_dir, fname)) + if len(concatenate_files) != 2: + raise ValueError(f'Unable to get 2 concatenateable conllu files from dir {input_dir!r}. '+ + 'Is the directory correct?') + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_dir = config[section]['output_dir'] + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + # + # Concatenate two files (typically train and dev) into one file + # + train_full_file = os.path.join(output_dir, train_full_name) + join_train_dev( train_path=concatenate_files[0], dev_path=concatenate_files[1], + output_path=train_full_file ) + elif section.startswith('split_'): + # + # Load spltting configuration from the section + # Check validity of the parameters + # + section_found = True + print(f'Performing {section} ...') + split_type = config[section].get('split_type', 'crossvalidation') + split_type_clean = (split_type.strip()).lower() + if split_type_clean not in ['crossvalidation', 'smaller_data', 'half_data', 'single_file']: + raise ValueError('(!) Unexpected split type value: {!r}'.format(split_type)) + if split_type_clean == 'single_file': + # ----------------------------------- + # 'single_file_split' + # ----------------------------------- + if not config.has_option(section, 'input_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "input_file" parameter.') + input_file = config[section]['input_file'] + split_ratio = config[section].get('split_ratio', '80, 10, 10') + split_subset_ratios = [] + for subset in split_ratio.split(','): + subset = subset.strip() + if not subset.isnumeric(): + raise ValueError(f'Error in {conf_file}: {section}.split_ratio has invalid value {subset}.') + split_subset_ratios.append( int(subset) ) + if len(split_subset_ratios) != 3: + raise ValueError(f'Error in {conf_file}: {section}.split_ratio has invalid value {subset}: must have 3 ratios.') + seed = config[section].getint('seed', 9) + shuffle = config[section].getboolean('shuffle', False) + subset_size = config[section].getint('subset_size', None) + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_dir = config[section]['output_dir'] + create_single_file_split(input_file, output_dir, + train=split_subset_ratios[0], + dev=split_subset_ratios[1], + test=split_subset_ratios[2], + subset_size=subset_size, + shuffle=shuffle, + seed=seed) + else: + # ----------------------------------- + # 'crossvalidation', + # 'smaller_data', + # 'half_data' + # ----------------------------------- + if not config.has_option(section, 'input_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "input_dir" parameter.') + input_dir = config[section]['input_dir'] + if not os.path.isdir(input_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "input_dir" value {input_dir!r} in {section!r}.') + concatenate = config[section].get('concatenate', 'train, dev') + target_subsets = concatenate.split(',') + if len(target_subsets) != 2: + raise ValueError(f'Error in {conf_file}: {section}.concatenate must have 2 values, '+\ + f'not {len(target_subsets)}.' ) + # Collect input files + concatenate_files = [] + for subset in target_subsets: + subset = subset.strip() + if subset not in ['train', 'dev', 'test']: + raise ValueError(f'Error in {conf_file}: {section}.concatenate has invalid value {subset}.') + # Find corresponding file from the input dir + for fname in os.listdir(input_dir): + if fname == 'train_full.conllu': + continue + if fname.endswith('.conllu') and subset in fname: + concatenate_files.append(os.path.join(input_dir, fname)) + if len(concatenate_files) != 2: + raise ValueError(f'Unable to get 2 concatenateable conllu files from dir {input_dir!r}. '+ + 'Is the directory correct?') + split_file = config[section].get('split_file', 'splits.csv') + block_count = config[section].getint('block_count', 195) + split_count = config[section].getint('split_count', 10) + seed = config[section].getint('seed', 9) + if not config.has_option(section, 'first_output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "first_output_dir" parameter.') + first_output_dir = config[section]['first_output_dir'] + if not config.has_option(section, 'final_output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "final_output_dir" parameter.') + final_output_dir = config[section]['final_output_dir'] + # + # Perform splitting according to the split type + # + if split_type_clean == 'crossvalidation': + create_crossvalidation_splits(concatenate_files[0], concatenate_files[1], first_output_dir, + final_output_dir, split_csv_file=split_file, block_count=block_count, + split_count=split_count, seed=seed) + if split_type_clean == 'smaller_data': + create_smaller_data_splits(concatenate_files[0], concatenate_files[1], first_output_dir, + final_output_dir, split_csv_file=split_file, block_count=block_count, + split_count=split_count, seed=seed) + if split_type_clean == 'half_data': + # Warn about parameters not changable (not implemented) + for param in ['block_count', 'split_count']: + if config.has_option(section, param): + msg = f'In {conf_file}, section {section!r}: parameter {param!r} not changable in half_data experiments.' + warnings.warn( msg ) + create_half_data_splits(concatenate_files[0], concatenate_files[1], first_output_dir, final_output_dir, + split_csv_file=split_file, seed=seed) + if not section_found: + print(f'No section starting with "split_" or "join_" in {conf_file}.') + + +def create_crossvalidation_splits(train_file, dev_file, first_splits_path, crossval_split_path, + split_csv_file='splits.csv', block_count=195, split_count=10, seed=9): + ''' + Creates crossvalidation splits based on given input CONLLU files (`train_file` and `dev_file`). + First, concatenates train_file and dev_file into one file and saves under first_splits_path. + Then splits the concatenated conllu file randomly into `split_count` sub sets. + + The splitting process: 1) the input is first split at the word-level into roughly + equal-sized blocks (`block_count` blocks), 2) the order of blocks is shuffled, and + 3) the shuffling result is split into `split_count` sub sets. + + Saves sub sets into folder `first_splits_path`. + + Finally, rotates over subsets in a way that each sub set belongs to test and dev at once, + and the remaining subsets belong to train. Saves results (crossvalidation subsets) into + folder `crossval_split_path`. + ''' + # Validate inputs + if not os.path.exists(train_file) or not os.path.isfile(train_file): + raise FileNotFoundError(f'(!) Missing or invalid input file {train_file}') + if not os.path.exists(dev_file) or not os.path.isfile(dev_file): + raise FileNotFoundError(f'(!) Missing or invalid input file {dev_file}') + if not os.path.exists(first_splits_path): + os.makedirs(first_splits_path, exist_ok=True) + + # Concatenate train and dev into one file + train_full_file = os.path.join(first_splits_path, 'train_full.conllu') + join_train_dev(train_path=train_file, dev_path=dev_file, output_path=train_full_file) + # Split train_full_file randomly into `split_count` sub sets (roughly equal size blocks) + splitting(input_file=train_full_file, output_dir=first_splits_path, split_file=split_csv_file, + block_count=block_count, split_count=split_count, seed=seed) + + # Divide files to dev - test - train, save into crossvalidation folder + if not os.path.exists(crossval_split_path): + os.makedirs(crossval_split_path, exist_ok=True) + split_conllu_files = [fname for fname in os.listdir(first_splits_path) if fname.endswith('.conllu')] + split_conllu_files.remove('train_full.conllu') + + # CSV file with logging/debugging info + splits_csv = open(os.path.join(crossval_split_path, split_csv_file), 'w', newline='', encoding='utf-8') + csv_writer = csv.writer(splits_csv) + csv_writer.writerow(['split', 'dev', 'test', 'train']) + + # Rotating splits + for i in range(len(split_conllu_files)): + split_key = '{:02d}'.format(i+1) + print(split_key) + + dev = split_conllu_files[i] + if i == len(split_conllu_files) - 1: + test = split_conllu_files[0] + train = split_conllu_files[1:len(split_conllu_files) - 1] + else: + test = split_conllu_files[i+1] + train = split_conllu_files[i + 2:] + split_conllu_files[:i] + + csv_writer.writerow([i+1, dev, test, train]) + + # Create new splits into crossvalidation data folder + with open(os.path.join(crossval_split_path, '{}_dev.conllu'.format(split_key)), 'w', encoding='utf-8') as fout: + fout.write(join_file_contents(first_splits_path, [dev])) + + with open(os.path.join(crossval_split_path, '{}_test.conllu'.format(split_key)), 'w', encoding='utf-8') as fout: + fout.write(join_file_contents(first_splits_path, [test])) + + with open(os.path.join(crossval_split_path, '{}_train.conllu'.format(split_key)), 'w', encoding='utf-8') as fout: + fout.write(join_file_contents(first_splits_path, train)) + + with open(os.path.join(crossval_split_path, '{}_train_all.conllu'.format(split_key)), 'w', encoding='utf-8') as fout: + train_all = train + [dev] + fout.write(join_file_contents(first_splits_path, train_all)) + + splits_csv.close() + + +def create_smaller_data_splits(train_file, dev_file, first_splits_path, smaller_data_split_path, + split_csv_file='splits.csv', block_count=195, split_count=10, seed=9): + ''' + Creates dataset size increasing crossvalidation splits based on given input CONLLU files + (`train_file` and `dev_file`). + First, concatenates train_file and dev_file into one file and saves under first_splits_path. + Then splits the concatenated conllu file randomly into `split_count` sub sets. + + The splitting process: 1) the input is first split at the word-level into roughly + equal-sized blocks (`block_count` blocks), 2) the order of blocks is shuffled, and + 3) the shuffling result is split into `split_count` sub sets. + + Saves initial sub sets into folder `first_splits_path`. + + Creates `split_count` training sets with gradually increasing size: first set contains + only 1 sub set from `first_splits_path`, second set contains 2 sub sets from `first_splits_path`, + and so on. + In the process, these training sets ("train_all" sets) will be further split into train and + dev subsets, with increasing dev set sizes / proportions: 10% if training consist of 1-3 sub sets, + 15% if training consists of 4-6 sub sets, and 20% if training is larger than 6 sub sets. + Finally, saves results (sub sets with increasing sizes) into folder `smaller_data_split_path`. + ''' + if not os.path.exists(train_file) or not os.path.isfile(train_file): + raise FileNotFoundError(f'(!) Missing or invalid input file {train_file}') + if not os.path.exists(dev_file) or not os.path.isfile(dev_file): + raise FileNotFoundError(f'(!) Missing or invalid input file {dev_file}') + if not os.path.exists(first_splits_path): + os.makedirs(first_splits_path, exist_ok=True) + + # Concatenate train and dev into one file + train_full_file = os.path.join(first_splits_path, 'train_full.conllu') + join_train_dev(train_path=train_file, dev_path=dev_file, output_path=train_full_file) + # Split train_full_file randomly into `split_count` sub sets (roughly equal size blocks) + splitting(input_file=train_full_file, output_dir=first_splits_path, split_file=split_csv_file, + block_count=block_count, split_count=split_count, seed=seed) + + if not os.path.exists(smaller_data_split_path): + os.makedirs(smaller_data_split_path, exist_ok=True) + + # read all splits to list: + undivided_splits = [fname for fname in os.listdir(first_splits_path) if fname.endswith('.conllu')] + undivided_splits.remove('train_full.conllu') + + # CSV file with logging/debugging info + splits_csv = open(os.path.join(smaller_data_split_path, split_csv_file), 'w', newline='', encoding='utf-8') + csv_writer = csv.writer(splits_csv) + csv_writer.writerow(['split', 'train']) + + def create_train_dev(training_whole, development_percentage): + divider = len(training_whole) // 100 * (100 - development_percentage) # point between train and dev + training_train = training_whole[:divider] + training_dev = training_whole[divider:] + return training_train, training_dev + + for j in range(len(undivided_splits), 0, -1): + split_key = '{:03d}'.format(j*10) + print(split_key) + + # save splits by sentence-ids + csv_writer.writerow([split_key, undivided_splits]) + + # % of sentences to be divided for development + dev_percentage = 20 + if j in [6, 5, 4]: + dev_percentage = 15 + elif j in [3, 2, 1]: + dev_percentage = 10 + + # Get conllu TokenLists from IDs + data = '' + for filename in undivided_splits: + with open(os.path.join(first_splits_path, filename), "r", encoding="utf-8") as split: + data += split.read() + + training_whole = conllu.parse(data) + + training_train, training_dev = create_train_dev(training_whole, dev_percentage) + + assert len(training_dev) + len(training_train) == len(training_whole) + + with open(os.path.join(smaller_data_split_path, f'{split_key}_train_all.conllu'), 'w', encoding='utf-8') as tr: + tr.write(create_conllu(training_whole)) + with open(os.path.join(smaller_data_split_path, f'{split_key}_train.conllu'), 'w', encoding='utf-8') as tr: + tr.write(create_conllu(training_train)) + with open(os.path.join(smaller_data_split_path, f'{split_key}_dev.conllu'), 'w', encoding='utf-8') as dev: + dev.write(create_conllu(training_dev)) + undivided_splits.pop( random.Random(4).randrange(len(undivided_splits)) ) + random.Random(4).shuffle( undivided_splits ) + + splits_csv.close() + + +def create_half_data_splits(train_file, dev_file, first_splits_path, half_data_split_path, + split_csv_file='splits.csv', seed=9): + ''' + Creates half training set size crossvalidation splits based on given input CONLLU files + (`train_file` and `dev_file`). + First, concatenates `train_file` and `dev_file` into one file and saves under + `first_splits_path`. + + The splitting process: 1) the concatenated conllu file is first split into 194 blocks of + sentences, each block roughly same size in words. + 2) blocks are shuffled and two different train-dev-test splits are formed, with sizes: + 78 blocks for train, 19 for dev and 97 test (one selects first half for train-dev, other + selects the second half for train-dev). + 3) step 2 gets repeated 5 times, resulting in 10 splits at total. + + Finally, saves results (sub sets with halved train sizes) into folder `half_data_split_path`. + ''' + # Validate inputs + if not os.path.exists(train_file) or not os.path.isfile(train_file): + raise FileNotFoundError(f'(!) Missing or invalid input file {train_file}') + if not os.path.exists(dev_file) or not os.path.isfile(dev_file): + raise FileNotFoundError(f'(!) Missing or invalid input file {dev_file}') + if not os.path.exists(first_splits_path): + os.makedirs(first_splits_path, exist_ok=True) + + # Concatenate train and dev into one file + train_full_file = os.path.join(first_splits_path, 'train_full.conllu') + join_train_dev(train_path=train_file, dev_path=dev_file, output_path=train_full_file) + + # Extract all tokens and label them with sentence id-s + all_sentences = [] + with open(train_full_file, 'r', encoding='utf-8') as in_f: + all_sentences = conllu.parse( in_f.read() ) + assert len(all_sentences) > 0 + full_tokenlist = list() + for i, sentence in enumerate(all_sentences): + full_tokenlist.extend([i] * len(sentence)) + + # Splitting given number of sentence blocks, organizing by sentence id-s + # (block_count=194 results in splits of about ~~2000 words) + sentence_blocks = correct_splits(split(full_tokenlist, 194)) + + def extract_sentences(tokenlists, train, dev, test): + train_sents = [tokenlists[no] for block in train for no in block] + test_sents = [tokenlists[no] for block in test for no in block] + dev_sents = [tokenlists[no] for block in dev for no in block] + return train_sents, dev_sents, test_sents + + def save_split_files(train, dev, test, output_path, split_no): + if not os.path.exists(output_path): + os.makedirs(output_path, exist_ok=True) + + with open(os.path.join(output_path, '{}_dev.conllu'.format(split_no)), 'w', encoding='utf-8') as fout: + fout.write(create_conllu(dev)) + + with open(os.path.join(output_path, '{}_test.conllu'.format(split_no)), 'w', encoding='utf-8') as fout: + fout.write(create_conllu(test)) + + with open(os.path.join(output_path, '{}_train.conllu'.format(split_no)), 'w', encoding='utf-8') as fout: + fout.write(create_conllu(train)) + + join_train_dev(train_path=os.path.join(output_path, '{}_train.conllu'.format(split_no)), + dev_path=os.path.join(output_path, '{}_dev.conllu'.format(split_no)), + output_path=os.path.join(output_path, '{}_train_all.conllu'.format(split_no))) + + # CSV file with logging/debugging info + splits_csv = open(os.path.join(half_data_split_path, split_csv_file), 'w', newline='', encoding='utf-8') + csv_writer = csv.writer(splits_csv) + csv_writer.writerow(['split', 'dev', 'test', 'train']) + + # Split corresponding sentence sequence numbers of full train data + rnd = random.Random(seed) + split_counter = 0 + saved_splits = dict() + for i in range(1, 6, 1): + rnd.shuffle(sentence_blocks) + + # 19 blocks for dev + train_1 = sentence_blocks[:78] + dev_1 = sentence_blocks[78:97] + test_1 = sentence_blocks[97:] + + train_2 = sentence_blocks[97:175] + dev_2 = sentence_blocks[175:] + test_2 = sentence_blocks[:97] + + split_key = '{:03d}'.format(split_counter+1) + print(f'{split_key} -- train: #{len(train_1)} sents, dev: #{len(dev_1)} sents, test: #{len(test_1)} sents') + split_counter += 1 + csv_writer.writerow(['split_{}'.format(split_key), dev_1, test_1, train_1]) + train, dev, test = extract_sentences(all_sentences, train_1, dev_1, test_1) + save_split_files(train, dev, test, half_data_split_path, split_key) + + split_key = '{:03d}'.format(split_counter+1) + print(f'{split_key} -- train: #{len(train_2)} sents, dev: #{len(dev_2)} sents, test: #{len(test_2)} sents') + split_counter += 1 + csv_writer.writerow(['split_{}'.format(split_key), dev_2, test_2, train_2]) + train, dev, test = extract_sentences(all_sentences, train_2, dev_2, test_2) + save_split_files(train, dev, test, half_data_split_path, split_key) + + splits_csv.close() + + +def create_single_file_split(input_file, output_dir, train=80, dev=10, test=10, subset_size=None, shuffle=False, seed=9): + ''' + Splits single conllu file (sentence-wise) into train, dev and test sub sets. + Writes sub sets into separate files. + Parameters train, dev, and test correspond to relative sizes of + corresponding sub sets and must add up to 100. + + If shuffle=True, then sentences in the file will be shuffled before + making the split. + + If subset_size is specified, then only the given amount of sentences + will be taken for splitting (applied after shuffling, if shuffling is + enabled). + + This function is used for testing and debugging model training. + ''' + if train+dev+test != 100: + raise ValueError( f'(!) Parameter values train + dev + test ({train} + {dev} + {test}) '+\ + f'do not add up to 100.' ) + all_sentences = [] + with open(input_file, 'r', encoding='utf-8') as in_f: + all_sentences = conllu.parse( in_f.read() ) + if shuffle: + random.Random(seed).shuffle(all_sentences) + if subset_size is not None: + assert isinstance(subset_size, int) + if subset_size > len(all_sentences): + raise ValueError( f'(!) subset_size={subset_size} exceeds the number '+\ + f'of sentences in {input_file!r} ({len(all_sentences)}).' ) + all_sentences = all_sentences[:subset_size] + # Split sentences into train, dev, test + collected_train = [] + collected_dev = [] + collected_test = [] + for i in range(len(all_sentences)): + percentage = int(i*100.0/len(all_sentences)) + if percentage < train: + collected_train.append( all_sentences[i] ) + elif percentage >= train and percentage < train+dev: + collected_dev.append( all_sentences[i] ) + else: + collected_test.append( all_sentences[i] ) + # Sanity check + assert len(collected_train) + len(collected_dev) + len(collected_test) == len(all_sentences) + # Write outputs + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + with open(os.path.join(output_dir, f'train.conllu'), 'w', encoding='utf-8') as tr: + tr.write(create_conllu(collected_train)) + print( f'{len(collected_train)} sentences for training.') + with open(os.path.join(output_dir, f'dev.conllu'), 'w', encoding='utf-8') as dv: + dv.write(create_conllu(collected_dev)) + print( f'{len(collected_dev)} sentences for development.') + with open(os.path.join(output_dir, f'test.conllu'), 'w', encoding='utf-8') as tst: + tst.write(create_conllu(collected_test)) + print( f'{len(collected_test)} sentences for test.') + + +# =============================================================== +# Utilities required by splitting functions +# =============================================================== + +def join_train_dev(train_path, dev_path, output_path): + """ + Concatenates two conllu files into one file. + It is used to concatenate train and dev files. + """ + with open(output_path, 'w', encoding='utf-8') as fout: + train_file = open(train_path, 'r', encoding='utf-8') + dev_file = open(dev_path, 'r', encoding='utf-8') + fout.write(train_file.read()) + fout.write(dev_file.read()) + train_file.close() + dev_file.close() + + +def join_file_contents(input_path: str, filenames: list): + ''' + Reads contents of given (conllu) files from input_path and returns their concatenation. + TODO: merge `join_train_dev` and `join_file_contents` into one function + ''' + data = '' + for filename in filenames: + with open(os.path.join(input_path, filename), 'r', encoding='utf-8') as fin: + data += fin.read() + return data + + +def split(a, n): + """ + Splits list `a` into `n` roughly equal-sized subsets. + If `a` is not exactly divisible by `n`, then finds the + reminder `r` of the division and enlarges sizes of first + `r` subsets by 1. + Returns a generator of the split. + + Examples: + + >>> sp1 = split([1,1,2,2,3,3], 3) + >>> list(sp1) + [[1, 1], [2, 2], [3, 3]] + >>> sp2 = split([1,2,2,3,3,3,4,4,4,4,5,5,5,5,5], 6) + >>> list(sp2) + [[1, 2, 2], [3, 3, 3], [4, 4, 4], [4, 5], [5, 5], [5, 5]] + >> sp3 = split([[1], [2,2], [3,3,3], [4,4,4,4]], 3) + >> list(sp3) + [[[1], [2, 2]], [[3, 3, 3]], [[4, 4, 4, 4]]] + """ + k, m = divmod(len(a), n) + return (a[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in range(n)) + + +def correct_splits(splits): + """ + Corrects given splits (lists of lists of sentence id-s) in a way that + each sentence id can only be in one sub list. Motivation: each sentence + should belong to exactly one cross-validation subset. + + The input is a list of lists, where each sub list element is the number + of sentence the word belongs to, e.g [1, 1, 1, 2, 2, 3, 3, 3]. + Assumingly, the division of corpus words into sub lists has been done with + function `split(a, n)`. + Returns list of lists, where each sublist contains unique id-s of sentences + and one sentence id can be in one list only. + + When run with the default settings (Estonian UD train+dev split into 195 + sub sets), then each sub list will have the size of (approx) 2000 words. + """ + splits = list(splits) # convert generator to list + last_sentences_set = set() # for sanity checking + sentence_splits = [] + + for i, subsplit in enumerate(splits): + current_sentences_set = set(subsplit) + + if i == 0: # Has no previous sentence + sentence_splits.append(sorted(list(current_sentences_set))) + last_sentences_set = current_sentences_set + continue + + # If first sentence of is not complete (is in previous list, too), remove it from current list. + last_id = splits[i - 1][-1] + if last_id in current_sentences_set: + current_sentences_set.remove(last_id) + + assert len(current_sentences_set.intersection(last_sentences_set)) == 0 + + sentence_splits.append(sorted(list(current_sentences_set))) # sort to keep correct order + + last_sentences_set = current_sentences_set + + return sentence_splits + + +def create_conllu(tokenlists): + """Serializes given TokenLists into conllu string that can be written to file.""" + conllu_list = [] + for sentence in tokenlists: + conllu_list.append(sentence.serialize()) + return ''.join(conllu_list) + + +def splitting(input_file, output_dir, split_file, block_count=195, split_count=10, seed=0): + """ + Splits `input_file` (a large conllu file) randomly into `split_count` sub sets. + The splitting process: 1) the input is first split at the word-level into roughly + equal-sized blocks (`block_count` blocks), 2) the order of blocks is shuffled, and + 3) the shuffling result is split into `split_count` sub sets. + + The split data will be written into `output_dir`, and each sub set of the split will + be written into separate file named 'split_{split_nr}.conllu'. + Ordered blocks of sentence id-s of each split will be written into `split_file`. + + When run with the default settings (input is Estonian UD TreeBank train+dev, + block_count=195, split=10, and seed=9), then each block will have a size of approx + 2000 words, and each final split contains roughly 2500-3000 sentences. + + :param input_file: input conllu file + :param output_dir: directory for generated splits + :param split_file: name for csv where to save data about splits (ordered sentence IDs) + :param block_count: number of blocks of sentences to split data into + :param split_count: number of final splits + :param seed: seed value to be used for reproducibility + :return: none + """ + + split_file = open(os.path.join(output_dir, split_file), 'w', newline='', encoding='utf-8') + input_file = open(input_file, 'r', encoding='utf-8') + writer = csv.writer(split_file) + + all_sentences = conllu.parse(input_file.read()) + full_tokenlist = list() + for i, sentence in enumerate(all_sentences): + full_tokenlist.extend([i] * len(sentence)) + input_file.close() + + # Splitting given number of sentence blocks, organizing by sentence id-s + # (block_count=195 results in splits of about 2000 words) + sentence_blocks = correct_splits(split(full_tokenlist, block_count)) + random.Random(seed).shuffle(sentence_blocks) + + training_splits = list(split(sentence_blocks, split_count)) # List of sentence blocks divided to 10 + + #training_split_map = dict() + for j, training_split in enumerate(training_splits, 1): + # Get conllu TokenLists corresponding to sentence IDs + training_whole = [] + for block in training_split: + training_whole.extend([all_sentences[id] for id in block]) + with open(os.path.join(output_dir, 'split_{}.conllu'.format(str(j))), 'w', encoding='utf-8') as tr: + tr.write(create_conllu(training_whole)) + writer.writerow([str(j), training_split]) + #training_split_map[j] = training_split + + split_file.close() + #return training_split_map + + +if __name__ == '__main__': + if len(sys.argv) < 2: + raise Exception('(!) Missing input argument: name of the configuration INI file.') + # Try to execute all input files as configurations + for conf_file in sys.argv[1:]: + create_train_splits_joins_main( conf_file ) + diff --git a/02b_make_gaps.py b/02b_make_gaps.py new file mode 100644 index 00000000..08e0a3b0 --- /dev/null +++ b/02b_make_gaps.py @@ -0,0 +1,330 @@ +# +# Gap experiments: delete systematically conllu fields +# form, lemma and upos. +# +# Implemented conllu file modifications: +# +# '01_no_wordforms' -- remove 'form' of every word; +# '02_no_lemmas' -- remove 'lemma' of every word; +# '02_no_pos' -- remove 'upos' and 'xpos' of every word; +# '03_no_adj_noun_lemmas' -- remove 'lemma' if a word is noun or adj; +# '03_no_wordforms_adj_noun_lemmas' -- remove 'form' of every word, and remove 'lemma' if a word is noun or adj; +# '04_no_verb_adpos_lemmas' -- remove 'lemma' if a word is verb or adposition; +# '04_no_wordforms_verb_adpos_lemmas' -- remove 'form' of every word, and remove 'lemma' if a word is verb or adposition; +# '05_only_cg_list_wordforms_lemmas' -- remove 'form' of every word, and remove 'lemma' if a word is not in CG lemmas list; +# '06_no_wordform_lemma_pos_keep_conj' -- remove 'form', 'lemma' and 'upos'/'xpos' of word if word is no conjunction; +# '07_no_wordform_lemma_pos' -- remove 'form', 'lemma' and 'upos'/'xpos' of every word; +# '08_only_wordforms' -- keep only 'form' and remove 'lemma', 'upos'/'xpos', 'feats'; +# '09_only_pos_feats' -- keep only 'upos', 'xpos', and 'feats' and remove 'form' and 'lemma'; +# + +from datetime import datetime +import os, os.path +import sys, re + +import configparser +import warnings + +from conllu import parse_incr + +gap_experiment_names = [ \ + '01_no_wordforms', + '02_no_lemmas', + '02_no_pos', + '03_no_adj_noun_lemmas', + '03_no_wordforms_adj_noun_lemmas', + '04_no_verb_adpos_lemmas', + '04_no_wordforms_verb_adpos_lemmas', + '05_only_cg_list_wordforms_lemmas', + '06_no_wordform_lemma_pos_keep_conj', + '07_no_wordform_lemma_pos', + '08_only_wordforms', + '09_only_pos_feats', +] + +def perform_gap_experiment_modifications( conf_file ): + ''' + Modifies conllu files for gap experiments based on the configuration. + Settings/parameters of modifications will be read from the given + `conf_file`. + Executes sections in the configuration starting with prefix + 'modify_conllu_'. + ''' + # Parse configuration file + config = configparser.ConfigParser() + if conf_file is None or not os.path.exists(conf_file): + raise FileNotFoundError("Config file {} does not exist".format(conf_file)) + if len(config.read(conf_file)) != 1: + raise ValueError("File {} is not accessible or is not in valid INI format".format(conf_file)) + section_found = False + for section in config.sections(): + if section.startswith('modify_conllu_'): + section_found = True + print(f'Performing {section} ...') + # Collect conllu modification parameters + if not config.has_option(section, 'input_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "input_dir" parameter.') + input_dir = config[section]['input_dir'] + if not os.path.isdir(input_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "input_dir" value {input_dir!r} in {section!r}.') + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_dir = config[section]['output_dir'] + if not config.has_option(section, 'gap_experiments'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "gap_experiments" parameter.') + gap_experiments_str = config[section]['gap_experiments'] + collected_gap_experiments = [] + for gap_exp in gap_experiments_str.split(','): + gap_exp_norm = (gap_exp.strip()).lower() + if gap_exp_norm not in gap_experiment_names: + raise ValueError( f'Error in {conf_file} section {section!r} parameter "gap_experiments": '+\ + f'unknown gap experiment name: {gap_exp!r}. \n'+\ + f'Legal names are: {gap_experiment_names}' ) + collected_gap_experiments.append( gap_exp_norm ) + if len(collected_gap_experiments) == 0: + raise ValueError( f'Error in {conf_file} section {section!r} parameter "gap_experiments": '+\ + f'no gap experiment names found from the variable. \n'+\ + f'Legal names are: {gap_experiment_names}' ) + conll_file_pat=None + # Customize sub-experiment pattern (if required) + if config.has_option(section, 'conll_file_pat'): + conll_file_pat = config[section]['conll_file_pat'] + suppress_checks = config[section].getboolean('suppress_checks', False) + # Perform file modifications + modify_directory( input_dir, output_dir, collected_gap_experiments, + conll_file_pat=conll_file_pat, + suppress_checks=suppress_checks ) + if not section_found: + print(f'No section starting with "modify_conllu_" in {conf_file}.') + + +def modify_directory( in_dir, out_dir, gap_experiments, conll_file_pat=None, skip_files=['train_full.conllu'], suppress_checks=False ): + ''' + Iteratively processes all train/test/dev conllu files from in_dir, performing all + modifications listed in gap_experiments. Saves modified files into out_dir. + + If in_dir contains files of multiple sub experiments, conll_file_pat should be a + regular expression that matches generalizes file name over all sub experiments, and + allows to extract sub experiment name (the regex should have a named group 'exp', + capturing the name of the sub experiment). + + Note that for each sub experiment, there can be only 1 train, 1 test and 1 dev + conllu file. If there are multiple candidate files (and suppress_checks==True), + then an exception will be raised. + However, you can use suppress_checks=False to disable the exception rising + behaviour, or alternatively, provide list of skip_files with the names of + files to be skipped from modifications. + ''' + start_time = datetime.now() + # If conll_file_pat is given, try to convert it to regular expression + conll_file_regexp = None + if conll_file_pat is not None: + # Convert file pattern to regular experssion + if not isinstance(conll_file_pat, str): + raise TypeError(f'conll_file_pat must be a string') + try: + conll_file_regexp = re.compile(conll_file_pat) + except Exception as err: + raise ValueError(f'Unable to convert {conll_file_pat!r} to regexp') from err + if 'exp' not in conll_file_regexp.groupindex: + raise ValueError(f'Regexp {conll_file_pat!r} is missing named group "exp"') + # Collect conllu files from input dir + input_files = [] + for fname in sorted(os.listdir( in_dir )): + if (fname.lower()) in skip_files: + continue + if (fname.lower()).endswith('.conllu'): + # Check conllu file pattern (if required) + sub_exp = None + if conll_file_regexp is not None: + m = conll_file_regexp.match( fname ) + if m: + sub_exp = m.group('exp') + else: + # Skip file if it does not match the pattern + continue + # Determine file type: (train_all,) train, dev or test + cur_ftype = None + for f_type in ['train_all', 'train', 'dev', 'test']: + if f_type in fname.lower(): + cur_ftype = f_type + break + if cur_ftype is None: + warnings.warn(f'(!) Could not determine if {fname!r} is train, dev or test file. '+ + 'Skipping file') + continue + fpath = os.path.join( in_dir, fname ) + input_files.append( (cur_ftype, fpath, sub_exp) ) + if len(input_files) == 0: + raise FileNotFoundError(f'(!) No suitable conllu files found from directory {in_dir!r}.') + # Validate that there is an equal number of files in each sub set + if not suppress_checks: + dev_files = [in_file for in_type, in_file, sub_exp in input_files if in_type == 'dev'] + test_files = [in_file for in_type, in_file, sub_exp in input_files if in_type == 'test'] + train_files = [in_file for in_type, in_file, sub_exp in input_files if in_type == 'train'] + if len(dev_files) != len(train_files): + raise Exception( f'(!) Number of collected train files does not match with the number of '+\ + f'dev files. train_files: {train_files!r} vs dev_files: {dev_files!r}. '+\ + 'Please make sure there is equal number of train, dev and test files for '+\ + 'each experiment.' ) + if len(test_files) != len(train_files): + raise Exception( f'(!) Number of collected train files does not match with the number of '+\ + f'test files. train_files: {train_files!r} vs test_files: {test_files!r}. '+\ + 'Please make sure there is equal number of train, dev and test files for '+\ + 'each experiment.' ) + if not os.path.exists(out_dir): + os.makedirs(out_dir, exist_ok=True) + cg_lemmas_set = None + if '05_only_cg_list_wordforms_lemmas' in gap_experiments: + cg_lemmas_set = load_cg_list() + print(f' Loaded {len(cg_lemmas_set)} EstCG lemmas.') + for exp_name in gap_experiments: + exp_name = exp_name.lower() + for in_type, in_file, sub_exp in input_files: + subset_name = '' + # Try to fetch subset name, if corresponding pattern is defined + if conll_file_regexp is not None: + in_path, fname = os.path.split(in_file) + m = conll_file_regexp.match(fname) + if m: + subset_name = m.group('exp') + if not subset_name.endswith('_'): + subset_name = subset_name + '_' + out_file_temp = os.path.join( out_dir, f'{exp_name}_{subset_name}{in_type}_temp.conllu' ) + out_file_final = os.path.join( out_dir, f'{exp_name}_{subset_name}{in_type}.conllu' ) + if exp_name == '01_no_wordforms': + # gap_experiments/experiment_x/ -- kustuta kõik sõnavormid + modify_file(in_file, out_file_final, remove_fields=['form'], token_picker=None, remove_meta=False) + elif exp_name == '02_no_lemmas': + # gap_experiments/experiment_2/ -- kustuta kõik lemmad + modify_file(in_file, out_file_final, remove_fields=['lemma'], token_picker=None, remove_meta=False) + elif exp_name == '02_no_pos': + # gap_experiments/experiment_x/ -- kustuta kõik sõnaliigimärgendid + modify_file(in_file, out_file_final, remove_fields=['upos', 'xpos'], token_picker=None, remove_meta=False) + elif exp_name == '03_no_adj_noun_lemmas': + # gap_experiments/experiment_3/ -- nimisõnade ja omadussõnade lemmad kustutatud + modify_file(in_file, out_file_final, remove_fields=['lemma'], token_picker=lambda x: x['xpos'] in ['S', 'A']) + elif exp_name == '03_no_wordforms_adj_noun_lemmas': + # gap_experiments/experiment_3_2/ -- kõik sõnavormid ja nimisõnade, omadussõnade lemmad kustutatud + modify_file(in_file, out_file_temp, remove_fields=['form'], token_picker=None) + modify_file(out_file_temp, out_file_final, remove_fields=['lemma'], token_picker=lambda x: x['xpos'] in ['S', 'A']) + os.remove(out_file_temp) + elif exp_name == '04_no_verb_adpos_lemmas': + # gap_experiments/experiment_4/ -- verbide ja kaassõnade lemmad kustutatud + modify_file(in_file, out_file_final, remove_fields=['lemma'], token_picker=lambda x: x['xpos'] in ['V', 'K']) + elif exp_name == '04_no_wordforms_verb_adpos_lemmas': + # gap_experiments/experiment_4_2/ -- kõik sõnavormid + verbide ja kaassõnade lemmad kustutatud + modify_file(in_file, out_file_temp, remove_fields=['form'], token_picker=None) + modify_file(out_file_temp, out_file_final, remove_fields=['lemma'], token_picker=lambda x: x['xpos'] in ['V', 'K']) + os.remove(out_file_temp) + elif exp_name == '05_only_cg_list_wordforms_lemmas': + # gap_experiments/experiment_5/ -- kõik sõnavormid + lemmad kustutatud, kui lemma pole CG listis + modify_file(in_file, out_file_temp, remove_fields=['form'], token_picker=None) + modify_file(out_file_temp, out_file_final, remove_fields=['lemma'], token_picker=lambda x: not cg_lemma_match(x, cg_lemmas_set)) + os.remove(out_file_temp) + elif exp_name == '06_no_wordform_lemma_pos_keep_conj': + # gap_experiments/experiment_conjunction/ -- kustuta sõnavorm & lemma & upos, kui pole tegemist sidesõnaga + modify_file(in_file, out_file_final, remove_fields=['form', 'lemma', 'upos', 'xpos'], token_picker=lambda x: x['xpos'] not in ['J']) + elif exp_name == '07_no_wordform_lemma_pos': + # POS+MORPH experiments -- kustuta igalt poolt sõnavorm & lemma & upos (maksimaalne kustutamine) + modify_file(in_file, out_file_final, remove_fields=['form', 'lemma', 'upos', 'xpos'], token_picker=None) + elif exp_name == '08_only_wordforms': + # Keep only 'form' and remove 'lemma', 'upos'/'xpos', 'feats'; + modify_file(in_file, out_file_final, remove_fields=['lemma', 'upos', 'xpos', 'feats'], token_picker=None) + elif exp_name == '09_only_pos_feats': + # Keep only 'upos', 'xpos', and 'feats' and remove 'form' and 'lemma'; + modify_file(in_file, out_file_final, remove_fields=['form', 'lemma'], token_picker=None) + else: + warnings.warn(f'(!) Unknown gap experiment {exp_name!r}. Skipping that modification step.') + print(f'Total time elapsed: {datetime.now()-start_time}') + + +def load_cg_list( in_file='background_data/visl_lemmas.txt', clean_lemmas=True, return_set=True ): + ''' + Loads list of lemmas that were used in the EstCG syntax from in_file. + Cleans the list: removes lemmas that are likely regular expression patterns, + and if clean_lemmas==True, also deletes '=' and '_' symbols inside lemmas. + Returns a list of lemmas or set of lemmas if return_set==True. + ''' + if not os.path.exists(in_file): + raise FileNotFoundError(f'(!) Unable to find cg list file {in_file!r}') + visl_regexes = [] + visl_lemmas_clean = [] + with open(in_file, 'r', encoding='utf-8') as fin: + lemmas_raw_all = fin.readlines() + lemmas_raw = [l.strip() for l in lemmas_raw_all] + for l in lemmas_raw: + if re.search('[^-a-züõöäÜÕÖÄšžŽŠA-Z=_]', l): + visl_regexes.append(l) + else: + if clean_lemmas: + # Remove _ & = + l = (l.replace('_', '')).replace('=', '') + visl_lemmas_clean.append(l) + return visl_lemmas_clean if not return_set else set(visl_lemmas_clean) + + +def cg_lemma_match( candidate_token, cg_lemmas_set ): + ''' + Determines if lemma of the candidate_token is in cg_lemmas_set. + If the token is verb and ends with 'ma', then also tries to + find lemma with stripped-off 'ma' ending from the cg_lemmas_set + (because verb lemmas in cg_lemmas_set may or may not have the + 'ma' ending -- the listing is not systematic). + ''' + candidate_lemma = candidate_token['lemma'] + candidate_pos = candidate_token['xpos'] + if candidate_lemma.endswith('ma') and candidate_pos == 'V': + # If candidate is verb, try to match without 'ma' ending + candidate_lemma_stripped = re.sub('ma$', '', candidate_lemma) + return candidate_lemma in cg_lemmas_set or \ + candidate_lemma_stripped in cg_lemmas_set + else: + return candidate_lemma in cg_lemmas_set + + +def modify_file( in_file, output_file, remove_fields=[], token_picker=None, remove_meta=False ): + ''' + Modifies conllu in_file by removing specified fields. Optionally, if a token_picker (lambda + callable that should return a boolean) is defined, then removes fields only from tokens that + satisfy the token_picker. + Saves modified file into output_file. + ''' + if token_picker is not None: + if not callable(token_picker): + raise ValueError('(!) token_picker should be a function (callable) that '+\ + 'can be used for picking tokens for deletion.') + with open(in_file, 'r', encoding='utf-8') as conllu_file: + with open(output_file, 'w', encoding='utf-8') as fout: + for sentence in parse_incr(conllu_file): + if remove_meta: + sentence.metadata.pop('text') + sentence.metadata.pop('sent_id') + if 'newdoc id' in sentence.metadata: + sentence.metadata.pop('newdoc id') + for i, token in enumerate(sentence): + if token_picker is not None: + if not token_picker(token): + # Skip this token + continue + for key in token.keys(): + if key in remove_fields: + if key not in ['form', 'upos', 'xpos']: + token[key] = '_' + else: + # TODO: next time, use a token that does + # not appear among punctuation tokens of + # the corpus + token[key] = '---' + fout.write(sentence.serialize()) + +# ======================================================================== + +if __name__ == '__main__': + #print ( load_cg_list() ) + + if len(sys.argv) < 2: + raise Exception('(!) Missing input argument: name of the configuration INI file.') + # Try to execute all input files as configurations + for conf_file in sys.argv[1:]: + perform_gap_experiment_modifications( conf_file ) diff --git a/03_predict_stanza_morph.py b/03_predict_stanza_morph.py new file mode 100644 index 00000000..02d29e69 --- /dev/null +++ b/03_predict_stanza_morph.py @@ -0,0 +1,232 @@ +# +# Executes stanza's morphology models for prediction based on +# the given configuration. +# +# Supported models: +# * stanza's tagger (POS/morphological features tagger); +# * stanza's lemmatizer; +# Implemented settings: +# * full_data +# +import os +import os.path +import sys +import re +import argparse +from datetime import datetime + +from conllu import parse_incr +from conllu.serializer import serialize_field + +from stanza import Pipeline +from stanza.models.common.doc import Document +from stanza.utils.conll import CoNLL + +import configparser + +# =============================================================== +# Use Stanza for tagging morphological features (MAIN) +# =============================================================== + +def predict_stanza_morph_main( conf_file, dry_run=False ): + ''' + Executes stanza's morphological tagging models for predictions + based on the given configuration. + Settings/parameters of the training/prediction will be read from + the given `conf_file`. + Executes sections in the configuration starting with prefix + 'predict_morph_feats_stanza_'. + ''' + # Parse configuration file + config = configparser.ConfigParser() + if conf_file is None or not os.path.exists(conf_file): + raise FileNotFoundError("Config file {} does not exist".format(conf_file)) + if len(config.read(conf_file)) != 1: + raise ValueError("File {} is not accessible or is not in valid INI format".format(conf_file)) + section_found = False + for section in config.sections(): + # ------------------------------------------------------------ + # predict using stanza lemmatizer & morphological tagger + # ------------------------------------------------------------ + if section.startswith('predict_morph_feats_stanza_'): + section_found = True + print(f'Running {section} ...') + # ------------------------------------------ + # 'full_data' + # ------------------------------------------ + # skip_train: do not predict on train files + skip_train = config[section].getboolean('skip_train', False) + # train_file with path + train_file = None + if not skip_train: + if not config.has_option(section, 'train_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "train_file" parameter.') + train_file = config[section]['train_file'] + if not os.path.isfile(train_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid "train_file" value {train_file!r} in {section!r}.') + # test_file with path + if not config.has_option(section, 'test_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "test_file" parameter.') + test_file = config[section]['test_file'] + if not os.path.isfile(test_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid "test_file" value {test_file!r} in {section!r}.') + # output_dir + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_dir = config[section]['output_dir'] + dry_run = config[section].getboolean('dry_run', dry_run) + use_gpu = config[section].getboolean('use_gpu', False) + output_prefix = config[section].get('output_file_prefix', 'morph_predicted_') + lang = config[section].get('lang', 'et') + # predict with a single model: get models file with paths + lemmatizer_model_file = config[section].get('lemmatizer_model_file', None) + morph_tagger_model_file = config[section].get('morph_tagger_model_file', None) + # or, alternatively, download stanza's models + download_models = config[section].getboolean('download_models', False) + if lemmatizer_model_file is None and morph_tagger_model_file is None and not download_models: + raise ValueError(f'Error in {conf_file}: section {section!r} is missing parameters '+\ + '"lemmatizer_model_file" and "morph_tagger_model_file". At least one '+\ + 'of these parameters must be defined.') + if lemmatizer_model_file is not None and not os.path.isfile( lemmatizer_model_file ): + raise FileNotFoundError(f'Error in {conf_file}: invalid "lemmatizer_model_file" value '+\ + f'{lemmatizer_model_file!r} in {section!r}. ') + if morph_tagger_model_file is not None and not os.path.isfile( morph_tagger_model_file ): + raise FileNotFoundError(f'Error in {conf_file}: invalid "morph_tagger_model_file" value '+\ + f'{morph_tagger_model_file!r} in {section!r}. ') + if not dry_run: + start_time = datetime.now() + # Predict on train + if not skip_train: + train_path, train_file_name = os.path.split(train_file) + train_output = os.path.join(output_dir, f'{output_prefix}{train_file_name}') + predict_with_stanza_pipeline(train_file, lemmatizer_model_file, morph_tagger_model_file, + train_output, download_models=download_models, lang=lang, + use_gpu=use_gpu) + # Predict on test + test_path, test_file_name = os.path.split(test_file) + test_output = os.path.join(output_dir, f'{output_prefix}{test_file_name}') + predict_with_stanza_pipeline(test_file, lemmatizer_model_file, morph_tagger_model_file, + test_output, download_models=download_models, lang=lang, + use_gpu=use_gpu) + print() + print(f'Total time elapsed: {datetime.now()-start_time}') + + if not section_found: + print(f'No section starting with "predict_morph_feats_stanza_" in {conf_file}.') + + +# ======================================================================== +# Stanza predictions: use pipeline to predict both lemmas & morph feats +# ======================================================================== + +def create_stanza_document(input_path, mask_morph_feats=True): + """ + Loads sentences from given CONLLU file and creates stanza's Document. + Document will be pretagged: it contains id, text, lemma, upos, xpos, + feats values loaded from the CONLLU file, and empty values in place + of other conllu fields. + Returns loaded Document. + + :param input_path: path to conllu file to be loaded + :param mask_morph_feats: if set (default), then masks all + morphological features in the document with '---'. + :return: stanza Document + """ + with open(input_path, 'r', encoding='utf-8') as conllu_file: + data = [] + for tokenlist in parse_incr(conllu_file): + pretagged_sent = [] + for word in tokenlist: + if not isinstance(word['id'], int): + # Because stanza cannot handle ellipsis (considers it + # a multi-word), we leave ellipsis word out + continue + word_feats = { + 'id': word['id'], + 'text': word['form'], + 'lemma': word['lemma'], + 'upos': word['upos'], + 'xpos': word['xpos'], + 'feats': serialize_field( word['feats'] ), + # Carry over gold standard head & deprel + 'head': word['head'], + 'deprel': word['deprel'] + } + if mask_morph_feats: + word_feats['lemma'] = '_' + word_feats['upos'] = '---' + word_feats['xpos'] = '---' + word_feats['feats'] = serialize_field( '_' ) + pretagged_sent.append(word_feats) + data.append(pretagged_sent) + # create Document-obj from sentences of the pretagged file + return Document(data) + +def predict_with_stanza_pipeline(input_path, lemmatizer_model_path, morph_tagger_model_path, + output_path, download_models=False, lang='et', use_gpu=False): + ''' + Applies stanza's lemmatizer_model/morph_tagger_model on given input CONLLU file to + get depparse predictions. Alternatively, uses lemmatizer/morph_tagger_model downloaded + from stanza's resources for predictions. + Saves predictions to output CONLLU file. + + :param input_path: path to conllu file to be annotated + :param lemmatizer_model_path: path to lemmatizer model to be used for predictions + :param morph_tagger_model_path: path to tagger model to be used for predictions + :param output_path: path to output conllu file + :param download_models: whether models should be downloaded instead of using existing ones + ''' + if morph_tagger_model_path is None and lemmatizer_model_path is None and not download_models: + raise ValueError('(!) At least one of the model paths lemmatizer_model_path and '+\ + 'morph_tagger_model_path must be provided.') + elif (morph_tagger_model_path is not None or lemmatizer_model_path is not None) and download_models: + raise ValueError('(!) Conflicting parameters: cannot use morph_tagger_model_path or '+\ + 'lemmatizer_model_path if download_models is switched on. ') + processors = [] + if morph_tagger_model_path is not None: + processors.append('pos') + if lemmatizer_model_path is not None: + processors.append('lemma') + if download_models: + processors = ['pos', 'lemma'] + config = { + 'processors': 'tokenize,'+(','.join(processors)), # Comma-separated list of processors to use + 'lang': lang, # Language code for the language to build the Pipeline in + 'use_gpu': use_gpu + } + if not download_models: + # Use existing models, do not download anything + config['download_method'] = 0 # NONE won't download anything + if morph_tagger_model_path is not None: + config['pos_model_path'] = morph_tagger_model_path + if lemmatizer_model_path is not None: + config['lemma_model_path'] = lemmatizer_model_path + # Note: "tokenize" is listed in 'processors' because its is + # mandatory for lemma/pos. However, we don't want to use it. + # Use pretokenized text as input and disable tokenization + config['tokenize_pretokenized'] = True + nlp = Pipeline(**config) + doc = create_stanza_document(input_path, mask_morph_feats=True) + nlp(doc) + output_dir, output_fname = os.path.split(output_path) + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + write_stanza_doc_to_conll(doc, output_path) + +def write_stanza_doc_to_conll(doc, output_path): + '''Writes given stanza Document to CoNLLU format output file.''' + conll = CoNLL.convert_dict(doc.to_dict()) + with open(output_path, 'w', encoding='utf-8') as fout: + for sentence in conll: + for word in sentence: + fout.write('\t'.join(word) + '\n') + fout.write('\n') + fout.write('\n' * 2) + +# ======================================================================== + +if __name__ == '__main__': + if len(sys.argv) < 2: + raise Exception('(!) Missing input argument: name of the configuration INI file.') + conf_file = sys.argv[1] + predict_stanza_morph_main( conf_file ) \ No newline at end of file diff --git a/03_train_stanza.py b/03_train_stanza.py new file mode 100644 index 00000000..355afb54 --- /dev/null +++ b/03_train_stanza.py @@ -0,0 +1,383 @@ +# +# Trains models according to experiment settings. +# Supported models: +# * stanza syntax (depparse) +# Implemented settings: +# * full_data +# * multi_experiment (general) +# * crossvalidation +# * half_data +# * smaller_data +# +import os +import os.path +import sys +import re +import argparse +from datetime import datetime + +from stanza.models.parser import main as stanza_main + +from stanza.utils.conll18_ud_eval import load_conllu_file as stanza_load_conllu_file +from stanza.utils.conll18_ud_eval import evaluate +from stanza.utils.conll18_ud_eval import build_evaluation_table + +import configparser + +# =============================================================== +# Train Stanza for syntax (MAIN) +# =============================================================== + +def train_models_main( conf_file, subexp=None, dry_run=False ): + ''' + Trains models based on the configuration. + Settings/parameters of the training will be read from the given + `conf_file`. + Executes sections in the configuration starting with prefix + 'train_stanza_'. + + Optinally, if `subexp` is defined, then trains and evaluates only + that sub-experiment and skips all other sub-experiments (in + crossvalidation, smaller_data and half_data experiments). + ''' + # Parse configuration file + config = configparser.ConfigParser() + if conf_file is None or not os.path.exists(conf_file): + raise FileNotFoundError("Config file {} does not exist".format(conf_file)) + if len(config.read(conf_file)) != 1: + raise ValueError("File {} is not accessible or is not in valid INI format".format(conf_file)) + section_found = False + for section in config.sections(): + # ------------------------------------------ + # s t a n z a + # ------------------------------------------ + if section.startswith('train_stanza_'): + section_found = True + subexp_str = '' if subexp is None else f' ({subexp})' + print(f'Running {section}{subexp_str} ...') + experiment_type = config[section].get('experiment_type', 'full_data') + experiment_type_clean = (experiment_type.strip()).lower() + if experiment_type_clean not in ['full_data', 'crossvalidation', 'half_data', 'smaller_data', 'multi_experiment']: + raise ValueError('(!) Unexpected experiment_type value: {!r}'.format(experiment_type)) + if experiment_type_clean == 'full_data': + # ------------------------------------------ + # 'full_data' + # ------------------------------------------ + # train_file with path + if not config.has_option(section, 'train_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "train_file" parameter.') + train_file = config[section]['train_file'] + if not os.path.isfile(train_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid "train_file" value {train_file!r} in {section!r}.') + # eval_file with path + if not config.has_option(section, 'eval_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "eval_file" parameter.') + eval_file = config[section]['eval_file'] + if not os.path.isfile(eval_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid "eval_file" value {eval_file!r} in {section!r}.') + # output_dir + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_model_dir = config[section]['output_dir'] + # other parameters + output_model_file = config[section].get('model_file', ' model.pt') + extra_args = config[section].get('args', '') + dry_run = config[section].getboolean('dry_run', dry_run) + predict_after = config[section].getboolean('predict_after', False) + parser = 'stanza' + eval_path, eval_file_name = os.path.split(eval_file) + output_file = os.path.join( output_model_dir, 'train_output_'+eval_file_name ) + output_eval_score_file = 'eval_'+(eval_file_name.replace('.conllu', '_score.txt')) + output_eval_score_file = os.path.join(output_model_dir, output_eval_score_file) + + print(f'Training {parser} parser with {train_file}, {eval_file} --> {output_file}, '+ + f'{output_model_dir}/{output_model_file}') + print(f'Parameters: {extra_args}') + + train_stanza( train_file, eval_file, output_model_dir, output_model_file, \ + output_file, args=extra_args, dry_run=dry_run ) + if predict_after: + predict_eval_with_stanza(eval_file, output_model_dir, output_model_file, output_file, + dry_run=dry_run) + run_conll18_ud_eval(eval_file, output_file, return_type='las_f1', + save_results_file=output_eval_score_file, dry_run=dry_run) + elif experiment_type_clean in ['crossvalidation', 'half_data', 'smaller_data', 'multi_experiment']: + # ------------------------------------------ + # 'multi_experiment' (general) + # 'crossvalidation' + # 'half_data' + # 'smaller_data' + # ------------------------------------------ + # input_dir + if not config.has_option(section, 'input_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "input_dir" parameter.') + input_dir = config[section]['input_dir'] + if not os.path.isdir(input_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "input_dir" value {input_dir!r} in {section!r}.') + # output_dir + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_model_dir = config[section]['output_dir'] + extra_args = config[section].get('args', '') + dry_run = config[section].getboolean('dry_run', dry_run) + predict_after = config[section].getboolean('predict_after', False) + parser = 'stanza' + # Patterns for capturing names of sub-experiment files + train_file_pat = r'(?P\d+)_train.conllu' + dev_file_pat = r'(?P\d+)_dev.conllu' + test_file_pat = r'(?P\d+)_dev.conllu' + # Override sub-experiment patterns (if required) + if config.has_option(section, 'train_file_pat'): + train_file_pat = config[section]['train_file_pat'] + if config.has_option(section, 'dev_file_pat'): + dev_file_pat = config[section]['dev_file_pat'] + if config.has_option(section, 'test_file_pat'): + test_file_pat = config[section]['test_file_pat'] + # Launch experiments + bulk_train( input_dir, train_file_pat, dev_file_pat, test_file_pat, + output_model_dir, subexp=subexp, parser=parser, + predict_after=predict_after, args=extra_args, + dry_run=dry_run ) + if not section_found: + print(f'No section starting with "train_stanza_" in {conf_file}.') + + +# ======================================================================== +# Bulk training (for crossvalidation, half-data etc.) +# ======================================================================== + +def bulk_train( data_folder, train_file_pat, dev_file_pat, test_file_pat, output_path, + subexp=None, parser='stanza', predict_after=True, args='', dry_run=False ): + ''' + Trains models of multiple sub-experiments on (train/dev) files from `data_folder`. + Optinally, also evaluates each sub-experiment model (on test files). + Outputs trained models and evaluation results to `output_path`. + + Parameters `train_file_pat`, `dev_file_pat` and `test_file_pat` must be strings + compilable into regexp patterns that can be used to detect data sets of all sub- + experiments. + Each of these patterns must have the named group 'exp', indicating part of the + pattern matching sub-experiment name. + + Use parameter `subexp` to restrict training and evaluation only to a single + sub-experiment instead of performing all sub-experiments. + This is useful when multiple instances of the Python are launched for + parallelization. + + Additional (training) parameters for parser can be provided via `args`. + ''' + # Validate input arguments + supported_parsers = ['stanza'] + if not isinstance(parser, str) or parser.lower() not in supported_parsers: + raise ValueError( f'(!) Unexpected parser: {parser!r}. '+\ + f'Supported parsers: {supported_parsers!r}' ) + parser = parser.lower() + if not os.path.exists(data_folder) or not os.path.isdir(data_folder): + raise Exception(f'(!) Missing or invalid input directory {data_folder!r}') + file_patterns = [ ['train', train_file_pat], + ['dev', dev_file_pat], + ['test', test_file_pat] ] + # Convert file patterns to regular experssions + regexp_file_patterns = [] + for subset, file_pat in file_patterns: + if not isinstance(file_pat, str): + raise TypeError(f'{subset}_file_pat must be a string') + regexp = None + try: + regexp = re.compile(file_pat) + except Exception as err: + raise ValueError(f'Unable to convert {file_pat!r} to regexp') from err + if 'exp' not in regexp.groupindex: + raise ValueError(f'Regexp {file_pat!r} is missing named group "exp"') + regexp_file_patterns.append( [subset, regexp] ) + # Collect experiment input files + experiment_data = { 'train':[], 'dev':[], 'test':[], 'numbers':[] } + for fname in sorted( os.listdir(data_folder) ): + for [subset, regex_file_pat] in regexp_file_patterns: + m = regex_file_pat.match(fname) + if m: + if not (fname.lower()).endswith('.conllu'): + raise Exception( f'(!) invalid file {fname}: {subset} file '+\ + 'must have extension .conllu' ) + fpath = os.path.join(data_folder, fname) + experiment_data[subset].append( fpath ) + no = m.group('exp') + if no not in experiment_data['numbers']: + experiment_data['numbers'].append(no) + # Validate that we have all required files + for [subset, file_pat] in file_patterns: + if len(experiment_data[subset]) == 0: + raise Exception(f'Unable to find any {subset} files '+\ + f'matching {file_pat!r} in dir {data_folder!r}.') + if len(experiment_data[subset]) != len(experiment_data['numbers']): + no1 = len(experiment_data[subset]) + no2 = len(experiment_data['numbers']) + raise Exception(f'Number of {subset} files ({no1}) does not match '+\ + f'the number of experiments ({no2}).') + if subexp is not None: + if subexp not in experiment_data['numbers']: + raise ValueError( f'(!) sub-experiment {subexp!r} not in collected '+\ + f'experiment names: {experiment_data["numbers"]}.' ) + # Launch experiments + start_time = datetime.now() + for i in range( len(experiment_data['numbers']) ): + exp_no = experiment_data['numbers'][i] + train_file = experiment_data['train'][i] + dev_file = experiment_data['dev'][i] + test_file = experiment_data['test'][i] + if subexp is not None and exp_no != subexp: + # Skip other experiments + continue + output_model_dir = output_path + output_model_file = f"model_{exp_no}.pt" + test_path, test_file_name = os.path.split(test_file) + output_file = os.path.join( output_model_dir, 'train_output_'+test_file_name ) + output_eval_score_file = 'eval_'+(test_file_name.replace('.conllu', '_score.txt')) + output_eval_score_file = os.path.join(output_model_dir, output_eval_score_file) + + print('='*(len(exp_no)*2)) + print(f' {exp_no}') + print('='*(len(exp_no)*2)) + print(f'Training {parser} parser with {train_file}, {dev_file}, {test_file} --> '+ + f'{output_file}, {output_model_dir}/{output_model_file}') + print(f'Parameters: {args}') + + if parser == 'stanza': + train_stanza( train_file, dev_file, output_model_dir, output_model_file, \ + output_file, args=args, dry_run=dry_run ) + if predict_after: + predict_eval_with_stanza(test_file, output_model_dir, output_model_file, output_file, + dry_run=dry_run) + las = run_conll18_ud_eval(test_file, output_file, return_type='las_f1', + save_results_file=output_eval_score_file, + dry_run=dry_run) + #print(f'Best model eval score: {las}') + print() + print() + print(f'Total time elapsed: {datetime.now()-start_time}') + +# ======================================================================== +# Stanza interface: training models and predicting on eval set +# ======================================================================== + +def train_stanza(train_file, eval_file, output_model_dir, output_model_file, output_file, + lang='et', treebank='et_edt', args='', dry_run=False): + ''' + Trains single stanza model on `train_file` using `eval_file` for parameter tuning + and model evaluation. + + Note: in previous experiments, in addition to `eval_file`, a separate parameter + `gold_file` was defined. Here, we assume that `gold_file` == `eval_file`, so only + parameter `eval_file` is required. + + Uses parameters of stanza parser: + --save_dir : Root dir for saving models (output_model_dir) + --save_name : File name to save the model (output_model_file) + --train_file : Input file for data loader. + --eval_file : Input file for data loader. + --no_pretrain : Turn off pretrained embeddings. + --output_file : Output CoNLL-U file. + --gold_file : Output CoNLL-U file. (gold labels for eval_file) + --lang : Language + --shorthand : Treebank shorthand + --mode : choices=['train', 'predict'] + --batch_size : default=5000 + ''' + if not os.path.exists(output_model_dir): + os.makedirs(output_model_dir, exist_ok=True) + stanza_args = \ + f'--save_dir {output_model_dir} --save_name {output_model_file} --train_file {train_file} --eval_file {eval_file} --no_pretrain '+\ + f'--output_file {output_file} --gold_file {eval_file} --lang {lang} --shorthand {treebank} --mode train {args}' + if dry_run: + return + stanza_main( args=stanza_args.split() ) + + +def predict_eval_with_stanza(eval_file, output_model_dir, output_model_file, output_file, + lang='et', treebank='et_edt', args='', dry_run=False): + ''' + Uses existing stanza's model `output_model_file` to predict labels for `eval_file`. + + Note: in previous experiments, in addition to `eval_file`, a separate parameter + `gold_file` was defined. Here, we assume that `gold_file` == `eval_file`, so only + parameter `eval_file` is required. + + Uses parameters of stanza parser: + --save_dir : Root dir for saving models (output_model_dir) + --save_name : File name to save the model (output_model_file) + --eval_file : Input file for data loader. + --no_pretrain : Turn off pretrained embeddings. + --output_file : Output CoNLL-U file. + --gold_file : Output CoNLL-U file. (gold labels for eval_file) + --lang : Language + --shorthand : Treebank shorthand + --mode : choices=['train', 'predict'] + ''' + if not os.path.exists(output_model_dir): + raise ValueError(f'(!) Non-existent model path: {output_model_dir}/{output_model_file}') + stanza_args = \ + f'--save_dir {output_model_dir} --save_name {output_model_file} --no_pretrain --eval_file {eval_file} '+\ + f'--output_file {output_file} --gold_file {eval_file} --lang {lang} --shorthand {treebank} --mode predict '+\ + f'{args}' + if dry_run: + return + stanza_main( args=stanza_args.split() ) + + +# ======================================================================== +# Stanza interface: evaluation +# ======================================================================== + +def run_conll18_ud_eval(gold_file, system_file, return_type='las_f1', save_results_file=None, dry_run=False): + ''' + Calculates CONLL-2018 evaluation scores based on given `gold_file` and `system_file`. + If return_type == 'las_f1' (default), then returns LAS score (as string). + If return_type == 'table' (default), then returns CONLL-2018 evaluation table (as string). + Optionally, if `save_results_file` is provided, saves returned value into given file. + ''' + if not isinstance(return_type, str) or \ + return_type.lower() not in ['las_f1', 'table']: + raise ValueError(f'(!) Unexpected return type {return_type!r}') + if dry_run: + return None + # Evaluate + # The following code is based on: + # https://github.com/stanfordnlp/stanza/blob/main/stanza/utils/conll18_ud_eval.py#L658-L673 + treebank_type = {} + treebank_type['no_gapping'] = 0 + treebank_type['no_shared_parents_in_coordination'] = 0 + treebank_type['no_shared_dependents_in_coordination'] = 0 + treebank_type['no_control'] = 0 + treebank_type['no_external_arguments_of_relative_clauses'] = 0 + treebank_type['no_case_info'] = 0 + treebank_type['no_empty_nodes'] = False + treebank_type['multiple_roots_okay'] = False + # Load CoNLL-U files + gold_ud = stanza_load_conllu_file(gold_file, treebank_type) + system_ud = stanza_load_conllu_file(system_file, treebank_type) + eval_result = evaluate(gold_ud, system_ud) + # Format results + if return_type.lower() == 'las_f1': + # result is LAS f1 score + result = f'{(100*eval_result["LAS"].f1):.2f}' + elif return_type.lower() == 'table': + # result is a table of scores + result = build_evaluation_table(eval_result, True, False, True) + if save_results_file is not None: + # Save results if needed + assert isinstance(save_results_file, str) and len(save_results_file) > 0 + with open(save_results_file, 'w', encoding='utf-8') as out_f: + out_f.write(str(result)) + return result + + +# ======================================================================== + +if __name__ == '__main__': + if len(sys.argv) < 2: + raise Exception('(!) Missing input argument: name of the configuration INI file.') + conf_file = sys.argv[1] + subexp = None + if len(sys.argv) > 2: + subexp = sys.argv[2] + train_models_main( conf_file, subexp=subexp ) \ No newline at end of file diff --git a/03b_optimize_malt.py b/03b_optimize_malt.py new file mode 100644 index 00000000..979e4d87 --- /dev/null +++ b/03b_optimize_malt.py @@ -0,0 +1,143 @@ +# +# Runs MaltOptimizer according to given configuration. +# This is an optimization step before training MaltParser. +# +# Requirements: +# Perl and Linux command line +# Python 2.7 (because optimizer uses some legacy scripts) +# Maltparser from https://maltparser.org/index.html +# MaltOptimizer from http://nil.fdi.ucm.es/maltoptimizer/ +# input conllu files without ANY sentence metadata (otherwise phase 1 will hang) +# + +from datetime import datetime +import subprocess +import os, os.path +import sys, re + +if not (sys.version_info[0] == 2 and sys.version_info[1] == 7): + raise Exception('(!) Unexpected Python version. MaltOptimizer script is only runnable with Python 2.7.') + +import ConfigParser # specific to Python 2.7 + +# Change to local paths & files, if required +maltoptimizer_dir = 'MaltOptimizer-1.0.3' +maltparser_jar = 'maltparser-1.9.2.jar' +maltoptimizer_jar = 'MaltOptimizer.jar' + +def run_maltoptimizer_main( conf_file, verbose=True ): + ''' + Runs maltoptimizer which provides feature selection before MaltParser training. + Settings/parameters will be read from the given `conf_file`. + Executes sections in the configuration starting with 'maltoptimize_'. + ''' + # Parse configuration file + config = ConfigParser.ConfigParser() + if conf_file is None or not os.path.exists(conf_file): + raise Exception("Config file {} does not exist".format(conf_file)) + if len(config.read(conf_file)) != 1: + raise ValueError("File {} is not accessible or is not in valid INI format".format(conf_file)) + start = datetime.now() + section_found = False + for section in config.sections(): + if section.startswith('maltoptimize_'): + # input_files -- one file or a list of files (with full paths) separated by ; + if not config.has_option(section, 'input_files'): + raise ValueError('Error in %s: section %s is missing "input_files" parameter.' % (conf_file, section) ) + input_files = config.get(section, 'input_files') + if ',' in input_files: + input_files = [fname.strip() for fname in input_files.split(',')] + elif ';' in input_files: + input_files = [fname.strip() for fname in input_files.split(';')] + else: + input_files = [input_files] + # output_dir -- dir where to but finalOptionsFile.xml & [featureFile.xml] + if not config.has_option(section, 'output_dir'): + raise ValueError('Error in %s: section %s is missing "output_dir" parameter.' % (conf_file, section) ) + output_dir = config.get(section, 'output_dir') + conll_file_pat = None + conll_file_regexp = None + # Customize sub-experiment pattern (if required) + if config.has_option(section, 'conll_file_pat'): + conll_file_pat = config.get(section, 'conll_file_pat') + # Convert file pattern to regular experssion + if not isinstance(conll_file_pat, basestring): + raise TypeError('conll_file_pat must be a string') + try: + conll_file_regexp = re.compile(conll_file_pat) + except Exception as err: + raise ValueError('Unable to convert {!r} to regexp'.format(conll_file_pat)) + if 'exp' not in conll_file_regexp.groupindex: + raise ValueError('Regexp {!r} is missing named group "exp"'.format(conll_file_pat)) + # Run optimizer on all input files + for input_file in input_files: + sub_exp='' + if conll_file_regexp is not None: + m = conll_file_regexp.match( input_file ) + if m: + sub_exp = m.group('exp') + else: + raise ValueError('Input file {!r} does not match pattern {!r}').format(input_file, conll_file_pat) + optimize_maltparser(input_file, output_dir=output_dir, sub_exp=sub_exp) + section_found = True + if section_found: + print('Total processing time: %s' % (datetime.now()-start)) + else: + print('No section starting with "maltoptimize_" in %s.' % (conf_file)) + +def optimize_maltparser(input_conll_file, output_dir=None, sub_exp=''): + ''' + Runs MaltOptimizer.jar on given input_conll_file (dev dataset). + + See also: + https://github.com/estnltk/maltparser_training + https://github.com/estnltk/syntax_experiments/blob/devel/03_create_training_testing_data/MaltOptimizer-1.0.3/optimize_maltparser.py + ''' + global maltoptimizer_dir, maltparser_jar, maltoptimizer_jar + + # Make input file path absolute + # (otherwise maltparser fails to load the file) + if input_conll_file != os.path.abspath(input_conll_file): + input_conll_file = os.path.abspath(input_conll_file) + + phase1 = 'java -jar %s -p 1 -m %s -c %s' % (maltoptimizer_jar, maltparser_jar, input_conll_file) + subprocess.call(phase1, shell=True, cwd=maltoptimizer_dir) + + phase2 = 'java -jar %s -p 2 -m %s -c %s' % (maltoptimizer_jar, maltparser_jar, input_conll_file) + subprocess.call(phase2, shell=True, cwd=maltoptimizer_dir) + + phase3 = 'java -jar %s -p 3 -m %s -c %s' % (maltoptimizer_jar, maltparser_jar, input_conll_file) + subprocess.call(phase3, shell=True, cwd=maltoptimizer_dir) + + phase3_optFile = os.path.join(maltoptimizer_dir, 'phase3_optFile.txt') + with open(phase3_optFile, 'r' ) as f: + lines = [l for l in f.read().split('\n') if len(l) > 0] + feature_model = lines[-1].split(':')[-1] + + if output_dir is not None: + # move the files to correct place: + if not os.path.exists(output_dir): + os.makedirs(output_dir) + os.rename(os.path.join(maltoptimizer_dir, 'finalOptionsFile.xml'), + os.path.join(output_dir, 'finalOptionsFile%s.xml' % sub_exp)) + if os.path.isfile(os.path.join(maltoptimizer_dir, feature_model)): + os.rename(os.path.join(maltoptimizer_dir, feature_model), + os.path.join(output_dir, 'featureFile%s.xml' % sub_exp)) + + +if __name__ == '__main__': + # First, check that required folders and jar files are present + if not os.path.isdir(maltoptimizer_dir): + raise Exception( ('Missing directory: \%s. Please get MaltOptimizer from: http://nil.fdi.ucm.es/maltoptimizer/') % (maltoptimizer_dir) ) + malt_dir_files = list(os.listdir(maltoptimizer_dir)) + if maltparser_jar not in malt_dir_files: + jar_path = os.path.join(maltoptimizer_dir, maltparser_jar) + raise Exception( ('Missing jar file: \%s. Please get MaltParser from: https://maltparser.org/index.html') % (jar_path) ) + if maltoptimizer_jar not in malt_dir_files: + jar_path = os.path.join(maltoptimizer_dir, maltoptimizer_jar) + raise Exception( ('Missing jar file: \%s. Please get MaltOptimizer from: http://nil.fdi.ucm.es/maltoptimizer/') % (jar_path) ) + # Get parameters from command line + if len(sys.argv) < 2: + raise Exception('(!) Missing input argument: name of the configuration INI file.') + conf_file = sys.argv[1] + run_maltoptimizer_main( conf_file, verbose=True ) diff --git a/03c_train_malt_udpipe.py b/03c_train_malt_udpipe.py new file mode 100644 index 00000000..00ad9bd8 --- /dev/null +++ b/03c_train_malt_udpipe.py @@ -0,0 +1,407 @@ +# +# Trains MaltParser/UDPipe1 models according to experiment settings. +# Supported settings: +# * full_data +# * multi_experiment (general) +# * crossvalidation +# * half_data +# * smaller_data +# +import subprocess +import os, os.path +import sys, re +import pkgutil +import configparser + +from conllu import parse_incr + +# Change to local paths & files, if required +DEFAULT_MALTPARSER_DIR = 'MaltOptimizer-1.0.3' +DEFAULT_MALTPARSER_JAR = 'maltparser-1.9.2.jar' +#DEFAULT_UDPIPE_DIR = 'udpipe-1.2.0-bin\\bin-win64' +DEFAULT_UDPIPE_DIR = 'udpipe-1.2.0-bin/bin-linux64' + +def train_malt_udpipe_main( conf_file, subexp=None, dry_run=False ): + ''' + Trains MaltParser/UDPipe-1 models based on the configuration. + Settings/parameters of the training will be read from the given + `conf_file`. + Executes sections in the configuration starting with prefix + 'train_malt_' and 'train_udpipe1_'. + + Optinally, if `subexp` is defined, then trains only that + sub-experiment and skips all other sub-experiments (in + crossvalidation, smaller_data and half_data experiments). + ''' + # Parse configuration file + config = configparser.ConfigParser() + if conf_file is None or not os.path.exists(conf_file): + raise FileNotFoundError("Config file {} does not exist".format(conf_file)) + if len(config.read(conf_file)) != 1: + raise ValueError("File {} is not accessible or is not in valid INI format".format(conf_file)) + section_found = False + for section in config.sections(): + if section.startswith('train_malt_') or section.startswith('train_udpipe1_'): + parser = 'maltparser' if section.startswith('train_malt_') else 'udpipe1' + section_found = True + subexp_str = '' if subexp is None else f' ({subexp})' + print(f'Running {section}{subexp_str} ...') + experiment_type = config[section].get('experiment_type', 'full_data') + experiment_type_clean = (experiment_type.strip()).lower() + if experiment_type_clean not in ['full_data', 'crossvalidation', 'half_data', 'smaller_data', 'multi_experiment']: + raise ValueError('(!) Unexpected experiment_type value: {!r}'.format(experiment_type)) + if experiment_type_clean == 'full_data': + # ------------------------------------------ + # 'full_data' + # ------------------------------------------ + # train_file with path + if not config.has_option(section, 'train_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "train_file" parameter.') + train_file = config[section]['train_file'] + if not os.path.isfile(train_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid "train_file" value {train_file!r} in {section!r}.') + # output_dir + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_model_dir = config[section]['output_dir'] + # other parameters + dry_run = config[section].getboolean('dry_run', dry_run) + default_model = 'model.mco' if parser == 'maltparser' else 'model.udpipe' + output_model_file = config[section].get('model_file', default_model) + # MaltParser options + final_options_file = config[section].get('final_options_file', None) + feature_model_file = config[section].get('feature_model_file', None) + maltparser_dir = config[section].get('maltparser_dir', DEFAULT_MALTPARSER_DIR) + maltparser_jar = config[section].get('maltparser_jar', DEFAULT_MALTPARSER_JAR) + # UDPipe-1 options + create_embeddings_file = config[section].get('create_embeddings_file', None) + parser_options = config[section].get('parser_options', None) + udpipe_dir = config[section].get('udpipe_dir', DEFAULT_UDPIPE_DIR) + if not dry_run: + if parser == 'maltparser': + train_maltparser(output_model_file, train_file, output_dir=output_model_dir, + final_options_file=final_options_file, + feature_model_file=feature_model_file, + maltparser_dir=maltparser_dir, + maltparser_jar=maltparser_jar) + elif parser == 'udpipe1': + train_udpipe1(output_model_file, train_file, output_model_dir, + parser_options=parser_options, + create_embeddings_file=create_embeddings_file, + udpipe_dir=udpipe_dir) + else: + raise Exception(f'Unexpected parser name: {parser!r}') + elif experiment_type_clean in ['crossvalidation', 'half_data', 'smaller_data', 'multi_experiment']: + # ------------------------------------------ + # 'multi_experiment' (general) + # 'crossvalidation' + # 'half_data' + # 'smaller_data' + # ------------------------------------------ + # input_dir + if not config.has_option(section, 'input_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "input_dir" parameter.') + input_dir = config[section]['input_dir'] + if not os.path.isdir(input_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "input_dir" value {input_dir!r} in {section!r}.') + # output_dir + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_model_dir = config[section]['output_dir'] + # Common options + dry_run = config[section].getboolean('dry_run', dry_run) + train_file_pat = r'(?P\d+)_train.conllu' + train_file_re = None + if config.has_option(section, 'train_file_pat'): + train_file_pat = config[section]['train_file_pat'] + train_file_re = _create_regexp_pattern(train_file_pat, 'train_file_pat') + # MaltParser options + maltparser_dir = config[section].get('maltparser_dir', DEFAULT_MALTPARSER_DIR) + maltparser_jar = config[section].get('maltparser_jar', DEFAULT_MALTPARSER_JAR) + feature_files_dir = config[section].get('feature_files_dir', None) + final_options_file_pat = r'finalOptionsFile(?P\S+)\.xml' + feature_model_file_pat = r'featureFile(?P\S+)\.xml' + final_options_file_re = None + feature_model_file_re = None + if config.has_option(section, 'final_options_file_pat'): + final_options_file_pat = config[section]['final_options_file_pat'] + if config.has_option(section, 'feature_model_file_pat'): + feature_model_file_pat = config[section]['feature_model_file_pat'] + final_options_file_re = _create_regexp_pattern( final_options_file_pat, + 'final_options_file_pat') + feature_model_file_re = _create_regexp_pattern( feature_model_file_pat, + 'feature_model_file_pat') + all_feature_files = [] + if feature_files_dir is not None and os.path.isdir(feature_files_dir): + all_feature_files = [fname for fname in os.listdir(feature_files_dir)] + # UDPipe-1 options + create_embeddings_file = config[section].get('create_embeddings_file', None) + parser_options = config[section].get('parser_options', None) + udpipe_dir = config[section].get('udpipe_dir', DEFAULT_UDPIPE_DIR) + # Iterate over input files and train + for in_fname in os.listdir(input_dir): + if in_fname.endswith('.conllu'): + m = train_file_re.match(in_fname) + if m: + # Candidate for a training file + train_file = os.path.join(input_dir, in_fname) + cur_subexp = m.group('exp') + if subexp is not None: + if cur_subexp != subexp: + continue + if parser == 'maltparser': + output_model_file = f'model_{cur_subexp}.mco' + else: + output_model_file = f'model_{cur_subexp}.udpipe' + # Fetch Maltparser feature files + final_options_file = None + feature_model_file = None + # Try to find feature selection files (if any provided) + if len(all_feature_files) > 0: + cur_subexp_lstrip = cur_subexp.lstrip('0') + for feats_file in all_feature_files: + f1 = final_options_file_re.match(feats_file) + f2 = feature_model_file_re.match(feats_file) + if f1 and (f1.group('exp') == cur_subexp or \ + f1.group('exp') == cur_subexp_lstrip): + final_options_file = os.path.join(feature_files_dir, + feats_file) + if f2 and (f2.group('exp') == cur_subexp or \ + f2.group('exp') == cur_subexp_lstrip): + feature_model_file = os.path.join(feature_files_dir, + feats_file) + if final_options_file is None: + raise Exception(f'Unable to find final_options_file for experiment {cur_subexp!r}') + if feature_model_file is None: + raise Exception(f'Unable to find feature_model_file for experiment {cur_subexp!r}') + # Launch training + if not dry_run: + if parser == 'maltparser': + print(f' Training Maltparser on {train_file} (exp: {cur_subexp}) ...') + train_maltparser(output_model_file, train_file, + output_dir=output_model_dir, + final_options_file=final_options_file, + feature_model_file=feature_model_file, + maltparser_dir=maltparser_dir, + maltparser_jar=maltparser_jar) + elif parser == 'udpipe1': + cur_embeddings_file = create_embeddings_file + if cur_embeddings_file is not None: + cur_embeddings_file = f'{cur_subexp}_{cur_embeddings_file}' + train_udpipe1(output_model_file, train_file, output_model_dir, + parser_options=parser_options, + create_embeddings_file=cur_embeddings_file, + udpipe_dir=udpipe_dir) + else: + raise Exception(f'Unexpected parser name: {parser!r}') + if not section_found: + print(f'No section starting with "train_malt_" or "train_udpipe1_" in {conf_file}.') + +def _create_regexp_pattern(fpattern, pattern_var_name): + # Convert file pattern to regular experssion + if not isinstance(fpattern, str): + raise TypeError(f'{pattern_var_name} must be a string') + regexp = None + try: + regexp = re.compile(fpattern) + except Exception as err: + raise ValueError(f'Unable to convert {fpattern!r} to regexp') from err + if 'exp' not in regexp.groupindex: + raise ValueError(f'Regexp {fpattern!r} is missing named group "exp"') + return regexp + +# =============================================================== +# Train MaltParser +# =============================================================== + +def check_maltparser_requirements(maltparser_dir=DEFAULT_MALTPARSER_DIR, + maltparser_jar=DEFAULT_MALTPARSER_JAR): + ''' + Check that MaltParser's required folders and jar files are present. + Raises an expection if anything is missing. + ''' + if not os.path.isdir(maltparser_dir): + raise Exception( ('Missing directory: \%s. Please get MaltParser from: https://maltparser.org/index.html') % (maltparser_dir) ) + malt_dir_files = list(os.listdir(maltparser_dir)) + if maltparser_jar not in malt_dir_files: + jar_path = os.path.join(maltparser_dir, maltparser_jar) + raise Exception( ('Missing jar file: \%s. Please get MaltParser from: https://maltparser.org/index.html') % (jar_path) ) + if 'lib' not in malt_dir_files: + lib_path = os.path.join(maltparser_dir, 'lib') + raise Exception( ('Missing java libraries dir: \%s. Please get MaltParser from: https://maltparser.org/index.html') % (lib_path) ) + return True + +def train_maltparser(output_model, train_corpus, output_dir=None, final_options_file=None, feature_model_file=None, + maltparser_dir=DEFAULT_MALTPARSER_DIR, maltparser_jar=DEFAULT_MALTPARSER_JAR): + ''' + Trains MaltParser on train_corpus, creates output_model and saves into output_dir. + Optionally, uses final_options_file and feature_model_file for feature selection. + ''' + check_maltparser_requirements(maltparser_dir=maltparser_dir, maltparser_jar=maltparser_jar) + # Make input file paths absolute + if train_corpus != os.path.abspath(train_corpus): + train_corpus = os.path.abspath(train_corpus) + if final_options_file is not None and final_options_file != os.path.abspath(final_options_file): + final_options_file = os.path.abspath(final_options_file) + if feature_model_file is not None and feature_model_file != os.path.abspath(feature_model_file): + feature_model_file = os.path.abspath(feature_model_file) + # Construct command + if final_options_file is not None and feature_model_file is not None: + train_command = \ + ('java -Xmx6g -jar {jar} -i {train_corpus} -c {output_model} -m learn -f {final_options_file} -F {feature_model_file}').\ + format(jar=maltparser_jar, output_model=output_model, train_corpus=train_corpus, + final_options_file=final_options_file, feature_model_file=feature_model_file) + else: + train_command = \ + ('java -Xmx6g -jar {jar} -i {train_corpus} -c {output_model} -m learn').\ + format(jar=maltparser_jar, train_corpus=train_corpus, output_model=output_model) + # Execute training + subprocess.call(train_command, shell=True, cwd=maltparser_dir) + if output_dir is not None: + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + # Remove old file + if os.path.exists(os.path.join(output_dir, output_model)): + os.remove(os.path.join(output_dir, output_model)) + # Move model file to output dir + os.rename(os.path.join(maltparser_dir, output_model), + os.path.join(output_dir, output_model)) + + +# =============================================================== +# Train UDPipe-1 (preprocessing) +# =============================================================== + +def is_gensim_available(): + """ + Checks if the package gensim is available. + This is required for creating word2vec embeddings for gensim. + """ + return pkgutil.find_loader('gensim') is not None + +def load_conllu_tokens_sentences(input_conllu): + ''' + Loads conllu file's textual content. + Returns a list of lists: sentences of tokens. + ''' + sentences = [] + with open(input_conllu, 'r', encoding='utf-8') as conllu_file: + for sentence in parse_incr(conllu_file): + sentences.append([]) + for i, token in enumerate(sentence): + sentences[-1].append(token['form']) + return sentences + +def create_word2vec_model(input_conllu, output_path): + ''' + Trains word2vec embeddings file for UDPipe-1. + Saves text format model to output_path. + ''' + if not is_gensim_available(): + raise Exception('(!) Package gensim is required for pre-training embeddings for udpipe. '+\ + 'Get the package from here: https://radimrehurek.com/gensim/ ') + import gensim.models + sentences = load_conllu_tokens_sentences(input_conllu) + # Following pre-training settings mentioned here: + # https://ufal.mff.cuni.cz/udpipe/1/users-manual#udpipe_training_parser_embeddings + model = gensim.models.Word2Vec( + sentences=sentences, + min_count=2, + vector_size=50, + window=10, + hs=0, + sg=1, # skip-gram + sample=1e-3, + epochs=15, + negative=5 + ) + model.wv.save_word2vec_format(output_path, binary=False) + +# =============================================================== +# Train UDPipe-1 +# =============================================================== + +def check_if_udpipe_is_in_path(udpipe_cmd='udpipe'): + ''' Checks whether given udpipe is in system's PATH. Returns True, there is + a file with given name (udpipe_cmd) in the PATH, otherwise returns False; + The idea borrows from: http://stackoverflow.com/a/377028 + ''' + if os.getenv("PATH") == None: + return False + for path in os.environ["PATH"].split(os.pathsep): + path1 = path.strip('"') + file1 = os.path.join(path1, udpipe_cmd) + if os.path.isfile(file1) or os.path.isfile(file1 + '.exe'): + return True + return False + +def train_udpipe1(output_model, train_corpus, output_dir, parser_options=None, create_embeddings_file=None, verbose=True, + udpipe_dir=DEFAULT_UDPIPE_DIR): + ''' + Trains UDPipe-1 on train_corpus, creates output_model and saves into output_dir. + List of parser options can be provided via string parser_options. + If create_embeddings_file is not None, then creates word2vec form embeddings from train_corpus + and saves into file named create_embeddings_file. + + Note: if parser_options is not provided, then UDPipe's default training options are: + Parser transition options: system=projective, oracle=dynamic, structured_interval=8, single_root=1 + Parser uses lemmas/upos/xpos/feats: from gold data + Parser embeddings options: upostag=20, feats=20, xpostag=0, form=50, lemma=0, deprel=20 + form mincount=2, precomputed form embeddings=none + lemma mincount=2, precomputed lemma embeddings=none + Parser network options: iterations=10, hidden_layer=200, batch_size=10, + learning_rate=0.0200, learning_rate_final=0.0010, l2=0.5000, early_stopping=0 + ''' + udpipe_dir_exists = udpipe_dir is not None and os.path.isdir(udpipe_dir) + udpipe_is_in_path = check_if_udpipe_is_in_path() + if not udpipe_dir_exists and not udpipe_is_in_path: + raise Exception('(!) Could not find UDPipe executable. '+\ + 'Please make sure udpipe is installed and available in system PATH. '+\ + 'Or, alternatively, provide location of UDPipe via variable udpipe_dir. '+\ + 'You can download udpipe from: https://ufal.mff.cuni.cz/udpipe/1/') + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + embeddings_path = None + if create_embeddings_file is not None: + embeddings_path = os.path.join(output_dir, create_embeddings_file) + if verbose: + print(f'Creating word2vec embeddings file {create_embeddings_file} ...') + create_word2vec_model(train_corpus, embeddings_path) + output_model_path = os.path.join(output_dir, output_model) + if parser_options is None: + parser_options = 'use_gold_tags=1' + else: + if 'use_gold_tags=1' not in parser_options: + parser_options += ';use_gold_tags=1' + if embeddings_path is not None: + if 'embedding_form_file=' not in parser_options: + parser_options += f';embedding_form_file={embeddings_path}' + else: + # Updated from embeddings file path + parser_options = re.sub(r';embedding_form_file=([^; ])+', + f';embedding_form_file={embeddings_path}', + parser_options) + if verbose: + print(f' Training UDPipe-1 on {train_corpus!r} with settings {parser_options!r} ...') + udpipe_cmd = 'udpipe' + if udpipe_dir_exists: + udpipe_cmd = os.path.join(udpipe_dir, udpipe_cmd) + # Linux shell note: parser_options must be surrounded by ' and ', otherwise udpipe is unable + # to parse them and hangs while waiting for training input. + train_command = \ + ('{udpipe_cmd} --train {output_model_path} --tokenizer=none --tagger=none --parser={parser_options!r} {train_corpus}').\ + format(udpipe_cmd=udpipe_cmd, output_model_path=output_model_path, parser_options=parser_options, + train_corpus=train_corpus) + # Execute training + subprocess.call(train_command, shell=True) + + + +if __name__ == '__main__': + if len(sys.argv) < 2: + raise Exception('(!) Missing input argument: name of the configuration INI file.') + subexp=None + if len(sys.argv) > 2: + subexp = sys.argv[2] + # Try to execute input file as configuration + train_malt_udpipe_main( sys.argv[1], subexp=subexp, dry_run=False ) \ No newline at end of file diff --git a/04_predict_stanza.py b/04_predict_stanza.py new file mode 100644 index 00000000..22b18b47 --- /dev/null +++ b/04_predict_stanza.py @@ -0,0 +1,748 @@ +# +# Applies models to get predictions. +# Supported models: +# * stanza syntax (depparse) +# * stanza syntax ensemble (depparse) +# Implemented settings: +# * full_data +# * multi_experiment (general) +# * crossvalidation +# * half_data +# * smaller_data +# +import os, os.path +import re +import sys +from datetime import datetime +import warnings + +from conllu import parse_incr +from conllu.serializer import serialize_field + +from stanza import Pipeline +from stanza.models.common.doc import Document +from stanza.utils.conll import CoNLL + +import configparser + +# =============================================================== +# Run trained models / get predictions (MAIN) +# =============================================================== + +def run_models_main( conf_file, subexp=None, dry_run=False ): + ''' + Runs model(s) based on the configuration. + Settings/parameters of running model(s) will be read from the + given `conf_file`. + Executes sections in the configuration starting with prefix + 'predict_stanza_'. + + Optinally, if `subexp` is defined, then runs only that + sub-experiment and skips all other sub-experiments (in + crossvalidation, smaller_data and half_data experiments). + ''' + # Parse configuration file + config = configparser.ConfigParser() + if conf_file is None or not os.path.exists(conf_file): + raise FileNotFoundError("Config file {} does not exist".format(conf_file)) + if len(config.read(conf_file)) != 1: + raise ValueError("File {} is not accessible or is not in valid INI format".format(conf_file)) + section_found = False + for section in config.sections(): + # ------------------------------------------ + # s t a n z a + # ------------------------------------------ + if section.startswith('predict_stanza_'): + section_found = True + subexp_str = '' if subexp is None else f' ({subexp})' + print(f'Running {section}{subexp_str} ...') + experiment_type = config[section].get('experiment_type', 'full_data') + experiment_type_clean = (experiment_type.strip()).lower() + if experiment_type_clean not in ['full_data', 'crossvalidation', 'half_data', 'smaller_data', 'multi_experiment']: + raise ValueError('(!) Unexpected experiment_type value: {!r}'.format(experiment_type)) + if experiment_type_clean == 'full_data': + # ------------------------------------------ + # 'full_data' + # ------------------------------------------ + # train_file with path + if not config.has_option(section, 'train_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "train_file" parameter.') + train_file = config[section]['train_file'] + if not os.path.isfile(train_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid "train_file" value {train_file!r} in {section!r}.') + # test_file with path + if not config.has_option(section, 'test_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "test_file" parameter.') + test_file = config[section]['test_file'] + if not os.path.isfile(test_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid "test_file" value {test_file!r} in {section!r}.') + # output_dir + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_dir = config[section]['output_dir'] + # 1) use_estnltk=True and use_ensemble=False -- run model with estnltk's preprocessing and StanzaSyntaxTagger; + # 2) use_estnltk=True and use_ensemble=True -- run model with estnltk's preprocessing and StanzaSyntaxEnsembleTagger + # and use_majority_voting=False (with aggregation_algorithm="las_coherence"); + # 3) use_estnltk=True and use_ensemble=True -- run model with estnltk's preprocessing and StanzaSyntaxEnsembleTagger + # and use_majority_voting=True (with aggregation_algorithm="majority_voting"); + # 4) use_estnltk=False -- run model on input feats loaded from conllu file; + use_estnltk = config[section].getboolean('use_estnltk', False) + use_ensemble = config[section].getboolean('use_ensemble', False) + use_majority_voting = config[section].getboolean('use_majority_voting', False) + if use_ensemble and not use_estnltk: + raise ValueError(f'Error in {conf_file}: section {section!r} conflicting '+\ + 'configuration use_estnltk=False and use_ensemble=True. '+\ + 'Cannot use ensemble tagger without estnltk.' ) + if use_majority_voting and not use_ensemble: + raise ValueError(f'Error in {conf_file}: section {section!r} conflicting '+\ + 'configuration use_ensemble=False and use_majority_voting=True. '+\ + 'Cannot use majority_voting without ensemble.' ) + default_tagger_path = 'estnltk_neural.taggers.StanzaSyntaxTagger' if not use_ensemble else \ + 'estnltk_neural.taggers.StanzaSyntaxEnsembleTagger' + tagger_path = config[section].get('tagger_path', default_tagger_path) + dry_run = config[section].getboolean('dry_run', dry_run) + use_gpu = config[section].getboolean('use_gpu', False) + # seed for randomly picking one analysis from ambiguous morph analyses + seed = config[section].getint('seed', 43) + # seed for randomly choosing one dependency result from results with maximum scores + scores_seed = config[section].getint('scores_seed', 3) + output_prefix = config[section].get('output_file_prefix', 'predicted_') + lang = config[section].get('lang', 'et') + # Get model file or files + model_file = None + model_files = [] + if use_ensemble: + # predict with ensemble: get models_dir + if not config.has_option(section, 'models_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "models_dir" parameter.') + models_dir = config[section]['models_dir'] + if not os.path.isdir(models_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "models_dir" value {models_dir!r} in {section!r}.') + # collect all model files from the directory + model_file_name_pattern = re.compile( "^model_(.+)\.pt$") + for fname in os.listdir(models_dir): + if model_file_name_pattern.match(fname): + model_files.append( os.path.join(models_dir, fname) ) + if len(model_files) == 0: + raise Exception( f'Error in {conf_file}: section {section!r}: Did not find any model files for '+\ + 'the ensemble tagger from models_dir={models_dir!r}.' ) + else: + # predict with a single model: get model file with path + if not config.has_option(section, 'model_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "model_file" parameter.') + model_file = config[section]['model_file'] + if not os.path.isfile(model_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid "model_file" value {model_file!r} in {section!r}.') + # Run predictions + if not dry_run: + start_time = datetime.now() + # Predict on train data + train_output = os.path.join(output_dir, f'{output_prefix}train.conllu') + if use_estnltk: + if not config.has_option(section, 'morph_layer'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "morph_layer" parameter.') + morph_layer = config[section]['morph_layer'] + if not use_ensemble: + # run StanzaSyntaxTagger + predict_with_stanza_tagger(train_file, morph_layer, model_file, train_output, + tagger_path=tagger_path, seed=seed, lang=lang, + use_gpu=use_gpu) + else: + # run StanzaSyntaxEnsembleTagger + predict_with_stanza_ensemble_tagger(train_file, morph_layer, model_files, train_output, + tagger_path=tagger_path, seed=seed, lang=lang, + use_majority_voting=use_majority_voting, + use_gpu=use_gpu, scores_seed=scores_seed) + else: + # run vanilla stanza + predict_with_stanza(train_file, model_file, train_output, lang=lang, use_gpu=use_gpu) + # Predict on test data + test_output = os.path.join(output_dir, f'{output_prefix}test.conllu') + if use_estnltk: + if not config.has_option(section, 'morph_layer'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "morph_layer" parameter.') + morph_layer = config[section]['morph_layer'] + if not use_ensemble: + # run StanzaSyntaxTagger + predict_with_stanza_tagger(test_file, morph_layer, model_file, test_output, + tagger_path=tagger_path, seed=seed, lang=lang, + use_gpu=use_gpu) + else: + # run StanzaSyntaxEnsembleTagger + predict_with_stanza_ensemble_tagger(test_file, morph_layer, model_files, test_output, + tagger_path=tagger_path, seed=seed, lang=lang, + use_majority_voting=use_majority_voting, + use_gpu=use_gpu, scores_seed=scores_seed) + else: + # run vanilla stanza + predict_with_stanza(test_file, model_file, test_output, lang=lang, use_gpu=use_gpu) + print(f'Total time elapsed: {datetime.now()-start_time}') + elif experiment_type_clean in ['crossvalidation', 'half_data', 'smaller_data', 'multi_experiment']: + # ------------------------------------------ + # 'multi_experiment' (general) + # 'crossvalidation' + # 'half_data' + # 'smaller_data' + # ------------------------------------------ + # input_dir (training conllu files) + if not config.has_option(section, 'input_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "input_dir" parameter.') + input_dir = config[section]['input_dir'] + if not os.path.isdir(input_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "input_dir" value {input_dir!r} in {section!r}.') + # models_dir + if not config.has_option(section, 'models_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "models_dir" parameter.') + models_dir = config[section]['models_dir'] + if not os.path.isdir(models_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "models_dir" value {models_dir!r} in {section!r}.') + # test_file with full path, or a pattern for finding test file from input_dir + if not config.has_option(section, 'test_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "test_file" parameter.') + test_file = config[section]['test_file'] + test_file_is_pattern = config[section].getboolean('test_file_is_pattern', False) + if not test_file_is_pattern and not os.path.isfile(test_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid "test_file" value {test_file!r} in {section!r}.') + # output_dir + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_dir = config[section]['output_dir'] + # use_estnltk=True -- run model with estnltk's preprocessing and StanzaSyntaxTagger; + # use_estnltk=False -- run model on input feats loaded from conllu file; + use_estnltk = config[section].getboolean('use_estnltk', False) # use estnltk's StanzaSyntaxTagger + if config[section].getboolean('use_ensemble', False): + # Ensemble models are not supported here + raise NotImplementedError(f'Error in {conf_file}: section {section!r}: experiment_type='+\ + f'{experiment_type_clean!r} does not support ensemble models.') + tagger_path = config[section].get('tagger_path', 'estnltk_neural.taggers.StanzaSyntaxTagger') + dry_run = config[section].getboolean('dry_run', dry_run) + use_gpu = config[section].getboolean('use_gpu', False) + # skip_train: do not predict on train files + skip_train = config[section].getboolean('skip_train', False) + # test_matrix prediction mode: run all models on all test files + test_matrix = config[section].getboolean('test_matrix', False) + if test_matrix and not test_file_is_pattern: + raise ValueError('(!) test_matrix can only be used if test file name is a regular expression') + output_prefix = config[section].get('output_file_prefix', 'predicted_') + seed = config[section].getint('seed', 43) + lang = config[section].get('lang', 'et') + morph_layer = None + if use_estnltk: + if not config.has_option(section, 'morph_layer'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "morph_layer" parameter.') + morph_layer = config[section]['morph_layer'] + # Patterns for capturing names of training sub-experiment files + train_file_pat = r'(?P\d+)_train_all.conllu' + # Override sub-experiment patterns (if required) + if config.has_option(section, 'train_file_pat'): + train_file_pat = config[section]['train_file_pat'] + parser = 'stanza' + # Run models + bulk_predict( input_dir, models_dir, train_file_pat, test_file, + output_dir, output_file_prefix=output_prefix, subexp=subexp, + test_file_is_pattern=test_file_is_pattern, parser=parser, + use_estnltk=use_estnltk, morph_layer=morph_layer, seed=seed, + tagger_path=tagger_path, lang=lang, skip_train=skip_train, + test_matrix=test_matrix, use_gpu=use_gpu, dry_run=dry_run ) + if not section_found: + print(f'No section starting with "predict_stanza_" in {conf_file}.') + + +def bulk_predict( data_folder, models_folder, train_file_pattern, test_file_path, + output_path, output_file_prefix='predicted_', subexp=None, + test_file_is_pattern=False, parser='stanza', use_estnltk=False, + morph_layer=None, seed=None, tagger_path=None, lang='et', + skip_train=False, test_matrix=False, use_gpu=False, dry_run=False ): + ''' + Runs models of multiple sub-experiments on (train/test) files from `data_folder`. + Outputs prediction conllu files to `output_path`. + + Parameter `train_file_pattern` must be a string compilable into regexp pattern + that can be used to detect training data sets of all sub-experiments. + This patterns must have the named group 'exp', indicating part of the pattern + matching sub-experiment name. + Optinally, if test_file_is_pattern=True, then `test_file_path` is compiled to + a regular expression (must have named group 'exp') and used to find a test file + corresponding to train file from `data_folder`. + + By default, each model is evaluated on a train file, and on either a single + test file or a test file corresponding to the training file (of the sub- + experiment). + If skip_train==True, then no evaluation is done on train files. Note, however, + that even with skip_train==True, `train_file_pattern` must be provided, as it + is required for determining sub-experiment names and finding corresponding + models. + If test_matrix==True, then each model is evaluated on all test files. + The test_matrix mode only works with test_file_is_pattern=True option. + + Use parameter `subexp` to restrict predictions only to a single sub-experiment + instead of performing all sub-experiments. + This is useful when multiple instances of the Python are launched for + parallelization. + ''' + # Validate input arguments + supported_parsers = ['stanza'] + if not isinstance(parser, str) or parser.lower() not in supported_parsers: + raise ValueError( f'(!) Unexpected parser: {parser!r}. '+\ + f'Supported parsers: {supported_parsers!r}' ) + parser = parser.lower() + if parser == 'stanza' and tagger_path is None: + tagger_path = 'estnltk_neural.taggers.StanzaSyntaxTagger' + if not os.path.exists(data_folder) or not os.path.isdir(data_folder): + raise Exception(f'(!) Missing or invalid data_folder {data_folder!r}') + if not os.path.exists(models_folder) or not os.path.isdir(models_folder): + raise Exception(f'(!) Missing or invalid models_folder {models_folder!r}') + if use_estnltk and morph_layer is None: + raise Exception(f'(!) Unexpected None value for morph_layer with use_estnltk') + test_file_regex = None + if not test_file_is_pattern: + # Test file is always the same: should be a full path + if not os.path.exists(test_file_path) or not os.path.isfile(test_file_path): + raise Exception(f'(!) Missing or invalid test_file_path {test_file_path!r}') + else: + # Test file should be found via regexp: + # Convert test_file to regular experssion + if not isinstance(test_file_path, str): + raise TypeError('test_file_path must be a string') + try: + test_file_regex = re.compile(test_file_path) + except Exception as err: + raise ValueError(f'Unable to convert {test_file_path!r} to regexp') from err + if 'exp' not in test_file_regex.groupindex: + raise ValueError(f'Regexp {test_file_path!r} is missing named group "exp"') + if test_matrix and test_file_regex is None: + raise Exception(f'(!) test_matrix can only be used if test_file_regex is provided') + # Convert train_file_pattern to regular experssion + train_file_regex = None + if not isinstance(train_file_pattern, str): + raise TypeError('train_file_pattern must be a string') + try: + train_file_regex = re.compile(train_file_pattern) + except Exception as err: + raise ValueError(f'Unable to convert {train_file_pattern!r} to regexp') from err + if 'exp' not in train_file_regex.groupindex: + raise ValueError(f'Regexp {train_file_pattern!r} is missing named group "exp"') + # + # Collect experiment input files + # + models_folder_files = [ fname for fname in os.listdir(models_folder) ] + experiment_data = { 'train':[], 'test':[], 'models': [], 'numbers':[] } + if not test_matrix: + # ============================================================== + # Default mode: + # * run each model on its train file (if not skip_train) + # * run each model on its test file or on the global test file + # ============================================================== + for fname in sorted( os.listdir(data_folder) ): + m = train_file_regex.match(fname) + if m: + if not (fname.lower()).endswith('.conllu'): + raise Exception( f'(!) invalid file {fname}: train file '+\ + 'must have extension .conllu' ) + fpath = os.path.join(data_folder, fname) + # Training file varies, depending on the sub set of data + experiment_data['train'].append( fpath ) + no = m.group('exp') + if no not in experiment_data['numbers']: + experiment_data['numbers'].append(no) + if test_file_regex is None: + # No regexp for test file: + # Test file is always the same (global test file) + experiment_data['test'].append( test_file_path ) + else: + # Test file regexp provided: + # Find test file corresponding to train file + found_test_file = None + for fname_2 in sorted( os.listdir(data_folder) ): + m2 = test_file_regex.match(fname_2) + if m2: + no2 = m2.group('exp') + if no2 == no: + found_test_file = \ + os.path.join(data_folder, fname_2) + break + if found_test_file is not None: + experiment_data['test'].append( found_test_file ) + else: + raise Exception(f'(!) Unable to find test file corresponding '+\ + f'to train file {fname!r} from {data_folder!r}.') + # Find corresponding model from the models folder + target_model_file = f"model_{no}.pt" + model_found = False + for model_fname in models_folder_files: + if model_fname == target_model_file: + mfpath = os.path.join(models_folder, model_fname) + experiment_data['models'].append(mfpath) + model_found = True + break + if not model_found: + if not dry_run: + raise Exception(f'(!) Unable to find model {target_model_file!r} from {models_folder!r}') + else: + # Try run, emulate only, don't chk for models + experiment_data['models'].append(target_model_file) + else: + # ============================================================== + # Test matrix mode: + # * run each model on its train file (if train file is available) + # * run each model on all test files + # ============================================================== + for fname in sorted( os.listdir(data_folder) ): + m = test_file_regex.match(fname) + if m: + if not (fname.lower()).endswith('.conllu'): + raise Exception( f'(!) invalid file {fname}: test file '+\ + 'must have extension .conllu' ) + no = m.group('exp') + if no not in experiment_data['numbers']: + experiment_data['numbers'].append(no) + # Placeholder for test file to pass checks below + found_test_file = os.path.join(data_folder, fname) + experiment_data['test'].append( found_test_file ) + # Find corresponding model from the models folder + target_model_file = f"model_{no}.pt" + model_found = False + for model_fname in models_folder_files: + if model_fname == target_model_file: + mfpath = os.path.join(models_folder, model_fname) + experiment_data['models'].append(mfpath) + model_found = True + break + if not model_found: + if not dry_run: + raise Exception(f'(!) Unable to find model {target_model_file!r} from {models_folder!r}') + else: + # Try run, emulate only, don't chk for models + experiment_data['models'].append(target_model_file) + # Try to find corresponding train file (optional) + found_train_file = None + for fname_2 in sorted( os.listdir(data_folder) ): + m2 = train_file_regex.match(fname_2) + if m2: + no2 = m2.group('exp') + if no2 == no: + found_train_file = \ + os.path.join(data_folder, fname_2) + break + experiment_data['train'].append(found_train_file) + # + # Validate that we have correct numbers of experiment files + # + for subset in ['train']: + if len(experiment_data[subset]) == 0: + raise Exception(f'Unable to find any {subset} files '+\ + f'matching {train_file_pattern!r} in dir {data_folder!r}.') + if len(experiment_data[subset]) != len(experiment_data['numbers']): + no1 = len(experiment_data[subset]) + no2 = len(experiment_data['numbers']) + raise Exception(f'Number of {subset} files ({no1}) does not match '+\ + f'the number of experiments ({no2}).') + if len(experiment_data[subset]) != len(experiment_data['models']): + no1 = len(experiment_data[subset]) + no2 = len(experiment_data['models']) + raise Exception(f'Number of {subset} files ({no1}) does not match '+\ + f'the number of models ({no2}).') + if subexp is not None: + if subexp not in experiment_data['numbers']: + raise ValueError( f'(!) sub-experiment {subexp!r} not in collected '+\ + f'experiment names: {experiment_data["numbers"]}.' ) + # + # Launch experiments + # + if not dry_run: + start_time = datetime.now() + for i in range( len(experiment_data['numbers']) ): + exp_no = experiment_data['numbers'][i] + train_file = experiment_data['train'][i] + test_file = experiment_data['test'][i] + model_file = experiment_data['models'][i] + if subexp is not None and exp_no != subexp: + # Skip other experiments + continue + if parser == 'stanza': + # Predict on train data (optional, can be skipped) + if train_file is not None and not skip_train: + train_output = os.path.join(output_path, f'{output_file_prefix}train_{exp_no}.conllu') + if use_estnltk: + predict_with_stanza_tagger(train_file, morph_layer, model_file, train_output, + tagger_path=tagger_path, seed=seed, lang=lang, + use_gpu=use_gpu) + else: + predict_with_stanza(train_file, model_file, train_output, lang=lang, use_gpu=use_gpu) + # Predict on test data + if not test_matrix: + # Predict on single test file + test_output = os.path.join(output_path, f'{output_file_prefix}test_{exp_no}.conllu') + if use_estnltk: + predict_with_stanza_tagger(test_file, morph_layer, model_file, test_output, + tagger_path=tagger_path, seed=seed, lang=lang, + use_gpu=use_gpu) + else: + predict_with_stanza(test_file, model_file, test_output, lang=lang, use_gpu=use_gpu) + else: + # Predict for matrix: predict on all test files + for j in range( len(experiment_data['numbers']) ): + exp_no2 = experiment_data['numbers'][j] + test_file2 = experiment_data['test'][j] + test_output = os.path.join(output_path, \ + f'{output_file_prefix}model_{exp_no}_test_{exp_no2}.conllu') + if use_estnltk: + predict_with_stanza_tagger(test_file2, morph_layer, model_file, test_output, + tagger_path=tagger_path, seed=seed, lang=lang, + use_gpu=use_gpu) + else: + predict_with_stanza(test_file2, model_file, test_output, lang=lang, use_gpu=use_gpu) + print() + print() + print(f'Total time elapsed: {datetime.now()-start_time}') + +# ======================================================================== +# Stanza interface: run models to get depparse predictions +# Two ways: +# 1) run model with estnltk's preprocessing and StanzaSyntax(Ensemble)Tagger; +# 2) run model on input feats loaded from conllu file; +# ======================================================================== + +def create_estnltk_document( input_path, morph_layer='morph_extended', + syntax_layer='gold_syntax' ): + """ + Loads given CONLLU file as estnltk's Text object. + Preannotates text: adds tokenization layers and morph_layer + (either 'morph_extended' or 'morph_analysis'). + Returns loaded Text object. + + Requires: estnltk v1.7.2+ + + :param input_path: path to conllu file to be loaded + :param morph_layer: name of estnltk's morphological analysis layer + :param syntax_layer: name of syntactic analysis layer loaded from file + :return: estnltk Text object + """ + from estnltk import Text + from estnltk.taggers import WhiteSpaceTokensTagger + from estnltk.taggers import PretokenizedTextCompoundTokensTagger + from estnltk.converters.conll.conll_importer import conll_to_text + text_obj = conll_to_text(input_path, syntax_layer) + assert 'words' in text_obj.layers + assert 'sentences' in text_obj.layers + if 'compound_tokens' not in text_obj.layers: + (WhiteSpaceTokensTagger()).tag( text_obj ) + (PretokenizedTextCompoundTokensTagger()).tag( text_obj ) + text_obj.tag_layer( morph_layer ) + return text_obj + +def create_stanza_document(input_path): + """ + Loads sentences from given CONLLU file and creates stanza's Document. + Document will be pretagged: it contains id, text, lemma, upos, xpos, + feats values loaded from the CONLLU file, and empty values in place + of other conllu fields. + Returns loaded Document. + + :param input_path: path to conllu file to be loaded + :return: stanza Document + """ + with open(input_path, 'r', encoding='utf-8') as conllu_file: + data = [] + for tokenlist in parse_incr(conllu_file): + pretagged_sent = [] + for word in tokenlist: + if not isinstance(word['id'], int): + # Because stanza cannot handle ellipsis (considers it + # a multi-word), we leave ellipsis word out + continue + word_feats = { + 'id': word['id'], + 'text': word['form'], + 'lemma': word['lemma'], + 'upos': word['upos'], + 'xpos': word['xpos'], + 'feats': serialize_field( word['feats'] ) + } + pretagged_sent.append(word_feats) + data.append(pretagged_sent) + # create Document-obj from sentences of the pretagged file + return Document(data) + +def predict_with_stanza(input_path, model_path, output_path, lang='et', use_gpu=False): + ''' + Applies stanza's model on given input CONLLU file to get depparse predictions. + Saves predictions to output CONLLU file. + + :param input_path: path to conllu file to be annotated + :param model_path: path to depparse model to be used for making predictions + :param output_path: path to output conllu file + ''' + config = { + 'processors': 'depparse', # Comma-separated list of processors to use + 'lang': lang, # Language code for the language to build the Pipeline in + 'depparse_pretagged': True, + 'depparse_model_path': model_path, + 'download_method': 0, # NONE will not download anything + 'use_gpu': use_gpu + } + nlp = Pipeline(**config) + doc = create_stanza_document(input_path) + nlp(doc) + output_dir, output_fname = os.path.split(output_path) + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + write_stanza_doc_to_conll(doc, output_path) + +def predict_with_stanza_tagger(input_path, morph_layer, model_path, output_path, + tagger_path='estnltk_neural.taggers.StanzaSyntaxTagger', + seed=None, lang='et', use_gpu=False): + ''' + Applies estnltk's StanzaSyntaxTagger on given input CONLLU file to get depparse predictions. + Uses estnltk's preprocessing to load and re-annotate document (adds morph_layer). + Saves predictions to output CONLLU file. + + By default, imports tagger from 'estnltk_neural.taggers.StanzaSyntaxTagger', but you can + use `tagger_path` to overwrite the importing path. Use this if you've customized the tagger + (e.g. made fixes for it), and want to test it out (instead of the default version). + + Requires: estnltk v1.7.2+ + + :param input_path: path to conllu file to be annotated + :param morph_layer: name of estnltk's morphological analysis layer + :param model_path: path to depparse model to be used for making predictions + :param output_path: path to output conllu file + :param tagger_path: full import path of StanzaSyntaxTagger + :param seed: seed of the random process creating unambiguous morph analysis layer + ''' + tagger_loader = \ + create_stanza_tagger_loader( tagger_path, model_path, morph_layer, use_gpu=use_gpu, seed=seed ) + tagger = tagger_loader.tagger # Load tagger + text_obj = create_estnltk_document(input_path, morph_layer=morph_layer) + tagger.tag(text_obj) + output_dir, output_fname = os.path.split(output_path) + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + write_estnltk_text_to_conll(text_obj, tagger.output_layer, output_path) + +def predict_with_stanza_ensemble_tagger(input_path, morph_layer, model_paths, output_path, + tagger_path='estnltk_neural.taggers.StanzaSyntaxEnsembleTagger', + seed=None, scores_seed=None, use_majority_voting=False, + lang='et', use_gpu=False, verbose=True): + ''' + Applies estnltk's StanzaSyntaxEnsembleTagger on given input CONLLU file to get depparse predictions. + Uses estnltk's preprocessing to load and re-annotate document (adds morph_layer). + Saves predictions to output CONLLU file. + + By default, imports tagger from 'estnltk_neural.taggers.StanzaSyntaxEnsembleTagger', but you can + use `tagger_path` to overwrite the importing path. Use this if you've customized the tagger + (e.g. made fixes for it), and want to test it out (instead of the default version). + + Requires: estnltk v1.7.2+ + + :param input_path: path to conllu file to be annotated + :param morph_layer: name of estnltk's morphological analysis layer + :param model_path: path to depparse model to be used for making predictions + :param output_path: path to output conllu file + :param tagger_path: full import path of StanzaSyntaxEnsembleTagger + :param seed: seed of the random process creating unambiguous morph analysis layer + :param scores_seed: seed of the random process picking one parse from multiple parses with max score + :param use_majority_voting: whether StanzaSyntaxEnsembleTagger should use 'majority_voting' as the + aggregation algorithm + ''' + tagger_loader = \ + create_stanza_ensemble_tagger_loader( tagger_path, model_paths, morph_layer, + use_majority_voting=use_majority_voting, + use_gpu=use_gpu, seed=seed, scores_seed=scores_seed ) + tagger = tagger_loader.tagger # Load tagger + if verbose: + print(f'Loaded {tagger_path!r} with {len(model_paths)} models for prediction.') + text_obj = create_estnltk_document(input_path, morph_layer=morph_layer) + if verbose: + print(f'Preprocessed {input_path!r}.') + tagger.tag(text_obj) + if verbose: + print(f'Parsed {input_path!r} with the ensemble tagger.') + output_dir, output_fname = os.path.split(output_path) + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + write_estnltk_text_to_conll(text_obj, tagger.output_layer, output_path) + +def create_stanza_tagger_loader( tagger_path, model_path, input_morph_layer, use_gpu=False, seed=None ): + '''Creates estnltk's TaggerLoader for customized importing of StanzaSyntaxTagger.''' + from estnltk_core.taggers import TaggerLoader + parameters={ 'input_morph_layer': input_morph_layer, + 'input_type': input_morph_layer, + 'depparse_path': model_path, + 'use_gpu': use_gpu } + if isinstance(seed, int): + parameters['random_pick_seed'] = seed + return TaggerLoader( 'stanza_syntax', + ['sentences', input_morph_layer, 'words'], + tagger_path, + output_attributes=('id', 'lemma', 'upostag', 'xpostag', 'feats', 'head', 'deprel', 'deps', 'misc'), + parameters=parameters ) + +def create_stanza_ensemble_tagger_loader( tagger_path, model_paths, input_morph_layer, use_majority_voting=False, + use_gpu=False, seed=None, scores_seed=None ): + '''Creates estnltk's TaggerLoader for customized importing of StanzaSyntaxEnsembleTagger.''' + from estnltk_core.taggers import TaggerLoader + parameters={ 'input_morph_layer': input_morph_layer, + 'model_paths': model_paths, + 'use_gpu': use_gpu } + if isinstance(seed, int): + parameters['random_pick_seed'] = seed + if isinstance(scores_seed, int): + parameters['random_pick_max_score_seed'] = scores_seed + if use_majority_voting: + parameters['aggregation_algorithm'] = 'majority_voting' + return TaggerLoader( 'stanza_ensemble_syntax', + ['sentences', input_morph_layer, 'words'], + tagger_path, + output_attributes=('id', 'lemma', 'upostag', 'xpostag', 'feats', 'head', 'deprel', 'deps', 'misc'), + parameters=parameters ) + +def write_stanza_doc_to_conll(doc, output_path): + '''Writes given stanza Document to CoNLLU format output file.''' + conll = CoNLL.convert_dict(doc.to_dict()) + with open(output_path, 'w', encoding='utf-8') as fout: + for sentence in conll: + for word in sentence: + fout.write('\t'.join(word) + '\n') + fout.write('\n') + fout.write('\n' * 2) + +def write_estnltk_text_to_conll(text, syntax_layer, output_path): + '''Writes given estnltk's Text with syntax_layer to CoNLLU format output file.''' + from estnltk.converters.conll.conll_exporter import layer_to_conll + text_conll_str = \ + layer_to_conll(text, syntax_layer, preserve_ambiguity=False) + with open(output_path, 'w', encoding='utf-8') as fout: + fout.write( text_conll_str ) + fout.write('\n') + layer = text[syntax_layer] + if 'entropy' in layer.attributes: + if output_path.endswith('.conllu'): + # Write out prediction entropy results + output_path_entropy = output_path.replace('.conllu', '.entropy') + last_word_id = None + with open(output_path_entropy, 'w', encoding='utf-8') as fout: + for syntax_word in layer: + ann = syntax_word.annotations[0] + cur_word_id = int(ann['id']) + if last_word_id is not None and cur_word_id == 1: + # add sentence break + fout.write('\n') + fout.write( str(ann['votes'])+'\t'+str(ann['entropy']) ) + fout.write('\n') + last_word_id = cur_word_id + fout.write('\n' * 2) + else: + warnings.warn( f'(!) Unexpected file ending in {output_path} (expected .conllu), '+\ + 'skipping entropy file creation.' ) + +# ======================================================================== + +if __name__ == '__main__': + if len(sys.argv) < 2: + raise Exception('(!) Missing input argument: name of the configuration INI file.') + conf_file = sys.argv[1] + subexp = None + if len(sys.argv) > 2: + subexp = sys.argv[2] + run_models_main( conf_file, subexp=subexp ) \ No newline at end of file diff --git a/04b_predict_malt_udpipe.py b/04b_predict_malt_udpipe.py new file mode 100644 index 00000000..075df34c --- /dev/null +++ b/04b_predict_malt_udpipe.py @@ -0,0 +1,594 @@ +# +# Applies MaltParser/UDPipe1 models to get predictions. +# Implemented settings: +# * full_data +# * multi_experiment (general) +# * crossvalidation +# * half_data +# * smaller_data +# +import os, os.path +import re +import sys +import shutil +from datetime import datetime +import subprocess +from collections import defaultdict +from decimal import Decimal, getcontext +from random import Random + +import conllu +import configparser + +# Change to local paths & files, if required +DEFAULT_MALTPARSER_DIR = 'MaltOptimizer-1.0.3' +DEFAULT_MALTPARSER_JAR = 'maltparser-1.9.2.jar' +#DEFAULT_UDPIPE_DIR = 'udpipe-1.2.0-bin\\bin-win64' +DEFAULT_UDPIPE_DIR = 'udpipe-1.2.0-bin/bin-linux64' + +def run_models_main( conf_file, subexp=None, dry_run=False ): + ''' + Runs MaltParser/UDPipe-1 models to get predictions based on + the configuration. + Settings/parameters of running models will be read from + the given `conf_file`. + Executes sections in the configuration starting with prefix + 'predict_malt_' and 'predict_udpipe1_'. + + Optinally, if `subexp` is defined, then predicts only + that sub-experiment and skips all other sub-experiments (in + crossvalidation, smaller_data and half_data experiments). + ''' + # Parse configuration file + config = configparser.ConfigParser() + if conf_file is None or not os.path.exists(conf_file): + raise FileNotFoundError("Config file {} does not exist".format(conf_file)) + if len(config.read(conf_file)) != 1: + raise ValueError("File {} is not accessible or is not in valid INI format".format(conf_file)) + section_found = False + for section in config.sections(): + if section.startswith('predict_malt_') or section.startswith('predict_udpipe1_'): + parser = 'maltparser' if section.startswith('predict_malt_') else 'udpipe1' + section_found = True + subexp_str = '' if subexp is None else f' ({subexp})' + print(f'Running {section}{subexp_str} ...') + experiment_type = config[section].get('experiment_type', 'full_data') + experiment_type_clean = (experiment_type.strip()).lower() + if experiment_type_clean not in ['full_data', 'crossvalidation', 'half_data', 'smaller_data', 'multi_experiment']: + raise ValueError('(!) Unexpected experiment_type value: {!r}'.format(experiment_type)) + if experiment_type_clean == 'full_data': + # ------------------------------------------ + # 'full_data' + # ------------------------------------------ + # train_file with path + if not config.has_option(section, 'train_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "train_file" parameter.') + train_file = config[section]['train_file'] + if not os.path.isfile(train_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid "train_file" value {train_file!r} in {section!r}.') + # test_file with path + if not config.has_option(section, 'test_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "test_file" parameter.') + test_file = config[section]['test_file'] + if not os.path.isfile(test_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid "test_file" value {test_file!r} in {section!r}.') + # output_dir + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_dir = config[section]['output_dir'] + output_prefix = config[section].get('output_file_prefix', 'predicted_') + if not output_prefix.endswith('_'): + output_prefix += '_' + # use multiple models as an ensemble + use_ensemble = config[section].getboolean('use_ensemble', False) + use_majority_voting = config[section].getboolean('use_majority_voting', False) + aggregation_algorithm = 'las_coherence' if not use_majority_voting else 'majority_voting' + scores_seed = config[section].getint('scores_seed', 3) + # Get model file or files + model_file = None + model_files = [] + if use_ensemble: + # predict with ensemble: get models_dir + if not config.has_option(section, 'models_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "models_dir" parameter.') + models_dir = config[section]['models_dir'] + if not os.path.isdir(models_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "models_dir" value {models_dir!r} in {section!r}.') + # collect all model files from the directory + if parser == 'maltparser': + model_file_name_pattern = 'model_(.+).mco' + else: + model_file_name_pattern = 'model_(.+).udpipe' + model_file_name_pattern = re.compile(model_file_name_pattern) + for fname in os.listdir(models_dir): + if model_file_name_pattern.match(fname): + model_files.append( os.path.join(models_dir, fname) ) + if len(model_files) == 0: + raise Exception( f'Error in {conf_file}: section {section!r}: Did not find any model files for '+\ + 'the ensemble tagger from models_dir={models_dir!r}.' ) + else: + # predict with a single model: get model file with path + default_model = 'model.mco' if parser == 'maltparser' else 'model.udpipe' + model_file = config[section].get('model_file', default_model) + if not os.path.isfile(model_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid "model_file" value {model_file!r} in {section!r}.') + # other parameters + dry_run = config[section].getboolean('dry_run', dry_run) + # MaltParser options + maltparser_dir = config[section].get('maltparser_dir', DEFAULT_MALTPARSER_DIR) + maltparser_jar = config[section].get('maltparser_jar', DEFAULT_MALTPARSER_JAR) + # UDPipe-1 options + udpipe_dir = config[section].get('udpipe_dir', DEFAULT_UDPIPE_DIR) + if not dry_run: + if parser == 'maltparser': + # Predict on train + output_file = f'{output_prefix}train.conllu' + if not use_ensemble: + predict_maltparser(model_file, train_file, output_file, output_dir, + maltparser_dir=maltparser_dir, + maltparser_jar=maltparser_jar) + else: + predict_ensemble(parser, model_files, train_file, output_file, output_dir, + aggregation_algorithm = aggregation_algorithm, + random_pick_max_score_seed = scores_seed, + maltparser_dir=maltparser_dir, + maltparser_jar=maltparser_jar) + # Predict on test + output_file = f'{output_prefix}test.conllu' + if not use_ensemble: + predict_maltparser(model_file, test_file, output_file, output_dir, + maltparser_dir=maltparser_dir, + maltparser_jar=maltparser_jar) + else: + predict_ensemble(parser, model_files, test_file, output_file, output_dir, + aggregation_algorithm = aggregation_algorithm, + random_pick_max_score_seed = scores_seed, + maltparser_dir=maltparser_dir, + maltparser_jar=maltparser_jar) + elif parser == 'udpipe1': + # Predict on train + output_file = f'{output_prefix}train.conllu' + if not use_ensemble: + predict_udpipe1(model_file, train_file, output_file, output_dir, + udpipe_dir=udpipe_dir) + else: + predict_ensemble(parser, model_files, train_file, output_file, output_dir, + aggregation_algorithm = aggregation_algorithm, + random_pick_max_score_seed = scores_seed, + udpipe_dir=udpipe_dir) + # Predict on test + output_file = f'{output_prefix}test.conllu' + if not use_ensemble: + predict_udpipe1(model_file, test_file, output_file, output_dir, + udpipe_dir=udpipe_dir) + else: + predict_ensemble(parser, model_files, test_file, output_file, output_dir, + aggregation_algorithm = aggregation_algorithm, + random_pick_max_score_seed = scores_seed, + udpipe_dir=udpipe_dir) + else: + raise Exception(f'Unexpected parser name: {parser!r}') + elif experiment_type_clean in ['crossvalidation', 'half_data', 'smaller_data', 'multi_experiment']: + # ------------------------------------------ + # 'multi_experiment' (general) + # 'crossvalidation' + # 'half_data' + # 'smaller_data' + # ------------------------------------------ + # input_dir + if not config.has_option(section, 'input_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "input_dir" parameter.') + input_dir = config[section]['input_dir'] + if not os.path.isdir(input_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "input_dir" value {input_dir!r} in {section!r}.') + # output_dir + if not config.has_option(section, 'output_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "output_dir" parameter.') + output_dir = config[section]['output_dir'] + # models_dir + if not config.has_option(section, 'models_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "models_dir" parameter.') + models_dir = config[section]['models_dir'] + if not os.path.isdir(models_dir): + raise FileNotFoundError(f'Error in {conf_file}: invalid "models_dir" value {models_dir!r} in {section!r}.') + models_dir_files = [ fname for fname in os.listdir(models_dir) ] + if len(models_dir_files) == 0: + raise Exception(f'(!) No files found from models_dir {models_dir!r}') + # test_file with full path, or a pattern for finding test file from input_dir + if not config.has_option(section, 'test_file'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "test_file" parameter.') + test_file = config[section]['test_file'] + test_file_is_pattern = config[section].getboolean('test_file_is_pattern', False) + if not test_file_is_pattern and not os.path.isfile(test_file): + raise FileNotFoundError(f'Error in {conf_file}: invalid "test_file" value {test_file!r} in {section!r}.') + # Common options + dry_run = config[section].getboolean('dry_run', dry_run) + output_file_prefix = config[section].get('output_file_prefix', 'predicted_') + if not output_file_prefix.endswith('_'): + output_file_prefix += '_' + # Train file pattern + train_file_pat = r'(?P\d+)_train.conllu' + train_file_re = None + if config.has_option(section, 'train_file_pat'): + train_file_pat = config[section]['train_file_pat'] + train_file_re = _create_regexp_pattern(train_file_pat, 'train_file_pat') + # Test file pattern (if provided) + test_file_re = None + if test_file_is_pattern: + test_file_re = _create_regexp_pattern(test_file, 'test_file') + # skip_train: do not predict on train files + skip_train = config[section].getboolean('skip_train', False) + # test_matrix prediction mode: run all models on all test files + test_matrix = config[section].getboolean('test_matrix', False) + if test_matrix and test_file_re is None: + raise ValueError(f'(!) test_matrix can only be used if test file name is a regular expression') + # MaltParser options + maltparser_dir = config[section].get('maltparser_dir', DEFAULT_MALTPARSER_DIR) + maltparser_jar = config[section].get('maltparser_jar', DEFAULT_MALTPARSER_JAR) + # UDPipe-1 options + udpipe_dir = config[section].get('udpipe_dir', DEFAULT_UDPIPE_DIR) + # Collect input data + # Collect all train files + all_train_files = {} + for in_fname in sorted(os.listdir(input_dir)): + if in_fname.endswith('.conllu'): + m1 = train_file_re.match(in_fname) + if m1: + train_fpath = os.path.join(input_dir, in_fname) + subexp = m1.group('exp') + all_train_files[subexp] = train_fpath + # Collect all test files + all_test_files = {} + if test_file_re is not None: + # If test_file regex is provided, then collect test files via regexp + for in_fname in sorted(os.listdir(input_dir)): + if in_fname.endswith('.conllu'): + m2 = test_file_re.match(in_fname) + if m2: + test_file_subexp = m2.group('exp') + assert test_file_subexp not in all_test_files.keys(), \ + f'Duplicate test files for experiment {test_file_subexp}' + all_test_files[test_file_subexp] = \ + os.path.join(input_dir, in_fname) + else: + # If test_file regex is missing, assign a single test file for + # all experiments (global testing) + for cur_subexp in sorted( all_train_files.keys() ): + all_test_files[cur_subexp] = test_file + # Sanity checks + if len(all_test_files.keys()) > 0 and len(all_train_files.keys()) > 0: + if not (all_train_files.keys() == all_test_files.keys()): + raise ValueError('(!) Mismatching train and test sub-experiment '+\ + f'names. Train experiments: {all_train_files.keys()!r}; '+\ + f'Test experiments: {all_test_files.keys()!r} ') + elif len(all_test_files.keys()) == 0 and len(all_train_files.keys()) == 0: + raise ValueError(f'(!) No train or test files found from {input_dir}') + # + # Iterate over input files and predict + # + for cur_subexp in sorted( all_test_files.keys() ): + cur_test_file = all_test_files[cur_subexp] + cur_train_file = all_train_files.get(cur_subexp, None) + assert cur_train_file is not None or test_matrix + if parser == 'maltparser': + model_file = f'model_{cur_subexp}.mco' + else: + model_file = f'model_{cur_subexp}.udpipe' + # Try to find corresponding model from the models subdirectory + if model_file not in models_dir_files: + raise Exception(f'(!) Could not find model file {model_file!r} for experiment '+\ + f'{cur_subexp!r} from {models_dir!r}.') + model_path = os.path.join(models_dir, model_file) + # Run model for predictions + if not dry_run: + train_output_file = f'{output_file_prefix}train_{cur_subexp}.conllu' + test_output_file = f'{output_file_prefix}test_{cur_subexp}.conllu' + if parser == 'maltparser': + # Predict on train data (optional, can be skipped) + if cur_train_file is not None and not skip_train: + predict_maltparser(model_path, cur_train_file, train_output_file, output_dir, + maltparser_dir=maltparser_dir, + maltparser_jar=maltparser_jar) + # Predict on test + if not test_matrix: + predict_maltparser(model_path, cur_test_file, test_output_file, output_dir, + maltparser_dir=maltparser_dir, + maltparser_jar=maltparser_jar) + else: + # predict on all test files + for test_subexp in sorted(all_test_files.keys()): + test_output_fpath = os.path.join(output_path, \ + f'{output_file_prefix}model_{cur_subexp}_test_{test_subexp}.conllu') + cur_test_file = all_test_files[test_subexp] + predict_maltparser(model_path, cur_test_file, test_output_fpath, output_dir, + maltparser_dir=maltparser_dir, + maltparser_jar=maltparser_jar) + elif parser == 'udpipe1': + # Predict on train data (optional, can be skipped) + if cur_train_file is not None and not skip_train: + predict_udpipe1(model_path, cur_train_file, train_output_file, output_dir, + udpipe_dir=udpipe_dir) + # Predict on test + if not test_matrix: + predict_udpipe1(model_path, cur_test_file, test_output_file, output_dir, + udpipe_dir=udpipe_dir) + else: + # predict on all test files + for test_subexp in sorted(all_test_files.keys()): + test_output_fpath = os.path.join(output_path, \ + f'{output_file_prefix}model_{cur_subexp}_test_{test_subexp}.conllu') + cur_test_file = all_test_files[test_subexp] + predict_udpipe1(model_path, cur_test_file, test_output_fpath, output_dir, + udpipe_dir=udpipe_dir) + else: + raise Exception(f'Unexpected parser name: {parser!r}') + if not section_found: + print(f'No section starting with "predict_malt_" or "predict_udpipe1_" in {conf_file}.') + + +def _create_regexp_pattern(fpattern, pattern_var_name): + # Convert file pattern to regular experssion + if not isinstance(fpattern, str): + raise TypeError(f'{pattern_var_name} must be a string') + regexp = None + try: + regexp = re.compile(fpattern) + except Exception as err: + raise ValueError(f'Unable to convert {fpattern!r} to regexp') from err + if 'exp' not in regexp.groupindex: + raise ValueError(f'Regexp {fpattern!r} is missing named group "exp"') + return regexp + + +# =============================================================== +# Predict MaltParser +# =============================================================== + +def check_maltparser_requirements(maltparser_dir=DEFAULT_MALTPARSER_DIR, + maltparser_jar=DEFAULT_MALTPARSER_JAR): + ''' + Check that MaltParser's required folders and jar files are present. + Raises an expection if anything is missing. + ''' + if not os.path.isdir(maltparser_dir): + raise Exception( ('Missing directory: \%s. Please get MaltParser from: https://maltparser.org/index.html') % (maltparser_dir) ) + malt_dir_files = list(os.listdir(maltparser_dir)) + if maltparser_jar not in malt_dir_files: + jar_path = os.path.join(maltparser_dir, maltparser_jar) + raise Exception( ('Missing jar file: \%s. Please get MaltParser from: https://maltparser.org/index.html') % (jar_path) ) + if 'lib' not in malt_dir_files: + lib_path = os.path.join(maltparser_dir, 'lib') + raise Exception( ('Missing java libraries dir: \%s. Please get MaltParser from: https://maltparser.org/index.html') % (lib_path) ) + return True + +def predict_maltparser(model_path, test_corpus, output_file, output_dir, maltparser_dir=DEFAULT_MALTPARSER_DIR, + maltparser_jar=DEFAULT_MALTPARSER_JAR): + ''' + Runs MaltParser on given test_corpus to get predictions and saves results as conllu into output_file. + output_file should be a file name, use output_dir to specify its location. + ''' + check_maltparser_requirements(maltparser_dir=maltparser_dir, maltparser_jar=maltparser_jar) + # Make input file paths absolute + if test_corpus != os.path.abspath(test_corpus): + test_corpus = os.path.abspath(test_corpus) + if model_path != os.path.abspath(model_path): + model_path = os.path.abspath(model_path) + # Note: Maltparser's model must be at the same directory as maltparser jar, + # otherwise we'll run into error "Couldn't find the MaltParser configuration + # file". Copy the model. + model_copied = False + model_dir, model_name = os.path.split(model_path) + if len(model_dir) > 0 and model_dir not in maltparser_dir: + shutil.copyfile(model_path, os.path.join(maltparser_dir, model_name)) + model_copied = True + # Construct command + predict_command = \ + ('java -jar {jar} -c {model} -i {test_corpus} -o {output_file} -m parse').\ + format(jar=maltparser_jar, model=model_name, test_corpus=test_corpus, output_file=output_file) + # Execute + subprocess.call(predict_command, shell=True, cwd=maltparser_dir) + # Remove copied model + if model_copied: + os.remove( os.path.join(maltparser_dir, model_name) ) + # Relocate output + if output_dir is not None: + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + # Remove old file + if os.path.exists(os.path.join(output_dir, output_file)): + os.remove(os.path.join(output_dir, output_file)) + # Move predicted file to output dir + os.rename(os.path.join(maltparser_dir, output_file), + os.path.join(output_dir, output_file)) + +# =============================================================== +# Predict UDPipe1 +# =============================================================== + +def check_if_udpipe_is_in_path(udpipe_cmd='udpipe'): + ''' Checks whether given udpipe is in system's PATH. Returns True, there is + a file with given name (udpipe_cmd) in the PATH, otherwise returns False; + The idea borrows from: http://stackoverflow.com/a/377028 + ''' + if os.getenv("PATH") == None: + return False + for path in os.environ["PATH"].split(os.pathsep): + path1 = path.strip('"') + file1 = os.path.join(path1, udpipe_cmd) + if os.path.isfile(file1) or os.path.isfile(file1 + '.exe'): + return True + return False + +def predict_udpipe1(model_path, test_corpus, output_file, output_dir, udpipe_dir=DEFAULT_UDPIPE_DIR): + ''' + Runs UDPipe-1 on given test_corpus to get predictions and saves results as conllu into output_file. + ''' + udpipe_dir_exists = udpipe_dir is not None and os.path.isdir(udpipe_dir) + udpipe_is_in_path = check_if_udpipe_is_in_path() + if not udpipe_dir_exists and not udpipe_is_in_path: + raise Exception('(!) Could not find UDPipe executable. '+\ + 'Please make sure udpipe is installed and available in system PATH. '+\ + 'Or, alternatively, provide location of UDPipe via variable udpipe_dir. '+\ + 'You can download udpipe from: https://ufal.mff.cuni.cz/udpipe/1/') + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + udpipe_cmd = 'udpipe' + if udpipe_dir_exists: + udpipe_cmd = os.path.join(udpipe_dir, udpipe_cmd) + output_path = os.path.join(output_dir, output_file) + predict_command = \ + ('{udpipe_cmd} --parse {model_path} {test_corpus} --output=conllu --outfile={output_path} ').\ + format(udpipe_cmd=udpipe_cmd, model_path=model_path, test_corpus=test_corpus, + output_path=output_path) + # Execute + subprocess.call(predict_command, shell=True) + +# =============================================================== +# Predict with MaltParser or UDPipe1 ensemble +# =============================================================== + +def predict_ensemble(parser, model_files, test_corpus, output_file, output_dir, aggregation_algorithm = 'las_coherence', + random_pick_max_score_seed = 3, + udpipe_dir=DEFAULT_UDPIPE_DIR, + maltparser_dir=DEFAULT_MALTPARSER_DIR, + maltparser_jar=DEFAULT_MALTPARSER_JAR): + ''' + Runs an ensemble of MaltParser or UDPipe1 models on given test_corpus to get predictions and saves + results as conllu into output_file. + output_file should be a file name, use output_dir to specify its location. + ''' + assert parser in ['maltparser', 'udpipe1'] + assert aggregation_algorithm in ['las_coherence', 'majority_voting'] + # 1) Collect predictions from all of the models + temp_prediction_files = [] + for model_id, model_file in enumerate(model_files): + if parser == 'maltparser': + # Predict on train + temp_output_file = f'temp_malt_predict_{model_id}.conllu' + predict_maltparser(model_file, test_corpus, temp_output_file, output_dir, + maltparser_dir=maltparser_dir, + maltparser_jar=maltparser_jar) + elif parser == 'udpipe1': + # Predict on train + temp_output_file = f'temp_udpipe_predict_{model_id}.conllu' + predict_udpipe1(model_file, test_corpus, temp_output_file, output_dir, + udpipe_dir=udpipe_dir) + output_fpath = os.path.join(output_dir, temp_output_file) + temp_prediction_files.append( output_fpath ) + # 2) Load corresponding predicted conllu contents + predicted_docs = [] + for temp_output_file in temp_prediction_files: + assert os.path.exists(temp_output_file), \ + f'(!) Missing {parser} output file {temp_output_file!r}.' + with open(temp_output_file, 'r', encoding='utf-8') as conllu_file: + predicted_docs.append( conllu.parse(conllu_file.read()) ) + # 2.x) Validate that all docs have equal number of sentences + number_of_sentences = 0 + for i in range(0, len(predicted_docs), 2): + if i+1 < len( predicted_docs ): + doc1_file = temp_prediction_files[i] + doc2_file = temp_prediction_files[i+1] + doc1 = predicted_docs[i] + doc2 = predicted_docs[i+1] + if len(doc1) != len(doc2): + raise ValueError( f'(!) Number of sentences differ in predicted output files: '+\ + f' {doc1_file}: {len(doc1)} vs {doc2_file}: {len(doc2)}.' ) + number_of_sentences = len(doc1) + # Random generator for choosing one dependency label if there are multiple labes with maximum scores + random_shuffler = Random() + random_shuffler.seed(random_pick_max_score_seed) + # 3) Iterate over all sentences and get aggregate predictions of each sentence + output_doc = [] + for sent_id in range(number_of_sentences): + all_sent_predictions = [] + for doc in predicted_docs: + all_sent_predictions.append(doc[sent_id]) + if aggregation_algorithm == 'las_coherence': + # Find pairwise las scores for all sentences + lases_table = defaultdict(dict) + for model_a, sent_a in enumerate( all_sent_predictions ): + for model_b, sent_b in enumerate( all_sent_predictions ): + lases_table[model_a][model_b] = sentence_LAS(sent_a, sent_b) + # Find average LAS for each model + sent_scores = dict() + getcontext().prec = 4 + for base_model, score in lases_table.items(): + decimals = list(map(Decimal, score.values())) + avg_score = sum(decimals) / Decimal(len(all_sent_predictions)) + sent_scores[base_model] = avg_score + # Pick sentence with the highest avg LAS (the highest coherence) + max_score = max(sent_scores.values()) + max_score_count = 0 + max_score_models = [] + for model, score in sent_scores.items(): + if score == max_score: + max_score_count += 1 + max_score_models.append(model) + random_shuffler.shuffle( max_score_models ) + output_doc.append ( all_sent_predictions[ max_score_models[0] ] ) + elif aggregation_algorithm == 'majority_voting': + sentence_length = len(all_sent_predictions[0]) + extracted_words = [] + # Get deprel with maximal votes for each token + for token_id in range(sentence_length): + voting_table = defaultdict(int) + label_token_map = {} + for sentence in all_sent_predictions: + token = sentence[token_id] + label = '{}__{}'.format(token['deprel'], token['head']) + voting_table[label] += 1 + if label not in label_token_map.keys(): + label_token_map[label] = [] + label_token_map[label].append(token) + # Find maximum voting score and corresponding tokens + max_votes = max( voting_table.values() ) + max_votes_labels = [l for l, v in voting_table.items() if v==max_votes] + max_votes_tokens = [] + for label, tokens in label_token_map.items(): + if label in max_votes_labels: + max_votes_tokens.extend(tokens) + # In case of a tie, pick a token randomly + random_shuffler.shuffle(max_votes_tokens) + extracted_words.append(max_votes_tokens[0]) + assert len(extracted_words) == sentence_length + # Construct new sentence + new_sentence = all_sent_predictions[0].copy() + for token_id in range(sentence_length): + new_sentence[token_id] = extracted_words[token_id].copy() + output_doc.append( new_sentence ) + assert len(output_doc) == number_of_sentences + # 4) Output picked sentences + final_output_fpath = os.path.join(output_dir, output_file) + with open(final_output_fpath, 'w', encoding='utf-8') as out_f: + for sentence in output_doc: + out_f.write( sentence.serialize() ) + # 5) Finally, remove conllu files with temporary predictions + for temp_file in temp_prediction_files: + os.remove(temp_file) + + +def sentence_LAS(sent1, sent2): + '''Calculates LAS between two conllu sentences.''' + wrong = 0 + correct = 0 + for tok1, tok2 in zip(sent1, sent2): + if tok1['xpos'] != 'Z': + if tok1['head'] == tok2['head'] and tok1['deprel'] == tok2['deprel']: + correct += 1 + else: + wrong += 1 + if wrong == 0 and correct == 0: + return 1 + else: + return correct / (correct + wrong) + +# ======================================================================== + +if __name__ == '__main__': + if len(sys.argv) < 2: + raise Exception('(!) Missing input argument: name of the configuration INI file.') + conf_file = sys.argv[1] + subexp = None + if len(sys.argv) > 2: + subexp = sys.argv[2] + run_models_main( conf_file, subexp=subexp ) diff --git a/05_evaluate.py b/05_evaluate.py new file mode 100644 index 00000000..97d793d0 --- /dev/null +++ b/05_evaluate.py @@ -0,0 +1,1359 @@ +# +# Looks through all experiment configurations, and +# performs evaluations described in configuration +# files: compares predicted files to gold standard +# files and finds LAS/UAS scores or different types +# of parsing errors (E1, E2, E3). +# +# Supported settings: +# * full_data +# * multi_experiment (general) +# * crossvalidation +# * half_data +# * smaller_data +# +# Requires EstNLTK version 1.7.2+. +# + +import sys +import csv, re +import os, os.path +from statistics import mean +import random +import configparser +import warnings + +import conllu + +import scipy.stats as scipy_stats +from numpy import array as n_array + +from estnltk.converters.conll.conll_importer import conll_to_text +from estnltk.converters.conll.conll_importer import add_layer_from_conll + + +def eval_main(conf_file, collected_results=None, ignore_missing=True, verbose=True, round=True, count_words=False): + ''' + Performs evaluations described in given conf_file. Executes sections starting with prefix + 'eval_'. If `ignore_missing` is set, then skips evaluation sections where input files (gold + standard or prediction files) are missing. + + Evaluation results (train and test LAS/UAS scores, and gaps between train and test LAS) + will be saved into dictionary collected_results. + Use parameter collected_results to overwrite the dictionary with your own (in order to + collect evaluation results over multiple configuration files). If parameter is None, a + new dictionary will be created. + + A special case: if the evaluation counts different types of parsing errors (the + configuration setting count_error_types=True), then the configuration must provide + output_csv_file and results will be saved right away into the given csv file; + in this case, nothing will be added to collected_results. + + Returns collected_results. + + :param conf_file: configuration (INI) file defining evaluations + :param collected_results: dictionary where to save evaluation results + :param ignore_missing: if True, then missing evaluation files will be ignored. + Otherwise, an exception will be raised in case of a missing file. + :param verbose: if True, then scores will be output to screen immediately after calculation. + :param round: if True, then rounds scores to 4 decimals; otherwise collects unrounded scores. + :param count_words: if True, then reports evaluation word counts (under keys 'train_words' + and 'test_words'). Note that evaluation excludes null nodes from scoring (optionally, + punctuation can also be excluded by setting exclude_punct=True in conf_file). + ''' + if collected_results is None: + collected_results = dict() + config = configparser.ConfigParser() + if len(config.read(conf_file)) != 1: + raise ValueError("File {} is not accessible or is not in valid INI format".format(conf_file)) + for section in config.sections(): + # look for 'eval_' sections + if section.startswith('eval_'): + conf_path, conf_fname = os.path.split(conf_file) + print(f'{conf_fname}: Checking {section} ...') + experiment_type = config[section].get('experiment_type', 'full_data') + experiment_type_clean = (experiment_type.strip()).lower() + if experiment_type_clean not in ['full_data', 'crossvalidation', 'half_data', 'smaller_data', 'multi_experiment']: + raise ValueError('(!) Unexpected experiment_type value: {!r}'.format(experiment_type)) + intermediate_results = [] + output_csv_file = None + test_matrix = False + if experiment_type_clean == 'full_data': + # -------------------------------------------------------------- + # 'full_data' + # -------------------------------------------------------------- + # ============================================================== + # Full-data experiment evaluation modes: + # + # Default mode: + # * eval on train file, compute LAS and UAS (if not skip_train) + # * eval on test file, compute LAS and UAS + # + # Error types mode: + # * eval on train file, count different types of errors (if not + # skip_train) + # * eval on test file, count different types of errors + # ============================================================== + # gold_test and predicted_test with paths + if not config.has_option(section, 'gold_test'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "gold_test" parameter.') + gold_test = config[section]['gold_test'] + gold_test_exists = os.path.exists(gold_test) + if not config.has_option(section, 'predicted_test'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "predicted_test" parameter.') + predicted_test = config[section]['predicted_test'] + predicted_test_exists = os.path.exists(predicted_test) + # skip_train: do not evaluate on train files + skip_train = config[section].getboolean('skip_train', False) + # gold_train and predicted_train with paths + if skip_train: + # ignore train files. empty values as placeholders + gold_train = ''; gold_train_exists=True + predicted_train = ''; predicted_train_exists=True + else: + # validate that train files have been provided + if not config.has_option(section, 'gold_train'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "gold_train" parameter.') + gold_train = config[section]['gold_train'] + gold_train_exists = os.path.exists(gold_train) + if not config.has_option(section, 'predicted_train'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "predicted_train" parameter.') + predicted_train = config[section]['predicted_train'] + predicted_train_exists = os.path.exists(predicted_train) + # other parameters + error_types_mode = config[section].getboolean('count_error_types', False) + param_count_words = config[section].getboolean('count_words', count_words) + experiment_name = config[section].get('name', section) + exclude_punct = config[section].getboolean('exclude_punct', False) + punct_tokens_file = config[section].get('punct_tokens_file', None) + punct_tokens_set = load_punct_tokens( punct_tokens_file ) # Attempt to load from file. If None, return empty set + output_csv_file = config[section].get('output_csv_file', None) # Output results to csv file right after evaluation + error_sample_size = config[section].getint('error_sample_size', 100) + add_conf_intervals = config[section].getboolean('add_conf_intervals', False) + calc_scores_with_entropy = config[section].getboolean('calc_scores_with_entropy', False) + output_train_error_sample_file = config[section].get('output_train_error_sample_file', None) + output_test_error_sample_file = config[section].get('output_test_error_sample_file', None) + if not error_types_mode: + if output_train_error_sample_file is not None: + raise ValueError(f'Error in {conf_file}, section {section!r}: error sampling (output_train_error_sample_file) '+\ + 'only works with option count_error_types=True.') + if output_test_error_sample_file is not None: + raise ValueError(f'Error in {conf_file}, section {section!r}: error sampling (output_test_error_sample_file) '+\ + 'only works with option count_error_types=True.') + if error_types_mode: + if add_conf_intervals: + raise ValueError(f'Error in {conf_file}, section {section!r}: add_conf_intervals only works with the option '+\ + 'count_error_types=False.') + if calc_scores_with_entropy: + raise ValueError(f'Error in {conf_file}, section {section!r}: calc_scores_with_entropy only works with '+\ + 'the option count_error_types=False.') + if calc_scores_with_entropy and add_conf_intervals: + raise ValueError(f'Error in {conf_file}, section {section!r}: calc_scores_with_entropy and add_conf_intervals '+\ + 'cannot be used simultaneously. Set only one of the two parameters.') + if output_csv_file is None and error_types_mode: + raise ValueError(f'Error in {conf_file} section {section!r}: '+\ + f'count_error_types requires setting "output_csv_file" parameter.') + # Sanity check to avoid accidental confusion with 'multi_experiment' + if config.has_option(section, 'test_matrix') and config[section].getboolean('test_matrix', False): + raise ValueError(f'Error in {conf_file}, section {section!r}: '+\ + f'test_matrix option works only with experiment_type = multi_experiment.') + all_files_exist = gold_train_exists and gold_test_exists and \ + predicted_train_exists and predicted_test_exists + if all_files_exist: + format_string = ':.4f' if round else None + if not error_types_mode: + # + # Default eval mode (calculate LAS and UAS) + # + results = score_experiment( predicted_test, gold_test, predicted_train, gold_train, + gold_path=None, predicted_path=None, format_string=format_string, + add_conf_intervals=add_conf_intervals, + calc_scores_with_entropy=calc_scores_with_entropy, + count_words=param_count_words, skip_train=skip_train, + exclude_punct=exclude_punct, punct_tokens_set=punct_tokens_set ) + if verbose: + print(results) + # find experiment directory closest to root in experiment path + exp_root = get_experiment_path_root(gold_test) + if exp_root not in collected_results.keys(): + collected_results[exp_root] = dict() + collected_results[exp_root][experiment_name] = results + intermediate_results.append( (experiment_name, None, None, results) ) + else: + # + # Error types eval mode (count E1, E2, E3) + # + exp_name_raw = experiment_name if experiment_name.endswith('_') else experiment_name+'_' + if not skip_train: + train_errors = calculate_errors(gold_train, predicted_train, punct_tokens_set=punct_tokens_set, + remove_empty_nodes=True, add_counts=param_count_words, + format_string=format_string, error_sample_size=error_sample_size, + error_sample_output_file=output_train_error_sample_file) + if verbose: + print(f'{exp_name_raw}on_train |', train_errors) + intermediate_results.append( (f'{exp_name_raw}on_train', None, None, train_errors) ) + test_errors = calculate_errors(gold_test, predicted_test, punct_tokens_set=punct_tokens_set, + remove_empty_nodes=True, add_counts=param_count_words, + format_string=format_string, error_sample_size=error_sample_size, + error_sample_output_file=output_test_error_sample_file) + if verbose: + print(f'{exp_name_raw}on_test |', test_errors) + intermediate_results.append( (f'{exp_name_raw}on_test', None, None, test_errors) ) + else: + missing_files = [f for f in [predicted_test, gold_test, predicted_train, gold_train] if not os.path.exists(f)] + if ignore_missing: + print(f'Skipping evaluation because of missing files: {missing_files!r}') + else: + raise FileNotFoundError(f'(!) Cannot evaluate, missing evaluation files: {missing_files!r}') + elif experiment_type_clean in ['crossvalidation', 'half_data', 'smaller_data', 'multi_experiment']: + # -------------------------------------------------------------- + # 'multi_experiment' (general) + # 'crossvalidation' + # 'half_data' + # 'smaller_data' + # -------------------------------------------------------------- + # ============================================================== + # Multi-experiment evaluation modes (LAS, UAS): + # + # Default mode: + # * eval each model on its train file (if not skip_train) + # * eval each model on its test file or on the global test file + # + # Test matrix mode: + # * eval each model on its train file (if not skip_train) + # * eval each model on all test files + # ============================================================== + # gold_test and gold_splits_dir + if not config.has_option(section, 'gold_test'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "gold_test" parameter.') + gold_test = config[section]['gold_test'] + test_file_is_pattern = config[section].getboolean('test_file_is_pattern', False) + if not config.has_option(section, 'gold_splits_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "gold_splits_dir" parameter.') + gold_splits_dir = config[section]['gold_splits_dir'] + if not config.has_option(section, 'predictions_dir'): + raise ValueError(f'Error in {conf_file}: section {section!r} is missing "predictions_dir" parameter.') + # Sanity check to avoid accidental confusion with 'full_data' experiments + error_types_mode = config[section].getboolean('count_error_types', False) + if error_types_mode: + raise NotImplementedError(f'Error in {conf_file}, section {section!r}: '+\ + f'count_error_types is not implemented for {experiment_type} evaluation.') + predictions_dir = config[section]['predictions_dir'] + predictions_prefix = config[section].get('predictions_prefix', 'predicted_') + macro_average = config[section].getboolean('macro_average', False) + param_count_words = config[section].getboolean('count_words', count_words) + experiment_name_prefix = config[section].get('name_prefix', section) + if not experiment_name_prefix.endswith('_'): + experiment_name_prefix = experiment_name_prefix + '_' + exclude_punct = config[section].getboolean('exclude_punct', False) + punct_tokens_file = config[section].get('punct_tokens_file', None) + punct_tokens_set = load_punct_tokens( punct_tokens_file ) # Attempt to load from file. If None, return empty set + output_csv_file = config[section].get('output_csv_file', None) # Output results to csv file right after evaluation + # Patterns for capturing names of training sub-experiment files + train_file_pat = r'(?P\d+)_train_all.conllu' + # Override sub-experiment patterns (if required) + if config.has_option(section, 'train_file_pat'): + train_file_pat = config[section]['train_file_pat'] + # Convert train_file_pattern to regular experssion + train_file_regex = None + if not isinstance(train_file_pat, str): + raise TypeError('train_file_pat must be a string') + try: + train_file_regex = re.compile(train_file_pat) + except Exception as err: + raise ValueError(f'Unable to convert {train_file_pat!r} to regexp') from err + if 'exp' not in train_file_regex.groupindex: + raise ValueError(f'Regexp {train_file_pat!r} is missing named group "exp"') + # skip_train: do not evaluate on train files + skip_train = config[section].getboolean('skip_train', False) + test_file_regex = None + if test_file_is_pattern: + if not isinstance(gold_test, str): + raise TypeError('gold_test must be a string') + try: + test_file_regex = re.compile(gold_test) + except Exception as err: + raise ValueError(f'Unable to convert {gold_test!r} to regexp') from err + if 'exp' not in test_file_regex.groupindex: + raise ValueError(f'Regexp {gold_test!r} is missing named group "exp"') + # test_matrix evaluation mode: evaluate all models on all test files + test_matrix = config[section].getboolean('test_matrix', False) + if macro_average and test_matrix: + raise Exception('macro_average not implemented for test_matrix evaluation mode') + # Validate main paths + # Find out missing paths + paths_to_check = [predictions_dir, gold_splits_dir] + if not test_file_is_pattern: + paths_to_check.append(gold_test) + missing_paths = [] + for input_path in paths_to_check: + if not os.path.exists(input_path): + missing_paths.append(input_path) + if missing_paths: + # ================================================== + # Report missing main paths + # ================================================== + if ignore_missing: + print(f'Skipping evaluation because of missing dirs/files: {missing_paths!r}') + else: + raise FileNotFoundError(f'(!) Cannot evaluate, missing evaluation dirs/files: {missing_paths!r}') + else: + # ================================================== + # Main paths are OK. Proceed with gathering files + # ================================================== + # Try to collect evaluation files + # Gold train files + all_gold_train_files = {} + for gold_file in sorted( os.listdir(gold_splits_dir) ): + if (gold_file.lower()).endswith('.conllu'): + m = train_file_regex.match(gold_file) + if m: + no = m.group('exp') + all_gold_train_files[no] = \ + os.path.join(gold_splits_dir, gold_file) + # Gold test files + all_gold_test_files = {} + if test_file_regex is not None: + # Multiple test files + for gold_file in sorted(os.listdir(gold_splits_dir)): + if (gold_file.lower()).endswith('.conllu'): + m2 = test_file_regex.match(gold_file) + if m2: + no2 = m2.group('exp') + all_gold_test_files[no2] = \ + os.path.join(gold_splits_dir, gold_file) + else: + # Single test file for all models + for cur_subexp in sorted( all_gold_train_files.keys() ): + all_gold_test_files[cur_subexp] = gold_test + # Exception if no gold train/test files are available + if len(all_gold_train_files.keys()) == 0: + raise Exception('(!) Unable to construct sub experiment names, '+\ + f'because no train files were found from {gold_splits_dir}. '+\ + f'Please check that {train_file_pat!r} is a proper regexp for '+\ + 'recognizing train files from the directory.') + # Some sanity checks + if len(all_gold_train_files.keys()) > 0 or len(all_gold_test_files.keys()) > 0: + if not skip_train: + # Train and test set names must match + if not (all_gold_train_files.keys() == all_gold_test_files.keys()): + raise ValueError('(!) Mismatching train and test sub-experiment '+\ + 'names in gold_splits_dir. Missing any files? '+\ + f'Train experiments: {list(all_gold_train_files.keys())!r}; '+\ + f'Test experiments: {list(all_gold_test_files.keys())!r} ') + else: + if not len(all_gold_test_files.keys()) > 0: + raise ValueError(f'(!) No test files found from {gold_splits_dir}') + elif len(all_gold_train_files.keys()) == 0 and len(all_gold_test_files.keys()) == 0: + raise ValueError(f'(!) No train or test files found from {gold_splits_dir}') + # ================================================== + # Iterate over sub experiments and evaluate + # ================================================== + evaluations_done = 0 + results_macro_avg = dict() + for cur_subexp in sorted( all_gold_test_files.keys() ): + current_gold_test = all_gold_test_files[cur_subexp] + gold_train = all_gold_train_files.get(cur_subexp, None) + assert gold_train is not None or test_matrix + missing_prediction_files = [] + # Find corresponding train prediction + target_predicted_train = f'{predictions_prefix}train_{cur_subexp}.conllu' + predicted_train = os.path.join(predictions_dir, target_predicted_train) + # Find corresponding test predictions + if test_matrix: + # Multiple test predictions (1 from each model) + predicted_test_files = [] + predicted_test_models = [] + for model_subexp in sorted(all_gold_test_files.keys()): + test_output_fpath = os.path.join(predictions_dir, \ + f'{predictions_prefix}model_{model_subexp}_test_{cur_subexp}.conllu') + predicted_test_files.append( test_output_fpath ) + predicted_test_models.append( model_subexp ) + else: + # Single test prediction + target_predicted_test = f'{predictions_prefix}test_{cur_subexp}.conllu' + predicted_test_files = [os.path.join(predictions_dir, target_predicted_test)] + predicted_test_models = [None] + # Check for existence of files + paths_to_check2 = [] + if not skip_train: + paths_to_check2.append( predicted_train ) + paths_to_check2.extend( predicted_test_files ) + for predicted_path in paths_to_check2: + if not os.path.exists(predicted_path) or \ + not os.path.isfile(predicted_path): + missing_prediction_files.append(predicted_path) + if len(missing_prediction_files) == 0: + # ================================================== + # All required files exist: evaluate + # ================================================== + for predicted_test, model_subexp in zip(predicted_test_files, predicted_test_models): + results = score_experiment( predicted_test, current_gold_test, + predicted_train, gold_train, + format_string=None, + count_words=param_count_words, + skip_train=skip_train, + exclude_punct=exclude_punct, + punct_tokens_set=punct_tokens_set ) + if macro_average: + # Collect macro averages + for k, v in results.items(): + if k not in results_macro_avg.keys(): + results_macro_avg[k] = [] + results_macro_avg[k].append(v) + if round: + # Find rounded results + format_string = ':.4f' + results_rounded = dict() + for k, v in results.items(): + if k not in ['train_words', 'test_words']: + results_rounded[k] = ('{'+format_string+'}').format(v) + else: + results_rounded[k] = '{}'.format(v) + results = results_rounded + # experiment name (depends on whether we use test matrix or not) + if not test_matrix: + experiment_name = f'{experiment_name_prefix}{cur_subexp}' + else: + experiment_name = f'{experiment_name_prefix}model_{model_subexp}_test_{cur_subexp}' + # find experiment directory closest to root in experiment path + exp_root = get_experiment_path_root(current_gold_test) + if exp_root not in collected_results.keys(): + collected_results[exp_root] = dict() + if verbose: + print(exp_root, experiment_name, results) + collected_results[exp_root][experiment_name] = results + intermediate_results.append( (experiment_name, cur_subexp, model_subexp, results) ) + evaluations_done += 1 + else: + # Missing files + if ignore_missing: + print(f'Skipping evaluation because of missing files: {missing_prediction_files!r}') + else: + raise FileNotFoundError(f'(!) Cannot evaluate, missing evaluation files: {missing_prediction_files!r}') + if macro_average and evaluations_done > 1: + # Find macro averages + calculated_averages = dict() + for k, v in results_macro_avg.items(): + calculated_averages[k] = mean(v) + if round: + if k not in ['train_words', 'test_words']: + calculated_averages[k] = \ + ('{'+format_string+'}').format( calculated_averages[k] ) + else: + calculated_averages[k] = \ + ('{}').format( int(calculated_averages[k]) ) + assert exp_root in collected_results.keys() + experiment_name = f'{experiment_name_prefix}{"AVG"}' + if verbose: + print(exp_root, experiment_name, calculated_averages) + collected_results[exp_root][experiment_name] = calculated_averages + intermediate_results.append( (experiment_name, None, None, calculated_averages) ) + # ================================================== + # Output intermediate results (optional) + # ================================================== + if output_csv_file is not None and intermediate_results: + print(f'Writing evaluation results into {output_csv_file} ...') + with open(output_csv_file, 'w', encoding='utf-8', newline='') as output_csv: + csv_writer = csv.writer(output_csv) + if test_matrix: + # Write matrix of results + # rows: test sets, columns: models + subexp_names = [] + for r in intermediate_results: + if r[1] not in subexp_names: + subexp_names.append(r[1]) + header = [''] + subexp_names + csv_writer.writerow( header ) + values = [] + lines_written = 0 + for (full_exp_name, exp1, exp2, results) in intermediate_results: + if (exp1 is not None) and (exp2 is not None): + if not values: + values.append(exp1) + values.append(results['LAS_test']) + assert exp1 == values[0] + assert exp2 == subexp_names[len(values)-2] + if len(values) == len(subexp_names) + 1: + csv_writer.writerow( values ) + lines_written += 1 + values = [] + assert lines_written == len(subexp_names) + else: + # Write listing of results + result_fields = list(intermediate_results[0][3].keys()) + header = ['experiment'] + result_fields + csv_writer.writerow( header ) + for (full_exp_name, exp1, exp2, results) in intermediate_results: + values = [full_exp_name] + for key in header[1:]: + values.append( results[key] ) + assert len(values) == len(header) + csv_writer.writerow( values ) + return collected_results + + +def get_experiment_path_root( experiment_path ): + '''Finds directory closest to the root from the given experiment path.''' + closest_to_root = None + while len(experiment_path) > 0: + head, tail = os.path.split( experiment_path ) + if len(head) == 0: + closest_to_root = tail + experiment_path = head + return closest_to_root + +def load_punct_tokens( fname ): + ''' + Loads set of punctuation tokens from given file. + If file name is None, returns an empty set. + If file is given but missing, raises an exception. + Returns set of punctuation tokens. + ''' + punct_tokens = set() + if fname is not None: + if not os.path.exists(fname): + raise Exception(f'(!) Non-existend punct tokens file: {fname!r}') + with open(fname, 'r', encoding='utf-8') as in_f: + for line in in_f: + line = line.strip() + if len(line) > 0: + punct_tokens.add(line) + return punct_tokens + +def score_experiment( predicted_test, gold_test, predicted_train, gold_train, + gold_path=None, predicted_path=None, format_string=None, + add_conf_intervals=False, calc_scores_with_entropy=False, + skip_train=False, count_words=False, + exclude_punct=False, punct_tokens_set=None ): + ''' + Calculates train and test LAS/UAS scores and gaps between train and test LAS + using given predicted and gold standard conllu files. + If `format_string` provided (not None), then uses it to reformat all calculated + scores. For instance, if `format_string=':.4f'`, then all scores will be rounded + to 4 decimals. + Returns dictionary with calculated scores (keys: "LAS_test", "LAS_train", + "LAS_gap", "UAS_test", "UAS_train"). + If `count_words=True`, then adds evaluation word counts (keys 'train_words' + and 'test_words') to the results. + If `skip_train` is True, then calculates only test scores and returns dictionary + with calculated test scores (keys: "LAS_test", "UAS_test"). + If `add_conf_intervals` is True, then scores will be computed as mean values with + 95% confidence intervals. Instead of a single value, each score will then be in + form "lower_bound; mean; upper_bound". + If `calc_scores_with_entropy` is True, then adds additional computations to + default scores: computes LAS exclusively over words that have ensemble prediction + entropy 0, and finds a correlation between ensemble prediction entropy and reversed + gold standard match (0-match, 1-mismatch). Note: this only works if ensemble + predictions have generated prediction .entropy files in addition to prediction + .conllu files. + ''' + # Check/validate input files and parameters + input_files = { \ + 'predicted_test': predicted_test, + 'gold_test': gold_test, + 'predicted_train': predicted_train, + 'gold_train': gold_train } + for name, fpath in input_files.items(): + if skip_train and name.endswith('_train'): + # skip evaluation on train files + continue + full_path = fpath + if fpath is None: + raise FileNotFoundError(f'(!) Unexpected None value for {name} file name.') + # Update full path (if required) + if name.startswith('gold_') and gold_path is not None: + full_path = os.path.join(gold_path, fpath) + if name.startswith('predicted') and predicted_path is not None: + full_path = os.path.join(predicted_path, fpath) + if not os.path.isfile(full_path): + raise FileNotFoundError(f'(!) {name} file cannot be found at {full_path!r}.') + input_files[name] = full_path + if calc_scores_with_entropy and add_conf_intervals: + raise ValueError('(!) Cannot use calc_scores_with_entropy and add_conf_intervals simultaneously. '+\ + 'Pick only one of the input parameters.') + # Calculate scores + if add_conf_intervals: + test_scores = calculate_scores_with_conf_interval(input_files['gold_test'], + input_files['predicted_test'], + N=10, + seed=1, + count_words=count_words, + exclude_punct=exclude_punct, + punct_tokens_set=punct_tokens_set) + elif calc_scores_with_entropy: + test_scores = calculate_scores_with_entropy(input_files['gold_test'], + input_files['predicted_test'], + exclude_punct=exclude_punct, + punct_tokens_set=punct_tokens_set) + else: + test_scores = calculate_scores(input_files['gold_test'], + input_files['predicted_test'], + count_words=count_words, + exclude_punct=exclude_punct, + punct_tokens_set=punct_tokens_set) + + if skip_train: + train_scores = None + results_dict = {'LAS_test' : test_scores['LAS'], + 'UAS_test' : test_scores['UAS']} + else: + if add_conf_intervals: + train_scores = calculate_scores_with_conf_interval(input_files['gold_train'], + input_files['predicted_train'], + N=10, + seed=1, + count_words=count_words, + exclude_punct=exclude_punct, + punct_tokens_set=punct_tokens_set) + elif calc_scores_with_entropy: + train_scores = calculate_scores_with_entropy(input_files['gold_train'], + input_files['predicted_train'], + exclude_punct=exclude_punct, + punct_tokens_set=punct_tokens_set) + else: + train_scores = calculate_scores(input_files['gold_train'], + input_files['predicted_train'], + count_words=count_words, + exclude_punct=exclude_punct, + punct_tokens_set=punct_tokens_set) + results_dict = {'LAS_test' : test_scores['LAS'], + 'LAS_train' : train_scores['LAS'], + 'UAS_test' : test_scores['UAS'], + 'UAS_train' : train_scores['UAS']} + if add_conf_intervals: + results_dict['LAS_gap'] = (train_scores['LAS'][0] - test_scores['LAS'][0], + train_scores['LAS'][1] - test_scores['LAS'][1], + train_scores['LAS'][2] - test_scores['LAS'][2]) + else: + results_dict['LAS_gap'] = train_scores['LAS'] - test_scores['LAS'] + # Add scores with entropy + if calc_scores_with_entropy: + # LAS with zero entropy + results_dict['test_LAS_0_entropy'] = \ + test_scores['LAS_with_0_entropy'] + results_dict['test_LAS_0_entropy_matches'] = \ + test_scores['LAS_with_0_entropy_matches'] + results_dict['test_LAS_0_entropy_total_words'] = \ + test_scores['LAS_with_0_entropy_total_words'] + # Size of LAS with zero entropy with respect to full test + if 'total_words' in test_scores: + results_dict['test_LAS_0_entropy_total_words_%'] = \ + (results_dict['test_LAS_0_entropy_total_words']/test_scores['total_words'])*100.0 + # Correlation between LAS match and entropy + results_dict['test_LAS_vs_entropy_corr'] = \ + test_scores['LAS_vs_entropy_correlation'] + results_dict['test_LAS_vs_entropy_corr_pvalue'] = \ + test_scores['LAS_vs_entropy_corr_pvalue'] + if train_scores is not None: + # LAS with zero entropy + results_dict['train_LAS_0_entropy'] = \ + train_scores['LAS_with_0_entropy'] + results_dict['train_LAS_0_entropy_matches'] = \ + train_scores['LAS_with_0_entropy_matches'] + results_dict['train_LAS_0_entropy_total_words'] = \ + train_scores['LAS_with_0_entropy_total_words'] + # Correlation between LAS match and entropy + results_dict['train_LAS_vs_entropy_corr'] = \ + train_scores['LAS_vs_entropy_correlation'] + results_dict['train_LAS_vs_entropy_corr_pvalue'] = \ + train_scores['LAS_vs_entropy_corr_pvalue'] + # Size of LAS with zero entropy with respect to full train + if 'total_words' in train_scores: + results_dict['train_LAS_0_entropy_total_words_%'] = \ + (results_dict['train_LAS_0_entropy_total_words']/train_scores['total_words'])*100.0 + # Reformat results + if format_string is not None: + for k, v in results_dict.items(): + if isinstance(v, int): + continue + elif isinstance(v, float): + results_dict[k] = ('{'+format_string+'}').format(v) + elif isinstance(v, tuple): + parts = [('{'+format_string+'}').format(vi) for vi in v] + results_dict[k] = '; '.join(parts) + else: + raise TypeError('(!) Value {!r} has unexpected type: {!r}'.format(v, type(v))) + if count_words: + if 'total_words' in test_scores: + results_dict['test_words'] = test_scores['total_words'] + if not skip_train and 'total_words' in train_scores: + results_dict['train_words'] = train_scores['total_words'] + return results_dict + + +def calculate_scores(gold_path: str, predicted_path: str, count_words=False, exclude_punct=False, punct_tokens_set=None): + ''' + Calculates LAS, UAS and LA scores based on gold annotations and predicted annotations + loaded from conllu files `gold_path` and `predicted_path`. + Assumes that there are no tokenization differences in the gold and predicted outputs. + If exclude_punct==True, then discards punctuation (tokens with xpos == 'Z', or alternatively, + tokens appearing in given punct_tokens_set) from calculations. + Always discards null nodes (tokens with non-integer id-s) from calculations. + Returns dictionary with scores (keys: "LAS", "UAS", "LA"). + If `count_words=True`, then adds evaluation word count (key 'total_words') to the results. + + Note: if exclude_punct==False, then the LAS calculated here is compatible with the LAS.f1 + calculated in CoNLL 2018 Shared Task. However, there's an exception: in the CoNLL 2018 + evaluation, language-specific deprel subtypes are truncated (e.g. 'acl:relcl' is reduced + to 'acl' in both predicted and gold), but we compare deprels as they are. As a result, + CoNLL 2018 evaluation can give higher scores than this method. + ''' + # Load annotated texts from conllu files + gold_sents = None + predicted_sents = None + with open(gold_path, 'r', encoding='utf-8') as in_f: + gold_sents = conllu.parse(in_f.read()) + with open(predicted_path, 'r', encoding='utf-8') as in_f_2: + predicted_sents = conllu.parse(in_f_2.read()) + assert len(gold_sents) == len(predicted_sents), \ + f'(!) Mismatching sizes: gold_sents: {len(gold_sents)}, predicted_sents: {len(predicted_sents)}' + + las_match_count = 0 + uas_match_count = 0 + la_match_count = 0 + total_words = 0 + + for i, gold_sentence in enumerate(gold_sents): + predicted_sentece = predicted_sents[i] + gold_sentence_words = [w['form'] for w in gold_sentence] + auto_sentence_words = [w['form'] for w in predicted_sentece] + assert gold_sentence_words == auto_sentence_words, \ + f'Tokenization mismatch in {predicted_path!r} | {gold_sentence_words} vs {auto_sentence_words}' + word_tracker = 0 + for gold_word in gold_sentence: + if not isinstance(gold_word['id'], int): + continue + if exclude_punct: + # Exclude punctuation from evaluation + if punct_tokens_set is not None: + if gold_word['form'] in punct_tokens_set: + word_tracker += 1 + continue + if gold_word['xpos'] == 'Z': + word_tracker += 1 + continue + + total_words += 1 + + predicted_word = predicted_sentece[word_tracker] + + if predicted_word['deprel'] == gold_word['deprel'] and predicted_word['head'] == gold_word['head']: + las_match_count += 1 + la_match_count += 1 + uas_match_count += 1 + elif predicted_word['deprel'] == gold_word['deprel']: + la_match_count += 1 + elif predicted_word['head'] == gold_word['head']: + uas_match_count += 1 + + word_tracker += 1 + result = \ + {'LAS': las_match_count / total_words, + 'UAS': uas_match_count / total_words, + 'LA': la_match_count / total_words} + if count_words: + result['total_words'] = total_words + return result + + +def calculate_scores_with_conf_interval(gold_path: str, predicted_path: str, N:int=10, + seed:int=1, confidence: float=0.95, count_words=False, + exclude_punct=False, punct_tokens_set=None): + ''' + A variant of `calculate_scores` that calculates LAS, UAS and LA scores with confidence intervals. + For that, the test sentences are shuffled and split into N sub sets, scores are measured on each + sub set, and, finally, the confidence intervals for means of scores are computed assuming a Normal + distribution. + + Returns dictionary with scores (keys: "LAS", "UAS", "LA"), in which each value is a triple: + (lower_bound, mean, upper_bound). + ''' + # Load annotated texts from conllu files + gold_sents = None + predicted_sents = None + with open(gold_path, 'r', encoding='utf-8') as in_f: + gold_sents = conllu.parse(in_f.read()) + with open(predicted_path, 'r', encoding='utf-8') as in_f_2: + predicted_sents = conllu.parse(in_f_2.read()) + assert len(gold_sents) == len(predicted_sents), \ + f'(!) Mismatching sizes: gold_sents: {len(gold_sents)}, predicted_sents: {len(predicted_sents)}' + # Pair all predicted and gold sentences + all_sentence_pairs = [] + for i, gold_sentence in enumerate(gold_sents): + predicted_sentece = predicted_sents[i] + all_sentence_pairs.append( (i, gold_sentence, predicted_sentece) ) + rnd = random.Random(seed) + # Randomize pairs and divide into N sub groups + rnd_groups = [] + rnd.shuffle( all_sentence_pairs ) + for i in range(N): + rnd_groups.append([]) + for pid, (j, gold_s, pred_s) in enumerate(all_sentence_pairs): + rnd_groups[pid % N].append( (j, gold_s, pred_s) ) + assert len(all_sentence_pairs) == sum([len(g) for g in rnd_groups]) + # Calculate LAS scores for sub groups + LA_scores = [] + UAS_scores = [] + LAS_scores = [] + abs_total_words = 0 + for gid, group in enumerate(rnd_groups): + las_match_count = 0 + uas_match_count = 0 + la_match_count = 0 + total_words = 0 # total words in the group + for i, (j, gold_sentence, predicted_sentece) in enumerate(group): + gold_sentence_words = [w['form'] for w in gold_sentence] + auto_sentence_words = [w['form'] for w in predicted_sentece] + assert gold_sentence_words == auto_sentence_words, \ + f'Tokenization mismatch in {predicted_path!r} | {gold_sentence_words} vs {auto_sentence_words}' + word_tracker = 0 + for gold_word in gold_sentence: + if not isinstance(gold_word['id'], int): + continue + if exclude_punct: + # Exclude punctuation from evaluation + if punct_tokens_set is not None: + if gold_word['form'] in punct_tokens_set: + word_tracker += 1 + continue + if gold_word['xpos'] == 'Z': + word_tracker += 1 + continue + total_words += 1 + predicted_word = predicted_sentece[word_tracker] + if predicted_word['deprel'] == gold_word['deprel'] and predicted_word['head'] == gold_word['head']: + las_match_count += 1 + la_match_count += 1 + uas_match_count += 1 + elif predicted_word['deprel'] == gold_word['deprel']: + la_match_count += 1 + elif predicted_word['head'] == gold_word['head']: + uas_match_count += 1 + word_tracker += 1 + # Calculate group scores + group_LAS = las_match_count / total_words + group_UAS = uas_match_count / total_words + group_LA = la_match_count / total_words + LA_scores.append( group_LA ) + UAS_scores.append( group_UAS ) + LAS_scores.append( group_LAS ) + abs_total_words += total_words + assert len(LA_scores) == N + assert len(LAS_scores) == N + assert len(UAS_scores) == N + # Calculate confidence intervals + LA_mean = mean(LA_scores) + LAS_mean = mean(LAS_scores) + UAS_mean = mean(UAS_scores) + LAS_lower, LAS_upper = scipy_stats.norm.interval(confidence, loc=LAS_mean, + scale=scipy_stats.sem(n_array(LAS_scores))) + LA_lower, LA_upper = scipy_stats.norm.interval(confidence, loc=LA_mean, + scale=scipy_stats.sem(n_array(LA_scores))) + UAS_lower, UAS_upper = scipy_stats.norm.interval(confidence, loc=UAS_mean, + scale=scipy_stats.sem(n_array(UAS_scores))) + result = \ + {'LAS': (LAS_lower, LAS_mean, LAS_upper), + 'UAS': (UAS_lower, UAS_mean, UAS_upper), + 'LA': (LA_lower, LA_mean, LA_upper)} + if count_words: + result['total_words'] = abs_total_words + return result + + +def entropy_file_exists(predicted_path: str): + ''' + Checks whether an .entropy file corresponding to the given .conllu file exists. + ''' + predicted_path_entropy = None + if predicted_path.endswith('.conllu'): + predicted_path_entropy = predicted_path.replace('.conllu', '.entropy') + return os.path.isfile(predicted_path_entropy) + return False + + +def calculate_scores_with_entropy(gold_path: str, predicted_path: str, exclude_punct=False, punct_tokens_set=None): + ''' + Calculates LAS, UAS and LA scores based on gold annotations and predicted annotations, + and extends the results with entropy scores. + For entropy scores, computes LAS exclusively over words that have ensemble prediction + entropy 0, and finds Pearson's correlation between the ensemble prediction entropy and a + reversed gold standard matching (encoded as: 0-match, 1-mismatch). Note: this function + only works if ensemble predictions have generated prediction .entropy files in addition + to prediction .conllu files. If .entropy files are missing, the function fails with an + exception. + Flags `exclude_punct` and `punct_tokens_set` behave the same way as in the function + `calculate_scores`. + ''' + from scipy.stats import pearsonr + # Load annotated texts from conllu files + gold_sents = None + predicted_sents = None + with open(gold_path, 'r', encoding='utf-8') as in_f: + gold_sents = conllu.parse(in_f.read()) + with open(predicted_path, 'r', encoding='utf-8') as in_f_2: + predicted_sents = conllu.parse(in_f_2.read()) + assert len(gold_sents) == len(predicted_sents), \ + f'(!) Mismatching sizes: gold_sents: {len(gold_sents)}, predicted_sents: {len(predicted_sents)}' + # Get entropy file path + predicted_path_entropy = None + if entropy_file_exists(predicted_path): + predicted_path_entropy = predicted_path.replace('.conllu', '.entropy') + else: + if not predicted_path.endswith('.conllu'): + warnings.warn( f'(!) Unexpected file ending in {predicted_path} (expected .conllu). ') + raise FileNotFoundError(\ + f"(!) Unable to find .entropy file corresponding to {predicted_path!r}") + # Load entropy annotations + sentence_id = 0 + words_entropy = [] + with open(predicted_path_entropy, 'r', encoding='utf-8') as in_f_3: + for line in in_f_3: + line_clean = line.strip() + if len(line_clean) > 0: + votes, entropy = line_clean.split('\t') + entropy = float(entropy) + words_entropy.append( \ + { 'sentence_id': sentence_id, + 'entropy': entropy} ) + else: + # Next sentence + sentence_id += 1 + entropy_all = [] + LAS_accuracy = [] + LAS_accuracy_with_zero_entropy = [] + UAS_accuracy = [] + LA_accuracy = [] + global_word_tracker = 0 + for i, gold_sentence in enumerate(gold_sents): + predicted_sentece = predicted_sents[i] + gold_sentence_words = [w['form'] for w in gold_sentence] + auto_sentence_words = [w['form'] for w in predicted_sentece] + assert gold_sentence_words == auto_sentence_words, \ + f'Tokenization mismatch in {predicted_path!r} | {gold_sentence_words} vs {auto_sentence_words}' + sentence_word_tracker = 0 + for gold_word in gold_sentence: + if not isinstance(gold_word['id'], int): + continue + if exclude_punct: + # Exclude punctuation from evaluation + if punct_tokens_set is not None: + if gold_word['form'] in punct_tokens_set: + global_word_tracker += 1 + sentence_word_tracker += 1 + continue + if gold_word['xpos'] == 'Z': + global_word_tracker += 1 + sentence_word_tracker += 1 + continue + cur_word_entropy = words_entropy[global_word_tracker]['entropy'] + predicted_word = predicted_sentece[sentence_word_tracker] + has_las_match = \ + predicted_word['deprel'] == gold_word['deprel'] and predicted_word['head'] == gold_word['head'] + has_la_match = \ + predicted_word['deprel'] == gold_word['deprel'] + has_uas_match = \ + predicted_word['head'] == gold_word['head'] + global_word_tracker += 1 + sentence_word_tracker += 1 + entropy_all.append(cur_word_entropy) + # Use encoding: 0-match, 1-mismatch in order + # to better align matching with the entropy + # ( H(X)=0 certainty, H(X)>0 uncertainty ) + LAS_accuracy.append(0 if has_las_match else 1) + LA_accuracy.append(0 if has_la_match else 1) + UAS_accuracy.append(0 if has_uas_match else 1) + if cur_word_entropy == 0.0: + # Use encoding: 0-mismatch, 1-match + LAS_accuracy_with_zero_entropy.append(1 if has_las_match else 0) + # Find correlation + corr, pvalue = pearsonr(entropy_all, LAS_accuracy) + result = \ + {'LAS': LAS_accuracy.count(0) / len(LAS_accuracy), + 'UAS': UAS_accuracy.count(0) / len(UAS_accuracy), + 'LA': LA_accuracy.count(0) / len(LA_accuracy), + # LAS with zero entropy + 'LAS_with_0_entropy_matches' : LAS_accuracy_with_zero_entropy.count(1), + 'LAS_with_0_entropy_total_words' : len(LAS_accuracy_with_zero_entropy), + 'LAS_with_0_entropy': \ + LAS_accuracy_with_zero_entropy.count(1) / len(LAS_accuracy_with_zero_entropy), + # Correlation between LAS match and entropy + 'LAS_vs_entropy_correlation': corr, + 'LAS_vs_entropy_corr_pvalue': pvalue, + # All words used in calculations + 'total_words': len(LAS_accuracy) + } + return result + + +def calculate_errors(gold_file, predicted_file, punct_tokens_set=None, remove_empty_nodes=True, + add_impact=True, add_rel_error=True, add_counts=True, root_outside_clause=True, + format_string=None, error_sample_size=None, error_sample_output_file=None): + ''' + Adds automatic clause annotations to the input text (via EstNLTK), and decomposes + errors made by the system according to dependency misplacements inside or outside + the clause. The following error types are distinguished: + + * E1 (local error): according to both gold standard and parser, the head of a word is + inside the same clause but gold standard and parser do not agree on the exact word + and/or deprel; + + * E2 (local-global error): according to gold standard, the head of a word is inside + the same clause, but parser thinks it is outside of the clause; + + * E3 (global error): according to gold standard, the word's head is outside of the + clause, and the parser got the head wrong (placed it either inside or outside the + clause); + + Discards punctuation (tokens with xpos == 'Z', or alternatively, tokens appearing + in given punct_tokens_set) from error calculations. + + Note: by default, root nodes (words with head==0) are always considered as being + "outside the clause". Use flag root_outside_clause=False to count root nodes as + being "inside the clause" (this will increase E1 errors while decreasing E2 and E3 + errors). + + Returns dictionary with calculated error counts ('E1', 'E2', 'E3'), and + additional statistics (see parameters add_impact, add_rel_error and add_counts + below for details). + + Requires EstNLTK version 1.7.2+. + + Parameters + ----------- + gold_file + CONLL-U format input file with gold standard syntax annotations + predicted_file + CONLL-U format input file with automatically predicted syntax annotations + punct_tokens_set + set with tokens that are considered as punctionation and that will be + discarded from evaluation. Undefined by default. + remove_empty_nodes + If True (default), then null / empty nodes (of the enhanced representation) + will be removed from input files (and discarded from calculations). + add_impact + If True (default), then calculates impacts and adds 'E1_impact', 'E2_impact', + and 'E3_impact' to the returned dictionary. Impact is the percentage of the + error from all errors. + add_rel_error + If True (default), then calculates relative errors and adds 'E1_rel_error', + 'E2_rel_error', and 'E3_rel_error' to the returned dictionary. Relative error + is the percentage from all arcs that can lead to given error type. + add_counts + If True (default), then adds token counts 'total_no_punct', 'correct', + 'gold_in_clause', 'gold_out_of_clause', 'total_words', 'punct', + 'unequal_length' 'E1_missed_root', 'E2_missed_root', 'E3_missed_root', + 'E2_with_E3', 'E2_without_E3' to the returned dictionary. + root_outside_clause: + If True (default), then root nodes (words with head==0) are always considered + as being "outside the clause". Otherwise, root nodes are considered as being + "inside the clause". + format_string + If `format_string` provided (not None), then uses it to reformat values of + impacts and relative errors. For instance, if `format_string=':.4f'`, then + impacts and relative errors will be rounded to 4 decimals. + error_sample_size + Optional. The number of erroneously parsed sentences to be randomly extracted + and saved into error_sample_output_file. + Only applies if both error_sample_size and error_sample_output_file are defined. + error_sample_output_file + Optional. A path to a output file into which randomly extracted erroneously + parsed sentences will be saved. + Only applies if both error_sample_size and error_sample_output_file are defined. + ''' + if punct_tokens_set is None: + punct_tokens_set = set() + # Load gold standard and automatic annotations into separate layers + text = conll_to_text(gold_file, syntax_layer='gold', remove_empty_nodes=remove_empty_nodes) + add_layer_from_conll(file=predicted_file, text=text, syntax_layer='parsed') + # Validate input sizes + assert len(text['gold']) == len(text['parsed']), \ + f"(!) Mismatching input sizes: gold_words: {len(text['gold'])}, predicted_words: {len(text['parsed'])}" + # Add automatic clauses annotation + text.tag_layer('clauses') + # Count errors + punct = 0 + e1 = 0 + e2 = 0 + e3 = 0 + correct = 0 + unequal_length = 0 + total = 0 + total_no_punct = 0 + gold_in_clause = 0 + gold_out_of_clause = 0 + e1_missed_root = 0 + e2_missed_root = 0 + e3_missed_root = 0 + e2_together_with_e3 = 0 + e2_without_e3 = 0 + error_locations = {'E1':[], 'E2':[], 'E3': []} + for idx, clause in enumerate(text.clauses): + wordforms = list(clause.gold.text) + gold_ids = list(clause.gold.id) + gold_heads = list(clause.gold.head) + gold_deprel = list(clause.gold.deprel) + gold_pos = list(clause.gold.xpostag) + gold_word_loc = list(clause.base_span) + assert len(gold_word_loc) == len(wordforms) + try: + parsed_heads = list(clause.parsed.head) + parsed_deprel = list(clause.parsed.deprel) + except AssertionError: + unequal_length += len(gold_heads) + total += len(gold_heads) + continue + in_clause_heads = gold_ids[:] + # Collect clause roots (words pointing outside the clause) + clause_roots = [] + for wid, head in zip(gold_ids, gold_heads): + if head not in gold_ids: + clause_roots.append(wid) + if not root_outside_clause: + # Count root node as being "inside + # the clause" rather than "outside + # the clause" (which is default). + # This shifts error distribution + # from E2 & E3 to E1. + in_clause_heads.append( 0 ) + # Collect specific errors inside clause + # (which can be correlated) + clause_e2 = [] + clause_e3 = [] + for gold_id, word_loc, wordform, pos, gold_head, parsed_head, gold_dep, parsed_dep in \ + zip(gold_ids, gold_word_loc, wordforms, gold_pos, gold_heads, parsed_heads, gold_deprel, parsed_deprel): + is_gold_root = (gold_head == 0) + (gold_start, gold_end) = word_loc.start, word_loc.end + total += 1 + if wordform in punct_tokens_set: + punct += 1 + pass + elif pos == 'Z': + punct += 1 + pass + else: + total_no_punct += 1 + if gold_head in in_clause_heads: + gold_in_clause += 1 + if gold_head == parsed_head and gold_dep == parsed_dep: + correct += 1 + else: + if parsed_head in in_clause_heads: + # local error: misplaced dependency inside the clause + e1 += 1 + if is_gold_root: + e1_missed_root += 1 + error_locations['E1'].append( (gold_start, gold_end) ) + else: + # overarcing error: + # misplaced dependency outside the clause, + # although it should be inside the clause + e2 += 1 + if is_gold_root: + e2_missed_root += 1 + if gold_head != parsed_head: + clause_e2.append(gold_id) + error_locations['E2'].append( (gold_start, gold_end) ) + else: + gold_out_of_clause += 1 + if gold_head == parsed_head and gold_dep == parsed_dep: + correct += 1 + else: + # global error: misplaced dependency which should be + # outside the clause (but was placed incorrectly + # inside or outside the clause) + e3 += 1 + if is_gold_root: + e3_missed_root += 1 + if gold_head != parsed_head and gold_id in clause_roots: + clause_e3.append(gold_id) + error_locations['E3'].append( (gold_start, gold_end) ) + if len(clause_e2) > 0: + if len(clause_e3) > 0: + # At least some of the clause roots were wrong and E2 errors appeared + e2_together_with_e3 += len( clause_e2 ) + elif len(clause_e3) == 0: + # Clause roots were correct, but E2 errors still appeared + # (parser pointed accidentially outside of the clause) + e2_without_e3 += len( clause_e2 ) + if error_sample_output_file is not None and isinstance(error_sample_size, int): + # Pick randomly a sample of errors and write into a file + extract_error_samples(text, error_locations, + error_sample_output_file, + n=error_sample_size, seed=1) + # Calculate impacts/relative errors and format results (if required) + result = {'E1': e1, 'E2': e2, 'E3': e3} + if add_impact: + result['E1_impact'] = e1/(total_no_punct-correct) + result['E2_impact'] = e2/(total_no_punct-correct) + result['E3_impact'] = e3/(total_no_punct-correct) + if add_rel_error: + result['E1_rel_error'] = e1/gold_in_clause + result['E2_rel_error'] = e2/gold_in_clause + result['E3_rel_error'] = e3/gold_out_of_clause + if add_counts: + result['total_no_punct'] = total_no_punct + result['correct'] = correct + result['gold_in_clause'] = gold_in_clause + result['gold_out_of_clause'] = gold_out_of_clause + result['total_words'] = total + result['punct'] = punct + result['unequal_length'] = unequal_length + result['E1_missed_root'] = e1_missed_root + result['E2_missed_root'] = e2_missed_root + result['E3_missed_root'] = e3_missed_root + # E2 in clause which has (at least one) E3 + result['E2_with_E3'] = e2_together_with_e3 + # E2 in clause which does not have any E3 + result['E2_without_E3'] = e2_without_e3 + if format_string is not None: + # Reformat impacts and relative errors + for k, v in result.items(): + if k.endswith(('_impact', '_rel_error')): + result[k] = ('{'+format_string+'}').format(v) + return result + + +def extract_error_samples(text, error_locations, output_file, n=100, seed=1, clauses_layer='clauses', + senteces_layer='sentences', gold_syntax_layer='gold', + auto_syntax_layer='parsed'): + ''' + Extracts erroneously parsed sentences based on collected E1, E2, E3 error_locations. + By default, extracts randomly n=100 samples of each error, or, if n >= len(errors), + extracts all erroneously parsed sentences from the error type. + Writes extracted sentences into output_file. + ''' + assert clauses_layer in text.layers + assert senteces_layer in text.layers + assert gold_syntax_layer in text.layers + assert auto_syntax_layer in text.layers + sent_locations = [(s.start, s.end, sid) for sid, s in enumerate(text[senteces_layer])] + clause_starts = set([cl.start for cl in text[clauses_layer]]) + clause_ends = set([cl.end for cl in text[clauses_layer]]) + # Clear output file + with open(output_file, 'w', encoding='utf-8') as out_f: + pass + rnd = random.Random(seed) + for err_key in error_locations.keys(): + err_sentences = [] + extracted_err_sents = [] + # Get all sentences containing errors + for (err_start, err_end) in error_locations[err_key]: + for (s_start, s_end, s_id) in sent_locations: + if s_start <= err_start and err_end <= s_end: + if (s_start, s_end, s_id) not in err_sentences: + err_sentences.append( (s_start, s_end, s_id) ) + break + if n < len(err_sentences): + # Make a random pick (if more than n sentences) + picked_sentences = rnd.sample(err_sentences, n) + else: + # Take all sentences with errors + picked_sentences = err_sentences + # Format sentences (mark errors) + for (s_start, s_end, s_id) in picked_sentences: + # Gather all errors inside the given sentence + sent_focus_errors = set() + sent_other_errors = dict() + for alt_err_key in error_locations.keys(): + for (err_start, err_end) in error_locations[alt_err_key]: + if s_start <= err_start and err_end <= s_end: + # Error inside the given sentence + if err_key == alt_err_key: + sent_focus_errors.add( (err_start, err_end) ) + else: + if (err_start, err_end) not in sent_other_errors: + sent_other_errors[(err_start, err_end)] = [] + sent_other_errors[(err_start, err_end)].append( alt_err_key ) + # Display words with labellings & errors + sent_formatted = [] + for word_span in text[senteces_layer][s_id]: + gold_word = text[gold_syntax_layer].get(word_span.base_span) + auto_word = text[auto_syntax_layer].get(word_span.base_span) + w_start, w_end = word_span.start, word_span.end + gold_id = gold_word.annotations[0]['id'] + gold_deprel = gold_word.annotations[0]['deprel'] + auto_deprel = auto_word.annotations[0]['deprel'] + gold_head = gold_word.annotations[0]['head'] + auto_head = auto_word.annotations[0]['head'] + word_text = word_span.text + error_type = '' + deprel_with_err = f'{gold_deprel}' + if (gold_deprel != auto_deprel): + deprel_with_err = f'{deprel_with_err} (auto: {auto_deprel})' + head_with_err = f'{gold_head}' + if (gold_head != auto_head): + head_with_err = f'{head_with_err} (auto: {auto_head})' + if (w_start, w_end) in sent_focus_errors: + error_type = f'((!{err_key}))' + elif (w_start, w_end) in sent_other_errors: + other_err_keys = [e.lower() for e in sent_other_errors[(w_start, w_end)]] + error_type = ';'.join(other_err_keys) + if word_span.start in clause_starts: + sent_formatted.append('CLAUSE_START') + sent_formatted.append( f'{gold_id} {word_text} {head_with_err} {deprel_with_err} {error_type}' ) + if word_span.end in clause_ends: + sent_formatted.append('CLAUSE_END') + sent_formatted.append('') + extracted_err_sents.append( '\n'.join(sent_formatted) ) + print(f'Writing {len(extracted_err_sents)} samples of {err_key} errors into {output_file} ...') + with open(output_file, 'a', encoding='utf-8') as out_f: + out_f.write('='*100) + out_f.write('\n') + out_f.write(f' {err_key}') + out_f.write('\n') + out_f.write('='*100) + out_f.write('\n') + for err_sent in extracted_err_sents: + out_f.write(err_sent) + out_f.write('\n') + + +# ======================================================================== + +if __name__ == '__main__': + # 1) Try to get configuration files from input args + # Optionally, user can also pass name of the output csv file + conf_files = [] + output_csv_file = 'results.csv' + for input_arg in sys.argv[1:]: + if (input_arg.lower()).endswith('.ini'): + conf_files.append( input_arg ) + elif (input_arg.lower()).endswith('.csv'): + output_csv_file = input_arg + if len(conf_files) == 0: + # 2) Try to collect configuration files from the root dir + root_dir = '.' + for fname in sorted( os.listdir(root_dir) ): + if (fname.lower()).endswith('.ini'): + # Attempt to open experiment configuration from INI file + conf_file = os.path.join(root_dir, fname) + conf_files.append(conf_file) + if len(conf_files) > 0: + # Perform evaluations defined in configurations + collected_results = dict() + for conf_file in conf_files: + eval_main(conf_file, collected_results=collected_results, ignore_missing=True, + verbose=True, round=True, count_words=False) + # Save collected results into experiment root directory csv file + for exp_root in collected_results.keys(): + filename = os.path.join(exp_root, output_csv_file) if os.path.exists(exp_root) else f'results_{exp_root}.csv' + print(f'Writing evaluation results into {filename} ...') + with open(filename, 'w', encoding='utf-8', newline='') as output_csv: + csv_writer = csv.writer(output_csv) + header = None + for exp_name in collected_results[exp_root].keys(): + exp_fields = list(collected_results[exp_root][exp_name].keys()) + if header is None: + header = ['experiment'] + exp_fields + csv_writer.writerow( header ) + else: + assert header[1:] == exp_fields, \ + f'Error writing CSV: mismatching headers: {header[1:]!r} vs {exp_fields!r}' + values = [exp_name] + for key in header[1:]: + values.append( collected_results[exp_root][exp_name][key] ) + assert len(values) == len(header) + csv_writer.writerow( values ) + else: + raise Exception('(!) Could not found any configuration INI file from the root directory nor from input arguments') \ No newline at end of file diff --git a/06_result_tables.ipynb b/06_result_tables.ipynb new file mode 100644 index 00000000..798e8581 --- /dev/null +++ b/06_result_tables.ipynb @@ -0,0 +1,2724 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0ed091b5", + "metadata": {}, + "source": [ + "## Inspecting result CSV files" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "d31710fd", + "metadata": {}, + "outputs": [], + "source": [ + "import os, os.path\n", + "import re\n", + "\n", + "from pandas import DataFrame as df\n", + "from pandas import read_csv\n", + "import pandas" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "dcdbccd8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "results_crossvalidation.csv\n", + "results_ensemble_conf_intervals.csv\n", + "results_ensemble_entropy.csv\n", + "results_ensemble_majority_voting.csv\n", + "results_full_data_malt_udpipe1.csv\n", + "results_gap_experiments.csv\n", + "results_gold_and_auto_ud_morph.csv\n", + "results_half_data.csv\n", + "results_smaller_data.csv\n", + "results_stanza_basic.csv\n", + "results_stanza_MA_ensembles.csv\n", + "results_stanza_ME_conf_intervals.csv\n", + "results_stanza_ME_error_types.csv\n", + "results_stanza_ME_full_predict_on_clauses.csv\n", + "results_stanza_ME_full_predict_on_clauses_error_types.csv\n", + "results_stanza_ME_on_clauses.csv\n", + "results_stanza_ME_on_clauses_error_types.csv\n", + "results_stanza_ME_sketches_5groups_knockout.csv\n", + "results_stanza_ME_sketches_5groups_knockout_matrix.csv\n", + "results_stanza_ME_sketches_5randomgroups_knockout.csv\n", + "results_stanza_ME_sketches_5randomgroups_knockout_matrix.csv\n", + "results_stanza_UD_on_auto_UD_morph.csv\n" + ] + } + ], + "source": [ + "input_dir = 'edt_2.6' # Experiments done on Estonian Dependency Treebank version 2.6\n", + "\n", + "for fname in os.listdir(input_dir):\n", + " if fname.startswith('results_') and fname.endswith('.csv'):\n", + " print(fname)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "52ffdbec", + "metadata": {}, + "outputs": [], + "source": [ + "pandas.set_option('display.max_colwidth', None)" + ] + }, + { + "cell_type": "markdown", + "id": "fe406614", + "metadata": {}, + "source": [ + "### MaltParser and UDPipe-1" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "8a97e325", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainLAS_gapUAS_testUAS_train
0eval_malt_morph_extended_full0.71860.84460.12600.76810.8715
1eval_udpipe1_default_morph_extended0.75600.85010.09410.80090.8811
2eval_udpipe1_embeddings_morph_extended0.77130.91470.14340.81350.9330
\n", + "
" + ], + "text/plain": [ + " experiment LAS_test LAS_train LAS_gap \\\n", + "0 eval_malt_morph_extended_full 0.7186 0.8446 0.1260 \n", + "1 eval_udpipe1_default_morph_extended 0.7560 0.8501 0.0941 \n", + "2 eval_udpipe1_embeddings_morph_extended 0.7713 0.9147 0.1434 \n", + "\n", + " UAS_test UAS_train \n", + "0 0.7681 0.8715 \n", + "1 0.8009 0.8811 \n", + "2 0.8135 0.9330 " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# MaltParser and UDPipe-1 results\n", + "read_csv('edt_2.6/results_full_data_malt_udpipe1.csv')" + ] + }, + { + "cell_type": "markdown", + "id": "a18f8d5f", + "metadata": {}, + "source": [ + "### Basic stanza on Vabamorf's annotations (morph_analysis, morph_extended)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "d62afeb0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainLAS_gapUAS_testUAS_traintest_wordstrain_words
0eval_stanza_morph_analysis_full_data0.85070.92310.07240.88020.942248491389278
1eval_stanza_morph_extended_full_data0.84860.91760.06890.87820.937848491389278
\n", + "
" + ], + "text/plain": [ + " experiment LAS_test LAS_train LAS_gap \\\n", + "0 eval_stanza_morph_analysis_full_data 0.8507 0.9231 0.0724 \n", + "1 eval_stanza_morph_extended_full_data 0.8486 0.9176 0.0689 \n", + "\n", + " UAS_test UAS_train test_words train_words \n", + "0 0.8802 0.9422 48491 389278 \n", + "1 0.8782 0.9378 48491 389278 " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Basic Stanza parser results\n", + "read_csv('edt_2.6/results_stanza_basic.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "d93d2d8e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainUAS_testUAS_trainLAS_gaptest_wordstrain_words
0eval_stanza_morph_extended_full_data_conf_intervals0.8401; 0.8486; 0.85720.9161; 0.9176; 0.91910.8697; 0.8782; 0.88670.9363; 0.9378; 0.93930.0760; 0.0689; 0.061948491389278
\n", + "
" + ], + "text/plain": [ + " experiment \\\n", + "0 eval_stanza_morph_extended_full_data_conf_intervals \n", + "\n", + " LAS_test LAS_train UAS_test \\\n", + "0 0.8401; 0.8486; 0.8572 0.9161; 0.9176; 0.9191 0.8697; 0.8782; 0.8867 \n", + "\n", + " UAS_train LAS_gap test_words train_words \n", + "0 0.9363; 0.9378; 0.9393 0.0760; 0.0689; 0.0619 48491 389278 " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Basic Stanza ME parser with 95% conf intervals\n", + "read_csv('edt_2.6/results_stanza_ME_conf_intervals.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "15aa1546", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentE1E2E3E1_impactE2_impactE3_impactE1_rel_errorE2_rel_errorE3_rel_error...gold_in_clausegold_out_of_clausetotal_wordspunctunequal_lengthE1_missed_rootE2_missed_rootE3_missed_rootE2_with_E3E2_without_E3
0stanza_ME_error_types_on_test482139811620.75550.06240.18210.14130.01170.1733...34108670748491767600025436038
\n", + "

1 rows × 22 columns

\n", + "
" + ], + "text/plain": [ + " experiment E1 E2 E3 E1_impact E2_impact \\\n", + "0 stanza_ME_error_types_on_test 4821 398 1162 0.7555 0.0624 \n", + "\n", + " E3_impact E1_rel_error E2_rel_error E3_rel_error ... gold_in_clause \\\n", + "0 0.1821 0.1413 0.0117 0.1733 ... 34108 \n", + "\n", + " gold_out_of_clause total_words punct unequal_length E1_missed_root \\\n", + "0 6707 48491 7676 0 0 \n", + "\n", + " E2_missed_root E3_missed_root E2_with_E3 E2_without_E3 \n", + "0 0 254 360 38 \n", + "\n", + "[1 rows x 22 columns]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Stanza parser error types on test set\n", + "read_csv('edt_2.6/results_stanza_ME_error_types.csv')" + ] + }, + { + "cell_type": "markdown", + "id": "dc0a2868", + "metadata": {}, + "source": [ + "### Stanza with crossvalidation" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d3c857dd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainLAS_gapUAS_testUAS_train
0eval_stanza_morph_extended_crossvalidation_010.84850.92920.08070.87780.9476
1eval_stanza_morph_extended_crossvalidation_020.84800.92170.07370.87670.9412
2eval_stanza_morph_extended_crossvalidation_030.84600.92740.08130.87600.9463
3eval_stanza_morph_extended_crossvalidation_040.84680.92630.07950.87630.9452
4eval_stanza_morph_extended_crossvalidation_050.85140.93080.07930.87970.9484
5eval_stanza_morph_extended_crossvalidation_060.84730.92030.07300.87720.9405
6eval_stanza_morph_extended_crossvalidation_070.84650.92010.07350.87590.9402
7eval_stanza_morph_extended_crossvalidation_080.84820.92970.08150.87790.9479
8eval_stanza_morph_extended_crossvalidation_090.84820.92410.07580.87820.9430
9eval_stanza_morph_extended_crossvalidation_100.85030.93220.08190.88000.9499
10eval_stanza_morph_extended_crossvalidation_AVG0.84810.92620.07800.87760.9450
11eval_stanza_ensemble_tagger_morph_extended_default_full_data0.85680.93370.07690.88510.9515
\n", + "
" + ], + "text/plain": [ + " experiment LAS_test \\\n", + "0 eval_stanza_morph_extended_crossvalidation_01 0.8485 \n", + "1 eval_stanza_morph_extended_crossvalidation_02 0.8480 \n", + "2 eval_stanza_morph_extended_crossvalidation_03 0.8460 \n", + "3 eval_stanza_morph_extended_crossvalidation_04 0.8468 \n", + "4 eval_stanza_morph_extended_crossvalidation_05 0.8514 \n", + "5 eval_stanza_morph_extended_crossvalidation_06 0.8473 \n", + "6 eval_stanza_morph_extended_crossvalidation_07 0.8465 \n", + "7 eval_stanza_morph_extended_crossvalidation_08 0.8482 \n", + "8 eval_stanza_morph_extended_crossvalidation_09 0.8482 \n", + "9 eval_stanza_morph_extended_crossvalidation_10 0.8503 \n", + "10 eval_stanza_morph_extended_crossvalidation_AVG 0.8481 \n", + "11 eval_stanza_ensemble_tagger_morph_extended_default_full_data 0.8568 \n", + "\n", + " LAS_train LAS_gap UAS_test UAS_train \n", + "0 0.9292 0.0807 0.8778 0.9476 \n", + "1 0.9217 0.0737 0.8767 0.9412 \n", + "2 0.9274 0.0813 0.8760 0.9463 \n", + "3 0.9263 0.0795 0.8763 0.9452 \n", + "4 0.9308 0.0793 0.8797 0.9484 \n", + "5 0.9203 0.0730 0.8772 0.9405 \n", + "6 0.9201 0.0735 0.8759 0.9402 \n", + "7 0.9297 0.0815 0.8779 0.9479 \n", + "8 0.9241 0.0758 0.8782 0.9430 \n", + "9 0.9322 0.0819 0.8800 0.9499 \n", + "10 0.9262 0.0780 0.8776 0.9450 \n", + "11 0.9337 0.0769 0.8851 0.9515 " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Stanza parser crossvalidation\n", + "read_csv('edt_2.6/results_crossvalidation.csv')" + ] + }, + { + "cell_type": "markdown", + "id": "42d78955", + "metadata": {}, + "source": [ + "### Stanza ablation experiments" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "11e86a5e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainLAS_gapUAS_testUAS_train
0eval_stanza_morph_extended_gap_experiments_01_no_wordforms0.83960.92360.08400.87340.9461
1eval_stanza_morph_extended_gap_experiments_02_no_lemmas0.84830.93080.08250.87890.9496
2eval_stanza_morph_extended_gap_experiments_02_no_pos0.84920.92840.07920.87810.9471
3eval_stanza_morph_extended_gap_experiments_03_no_wordforms_adj_noun_lemmas0.82000.91090.09090.86100.9396
4eval_stanza_morph_extended_gap_experiments_04_no_wordforms_verb_adpos_lemmas0.78220.91060.12850.81810.9370
5eval_stanza_morph_extended_gap_experiments_05_only_cg_list_wordforms_lemmas0.81580.91530.09950.85720.9433
6eval_stanza_morph_extended_gap_experiments_06_no_wordform_lemma_pos_keep_conj0.68690.81000.12300.75720.8701
7eval_stanza_morph_extended_gap_experiments_07_no_wordform_lemma_pos0.67230.76480.09250.75320.8351
8eval_stanza_morph_extended_gap_experiments_08_only_wordforms0.84070.92020.07950.87420.9391
9eval_stanza_morph_extended_gap_experiments_09_only_pos_feats0.70180.82130.11950.77090.8799
\n", + "
" + ], + "text/plain": [ + " experiment \\\n", + "0 eval_stanza_morph_extended_gap_experiments_01_no_wordforms \n", + "1 eval_stanza_morph_extended_gap_experiments_02_no_lemmas \n", + "2 eval_stanza_morph_extended_gap_experiments_02_no_pos \n", + "3 eval_stanza_morph_extended_gap_experiments_03_no_wordforms_adj_noun_lemmas \n", + "4 eval_stanza_morph_extended_gap_experiments_04_no_wordforms_verb_adpos_lemmas \n", + "5 eval_stanza_morph_extended_gap_experiments_05_only_cg_list_wordforms_lemmas \n", + "6 eval_stanza_morph_extended_gap_experiments_06_no_wordform_lemma_pos_keep_conj \n", + "7 eval_stanza_morph_extended_gap_experiments_07_no_wordform_lemma_pos \n", + "8 eval_stanza_morph_extended_gap_experiments_08_only_wordforms \n", + "9 eval_stanza_morph_extended_gap_experiments_09_only_pos_feats \n", + "\n", + " LAS_test LAS_train LAS_gap UAS_test UAS_train \n", + "0 0.8396 0.9236 0.0840 0.8734 0.9461 \n", + "1 0.8483 0.9308 0.0825 0.8789 0.9496 \n", + "2 0.8492 0.9284 0.0792 0.8781 0.9471 \n", + "3 0.8200 0.9109 0.0909 0.8610 0.9396 \n", + "4 0.7822 0.9106 0.1285 0.8181 0.9370 \n", + "5 0.8158 0.9153 0.0995 0.8572 0.9433 \n", + "6 0.6869 0.8100 0.1230 0.7572 0.8701 \n", + "7 0.6723 0.7648 0.0925 0.7532 0.8351 \n", + "8 0.8407 0.9202 0.0795 0.8742 0.9391 \n", + "9 0.7018 0.8213 0.1195 0.7709 0.8799 " + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Stanza parser gap experiments, trained on full data, results\n", + "read_csv('edt_2.6/results_gap_experiments.csv')" + ] + }, + { + "cell_type": "markdown", + "id": "15db66c1", + "metadata": {}, + "source": [ + "### Stanza half training data experiments" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "6c75808d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainLAS_gapUAS_testUAS_train
0eval_stanza_morph_extended_half_data_0010.82950.94480.11530.86250.9564
1eval_stanza_morph_extended_half_data_0020.82780.92030.09250.86070.9392
2eval_stanza_morph_extended_half_data_0030.82760.94530.11770.86020.9568
3eval_stanza_morph_extended_half_data_0040.82650.93840.11190.85800.9521
4eval_stanza_morph_extended_half_data_0050.82640.92890.10240.86010.9443
5eval_stanza_morph_extended_half_data_0060.82480.92550.10070.85860.9425
6eval_stanza_morph_extended_half_data_0070.82750.92710.09960.86020.9438
7eval_stanza_morph_extended_half_data_0080.82870.93530.10650.86170.9495
8eval_stanza_morph_extended_half_data_0090.83040.93680.10650.86270.9504
9eval_stanza_morph_extended_half_data_0100.82520.92750.10230.85790.9445
10eval_stanza_morph_extended_half_data_AVG0.82740.93300.10550.86030.9480
11eval_stanza_morph_extended_half_data_ensemble0.84460.91510.07050.87600.9344
\n", + "
" + ], + "text/plain": [ + " experiment LAS_test LAS_train \\\n", + "0 eval_stanza_morph_extended_half_data_001 0.8295 0.9448 \n", + "1 eval_stanza_morph_extended_half_data_002 0.8278 0.9203 \n", + "2 eval_stanza_morph_extended_half_data_003 0.8276 0.9453 \n", + "3 eval_stanza_morph_extended_half_data_004 0.8265 0.9384 \n", + "4 eval_stanza_morph_extended_half_data_005 0.8264 0.9289 \n", + "5 eval_stanza_morph_extended_half_data_006 0.8248 0.9255 \n", + "6 eval_stanza_morph_extended_half_data_007 0.8275 0.9271 \n", + "7 eval_stanza_morph_extended_half_data_008 0.8287 0.9353 \n", + "8 eval_stanza_morph_extended_half_data_009 0.8304 0.9368 \n", + "9 eval_stanza_morph_extended_half_data_010 0.8252 0.9275 \n", + "10 eval_stanza_morph_extended_half_data_AVG 0.8274 0.9330 \n", + "11 eval_stanza_morph_extended_half_data_ensemble 0.8446 0.9151 \n", + "\n", + " LAS_gap UAS_test UAS_train \n", + "0 0.1153 0.8625 0.9564 \n", + "1 0.0925 0.8607 0.9392 \n", + "2 0.1177 0.8602 0.9568 \n", + "3 0.1119 0.8580 0.9521 \n", + "4 0.1024 0.8601 0.9443 \n", + "5 0.1007 0.8586 0.9425 \n", + "6 0.0996 0.8602 0.9438 \n", + "7 0.1065 0.8617 0.9495 \n", + "8 0.1065 0.8627 0.9504 \n", + "9 0.1023 0.8579 0.9445 \n", + "10 0.1055 0.8603 0.9480 \n", + "11 0.0705 0.8760 0.9344 " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Stanza parser trained on half-data (i.e training data halved)\n", + "read_csv('edt_2.6/results_half_data.csv')" + ] + }, + { + "cell_type": "markdown", + "id": "c4821788", + "metadata": {}, + "source": [ + "### Stanza increasing training data experiments" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "95e64940", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainLAS_gapUAS_testUAS_traintest_wordstrain_words
0eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0100.68640.94910.26280.76050.95914849139886
1eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0200.74900.96360.21460.80040.97004849177759
2eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0300.78190.93650.15460.82710.948948491117642
3eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0400.79890.93660.13770.83880.950248491157491
4eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0500.81520.92340.10820.85430.940648491195477
5eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0600.82500.92990.10490.85950.945848491235468
6eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0700.82410.91790.09380.86000.937048491273422
7eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0800.83270.91940.08670.86740.938348491311383
8eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0900.83430.91510.08080.86800.936248491349322
9eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_1000.83770.91040.07280.87230.932048491389278
10eval_stanza_morph_extended_smaller_data_02_keep_all_0100.75090.92860.17780.79950.94024849139886
11eval_stanza_morph_extended_smaller_data_02_keep_all_0200.78950.95520.16570.82970.96304849177759
12eval_stanza_morph_extended_smaller_data_02_keep_all_0300.80620.92680.12070.84390.944048491117642
13eval_stanza_morph_extended_smaller_data_02_keep_all_0400.82090.94140.12050.85450.954248491157491
14eval_stanza_morph_extended_smaller_data_02_keep_all_0500.82990.93600.10600.86160.950548491195477
15eval_stanza_morph_extended_smaller_data_02_keep_all_0600.84090.93500.09410.87230.950548491235468
16eval_stanza_morph_extended_smaller_data_02_keep_all_0700.83630.92480.08840.86750.942848491273422
17eval_stanza_morph_extended_smaller_data_02_keep_all_0800.84350.92950.08600.87390.946348491311383
18eval_stanza_morph_extended_smaller_data_02_keep_all_0900.84630.92610.07980.87560.943748491349322
19eval_stanza_morph_extended_smaller_data_02_keep_all_1000.84660.92360.07710.87630.942248491389278
20eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0100.62850.81740.18890.71210.85684849139886
21eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0200.65440.85420.19970.73360.89124849177759
22eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0300.66520.78470.11950.74120.844548491117642
23eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0400.67300.78890.11590.74790.847148491157491
24eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0500.68020.78780.10760.75360.847948491195477
25eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0600.68800.81240.12440.75950.866848491235468
26eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0700.69150.80000.10850.76380.859448491273422
27eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0800.69390.80010.10610.76530.857548491311383
28eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0900.69520.80810.11280.76580.866448491349322
29eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_1000.69700.78740.09040.76700.848948491389278
\n", + "
" + ], + "text/plain": [ + " experiment \\\n", + "0 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_010 \n", + "1 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_020 \n", + "2 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_030 \n", + "3 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_040 \n", + "4 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_050 \n", + "5 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_060 \n", + "6 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_070 \n", + "7 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_080 \n", + "8 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_090 \n", + "9 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_100 \n", + "10 eval_stanza_morph_extended_smaller_data_02_keep_all_010 \n", + "11 eval_stanza_morph_extended_smaller_data_02_keep_all_020 \n", + "12 eval_stanza_morph_extended_smaller_data_02_keep_all_030 \n", + "13 eval_stanza_morph_extended_smaller_data_02_keep_all_040 \n", + "14 eval_stanza_morph_extended_smaller_data_02_keep_all_050 \n", + "15 eval_stanza_morph_extended_smaller_data_02_keep_all_060 \n", + "16 eval_stanza_morph_extended_smaller_data_02_keep_all_070 \n", + "17 eval_stanza_morph_extended_smaller_data_02_keep_all_080 \n", + "18 eval_stanza_morph_extended_smaller_data_02_keep_all_090 \n", + "19 eval_stanza_morph_extended_smaller_data_02_keep_all_100 \n", + "20 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_010 \n", + "21 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_020 \n", + "22 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_030 \n", + "23 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_040 \n", + "24 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_050 \n", + "25 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_060 \n", + "26 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_070 \n", + "27 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_080 \n", + "28 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_090 \n", + "29 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_100 \n", + "\n", + " LAS_test LAS_train LAS_gap UAS_test UAS_train test_words train_words \n", + "0 0.6864 0.9491 0.2628 0.7605 0.9591 48491 39886 \n", + "1 0.7490 0.9636 0.2146 0.8004 0.9700 48491 77759 \n", + "2 0.7819 0.9365 0.1546 0.8271 0.9489 48491 117642 \n", + "3 0.7989 0.9366 0.1377 0.8388 0.9502 48491 157491 \n", + "4 0.8152 0.9234 0.1082 0.8543 0.9406 48491 195477 \n", + "5 0.8250 0.9299 0.1049 0.8595 0.9458 48491 235468 \n", + "6 0.8241 0.9179 0.0938 0.8600 0.9370 48491 273422 \n", + "7 0.8327 0.9194 0.0867 0.8674 0.9383 48491 311383 \n", + "8 0.8343 0.9151 0.0808 0.8680 0.9362 48491 349322 \n", + "9 0.8377 0.9104 0.0728 0.8723 0.9320 48491 389278 \n", + "10 0.7509 0.9286 0.1778 0.7995 0.9402 48491 39886 \n", + "11 0.7895 0.9552 0.1657 0.8297 0.9630 48491 77759 \n", + "12 0.8062 0.9268 0.1207 0.8439 0.9440 48491 117642 \n", + "13 0.8209 0.9414 0.1205 0.8545 0.9542 48491 157491 \n", + "14 0.8299 0.9360 0.1060 0.8616 0.9505 48491 195477 \n", + "15 0.8409 0.9350 0.0941 0.8723 0.9505 48491 235468 \n", + "16 0.8363 0.9248 0.0884 0.8675 0.9428 48491 273422 \n", + "17 0.8435 0.9295 0.0860 0.8739 0.9463 48491 311383 \n", + "18 0.8463 0.9261 0.0798 0.8756 0.9437 48491 349322 \n", + "19 0.8466 0.9236 0.0771 0.8763 0.9422 48491 389278 \n", + "20 0.6285 0.8174 0.1889 0.7121 0.8568 48491 39886 \n", + "21 0.6544 0.8542 0.1997 0.7336 0.8912 48491 77759 \n", + "22 0.6652 0.7847 0.1195 0.7412 0.8445 48491 117642 \n", + "23 0.6730 0.7889 0.1159 0.7479 0.8471 48491 157491 \n", + "24 0.6802 0.7878 0.1076 0.7536 0.8479 48491 195477 \n", + "25 0.6880 0.8124 0.1244 0.7595 0.8668 48491 235468 \n", + "26 0.6915 0.8000 0.1085 0.7638 0.8594 48491 273422 \n", + "27 0.6939 0.8001 0.1061 0.7653 0.8575 48491 311383 \n", + "28 0.6952 0.8081 0.1128 0.7658 0.8664 48491 349322 \n", + "29 0.6970 0.7874 0.0904 0.7670 0.8489 48491 389278 " + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Stanza parser trained while incrementally increasing training set sizes\n", + "# 01_08_only_wordforms -- trained on form (text); deleted: lemma, upos, xpos, feats;\n", + "# 02_keep_all -- trained on all fields: form (text), lemma, upos, xpos, feats;\n", + "# 03_only_pos_feats_09_only_pos_feats -- trained on upos, xpos, feats; deleted: form(text), lemma;\n", + "read_csv('edt_2.6/results_smaller_data.csv')" + ] + }, + { + "cell_type": "markdown", + "id": "6cabedef", + "metadata": {}, + "source": [ + "### Ensemble of Stanza ME models (full data, half data, conf intervals)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "b00f74b2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainUAS_testUAS_trainLAS_gap
0eval_stanza_morph_extended_full_data_conf_intervals0.8401; 0.8486; 0.85720.9161; 0.9176; 0.91910.8697; 0.8782; 0.88670.9363; 0.9378; 0.93930.0760; 0.0689; 0.0619
1eval_stanza_ensemble_morph_extended_full_data_las_coherence_conf_intervals0.8488; 0.8568; 0.86490.9327; 0.9337; 0.93470.8774; 0.8852; 0.89290.9505; 0.9515; 0.95250.0839; 0.0769; 0.0698
2eval_stanza_ensemble_morph_extended_full_data_majority_voting_conf_intervals0.8487; 0.8564; 0.86410.9327; 0.9337; 0.93470.8775; 0.8849; 0.89240.9507; 0.9517; 0.95270.0840; 0.0773; 0.0707
3eval_stanza_ensemble_morph_extended_half_data_las_coherence_conf_intervals0.8383; 0.8447; 0.85100.9134; 0.9152; 0.91690.8699; 0.8761; 0.88220.9325; 0.9344; 0.93630.0751; 0.0705; 0.0659
4eval_stanza_ensemble_morph_extended_half_data_majority_voting_conf_intervals0.8386; 0.8449; 0.85120.9109; 0.9126; 0.91430.8699; 0.8762; 0.88260.9306; 0.9325; 0.93440.0722; 0.0677; 0.0631
\n", + "
" + ], + "text/plain": [ + " experiment \\\n", + "0 eval_stanza_morph_extended_full_data_conf_intervals \n", + "1 eval_stanza_ensemble_morph_extended_full_data_las_coherence_conf_intervals \n", + "2 eval_stanza_ensemble_morph_extended_full_data_majority_voting_conf_intervals \n", + "3 eval_stanza_ensemble_morph_extended_half_data_las_coherence_conf_intervals \n", + "4 eval_stanza_ensemble_morph_extended_half_data_majority_voting_conf_intervals \n", + "\n", + " LAS_test LAS_train UAS_test \\\n", + "0 0.8401; 0.8486; 0.8572 0.9161; 0.9176; 0.9191 0.8697; 0.8782; 0.8867 \n", + "1 0.8488; 0.8568; 0.8649 0.9327; 0.9337; 0.9347 0.8774; 0.8852; 0.8929 \n", + "2 0.8487; 0.8564; 0.8641 0.9327; 0.9337; 0.9347 0.8775; 0.8849; 0.8924 \n", + "3 0.8383; 0.8447; 0.8510 0.9134; 0.9152; 0.9169 0.8699; 0.8761; 0.8822 \n", + "4 0.8386; 0.8449; 0.8512 0.9109; 0.9126; 0.9143 0.8699; 0.8762; 0.8826 \n", + "\n", + " UAS_train LAS_gap \n", + "0 0.9363; 0.9378; 0.9393 0.0760; 0.0689; 0.0619 \n", + "1 0.9505; 0.9515; 0.9525 0.0839; 0.0769; 0.0698 \n", + "2 0.9507; 0.9517; 0.9527 0.0840; 0.0773; 0.0707 \n", + "3 0.9325; 0.9344; 0.9363 0.0751; 0.0705; 0.0659 \n", + "4 0.9306; 0.9325; 0.9344 0.0722; 0.0677; 0.0631 " + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Ensemble results with 95% confidence intervals (normal distribution):\n", + "# 0 -- single model (baseline)\n", + "# 1-2 -- full data ensemble models\n", + "# 3-4 -- half data ensemble models\n", + "ensemble_conf_intervals = read_csv('edt_2.6/results_ensemble_conf_intervals.csv')\n", + "ensemble_conf_intervals" + ] + }, + { + "cell_type": "markdown", + "id": "f1e29bbc", + "metadata": {}, + "source": [ + "### Ensemble of Stanza MA models (full data)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "a71530db", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainUAS_testUAS_trainLAS_gap
0eval_stanza_ensemble_morph_analysis_full_data_default0.85730.93300.88510.9510.0757
1eval_stanza_ensemble_morph_analysis_full_data_majority_voting0.85730.93290.88580.9510.0756
\n", + "
" + ], + "text/plain": [ + " experiment LAS_test \\\n", + "0 eval_stanza_ensemble_morph_analysis_full_data_default 0.8573 \n", + "1 eval_stanza_ensemble_morph_analysis_full_data_majority_voting 0.8573 \n", + "\n", + " LAS_train UAS_test UAS_train LAS_gap \n", + "0 0.9330 0.8851 0.951 0.0757 \n", + "1 0.9329 0.8858 0.951 0.0756 " + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ensemble_morph_analysis = read_csv('edt_2.6/results_stanza_MA_ensembles.csv')\n", + "ensemble_morph_analysis" + ] + }, + { + "cell_type": "markdown", + "id": "de31ccc1", + "metadata": {}, + "source": [ + "### Stanza on Stanza's default UD morph (automatically created)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "6ff4d564", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainUAS_testUAS_trainLAS_gap
0eval_stanza_ud_on_stanza_auto_morph_full_data0.85190.92910.88010.94670.0772
\n", + "
" + ], + "text/plain": [ + " experiment LAS_test LAS_train \\\n", + "0 eval_stanza_ud_on_stanza_auto_morph_full_data 0.8519 0.9291 \n", + "\n", + " UAS_test UAS_train LAS_gap \n", + "0 0.8801 0.9467 0.0772 " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Stanza parser trained on stanza's UD morph annotations:\n", + "read_csv('edt_2.6/results_stanza_UD_on_auto_UD_morph.csv')" + ] + }, + { + "cell_type": "markdown", + "id": "3bb1fe46", + "metadata": {}, + "source": [ + "### Stanza on UD morph analysis (auto, gold)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "e0de7753", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainLAS_gapUAS_testUAS_train
0eval_stanza_ud_auto_morph_full_data0.85230.93330.0810.88080.9508
1eval_stanza_ud_gold_morph_full_data0.88100.93800.0570.89870.9502
\n", + "
" + ], + "text/plain": [ + " experiment LAS_test LAS_train LAS_gap \\\n", + "0 eval_stanza_ud_auto_morph_full_data 0.8523 0.9333 0.081 \n", + "1 eval_stanza_ud_gold_morph_full_data 0.8810 0.9380 0.057 \n", + "\n", + " UAS_test UAS_train \n", + "0 0.8808 0.9508 \n", + "1 0.8987 0.9502 " + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Stanza parser trained on UD annotations:\n", + "# ud_auto -- UD annotations automatically converted from Vabamorf's morph_analysis layer;\n", + "# ud_gold -- original gold standard UD annotations from the EDT corpus;\n", + "read_csv('edt_2.6/results_gold_and_auto_ud_morph.csv')" + ] + }, + { + "cell_type": "markdown", + "id": "35944100", + "metadata": {}, + "source": [ + "### Stanza basic on the latest version of the corpus (2.11)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "0a34ba81", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainLAS_gapUAS_testUAS_train
0eval_stanza_morph_extended_full_data0.84840.92470.07640.87750.943
\n", + "
" + ], + "text/plain": [ + " experiment LAS_test LAS_train LAS_gap \\\n", + "0 eval_stanza_morph_extended_full_data 0.8484 0.9247 0.0764 \n", + "\n", + " UAS_test UAS_train \n", + "0 0.8775 0.943 " + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Stanza trained and evaluated on the latest version of the corpus (EDT 2.11)\n", + "read_csv('edt_2.11/results_stanza_basic.csv')" + ] + }, + { + "cell_type": "markdown", + "id": "16cb46f8", + "metadata": {}, + "source": [ + "### Composite table (Table 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "f826a55d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainLAS_gapUAS_testUAS_train
0eval_malt_morph_extended_full0.71860.84460.12600.76810.8715
1eval_udpipe1_default_morph_extended0.75600.85010.09410.80090.8811
2eval_udpipe1_embeddings_morph_extended0.77130.91470.14340.81350.9330
0eval_stanza_ud_on_stanza_auto_morph_full_data0.85190.92910.07720.88010.9467
8eval_stanza_morph_extended_gap_experiments_08_only_wordforms0.84070.92020.07950.87420.9391
0eval_stanza_morph_analysis_full_data0.85070.92310.07240.88020.9422
1eval_stanza_morph_extended_full_data (EDT_2.6)0.84860.91760.06890.87820.9378
0eval_stanza_morph_extended_full_data (EDT_2.11)0.84840.92470.07640.87750.9430
0eval_stanza_morph_extended_full_data_ensemble_majority_voting0.85640.93370.07730.88490.9517
0eval_stanza_ensemble_morph_analysis_full_data_default0.85730.93300.07570.88510.9510
1eval_stanza_ud_gold_morph_full_data0.88100.93800.05700.89870.9502
\n", + "
" + ], + "text/plain": [ + " experiment LAS_test \\\n", + "0 eval_malt_morph_extended_full 0.7186 \n", + "1 eval_udpipe1_default_morph_extended 0.7560 \n", + "2 eval_udpipe1_embeddings_morph_extended 0.7713 \n", + "0 eval_stanza_ud_on_stanza_auto_morph_full_data 0.8519 \n", + "8 eval_stanza_morph_extended_gap_experiments_08_only_wordforms 0.8407 \n", + "0 eval_stanza_morph_analysis_full_data 0.8507 \n", + "1 eval_stanza_morph_extended_full_data (EDT_2.6) 0.8486 \n", + "0 eval_stanza_morph_extended_full_data (EDT_2.11) 0.8484 \n", + "0 eval_stanza_morph_extended_full_data_ensemble_majority_voting 0.8564 \n", + "0 eval_stanza_ensemble_morph_analysis_full_data_default 0.8573 \n", + "1 eval_stanza_ud_gold_morph_full_data 0.8810 \n", + "\n", + " LAS_train LAS_gap UAS_test UAS_train \n", + "0 0.8446 0.1260 0.7681 0.8715 \n", + "1 0.8501 0.0941 0.8009 0.8811 \n", + "2 0.9147 0.1434 0.8135 0.9330 \n", + "0 0.9291 0.0772 0.8801 0.9467 \n", + "8 0.9202 0.0795 0.8742 0.9391 \n", + "0 0.9231 0.0724 0.8802 0.9422 \n", + "1 0.9176 0.0689 0.8782 0.9378 \n", + "0 0.9247 0.0764 0.8775 0.9430 \n", + "0 0.9337 0.0773 0.8849 0.9517 \n", + "0 0.9330 0.0757 0.8851 0.9510 \n", + "1 0.9380 0.0570 0.8987 0.9502 " + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# MaltParser and UDPipe-1 results\n", + "res1 = read_csv('edt_2.6/results_full_data_malt_udpipe1.csv')\n", + "# Stanza parser trained on stanza's UD morph annotations:\n", + "res2x = read_csv('edt_2.6/results_stanza_UD_on_auto_UD_morph.csv')\n", + "# Stanza only_wordforms experiment\n", + "res2 = read_csv('edt_2.6/results_gap_experiments.csv')\n", + "res2 = res2.loc[res2['experiment'].str.contains('08_only_wordforms')]\n", + "# Basic Stanza MA and ME parser results on EDT 2.6\n", + "res3 = read_csv('edt_2.6/results_stanza_basic.csv')\n", + "# Rename models\n", + "res3.loc[:, 'experiment'] = res3.apply(lambda x: re.sub(r'eval_stanza_morph_extended_full_data',\\\n", + " 'eval_stanza_morph_extended_full_data (EDT_2.6)',\n", + " x['experiment']), axis=1)\n", + "# Stanza trained and evaluated EDT 2.11\n", + "res4 = read_csv('edt_2.11/results_stanza_basic.csv')\n", + "res4.loc[:, 'experiment'] = res4.apply(lambda x: re.sub(r'eval_stanza_morph_extended_full_data',\\\n", + " 'eval_stanza_morph_extended_full_data (EDT_2.11)',\n", + " x['experiment']), axis=1)\n", + "# ME ensemble results with aggregation_algorithm = 'majority_voting'\n", + "res5 = read_csv('edt_2.6/results_ensemble_majority_voting.csv')\n", + "res5 = res5.loc[res5['experiment'].str.contains('full_data_ensemble_majority_voting')]\n", + "# MA ensemble results with default aggregation_algorithm\n", + "res6 = read_csv('edt_2.6/results_stanza_MA_ensembles.csv')\n", + "res6 = res6.loc[res6['experiment'].str.contains('eval_stanza_ensemble_morph_analysis_full_data_default')]\n", + "# Results on UD gold\n", + "res7 = read_csv('edt_2.6/results_gold_and_auto_ud_morph.csv')\n", + "res7 = res7.loc[res7['experiment'].str.contains('ud_gold')]\n", + "composite_res = pandas.concat([res1, res2x, res2, res3, res4, res5, res6, res7])\n", + "composite_res = composite_res.drop(['test_words', 'train_words'], axis=1)\n", + "composite_res" + ] + }, + { + "cell_type": "markdown", + "id": "44cac810", + "metadata": {}, + "source": [ + "### Hoeffding's bounds" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "e61a80a3", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "\n", + "# Calculates Hoeffding's bound. \n", + "# Parameters:\n", + "# n -- evaluation set size in words\n", + "# alpha -- confidence level is (1 - alpha)\n", + "def hoeffding_bounds(n, alpha):\n", + " return math.sqrt( (1/(2*n)*math.log(2/(alpha))) )" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "27062c90", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.006167393570927968" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# bounds for test set\n", + "hfb_test = hoeffding_bounds(n=48491, alpha=0.05)\n", + "hfb_test" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "1b449363", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.0021767186774462486" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# bounds for train set\n", + "hfb_train = hoeffding_bounds(n=389278, alpha=0.05)\n", + "hfb_train" + ] + }, + { + "cell_type": "markdown", + "id": "fe8720c6", + "metadata": {}, + "source": [ + "#### Table 1" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "640af725", + "metadata": {}, + "outputs": [], + "source": [ + "hfb_res = composite_res.copy()\n", + "hfb_res = hfb_res.drop(['UAS_test', 'UAS_train'], axis=1)\n", + "hfb_res.loc[:, 'LAS_test_lower'] = hfb_res.apply(lambda x: round(x['LAS_test'] - hfb_test, 4), axis=1)\n", + "hfb_res.loc[:, 'LAS_test_upper'] = hfb_res.apply(lambda x: round(x['LAS_test'] + hfb_test, 4), axis=1)\n", + "hfb_res.loc[:, 'LAS_train_lower'] = hfb_res.apply(lambda x: round(x['LAS_train'] - hfb_train, 4), axis=1)\n", + "hfb_res.loc[:, 'LAS_train_upper'] = hfb_res.apply(lambda x: round(x['LAS_train'] + hfb_train, 4), axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "529e2f74", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_test_lowerLAS_testLAS_test_upperLAS_train_lowerLAS_trainLAS_train_upperLAS_gap
0eval_malt_morph_extended_full0.71240.71860.72480.84240.84460.84680.1260
1eval_udpipe1_default_morph_extended0.74980.75600.76220.84790.85010.85230.0941
2eval_udpipe1_embeddings_morph_extended0.76510.77130.77750.91250.91470.91690.1434
0eval_stanza_ud_on_stanza_auto_morph_full_data0.84570.85190.85810.92690.92910.93130.0772
8eval_stanza_morph_extended_gap_experiments_08_only_wordforms0.83450.84070.84690.91800.92020.92240.0795
0eval_stanza_morph_analysis_full_data0.84450.85070.85690.92090.92310.92530.0724
1eval_stanza_morph_extended_full_data (EDT_2.6)0.84240.84860.85480.91540.91760.91980.0689
0eval_stanza_morph_extended_full_data (EDT_2.11)0.84220.84840.85460.92250.92470.92690.0764
0eval_stanza_morph_extended_full_data_ensemble_majority_voting0.85020.85640.86260.93150.93370.93590.0773
0eval_stanza_ensemble_morph_analysis_full_data_default0.85110.85730.86350.93080.93300.93520.0757
1eval_stanza_ud_gold_morph_full_data0.87480.88100.88720.93580.93800.94020.0570
\n", + "
" + ], + "text/plain": [ + " experiment \\\n", + "0 eval_malt_morph_extended_full \n", + "1 eval_udpipe1_default_morph_extended \n", + "2 eval_udpipe1_embeddings_morph_extended \n", + "0 eval_stanza_ud_on_stanza_auto_morph_full_data \n", + "8 eval_stanza_morph_extended_gap_experiments_08_only_wordforms \n", + "0 eval_stanza_morph_analysis_full_data \n", + "1 eval_stanza_morph_extended_full_data (EDT_2.6) \n", + "0 eval_stanza_morph_extended_full_data (EDT_2.11) \n", + "0 eval_stanza_morph_extended_full_data_ensemble_majority_voting \n", + "0 eval_stanza_ensemble_morph_analysis_full_data_default \n", + "1 eval_stanza_ud_gold_morph_full_data \n", + "\n", + " LAS_test_lower LAS_test LAS_test_upper LAS_train_lower LAS_train \\\n", + "0 0.7124 0.7186 0.7248 0.8424 0.8446 \n", + "1 0.7498 0.7560 0.7622 0.8479 0.8501 \n", + "2 0.7651 0.7713 0.7775 0.9125 0.9147 \n", + "0 0.8457 0.8519 0.8581 0.9269 0.9291 \n", + "8 0.8345 0.8407 0.8469 0.9180 0.9202 \n", + "0 0.8445 0.8507 0.8569 0.9209 0.9231 \n", + "1 0.8424 0.8486 0.8548 0.9154 0.9176 \n", + "0 0.8422 0.8484 0.8546 0.9225 0.9247 \n", + "0 0.8502 0.8564 0.8626 0.9315 0.9337 \n", + "0 0.8511 0.8573 0.8635 0.9308 0.9330 \n", + "1 0.8748 0.8810 0.8872 0.9358 0.9380 \n", + "\n", + " LAS_train_upper LAS_gap \n", + "0 0.8468 0.1260 \n", + "1 0.8523 0.0941 \n", + "2 0.9169 0.1434 \n", + "0 0.9313 0.0772 \n", + "8 0.9224 0.0795 \n", + "0 0.9253 0.0724 \n", + "1 0.9198 0.0689 \n", + "0 0.9269 0.0764 \n", + "0 0.9359 0.0773 \n", + "0 0.9352 0.0757 \n", + "1 0.9402 0.0570 " + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hfb_res[['experiment', 'LAS_test_lower', 'LAS_test', 'LAS_test_upper', 'LAS_train_lower', 'LAS_train', 'LAS_train_upper', 'LAS_gap']]" + ] + }, + { + "cell_type": "markdown", + "id": "d6711679", + "metadata": {}, + "source": [ + "#### Table 3" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "253f564c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainUAS_testUAS_trainLAS_gap
0eval_stanza_morph_extended_full_data_conf_intervals0.8401; 0.8486; 0.85720.9161; 0.9176; 0.91910.8697; 0.8782; 0.88670.9363; 0.9378; 0.93930.0760; 0.0689; 0.0619
1eval_stanza_ensemble_morph_extended_full_data_las_coherence_conf_intervals0.8488; 0.8568; 0.86490.9327; 0.9337; 0.93470.8774; 0.8852; 0.89290.9505; 0.9515; 0.95250.0839; 0.0769; 0.0698
2eval_stanza_ensemble_morph_extended_full_data_majority_voting_conf_intervals0.8487; 0.8564; 0.86410.9327; 0.9337; 0.93470.8775; 0.8849; 0.89240.9507; 0.9517; 0.95270.0840; 0.0773; 0.0707
3eval_stanza_ensemble_morph_extended_half_data_las_coherence_conf_intervals0.8383; 0.8447; 0.85100.9134; 0.9152; 0.91690.8699; 0.8761; 0.88220.9325; 0.9344; 0.93630.0751; 0.0705; 0.0659
4eval_stanza_ensemble_morph_extended_half_data_majority_voting_conf_intervals0.8386; 0.8449; 0.85120.9109; 0.9126; 0.91430.8699; 0.8762; 0.88260.9306; 0.9325; 0.93440.0722; 0.0677; 0.0631
\n", + "
" + ], + "text/plain": [ + " experiment \\\n", + "0 eval_stanza_morph_extended_full_data_conf_intervals \n", + "1 eval_stanza_ensemble_morph_extended_full_data_las_coherence_conf_intervals \n", + "2 eval_stanza_ensemble_morph_extended_full_data_majority_voting_conf_intervals \n", + "3 eval_stanza_ensemble_morph_extended_half_data_las_coherence_conf_intervals \n", + "4 eval_stanza_ensemble_morph_extended_half_data_majority_voting_conf_intervals \n", + "\n", + " LAS_test LAS_train UAS_test \\\n", + "0 0.8401; 0.8486; 0.8572 0.9161; 0.9176; 0.9191 0.8697; 0.8782; 0.8867 \n", + "1 0.8488; 0.8568; 0.8649 0.9327; 0.9337; 0.9347 0.8774; 0.8852; 0.8929 \n", + "2 0.8487; 0.8564; 0.8641 0.9327; 0.9337; 0.9347 0.8775; 0.8849; 0.8924 \n", + "3 0.8383; 0.8447; 0.8510 0.9134; 0.9152; 0.9169 0.8699; 0.8761; 0.8822 \n", + "4 0.8386; 0.8449; 0.8512 0.9109; 0.9126; 0.9143 0.8699; 0.8762; 0.8826 \n", + "\n", + " UAS_train LAS_gap \n", + "0 0.9363; 0.9378; 0.9393 0.0760; 0.0689; 0.0619 \n", + "1 0.9505; 0.9515; 0.9525 0.0839; 0.0769; 0.0698 \n", + "2 0.9507; 0.9517; 0.9527 0.0840; 0.0773; 0.0707 \n", + "3 0.9325; 0.9344; 0.9363 0.0751; 0.0705; 0.0659 \n", + "4 0.9306; 0.9325; 0.9344 0.0722; 0.0677; 0.0631 " + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ensemble_conf_intervals = read_csv('edt_2.6/results_ensemble_conf_intervals.csv')\n", + "ensemble_conf_intervals" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "b5f79a20", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "eval_stanza_morph_extended_full_data_conf_intervals | 0.8401 0.8486 0.8572 | -0.0085 | +0.0086\n", + "eval_stanza_ensemble_morph_extended_full_data_las_coherence_conf_intervals | 0.8488 0.8568 0.8649 | -0.0080 | +0.0081\n", + "eval_stanza_ensemble_morph_extended_full_data_majority_voting_conf_intervals | 0.8487 0.8564 0.8641 | -0.0077 | +0.0077\n", + "eval_stanza_ensemble_morph_extended_half_data_las_coherence_conf_intervals | 0.8383 0.8447 0.8510 | -0.0064 | +0.0063\n", + "eval_stanza_ensemble_morph_extended_half_data_majority_voting_conf_intervals | 0.8386 0.8449 0.8512 | -0.0063 | +0.0063\n" + ] + } + ], + "source": [ + "# Decompose test LAS conf bounds\n", + "for i in ensemble_conf_intervals.index:\n", + " exp = ensemble_conf_intervals['experiment'][i]\n", + " parts = ensemble_conf_intervals['LAS_test'][i].split(';')\n", + " low, mid, high = [float(part.strip()) for part in parts]\n", + " out = f'{exp:77s} | {low:.4f} {mid:.4f} {high:.4f} | -{round(mid-low, 4):.4f} | +{round(high-mid,4):.4f}'\n", + " print(out)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "12a0869e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testtest_LAS_0_entropytest_LAS_0_entropy_total_words_%test_LAS_vs_entropy_corr
0eval_stanza_ensemble_morph_extended_full_data_las_coherence_entropy0.85680.925682.22140.4431
1eval_stanza_ensemble_morph_extended_full_data_majority_voting_entropy0.85640.925682.22140.4465
2eval_stanza_ensemble_morph_extended_half_data_las_coherence_entropy0.84460.940573.57860.4760
3eval_stanza_ensemble_morph_extended_half_data_majority_voting_entropy0.84490.940573.57860.4777
\n", + "
" + ], + "text/plain": [ + " experiment \\\n", + "0 eval_stanza_ensemble_morph_extended_full_data_las_coherence_entropy \n", + "1 eval_stanza_ensemble_morph_extended_full_data_majority_voting_entropy \n", + "2 eval_stanza_ensemble_morph_extended_half_data_las_coherence_entropy \n", + "3 eval_stanza_ensemble_morph_extended_half_data_majority_voting_entropy \n", + "\n", + " LAS_test test_LAS_0_entropy test_LAS_0_entropy_total_words_% \\\n", + "0 0.8568 0.9256 82.2214 \n", + "1 0.8564 0.9256 82.2214 \n", + "2 0.8446 0.9405 73.5786 \n", + "3 0.8449 0.9405 73.5786 \n", + "\n", + " test_LAS_vs_entropy_corr \n", + "0 0.4431 \n", + "1 0.4465 \n", + "2 0.4760 \n", + "3 0.4777 " + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ensemble_entropy = read_csv('edt_2.6/results_ensemble_entropy.csv')\n", + "ensemble_entropy[['experiment', 'LAS_test', 'test_LAS_0_entropy', 'test_LAS_0_entropy_total_words_%', 'test_LAS_vs_entropy_corr']]" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.17" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/07_smaller_data_exp_and_extrapolation.ipynb b/07_smaller_data_exp_and_extrapolation.ipynb new file mode 100644 index 00000000..9d0a7e15 --- /dev/null +++ b/07_smaller_data_exp_and_extrapolation.ipynb @@ -0,0 +1,1138 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0414f820", + "metadata": {}, + "source": [ + "## Draw figures about results of smaller data experiments" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "d31710fd", + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "from pandas import DataFrame as df\n", + "from pandas import read_csv\n", + "import pandas\n", + "from plotnine import ggplot, geom_point, aes, geom_line, ylab\n", + "from plotnine import geom_abline, geom_hline, ggtitle, theme, theme_bw" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "8a97e325", + "metadata": {}, + "outputs": [], + "source": [ + "df = read_csv('edt_2.6/results_smaller_data.csv') \\\n", + " .rename(columns={'LAS_test': 'test LAS'}) \\\n", + " .rename(columns={'LAS_train': 'train LAS'}) \\\n", + " .drop(['LAS_gap', 'UAS_test', 'UAS_train'], axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "11e86a5e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimenttest LAStrain LAStest_wordstrain_words
0eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0100.68640.94914849139886
1eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0200.74900.96364849177759
2eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0300.78190.936548491117642
3eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0400.79890.936648491157491
4eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0500.81520.923448491195477
5eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0600.82500.929948491235468
6eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0700.82410.917948491273422
7eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0800.83270.919448491311383
8eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_0900.83430.915148491349322
9eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_1000.83770.910448491389278
10eval_stanza_morph_extended_smaller_data_02_keep_all_0100.75090.92864849139886
11eval_stanza_morph_extended_smaller_data_02_keep_all_0200.78950.95524849177759
12eval_stanza_morph_extended_smaller_data_02_keep_all_0300.80620.926848491117642
13eval_stanza_morph_extended_smaller_data_02_keep_all_0400.82090.941448491157491
14eval_stanza_morph_extended_smaller_data_02_keep_all_0500.82990.936048491195477
15eval_stanza_morph_extended_smaller_data_02_keep_all_0600.84090.935048491235468
16eval_stanza_morph_extended_smaller_data_02_keep_all_0700.83630.924848491273422
17eval_stanza_morph_extended_smaller_data_02_keep_all_0800.84350.929548491311383
18eval_stanza_morph_extended_smaller_data_02_keep_all_0900.84630.926148491349322
19eval_stanza_morph_extended_smaller_data_02_keep_all_1000.84660.923648491389278
20eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0100.62850.81744849139886
21eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0200.65440.85424849177759
22eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0300.66520.784748491117642
23eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0400.67300.788948491157491
24eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0500.68020.787848491195477
25eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0600.68800.812448491235468
26eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0700.69150.800048491273422
27eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0800.69390.800148491311383
28eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_0900.69520.808148491349322
29eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_1000.69700.787448491389278
\n", + "
" + ], + "text/plain": [ + " experiment \n", + "0 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_010 \\\n", + "1 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_020 \n", + "2 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_030 \n", + "3 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_040 \n", + "4 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_050 \n", + "5 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_060 \n", + "6 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_070 \n", + "7 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_080 \n", + "8 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_090 \n", + "9 eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_100 \n", + "10 eval_stanza_morph_extended_smaller_data_02_keep_all_010 \n", + "11 eval_stanza_morph_extended_smaller_data_02_keep_all_020 \n", + "12 eval_stanza_morph_extended_smaller_data_02_keep_all_030 \n", + "13 eval_stanza_morph_extended_smaller_data_02_keep_all_040 \n", + "14 eval_stanza_morph_extended_smaller_data_02_keep_all_050 \n", + "15 eval_stanza_morph_extended_smaller_data_02_keep_all_060 \n", + "16 eval_stanza_morph_extended_smaller_data_02_keep_all_070 \n", + "17 eval_stanza_morph_extended_smaller_data_02_keep_all_080 \n", + "18 eval_stanza_morph_extended_smaller_data_02_keep_all_090 \n", + "19 eval_stanza_morph_extended_smaller_data_02_keep_all_100 \n", + "20 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_010 \n", + "21 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_020 \n", + "22 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_030 \n", + "23 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_040 \n", + "24 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_050 \n", + "25 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_060 \n", + "26 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_070 \n", + "27 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_080 \n", + "28 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_090 \n", + "29 eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_100 \n", + "\n", + " test LAS train LAS test_words train_words \n", + "0 0.6864 0.9491 48491 39886 \n", + "1 0.7490 0.9636 48491 77759 \n", + "2 0.7819 0.9365 48491 117642 \n", + "3 0.7989 0.9366 48491 157491 \n", + "4 0.8152 0.9234 48491 195477 \n", + "5 0.8250 0.9299 48491 235468 \n", + "6 0.8241 0.9179 48491 273422 \n", + "7 0.8327 0.9194 48491 311383 \n", + "8 0.8343 0.9151 48491 349322 \n", + "9 0.8377 0.9104 48491 389278 \n", + "10 0.7509 0.9286 48491 39886 \n", + "11 0.7895 0.9552 48491 77759 \n", + "12 0.8062 0.9268 48491 117642 \n", + "13 0.8209 0.9414 48491 157491 \n", + "14 0.8299 0.9360 48491 195477 \n", + "15 0.8409 0.9350 48491 235468 \n", + "16 0.8363 0.9248 48491 273422 \n", + "17 0.8435 0.9295 48491 311383 \n", + "18 0.8463 0.9261 48491 349322 \n", + "19 0.8466 0.9236 48491 389278 \n", + "20 0.6285 0.8174 48491 39886 \n", + "21 0.6544 0.8542 48491 77759 \n", + "22 0.6652 0.7847 48491 117642 \n", + "23 0.6730 0.7889 48491 157491 \n", + "24 0.6802 0.7878 48491 195477 \n", + "25 0.6880 0.8124 48491 235468 \n", + "26 0.6915 0.8000 48491 273422 \n", + "27 0.6939 0.8001 48491 311383 \n", + "28 0.6952 0.8081 48491 349322 \n", + "29 0.6970 0.7874 48491 389278 " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pandas.set_option('display.max_colwidth', None)\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6c75808d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
test LAStrain LAStest_wordstrain_wordsmodeltraining size
068.6494.914849139886stanza ME only wordforms10
174.9096.364849177759stanza ME only wordforms20
278.1993.6548491117642stanza ME only wordforms30
379.8993.6648491157491stanza ME only wordforms40
481.5292.3448491195477stanza ME only wordforms50
582.5092.9948491235468stanza ME only wordforms60
682.4191.7948491273422stanza ME only wordforms70
783.2791.9448491311383stanza ME only wordforms80
883.4391.5148491349322stanza ME only wordforms90
983.7791.0448491389278stanza ME only wordforms100
1075.0992.864849139886stanza ME all features10
1178.9595.524849177759stanza ME all features20
1280.6292.6848491117642stanza ME all features30
1382.0994.1448491157491stanza ME all features40
1482.9993.6048491195477stanza ME all features50
1584.0993.5048491235468stanza ME all features60
1683.6392.4848491273422stanza ME all features70
1784.3592.9548491311383stanza ME all features80
1884.6392.6148491349322stanza ME all features90
1984.6692.3648491389278stanza ME all features100
2062.8581.744849139886stanza ME only postags and feats10
2165.4485.424849177759stanza ME only postags and feats20
2266.5278.4748491117642stanza ME only postags and feats30
2367.3078.8948491157491stanza ME only postags and feats40
2468.0278.7848491195477stanza ME only postags and feats50
2568.8081.2448491235468stanza ME only postags and feats60
2669.1580.0048491273422stanza ME only postags and feats70
2769.3980.0148491311383stanza ME only postags and feats80
2869.5280.8148491349322stanza ME only postags and feats90
2969.7078.7448491389278stanza ME only postags and feats100
\n", + "
" + ], + "text/plain": [ + " test LAS train LAS test_words train_words \n", + "0 68.64 94.91 48491 39886 \\\n", + "1 74.90 96.36 48491 77759 \n", + "2 78.19 93.65 48491 117642 \n", + "3 79.89 93.66 48491 157491 \n", + "4 81.52 92.34 48491 195477 \n", + "5 82.50 92.99 48491 235468 \n", + "6 82.41 91.79 48491 273422 \n", + "7 83.27 91.94 48491 311383 \n", + "8 83.43 91.51 48491 349322 \n", + "9 83.77 91.04 48491 389278 \n", + "10 75.09 92.86 48491 39886 \n", + "11 78.95 95.52 48491 77759 \n", + "12 80.62 92.68 48491 117642 \n", + "13 82.09 94.14 48491 157491 \n", + "14 82.99 93.60 48491 195477 \n", + "15 84.09 93.50 48491 235468 \n", + "16 83.63 92.48 48491 273422 \n", + "17 84.35 92.95 48491 311383 \n", + "18 84.63 92.61 48491 349322 \n", + "19 84.66 92.36 48491 389278 \n", + "20 62.85 81.74 48491 39886 \n", + "21 65.44 85.42 48491 77759 \n", + "22 66.52 78.47 48491 117642 \n", + "23 67.30 78.89 48491 157491 \n", + "24 68.02 78.78 48491 195477 \n", + "25 68.80 81.24 48491 235468 \n", + "26 69.15 80.00 48491 273422 \n", + "27 69.39 80.01 48491 311383 \n", + "28 69.52 80.81 48491 349322 \n", + "29 69.70 78.74 48491 389278 \n", + "\n", + " model training size \n", + "0 stanza ME only wordforms 10 \n", + "1 stanza ME only wordforms 20 \n", + "2 stanza ME only wordforms 30 \n", + "3 stanza ME only wordforms 40 \n", + "4 stanza ME only wordforms 50 \n", + "5 stanza ME only wordforms 60 \n", + "6 stanza ME only wordforms 70 \n", + "7 stanza ME only wordforms 80 \n", + "8 stanza ME only wordforms 90 \n", + "9 stanza ME only wordforms 100 \n", + "10 stanza ME all features 10 \n", + "11 stanza ME all features 20 \n", + "12 stanza ME all features 30 \n", + "13 stanza ME all features 40 \n", + "14 stanza ME all features 50 \n", + "15 stanza ME all features 60 \n", + "16 stanza ME all features 70 \n", + "17 stanza ME all features 80 \n", + "18 stanza ME all features 90 \n", + "19 stanza ME all features 100 \n", + "20 stanza ME only postags and feats 10 \n", + "21 stanza ME only postags and feats 20 \n", + "22 stanza ME only postags and feats 30 \n", + "23 stanza ME only postags and feats 40 \n", + "24 stanza ME only postags and feats 50 \n", + "25 stanza ME only postags and feats 60 \n", + "26 stanza ME only postags and feats 70 \n", + "27 stanza ME only postags and feats 80 \n", + "28 stanza ME only postags and feats 90 \n", + "29 stanza ME only postags and feats 100 " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.loc[:, 'model'] = df.apply(lambda x: x['experiment'].replace('eval_stanza_morph_extended_smaller_data_', 'stanza_ME_'), axis=1)\n", + "df.loc[:, 'model'] = df.apply(lambda x: re.sub(r'_[0-9]+$','',x['model']), axis=1)\n", + "df.loc[:, 'model'] = df.apply(lambda x: re.sub(r'_09_only_pos_feats$','',x['model']), axis=1)\n", + "df.loc[:, 'model'] = df.apply(lambda x: re.sub(r'_08','',x['model']), axis=1)\n", + "# Rename models\n", + "df.loc[:, 'model'] = df.apply(lambda x: re.sub(r'stanza_ME_01_only_wordforms','stanza ME only wordforms',x['model']), axis=1)\n", + "df.loc[:, 'model'] = df.apply(lambda x: re.sub(r'stanza_ME_02_keep_all', 'stanza ME all features',x['model']), axis=1)\n", + "df.loc[:, 'model'] = df.apply(lambda x: re.sub(r'stanza_ME_03_only_pos_feats','stanza ME only postags and feats',x['model']), axis=1)\n", + "df.loc[:, 'training size'] = df.apply(lambda x: int((x['experiment'].split('_')[-1]).lstrip('0')), axis=1) \n", + "df = df.drop(['experiment'], axis=1)\n", + "# Normalize LAS scores\n", + "df['test LAS'] = df['test LAS'].multiply(100.0)\n", + "df['train LAS'] = df['train LAS'].multiply(100.0)\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "95e64940", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAokAAAIFCAYAAAC+pWeCAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACHx0lEQVR4nOzdd3gU5doG8Ht7TbKbnkBCJ3QJICIqICJKE6XaQUGsR/F4UDjoEftnL9hAPaIeUVFAEcQCgorSm/TeIb1uts+83x9JFpZQAiSZ3ez948pFdmZ29pl5dzb3vtNUQggBIiIiIqITqJUugIiIiIhCD0MiEREREVXBkEhEREREVTAkEhEREVEVDIlEREREVAVDIhERERFVwZBIRERERFUwJBIRERFRFQyJRERERFQFQyIRERERVcGQSERERERVMCQSERERURUMiURERERUBUMiEREREVXBkEhEREREVTAkEhEREVEVDIlEREREVAVDIhERERFVwZBIRERERFUwJBIRERFRFQyJRERERFQFQyIRERERVcGQSERERERVMCQSERERURUMiURERERUBUMiEREREVXBkEhEREREVTAkEhEREVEVDIlEREREVAVDIhERERFVEREhsWPHjhg9evQ5PWf//v1QqVT45ptvaqcoIiIiohAWESGRiIiIiM4NQyIRERERVVHnIXH06NFo164dFi1ahA4dOsBkMqFnz57Yv38/CgoKMGLECERHR6NZs2b46quvgp47bdo0ZGRkwGAwoHHjxnj22Wchy3LQNH/99Rc6d+4Mo9GIdu3aYeHChaesY/ny5ejduzcsFgtiYmJw8803Iycnp9aWm4iIiCicKNKTmJWVhUceeQSTJ0/G559/jj179uCWW27ByJEj0b59e8yePRudO3fGrbfeigMHDgAApk6dinvuuQfXXHMNvv/+e4wePRpTpkzBo48+GjTfa665BgaDAbNmzcKECRNw77334siRI0Gvv3z5cvTq1QsxMTH46quvMH36dKxevRqDBw+u0/VAREREFLJEHRs1apRQqVRi8+bNgWFTp04VAMRjjz0WGFZYWCg0Go144403hN/vF/Hx8eLGG28MmtekSZOEXq8XeXl5QgghHnvsMREVFSWKiooC0yxevFgAEKNGjQoM69Gjh+jevbuQZTkwbMuWLUKlUokFCxYIIYTYt2+fACC+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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Programmid\\Miniconda3\\envs\\py38_estnltk_neural\\lib\\site-packages\\plotnine\\ggplot.py:718: PlotnineWarning: Saving 10 x 8 in image.\n", + "C:\\Programmid\\Miniconda3\\envs\\py38_estnltk_neural\\lib\\site-packages\\plotnine\\ggplot.py:719: PlotnineWarning: Filename: figure_1_test.pdf\n" + ] + } + ], + "source": [ + "p = ggplot(df) + theme_bw() + geom_point(aes(x='training size', y='test LAS', color='model'))\n", + "p = p + geom_line(aes(x='training size', y='test LAS', color='model')) + theme(legend_position='top')\n", + "display(p)\n", + "p.save('figure_1_test.pdf', height=8, width=10)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c4457cc7", + "metadata": {}, + "outputs": [], + "source": [ + "# Separate test and train for results visualization\n", + "train_df = df.drop(['test LAS'], axis=1)\n", + "train_df.loc[:, 'eval set'] = df.apply(lambda x: 'train', axis=1)\n", + "train_df = train_df.rename(columns={'train LAS': 'LAS'})\n", + "test_df = df.drop(['train LAS'], axis=1)\n", + "test_df.loc[:, 'eval set'] = df.apply(lambda x: 'test', axis=1)\n", + "test_df = test_df.rename(columns={'test LAS': 'LAS'})\n", + "#joint_df = pandas.concat([train_df, test_df])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e0de7753", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAokAAAORCAYAAACeJThjAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAADQ3klEQVR4nOzdd3xb1d0G8OdeXW3ZlrxHYmfvvQgJSZhhb8LelFlKS1mlQNsXaNMCBcookDBbwmxooS2zrEBIyA5JIIMMO4n3trbuvef9Q7JsR9mWLVt6vvn4o3V19ZOOx5NzzzlXEkIIEBERERG1Iye6ACIiIiLqeRgSiYiIiCgGQyIRERERxWBIJCIiIqIYDIlEREREFIMhkYiIiIhiMCQSERERUQyGRCIiIiKKoSS6AOqosbERXq830WUQERH1OjabDU6nM9FlJA2GxB6ksbERTz31FFRVTXQpREREvY6iKLj55psZFOOEIbEH8Xq9UFUV48ePh8PhSHQ5REREvYbb7cbq1avh9XoZEuOEIbEHcjgc/AYnIiKihOLEFSIiIiKKwZBIRERERDEYEoko5f3kJz/BypUrO72fM844A7t27YpDRUREiceQSEREREQxOHGFiFLaww8/jJqaGsydOxeyLOP000/Hcccdh3nz5mHLli2w2+0466yzcMoppwAAtmzZgueeew47d+6E0WjE+PHjcdttt+HOO+8EANx6662QJAlXX301TjrppES+NSKiTmFIJKKUdscdd2DTpk248cYbMXHiRAQCAdx4440477zzcO+996KyshK/+c1vUFBQgPHjx2PevHmYMmUKHnroIaiqih9//BEA8NBDD+GMM87AY489hj59+iT4XRERdR4PNxMRtbN8+XJkZmbilFNOgaIo6NOnD2bPno1FixYBCC/WW11djfr6ephMJowYMSLBFRMRdQ32JBIRtVNVVYVt27bhoosuit6n63o0DN5yyy147bXXcOuttyI9PR1nnXUWTjjhhESVS0TUZRgSiYjaycnJwbBhw/CHP/xhr48XFBTgtttugxAC69evx29/+1uMHDkShYWF3VwpEVHX4uFmIkp5TqcTlZWVAIDJkyejuroaH3/8MUKhEDRNw44dO7BlyxYAwGeffYbGxkZIkgS73Q4AkGU5up+KiorEvAkiojhjTyIRpbzzzjsP8+fPx6uvvopTTz0V999/P1588UX8/e9/h6qq6NOnDy699FIAwJo1a/DSSy8hEAjA5XLhhhtuQH5+PgDg4osvxlNPPYVgMIgrr7wSJ554YiLfFhFRp0hCCJHoIiisvLwc8+bNw4wZM3juZiIiokPQ2NiIr776Ctdddx2Hf8QJDzcTERERUQyGRCIiIiKKwZBIRERERDEYEomIiIgoBkMiEREREcXgEjg9kNvtTnQJREREvQr/dsYfQ2IPoqoqAGD16tUJroSIiKh3av1bSp3HkNiDKEq4OY455hi4XK6E1GAwGJCWloaWlhZompaQGii+2KbJie2afNimndPQ0IDPP/88+reUOo+fZA80ePDghC0EKoSAqqro27cvJElKSA3dRdd1VFZWIj8/P3patWTENk1OqdKubNPk1BXtWl5ejs8//zwu+6Kw5P6JIyIiIqLDwpBIRERERDEYEomIiIgoBkMiEREREcVgSCQiIiKiGAyJRERERBSDIZGIiIiIYjAkEhEREVEMhkQiIiIiisGQSEREREQxGBKJiIiIKAZDIhERERHFYEgkIiIiohgMiUREREQUgyGRiIiIiGIwJBIRERFRDIZEIiIiIorBkEhEREREMRgSiYiIiCgGQyIRERERxWBIJCIiIqIYDIlEREREFIMhkYiIiIhiMCQSERERUQyGRCIiIiKKwZBIRERERDEYEomIiIgohpLoAqgjh8MBRVEghEjI67e+bqJevzsJIaKfdTK/X7ZpckqVdmWbJqeuaFdFYaSJN36iPcz48ePhcrmgqmpC69A0LaGv311cLhd0XYeu64kupcuxTZNTKrQr2zQ5xbtdXS5XXPZDbRgSe5jVq1dj9OjRyMnJScjrCyGgaRoMBgMkSUpIDd1F13XU1dUhKysLspy8Iy/YpskpVdqVbZqcuqJda2pq4rIfasOQ2MO43W6oqprwXxCSJCW8hq4mSVL0s0729wqwTZNVsr9Xtmly6op2TfQRuGSU3P8tIyIiIqLDwpBIRERERDEYEomIiIgoBkMiEREREcVgSCQiIiKiGAyJRERERBSDIZGIiIiIYjAkEhEREVEMhkQiIiIiisGQSEREREQxGBKJiIiIKAZDIhERERHFYEgkIiIiohgMiUREREQUgyGRiIiIiGIwJBIRERFRDIZEIiIiIorBkEhEREREMRgSiYiIiCgGQyIRERERxWBIJCIiIqIY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RCTDkEhEREREMgyJRERERCTDkEhEREREMgyJRERERCTDkEhEREREMgyJRERERCTDkEhEREREMgyJRERERCTDkEhEREREMgyJRERERCTDkEhEREREMgyJRERERCTDkEhEREREMgyJRERERCTDkEhEREREMgyJRERERCTDkEhEREREMgyJRERERCTDkEhEREREMgyJRERERCTDkEhEREREMgyJRERERCTDkEhEREREMgyJRERERCTDkEhEREREMgyJRERERCTDkEhEREREMgyJRERERCTDkEhEREREMipzF1CVDRkyxOS1TqdDq1atMGPGDABAVFQUlixZgsjISHh4eOCFF15A06ZNzVEqERERUakwJD6GzZs3S/82GAwYO3YsnnjiCQCAXq/H7Nmz0b17d8ybNw/Hjx/HvHnzsGLFCmi1WjNVTERERFQyPN1cRv744w/k5OQgNDQUAHDu3Dnk5uZi0KBBsLS0RMeOHeHv749ff/3VzJUSERERPRxDYhk5cOAAOnbsCGtrawDAjRs3EBgYCAuL+5u4du3aiIqKMleJRERERCXG081lIC0tDSdOnMC8efOkYdnZ2bC3tzcZz97eHgkJCSbDkpKSkJSUBABITEyEwWAAABiNxnKuunBCCBiNRhiNRigUCrPUUFHyt7G5tnVFYZtWTzWlXdmm1VNNateqjCGxDBw+fBheXl4ICgqShtna2iIzM9NkvMzMTNja2poM27p1Kz7//HPp9TPPPAMAiIuLK8eKqaAHgztVfWzT6odtWj2xXSs3hsQycODAAXTt2tVkmL+/P7Zu3Qqj0Sidcr5+/To6depkMt7AgQPRuXNnAHePJO7btw8A4OnpWQGVywkhoNfroVKpasRfsgkJCXB3dze5LKC6YZtWTzWlXdmm1VN5tCsPrpQ9hsTHdO3aNdy4cQNhYWEmw0NCQmBlZYVt27ahX79++P333xEVFSXd/ZzP1dUVrq6uAABHR0colUoAMNuHoRACFhYWsLCwqPYfUvny17e6YptWTzWtXdmm1VNNaNeqjCHxMf30009o1aoVnJycTIarVCrMmDEDn332Gb755hu4u7tj2rRpfPwNERERVQkMiY9pwoQJRb4XGBiIhQsXVmA1RERERGWDx3iJiIiISIYhkYiIiIhkGBKJiIiISIYhkYiIiIhkGBKJiIiISIYhkYiIiIhkGBKJ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VatWqZebryQhseD7L774onjrrbdM5hEeHi7WrVsnMjIyhK2trVixYoVITU0tcp0eVDD8leXPg7iP3jd27FgxadIk6bXBYBBeXl7ip59+kuZVMKxs3bpVNGrUyGTd8/eLTz75RPTp00cIIURubq5wcXERJ06cKHS5M2fOFC1atJCt//r160V0dLRQKBRSYBRCiOXLl4sOHToIIYR49913Rfv27cWff/4pm29JfskW3M5dunQRS5Yskd47d+6cSUj09/cX7777rkhMTCx2ng8qrM8VtR3XrVtX6LYoaUjctGmTaNOmjck477//vhgzZkyh0xe2/xbs22vWrBH16tUTx44dEwaD4eErK4RJ+CvLnwex795nrr47e/ZsMXbsWCGEEL6+vmLNmjVi6NC7WaB+/frS9qxTp47YsWOHNN3FixeFUqkUubm50vILHvh47733xIABA6TXer1euLq6FhkS169fL5o1a2ZS29ChQ8WMGTOEEHc/Yx7cH0aNGiX9XsvfJvb29kKv10vDrKysxLlz54QQj97/y0qlO91cFurUqYN169Zh8eLF8PDwQO/evYu9KPrKlSuIiIiAp6cnHB0dMW3aNCQlJZmM4+npKf3bzs4OGRkZ0uvMzEz07dsXL7/8MgYMGCAN/+qrr9CsWTNotVpotVqcO3dONt8HWVlZYejQofjkk0/w22+/oX///oWO98knnyAlJUX6Kepu46ysLEyYMAEBAQFwdHREx44dkZGRYXJY/1FERkbCYDDAz89PWr+IiAgkJCSU63IDAgJMaliyZIm0fK1Wi+PHjyM2Nhb29vb4/vvvsX37dvj5+aFz5844duzYYy27LHEfvS8mJgaBgYHSawsLC/j7+yMmJqZE61bQ8OHDceDAASQmJmLXrl1wd3dH69ati1wXf39/k9cBAQGIiYlBTEwMNBoNnJycpPcCAwOlmiZPnowOHTpg4MCB8Pb2xltvvYW8vLwil1Pcdo6NjYWfn580bsF/A8D27dtx7tw51KtXD61btzY5nVVQSfpcUdsxNja20G1RUpGRkThz5oxJX5w/fz7i4uIAlGz/LWjEiBEYNWoUXnjhBbi7u+OFF15AWlpaiespT+y795mr74aFheHQoUO4cuUKnJycMGDAABw5cgSxsbG4ceOGdCPOg/UFBgbCYDBI+yVgup8/2A+USiV8fHwKraGw+ecvo+D6F9aPHtwmrq6uUCqV0jBbW1tpO5W0/5eXahkSAWDIkCH4+eefERcXhzp16kh3dSkUCtm4L774IurWrYsrV64gLS0N8+bNgxCiRMsRQmDEiBEIDg7G9OnTpeFRUVEYN24cFi9ejNu3byMlJQUhISElmu/o0aPx6aefon///rCxsSnhGhfu448/xoULF3D8+HGkpaVJ1+yVdP2K4u/vD5VKhYSEBOmDJDU1VdqxH7bcwtrB3t4eWVn3r0Et2JHzFZzO398fb731lsmHWWZmJqZOnQoA6Nq1K3788UckJiaid+/eGDJkyGOtc1njPnqXj4+PdP0dcPcavejo6GI/nIvi5uaG7t27Y+PGjfjqq68eehf+g9fn3rhxAz4+PvDx8UFqaipSUlKk9yIjI6Wa1Go15s+fj6tXr+LIkSPYsWMHvvrqKwDy9nvYdvb29kZ0dLQ0fsF/A0CLFi2wbds2JCUl4fXXX8egQYOQmZkpW5fH6eve3t6FbouS8vf3R2hoqElfTE9Px+7duwGUfv9VqVSYPn06/vrrL5w7dw4XLlzAggULSlxPeWPfvctcfbdNmzaIi4vD+vXr0aVLF2g0Gnh6emL58uVo27attF4P1hcZGQkLCwuTkFawzR7sBwaDwSTwPejB+ecvo+D6F7ZPlEZJ+395qdCv5Ut99e4Fl0V9bZIxOwuJMyYDBS9UtbKC25wFsLC1K3Sawr426fLly4iOjkaHDh1gbW0tXfALAB4eHoiJiUFubi6sra0BAOnp6XB0dISDgwOuXLmCFStWQKPRlGidZsyYgdjYWBw+fNhkeH4jurm5Abj7V9/ff/9donm2aNECBw4cQMOGDUs0fnHS09Nha2sLrVaL5ORkzJ49+7HnCdz9S6h37974z3/+gw8++ABarRY3b97E+fPn0bNnz4cu18PDA9euXTMZ1rx5c6xfvx5NmzbF77//ju+++w79+vUrsoYXXngBERER6Nq1K0JDQ5Gbm4vjx4+jXr16sLS0xLFjx9C1a1fY29vDwcHB5C+1okxOfRVA0ftoljEbkxNnQIf7+6gVrLDAbQ7sLGwLnYb7aPGGDRuGAQMG4Nlnn0Xz5s2xcOFCWFlZoUOHDo80vzFjxmDKlCmIiorC0qVLix33zz//xIYNGzBkyBBs3LgR165dQ69eveDs7IyOHTvizTffxJIlSxAbG4uPP/5Yumj8hx9+QP369VG3bl04OjrC0tLSpP0K7tsP2875R3b69OkDFxcXk76i0+mwadMmREREwMnJCVqtFgqFotB9+XH6eu/evfHyyy+bbItz586hb9++JZo+IiICU6dOxerVqzF8+HAolUpcvHgROTk5aN269UP33/xt1qBBAwB3b6JxdnZG48aNoVarYW1t/dD+++rkuzckFNV3s7OMmDE58cFfL5izwA22doUfL2HfLZ65+q6VlRXat2+PxYsXS0c5w8LCsHjxYrz++usm9c2ePRtt2rSBvb09pk6diqFDh8LKyqrQ+Q4ePBgffvghDh48iI4dO2LBggVITk4uso5evXrh1VdfxZo1azBy5EgcPnwYO3fuxKlTpx5p/R9Umv5fXirVkUQLWzu4zVkA6+atoPILgHXzVsUGxKLk5uZi+vTpcHNzg4uLC44fPy49tyw8PBzNmjWDl5cXtFotMjMzsXDhQmzevBkODg4YO3YsBg8eXOJlff311zh79ixcXV2lO9A++OADBAcHY/LkyXjiiSfg4eGBs2fPSnfxlUSXLl1M/tp5UP7dk/k/RY07adIk6HQ6uLm5oW3btujWrVuJa3iYdevWwdLSEs2aNYNGo0GPHj2kD9WHLfc///kPvv/+ezg5OUl/MS5evBgHDhyAVqvFxx9/jGeffbbY5bdo0QLr1q3D5MmT4eLigoCAAHz88ccwGo0wGo1YtGgRfH19odVqsXbtWmzYsOGx19nOwhYL3OaglXVzBKj80Mq6ebEBsSjcR03n8/HHH2PEiBFwc3PDvn37sGvXLumXbGn16tULycnJ6NSpE7y9vYsdt2/fvti9ezecnJwwb948bN++Hc7OzgCAjRs34vbt2/D19UV4eDhGjhyJcePGAQD++ecf9OzZEw4ODmjSpAm6d+8u3SX64L79sO08btw49O3bF61atULDhg0RHh4OANL6b9iwAXXq1IGDgwOmTJmCzZs3F3oE6HH6uouLC7Zv3445c+bAyckJ+/fvR58+fUo8vVqtxv79+/H999/Dz88Pbm5uJqeIH7b/vvfeexg3bhy0Wi0+//xzxMXFYejQodBoNKhTpw58fHwe+65OWzsLzFnghuatrOEXoELzVtbFBsSisO+azsdcfTcsLAzp6eno1KmTVEtaWpp0ZzMAvP322+jcuTNatWol/UFX8K77BzVs2BBffPEFxo4dC3d3d9y+fRutWrUqcnxnZ2fs3r0bK1euhIuLCyZNmoQNGzaUSQDPV9L+X14U4nHPO5ZC/rOWyvoL2PmgTyor3EerhxYtWmDy5MnF/qExa9YsXLp0Cd98800FVvZwZ86cQdu2bZGTkwMLi0r1d3ylxr5bPZSk71LF4ScQEVUrBw4cQExMjMlF/pWZTqfDzp07odfrkZiYiGnTpuHpp59mQKQap6r13ZqAn0JEVG107doVQ4YMwdKlSx/5lFdFE0JIp3kbNGgABwcHfPbZZ+Yui6hCVcW+WxNU6I0rRETl6aeffirxuPnfXGFu1tbW+P33381dBpFZlabvUsXhkUQiIiIikmFIJCIiIiIZs5xuLuzZU0SVCfdRoqqJfZeo7FToI3CIiIiIqGrg6WYiIiIikmFIJCIiIiIZhkQiIiIikmFIJCIiIiIZhkQiIiIikmFIJCIiIiIZhkQiIiIikmFIJCIiIiIZhkQiIiIikmFIJCIiIiIZhkQiIiIikmFIJCIiIiIZhkQiIiIikmFIJCIiIiIZhkQiIiIikmFIJCIiIiIZhkQiIiIikmFIJCIiIiIZhkQiIiIikmFIJCIiIiIZhkQiIiIikmFIJCIiIiIZhkQiIiIikmFIJCIiIiIZhkQiIiIikmFIJCIiIiKZ/wdzoJTZcFe+QAAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Programmid\\Miniconda3\\envs\\py38_estnltk_neural\\lib\\site-packages\\plotnine\\ggplot.py:718: PlotnineWarning: Saving 6 x 10 in image.\n", + "C:\\Programmid\\Miniconda3\\envs\\py38_estnltk_neural\\lib\\site-packages\\plotnine\\ggplot.py:719: PlotnineWarning: Filename: figure_1_train_and_test.pdf\n" + ] + } + ], + "source": [ + "# Test and training set performance as a function of training set size (%)\n", + "from plotnine import labs, facet_wrap\n", + "p = ggplot(train_df) + theme_bw()\n", + "p = p + labs(x='training size (% of all training data)')\n", + "p = p + geom_point(train_df, aes(x='training size', y='LAS', color='model'))\n", + "p = p + geom_line(train_df, aes(x='training size', y='LAS', color='model'))\n", + "p = p + geom_point(test_df, aes(x='training size', y='LAS', color='model'))\n", + "p = p + geom_line(test_df, aes(x='training size', y='LAS', color='model'))\n", + "p = p + theme(legend_position='bottom', figure_size=(6, 10), legend_box_spacing=.25) + facet_wrap(['eval set'], ncol=1)\n", + "display(p)\n", + "p.save('figure_1_train_and_test.pdf')" + ] + }, + { + "cell_type": "markdown", + "id": "f23456a5", + "metadata": {}, + "source": [ + "## Extrapolation of test and training errors\n", + "\n", + "* We use the range 70% - 100% for the extrapolation\n", + "* We use linear regression to find the linear trend\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1ce80300", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "stanza ME only wordforms\n", + "Test fit: 0.04239999999999965 * x + 79.61600000000003\n", + "Train fit: -0.02680000000000035 * x + 93.84800000000003\n", + "Improvement rate 108.92\n", + "Min stablisation size: 205.66% 800608\n", + "Limiting training error: 8833.62%\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "stanza ME all features\n", + "Test fit: 0.03369999999999961 * x + 81.45300000000003\n", + "Train fit: -0.006999999999999744 * x + 93.195\n", + "Improvement rate 86.57\n", + "Min stablisation size: 288.50% 1123072\n", + "Limiting training error: 9117.55%\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "stanza ME only postags and feats\n", + "Test fit: 0.017799999999999584 * x + 67.92700000000004\n", + "Train fit: -0.029800000000000177 * x + 82.42300000000002\n", + "Improvement rate 45.73\n", + "Min stablisation size: 304.54% 1185499\n", + "Limiting training error: 7334.78%\n" + ] + }, + { + "data": { + "image/png": "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Va1aNbVu3To1dOhQBUCFh4crpZS6cOGCeuONN1SFChWUh4eHcnFxUaVKlVJvvfWWunr1aqbeu4ULF6pnnnlGOTs7Kz8/P9WlSxd19erVDOsJgOrZs2e6x9ixY0e6nzGllLpx44bq1q2b8vb2Vi4uLqpevXrq999/f+Rn5kEPmyqtlLyvEyZMUFWqVFEuLi7Kzc1NlSpVSnXt2lVt2bLliV9neHi4evnll5W3t7fZ9PQjR46oXr16qdKlSyt3d3fl5uamypUrp0aMGKFu376dqddDeZemVC6N2iKifOXOnTsoVKgQWrVqhXXr1mX6ukuXLqF48eIYPXp0vt5bqE2bNti1axdiYmJMXaVElD34E0VET2T+/PlISEjAG2+8YemiWNS9e/fSHfv777+xefNmNG/enMGFKAdwzAsRZcmqVatw/fp1jB07FjVr1szyQN385s0338SdO3fQoEEDeHp64sSJE1iwYAFcXFwwduxYSxePKF9ieCGiLOncuTOcnJxQt25dbnAHoEWLFpg9ezYmT56MmJgYFCxYEG3btsXo0aMzXFyPiJ4ex7wQERGRVWFnLBEREVkVhhciIiKyKgwvREREZFUYXoiIiMiqMLwQERGRVcm3U6Wjo6MzXDyKiIiI8i5XV1d4eXk98px8GV6io6Mxe/bsdDu4WoK7uztCQ0Nx5MgRxMXFWbo4ucbOzs70up92x1trY6t1DthuvbPOba/OAdut95yucwcHBwwaNOiRASZfrvNy48YNzJs3Dy+//PJT77b6tHQ6Hby9vXHnzp2n2qmYrAfr3Pawzm0T6z37RUVFYd26dejfvz8KFSr00PPyZcuLka+v7yNffG5QSkGv18PPzw+aplm0LLnJYDAgLCwMgYGBNre3i63WOWC79c46t706B2y33vNCndvWJ42IiIisHsMLERERWRWGFyIiIrIqDC9ERERkVRheiIiIyKowvBAREZFVYXghIiIiq8LwQkRERFaF4YWIiIisCsMLERERWRWGFyIiIrIqDC9ERERkVRheiIiIyKowvNii+/eBHTssXQoiIqInwvBia5KTgU6dgGbNgIMHLV0aIiKiLGN4sSUGA9C3L/D330CjRsD48ZYuERERUZbpLF0AAAgPD8c333yD//77D/b29qhWrRoGDBgAV1dXfPzxxzh9+jTs7e1N569evdqCpbViH34IrF8P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1xdxcXG4ffu22TWW+IwY709KSoKTk9NjPyMRERFm/yc8yWfE3t4ehQsXzrXPiMFgwLVr18yOe3h4wNPTE9HR0bh37x40TTPd97D/RwoUKABvb2+r+4wEBATAyckJN2/eRHJyMgwGAyIjI6FpGoKCgrL1/5FMU1ng7u6uChQo8Nibi4uLsrOzy8pDZ6vr16+rxo0bKwBmt7t37yqllHJwcDA7PmjQIKWUUlOmTEl3zfXr15VSSnl7e5sd79mzp1JKqTlz5qS75vTp00oppYoWLWp2vEOHDkoppZYtW5bumkOHDimllCpfvrzZ8eeff14ppdSPP/6Y7ppdu3YppZSqWbOm2fH69esrpZTatm1bums2bNiglFKqadOmZserVKmilFJq//796a75/vvvlVJKtWvXzux4yZIllVJKnThxIt018+bNU9evX1edO3c2O+7v76+UUurSpUvprpkxY4ZSSqn+/fubHXd1dVVKKRUVFZXumnHjximllBo6dGi6+wwGg0pISEh3/KOPPlJKKfXJJ5+kuy8mJkYppZROpzM7PnjwYKWUUp9//nm6a27cuKGUUsrLy8vseK9evZRSSn399dfprjlz5oxSSqkiRYqYHe/YsaNSSqlvv/023TWHDx9WSilVrlw5s+MtW7ZUSim1bt26dNfs3r1bKaVUjRo1zI43aNBAKaXU1q1b012zceNGpZRSTZo0MTtetWpVpZRSf/zxR7prVq9erZRSqm3btmbHS5UqpZRS6p9//kl3zaJFi5RSKt1nJCAgQCml1MWLF9Nd8+WXXyqllOrXr5/ZcTc3N6WUUpGRkemuGT9+vFJKqSFDhqS7Tyml7t+/n+748OHDlVJKjRo1Kt19sbGxSiml7O3tzY4PGjRIGQwGNXny5HTX3Lx5UymllKenp9nx119/XSml1OzZs9Ndc/bsWaWUUoULFzY73qlTJ6WUUkuXLk13zZEjR5RSSpUtW9bseKtWrZRSSq1duzbdNXv27FFKKVW9enWz4w0bNlRKKbVly5Z012zatEkppdL9PxsaGqqUUmrfvn3prvnhhx+UUkq1adPG7Hjp0qWVUkodP3483TWLFy9WSin12muvmR0PCgpSSil14cKFdNfMnDlTKaVU3759zY67u7srpZSKiIhId82ECROUUkq9++67GX5G7t27l+74xx9/rAwGgxo+fHi6++Li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99+bjauJiorCkSNH4ODgYHYbN24crl69+tjHJaLswTEvRDZK07R0x3744QdUrVoV33zzjenYrl27crNY6QQFBSEyMjLd8YiIiExdu2jRIixYsACHDh3CuHHj8Oqrr+L06dMoUaJEhtds2rQJw4cPx9SpU9O17hQsWBCVK1fGwoULn+zFEFG2YHghysccHR0f2/KR1v37900tH0bGwb6WUrNmTURHR2P37t1o1KgRACAuLg7bt29P12rzMHZ2dqhZsybGjRuHn3/+GefOncswvJw+fRqdO3dG165dzWYlGTVv3hwbN25EoUKFUKhQoad6XUT05BheiPKx8uXLY9GiRfjuu+9QunRp+Pr6PnJ68XPPPYdBgwZh7NixqFu3LjZu3Ijt27fnXoEz0KpVK1SrVg1dunTBxIkT4eXlhc8//xwFChR45Gyju3fv4vnnn0f37t1RtmxZJCUlYdasWfDy8kK1atUyvKZdu3ZwcXFB7969sX//ftNxDw8PVKhQAT169MA333yDJk2a4P3330eZMmUQHR2NI0eOICkpCRMnTsz2109E6TG8EOVjffr0wYEDB/DWW2/h1q1b6NmzJ5YsWfLQ8wcMGIALFy5g1qxZmDJlCp5//nmsXLnSoovLaZqGn376CQMGDED//v3h7e2Nt99+G6dPn8bRo0cfep2zszMqVaqEWbNm4cqVK3BxcUGNGjWwdetW+Pr6ZniNcVBy06ZNzY43btwYO3fuhJOTE37//XeMGTMG48ePx82bN+Hr64vQ0FAMHDgw214zET0atwcgIquTlJSEChUqoGHDhli8eLGli0NEuYwtL0SU582bNw8GgwFly5bFnTt3MGfOHFy6dAmrVq2ydNGIyAIYXogoz3N2dsakSZNw6dIlAECVKlWwYcMG1KhRw7IFIyKLYLcRERERWRUuUkdERERWheGFiIiIrArDCxEREVkVhhciIiKyKgwvREREZFUYXoiIiMiqMLwQERGRVWF4ISIiIqvyP6sfiiEVl9xRAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from sklearn.linear_model import LinearRegression\n", + "\n", + "#\n", + "# (A) this is training set size if we ignore punctuation \n", + "# tokens while calculating LAS scores.\n", + "#\n", + "tr_size = 325531\n", + "\n", + "#\n", + "# (B) this is training set size if we include punctuation \n", + "# tokens while calculating LAS scores.\n", + "#\n", + "tr_size = 389278\n", + "\n", + "plots = []\n", + "lm = LinearRegression()\n", + "for model in df['model'].unique():\n", + "\n", + " print(model)\n", + " data = df.set_index('model').loc[model]\n", + "\n", + " # Test\n", + " X = data.loc[data['training size']>=70, ['training size']]\n", + " y = data.loc[data['training size']>=70, 'test LAS']\n", + " lm.fit(X,y) \n", + " a = lm.coef_[0]\n", + " b = lm.intercept_\n", + " print('Test fit: {} * x + {}'.format(a, b))\n", + "\n", + " X = data.loc[data['training size']>=70, ['training size']]\n", + " y = data.loc[data['training size']>=70, 'train LAS']\n", + " lm.fit(X,y) \n", + " c = lm.coef_[0]\n", + " d = lm.intercept_\n", + " print('Train fit: {} * x + {}'.format(c, d))\n", + "\n", + " print('Improvement rate {:.2f}'.format((100 * a) * (100000/tr_size * 100)) )\n", + "\n", + " e = (d-b)/(a-c)\n", + " lim_las = (a * e + b)\n", + " print('Min stablisation size: {:.2f}% {:.0f}'.format(e, e * tr_size / 100))\n", + " print('Limiting training error: {:.2f}%'.format(100 * lim_las)) \n", + "\n", + " p = ggplot(data) + theme_bw() + geom_point(aes(x='training size', y='test LAS'), color='blue')\n", + " p = p + geom_line(aes(x='training size', y='test LAS'), color='blue')\n", + " p = p + geom_line(aes(x='training size', y='train LAS'), color='red')\n", + " p = p + geom_abline(intercept=b, slope=a, linetype='dashed', color = 'blue')\n", + " p = p + geom_abline(intercept=d, slope=c, linetype='dashed', color = 'red')\n", + " p = p + geom_hline(yintercept=lim_las, linetype='dashed')\n", + " p = p + ylab(\"LAS\")\n", + " p = p + ggtitle(model)\n", + " plots.append(p)\n", + " display(p)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "b0505ab7", + "metadata": {}, + "outputs": [], + "source": [ + "#\n", + "# Install patchworklib which allows to arrange multiple plots into a single plot\n", + "# https://github.com/ponnhide/patchworklib\n", + "#\n", + "!pip install patchworklib" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "a2ad35d0", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "meta NOT subset; don't know how to subset; dropped\n" + ] + }, + { + "data": { + "image/png": "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qlWrWi1/9NFHlVJKrV692madLVu2KKWUatCggdXyZs2aKaWU+vXXX23WWbt2rVJKqVatWlktv++++275Gfnmm2+UUkp16NDBanlkZKRSSqn//vvPZp0lS5YopZTq1auX1fLg4GCllFKnT5+2WeeDDz5QSik1ePBgq+Wenp5KKaUSExNt1pkyZYpSSqnRo0fb3KeUyvEzMmHCBKWUUq+++qrNfdeuXVNGo1Hp9Xqr5SNHjlRKKfXuu+/arHPhwgWllFKlSpWyWj5gwACllFLz58+3Wef48eNKKaXCw8Otlnfv3l0ppdSyZcts1tmzZ49SSqkqVapYLW/Xrp1SSqlVq1bZrPP7778rk8mk6tevb7W8efPmSiml1q9fb7POunXrlFLK5nu2bt26Simltm3bZrPOypUrlVJKPf7441bLK1eurJRSav/+/TbrLF26VCml1JNPPmm1PDQ0VCml1KlTp275GRk0aJDVci8vL6WUUgkJCTbrTJ06VSml1AsvvKAA5Or4rSmV7VJGMRIbG4uFCxdiyJAhCAsLK5TnVMVoKpG4uDiEhIQU+Uqq3CeOh/vE8XCfWLPH8YMsYmNjMWfOHHTt2hXBwcFZy9kDfOeem7S0NBw4cACBgYHFogfYvO+yK6o9wLVq1WIPsAP1AO/evdvq/6So9wDHx8cXix7gqKioe+oBPnHiBH7++ec7Hr8ZAOcjnkQ6Hu4Tx8N94ni4T6wxALavwn7/i8vnHyg+30vcJ46H+8TxcJ/Yyu3xo+judSIiIiIiIqK7wACYiIiIiIiISgQGwERERERERFQiMAAmIiIiIiKiEoEBMBEREREREZUIDICJiIiIiIioRGAATERERERERCUCA2AiIiIiIiIqERgAExERERERUYnAAJiIiIiIiIhKBAbAREREREREVCIwACYiIiIiIqISgQEwERERERERlQgMgImIiIiIiKhEYABMREREREREJQIDYCIiIiIiIioRGAATERERERFRicAAmIiIiIiIiEoEBsBERERERERUIjAAJiIiIiIiohKBATARERERERGVCAyAiYiIiIiIqERgAExEREREREQlAgNgIiIiIiIiKhEYABMREREREVGJwACYiIiIiIiISgQGwERERERERFQiMAAmIiIiIiKiEoEBMBEREREREZUIDICJiIiIiIioRGAATERERERERCUCA2Ai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m1z3Jycna9y4cVqzZo2ioqLUs2dP17qPyD9vvvmmUlJS9MQTT7htT0xM5PekgHzwwQeaMWOG27bmzZtrwIAB2rp1q8aPH6+tW7cqLi5O9957r9tagj/99JPee+89/fvvv6pevboefvhhrvFCocb52/tw/vZOnL89y1vP3QRgAAAAAIBPYAg0AAAAAMAnEIABAAAAAD6BAAwAAAAA8AkEYAAAAACATyAAAwAAAAB8AgEYAAAAAOATCMAAAAAAAJ9AAAbyGEtrAwBQuHDuBnwHARjIQ1988YV69+6dJ481depUORwObd26NV+PyS+LFi2Sw+HQokWLPF0KAABnxLk7G+du+AKH4SMvIM80a9ZMkvLkxJGcnKxNmzapXr16Cg4Ozrdj8suhQ4f0zz//qFatWoqMjPRoLQAAnAnn7mycu+ELCMBAHsrLkygAAMh/nLsB38IQaCCPNGvWTD/88IN++OEH1/ChrKFEb775pipUqKDIyEjNnz9fkvTWW2+pQYMGCg8PV2hoqC6//HJ98sknrsc7dUjUnXfeqRYtWujdd99VtWrVFBwcrMsvv1xff/31RR0jSb/88ouaNGmi8PBwJSQk6LXXXlOLFi105513nvH1pqam6v7771e5cuUUHBysGjVqaPTo0a77Tx1GVbFiRTkcjtPesupNS0vT448/rvLlyys4OFh16tTRRx99dIEtAgDA2XHu5twN3xPg6QKAouKNN95Qjx49XF/XqlVLy5cvlyQNHz5c48aNU2pqqho2bKgJEybooYce0vDhwzV69Gjt379fL774orp3765rr71W5cqVO+1z/PHHH9q1a5eeffZZFS9eXE8++aQ6duyonTt3Kjo6+oKOWbt2rW644QY1aNBAM2bMUEpKioYMGaIDBw7o9ttvP+PrHTBggL799luNHj1a8fHxmjdvngYNGqSSJUuqT58+OfafNWuWjh8/7vo+KSlJ3bt3V+PGjVW+fHkZY3TrrbdqyZIlGj58uGrVqqVZs2bp9ttv1/Hjx9WrV69ctwUAALnBuZtzN3wPARjIIydfL3PNNde43Xf//ferc+fOru83b96sQYMGadiwYa5tFStWVP369fXTTz+d8eR18OBBLVu2TFWqVJEkhYeHq2nTplq4cKE6dep0Qcc8//zzKl68uL7++muFhYVJkmrUqKGGDRue9fX+8MMPuvHGG121NmvWTBEREYqLizvt/vXq1XN9ffz4cTVt2lSlS5fWjBkz5O/vr/nz5+vrr7/WjBkz1LVrV0lSq1atdPToUQ0ePFjdu3dXQAD/ZQEA8g7nbs7d8D38RAIF4PLLL3f7/pVXXpEkHThwQGvXrtXGjRv1/fffS5LbJ62nio2NdZ0MJbk+bT569OgFH7Nw4UK1adPGdQKVpGuvvVYVK1Y862u6/vrrNWnSJO3YsUNt2rRR27Zt9eSTT571mCx9+/bV33//rV9++cX16feCBQvkcDjUtm1bZWRkuPZt3769pk+frr///jvH+wgAQH7h3O2OczeKCq4BBgpARESE2/ebNm1SixYtFB0draZNm+rll1/WiRMnJJ19LcKTT3SS5Odnf4WdTucFH5OcnHzaT35LlSp1xseUpLFjx2rkyJHasmWL+vfvr8qVK6thw4b666+/znrcCy+8oOnTp2vq1KmqXbu2a/u+fftkjFGxYsUUGBjounXp0kWStGvXrrM+LgAAeYlzdzbO3ShK6AEGCpjT6VTbtm0VFBSk33//XZdffrkCAgL0zz//aNq0aQVeT7ly5bRnz54c2/fu3asaNWqc8bjg4GA98cQTeuKJJ5SYmKgvv/xSI0aMUPfu3bV69erTHvPll1/qiSee0JAhQ9yGlUlSVFSUIiIiXJ+mn6pq1arn8aoAAMg7nLs5d6PooAcYyEP+/v7n3CclJUXr1q3T3XffrQYNGriujZk3b56ks38inB+aNm2qefPmKS0tzbVtxYoV2rJlyxmPSU1NVbVq1VzDwRISEvTAAw+oW7du2rZt22mP+fvvv3XHHXeoVatWGjly5GnrOHLkiIwxatCggeu2atUqDR8+3G1oFQAAeYVzN+du+BZ6gIE8FBUVpV9++UULFy50mzjiZHFxcapYsaLGjx+vcuXKKTo6Wl9//bXGjh0r6ezXBOWHoUOHasaMGWrdurUeffRRHThwQMOGDZOfn59ryNWpQkNDVb9+fQ0fPlxBQUGqU6eO1q1bp6lTp+b4dFiS9u/fr3bt2ik8PFxDhw7VH3/84fbHQpUqVdSmTRs1adJEHTp00JNPPqmaNWtq6dKleuqpp3TTTTcpJiYm394DAIDv4tzNuRu+hR5gIA89+OCDCgwMVOvWrV2fCp/O7NmzVbZsWd15553q0qWLfv31V3355ZeqUaOGFi9eXIAV2+FJ33zzjVJTU9W5c2cNHTpUgwcPVunSpXNc/3SyyZMnq0+fPho9erRatmypESNGqG/fvpo4cWKOfVeuXKmtW7cqKSlJjRs31tVXX61rr73WdZszZ478/Pw0d+5c3X777Xr++efVqlUrTZo0SQMHDtSMGTPy8y0AAPgwzt2cu+FbHOZsV+0DKPIWLFigoKAgNW7c2LXtwIEDiouL0+jRo/XQQw95sDoAAHAqzt3AhaMHGPBxy5cvV8uWLTV27Fj9+OOPmjVrltq1a6eoqCh169bN0+UBAIBTcO4GLhzXAAM+7tFHH9Xx48c1ceJEJSYmKiIiQs2aNdO7776r2NhYT5cHAABOwbkbuHAMgQYAAAAA+ASGQAMAAAAAfAIBGAAAAADgEwjAAAAAAACfQAAGAAAAAPgEAjAAAAAAwCcQgAEAAAAAPoEADAAAAADwCQRgAAAAAIBPIAADAAAAAHwCARgAAAAA4BMIwAAAAAAAn0AABgAAAAD4BAIwAAAAAMAnEIABAAAAAD6BAAwAAAAA8AkEYAAAAACATyAAAwAAAAB8AgEYAAAAAOATCMAAAAAAAJ9AAAYAAAAA+AQCMAAAAADAJxCAAQAAAAA+gQAMAAAAAPAJBGAAAAAAgE8gAAMAAAAAfAIBGAAAAADgEwjAAAAAAACfQAAGAAAAAPgEAjAAAAAAwCcQgAEAAAAAPoEADAAAAADwCQRgAAAAAIBPIAADAAAAAHwCARgAAAAA4BMIwAAAAAAAn0AABgAAAAD4BAIwAAAAAMAnEIABAAAAAD6BAAwAAAAA8AkEYAAAAACATyAAAwAAAAB8AgEYAAAAAOATCMAAAAAAAJ9AAAYAAAAA+AQCMAAAAADAJxCAAQAAAAA+gQAMAAAAAPAJBGAAAAAAgE8gAAMAAAAAfAIB2MedOHFC27dv93QZF2XRokVyOBxyOBwaNmzYGff7/fffXft9/fXXru0VK1Z0bT/T7c477yyAV5Jt6tSpOeosjDZt2uTpEjzumWeekcPh0Nq1a8+6344dO9SiRQuFhYWpRIkSWrZsWb7Uc+TIEe3ZsydfHhsAAMDbBXi6AHjOtm3b1KpVKw0YMED/+c9/PF1Onpg9e7ZGjhx52vtmzpx5xuNiYmL06quvnvH+KlWqXHRtvua5557TxIkTtWPHDk+XUigMGDBACxYs0LBhw1S+fHlVr149z59j2bJl6tChg9566y3ddNNNef74AAAA3o4A7MO2bNmidevWebqMPFO5cmWtXr1aGzduVNWqVXPc/9lnnyk2NlbJyck57gsPD1ePHj0KokyfMX/+fGVkZHi6jEJj5cqVqlmzpkaMGJFvz7Fq1Srt3Lkz3x4fAADA2zEEGkXGrbfeKkmaNWtWjvtWrVqlDRs2uPYBvE16eroiIyM9XQYAAECRRgAuog4dOqS+ffuqQoUKCg4OVoUKFfTggw9q3759kuw1ptdff70k6b777pPD4XAd+9dff+n2229X6dKlFRgYqJIlS6pdu3ZatWqVa5+tW7fK4XDo7bff1ogRI1ShQgWFhISobt26+vTTT137ZV3Leqbb1KlTXfu+/fbbatiwoSIjIxUUFKTKlStr0KBBSktLy9Vrrlq1qmrXrq3Zs2fnuG/mzJkqW7asrrnmmvN5G8/Lr7/+qtatWysyMlJhYWFq2LChPv/8c7d9sq4H3bp1qzp27KjixYsrMjJSHTt21NatW0/7uE6nU+XLl1ft2rVz3LdhwwY5HA49//zzZ6yrWbNmuu666/Ttt9+qbt26Cg0NVc2aNTVp0qQc++7cuVN9+vRRqVKlFBwcrJo1a+qll15SZmam235ffPGFrr76akVGRqpYsWK67rrr3F5rxYoV9cMPP2jPnj1yOBx65plnJEkZGRkaPXq0rrjiCkVERCgkJEQ1atTQqFGj5HQ63Z5j2bJlatWqlYoXL664uDg98sgjeuutt1zvX5a3335bl19+ucLDwxUVFaVWrVrpp59+OuP7keXIkSN68skndemllyosLExhYWGqW7eupkyZkuP9a9asmb7//ntde+21Cg0NVXx8vAYMGKDU1FS3fVetWqWbb75ZxYsXV6lSpTR48GCdOHHirHVkXcO+bds2/fbbbzmuOX///fdVv359hYaGqmTJkurSpUuOa6tz874+88wz6tOnjySpdevWqlixouv4UaNGqWbNmgoNDVVMTIw6duyov//++5zvIQAAQKFjUCTddNNNJiwszAwZMsS89dZbZsCAASYgIMA0btzYGGPMpk2bzNChQ40k06dPHzNt2jRjjDGrV682YWFhpkaNGubll182kydPNvfff78JCgoycXFx5tixY8YYY7Zs2WIkmQoVKpgqVaqYl19+2YwZM8aUK1fOOBwOs2rVKtfzTJs2ze02ZcoUU6xYMRMVFWU2bdpkjDHm6aefNpJM165dzaRJk8zYsWNNkyZNjCTTv3//s77W77//3kgyEydONE8//bTx8/MzSUlJbvvUrl3b9O/f37z77rtGkpk3b57rvgoVKpjy5cub5OTkM94yMzPPWsOcOXNMQECAqVChghk5cqR55ZVXTN26dY0k8/rrr7v2y3qdFSpUMB07djSTJk0y/fv3N35+fubKK6907XdqnYMGDTKSzOrVq92ed/jw4cbhcJitW7eesbamTZua+Ph4ExwcbPr06WNef/1113s7fPhw137btm0z8fHxJjQ01Dz22GNmwoQJpn379kaSue2221z7LV682AQEBJjrrrvOTJgwwYwfP97UrVvXOBwO89133xljjJk1a5apUaOGKV68uJk2bZr566+/jDHG9O7d2zgcDnPvvfeayZMnm9GjR7vep1deecX1HMuXLzfh4eGmbNmyZtSoUea5554zpUuXNiVKlDCSzJYtW4wxxsyYMcNIMu3atTOTJk0yr7zyiqlUqZIJCgoya9asOWubNWvWzISEhJiBAweat956yzz//POmUqVKRpKZOXNmjvevWLFi5qGHHjKTJ082bdq0MZLMoEGDXPutW7fOREZGmri4ODNy5Ejz4osvmgoVKpiIiAgj6Yz1JCUlmWnTppmYmBhTpUoVM23aNPPzzz8bY4wZMWKE6/VNmDDBPP300yYuLs6UKFHCbNiwwfUYuXlf//rrL9OvXz9X3bNmzTLGGPPCCy8YSaZXr15m8uTJ5rnnnjOxsbEmOjrapKSknPU9BAAAKGwIwEXQ3r17jSTzwAMPuG0fNmyYqV+/vjlw4IAxxj04ZskKu7t27XI7dvDgwUaSWbJkiTEmOwCXKlXK9XgnP+bQoUPPWF+PHj2Mn5+fmTt3rjHGmPT0dFO8eHHTrl07t/1OnDhhypUrZy655JKzvt6TX8dff/1lJJk333zTdf/69euNJLNo0aIzBmBJZ71lBa7TycjIMOXLlzelSpUy+/btc21PTU01V1xxhQkJCXEF8qwA/J///MftMXr37m0kmfXr1xtjcgbglStXGklm2LBhbsfVqFHD9aHGmTRt2tRIMiNGjHBtO3HihGnUqJEJDg42e/bsMcYY061bNyPJLF682O34+++/30gys2fPdvs+OTnZtU9KSoqpVq2aGTNmjNvzlipVyvV9UlKS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aPTo0Xr66afdZhXP8tFHH+m3337T+PHj3YYeZ2RkaN++fSpTpowkqUqVKpJ02h7IrJqzrFmzRkePHlXTpk3VtGlTjR49Wv/884+aNGmi0aNHu2Y9PtUbb7yhjRs36quvvlLbtm1d23ft2nX6N/AcSpYsqcjIyNPWfOqMzbmVNUtzmTJldO2117rdt2DBAjkcDgUHB2vatGm5el9Px+l0atWqVfL391fr1q1dlyX89NNPat68ucaOHasbb7zxguoHAADwRvQAF0E7d+5Uw4YN9dJLL7m2+fn56corr5QkVwjK+vfkHqp9+/apXLlybuH3wIEDrt60jIyM86olMzNTXbt21ebNm/X++++fdoho1tJMtWrVcts+Z84cbdiw4byf0+Fw6JZbbtHcuXP1ySef6JZbblFAQP591lO/fn2VLVtWkyZNchtifvz4cY0ePVqBgYFq1apVnjxXz549tXHjRr3yyisqXry4br755lwdd/DgQbee94yMDL366quKjIxUq1at5O/vr7Zt2+qHH37IsYRQ1vD3du3aSZKGDRumFi1auI0kKF++vMqWLSt/f39X+PX398/xsyXlbOc333xTx44dc7VzbGysmjRpok8++cQ1xDfr+A8++MDt2L59+6pDhw46evSoa1uNGjUUFRXlFvZPdaZaXn31Vdf7cz4cDoduvfVWff/99/r9999d2w8ePKj33nvvvB4rS/v27SVJzz//vNv7uGXLFrVv316DBw+Ww+HI9fsq5fyddzgcuvnmm9W7d2+34fdXXHGFgoKCzvoeAgAAFEb0ABdBl1xyiTp37qxx48bp8OHDuuaaa7Rv3z6NHz9eMTEx6t69uyQbNCTpww8/VFBQkO688061bdtWH374ofr06aPrrrtOO3fu1Ntvv629e/dKkuu62twaOnSovvvuO3Xo0EHHjh3T+++/7zbMtkqVKmrUqJGioqL0+OOPa9euXYqLi9Nvv/2m9957TyEhITpy5IiMMaftVTyTjh076o033tCSJUv0xBNPnHP/o0eP5piE62Rnu9Y2ICBAb7zxhjp27KgrrrhC/fr1U2hoqKZNm6YVK1bolVdeyXHN74Xq2LGj7rvvPn344Ye66667znpt8qk1Pvjgg1q1apWqVq2qDz/8UL/++qsmT57s+rDjhRde0MKFC9WyZUs98MADqlSpkr799lt9/vnn6tChgzp06CBJGjhwoGbOnKkmTZqoX79+ioqK0sKFC7Vo0SK39zo2NlYpKSl6+eWX1aRJE7Vs2VKBgYHq06ePHnroIYWHh2vBggX65JNPFBIS4vazNWbMGF133XW68sorXetUT5w40TWhW9bPwuDBg9WhQwc1adJEvXr1UnBwsGbPnq1NmzblWM/3ZG3bttW4cePUoUMH9evXTw6HQ1988YXmz5+voKCg8/45l+wHBV9//bVatGihAQMGqHjx4po8efIZh5Wfy6WXXqqBAwdqzJgxatKkibp06aJjx45p/PjxyszM1CuvvCJJ5/W+Zv3OT548WQcOHFD37t01ePBgPfjgg7rxxhvVuXNnOZ1OTZs2TampqXrwwQcvqHYAAACv5anpp5G/jh49aoYNG2YuueQSExISYkqWLGm6dOli1q1b57Zf//79TbFixUxERITZuHGj+ffff02/fv1M6dKlTUhIiKlSpYrp16+fWb9+vQkICDB33XWXMSZ7GaT//ve/bo+XmppqJJnevXsbY7KXkDnTLWu/n3/+2TRu3NgUK1bMREdHm/r165uJEyea119/3UgyP/744xlf6+mWczpx4oQpWbKkiYqKMunp6a7tF7oMUvHixc/5nv/444+mRYsWJiIiwkRERJgmTZqYzz//3G2frGWQTl0SZ+LEiUaS+f77789YZ5as5YoWLFhwzpqMyV6OaN68eaZ69eomJCTENGjQwHz22Wc59t26davp0aOHiYmJMcHBwebSSy81r7zyisnIyHDbb/HixebGG280sbGxJjg42NSuXduMGzfObTmiZcuWmZo1a5rAwEDTr18/Y4wxX331lalfv74JCwszsbGxplGjRuaTTz4xjz32WI71jJcsWWIaN25sQkNDTWxsrHnsscfMwIEDjSSze/du136ff/65adSokYmOjjahoaHmyiuvNO+///4535d3333X1KpVy4SEhJj4+HjTokULs2DBAtO5c2cTERFhjh496vb+napr167m1P9CN2/ebDp37myioqJMVFSUue+++8y4ceMuaBmkLJMnTzb16tUzISEhJiYmxtx0003ml19+cdsnt+9renq66dKliwkNDTXR0dEmNTXVGGPMO++8Y6644goTGRlpwsPDTdOmTc0333xzzvcQAACgsHEYc4HdEwA8okePHvrxxx+1devWXE2m1axZM61du/aC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u3btyM3NPe7xLl26lN69e0dr7dGjB2lpaWRlZR31+R07doz+fuDAAbp3707z5s2ZO3cuiYmJvPrqqyxYsIC5c+dy2WWXAdC3b1/27dvHuHHjGDZsGElJ/i9LkiRJJ85vk1IN6NChQ6XHU6ZMAaC4uJgPP/yQjz/+mNdffx2g0ijpV2VmZkaDLBAdKd63b98J77N48WIGDBgQDb8AXbt2pXXr1sc9pgsuuIDp06fz97//nQEDBjBw4EAmTpx43H0OGz16NGvXrmXFihXRketFixYRCoUYOHAgZWVl0ededNFFzJ49m7Vr1x7xOUqSJEnfhtcASzUgLS2t0uNNmzbRq1cvGjduTPfu3XnggQc4dOgQcPz7CH85pAIkJERO4YqKihPep6io6Kijtk2bNj3mawLk5+czadIkNm/ezK9//WvatGlDbm4u77777nH3u++++5g9ezZPPPEE7du3j7bv3LmTcDjMSSedRL169aI/Q4YMAWDbtm3HfV1JkiTp6zgCLNWwiooKBg4cSP369fnrX/9Khw4dSEpKYt26dTz55JM1Xk+LFi347LPPjmgvLCykXbt2x9wvOTmZCRMmMGHCBAoKCpg3bx533303w4YN44MPPjjqPvPmzWPChAmMHz++0pRwgIyMDNLS0qIj4V91+umnf4ujkiRJko7kCLBUhRITE7/2OTt27GDDhg2MGjWKzp07R69rnT9/PnD80dzq0L17d+bPn09paWm07Z133mHz5s3H3KekpIQzzzwzOpU7Ozub66+/nqFDh7J169aj7rN27VquuOIK+vbty6RJk45ax969ewmHw3Tu3Dn68/7775OXl1dpWrQkSZJ0IhwBlqpQRkYGK1asYPHixZUWffqyrKwsWrduzcMPP0yLFi1o3LgxCxYsID8/Hzj+9bzV4fbbb2fu3Ln079+fW2+9leLiYu644w4SEhKi06W/qmHDhnTq1Im8vDzq16/Pueeey4YNG3jiiSeOGNkF2LVrF4MGDSI1NZXbb7+dlStXVgr6bdu2ZcCAAZx//vlcfPHFTJw4kbPPPpu3336b//qv/6Jfv340adKk2j4DSZIkxQdHgKUqdMMNN1CvXj369+8fHdE9mueee47TTjuNkSNHMmTIEN566y3mzZtHu3btePPNN2uw4sjU4oULF1JSUsLgwYO5/fbbGTduHM2bNz/i2uUvmzFjBldffTWTJ0+mT58+3H333YwePZpHH330iOe+9957bNmyhe3bt9OtWzd+9KMf0bVr1+jPSy+9REJCAi+//DKXX34599xzD3379mX69OnccsstzJ07tzo/AkmSJMWJUPh4K+5IqvMWLVpE/fr16datW7StuLiYrKwsJk+ezJgxYwKsTpIkSao6jgBLcW716tX06dOH/Px83njjDZ599lkGDRpERkYGQ4cODbo8SZIkqcp4DbAU52699VYOHDjAo48+SkFBAWlpafTo0YPHH3+czMzMoMuTJEmSqoxToCVJkiRJccEp0JIkSZKkuGAAliRJkiTFBQOwJEmSJCkuGIAlSZIkSXHBACxJkiRJigsGYEmSJElSXDAAS5IkSZLiggFYkiRJkhQX/h/V0/ZPgtWIrQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import patchworklib as pw\n", + "\n", + "p1 = pw.load_ggplot(plots[1], figsize=(4,4))\n", + "p2 = pw.load_ggplot(plots[0], figsize=(4,4))\n", + "p3 = pw.load_ggplot(plots[2], figsize=(4,4))\n", + "\n", + "arrangement = (p1|p2)[p1]/p3\n", + "arrangement.savefig(\"train_and_test_linear_trends.pdf\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/08_results_clauses_and_sketches_knockout_5groups.ipynb b/08_results_clauses_and_sketches_knockout_5groups.ipynb new file mode 100644 index 00000000..0f6352db --- /dev/null +++ b/08_results_clauses_and_sketches_knockout_5groups.ipynb @@ -0,0 +1,1229 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "92b47bf4", + "metadata": {}, + "source": [ + "## Results of the clauses experiment and the syntactic sketch knockout experiments" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b658bcdc", + "metadata": {}, + "outputs": [], + "source": [ + "import os, os.path\n", + "import re\n", + "\n", + "from pandas import DataFrame as df\n", + "from pandas import read_csv\n", + "import pandas\n", + "import seaborn as sns\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "8217e875", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "results_crossvalidation.csv\n", + "results_ensemble_conf_intervals.csv\n", + "results_ensemble_entropy.csv\n", + "results_ensemble_majority_voting.csv\n", + "results_full_data_malt_udpipe1.csv\n", + "results_gap_experiments.csv\n", + "results_gold_and_auto_ud_morph.csv\n", + "results_half_data.csv\n", + "results_smaller_data.csv\n", + "results_stanza_basic.csv\n", + "results_stanza_ME_conf_intervals.csv\n", + "results_stanza_ME_error_types.csv\n", + "results_stanza_ME_full_predict_on_clauses.csv\n", + "results_stanza_ME_full_predict_on_clauses_error_types.csv\n", + "results_stanza_ME_on_clauses.csv\n", + "results_stanza_ME_on_clauses_error_types.csv\n", + "results_stanza_ME_sketches_5groups_knockout.csv\n", + "results_stanza_ME_sketches_5groups_knockout_matrix.csv\n", + "results_stanza_ME_sketches_5randomgroups_knockout.csv\n", + "results_stanza_ME_sketches_5randomgroups_knockout_matrix.csv\n" + ] + } + ], + "source": [ + "input_dir = 'edt_2.6' # Experiments done on Estonian Dependency Treebank version 2.6\n", + "\n", + "for fname in os.listdir(input_dir):\n", + " if fname.startswith('results_') and fname.endswith('.csv'):\n", + " print(fname)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f8ecd862", + "metadata": {}, + "outputs": [], + "source": [ + "pandas.set_option('display.max_colwidth', None)" + ] + }, + { + "cell_type": "markdown", + "id": "1060eac5", + "metadata": {}, + "source": [ + "### A. the clauses experiment: train/evaluate stanza on clauses instead of on sentences" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "bf03c093", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
LAS_testLAS_trainLAS_gapUAS_testUAS_train
experiment
eval_stanza_morph_extended_on_clauses0.86230.92980.06740.89240.9492
\n", + "
" + ], + "text/plain": [ + " LAS_test LAS_train LAS_gap UAS_test \n", + "experiment \n", + "eval_stanza_morph_extended_on_clauses 0.8623 0.9298 0.0674 0.8924 \\\n", + "\n", + " UAS_train \n", + "experiment \n", + "eval_stanza_morph_extended_on_clauses 0.9492 " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# trained and evaluated on clauses\n", + "read_csv('edt_2.6/results_stanza_ME_on_clauses.csv', index_col=0)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f71e9b5e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
experimentLAS_testLAS_trainLAS_gapUAS_testUAS_traintest_wordstrain_words
1eval_stanza_morph_extended_full_data0.84860.91760.06890.87820.937848491389278
\n", + "
" + ], + "text/plain": [ + " experiment LAS_test LAS_train LAS_gap \n", + "1 eval_stanza_morph_extended_full_data 0.8486 0.9176 0.0689 \\\n", + "\n", + " UAS_test UAS_train test_words train_words \n", + "1 0.8782 0.9378 48491 389278 " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# for a comparison #1: trained and evaluated on sentences\n", + "full_data_results = read_csv('edt_2.6/results_stanza_basic.csv')\n", + "full_data_results[full_data_results['experiment'].str.contains('morph_extended')]" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "fad4c2ef", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
LAS_testLAS_trainUAS_testUAS_trainLAS_gap
experiment
eval_stanza_morph_extended_full_predict_on_clauses0.85440.90760.88730.93410.0532
\n", + "
" + ], + "text/plain": [ + " LAS_test LAS_train \n", + "experiment \n", + "eval_stanza_morph_extended_full_predict_on_clauses 0.8544 0.9076 \\\n", + "\n", + " UAS_test UAS_train \n", + "experiment \n", + "eval_stanza_morph_extended_full_predict_on_clauses 0.8873 0.9341 \\\n", + "\n", + " LAS_gap \n", + "experiment \n", + "eval_stanza_morph_extended_full_predict_on_clauses 0.0532 " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# for a comparison #2: trained on sentences and evaluated on clauses\n", + "read_csv('edt_2.6/results_stanza_ME_full_predict_on_clauses.csv', index_col=0)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "6dc2b383", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
E1E2E3E1_impactE2_impactE3_impactE1_rel_errorE2_rel_errorE3_rel_error
experiment
stanza_ME_on_clauses_error_types_on_test45013273370.87140.06330.06520.14050.01020.0555
\n", + "
" + ], + "text/plain": [ + " E1 E2 E3 E1_impact \n", + "experiment \n", + "stanza_ME_on_clauses_error_types_on_test 4501 327 337 0.8714 \\\n", + "\n", + " E2_impact E3_impact E1_rel_error \n", + "experiment \n", + "stanza_ME_on_clauses_error_types_on_test 0.0633 0.0652 0.1405 \\\n", + "\n", + " E2_rel_error E3_rel_error \n", + "experiment \n", + "stanza_ME_on_clauses_error_types_on_test 0.0102 0.0555 " + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Error types of the model trained and evaluated on clauses\n", + "err_types1 = read_csv('edt_2.6/results_stanza_ME_on_clauses_error_types.csv', index_col=0)\n", + "err_types1[['E1', 'E2', 'E3', 'E1_impact', 'E2_impact', 'E3_impact', 'E1_rel_error', 'E2_rel_error', 'E3_rel_error', ]]" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "42299eb8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
E1E2E3E1_impactE2_impactE3_impactE1_rel_errorE2_rel_errorE3_rel_error
experiment
stanza_ME_error_types_on_test482139811620.75550.06240.18210.14130.01170.1733
\n", + "
" + ], + "text/plain": [ + " E1 E2 E3 E1_impact E2_impact \n", + "experiment \n", + "stanza_ME_error_types_on_test 4821 398 1162 0.7555 0.0624 \\\n", + "\n", + " E3_impact E1_rel_error E2_rel_error \n", + "experiment \n", + "stanza_ME_error_types_on_test 0.1821 0.1413 0.0117 \\\n", + "\n", + " E3_rel_error \n", + "experiment \n", + "stanza_ME_error_types_on_test 0.1733 " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# for a comparison #1: Error types of the model trained and evaluated on sentences\n", + "err_types2 = read_csv('edt_2.6/results_stanza_ME_error_types.csv', index_col=0)\n", + "err_types2[['E1', 'E2', 'E3', 'E1_impact', 'E2_impact', 'E3_impact', 'E1_rel_error', 'E2_rel_error', 'E3_rel_error', ]]" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "1e12969c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
E1E2E3E1_impactE2_impactE3_impactE1_rel_errorE2_rel_errorE3_rel_error
experiment
stanza_ME_full_on_clauses_error_types_on_test47743523550.8710.06420.06480.1490.0110.0585
\n", + "
" + ], + "text/plain": [ + " E1 E2 E3 E1_impact \n", + "experiment \n", + "stanza_ME_full_on_clauses_error_types_on_test 4774 352 355 0.871 \\\n", + "\n", + " E2_impact E3_impact \n", + "experiment \n", + "stanza_ME_full_on_clauses_error_types_on_test 0.0642 0.0648 \\\n", + "\n", + " E1_rel_error E2_rel_error \n", + "experiment \n", + "stanza_ME_full_on_clauses_error_types_on_test 0.149 0.011 \\\n", + "\n", + " E3_rel_error \n", + "experiment \n", + "stanza_ME_full_on_clauses_error_types_on_test 0.0585 " + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# for a comparison #2: Error types of the model trained on sentences and evaluated on clauses\n", + "err_types2 = read_csv('edt_2.6/results_stanza_ME_full_predict_on_clauses_error_types.csv', index_col=0)\n", + "err_types2[['E1', 'E2', 'E3', 'E1_impact', 'E2_impact', 'E3_impact', 'E1_rel_error', 'E2_rel_error', 'E3_rel_error', ]]" + ] + }, + { + "cell_type": "markdown", + "id": "9ca17669", + "metadata": {}, + "source": [ + "### B. the syntactic sketches knockout experiments" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "043a8d4d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
grouped_sketchessupport
0[V]nsubj(L)obl(L);[V]nsubj(L)obl(L)obl(P);[S]advmod(L)cop(L)nsubj:cop(L);[S]cop(L)nsubj:cop(P);[V]aux(L)nsubj(L)obl(L);[V]obj(L)obl(L);[V]nsubj(L)xcomp(P);[V]obj(P)obl(L);[V]obj(P);[V]obj(L)4157
1[S]nmod(L);[V]advmod(L)nsubj(P);[V]aux(L)nsubj(L)obj(L);[S]amod(L);[V]advmod(L);[V];[V]nsubj(L)obj(P);[S];[V]nsubj(L)obj(P)obl(L);[V]nsubj(P)obl(L)5167
2[V]obj(L)obl(P);[V]nsubj(L)obj(L);[S]amod(L)cop(L)nsubj:cop(L);[V]advmod(L)obl(L);[S]cop(L)nmod(L)nsubj:cop(L);[V]advmod(L)nsubj(L)obj(L);[V]nsubj(L)obj(L)obl(P);[V]advmod(L)nsubj(L)obj(L)obl(L);[V]advmod(L)obj(L);[S]cop(L)nsubj:cop(L)3374
3[V]nsubj(L)obj(L)obl(L);[S]advmod(L);[S]cop(L)nsubj:cop(L)obl(L);[V]nsubj(L)obl(P);[V]nsubj(P)obl(P);[V]advmod(L)nsubj(L)obl(P);[V]nsubj(L);[V]advmod(L)nsubj(L);[V]nsubj(L)xcomp(L);[V]obl(P)4904
4[V]nsubj(L)obl(L)obl(L);[V]advmod(L)nsubj(L)obl(L);[V]aux(L);[X];[V]xcomp(P);[V]aux(L)nsubj(L);[S]nummod(L);[V]nsubj(P);[V]nsubj(P)obj(L);[V]obl(L)3332
\n", + "
" + ], + "text/plain": [ + " grouped_sketches \n", + "0 [V]nsubj(L)obl(L);[V]nsubj(L)obl(L)obl(P);[S]advmod(L)cop(L)nsubj:cop(L);[S]cop(L)nsubj:cop(P);[V]aux(L)nsubj(L)obl(L);[V]obj(L)obl(L);[V]nsubj(L)xcomp(P);[V]obj(P)obl(L);[V]obj(P);[V]obj(L) \\\n", + "1 [S]nmod(L);[V]advmod(L)nsubj(P);[V]aux(L)nsubj(L)obj(L);[S]amod(L);[V]advmod(L);[V];[V]nsubj(L)obj(P);[S];[V]nsubj(L)obj(P)obl(L);[V]nsubj(P)obl(L) \n", + "2 [V]obj(L)obl(P);[V]nsubj(L)obj(L);[S]amod(L)cop(L)nsubj:cop(L);[V]advmod(L)obl(L);[S]cop(L)nmod(L)nsubj:cop(L);[V]advmod(L)nsubj(L)obj(L);[V]nsubj(L)obj(L)obl(P);[V]advmod(L)nsubj(L)obj(L)obl(L);[V]advmod(L)obj(L);[S]cop(L)nsubj:cop(L) \n", + "3 [V]nsubj(L)obj(L)obl(L);[S]advmod(L);[S]cop(L)nsubj:cop(L)obl(L);[V]nsubj(L)obl(P);[V]nsubj(P)obl(P);[V]advmod(L)nsubj(L)obl(P);[V]nsubj(L);[V]advmod(L)nsubj(L);[V]nsubj(L)xcomp(L);[V]obl(P) \n", + "4 [V]nsubj(L)obl(L)obl(L);[V]advmod(L)nsubj(L)obl(L);[V]aux(L);[X];[V]xcomp(P);[V]aux(L)nsubj(L);[S]nummod(L);[V]nsubj(P);[V]nsubj(P)obj(L);[V]obl(L) \n", + "\n", + " support \n", + "0 4157 \n", + "1 5167 \n", + "2 3374 \n", + "3 4904 \n", + "4 3332 " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Randomly grouped sketches\n", + "read_csv('edt_2.6/preannotated/morph_extended_clauses/top_50_sketches_5_groups.csv', index_col=0)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "2f222980", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
group0group1group2group3group4
group089.3190.4691.0791.3990.93
group191.9390.4791.8991.5491.54
group292.9392.5091.3191.8692.46
group391.3691.2291.8689.0391.68
group491.9491.7992.0492.0489.47
\n", + "
" + ], + "text/plain": [ + " group0 group1 group2 group3 group4\n", + "group0 89.31 90.46 91.07 91.39 90.93\n", + "group1 91.93 90.47 91.89 91.54 91.54\n", + "group2 92.93 92.50 91.31 91.86 92.46\n", + "group3 91.36 91.22 91.86 89.03 91.68\n", + "group4 91.94 91.79 92.04 92.04 89.47" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Test matrix: rows are test sets, columns are models\n", + "test_matrix = read_csv('edt_2.6/results_stanza_ME_sketches_5groups_knockout_matrix.csv', index_col=0)\n", + "test_matrix = test_matrix.mul(100.0)\n", + "test_matrix" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "1298085d", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = sns.heatmap(test_matrix).get_figure()\n", + "plt.xlabel('Training (excludes group)', fontsize = 12)\n", + "plt.ylabel('Evaluation (exclusive to group)', fontsize = 12)\n", + "fig.savefig(\"sketches_5groups_knockout_matrix.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "f747e8d7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
group0group1group2group3group4
group091.1490.5791.5091.1491.00
group191.2991.7691.5490.9891.54
group291.9892.1892.3091.8691.98
group391.7991.5491.3691.6191.82
group492.3991.8491.8491.8492.04
\n", + "
" + ], + "text/plain": [ + " group0 group1 group2 group3 group4\n", + "group0 91.14 90.57 91.50 91.14 91.00\n", + "group1 91.29 91.76 91.54 90.98 91.54\n", + "group2 91.98 92.18 92.30 91.86 91.98\n", + "group3 91.79 91.54 91.36 91.61 91.82\n", + "group4 92.39 91.84 91.84 91.84 92.04" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Random control group test matrix: rows are test sets, columns are models\n", + "rnd_test_matrix = read_csv('edt_2.6/results_stanza_ME_sketches_5randomgroups_knockout_matrix.csv', index_col=0)\n", + "rnd_test_matrix = rnd_test_matrix.mul(100.0)\n", + "rnd_test_matrix" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "8618dc6b", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = sns.heatmap(rnd_test_matrix).get_figure()\n", + "plt.xlabel('Training (excludes group)', fontsize = 12)\n", + "plt.ylabel('Evaluation (exclusive to group)', fontsize = 12)\n", + "fig.savefig(\"sketches_5groups_random_knockout_matrix.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "13887eaf", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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ERUFzuNWeLI33FasWAEHBwecPHkSJ0+e1HtPkiQ23IiIiIiITJwQ8sypVorabfI33K5fvy53CEREREREVIyETNMB8I5bERo5cmS+5ZIkwdLSEl5eXggKCoKTk1MJR0ZERERERGQcZG+4nT59GqdOnYJGo4GPjw8A4NKlS1AqlahZsya+/fZbjBo1CuHh4fDz85M5WiIiIiIiMpQWAhLncSsU2UeVDAoKQqtWrXD37l1dP7fbt2/j7bffRpcuXXDnzh288cYbGDFihNyhEhERERHRK+B0AIUn+x232bNnY8+ePbCzs9OV2dvbIywsDK1bt8awYcMwceJEtG7dWsYoiYiIiIjoVWmFTHfcSlHDTfY7bklJSYiPj89Tfv/+fSQnJwMAHBwckJmZWdKhERERERFRERAy/istZG+4BQUFoU+fPti6dStu376N27dvY+vWrejbty+Cg4MBAMePH4e3t7e8gRIREREREclE9kclly5dihEjRqBz587Izs4GAJiZmSE0NBTz588HANSsWRPLly+XM0wiIiIiInpFfFSy8GRvuNnY2OC7777D/Pnzce3aNQBAtWrVYGNjo1unbt26MkVHRERERESFxXncCk/2hlsuGxsbBAQEyB0GEREREREVsZy+ZpwOoDCMpuFGRERERESlE++4FR4bbkREREREVKzYcCs82UeVJCIiIiIiohfjHTciIiIiIipWAgJCaGU5cqkhqMilp6eLSZMmifT0dLlDkQ1zwBwIwRwIwRyYMn52zIEQzIEQzEEu5uHVpKWlCTc3t9znJGV5ubm5ibS0NLlTUWiSEKXowU8jkZycDHt7eyQlJcHOzk7ucGTBHDAHAHMAMAemjJ8dcwAwBwBzkIt5eHXp6enIzMyU7fgqlQqWlpayHb+o8FFJIiIiIiIqNpaWlqWi4SQ3Dk5CRERERERk5NhwIyIiIiIiMnJsuBUDCwsLTJo0CRYWFnKHIhvmgDkAmAOAOTBl/OyYA4A5AJiDXMwDyY2DkxARERERERk53nEjIiIiIiIycmy4ERERERERGTk23IiIiIiIiIwcG25ERERERERGjg03I3br1i20b98eVlZWKF++PMaMGYPs7Gy5wypRQ4cORYMGDWBhYYG6devKHU6JO3v2LLp06QIPDw+o1Wr4+vpiwYIFcodVoh48eIC2bduiQoUKsLCwgIeHBwYP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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Display both heatmaps on a single figure\n", + "import matplotlib.pyplot as plt\n", + "\n", + "vmin = min(test_matrix.values.min(), rnd_test_matrix.values.min())\n", + "vmax = max(test_matrix.values.max(), rnd_test_matrix.values.max())\n", + "\n", + "fig, axs = plt.subplots(ncols=3, gridspec_kw=dict(width_ratios=[10,10,1]), figsize=(10, 4))\n", + "\n", + "sns.heatmap(test_matrix, annot=True, cbar=False, ax=axs[0], vmin=vmin, vmax=vmax)\n", + "sns.heatmap(rnd_test_matrix, annot=True, yticklabels=False, cbar=False, ax=axs[1], vmax=vmax, vmin=vmin)\n", + "\n", + "axs[0].set_title('main experiment', fontsize = 10)\n", + "axs[1].set_title('randomized control experiment', fontsize = 10)\n", + "axs[0].set_xlabel('Training (excludes group)', fontsize = 10)\n", + "axs[1].set_xlabel('Training (excludes group)', fontsize = 10)\n", + "axs[0].set_ylabel('Evaluation (exclusive to group)', fontsize = 10)\n", + "\n", + "fig.colorbar(axs[1].collections[0], cax=axs[2])\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "f080d4ba", + "metadata": {}, + "outputs": [], + "source": [ + "fig.savefig(\"sketches_5groups_both_experiment_matrices.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "d86a4d5e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
LAS_test
experiment
eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group091.42
eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group191.26
eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group291.61
eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group391.08
eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group491.29
\n", + "
" + ], + "text/plain": [ + " LAS_test\n", + "experiment \n", + "eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group0 91.42\n", + "eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group1 91.26\n", + "eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group2 91.61\n", + "eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group3 91.08\n", + "eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group4 91.29" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Results on test_50x50 file\n", + "r1 = read_csv('edt_2.6/results_stanza_ME_sketches_5groups_knockout.csv', index_col=0).drop(['UAS_test'], axis=1)\n", + "r1.mul(100.0)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "cf1e99b5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
LAS_test
experiment
eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group091.69
eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group191.54
eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group291.69
eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group391.48
eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group491.65
\n", + "
" + ], + "text/plain": [ + " LAS_test\n", + "experiment \n", + "eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group0 91.69\n", + "eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group1 91.54\n", + "eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group2 91.69\n", + "eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group3 91.48\n", + "eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group4 91.65" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Results on test_50x50 file (random control group)\n", + "r2 = read_csv('edt_2.6/results_stanza_ME_sketches_5randomgroups_knockout.csv', index_col=0).drop(['UAS_test'], axis=1)\n", + "r2.mul(100.0)" + ] + }, + { + "cell_type": "markdown", + "id": "566c2596", + "metadata": {}, + "source": [ + "#### Get Hoeffding's confidence bounds for test groups" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "dccae904", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "test_group0.conllu | words: 2777 | Hoeffding's bound: ± 2.58%\n", + "test_group1.conllu | words: 2318 | Hoeffding's bound: ± 2.82%\n", + "test_group2.conllu | words: 2532 | Hoeffding's bound: ± 2.70%\n", + "test_group3.conllu | words: 2825 | Hoeffding's bound: ± 2.56%\n", + "test_group4.conllu | words: 1985 | Hoeffding's bound: ± 3.05%\n", + "test_50x50.conllu | words: 12437 | Hoeffding's bound: ± 1.22%\n" + ] + } + ], + "source": [ + "import math\n", + "import os, os.path\n", + "import conllu\n", + "\n", + "# Counts words in conllu file.\n", + "def count_words(conllu_file):\n", + " with open(conllu_file, 'r', encoding='utf-8') as input_file:\n", + " conll_sentences = conllu.parse(input_file.read())\n", + " words = 0\n", + " for sentence in conll_sentences:\n", + " for word in sentence:\n", + " words += 1\n", + " return words\n", + "\n", + "# Calculates Hoeffding's bound. \n", + "# Parameters:\n", + "# n -- evaluation set size in words\n", + "# alpha -- confidence level is (1 - alpha)\n", + "def hoeffding_bounds(n, alpha):\n", + " return math.sqrt( (1/(2*n)*math.log(2/(alpha))) )\n", + "\n", + "# Calculate Hoeffding's bound for knockout test files\n", + "input_dirs = ['edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_splits', \\\n", + " 'edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_initial']\n", + "for input_dir in input_dirs:\n", + " for fname in os.listdir(input_dir):\n", + " if fname.startswith('test_'):\n", + " fpath = os.path.join(input_dir, fname)\n", + " words = count_words(fpath)\n", + " hfb_test = hoeffding_bounds(n=words, alpha=0.05)*100.0\n", + " print(fname, f\"| words: {words} | Hoeffding's bound: ± {hfb_test:.2f}%\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/README.md b/README.md deleted file mode 100644 index 545e8f67..00000000 --- a/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# Syntax experiments - -Various experiments with syntax analysers splitted among different branches. - -### I. Ablation experiments - -Investigations on the impact of training set size, morphological features and clausal patterns on syntactic parsing; experiments with parsing model ensembles; automatic evaluation and investigation of parsing errors - -**Branch:** [ablation_experiments](https://github.com/estnltk/syntax_experiments/tree/ablation_experiments) - - -### II. Syntax consistency - -Search for sentence level modifications that preserve syntax. In theory, syntax should be conserved when free entities are removed from sentences. Sometimes this is also true for bound entities such as objects. In practice, additional factors, such as interpunctuation and wording consistency, have a big inpact on the outcomes. - -The branch contains workflows for shortening sentences and preparing the results for manual labelling followed by small analysis of the results. Experiments are done for different phrase types where each phrase type is defined by the dependency relation predicted by stanza syntax analyser. - -**Branch:** [syntax_consistency](https://github.com/estnltk/syntax_experiments/tree/syntax_consistency) - - -### III. Consistency between adverbial phrases and named entities - -Adverbial phrases often coincide with geographical location or time expression, but not always. In these experiments, we study this problem in detail. For that we use a stanza syntax analyzer to extract adverbials and dedicated taggers for isolating named entities and time expressions. After that we build a workflow for extracting sentences where these annaotations are in potential conflict and extract corresponding sentences for manual labelling. - - -**Branch:** [adverbials](https://github.com/estnltk/syntax_experiments/tree/adverbials) - -### IV. Subcategorisation and argument structure - -Statistical methods for extracting information about subcategorisation of verbs using only automatically generated syntax for a large text corpus. -Subcategorization for verbs is defined by argument structure that specifies a list of selected arguments associated with specific lexical restrictions. -For Estonian, these restrictions are defined in terms of plausible cases. -It is imporant to note that not all arguments in the argument structure are mandatory and that the same verb can have more than one argument structure. - -Still it possible to use law of large numbers to extract important information about argument structure. -For that, we tabulate syntax level collocations between verb phrases and noun phrases defined through obl dependency relation. - -**Branch:** [subcat](https://github.com/estnltk/syntax_experiments/tree/subcat) - -### V. Semantic labelling - -Semantical categorisation based on the arguments structure. -Verbs place semantic restrictions on their arguments. -This can be exploited to categorise nouns into sementic categories and the other way around -- find whether a particular argument of a verb must satisfy certain restrictions. - -**Branch:** [semantic_labelling](https://github.com/estnltk/syntax_experiments/tree/semantic_labelling) - -### VI. Outdated experiments - -Contains code of legacy experiments that are no longer supported - -**Branch:** [legacy](https://github.com/estnltk/syntax_experiments/tree/legacy) diff --git a/X1_previously_reported_performances.ipynb b/X1_previously_reported_performances.ipynb new file mode 100644 index 00000000..0b5a6ee8 --- /dev/null +++ b/X1_previously_reported_performances.ipynb @@ -0,0 +1,917 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c5afafad", + "metadata": {}, + "source": [ + "### Previously reported dependency parsing performances for Estonian and English\n", + "\n", + "Sources:\n", + "\n", + "* Stanford @ CoNLL 2017 Shared Task: https://universaldependencies.org/conll17/results-las.html\n", + "* HIT-SCIR, StanfordNLP & UDPipe 1.2 @ CoNLL 2018 Shared Task: https://universaldependencies.org/conll18/results-las.html\n", + "* Stanza version 1.0: https://stanfordnlp.github.io/stanza/v100performance.html\n", + "* Stanza version 1.3: https://stanfordnlp.github.io/stanza/v130performance.html\n", + "* UDPipe 2: https://ufal.mff.cuni.cz/udpipe/2/models \n", + "* Udify: https://arxiv.org/pdf/1904.02099.pdf\n", + "* Trankit: https://trankit.readthedocs.io/en/latest/performance.html\n", + "* RobertNLP @ IWPT 2021 Shared Task: https://universaldependencies.org/iwpt21/results_official_fine.html ; https://aclanthology.org/2021.iwpt-1.21.pdf\n", + "\n", + "* Corpus sizes:\n", + " * Estonian:\n", + " * https://github.com/UniversalDependencies/UD_Estonian-EDT/blob/r2.0/stats.xml\n", + " * https://github.com/UniversalDependencies/UD_Estonian-EDT/blob/r2.2/stats.xml\n", + " * https://github.com/UniversalDependencies/UD_Estonian-EDT/blob/r2.5/stats.xml\n", + " * https://github.com/UniversalDependencies/UD_Estonian-EDT/blob/r2.7/stats.xml\n", + " * https://github.com/UniversalDependencies/UD_Estonian-EWT/blob/r2.7/stats.xml\n", + " * https://github.com/UniversalDependencies/UD_Estonian-EDT/blob/r2.8/stats.xml\n", + " * https://github.com/UniversalDependencies/UD_Estonian-EDT/blob/r2.10/stats.xml\n", + " * English:\n", + " * https://github.com/UniversalDependencies/UD_English-EWT/blob/r2.0/stats.xml\n", + " * https://github.com/UniversalDependencies/UD_English-EWT/blob/r2.2/stats.xml\n", + " * https://github.com/UniversalDependencies/UD_English-EWT/blob/r2.5/stats.xml\n", + " * https://github.com/UniversalDependencies/UD_English-EWT/blob/r2.7/stats.xml\n", + " * https://github.com/UniversalDependencies/UD_English-GUM/blob/r2.7/stats.xml\n", + " * https://github.com/UniversalDependencies/UD_English-EWT/blob/r2.6/stats.xml\n", + " * https://github.com/UniversalDependencies/UD_English-EWT/blob/r2.10/stats.xml\n", + "\n", + "(last checked: 2024-02-01)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "243cc05f", + "metadata": {}, + "outputs": [], + "source": [ + "#!pip install adjustText" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "b89be590", + "metadata": {}, + "outputs": [], + "source": [ + "from plotnine import ggplot, geom_point, aes, geom_line, geom_text\n", + "from plotnine import geom_abline, geom_hline, ggtitle, theme, xlab, labs\n", + "from plotnine import scale_color_manual, element_line, element_text, theme_bw\n", + "from plotnine import facet_wrap, geom_step\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e89c15a0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ud_versionexact_corpus_sizecorpus_sizeLASparsercorpusyear
02.0346283471.65Stanfordet_edt2017
12.236635136685.35HIT-SCIRet_edt2018
22.236635136683.84StanfordNLPet_edt2018
32.543776943783.82Stanza v1.0et_edt2020
42.843776743784.43Stanza v1.3et_edt2021
52.236635136675.02UDPipe 1.2et_edt2018
62.236635136683.26UDPipe 2 prototypeet_edt2018
72.143785043786.16UDPipe 2et_edt2022
82.3120323091203286.67Udify v1.0et_edt2019
92.749416849389.52RobertNLPet_edt2021
102.543776943789.52Trankit-Let_edt2021
112.022975322982.23Stanforden_ewt2017
122.225482925484.57HIT-SCIRen_ewt2018
132.225482925483.87StanfordNLPen_ewt2018
142.525482925483.59Stanza v1.0en_ewt2020
152.825483025484.91Stanza v1.3en_ewt2021
162.225482925477.56UDPipe 1.2en_ewt2018
172.225482925482.51UDPipe 2 prototypeen_ewt2018
182.125482125488.10UDPipe 2en_ewt2022
192.3120323091203288.50Udify v1.0en_ewt2019
202.736821436790.27RobertNLPen_ewt2021
212.525482925489.40Trankit-Len_ewt2021
\n", + "
" + ], + "text/plain": [ + " ud_version exact_corpus_size corpus_size LAS parser \n", + "0 2.0 34628 34 71.65 Stanford \\\n", + "1 2.2 366351 366 85.35 HIT-SCIR \n", + "2 2.2 366351 366 83.84 StanfordNLP \n", + "3 2.5 437769 437 83.82 Stanza v1.0 \n", + "4 2.8 437767 437 84.43 Stanza v1.3 \n", + "5 2.2 366351 366 75.02 UDPipe 1.2 \n", + "6 2.2 366351 366 83.26 UDPipe 2 prototype \n", + "7 2.1 437850 437 86.16 UDPipe 2 \n", + "8 2.3 12032309 12032 86.67 Udify v1.0 \n", + "9 2.7 494168 493 89.52 RobertNLP \n", + "10 2.5 437769 437 89.52 Trankit-L \n", + "11 2.0 229753 229 82.23 Stanford \n", + "12 2.2 254829 254 84.57 HIT-SCIR \n", + "13 2.2 254829 254 83.87 StanfordNLP \n", + "14 2.5 254829 254 83.59 Stanza v1.0 \n", + "15 2.8 254830 254 84.91 Stanza v1.3 \n", + "16 2.2 254829 254 77.56 UDPipe 1.2 \n", + "17 2.2 254829 254 82.51 UDPipe 2 prototype \n", + "18 2.1 254821 254 88.10 UDPipe 2 \n", + "19 2.3 12032309 12032 88.50 Udify v1.0 \n", + "20 2.7 368214 367 90.27 RobertNLP \n", + "21 2.5 254829 254 89.40 Trankit-L \n", + "\n", + " corpus year \n", + "0 et_edt 2017 \n", + "1 et_edt 2018 \n", + "2 et_edt 2018 \n", + "3 et_edt 2020 \n", + "4 et_edt 2021 \n", + "5 et_edt 2018 \n", + "6 et_edt 2018 \n", + "7 et_edt 2022 \n", + "8 et_edt 2019 \n", + "9 et_edt 2021 \n", + "10 et_edt 2021 \n", + "11 en_ewt 2017 \n", + "12 en_ewt 2018 \n", + "13 en_ewt 2018 \n", + "14 en_ewt 2020 \n", + "15 en_ewt 2021 \n", + "16 en_ewt 2018 \n", + "17 en_ewt 2018 \n", + "18 en_ewt 2022 \n", + "19 en_ewt 2019 \n", + "20 en_ewt 2021 \n", + "21 en_ewt 2021 " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data = {\n", + "'ud_version': [2.0, 2.2, 2.2, 2.5, 2.8, 2.2, 2.2, 2.10, 2.3, 2.7, 2.5]+\\\n", + " [2.0, 2.2, 2.2, 2.5, 2.8, 2.2, 2.2, 2.10, 2.3, 2.7, 2.5],\n", + "# exact corpus size (in words)\n", + "'exact_corpus_size':\n", + " [34628, 366351, 366351, 437769, 437767, 366351, 366351, 437850, 12032309, 437769+56399, 437769]+\\\n", + " [229753, 254829, 254829, 254829, 254830, 254829, 254829, 254821, 12032309, 254829+113385, 254829], \n", + "# total corpus size (in thousands of words)\n", + "'corpus_size': [ 34, 366, 366, 437, 437, 366, 366, 437, 12032, 437+56, 437]+\\\n", + " [229, 254, 254, 254, 254, 254, 254, 254, 12032, 254+113, 254], \n", + "'LAS': [71.65, 85.35, 83.84, 83.82, 84.43, 75.02, 83.26, 86.16, 86.67, 89.52, 89.52]+\\\n", + " [82.23, 84.57, 83.87, 83.59, 84.91, 77.56, 82.51, 88.10, 88.50, 90.27, 89.40],\n", + "'parser': ['Stanford', 'HIT-SCIR', 'StanfordNLP', 'Stanza v1.0', 'Stanza v1.3', 'UDPipe 1.2', 'UDPipe 2 prototype', 'UDPipe 2', 'Udify v1.0', 'RobertNLP', 'Trankit-L']+\\\n", + " ['Stanford', 'HIT-SCIR', 'StanfordNLP', 'Stanza v1.0', 'Stanza v1.3', 'UDPipe 1.2', 'UDPipe 2 prototype', 'UDPipe 2', 'Udify v1.0', 'RobertNLP', 'Trankit-L'],\n", + "# corpus name\n", + "'corpus': ['et_edt', 'et_edt', 'et_edt', 'et_edt', 'et_edt', 'et_edt', 'et_edt', 'et_edt', 'et_edt', 'et_edt', 'et_edt']+\\\n", + " ['en_ewt', 'en_ewt', 'en_ewt', 'en_ewt', 'en_ewt', 'en_ewt', 'en_ewt', 'en_ewt', 'en_ewt', 'en_ewt', 'en_ewt'],\n", + "'year': [2017, 2018, 2018, 2020, 2021, 2018, 2018, 2022, 2019, 2021, 2021]+\\\n", + " [2017, 2018, 2018, 2020, 2021, 2018, 2018, 2022, 2019, 2021, 2021],\n", + "}\n", + "results = pd.DataFrame.from_dict(data)\n", + "results" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b3500c8d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ud_versionexact_corpus_sizecorpus_sizeLASparsercorpusyearLAS_lowerLAS_upper
02.0346283471.65Stanfordet_edt201769.3473.96
12.236635136685.35HIT-SCIRet_edt201884.6486.06
22.236635136683.84StanfordNLPet_edt201883.1384.55
32.543776943783.82Stanza v1.0et_edt202083.1784.47
42.843776743784.43Stanza v1.3et_edt202183.7885.08
52.236635136675.02UDPipe 1.2et_edt201874.3175.73
62.236635136683.26UDPipe 2 prototypeet_edt201882.5583.97
72.143785043786.16UDPipe 2et_edt202285.5186.81
82.3120323091203286.67Udify v1.0et_edt201986.5586.79
92.749416849389.52RobertNLPet_edt202188.9190.13
102.543776943789.52Trankit-Let_edt202188.8790.17
112.022975322982.23Stanforden_ewt201781.3383.13
122.225482925484.57HIT-SCIRen_ewt201883.7285.42
132.225482925483.87StanfordNLPen_ewt201883.0284.72
142.525482925483.59Stanza v1.0en_ewt202082.7484.44
152.825483025484.91Stanza v1.3en_ewt202184.0685.76
162.225482925477.56UDPipe 1.2en_ewt201876.7178.41
172.225482925482.51UDPipe 2 prototypeen_ewt201881.6683.36
182.125482125488.10UDPipe 2en_ewt202287.2588.95
192.3120323091203288.50Udify v1.0en_ewt201988.3888.62
202.736821436790.27RobertNLPen_ewt202189.5690.98
212.525482925489.40Trankit-Len_ewt202188.5590.25
\n", + "
" + ], + "text/plain": [ + " ud_version exact_corpus_size corpus_size LAS parser \n", + "0 2.0 34628 34 71.65 Stanford \\\n", + "1 2.2 366351 366 85.35 HIT-SCIR \n", + "2 2.2 366351 366 83.84 StanfordNLP \n", + "3 2.5 437769 437 83.82 Stanza v1.0 \n", + "4 2.8 437767 437 84.43 Stanza v1.3 \n", + "5 2.2 366351 366 75.02 UDPipe 1.2 \n", + "6 2.2 366351 366 83.26 UDPipe 2 prototype \n", + "7 2.1 437850 437 86.16 UDPipe 2 \n", + "8 2.3 12032309 12032 86.67 Udify v1.0 \n", + "9 2.7 494168 493 89.52 RobertNLP \n", + "10 2.5 437769 437 89.52 Trankit-L \n", + "11 2.0 229753 229 82.23 Stanford \n", + "12 2.2 254829 254 84.57 HIT-SCIR \n", + "13 2.2 254829 254 83.87 StanfordNLP \n", + "14 2.5 254829 254 83.59 Stanza v1.0 \n", + "15 2.8 254830 254 84.91 Stanza v1.3 \n", + "16 2.2 254829 254 77.56 UDPipe 1.2 \n", + "17 2.2 254829 254 82.51 UDPipe 2 prototype \n", + "18 2.1 254821 254 88.10 UDPipe 2 \n", + "19 2.3 12032309 12032 88.50 Udify v1.0 \n", + "20 2.7 368214 367 90.27 RobertNLP \n", + "21 2.5 254829 254 89.40 Trankit-L \n", + "\n", + " corpus year LAS_lower LAS_upper \n", + "0 et_edt 2017 69.34 73.96 \n", + "1 et_edt 2018 84.64 86.06 \n", + "2 et_edt 2018 83.13 84.55 \n", + "3 et_edt 2020 83.17 84.47 \n", + "4 et_edt 2021 83.78 85.08 \n", + "5 et_edt 2018 74.31 75.73 \n", + "6 et_edt 2018 82.55 83.97 \n", + "7 et_edt 2022 85.51 86.81 \n", + "8 et_edt 2019 86.55 86.79 \n", + "9 et_edt 2021 88.91 90.13 \n", + "10 et_edt 2021 88.87 90.17 \n", + "11 en_ewt 2017 81.33 83.13 \n", + "12 en_ewt 2018 83.72 85.42 \n", + "13 en_ewt 2018 83.02 84.72 \n", + "14 en_ewt 2020 82.74 84.44 \n", + "15 en_ewt 2021 84.06 85.76 \n", + "16 en_ewt 2018 76.71 78.41 \n", + "17 en_ewt 2018 81.66 83.36 \n", + "18 en_ewt 2022 87.25 88.95 \n", + "19 en_ewt 2019 88.38 88.62 \n", + "20 en_ewt 2021 89.56 90.98 \n", + "21 en_ewt 2021 88.55 90.25 " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import math\n", + "\n", + "# Calculates Hoeffding's bound. \n", + "# Parameters:\n", + "# n -- evaluation set size in words\n", + "# alpha -- confidence level is (1 - alpha)\n", + "def hoeffding_bounds(n, alpha=0.05):\n", + " return math.sqrt( (1/(2*n)*math.log(2/(alpha))) )\n", + "\n", + "# Add Hoeffding bounds confidence intervals to each LAS in table\n", + "def add_hoeffding_bound_conf_intervals(dframe, test_proportion=0.1):\n", + " # Initialize new columns\n", + " dframe['LAS_lower'] = [0.0]*len(dframe)\n", + " dframe['LAS_upper'] = [0.0]*len(dframe)\n", + " # Calculate conf intervals for each LAS\n", + " for ind in dframe.index:\n", + " LAS = dframe['LAS'][ind]\n", + " corpus_size = dframe['exact_corpus_size'][ind]\n", + " test_corpus_size = corpus_size*test_proportion\n", + " hfb = hoeffding_bounds(test_corpus_size) * 100.0\n", + " dframe.loc[ind,'LAS_lower'] = round(LAS - hfb, 2)\n", + " dframe.loc[ind,'LAS_upper'] = round(LAS + hfb, 2)\n", + " return dframe\n", + "\n", + "add_hoeffding_bound_conf_intervals(results)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "897a20ce", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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4usrnRIR2SIWRkRERGSW4mLplIjIFrEwIiIiIrPcuCGdEhHZIhZGREREZJa6daVTIiJbxMKIiIiIzKJSSadERLaIhRERERGZJS1NOiUiskUsjIiIiMgstWpJp0REtoiFEREREZnF1VU6JSKyRSyMiIiIyCy3b0unRES2iIURERERmUUI6ZSIyBaxMCIiIiKz1K4tnRIR2SIWRkRERGSW3FzplIjIFrEwIiIiIrPcvfvXVKFQ4M6dO5L5QUFBiI2NBQBERESgYcOGCA8PR+PGjdG9e3f8+OOPxmVjYmLg4uKC8PBwtGjRAh07dsT58+cBAL169UJCQkKVxZ2cnIyIiAi4u7sjPDz8gcvOnDkTjz/+OFq2bIk2bdpgz549VRYHEVkHFkZERERkFn9/6fRhFi1ahNjYWFy5cgXvvvsuIiMj8d133xnnN23aFLGxsTh//jz69euHMWPGAAD++9//omnTplUWt5ubG95//31s2rTpoct26tQJZ8+exblz57B69WoMHDgQubxERvRIYWFEREREZikpkU4rIiIiAnPnzsUHH3wgO79nz57Gq0T3X3maPHkynnzySTRu3BhvvvkmxJ+jP6Snp2PQoEFo27YtwsLC8N5778luu3bt2ujYsSO0Wu1D43z++efh4uICAAgLC4MQAhkZGRU9XCKyYiyMiIiIyCzp6dJpRT311FO4dOmS7LzNmzejdevWsvPi4uJw5MgRnD9/HgcPHsTXX38NAIiMjMSrr76KEydO4OzZszh16hS++eabygUnY+3atXjssccQGBhYZdskIstTWToAIiIism3e3tKpHIVCUeY8cd843wkJCcbv/AQHB2P9+vWy640cORJqtRpqtRrDhw/H/v370bt3bxw4cAA3btwwLpeTk1Nl302Kjo7GvHnzsG/fvgceExHZHhZGREREZBZHx7+m3t7eyMzMhIeHh3H+zZs34ePjU+b6J0+eRPPmzY3vDd8xqiiFQmEsso4ePWq89a2qHDx4EGPGjMEPP/xQpd91IiLrYPHCaODAgZL3RUVFaNOmjfF+4HHjxuHOnTtQKkvv+vP29sann35a43ESERGRvOvX/5r26NEDn3/+OT788EMAwJdffonHHnsMfn5+suv+8ssvmDt3LpYtW1bh/X711VcYOnQodDodNm3ahGnTpsHV1RURERFYuHAh5s2bBwC4du0a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ky2KHiV+fn744Ycf4OHhUeZthY6OjvDw8MAPP/xgUhQ9SkJCQrBgwQI4ODiUa3kHBwcsWLCgQrfR2RIvLy9Mnjy5UgN0TJkyxaQoIiKyRyyMiMimBAcH49///jeuXbuGO3fu4Nq1a/j3v/8te1XhURQWFoajR4/i1VdfNX7nyPDLsJubG1599VUcPXoUYWFhlgyzRrz22muYP38+FArFAwtFhUKB+fPn47XXXqvhCGvWnDlz0KdPn3IXRyqVCn369MHs2bOrOTIiIttQsT8tERFZEXNGn7Nlfn5+iIqKwsyZM3H27FlkZWUZB1qo7KAEtkihUGDSpEno2rUrVq5ciY0bN6KgoABKpRJ6vR7Ozs4YPnw4xo8f/8heKbqXUqnE6tWrERgYiCVLlpjccmpguPV0ypQpmD17tt3+HBER3Y+FERGRjXJ2dka7du0sHYbFhYSEYNGiRYiKikJ8fDxycnLg6uqKkJAQu3vGjVKpxNy5czFp0iRs2LABn332meThr35+fnjllVcwfPhweHl5WTBSIiLrw8KIiIgeCbVq1ULbtm0tHYZV8PLywrRp0zB16lTk5eUZi0WNRiP7rCoiImJhRERE9MhSKBTQarVV9hBYIqJHGW8sJiIiIiIiu8fCiIiIiIiI7B5vpSMiq5SYmGjpECyOOSjFPJhiToiIqp5CVOSx4URERERERI8g3kpHRERERER2j4URERERERHZPRZGRERERERk91gYERERERGR3WNhREREREREdo+FERERERER2T0WRkREREREZPdYGBERERERkd1jYURERERERHaPhREREREREdk9FkZERERERGT3WBgREREREZHdY2FERERERER2j4URERERERHZPRZGRERERERk91gYERERERGR3WNhREREREREdo+FERERERER2T0WRkREREREZPdYGBERERERkd1jYURERERERHaPhREREREREdk9FkZERERERGT3WBgREREREZHdY2FERERERER2j4URERERERHZvf8HWzCT++FcmvYAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "p = ggplot(results) + theme_bw()\n", + "p = p + theme(legend_title=element_text(size=10), figure_size=(10, 4),)\n", + "p = p + facet_wrap(['corpus'], nrow = 1, labeller={'et_edt':'et_edt (Estonian)', 'en_ewt':'en_ewt (English)'})\n", + "p = p + geom_point(aes(x='year', y='LAS', color='corpus', size='corpus_size'), show_legend={'size': True})\n", + "# Stanza's 95% conf intervals on EN\n", + "p = p + geom_step(aes(x='year', y='LAS_upper'), \n", + " data=results[(results['corpus']=='en_ewt') & results['parser'].str.contains('Stan')], \n", + " linetype='dashed', color='red')\n", + "p = p + geom_step(aes(x='year', y='LAS_lower'), \n", + " data=results[(results['corpus']=='en_ewt') & results['parser'].str.contains('Stan')], \n", + " linetype='dashed', color='red')\n", + "# Stanza's 95% conf intervals on ET\n", + "p = p + geom_step(aes(x='year', y='LAS_upper'), \n", + " data=results[(results['corpus']=='et_edt') & results['parser'].str.contains('Stan')], \n", + " linetype='dashed', color='blue')\n", + "p = p + geom_step(aes(x='year', y='LAS_lower'), \n", + " data=results[(results['corpus']=='et_edt') & results['parser'].str.contains('Stan')], \n", + " linetype='dashed', color='blue')\n", + "# Complete conf interval lines (extend from 2021 to 2022)\n", + "last_en_ewt_stanza = \\\n", + " (results[(results['corpus']=='en_ewt') & results['parser'].str.contains('Stan')].tail(1)).copy()\n", + "last_en_ewt_stanza = pd.concat([last_en_ewt_stanza, last_en_ewt_stanza], ignore_index=True)\n", + "last_en_ewt_stanza.loc[1, 'year'] = 2022\n", + "last_et_edt_stanza = \\\n", + " (results[(results['corpus']=='et_edt') & results['parser'].str.contains('Stan')].tail(1)).copy()\n", + "last_et_edt_stanza = pd.concat([last_et_edt_stanza, last_et_edt_stanza], ignore_index=True)\n", + "last_et_edt_stanza.loc[1, 'year'] = 2022\n", + "p = p + geom_line(aes(x='year', y='LAS_upper'), \n", + " data=last_en_ewt_stanza, \n", + " linetype='dashed', color='red')\n", + "p = p + geom_line(aes(x='year', y='LAS_lower'), \n", + " data=last_en_ewt_stanza, \n", + " linetype='dashed', color='red')\n", + "p = p + geom_line(aes(x='year', y='LAS_upper'), \n", + " data=last_et_edt_stanza, \n", + " linetype='dashed', color='blue')\n", + "p = p + geom_line(aes(x='year', y='LAS_lower'), \n", + " data=last_et_edt_stanza, \n", + " linetype='dashed', color='blue')\n", + "# Override default colors\n", + "p = p + scale_color_manual(values = {'et_edt': 'blue', 'en_ewt': 'red'}, guide=False)\n", + "# Add adjusted labels\n", + "p = p + geom_text(aes(x='year', y='LAS', label=\"parser\"), data=results[results['corpus']=='et_edt'], size=8, \n", + " adjust_text={'arrowprops': {'arrowstyle': '-'}} )\n", + "p = p + geom_text(aes(x='year', y='LAS', label=\"parser\"), data=results[results['corpus']=='en_ewt'], size=8, \n", + " adjust_text={'arrowprops': {'arrowstyle': '-'}} )\n", + "# Override labels\n", + "p = p + labs(size = \"corpus size (thousands of words)\")\n", + "p = p + theme(legend_position='bottom', legend_box_spacing=0.25)\n", + "display(p)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "44524d40", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Programmid\\Miniconda3\\envs\\py38_estnltk_neural\\lib\\site-packages\\plotnine\\ggplot.py:718: PlotnineWarning: Saving 10 x 4 in image.\n", + "C:\\Programmid\\Miniconda3\\envs\\py38_estnltk_neural\\lib\\site-packages\\plotnine\\ggplot.py:719: PlotnineWarning: Filename: figure_reported_las_vs_years.pdf\n" + ] + } + ], + "source": [ + "p.save('figure_reported_las_vs_years.pdf')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/background_data/edt_punct_tokens.txt b/background_data/edt_punct_tokens.txt new file mode 100644 index 00000000..4212af0b --- /dev/null +++ b/background_data/edt_punct_tokens.txt @@ -0,0 +1,35 @@ +! +!!! +" +( +) +* ++ +, +,, +- +-- +-. +. +.. +... +.... +/ +: +; +< += +> +? +?? +????? +?????????? +????????????? +[ +] +oh-ma-olen-nii-hoolitsev-ja-seksika-hingeeluga +proto.groovetech.com/mk-demf.rxml?file=/demf/mainstage/day2/mainstageday2-kennydixonjr.rm +– +— +“ +” diff --git a/background_data/readme.md b/background_data/readme.md new file mode 100644 index 00000000..fe476b46 --- /dev/null +++ b/background_data/readme.md @@ -0,0 +1,4 @@ +# Background data used in experiments + +* `edt_punct_tokens.txt` -- all punctuation tokens of the EDT-2.6 corpus. Automatically aquired, searching for tokens with `xpos == 'Z'`; +* `visl_lemmas.txt` -- lemmas extracted from [Estonian Constraint Grammar](https://github.com/EstSyntax/EstCG) ruleset; \ No newline at end of file diff --git a/background_data/visl_lemmas.txt b/background_data/visl_lemmas.txt new file mode 100644 index 00000000..b4732596 --- /dev/null +++ b/background_data/visl_lemmas.txt @@ -0,0 +1,6508 @@ +(.*)\\. +[0-9]++(.*) +$1 +aamen +äärde +ääres +äärest +aaria +äärista +aasi +aasta +(.*)_aasta +Aasta +aastakümme +aasta_kümme +aastane +aasta=ne +aasta_päev +aasta_sajand +ab +AB +abi +abiellu +abi_ellu +abi_elluma +abi_elu +abikaasa +abi_kaasa +abil +abista +absorbeeru +abstraheeri +abstraheeru +Academia +adapteeru +admiral +adsorbeeru +adu +ae +aed +(.*)_aed +aeg +aega +aegajalt +aeg_ajalt +aegle +aeglustu +aegu +aele +äesta +afeel +aga +äga +ägetse +ägise +aglutineeru +ah +ahasta +ahata +ahelda +aheldu +ahene +aheta +ahheta +ähise +ahista +ahki +ähki +ähkle +ahku +ähma=ne +ähmastu +ähmene +ähmi +ähmu +ahnitse +ahtru +ahtu +ähvarda +ähvardus +ahvatle +ahvi +aiata +aida +aida=nu +aidanud +aida=nud +aieta +äiga +aim +aima +aimu +aina +aine +ainult +aisti +aita +aitama +aja +ajal +aja_leht +Aja_leht +ajalukk +aja_lukk +ajatele +ajatle +aja_vahemik +ajel +ajendu +aju +akadeemia +Akadeemia +akadeemik +akommodeeru +akompaneeri +aktiivistu +aktiviseeru +aktualiseeru +akumuleeru +akvaarium +al +alaarene +ala_ealine +alahinda +alanda +alandu +alane +alarmeeri +alates +alati +alevik +Alevik +alga +algul +algult +algus +alista +alistu +alkohol +all +alla +allapoole +alla_poole +alles +allkiri +all_kiri +allpool +all_pool +allu +alluta +alpi +alt +altpoolt +alt_poolt +alumine +alune +alus +alusta +alustu +ämber +Ameerika +ameerikastu +amele +amerikaniseeru +ameti_ala=ne +ametiala=ne +ametle +ammenda +ammendu +ammu +ammu=ne +amüseeri +an +anasta +and +andeks +andesta +andma +andu +ankurdu +annihileeru +ansambel +(.*)_ansambel +Ansambel +antav +an=tu +antud +an=tud +anu +äparda +äpardu +apelleeri +apelsinid +aplodeeri +aprill +aprillikuu +aprilli_kuu +ära +arabiseeru +arastu +ärata +arbu +arbuta +arenda +arene +ärge +ärgem +argipäevastu +argistu +ärgita +ärgitus +ärgu +argumenteeri +äri +äritse +ärka +armasta +armatse +armistu +armu +armusta +ärple +ärrita +ärritu +arst +(.*)arst +arsti +artikkel +aru +aruta +arutle +arv +arva +arvamus +arvel +arvele +arvelt +arvesta +arvukalt +arvusta +arvuta +as +AS +asemel +asemele +asenda +asendama +asendamine +asenda=mine +asenda_mine +asendu +asene +aseta +asetse +asetu +asi +asja_koha=ne +asjastu +askelda +assimileeru +ässita +assotsieeru +aste +astu +asu +asune +asusta +asuta +asutus +(.*)_asutus +Asutus +atakeeri +atrofeeru +august +aura +auru +aurustu +auskulteeri +austa +austatud +au_tasusta +automatiseeru +ava +avakueeru +avalda +avaldu +avameeldu +avameelitse +avane +avansseeri +avarda +avardu +avasta +avastu +([a-z]++)=(.*) +([a-z]++)_(.*) +\?[a-z]++ +([a-z]++)aasta=ne(.*) +([a-z]++)=ne +([a-z]++)päeva=ne(.*) +balansseeri +balti +Balti +band +(.*)_band +Band +barbariseeru +barrikadeeri +baseeru +bassein +blaseeru +blokeeri +blokeeru +boheemitse +boikoteeri +bols^eviseeru +braveeri +bravuuritse +broneeri +bürokraadistu +bürokratiseeru +ca +comes + cond) ( +Cpr +dateeri +dateeru +de +(.*)de +debateeri +debüteeri +defileeri +defineeri +deformeeru +degenereeru +degradeeru +dehumaniseeru +Dekaad +deklameeri +deklareeri +deklasseeru +deklineeru +dekvalifitseeru +demobiliseeru +demokratiseeru +demoraliseeru +denatureeru +desarmeeru +deserteeri +deserteeru +desinfitseeri +desorganiseeru +desorienteeri +desorienteeru +destabiliseeru +detailiseeri +detsember +detsentraliseeru +detsimeetrine +devalveeru +devieeri +diafragmeeri +diagnoosi +diferentseeru +ðifreeri +diftongeeru +difundeeru +dikteeri +direktiiv +direktor +disharmoneeru +diskrimineeri +diskuteeri +dislotseeru +dispergeeru +disputeeri +dissoneeri +dissotsieeru +distantseeru +distsiplineeru +divisjon +(.*)_divisjon +Divisjon +dogmatiseeru +doktor +doktoreeru +donna +dotsent +dr +drapeeru +(.*)dud +duelleri +duelli +(.*)e +ebale +eba_määra=ne +ebardu +ebele +edasi +Edasi +edenda +edene +edesta +edu +eduta +edvista +eel +eelda +eeli +eelista +eelistus +eelmine +eelne +eemalda +eemaldu +eemale +ees +eesmärk +ees_märk +ees_pool +eest +eesti +Eesti +eesti_maa +Eestimaa +Eesti_maa +eestistu +eest_poolt +efektitse +ega +ehita +ehk +ehkki +ehmata +ehmu +ehti +ei +eile +einesta +eineta +eita +eks +eksalteeru +eksi +eksisteeri +eksita +ekskureeri +ekspatrieeru +eksperimenteeri +ekspluateeri +ekstraheeru +ela +elata +elatu +elavne +elberda +elektriseeru +elik +elu +elu_aeg +Elu_aeg +elu_koht +elune +elustu +elutse +ema +ema=ne +emantsipeeru +emba +embkumb +emb_kumb +emb-kumb +emigreeru +enam +enamasti +enamik +enamus +end +enim +enne +ennusta +ent +eostu +eputa +eralda +eraldu +erane +erenda +ereta +erga +ergastu +erguta +eri_ala=ne +eriala=ne +erine +erista +eristu +erita +eriti +erku +erodeeru +eruta +erutu +esemestu +esi +esildu +esile +esile_kutsutud +esimees +esi_mees +esimene +esi_naine +esinda +esine +esiteks +esitle +esitus +eskorti +esmaspäev +esmas_päev +esteeditse +Estonia +et +etapp +etendu +ette +ette_panek +ette_poole +(.*)_ette_võte +ette_võte +Ette_võte +euro=ne +euroopa +Euroopa +euroopastu +evakueeri +evolutsioneeru +fassaaditse +faulitse +femineeru +feminiseeru +feodaliseeru +fikseeri +fikseeru +filosofeeri +filosoof +filosoofitse +filtreeru +finisheeri +firma +flirti +flööti +fluktueeri +föderatsioon +(.*)_föderatsioon +Föderatsioon +födereeru +folkloor +(.*)_folkloor +folkloriseeru +formaat +formeeri +formeeru +foto +fotograaf +fraasitse +funktsioneeri +gaasistu +galopeeri +garanteeri +gastroleeri +geminaadistu +gemineeru +generaliseeru +gleistu +grammine +gratuleeri +graveeri +graviteeri +grimassita +grupeeru +grüüneta +gümnaasium +(.*)_gümnaasium +Gümnaasium +hääbu +haaki +haaku +häälda +hääldu +häälesta +häälestu +hääleta +häälitse +haara +haarama +häärata +haardu +haarle +haava +haaval +haavandu +haavu +habenda +häbene +habeta +habetu +häbi +habise +habrastu +hädalda +hädamaandu +hädatse +hägune +hägustu +haheta +hahku +haigene +haigestu +haigla +(.*)_haigla +haigla=ne +haigle +haiguta +haihtu +häilata +häiri +häiru +haise +haist +haju +hakka +hakkama +hakki +hakul +hala +halasta +halb +hälbi +halda +haleta +haletse +halise +haljastu +haljenda +halju +hallata +hallenda +halline +hallistu +hallita +haltuuratse +halva +halvene +hälvita +halvusta +hämardu +hämarne +hambu +hämmasta +hämmastu +hämmelda +hämmeldu +hammusta +hammustama +hämuta +hangelda +hängelda +hangu +hanki +hansa +Hansa +hapu_piim +härdu +hargne +hari +härise +harju +harjuta +harku +härmatu +härmistu +harmoneeri +harmoneeru +harpa +härra +harrasta +härratse +harune +haruta +harvene +hasseta +hästi +hatuta +haudu +hauduma +hauku +hauta +hävi +hävine +hea +heaks +heameelitse +heiastu +heikle +heilu +hein +heisku +heit +heitle +heitma +heitu +hektar +hela +heldi +heldu +helenda +helestu +helga +helgahta +helgata +helgeta +helgu +helise +helista +helju +helki +helkle +helleniseeru +hellutse +helmenda +helvenda +helvestu +helveta +hepita +hetk +hibise +higista +higistu +higitse +hiilga +hiili +Hiina +hiinastu +hiirle +hiiu_maa +Hiiumaa +Hiiu_maa +hiiva +hikita +hilbenda +hilberda +hiline +hili=ne +hilistu +hilja +hiljaks +hiljem +hilju +hilpa +hilple +hilpne +hilpsa +hilpu +himu +himurdu +himusta +himutse +hinda +hing +hinga +hingasta +hingelda +hingestu +hingi +hinguta +hinku +hinna +hinnanud +hinna=nud +hinnatud +hirm +hirmu +hirnahta +hirnata +hirska +hõbeda=ne +hõbele +hõbenda +hõbene +hõbestu +hõbetu +hoiata +hoid +hoidma +hoidu +hõigata +hõika +hõikle +hõila +hõiska +hõlbustu +hõlju +holpsa +holpsata +homme +hommik +hommikune +hommiku=ne +hommiku_poole +hõngu +hoogle +hõõgu +hoogustu +hool +hoolda +hooli +hoolimata +hoolitse +hooma +hoople +hõõrdu +hööri +höörita +höörle +hõõru +hoova +hopsa +hõrene +hõrestu +hõrgutse +hubise +hübridiseeru +hüdise +hüdratiseeru +hüdrolüüsu +hühise +huigata +huikle +huilata +huiluta +huiska +hukka +hukku +hulgaliselt +hulgas +hulgast +huligaanitse +hulju +hulk +hulka +hulku +hulla +hullu +hullutse +hülpa +hulpi +hülpi +hulple +humise +hüpe +hüple +hüppa +hurmu +hurraata +hüüata +hüübi +hüüd +huuga +hüürga +huvi +huvile +huvita +(.*)_huvita +hüvita +huvitav +huvitu +idane +(.*)ide +idee +identifitseeru +idu +idüllitse +idutse +iga +igane +iga_päeva=ne +iga_sugune +igati +igatse +igatsus +igaüks +iga_üks +igavik +igavle +ignoreeri +igritse +iha +ihalda +ihale +ihalus +ihastu +ihka +ihku +ihnu +ihnutse +ihtu +iiba +iili +iilitse +(.*)ik +ikaldu +ikka +ilatse +ilge +ilgelt +ilku +illumineeri +illustreeri +ilma +ilmesta +ilmlikustu +ilmne +ilmselt +ilmsiks +ilmu +ilmuta +ilmutu +ilusta +ilutse +iluuisuta +iluvõimle +ima +imbu +ime +imendu +imesta +imestu +imetele +imetle +immatrikuleeru +immigreeri +immigreeru +immitse +immuta +imponeeri +importi +ina +inaktiveeru +individuaalitse +industrialiseeru +infiltreeru +infitseeru +informeeri +inimene +inimestu +innusta +innustu +inspireeri +inspireeru +instituut +(.*)_instituut +Instituut +integreeru +intensiivista +intensiivistu +internatsionaliseeru +interpelleeri +intriigitse +invaliidistu +ioniseeru +irdu +irgitse +iriseeri +ironiseeri +iroonitse +irriteeri +irska +irvenda +irvita +(.*)is +isa +isa=ne +ise +iseäratse +iseenese +ise_enese +isegi +iseloomusta +isemeelitse +iseseisvu +iseseisvumine +ise_seisvu=mine +ise_sugune +isetolmle +isoleeri +isoleeru +isomeriseeru +issand +(.*)ist +istu +istuli +istung +Istung +istuskele +istuskle +istuta +isu +isune +isutse +itsita +ivake +[I|V|X|C|M]++(.*) +ja +(.*)ja +(.+)ja +jaa +jää +jäädvusta +jäädvustu +jaam +(.*)_jaam +Jaam +jääma +jaanuar +jäätu +jaburda +jaburdu +jädendu +jaga +jagele +jagu +jagune +jah +jahenda +jahene +jahmerda +jahtu +jahuta +jahvata +jäigastu +jäiku +jalastu +jälesta +jalg +jälg +jalga +jälgi +jälile +jälita +jäljenda +jalule +jaluta +jalutele +jalutle +jama +jämedune +jamele +jämene +jämetse +jammi +jampsi +janda +jända +jandita +jankle +jänni +janti +jantle +janune +jaoks +jaole +jaota +jaotu +järel +järelda +järeldu +järele +järelikult +järelkääri +järelkuiva +järelküpse +järelt +järelvalmi +järg +järgi +järgmine +järgne +järjest +järjesta +järjestu +järk +järsene +järv +(.*)jas +jäsele +jät +jätka +jätku +jättu +jauna +jaura +ja/või +joba +jobise +jobuta +jõgi +jõhkrutse +johtu +joigu +jokki +jokuta +jõla +jõletse +jõlki +jõlku +jõllita +jõllitama +jomise +jommi +jommita +jõmpsu +jõngerda +jõnki +jonkle +jõnkle +jõnksa +jonksata +jõnksata +jonksle +jõnksle +jonksu +jõnksu +jõnku +jonle +jontsi +jöntsi +jõntsi +joo +jõõbita +joobu +jooks +jooksle +jooksul +jooma +joon +joonestu +joonis +joonista +jõõrdu +joot +joovastu +joovatse +jortsata +jõu +jõud +jõudu +jõukle +jõul +jõustu +jõuta +jõutama +jt +ju +juba +jubetse +judi +judise +judista +juha +juhata +juhataja +(.*)juhataja +juhenda +juhendaja +juhindu +juhitav +juhmistu +juhmu +(.*)juht +juhti +juhtiv +juhtu +jukerda +julge +julgus +julgusta +julmutse +(.*)jumal +jumalda +jumestu +junna +jupasta +juperda +jupsi +just +justkui +just_kui +jutle +jutusta +juubelda +juuli +juulikuu +juuli_kuu +juuni +juunikuu +juuni_kuu +juurde +juurdu +juures +juurest +juuri +juustundu +ka +kaaberda +kääbustu +kääksata +kääksu +kaalu +kaamene +kaamestu +kääna +käändu +kaanetu +käänle +kaapa +kaapi +kaardu +käärdu +kääri +kaarle +käärle +kaasa +kaasajastu +kaasas +kaasle +kaasne +kaasu +kaatsa +käätsata +kaatsi +käätsu +kabestu +kabeta +kabise +kadesta +kädise +kadu +kae +kaeba +kaeble +kael +kaela +kaelas +kaelast +kaelusta +käe_pära=ne +käes +käest +kaeva +kaevandu +kaeva=nu +kaevanud +kaeva=nud +kaevu +kägardu +kägise +kaha +kahanda +kahane +kahardu +kähardu +kahekordistu +kahendik +kahendu +kahestu +kahetse +kahi +kahise +kähise +kahjatse +kahju +kahjurõõmutse +kahjustu +kahk +kahku +kahla +kahmeta +kahmi +kahtle +kahtlemata +kahtlusta +kahtu +kahuta +kahvata +kahvatu +käi +käia +käiama +käibi +käigus +kaiku +käima +kainenda +kainene +kaits +kaitse +kaitsema +kaitsma +käitu +käivitu +kaja +kajasta +kajastu +käkerdu +kakle +käkru +kaks +kaksita +käksu +kala +kalasta +kaldu +kalestu +kalgastu +kalge +kalgendu +kalgine +kalgistu +kalla +kallal +kallale +kallalt +kallerda +kallerdu +kalline +kallista +kalluta +kallutu +kalpa +kalpsa +kaltsa +kaltserda +kamakas +kamanda +kamardu +kamp +kampa +kämpu +kan +kanal +(.*)_kanal +Kanal +kand +(.*)kandidaat +kandideeri +kandis +kandu +kangastu +kange +kangene +kangestu +kängitse +kangu +kängu +kanguta +kannata +känni +kannul +kannule +kannult +kanoniseeru +kanti +(.*)kantsler +käntsu +kaos +kaota +kaotsi +kaperda +kapitaliseeru +kapituleeru +kappa +kapriisitse +kapsastu +kapseldu +kapsu +kapten +karamellistu +karamellu +karasta +karastu +kärata +käratse +karbata +kärbata +kärbu +karestu +kareta +karga +kärgahta +kärgata +kargle +kargu +kärgu +karguta +kari +kärise +karista +kärista +karja=ne +karjatu +karju +karjuta +kärki +kärmasta +kärmene +karmine +karmistu +karmu +kärnata +kärpi +kärsi +kärsiskle +kärsitse +kärssa +kärssu +karstu +kart +kärtsa +kärtsata +kärtsu +kartus +käruta +karv +kärva +karva=ne +karvastu +karvenda +karvusta +kas +käsi +käsil +käsile +käsita +käsitle +käsk +käski +käsnata +käsnu +kast +kastu +kasu +kasuta +käsuta +kasv +kasva +kasvata +kasvataja +kat +katapulteeru +katke +kätke +katkesta +katkestu +katki +katku +kätsata +katse +katseta +katsu +kätte +kattu +kaua +kauaks +kaubitse +kaudu +kauem +kaugel +kaugele +kaugenda +kaugene +kaugu +kaugus +kaugusel +kauguselt +kauguse=lt +käuksata +käuksu +kaunine +kaunis +kaunista +kaunitse +käunu +kaupa +kauple +kava +kavalda +kavanda +kavatse +kavatsus +kebi +kee +keegi +keel +(.*)_keel +keela +keeld +keelita +keepa +keera +keerdu +keerle +keerustu +keeruta +keerutu +keet +keevatse +keevitu +kehane +kehasta +kehastu +kehita +kehki +kehkle +kehtesta +kehtestu +kehti +kehvene +kehvu +keiser +kekerda +kekka +keksa +keksi +keksle +kekuta +kelguta +kelka +kelki +kell +kella +kemple +kena +kenastu +kenita +kenitle +kenka +kenksa +kepelda +kepsa +kepsle +kerge +kergene +kergita +keri +kerki +kes +keset +kesk +keskel +keskele +keskelt +keskenda +keskendu +keskmine +kesk_pära=ne +keskus +(.*)_keskus +Keskus +keskustele +keskustle +kest +kesta +kestel +kestenda +ketenda +ketistu +ketra +ketuta +kevad +kiba +kibene +kibestu +kibise +kibrastu +kibruta +kidele +kidise +kidu +kidune +kidutse +kigalda +kigatse +kigise +kiha +kihardu +kihele +kiherda +kiherdu +kihine +kihise +kihistu +kihitu +kihkle +kihku +kihlu +kiht +kihuta +kihvata +kihvatu +kiiguta +kiikle +kiiksata +kiiksu +kiiksuta +kiiku +kiila +kiilane +kiilastu +kiildu +kiilga +kiilu +kiiluma +kiimatu +kiimelda +kiimle +kiindu +kiirata +kiire +kiirenda +kiirene +kiiresti +kiirga +kiirgu +kiirita +kiiritu +kiirusta +kiirustle +kiirutse +kiiska +kiit +kiitle +kiitma +kiitsa +kiitsata +kiitsu +kiivastu +kikitu +kila +kilahta +kilatse +kilda +kildu +kilenda +kilgenda +kilise +kiljata +kiljatle +kilju +kilka +kilksata +kilksu +kilku +killas +killast +killenda +killu~ke +killune +killusta +killustu +kilo +kilogrammine +kilomeeter +kilo_meeter +kima +kimardu +kimbuta +kimele +kimp +kimple +kimpu +kindlusta +kindlustu +kindral +kindu +king +kinki +kinni +kinnista +kinnistu +kinnita +kinnitu +kiperda +kipitele +kipitle +kipitse +kippa +kippu +kipra +kipru +kira +kirata +kiratse +kirenda +kireta +kirga +kirgastu +kirgle +kirgu +kiri +(.*)_kiri +Kiri +kirik +Kirik +kirise +kirista +kirja +(.*)_kirjandus +kirjanik +kirjastus +(.*)_kirjastus +Kirjastus +kirjelda +kirjuta +kirjutama +kirka +kirma +kirmenda +kirmeta +kirmetu +kirtsuta +kiru +kirurg +kirvelda +kirvenda +kisa +kisenda +kiskle +kisku +kissi +kissitu +kitsam +kitsene +kitsine +kitsu +kitsune +kiuhka +kiuksata +kiuksu +kiunahta +kiunata +kiunu +kiunuta +kiusa +kiusle +kiuste +kiutsu +(.*)kivi +kivine +kivista +kivistu +klaari +klaaru +klaas +klaasistu +klabise +klähma +klähvata +klähvi +klammerdu +klani +klappi +klapsata +klapsu +klass +kleebi +kleebi=tu +kleebitud +kleebi=tud +kleepi +kleepu +(.*)_kliinik +kliisterda +kliisterdu +klirise +klobata +klõbata +klobise +klõbise +klõbista +klõgise +klõksata +kloksu +klõksu +klõmata +klõmmu +klompsata +klõmpsata +klompsu +klõmpsu +klompu +kloppi +klopsata +klopsi +klubi +(.*)_klubi +Klubi +klugise +kluguta +kluksata +kluksu +kluuguta +kluuksata +kluuksu +kluuta +km2 +km3 +koaguleeru +koaleeru +koba +kobardu +kobene +kobestu +kobi +kõbi +kõbise +kõbista +(.*)_koda +kodakondsus +kodanik +kodanlikustu +kodanlustu +kodi +kodja +kodu +kõdu +kodune +kodu=ne +kõdune +kodus +kodust +kodustu +koeksisteeri +(.*)koer +koerusta +koerutse +koge +kogelda +kogemata +kogise +kõgise +kogu +(.*)_kogu +(.+)_kogu +kogune +kohahta +kohal +kohalda +kohale +kohalt +kohanda +kohandu +kohane +kohaselt +kohastu +kohata +kohe +kõhele +kohenda +kohendu +kohene +kõhetu +kohise +köhise +kõhise +kohka +kõhkle +kõhklus +kohku +kohla +kohma +köhmi +köhmitse +kõhmitse +kohmu +kõhnene +kõhnitse +kõhnu +kohra +kohru +koht +kohta +kohtle +kohtu +kohtustus +kõhulda +kõhuli +kohusta +kohustu +kohustus +kohuta +kõhuta +kohv +koiba +koiberda +kõige +koigerda +kõiguta +kõik +kõiksugu +kõiku +koipa +koit +köit +koiva +koju +kökerda +kõketa +koketeeri +kokku +koksa +koksata +kõksata +koksi +koksistu +kola +kõla +kolahta +kõlahta +kolata +kõlata +kõlba +kõlestu +kolgastu +kõlguta +koli +kolise +kõlise +kolka +kolksa +kolksahta +kolksata +kõlksata +kolksu +kõlksu +kolku +kõlku +kolla +kollabeeru +(.*)_kollektiiv +kollektsioneeri +kollenda +kollerda +kolleta +kolletu +kolmandik +kolmapäev +kolma_päev +kolmas +kolmene +kolmestu +kolm_veerand +kõlpa +kolpsa +koltu +kõma +kõmahta +kõmata +kombe_koha=ne +kombel +komberda +kombineeri +kombineeru +kõmise +komisjon +(.*)_komisjon +Komisjon +komista +komistle +komitee +(.*)_komitee +Komitee +komme +(.*)_komme +kõmmeldu +kommenteeri +kõmmu +kõmmuta +kompa +kompareeru +kompenseeru +kompi +kõmpi +komple +kompleks +komplekt +komplekteeri +komplekteeru +komplitseeri +komplitseeru +komposteeru +kõmpsa +kompsata +kõmpsi +kompsu +kõmpsu +konardu +(.*)kond +(.*)_kond +konda +kondenseeru +kõndi +köndistu +kõnele +kõneta +konfereeri +konfiskeeri +konföderatsioon +(.*)_konföderatsioon +Konföderatsioon +konformeeru +kõnge +kongerda +kongrueeru +kõngu +konjugeeru +konka +könka +konkretiseeru +konksu +könksu +konksuta +kõnku +konkureeri +konkurss +(.*)_konkurss +könni +kõnni +kõnniskele +kõnniskle +konserveeru +konsolideeru +konspireeri +konstateeri +konstitueeru +konsulteeri +konta +könta +kontakteeru +kontamineeru +kontraheeru +kontrasteeru +kontrolli +kontsentreeru +kontserteeri +köntsi +kontsu +konuta +könuta +konverents +Konverents +konvergeeru +konvoeeri +kooberda +kööberda +kooderda +kooga +köögerda +kõõguta +kooka +kõõksata +kõõksu +kooku +kool +(.*)_kool +Kool +kooldu +koole +koolita +kõõlu +kooluta +kõõma +kõõmenda +kõõmeta +kõõmuta +koonda +(.*)_koondis +koondu +koonerda +köönerda +koonuta +kööpa +koopereeru +koordineeri +koordu +koori +koorma +kooru +koos +kooserda +kooskõlasta +kooskõlastu +koosne +koos_olek +Koos_olek +koosta +koostu +koosuta +kootsi +köötsuta +kootu +koperda +köperda +kopikaline +kopita +kopitse +kopra +kopru +köpru +kopsa +kopsata +kõpsata +kopsi +kopsu +kõpsu +kõra +korbastu +korbata +korbatu +kõrbe +kõrbestu +kõrbu +kord +korda +kordistu +korditu +kordu +kõrge +kõrgem +kõrgene +korgistu +kõrgistu +kõrgitse +kõrgu +kõrgune +kõrgu=ne +kõrgus +korise +körise +korista +korja +korju +korki +korpu +korraga +korral +korralda +korraldu +korraldus +korras +korrastu +korreleeru +korrodeeru +korrumpeeru +korsata +korska +kõrsu +kortsu +korv +kõrval +kõrvalda +kõrvaldu +kõrvale +kõrvalt +kõrveta +korvu +kõrvuta +kõrvutu +kõsise +koska +kössi +kössita +kossuta +kössuta +kõssuta +kost +kostita +kostu +kostümeeru +kosu +koterda +kotita +kotserda +kötsita +kott +kotti +kõtuta +kouki +kõvastu +kõvene +kõverdu +kõvetu +kraakle +krääksata +kraaksu +krääksu +krääksuta +kraamelda +kraami +kraapi +kraaple +krääsle +krabise +kragise +krägise +krahv +kraksata +kraksu +krampi +krample +krampu +krapsa +krapsata +kratsi +kräuksata +kräuksu +kräunata +kräunuta +krebi +kribele +kribise +krigise +kriiksata +kriiksu +kriimusta +kriipsuta +kriisata +kriitsu +krilli +krimpsu +krimpsuta +kripelda +kristalliseeru +kritiseeri +kritselda +kriuksata +kriuksu +kriuksuta +kriunuta +krõbene +krobestu +krobise +krõbise +krõgise +krohv +krõksata +krõksu +krompsu +krõmpsu +krõmpsuta +kronksu +kroobata +krooksata +krooksu +krooli +kroon +kroonine +krooni=ne +krõpsata +krõpsu +krudise +krutti +kruuguta +kruuksata +kruuksu +kruvi +kübe +kubise +küde +kudu +kügele +kugise +kuhi +kuhja +kuhju +kühmita +kühmu +kühmus +kühmuta +kuhtu +kuhu +kuhugi +kühvelda +kui +kuid +kuidas +kuigi +kuika +kui ka +kui_palju +kuiva +kuivata +kuivene +kuiveta +kuivetu +kuivu +kuju +kujul +kujunda +kujundu +kujune +kujuta +kujutis +kujutle +kujutlus +kujutu +kujutus +kükerda +kukil +kukile +kukilt +kükita +kükki +kukku +kukuta +küla +(.*)_küla +küla_tee +kulda +kuletu +kulge +külge +külgne +külgu +külili +kulise +küljes +küljest +kulksu +küll +küllalt +küllasta +küllastu +kullenda +külm +külma +külmene +külmeta +külmetu +kulmineeri +kulmineeru +külmu +külmuta +kultuuristu +kultuuritse +kulu +kulul +kuluta +külva +külvu +kuma +kumb +kumbki +kümble +kumenda +kumerdu +kumeta +kumise +kumm +kummardle +kummardu +kümmekond +kummu +kummuli +kümnendik +kumuleeru +kuna +kunagi +künd +kuni +(.*)_kunst +kupla +kuplu +kuppa +küpse +küpseta +kuraasita +kuraasitse +kurameeri +kurat +kurbu +kurdistu +kurdu +kurise +kurjusta +kurjutse +kurluuta +kurna +(.*)_kurna +kurnäuta +kurnitse +kurnu +kurrutu +kurseeri +kurt +kurtu +kurvasta +kurvastu +kurvatse +kus +kusagil +kusagile +kusagilt +kuse +kusele +küsi +küsitle +kuskil +kuskilt +kust +kusta +kustama +kustkaudu +kust_kaudu +kustu +kustune +kustuta +küt +kuterda +kütkestu +kutsu +küttu +kuu +(.*)_kuu +küübestu +kuuekordistu +kuuendik +kuuga +kuuguta +kuuka +kuuksu +kuul +kuula +kuuldu +kuuletu +kuulu +kuuluta +kuum +kuuma +kuumene +kuumle +kuumu +küündi +kuune +kuu=ne +küünita +küünitu +kuupäev +kuu_päev +kuurdu +küürdu +küüri +küüru +kuurusta +küüruta +küüsi +küüsis +küüsist +küüti +küütle +küütsuta +kuvastu +kvaaku +kvalifitseeru +kviteeri +laaberda +laabu +laad +laadi +laadne +laadu +laagerdu +laamenda +lääpa +lääs +laasi +laasu +laatsata +laatu +laba=ne +labastu +läbe +läbi +labialiseeru +läbista +läbistu +läbi_viimine +ladene +ladestu +lädise +ladu +lae +laeku +laena +laenu +laenuta +lagedale +lagestu +lagise +lagista +lagu +lagunda +lagune +lähedal +lähedale +lähedalt +lahenda +lähenda +lahendu +lahene +lähene +lähestu +läheta +lahise +lähistel +lähistele +lähistelt +lahjene +lahju +lahkene +lahkne +lahku +laht +lahti +lahtu +lähtu +lahusta +lahustu +lahuta +lahutu +lai +läi +laia +laiali +laibastu +laiem +laienda +laiene +läigata +läigatle +läiki +läikle +laila +laineta +lainle +läinu +läinud +läi=nud +laiskle +laisku +lait +läit +läitu +laiu +laiune +laiu=ne +laius +laiuta +lakata +lakatle +lakerda +läkita +lakka +lakku +laksu +laksuta +lama +lamandu +lamaskele +lamaskle +lämbu +lamendu +lamene +lameskele +lameskle +lamestu +lammu +lammuta +lämpa +(.*)lane +lange +langeta +längu +lantsi +laokile +laostu +laota +laotu +läpastu +läpata +lapenda +laperda +läppa +lappi +läppu +laps +lapsestu +larise +lärise +lärmitse +lartsu +lärtsu +las +lask +laskma +lask=mine +lasku +lasti +lastu +lastud +las=tud +lasu +läti +Läti +lätistu +lätsa +lätsi +latsu +lätsu +lauge +laugle +laugu +laul +laupäev +lau_päev +lausa +lausu +lausumata +lausu=mata +laut +lavastu +laveeri +lävi +leba +lebaskele +lebaskle +leebi +leebu +leedi +leedu +Leedu +leegitse +leeki +leekle +leemenda +leemeta +leeri +leetu +leevendu +leevene +legaliseeru +lehistu +lehka +lehki +leht +(.*)_leht +Leht +lehti +lehvata +lehverda +lehvi +lehvita +lehvle +leid +leidu +leigene +leigu +leili +leina +lembu +lemmenda +lemmeta +lenda +lendle +lendu +lengerda +lenna +lenna=nu +lennanud +lenna=nud +lennuta +leondu +leostu +leota +lepita +leppi +lesestu +lesi +lesku +levi +levine +levita +libahta +libasta +libastu +liberaalitse +libestu +libise +libista +libistu +libitse +liblenda +lidise +lidista +lidu +ligastu +ligemale +ligi +ligidal +ligidale +ligidalt +ligikaudu +ligi_kaudu +liginda +ligine +ligistu +ligu +ligune +lihatse +lihavne +lihku +lihtsustu +lihu +lihva +lihvata +lihvu +liialt +liia=ne +liibu +liider +(.*)liider +liiderda +liig +liiga +liigagi +liigahta +liigata +liigendu +liigestu +liigita +liigitu +liigu +liiguta +liigutu +liik +liikle +liiku +liimenda +liimerda +liimi +liimitse +liimitusta +liimu +liisu +liit +(.*)_liit +Liit +liiter +liitrine +liitu +liiv +liiva=ne +liivastu +(.*)lik +likvideeru +lillenda +lillerda +lillu +lilluta +limpsi +lina=ne +linastu +linguta +linn +(.*)_linn +Linn +linnas +(.*)_linnas +linnastu +lipenda +liperda +lipitse +liple +lipne +lippa +lippu +lipsa +lipsu +lipustu +lirise +lirtsu +lisa +lisandu +lisane +litata +literda +litsu +litsuma +liugasta +liugle +liugu +liuska +lõbu +lõbune +lõbustu +lõbutse +lodise +lõdise +lodustu +lõdvene +lõdvestu +loend +loendu +loetle +loga +logele +logi +logise +lõgise +loha +lõhastu +lõhene +lõhesta +lõhestu +lohise +lõhise +lohista +lõhke +lõhki +lohku +lõhku +lohmerda +lõhn +lõhna +lõhustu +lohutu +lohva +lohverda +loidu +lõiga +lõiga=nu +lõiganud +lõiga=nud +lõige +loigerda +lõigustu +lõika +loima +lõimetu +loitle +loiva +loivi +lokaliseeru +lokata +lokenda +lõkenda +lokerda +lökita +lokka +lõkka +lõkku +loksa +loksata +loksi +lõksi +loksle +loksu +lõksu +loksuta +lökuta +lollistu +lömastu +lomberda +lömberda +lömita +lömpa +lõmpsata +lõmpsu +lompsuta +londerda +londita +longerda +lõngerda +longu +lõnguta +lonki +lõnksu +lõnku +lonti +lontsi +löntsi +loo +löö +looberda +lööberda +loobi +loobu +loodene +looderda +lõõgastu +loogelda +loogerda +loojene +looju +looka +lookle +looku +lööma +lõõma +lõõmahta +lõõmatele +lõõmatle +lõõmenda +loopi +lõõpi +looritu +looru +lõõsata +loosi +lõõska +looskle +loot +lõõtsata +lõõtsuta +lootus +loovuta +lõp +lopenda +loperda +lõpeta +lõpp +lõppe +loppu +lopsata +lopsu +lõpuks +lõpus +loputa +lörise +lörri +lortsata +lörtsata +lõrtsata +lortsi +lortsu +lörtsu +loruta +losku +lössita +lossuta +lössuta +losuta +lösuta +lõsuta +lotenda +loterda +lõtku +lötsu +lõtvu +lõunane +lõunasta +lõunata +lozheeri +(.*)lt +luba +lubjastu +luge +lugu +lugupeetud +lugu_peetud +lühem +lühene +lühistu +luhta +luhtu +luhvata +luidestu +luiguta +luiluta +luiska +luit +luita +luitu +lüka +lükka +lükki +lukku +lükku +luksata +luksu +lukustu +lulise +lülistu +lülita +lülitu +lullita +lumetu +lumistu +lumita +lümita +lumitu +lummu +lunasta +lüngu +luni +lupju +lupsa +lupsata +lupsu +lurise +lurtsata +lurtsu +lustita +lustitse +luuguta +lüüritse +luusi +luustu +m2 +m3 +ma +maa +(.*)_maa +Maa +maadle +maagistu +maa_kond +maaldu +maali +maandu +maantee +maan_tee +määr +määra +määrama +määramine +määra=mine +määra_mine +määratle +määrdi +määrdu +määri +määrus +maastu +maatustu +maa_vanem +madal +madalam +madaldu +mädane +mädastu +madise +maga +magene +magestu +mägise +magneetu +magnetiseeru +maha +mahene +mäherdune +mähki +mähku +mahla=ne +mahti +mahtu +mahutu +mai +maikuu +mai_kuu +maini +mais +maits +maitse +(.*)maja +Maja +major +majuta +majutu +maks +maksma +maksusta +malbu +malda +mälesta +mälestu +mälestus_kivi +mäleta +mall +man +Man +mana +mander +mandu +mängi +mängima +mängle +mangu +mängu +manifesteeru +manitse +mant +manu +maolda +maota +maraton +märatse +mardisandita +märgu +märka +märki +marodeeri +marrastu +marru +marssi +märts +märtsikuu +märtsi_kuu +maru +märulda +maruta +marutse +masendu +maskeeri +maskeeru +maskuleeru +mäsle +mässa +mässi +mässu +mässutse +mast +mat +(.*)mata +(.*)[=]+mata +(.*[^=])mata +matematiseeru +materda +materialiseeru +matistu +matka +matsata +matsereeru +mätsi +matsu +mattu +mauri +me +mediteeri +meel +meeldi +meeldu +meeleolutse +meele_pära=ne +meelest +meelesta +meelestu +meeliskele +meeliskle +meelita +meene +meenu +meenuta +mees +meeter +meetrine +meetri=ne +mehestu +mehhaniseeru +mehistu +mehkelda +meie +meie_sugune +meisterda +meistri_liiga +Meistri_liiga +melanhoolitse +memmestu +mendeleeru +menstrueeri +merulda +mesitse +metall +metastaseeru +metsastu +metsistu +mi +mida +midagi +migreeri +migreeru +Mihkel +mihuke +miila +miks +mil +Mil(.*) +milj +MILJ(.*) +millal +millimeetrine +milline +milli=ne +min +mina +mindu +mindud +min=dud +mine +(.*)[=]+mine +(.*[^=])mine +(.+)mine +minek +minem +minema +mineraliseeru +minesta +minestu +mineta +mingi +mingisugune +mingi_sugune +minister +(.*)minister +minki +minu_sugune +minut +(.*)minut +minutiline +mis +misjaoks +mis_jaoks +miski +miski P sg part +miski_sugune +mismoodi +mis_moodi +mispärast +mis_pärast +miss +missioon +Missioon +missis +missugune +mis_sugune +mistarvis +mis_tarvis +mister +mistõttu +mis_tõttu +misuke +mitmekesistu +mitmekordistu +mitmes +mitmestu +mitme_sugune +mitte +mitu +mi=tu +mitu_kümmend +mitu_sada +mitu_setu +mobiliseeri +mobiliseeru +moderniseeru +modernitse +modifitseeru +moel +mõel +mõeldud +mõelnud +mõel=nud +möhita +mõhku +möira +möirahta +möirata +möirga +mõis +(.*)_mõis +mõist +mõju +mõjul +mõjusta +mõjuta +moka +mökuta +möla +mõla +mõlema +mõlemad +mõlgistu +mõlguta +mölista +mõlki +mõlku +mölla +moluta +moment +momise +mõne_sugune +mõni +mõningane +mõningas +monopoliseeru +monteeri +mõnule +monument +mõnutse +mood +mööda +möödane +moodi +möödu +moodusta +moodustu +möögi +mööki +möön +mõõna +moonda +moondu +mõõt +möra +moraalitse +moraliseeri +mõrane +mõrastu +mörata +mõrka +mornistu +mornitse +mõrune +mõrustu +mõte +mõtel +mõtel=nu +mõtelnud +mõtel=nud +mõtesta +mõtestu +mõtiskle +motiveeri +mõtle +mõtlema +motoriseeru +möura +mõura +mr +ms +msc +mtü +MTÜ +mudastu +müdise +müdista +müdra +mugandu +mugane +mügardu +mugatse +mugavdu +mugavle +mugavne +mügerdu +mügi +müha +muhele +muhene +mühise +mühki +muhku +muiahta +muiata +muidu +muidugi +muiga +muigle +muiska +muiskle +muist +mükerda +mukki +müksa +müksle +mula +muldu +mulgustu +mulise +mulju +mulksata +mulksu +mülla +mulla=ne +mullastu +mulle +mullita +mullitse +mültu +mumifitseeru +mumise +munserda +munsterda +müra +mürahta +murasta +murd +murdma +murdu +murele +murene +mürgelda +mürgi=ne +mürgistu +mürgitu +mürise +mürra +mürtsu +mürtu +murustu +müsele +muserdu +musitseeri +must +mustenda +mustene +mustu +muteeru +mütsu +muu +müü +müübi +muugi +müügi +muula=ne +muuma +muumiastu +muundu +müüra +müürga +müüri +muut +muutu +naabrutse +naakle +nääkle +nääksata +naaldu +näändi +naa_sugune +nabi +nabise +näbrasta +nädal +(.*)nädal +nädala=ne +nädala_päev +naeldu +naelu +naeluta +naelutu +naer +naerata +naeratele +naeratle +naerle +naeruvääristu +näeta +näge +nagele +nägele +nägema +nägi +nagise +nägise +nagu +nägu +nägurda +nahastu +nahise +nahka +nahku +nähtu +nähva +näi +näida +näida=nu +näidanud +näida=nud +näide +naiivitse +näikse +naine +näinu +näinud +näi_nud +naisestu +nait +näita +näiteks +naitu +näitu +najal +najale +najalt +najatu +nakata +nakatu +nakka +näkka +nakku +naksu +nali +nälja=ne +naljata +naljatele +naljatle +näol +näpista +nappi +napsita +napsuta +näpu_vahele +närbi +närbu +näri +narise +närise +narmastu +narmenda +narrita +närtsi +närtsu +närune +närustu +närvastu +närveeri +närvelda +närvetu +närvitse +närvle +nasaleeru +näsele +nasise +nätsata +natsionaliseeri +nätsku +natsu +nätsu +natti +natuke +naturaliseeru +näugata +näuksata +näuksu +nauti +(.*)ne +neela +neelata +neeldu +neeti +neid +neiu +neiuke +neiu=ke +neljakordistu +neljandik +neljapäev +nelja_päev +nemad +nende_sugune +nenti +nesti +neutraliseeru +nigise +nihele +nihestu +nihku +nihuke +nihukene +nihuta +nihutu +nihvata +nihvelda +nihverda +nii +niiksata +niiksu +niipea +nii_pea +niisama +nii_sama +niisamasugune +nii_sama_sugune +niisama_sugune +niisamuti +nii_samuti +niisku +niisugune +nii_sugune +niisuke +niit +niitsata +niitsu +niivõrd +nii_võrd +nikastu +niksu +nimel +nime=line +nimeta +nimetus +nimi +nina=ne +ning +nipita +nireta +nirgu +nirise +nirtsuta +nisuke +nisukene +nitrifitseeru +niugu +niuksu +niutsu +nivelleeru +niverdu +(.*)nna +nõdrene +nõela +nõetu +nõgu +nõgune +noh +nohata +nohhita +nohise +nohista +nohuta +nõidu +nõjal +nõjatu +nokasta +nokastu +nõksata +nõksatu +nõksle +nõksu +nolguta +noluta +nomadiseeri +nõnda +nooguta +nooku +nooldu +noomi +noor +noordu +nöördu +noorene +nööri +noorutse +noosku +noppi +nördi +nõrenda +nõreta +nõrgene +nõrgestu +norgu +nõrgu +norguta +nõrise +nõristu +nõrke +nõrku +normaliseeru +norra +Norra +norska +nortsu +nõruta +nosise +nõtkestu +nõtku +nõtru +notti +nõu +nõud +nõue +nõukogu +nõu_kogu +nõus +nõuskle +nõus_olek +nõustu +nõuta +nõutle +november +nr +(.*)nt +(.*)[=]+[ntd]u +([^=]*)[ntd]u +(.*)[=]+[ntd]ud +([^=]*)[ntd]ud +nüansseeru +(.*)nud +nudistu +nugi +nuhata +nühele +nuhise +nuhki +nühki +nühku +nuhtle +nukku +nukru +nukrustle +nukrustu +nukrutse +null +number +nuple +nuppu +nuputa +nura +nurata +nürine +nurise +nüristu +nurja +nurju +nurkle +nurku +nurru +nuru +nut +nüüd +nuuksata +nuuksu +nuumu +nuusuta +nuutsa +nuutsata +nuutsu +objektiveeru +oblitereeru +observatoorium +Observatoorium +odavne +(.*)_õde +öel +öel=nu +öelnud +öel=nud +õelutse +ogardu +õgi +õgvene +ohata +ohatu +õhene +õheta +ohheta +õhk +ohka +õhka +ohkle +õhku +ohoota +ohtralt +ohtu +õhtu +õhtune +õhtu=ne +õhtu_poolik +õhuta +ohverda +ohverdu +oiata +õidu +õienda +oiga +õige +õigene +oigle +õigus +õigusta +õigustu +õilmitse +oimetu +oimitse +öine +öi=ne +oistu +õitse +oiuta +oja +okastu +okludeeru +oksenda +oksüdeeru +oktoober +ol +ole +olek +olele +olema +olene +oleskle +oleta +oletse +olgugi +olnu +olnud +ol=nud +õlul +õlule +oluliselt +õlult +oma +oma_jagu +omaks +oma_moodi +omanda +omane +oma_pära=ne +omapära=ne +omapäratse +oma_sugune +omavolitse +õmble +omista +õnge +õngitse +õnne +õnneks +õnnesta +õnnestu +õnnista +õnnitle +öö +ööbi +öögata +ööki +õõndu +õõnesta +ööstu +oota +õõtsata +õõtsu +õõtsuta +õpeta +õpetaja +õppi +opteeru +orastu +organisatsioon +(.*)_organisatsioon +Organisatsioon +organiseeri +organiseeru +orienteeri +orienteeru +originaalitse +orkester +(.*)_orkester +Orkester +õrnle +õrnu +õrnutse +õrrita +õrritu +osa +osakond +(.*)_osakond +Osakond +osale +osaleja +osale=mine +osalus +(.*)_osalus +Osalus +osane +osapool +osa_pool +osa_riik +osas +oska +oskus +osoneeru +ost +osuta +osutu +ots +otsa +otsama +otsas +otsast +otse +otsekui +otse_kui +otsi +otsus +otsusta +otsustu +oü +OÜ +õunad on subjekt +paadu +paagata +paagatu +paaku +paanitse +paar +(.*)_paar +paardu +paari +pääri +paarima +paaritu +paaru +pääse +pääsema +pääst +paastu +paatsi +paatu +pabista +padise +pae=ti +päev +(.*)_päev +päevane +päeva=ne +päevita +page +pagenda +pagise +pahanda +pahandu +pahane +paha=ne +pahatse +pähe +pahene +pähhita +pahise +pahmi +pahtu +pahuksisse +pahurda +pahutse +pahva +pahvata +paigalda +paigaldu +paigale +paigustu +paiguta +paigutu +paik +päike +Päike +paikne +paiku +paina +paindu +painuta +paisa +paisanud +paisa=nud +paisatud +paiska +paisku +paist +paisteta +paistetu +paisu +paisuta +paita +päitu +pajata +pakahta +pakata +pakita +pakitse +pakk +pakki +pakku +pakkuma +paksene +paksu +paksune +paksu=ne +paksustu +paku +pakundu +pakuta +pakutama +paku=tu +pakutud +paku=tud +pala +palavne +palistu +paljane +paljasta +paljastu +palju +paljud +paljune +palka +palmi +palmu +palu +paluma +palve +palveta +palvle +pan +pänderda +panditu +pane +panema +panetu +pank +panku +pann +pannalda +panni_täis +pannu +pannud +pan=nud +panti +pantsata +paoskle +paotu +par +par. +pära +paragrahv +parajasti +pärale +päralt +paranda +päranda +pärandu +parane +parasiteeri +pärast +parem +paremale +parene +päri +parim +pärine +päris +parise +päriselt +parista +pärit_olu +park +(.*)_park +Park +parki +parku +parlament +(.*)_parlament +Parlament +pärlenda +pärmi +parodeeri +parselda +pärssu +partei +(.*)_partei +Partei +parteistu +partsata +parvle +pasanda +pässandu +passi +passima +pasunda +patakas +paterda +päterda +pätistu +patra +päts +patsa +patsata +patseeri +patserda +pätserda +patsi +pätsi +patsu +patsuta +patusta +paugata +pauki +pauku +pea +peaaegu +pea_aegu +peal +peale +peale_poole +pealistu +pealkirjasta +pealne +peal_pool +pealt +pealt_poolt +peamine +peamiselt +peapiiskop +pea_piiskop +peas +peatu +peatükk +pea_tükk +pebrestu +peegelda +peegeldu +peendu +peenene +peenestu +peenutse +peeta +peetama +peetu +pehastu +pehki +pehmene +pehmu +peibuta +peit +peitu +peks +peksi +peksle +peksu +peleta +pelga +pelgu +peluta +pendi +pendu +pensioneeru +peo_täis +periheel +periood +(.*)_periood +perrooni_tolm +personifitseeri +peruta +pese +pesitse +pesitu +pesu_väel +pet +petiskele +petiskle +pettu +phd +pida +pidama +pidi +pidurda +pidurdu +pidusta +pidutse +pigem +pigista +pigistu +pihk +pihka +pihku +pihta +pihti +pihustu +piibita +piibuta +piiga +piigasta +piiksa +piiksata +piiksu +piil +piilu +piiluta +piim +piimastu +piinle +piinu +piira +piirdu +piires +piiristu +piirne +piisa +piisavalt +piiskop +piitsuta +pikali +pikem +pikene +piki +pikk +pikku +pikkune +pikku=ne +pikkus +piku=ne +pikuta +pilbastu +pildistu +pildu +pilet +pilguta +pilka +pilku +pilla +pilli +pilluta +pilune +pilvi=ne +pilvita +pimendu +pimene +pimestu +pindu +pinevne +pingesta +pingestu +pinguldu +pingustu +pinguta +pingutama +pinise +pinni +piparda +pipardu +pipra=ne +piraaditse +pirise +pirtsuta +piselrda +pisene +piserda +pissi +pissu +pist +pistma +pisut +pitsitu +pitsu +piuksata +piuksu +plaan +plaani +plaanitse +plaatsata +pladise +plädise +plagise +plahvata +plaksata +plaksu +planeeri +planeet +plara +plarise +plärise +plartsa +plartsata +plartsu +plärtsu +platra +platsu +plätsuta +pleeki +plinki +plirise +plõgisel +plõksata +plõksu +plõnksu +plötsi +plumpsa +plumpsata +plumpsu +põde +podise +poeg +poegi +poeta +poetu +põgene +pogise +põhi +põhine +põhjal +põhjenda +põhjendu +põhjene +põhjus +pohmitse +poi +põika +põikle +põiku +põimi +põimle +põimu +poiss +poiss_laps +põkku +poksle +põksu +põldu +põle +polemiseeri +põleta +põletikustu +põlga +polikliinik +põlistu +politiseeri +polk +polümeeru +polümeriseeru +põlv +põlvili +põlvita +põlvne +pomise +põmise +pommi +pommita +põmmuta +põndu +põnevustu +põnku +põnni +ponnistu +ponsu +pontsu +põntsu +põntsuta +poo +pooki +pool +poolastu +pooldu +poole +pooleli +poolest +poolitu +poolne +poolt +pool_teist +põõna +põõnuta +pööra +pööra=ne +pöörastu +pööratse +pöördu +pöörita +pöörle +pooseta +popsu +popsuta +põrahta +porise +põrise +põrka +põrki +põrku +põrmne +põrmustu +porsu +põru +põruta +posi +poti_täis +potsata +potsu +pott +põuastu +praadi +praaki +prääksata +prääksu +praali +praavi +praga +pragise +pragune +prahvata +praksu +prantsata +prantsuspärastu +prantsuspäratse +prantsustu +prassi +praugata +(.*)_prefektuur +preili +premeeri +presideeri +president +pressi +pressima +pretendeeri +prevaleeri +prigise +priskene +pritsi +pritsu +probleemitse +professor +programm +progresseeru +projekt +Projekt +prõksata +prööka +prooska +proov +proovi +prostitueeri +protesteeri +protsent +protsessi +proua +provints +provotseeri +pruuki +pruunistu +pruusata +pruuska +pubise +pudel +püdeldu +pudene +puder +pudise +pudune +püga +pügaldu +puge +(.*)püha +pühapäev +püha_päev +puhasta +pühenda +pühendu +püherda +pühitse +puhk +puhka +puhke +pühki +puhkle +puhtu +puhu +puhul +puhvi +puies_tee +puika +puikle +puista +puistu +puitu +pujahta +pukerda +püks +pukseeri +puksi +puksle +puksu +pulbitse +pulku +pullistu +pulmita +pulmitse +pulseeri +pulstu +pummelda +pumpa +pumpsata +punasta +punastu +pundu +punenda +puneta +püngi +pungitu +pungle +pungu +punkt +punkti +punktima +punni +punnita +punnitu +punsu +punt +punu +punundu +puperda +pupsu +purdu +pure +purele +pureta +pürgi +purgu +purise +purista +purjeta +purju +purjuta +purska +pursu +puruks +purune +purusta +purustu +pusi +püsi +püsistu +puski +pusserda +pussita +pussuta +püsti +püstita +püstitu +puterda +putita +putka +putkastu +putkestu +putku +putra +püüd +puuderdu +puudu +puuduta +püüe +puuki +puurdu +puuri +puurle +puustu +puutu +puutu=v +(.*)_raadio +räägata +raagu +rääki +rääkima +raamastu +raames +raami +raamistu +raamu +raasi +räästu +raasu +rääsu +raasuke +raatsi +raba +räbaldu +rabastu +rabele +rabendu +rabene +rabestu +rabise +rada +radise +rädise +raevle +raevu +raevutse +rägele +ragene +ragise +rägise +raha +rahesta +rähkle +rahku +rähmastu +rahmelda +rähmelda +rahmitse +rähmu +rahul +rahuldu +rahule +rahune +rahusta +rahustu +rahvas +(.*)_rahvas +Rahvas +rahvasta +rahvastu +rahvus +(.*)_rahvus +Rahvus +rahvusta +rahvustu +räidi +räihta +raipne +raipu +raiska +raisku +raiu +raiuma +rajandu +rajane +rajatu +rajutu +rakata +rakenda +rakendu +rakindu +rakku +raksata +räksata +raksu +räksu +ralli +rämbi +rambu +rammestu +rammi +rammustu +rampu +rända +randu +ränistu +ränna +räntsata +räpa=ne +räpastu +raple +rappu +rapsi +rapsu +raputa +rasestu +raske +raskendu +raskene +rasku +raspelda +rässi +rasvastu +rasvne +rasvu +raugastu +rauge +raugene +raugestu +rausata +ravi +ravitse +reageeri +realiseeri +realiseeru +reasta +reastu +rebene +rebesta +rebestu +rebi +rebima +reede +reegli_pära=ne +reeglipärastu +reegli_põhi=ne +reen +reet +reevakueeru +refereeri +reflekteeru +regenereeru +registreeri +registreeru +reguleeru +rehabiliteeru +rehkenda +reibastu +reis +reisi +reivle +reklaami +rekordita +rekorditse +rekvireeri +relvasta +relvastu +remilitariseeru +remonti +renteeri +renti +reorganiseeri +reostu +repatrieeru +repeta +repetu +repne +resideeri +resoneeru +resorbeeru +resulteeri +resulteeru +resümeeri +retsidiveeru +revolutsioneeru +ribele +ribise +rida +ridane +rigise +riidle +riieta +riietu +riigi_kogu +riigistu +riik +(.*)_riik +Riik +riimu +riisu +riiva +rikastu +rikestu +rikki +rikku +rikne +rikundu +ring +ringi +ringis +ringita +ringle +ringuta +rinnulda +rinnuta +ripakile +ripenda +riple +ripne +rippu +riputa +risk +riskeeri +riski +rista +risti +ristle +ristu +risustu +riukle +riuksu +rivaalitse +rivista +rivistu +robise +rögi +rõgise +roheldu +rohelistu +rohenda +roheta +röhitse +rohkem +rohkene +rohkesti +röhki +röhmitse +rohtu +rõhu +rõhuta +roida +roidu +roimle +roina +roisku +rõivasta +rõivastu +rokeeri +rõkka +rõksle +romaani +romahta +romaniseeru +rõmpsu +rondu +rõngastu +roni +roniskle +rõnssa +rooja=ne +roojastu +rooki +rööki +rõõm +rooma +roomaskele +roomaskle +roomastu +roomle +rõõmle +rõõmusta +rõõmustama +rõõmustele +rõõmustle +rõõmustu +rõõmutse +roosata +roosatu +rooska +roosteta +roostetu +roostu +rootsi +Rootsi +rootsistu +rootsu +röötsuta +röövi +ropenda +ropsata +roputse +rübele +rübi +rubise +rublane +rubla=ne +rudise +rühki +rühkle +rühm +rühma +rühmelda +rühmita +rühmitu +rühmu +ruiga +ruigata +ruigle +ruineeru +ruleeri +rulla +rulli +rullu +rumaldu +ründa +rüntsata +rüsele +ruska +rusku +russifitseeru +rusu +rusune +rusustu +rutle +rutta +ruttu +ruuga +rüüpa +rüüpi +ruupsi +rüüsta +rüütu +sa +saa +saabu +saad +saadik +saagi +saagitse +saali +saama +säärane +saardu +saare_maa +Saaremaa +Saare_maa +sääst +saastu +säästu +saat +saatel +saati +saavuta +sabi +sabise +sabitse +sada +sadakond +sädele +sadestu +sadine +sädista +sadu +sadulda +sageli +sagene +sagi +säherdune +sahise +sahkerda +sähvata +sähvi +säili +sajand +sakerda +sakri +sakru +Saks +saksa +Saksa +saksaskle +saksastu +salakuula +sala_pära=ne +salapära=ne +salene +salga +salku +sälku +salli +saluteeri +sama +samalaadne +sama_laadne +samane +sama_palju +samas +samasugune +sama_sugune +samle +samm +sammu +sämpu +samuti +sanktsioneeri +saple +säpru +sapsata +säpsata +sära +särdu +särele +särise +sarja +sarnane +sarnle +särtsata +särtsu +säru +sasele +sasise +säsise +sassi +sassu +sätenda +sätti +sattu +säuksu +säutsu +(.*)se +sead +seadus +(.*)seadus +seal +sealpool +seal_pool +sealt +sealtpoolt +sealt_poolt +seas +seast +seastu +§ +seda +see +seebita +seedi +seedle +seedu +seega +seemendu +seepärast +see_pärast +seepu +sees +seesama +see_sama +seesinane +see_sinane +sees_pool +seest +seest_poolt +seesugune +see_sugune +seetõttu +see_tõttu +sega +segamini +segele +segi +segundu +segune +segustu +seigu +seikle +seira +seis +seisata +seisne +seisund +seisuta +sekelda +sekka +sekretär +sekund +(.*)_sekund +sekundiline +sel +selekteeri +seleta +seletu +selg +selga +selgendu +selgita +selgne +selgu +selile +selili +selitu +seljas +seljataha +sellal +selleks +sellepärast +selle_pärast +selletaoline +selle_taoline +selletõttu +selle_tõttu +selline +selli=ne +selmet +selti +selts +(.*)_selts +Selts +seltsi +seltsimees +seltsi_mees +seltsis +seltsist +seltu +selvi +sembu +semitiseeru +semutse +senat +Senat +sendiline +seni +senikaua +seni_kaua +sensei +sentimeetrine +seo +seos +seoses +seosta +seostu +seotu +seotud +seo=tud +separeeru +september +serva +servama +serveeri +servistu +sest +sestpeale +sest_peale +sestsaadik +sest_saadik +setti +setustu +shokeeri +siba +sibele +sibi +sibli +sidi +sidu +sidundu +šifreeri +sigarett +sigatse +sigi +sihise +sihka +sihti +sihuke +siia +siiapoole +siia_poole +siiberda +siidi=ne +siin +siinpool +siin_pool +siirda +siirdu +siirle +siis +siiski +siit +siitpoolt +siit_poolt +siitsealt +siit_sealt +sikuta +sila +silbu +sildu +sileeru +silene +silise +silita +silka +silksa +silksata +silksu +silku +sillerda +silm +silma +silmi +silmitsi +siltsu +silu +sina +sinenda +sinerda +singu +sinine +sinna +sinnapoole +sinna_poole +sinu_sugune +siple +sirenda +sireta +sirgestu +sirgu +sirise +sirka +sirtsu +siru +siruli +siruta +sirutele +sirutle +sirutu +sisalda +sisaldu +sisenda +sisesta +sisise +sisista +siss +sisse +sisse_poole +sisune +sitkene +sitkestu +sitku +sitsi +sittu +siugata +siuke +siukene +siuksu +siuna +siutsu +skeem +s^likerda +snoobitse +söanda +sobele +sobi +sobise +sobitu +sõbrusta +sõbrutse +sõdi +sõelu +soeta +sogane +soga=ne +sogastu +sõgene +sohi +sohise +sohki +sõide +sõiduta +soigu +soiku +sõima +sõimle +soini +sõit +sõitle +sokk +sokuta +sola +solgata +solise +solka +solkne +solksata +solku +sõlmi +sõlmu +sõltu +solva +solvu +sõma +sõmerdu +sompu +sõna +sõnasta +sondu +sõnele +songle +söö +soodusta +soojenda +soojendu +soojene +sool +soola +soola=ne +sooldu +soome +Soome +soomitse +sõõna +soondu +soonistu +soopa +sõõrata +sõõrdu +soorita +sööst +soostu +sööt +sööti +soov +soovi +soovita +soovitus +soperda +sopista +sopra +sora +sordu +sorgelda +sori +sörki +sort +sorteeri +sorti +sortsu +sõtku +sotsialiseeru +sõud +spetsialiseeru +staar +staatus +stagneeru +starti +steppi +stimuleeri +strateegia +sublimeeru +sudi +süga +sügav +sügavune +sügavus +suge +sügele +sugene +sugi +sügis +sugu +suhkur +suhtes +suhtle +suhtu +suhtu=v +suigata +suiguta +suistu +suitse +suitseta +suju +sukeldu +sukk +sula +sulandu +sulane +sulata +sule +sulestu +sule=tu +suletud +sule=tud +sülga +sulge +sulgi +sulgne +sulgu +suli +sulise +sülita +sulju +sulpsata +sulpsi +sulpsle +suma +sumata +sümboliseeri +sumbu +sumise +summeeru +summerda +summi +summu +sumpa +sümpoosion +Sümpoosion +sümpoosium +Sümpoosium +sundi +sündi +süngestu +sunni +sunnil +sünnita +sünteesi +superda +suple +supp +sure +sureta +surise +surm +surtsata +suru +survu +susise +suspendeeru +suss +süst +süsti +süstima +süstma +sütita +sütti +süü +suubu +süüdi +süüdista +süüdistu +suudle +suuna +suunas +suunast +suunatud +suund +suunda +suundu +suunduv +suur +suurdu +suured apelsinid, väikesed õunad +suurenda +suurene +suursaatkond +suur_saatkond +suurune +suurus +suuruselt +suurusta +suurustele +suurustle +suut +süüta +süüvi +suvatse +süvene +suvi +suvita +ta +taagelda +taandu +taani +Taani +taaru +taaruta +taas +taba +tabel +taeva=ne +taga +tagane +tagant +tagant_poolt +taga_pool +taga_poole +tagasi +tagasta +tagu +tagune +tagurda +taha +tahapoole +taha_poole +tahe +tähelda +tähele +tähelepanu +tähele_panu +tähenda +tahene +tahes +tähista +tahku +taht +tähti +tähtima +tahtmine +tähtsusta +tahu +täi +täidu +täiene +täiesti +taipa +täis +täit +täita +täitu +täiustu +taju +takerdu +takista +takistus +takitse +takk +takka +täksi +talitse +tall +talla +talleta +talletu +taltsu +taltu +taluta +talv +talvita +talvitu +tammu +täna +tänama +tänav +tänita +tantsi +tantsiskle +tantsle +tänu +taoline +taotle +taotse +tap +taple +tapsi +täpsusta +täpsustu +(.*)tar +tära +tardu +targuta +targutele +targutle +targutse +tärise +tärista +tärka +tärlenda +tarretu +tarvis +tarvita +tarvitse +tasa +tasakaalusta +tasand +tase +tassi +tasu +tatsa +tätsa +tatsu +tatsuta +(.*)[=]+tav +(.*[^=])tav +tava +tavatse +te +tead +teade +teata +teater +(.*)_teater +Teater +teatud +tea=tud +tedele +tee +tee_kond +teel +teeni +teenitse +teeskle +tege +tegele +tegemine +tege=mine +tegi +tegu +tegutse +tehnikum +(.*)_tehnikum +Tehnikum +tehtle +tei +teie +teie_sugune +teine +teineteise +teine_teise +teinu +teinud +tei=nud +teiseks +teisendu +teisene +teisipäev +teisi_päev +teis_pool +teis_poole +teis_poolt +teis_sugune +teist_moodi +teist_sugune +teisu +tekita +tekitatud +tekitav +tekki +telefon +telegrafeeri +telki +tellerda +telli +tema +tema_sugune +tembelda +tembuta +temmelda +tempereeru +tempi +tendeeri +tendi +teosta +teostu +teotse +tera=ne +teravmeelitse +teravne +tere +tereta +teritu +terve +tervene +tervistu +tervita +tibi +tihene +tihka +tihku +tihti +tihuta +tiiksu +tiim +Tiim +tiirle +tiitel +tiivle +tikku +tiksata +tiksu +tilguta +tilise +tilka +tilku +tiltsata +tiltsu +timmi +timpi +tingi +tingitud +tingle +tinise +tippa +tipsuta +tiri +tirtsata +tituleeri +tiuksata +tiuksu +tiuksuta +tõbi=ne +tõde +toel +tõenäosus +tõenda +tõesti +tõestu +toeta +toetu +töga +togi +tohleta +tohti +tõhu +tohutult +toibu +toime +toimeta +toimi +toimu +toimumine +toimuv +töina +toit +toitu +tõke +tokerda +tokerdu +tõkesta +toksa +tolgenda +tolkne +töllerda +tölluta +tolm +tolma +tolmu +tolmu=ne +tolmuta +tõmba +tömbistu +tõmble +tõmbu +tõmma +tõmma=tu +tõmmatud +tõmma=tud +tõmmuta +tompu +tõngu +tönka +tonksa +tonn +tonnine +too +töö +(.*)_töö_andja +töö_andja +Töö_andja +toorene +toorestu +toorutse +toosama +too_sama +tõota +tööta +tõotus +toppa +toppi +toppu +(.*)tor +tore +tõrele +toretse +torga +torganud +torga=nud +tõrgasta +torgatud +torise +törise +tõrju +torka +torki +tõrku +torma +törruta +tortsata +tõsi +tosin +tõsine +tõsi=ne +toss +tossa +tossu +tõst +tõste +tõstu +tõtle +totru +totsu +tõtta +tõttu +tõu +tõug +tõuga +tõuga=nu +tõuganud +tõuga=nud +tõuka +tõukel +tõukle +tõuku +tõus +traagelda +traali +traavi +traditsioon +(.*)_traditsioon +trahvi +trallerda +tralli +trampi +trample +transformeeru +treener +treeni +trehva +trei +triivi +trimpa +triumfeeri +trobi_kond +trombu +tröödelda +trooni +trügi +trükindus +trükki +trummelda +trumpa +tsentuurio +tseremoonitse +tsirkuleeri +tsiteeri +(.*)tud +tudi +tüdi +tüdine +tüdruk +tuge +tugema +tugev +tugevne +tugine +tuh +tuhat +tuhatkond +tuhise +tühistu +tühjenda +tühjene +tühju +tuhk +tuhmu +tuhni +tuika +tuikle +tuiku +tuimene +tuimu +tuisa +tuisa=nu +tuisanud +tuisa=nud +tuiska +tujutse +tükk +tükki +tükkis +tukku +tuksata +tuksi +tuksu +tul +tule +tulek +tulema +tulene +tulenevalt +tuleta +tuletu +tuli +tulista +tülitse +tülli +tulnu +tulnud +tul=nud +tulvil +tumene +tümise +tummu +tümpsu +tund +(.*)_tund +tundma +tundu +tungi +tunnine +tunnista +tuntud +tupru +turbu +turdu +turgata +turguta +turni +turri +tursu +turtsu +turvu +tusa=ne +tüsene +tuska +tuskle +tusti +tütar +tütar_laps +tuttav +tuttu +tutvu +tutvusta +tutvustus +tuul +tuula +tuuldu +tuuluta +tuumenda +tüüp +tuupi +tüüri +tuuselda +tuusti +tüüta +udutse +üha +ühe +ühele_poole +ühel_pool +ühelt_poolt +ühenda +ühend_riik +ühendu +ühendus +(.*)_ühendus +Ühendus +ühes +ühine +ühing +(.*)_ühing +Ühing +uhise +uhka +uhkelda +uhkle +uhkusta +ühma +ühmata +uht +ühte +ühti +ühtlusta +uhtu +ühtu +uhundu +uima=ne +uinu +uiskle +uisuta +uita +uitle +uitseta +uju +ujula +ujuta +ukerda +ukraina +Ukraina +üks +üksi +ükski +üksnes +üksteise +üks_teise +ülal +ülal_pool +ülalt_poolt +ulata +ulatama +ulatse +ulatu +ulatus +üldi +üldista +üle +üle_eelmine +üle_eile +üle_homme +üle_järgmine +ülemine +ülenda +ülene +üles +ülesalla +üles_alla +ülespoole +üles_poole +ülesse +ületa +ülevaade +üleval +ülevalpool +üleval_pool +ülevalt +ülevaltpoolt +ülevalt_poolt +ulgu +üli_kool +Üli_kool +ülista +uljutse +üllata +üllatu +ulmi +ulu +ümahta +ümardu +ümata +umbe +ümber +ümbert +umbes +umb_kaudu +ümbritse +umbrohtu +umbusalda +umise +ümise +ümista +umme +ümmistu +ummuksisse +unarule +unda +unele +uni=ne +Unioon +unista +unistele +unistus +untsu +unusta +upita +uppi +uppu +üpris +uputa +ura +ürahta +urata +urba +urbne +urgitse +ürita +üritus +usalda +usk +usku +üsna +ussita +usutle +ütle +ütlema +utsita +uubu +uudista +uudu +uuendu +uuene +uurdu +üürga +uuri +üüri +uurista +uuta +(.*)v +(.*)[=]+v +va +v.a +vaada +vaada=nu +vaadanud +vaada=nud +vaagi +vääksata +vääksu +vaaku +vääna +väändu +väärata +väärdu +vääri +väärt +väärtu +väärtustu +vaaru +vääruta +vaata +vaatama +vaatamata +vaatle +vabanda +vabandu +vabane +vaba_riik +vabasta +vabastama +vabastamine +vabasta=mine +vabasta_mine +vabastu +vabise +vadele +vadi +vadista +väel +väele +vaesu +väeta +vaeva=ne +vaevle +vaevu +väga +vagatse +vägev +vagluta +vaglutu +vähe +vahel +vaheldu +vahele +vaheline +vahelt +vähem +vähemalt +vahemik +vähenda +vahene +vähene +vahest +vaheta +vahetu +vähkre +vahti +vahtima +vahtu +vahuta +vahutse +vahvu +vaibu +vaid +vaidle +vaigata +vaigistu +väike +vaiki +vaimustu +vait +väit +väita +väitle +vaja +vajadus +vajaka +vaju +vajuta +vakata +vakka +vaktsineeri +vala +vald +(.*)_vald +Vald +valda +valenda +valeta +valge +valgene +valgevene +valge_vene +Valgevene +Valge_vene +valgu +valgus +valgusta +valgustu +välguta +vali +välista +valitse +valitsus +Valitsus +välja +välja_poole +väljas_pool +väljasta +väljast_poolt +välja_vaade +väljenda +väljendu +valjene +väljene +välju +välkle +välku +valla +vallanda +vallandu +vallatle +valluta +valmi +valmidus +valmis +valmis_olek +valmista +valmistu +valssi +välta +vältel +välti +valu +välu +valuta +valva +vana +vana_ema +vana_isa +vanane +vanderda +vandu +vanem +vanguta +vanku +vanne +vannuta +vantsi +väntsu +vanu +vanune +vaple +vappu +vara +värahta +varal +varale +vara=ne +värba +värele +varem +vari +varise +varja +varju +varjus +varjust +varjuta +varjutu +varmasti +värskendu +värskene +varssu +varsti +vartu +varu +varul +varustu +värv +värvi +värvistu +värvu +vasakule +vasemale +väsi +(.*)_väsi +väsita +vassi +vassu +vasta +vastamisi +vastanda +vastane +vasta=ne +vastas +vastassugu +vastas_sugu +vastast +vastavalt +vastu +vaterda +vatra +veda +vedama +vedelik +veebruar +veel +veelgi +veen +veendu +veeni +veerand +veerde +veere +veeres +veerest +veereta +veeri +veet +veetle +vehki +vehkle +veiderda +veidi +veidike +veikle +velple +vene +Vene +venemaa +vene_maa +Venemaa +Vene_maa +venestu +veni +venita +vereta +veritse +vesi +vesista +vesitse +vest +vestle +vetru +vetti +vibale +vibele +vibise +vibreeri +vibuta +vidu +vigasta +vigastu +viha=ne +vihasta +vihastu +vihise +vihka +vihma +vihma=ne +vihtu +vii +viibi +viibuta +viiendik +viiksata +viiksu +viil +viili +viim +viimane +viima=ne +viimistle +viin +viirastu +viis +Vii[s|e]+(.*) +VII[S|E]+(.*) +viisi +viisil +viitle +viitsi +viivita +viivitle +viksi +vila +vilgata +vilista +vilju +vilkle +vilksa +vilksata +vilksi +vilksu +vilku +villa=ne +villi +viltu +vilu +vindu +vingerda +vinise +vinti +vintsku +vintsle +vintsu +viratse +virise +virn +virtsu +viru +viruta +virvenda +visa +visanud +visa=nud +visatud +viserda +(.*)_visioon +visise +viska +viskama +visku +vist +viunu +võbele +võbise +võdise +võe +või +võibolla +võib_olla +võib-olla +võidu +võiduma +võidutse +võim +võima +võimalda +võimaldav +võimaldu +võimalik +võimalus +võimas +võime +võimenda +võimutse +võistleja +võistle=mine +võistlus +(.*)_võistlus +võistu +võit +võitle +võitlus +voldine +võlgu +volise +volita +volksa +volksata +volti +voltu +võlu +võlvu +vonkle +võnkle +võnku +võnni +vood +vooda +voola +vooldu +võõpa +võõranda +võõrandu +võõrdu +võõritse +võõrusta +võõruta +vöötle +vöötu +võpata +võpatle +võppu +vopsata +võrdle +võrdne +võrdsusta +võrdu +võrise +vorm +vormi +vormu +võrra +võrsu +võsa=ne +võt +vudi +vuha +vuhise +vuhki +vula +vulise +vulksa +vulksata +vulpsa +vulpsata +vuntsi +vupsa +vupsata +vura +vurhvi +vurise +vurista +vurtsa +vürtsita +vurtsu +vusise +vusserda +vussi + Y) ( +z^estikuleeri +z^ongleeri diff --git a/confs/conf_edt_v211_Stanza_ME_full.ini b/confs/conf_edt_v211_Stanza_ME_full.ini new file mode 100644 index 00000000..82bbdd8a --- /dev/null +++ b/confs/conf_edt_v211_Stanza_ME_full.ini @@ -0,0 +1,69 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.11 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.11/preannotated/morph_extended + +[join_morph_extended_train_full] +input_dir = edt_2.11/preannotated/morph_extended +concatenate = train, dev +output_dir = edt_2.11/preannotated/morph_extended + +[train_stanza_morph_extended_full] +experiment_type = full_data +train_file = edt_2.11/preannotated/morph_extended/et_edt-ud-train-morph_extended.conllu +eval_file = edt_2.11/preannotated/morph_extended/et_edt-ud-dev-morph_extended.conllu +args = --batch_size 5000 +output_dir = edt_2.11/trained/morph_extended/stanza_full_data +model_file = model_morph_extended.pt +predict_after = True +#dry_run = True + +[predict_stanza_morph_extended_full] +experiment_type = full_data +train_file = edt_2.11/preannotated/morph_extended/train_full.conllu +test_file = edt_2.11/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +model_file = edt_2.11/trained/morph_extended/stanza_full_data/model_morph_extended.pt +output_dir = edt_2.11/evaluation/morph_extended/stanza_full_data +#dry_run = True + +#[predict_stanza_tagger_morph_extended_full] +#experiment_type = full_data +#train_file = edt_2.11/preannotated/morph_extended/train_full.conllu +#test_file = edt_2.11/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +#model_file = edt_2.11/trained/morph_extended/stanza_full_data/model_morph_extended.pt +#use_estnltk = True +#seed = 43 +#morph_layer = morph_extended +#output_file_prefix = predicted_stanzatagger_ +#output_dir = edt_2.11/evaluation/morph_extended/stanza_full_data + +[eval_stanza_morph_extended_full_data] +experiment_type = full_data +gold_train = edt_2.11/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.11/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.11/evaluation/morph_extended/stanza_full_data/predicted_train.conllu +predicted_test = edt_2.11/evaluation/morph_extended/stanza_full_data/predicted_test.conllu + +#[eval_stanza_tagger_morph_extended_full_data] +#experiment_type = full_data +#gold_train = edt_2.11/preannotated/morph_extended/train_full.conllu +#gold_test = edt_2.11/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +#predicted_train = edt_2.11/evaluation/morph_extended/stanza_full_data/predicted_stanzatagger_train.conllu +#predicted_test = edt_2.11/evaluation/morph_extended/stanza_full_data/predicted_stanzatagger_test.conllu + +#[eval_stanza_tagger_morph_extended_full_data_error_types] +#experiment_type = full_data +#count_error_types = True +#gold_train = edt_2.11/preannotated/morph_extended/train_full.conllu +#gold_test = edt_2.11/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +#predicted_train = edt_2.11/evaluation/morph_extended/stanza_full_data/predicted_train.conllu +#predicted_test = edt_2.11/evaluation/morph_extended/stanza_full_data/predicted_test.conllu +#name = stanza_ME_error_types +#skip_train = True +#count_words = True +#output_csv_file = edt_2.11/results_stanza_ME_error_types.csv \ No newline at end of file diff --git a/confs/conf_edt_v26_MaltParser_ME_full.ini b/confs/conf_edt_v26_MaltParser_ME_full.ini new file mode 100644 index 00000000..514c1fd7 --- /dev/null +++ b/confs/conf_edt_v26_MaltParser_ME_full.ini @@ -0,0 +1,44 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +# required by MaltOptimizer: +remove_metadata = True +output_dir = edt_2.6/preannotated/morph_extended_no_metadata + +[join_morph_extended_train_full] +input_dir = edt_2.6/preannotated/morph_extended_no_metadata +concatenate = train, dev +output_dir = edt_2.6/preannotated/morph_extended_no_metadata + +[maltoptimize_morph_extended_full] +input_files = edt_2.6/preannotated/morph_extended_no_metadata/et_edt-ud-dev-morph_extended.conllu +output_dir = edt_2.6/trained/morph_extended/maltparser_full_data + +[train_malt_morph_extended_full] +experiment_type = full_data +input_dir = edt_2.6/preannotated/morph_extended_no_metadata +train_file = edt_2.6/preannotated/morph_extended_no_metadata/et_edt-ud-train-morph_extended.conllu +final_options_file = edt_2.6/trained/morph_extended/maltparser_full_data/finalOptionsFile.xml +feature_model_file = edt_2.6/trained/morph_extended/maltparser_full_data/featureFile.xml +output_dir = edt_2.6/trained/morph_extended/maltparser_full_data +model_file = model_morph_extended.mco + +[predict_malt_morph_extended_full] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended_no_metadata/train_full.conllu +test_file = edt_2.6/preannotated/morph_extended_no_metadata/et_edt-ud-test-morph_extended.conllu +model_file = edt_2.6/trained/morph_extended/maltparser_full_data/model_morph_extended.mco +output_file_prefix = predicted_malt_ +output_dir = edt_2.6/evaluation/morph_extended/maltparser_full_data + +[eval_malt_morph_extended_full] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended_no_metadata/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended_no_metadata/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended/maltparser_full_data/predicted_malt_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/maltparser_full_data/predicted_malt_test.conllu \ No newline at end of file diff --git a/confs/conf_edt_v26_Stanza_MA_full.ini b/confs/conf_edt_v26_Stanza_MA_full.ini new file mode 100644 index 00000000..e0cc6322 --- /dev/null +++ b/confs/conf_edt_v26_Stanza_MA_full.ini @@ -0,0 +1,59 @@ +[preannotation_morph_analysis] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_analysis +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_analysis + +[join_morph_analysis_train_full] +input_dir = edt_2.6/preannotated/morph_analysis +concatenate = train, dev +output_dir = edt_2.6/preannotated/morph_analysis + +[train_stanza_morph_analysis_full] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_analysis/et_edt-ud-train-morph_analysis.conllu +eval_file = edt_2.6/preannotated/morph_analysis/et_edt-ud-dev-morph_analysis.conllu +args = --batch_size 5000 +output_dir = edt_2.6/trained/morph_analysis/stanza_full_data +model_file = model_morph_analysis.pt +predict_after = True +#dry_run = True + +[predict_stanza_morph_analysis_full] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_analysis/train_full.conllu +test_file = edt_2.6/preannotated/morph_analysis/et_edt-ud-test-morph_analysis.conllu +model_file = edt_2.6/trained/morph_analysis/stanza_full_data/model_morph_analysis.pt +output_dir = edt_2.6/evaluation/morph_analysis/stanza_full_data +#dry_run = True + +#[predict_stanza_tagger_morph_analysis_full] +#experiment_type = full_data +#train_file = edt_2.6/preannotated/morph_analysis/train_full.conllu +#test_file = edt_2.6/preannotated/morph_analysis/et_edt-ud-test-morph_analysis.conllu +#model_file = edt_2.6/trained/morph_analysis/stanza_full_data/model_morph_analysis.pt +#use_estnltk = True +#seed = 43 +#morph_layer = morph_analysis +#output_file_prefix = predicted_stanzatagger_ +#output_dir = edt_2.6/evaluation/morph_analysis/stanza_full_data + +[eval_stanza_morph_analysis_full_data] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_analysis/train_full.conllu +gold_test = edt_2.6/preannotated/morph_analysis/et_edt-ud-test-morph_analysis.conllu +predicted_train = edt_2.6/evaluation/morph_analysis/stanza_full_data/predicted_train.conllu +predicted_test = edt_2.6/evaluation/morph_analysis/stanza_full_data/predicted_test.conllu +count_words = True + +#[eval_stanza_tagger_morph_analysis_full_data] +#experiment_type = full_data +#gold_train = edt_2.6/preannotated/morph_analysis/train_full.conllu +#gold_test = edt_2.6/preannotated/morph_analysis/et_edt-ud-test-morph_analysis.conllu +#predicted_train = edt_2.6/evaluation/morph_analysis/stanza_full_data/predicted_stanzatagger_train.conllu +#predicted_test = edt_2.6/evaluation/morph_analysis/stanza_full_data/predicted_stanzatagger_test.conllu +#count_words = True diff --git a/confs/conf_edt_v26_Stanza_ME_crossvalidation.ini b/confs/conf_edt_v26_Stanza_ME_crossvalidation.ini new file mode 100644 index 00000000..a864c339 --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_crossvalidation.ini @@ -0,0 +1,50 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +# Make train_full (for evaluation) +[join_morph_extended_train_full] +input_dir = edt_2.6/preannotated/morph_extended +concatenate = train, dev +output_dir = edt_2.6/preannotated/morph_extended + +# we need 10 crossvalidation data splits +[split_morph_extended_crossvalidation] +input_dir = edt_2.6/preannotated/morph_extended +concatenate = train, dev +split_type = crossvalidation +block_count = 195 +split_count = 10 +seed = 9 +first_output_dir = edt_2.6/preannotated/morph_extended/crossvalidation_splits/initial_split +final_output_dir = edt_2.6/preannotated/morph_extended/crossvalidation_splits + +# train 10 crossvalidation models +[train_stanza_morph_extended_crossvalidation] +input_dir = edt_2.6/preannotated/morph_extended/crossvalidation_splits +experiment_type = crossvalidation +predict_after = True +args = --batch_size 5000 +output_dir = edt_2.6/trained/morph_extended/crossvalidation + +[predict_stanza_morph_extended_crossvalidation] +experiment_type = crossvalidation +input_dir = edt_2.6/preannotated/morph_extended/crossvalidation_splits +test_file = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +models_dir = edt_2.6/trained/morph_extended/crossvalidation +output_dir = edt_2.6/evaluation/morph_extended/stanza_crossvalidation + +# Evaluate all 10 models separately +[eval_stanza_morph_extended_crossvalidation] +experiment_type = crossvalidation +gold_splits_dir = edt_2.6/preannotated/morph_extended/crossvalidation_splits +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +predictions_dir = edt_2.6/evaluation/morph_extended/stanza_crossvalidation +macro_average = True + diff --git a/confs/conf_edt_v26_Stanza_ME_ensemble_conf_intervals.ini b/confs/conf_edt_v26_Stanza_ME_ensemble_conf_intervals.ini new file mode 100644 index 00000000..53dd3173 --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_ensemble_conf_intervals.ini @@ -0,0 +1,50 @@ +# Required preprocessing from configurations: +# * conf_edt_v26_stanza_ME_full.ini +# * conf_edt_v26_stanza_ME_ensemble_full.ini +# * conf_edt_v26_stanza_ME_ensemble_majority_voting_full.ini +# * conf_edt_v26_Stanza_ME_half_data.ini +# * conf_edt_v26_Stanza_ME_half_data_ensemble_majority_voting.ini + +# Single model (default) +[eval_stanza_morph_extended_full_data_conf_intervals] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended/stanza_full_data/predicted_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_full_data/predicted_test.conllu +add_conf_intervals = True + +# Full data ensemble +[eval_stanza_ensemble_morph_extended_full_data_las_coherence_conf_intervals] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +add_conf_intervals = True +predicted_train = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data/predicted_stanzaensembletagger_default_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data/predicted_stanzaensembletagger_default_test.conllu +#output_csv_file = edt_2.6/results_stanza_ME_conf_intervals.csv + +[eval_stanza_ensemble_morph_extended_full_data_majority_voting_conf_intervals] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +add_conf_intervals = True +predicted_train = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data/predicted_stanzaensembletagger_majority_voting_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data/predicted_stanzaensembletagger_majority_voting_test.conllu + +# Half data ensemble +[eval_stanza_ensemble_morph_extended_half_data_las_coherence_conf_intervals] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +add_conf_intervals = True +predicted_train = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data/predicted_stanzaensembletagger_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data/predicted_stanzaensembletagger_test.conllu + +[eval_stanza_ensemble_morph_extended_half_data_majority_voting_conf_intervals] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +add_conf_intervals = True +predicted_train = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data/predicted_stanzaensembletagger_majority_voting_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data/predicted_stanzaensembletagger_majority_voting_test.conllu \ No newline at end of file diff --git a/confs/conf_edt_v26_Stanza_ME_ensemble_entropy.ini b/confs/conf_edt_v26_Stanza_ME_ensemble_entropy.ini new file mode 100644 index 00000000..90f35c02 --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_ensemble_entropy.ini @@ -0,0 +1,43 @@ +# Required preprocessing from configurations: +# * conf_edt_v26_stanza_ME_ensemble_full.ini +# * conf_edt_v26_stanza_ME_ensemble_majority_voting_full.ini +# * conf_edt_v26_Stanza_ME_half_data.ini +# * conf_edt_v26_Stanza_ME_half_data_ensemble_majority_voting.ini + +# Full data ensemble +[eval_stanza_ensemble_morph_extended_full_data_las_coherence_entropy] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +calc_scores_with_entropy = True +count_words = True +predicted_train = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data/predicted_stanzaensembletagger_default_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data/predicted_stanzaensembletagger_default_test.conllu + +[eval_stanza_ensemble_morph_extended_full_data_majority_voting_entropy] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +calc_scores_with_entropy = True +count_words = True +predicted_train = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data/predicted_stanzaensembletagger_majority_voting_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data/predicted_stanzaensembletagger_majority_voting_test.conllu + +# Half data ensemble +[eval_stanza_ensemble_morph_extended_half_data_las_coherence_entropy] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +calc_scores_with_entropy = True +count_words = True +predicted_train = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data/predicted_stanzaensembletagger_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data/predicted_stanzaensembletagger_test.conllu + +[eval_stanza_ensemble_morph_extended_half_data_majority_voting_entropy] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +calc_scores_with_entropy = True +count_words = True +predicted_train = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data/predicted_stanzaensembletagger_majority_voting_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data/predicted_stanzaensembletagger_majority_voting_test.conllu \ No newline at end of file diff --git a/confs/conf_edt_v26_Stanza_ME_full.ini b/confs/conf_edt_v26_Stanza_ME_full.ini new file mode 100644 index 00000000..4b0dea4b --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_full.ini @@ -0,0 +1,81 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +[join_morph_extended_train_full] +input_dir = edt_2.6/preannotated/morph_extended +concatenate = train, dev +output_dir = edt_2.6/preannotated/morph_extended + +[train_stanza_morph_extended_full] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended/et_edt-ud-train-morph_extended.conllu +eval_file = edt_2.6/preannotated/morph_extended/et_edt-ud-dev-morph_extended.conllu +args = --batch_size 5000 +output_dir = edt_2.6/trained/morph_extended/stanza_full_data +model_file = model_morph_extended.pt +predict_after = True +#dry_run = True + +[predict_stanza_morph_extended_full] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended/train_full.conllu +test_file = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +model_file = edt_2.6/trained/morph_extended/stanza_full_data/model_morph_extended.pt +output_dir = edt_2.6/evaluation/morph_extended/stanza_full_data +#dry_run = True + +[predict_stanza_tagger_morph_extended_full] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended/train_full.conllu +test_file = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +model_file = edt_2.6/trained/morph_extended/stanza_full_data/model_morph_extended.pt +use_estnltk = True +seed = 43 +morph_layer = morph_extended +output_file_prefix = predicted_stanzatagger_ +output_dir = edt_2.6/evaluation/morph_extended/stanza_full_data + +[eval_stanza_morph_extended_full_data] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended/stanza_full_data/predicted_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_full_data/predicted_test.conllu +count_words = True + +[eval_stanza_morph_extended_full_data_conf_intervals] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended/stanza_full_data/predicted_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_full_data/predicted_test.conllu +count_words = True +add_conf_intervals = True +output_csv_file = edt_2.6/results_stanza_ME_full_conf_intervals.csv + +#[eval_stanza_tagger_morph_extended_full_data] +#experiment_type = full_data +#gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +#gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +#predicted_train = edt_2.6/evaluation/morph_extended/stanza_full_data/predicted_stanzatagger_train.conllu +#predicted_test = edt_2.6/evaluation/morph_extended/stanza_full_data/predicted_stanzatagger_test.conllu +#count_words = True + +[eval_stanza_tagger_morph_extended_full_data_error_types] +experiment_type = full_data +count_error_types = True +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended/stanza_full_data/predicted_stanzatagger_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_full_data/predicted_stanzatagger_test.conllu +name = stanza_ME_error_types +skip_train = True +count_words = True +output_csv_file = edt_2.6/results_stanza_ME_error_types.csv diff --git a/confs/conf_edt_v26_Stanza_ME_full_clauses.ini b/confs/conf_edt_v26_Stanza_ME_full_clauses.ini new file mode 100644 index 00000000..dc06d1e1 --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_full_clauses.ini @@ -0,0 +1,58 @@ +# * Prepares clauses ME dataset +# * Trains model on clauses ME dataset and also applies on clauses ME dataset +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +[extract_clauses_morph_extended] +input_dir = edt_2.6/preannotated/morph_extended +skip_list = train_full.conllu +output_dir = edt_2.6/preannotated/morph_extended_clauses + +[join_morph_extended_clauses] +input_dir = edt_2.6/preannotated/morph_extended_clauses +concatenate = train, dev +output_dir = edt_2.6/preannotated/morph_extended_clauses + +[train_stanza_morph_extended_on_clauses] +input_dir = edt_2.6/preannotated/morph_extended_clauses +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended_clauses/et_edt-ud-train-morph_extended.conllu +eval_file = edt_2.6/preannotated/morph_extended_clauses/et_edt-ud-dev-morph_extended.conllu +args = --batch_size 5000 +output_dir = edt_2.6/trained/morph_extended_clauses/stanza_full_data +model_file = model_morph_extended_clauses.pt +predict_after = True + +[predict_stanza_morph_extended_on_clauses] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended_clauses/train_full.conllu +test_file = edt_2.6/preannotated/morph_extended_clauses/et_edt-ud-test-morph_extended.conllu +model_file = edt_2.6/trained/morph_extended_clauses/stanza_full_data/model_morph_extended_clauses.pt +output_file_prefix = predicted_ +output_dir = edt_2.6/evaluation/morph_extended_clauses/stanza_full_data + +[eval_stanza_morph_extended_on_clauses] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended_clauses/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended_clauses/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended_clauses/stanza_full_data/predicted_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended_clauses/stanza_full_data/predicted_test.conllu + +[eval_stanza_morph_extended_on_clauses_error_types] +experiment_type = full_data +count_error_types = True +gold_train = edt_2.6/preannotated/morph_extended_clauses/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended_clauses/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended_clauses/stanza_full_data/predicted_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended_clauses/stanza_full_data/predicted_test.conllu +name = stanza_ME_on_clauses_error_types +skip_train = True +count_words = True +output_csv_file = edt_2.6/results_stanza_ME_on_clauses_error_types.csv \ No newline at end of file diff --git a/confs/conf_edt_v26_Stanza_ME_full_predict_clauses.ini b/confs/conf_edt_v26_Stanza_ME_full_predict_clauses.ini new file mode 100644 index 00000000..d29eae45 --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_full_predict_clauses.ini @@ -0,0 +1,67 @@ +# * Prepares clauses ME dataset and full ME dataset +# * Trains model on full ME dataset and applies on clauses ME dataset +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +# full ME dataset +[join_morph_extended_train_full] +input_dir = edt_2.6/preannotated/morph_extended +concatenate = train, dev +output_dir = edt_2.6/preannotated/morph_extended + +# clauses ME dataset +[extract_clauses_morph_extended] +input_dir = edt_2.6/preannotated/morph_extended +skip_list = train_full.conllu +output_dir = edt_2.6/preannotated/morph_extended_clauses + +[join_morph_extended_clauses] +input_dir = edt_2.6/preannotated/morph_extended_clauses +concatenate = train, dev +output_dir = edt_2.6/preannotated/morph_extended_clauses + +# train on full ME dataset +[train_stanza_morph_extended_full] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended/et_edt-ud-train-morph_extended.conllu +eval_file = edt_2.6/preannotated/morph_extended/et_edt-ud-dev-morph_extended.conllu +args = --batch_size 5000 +output_dir = edt_2.6/trained/morph_extended/stanza_full_data +model_file = model_morph_extended.pt +predict_after = True +#dry_run = True + +# predict with full ME model on clauses ME dataset +[predict_stanza_morph_extended_full_on_clauses] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended_clauses/train_full.conllu +test_file = edt_2.6/preannotated/morph_extended_clauses/et_edt-ud-test-morph_extended.conllu +model_file = edt_2.6/trained/morph_extended/stanza_full_data/model_morph_extended.pt +output_file_prefix = predicted_ +output_dir = edt_2.6/evaluation/morph_extended_clauses/stanza_full_data_not_trained_on_clauses + +[eval_stanza_morph_extended_full_predict_on_clauses] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended_clauses/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended_clauses/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended_clauses/stanza_full_data_not_trained_on_clauses/predicted_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended_clauses/stanza_full_data_not_trained_on_clauses/predicted_test.conllu + +[eval_stanza_morph_extended_full_predict_on_clauses_error_types] +experiment_type = full_data +count_error_types = True +gold_train = edt_2.6/preannotated/morph_extended_clauses/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended_clauses/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended_clauses/stanza_full_data_not_trained_on_clauses/predicted_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended_clauses/stanza_full_data_not_trained_on_clauses/predicted_test.conllu +name = stanza_ME_full_on_clauses_error_types +skip_train = True +count_words = True +output_csv_file = edt_2.6/results_stanza_ME_full_predict_on_clauses_error_types.csv \ No newline at end of file diff --git a/confs/conf_edt_v26_Stanza_ME_gap_experiments.ini b/confs/conf_edt_v26_Stanza_ME_gap_experiments.ini new file mode 100644 index 00000000..35e12151 --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_gap_experiments.ini @@ -0,0 +1,56 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +[modify_conllu_morph_extended_full] +input_dir = edt_2.6/preannotated/morph_extended +gap_experiments = 01_no_wordforms, + 02_no_lemmas, + 02_no_pos, + 03_no_wordforms_adj_noun_lemmas, + 04_no_wordforms_verb_adpos_lemmas, + 05_only_cg_list_wordforms_lemmas, + 06_no_wordform_lemma_pos_keep_conj, + 07_no_wordform_lemma_pos + 08_only_wordforms, + 09_only_pos_feats +output_dir = edt_2.6/preannotated/morph_extended/gap_experiments + +[train_stanza_morph_extended_gap_experiments] +input_dir = edt_2.6/preannotated/morph_extended/gap_experiments +experiment_type = multi_experiment +train_file_pat = (?P\S+)_train.conllu +dev_file_pat = (?P\S+)_dev.conllu +test_file_pat = (?P\S+)_dev.conllu +args = --batch_size 5000 +predict_after = True +output_dir = edt_2.6/trained/morph_extended/gap_experiments +#dry_run = True + +[predict_stanza_morph_extended_gap_experiments] +input_dir = edt_2.6/preannotated/morph_extended/gap_experiments +experiment_type = multi_experiment +train_file_pat = (?P\S+)_train.conllu +test_file = (?P\S+)_test.conllu +test_file_is_pattern = True +models_dir = edt_2.6/trained/morph_extended/gap_experiments +output_dir = edt_2.6/evaluation/morph_extended/gap_experiments +use_gpu = True +#dry_run = True + +[eval_stanza_morph_extended_gap_experiments] +experiment_type = multi_experiment +gold_splits_dir = edt_2.6/preannotated/morph_extended/gap_experiments +gold_test = (?P\S+)_test.conllu +test_file_is_pattern = True +train_file_pat = (?P\S+)_train.conllu +# we need to specify punct listing, because xpos/upos have been deleted ... +punct_tokens_file = background_data/edt_punct_tokens.txt +predictions_dir = edt_2.6/evaluation/morph_extended/gap_experiments +macro_average = False diff --git a/confs/conf_edt_v26_Stanza_ME_half_data.ini b/confs/conf_edt_v26_Stanza_ME_half_data.ini new file mode 100644 index 00000000..3301fc69 --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_half_data.ini @@ -0,0 +1,69 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +# make 50%-50% data splits +[split_morph_extended_half_data] +input_dir = edt_2.6/preannotated/morph_extended +concatenate = train, dev +split_type = half_data +seed = 9 +first_output_dir = edt_2.6/preannotated/morph_extended/half_data_splits/initial_data +final_output_dir = edt_2.6/preannotated/morph_extended/half_data_splits + +# train half data models +[train_stanza_morph_extended_half_data] +input_dir = edt_2.6/preannotated/morph_extended/half_data_splits +experiment_type = half_data +train_file_pat = (?P\S+)_train.conllu +dev_file_pat = (?P\S+)_dev.conllu +predict_after = True +args = --batch_size 5000 +output_dir = edt_2.6/trained/morph_extended/stanza_half_data + +[predict_stanza_morph_extended_half_data] +experiment_type = half_data +input_dir = edt_2.6/preannotated/morph_extended/half_data_splits +train_file_pat = (?P\S+)_train_all.conllu +test_file = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +models_dir = edt_2.6/trained/morph_extended/stanza_half_data +output_dir = edt_2.6/evaluation/morph_extended/stanza_half_data + +# predict using 10 models as an ensemble +[predict_stanza_morph_extended_half_data_ensemble] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended/train_full.conllu +test_file = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +models_dir = edt_2.6/trained/morph_extended/stanza_half_data +#tagger_path = ensemble_tagger.StanzaSyntaxEnsembleTagger +tagger_path = ensemble_tagger_with_entropy.StanzaSyntaxEnsembleTaggerWithEntropy +use_estnltk = True +use_ensemble = True +seed = 43 +scores_seed = 3 +use_gpu = True +morph_layer = morph_extended +output_file_prefix = predicted_stanzaensembletagger_ +output_dir = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data + +# Evaluate all 10 models separately +[eval_stanza_morph_extended_half_data] +experiment_type = crossvalidation +gold_splits_dir = edt_2.6/preannotated/morph_extended/half_data_splits +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +predictions_dir = edt_2.6/evaluation/morph_extended/stanza_half_data +macro_average = True + +# Evaluate the ensemble model +[eval_stanza_morph_extended_half_data_ensemble] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data/predicted_stanzaensembletagger_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data/predicted_stanzaensembletagger_test.conllu \ No newline at end of file diff --git a/confs/conf_edt_v26_Stanza_ME_half_data_ensemble_majority_voting.ini b/confs/conf_edt_v26_Stanza_ME_half_data_ensemble_majority_voting.ini new file mode 100644 index 00000000..6b627072 --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_half_data_ensemble_majority_voting.ini @@ -0,0 +1,54 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +# make 50%-50% data splits +[split_morph_extended_half_data] +input_dir = edt_2.6/preannotated/morph_extended +concatenate = train, dev +split_type = half_data +seed = 9 +first_output_dir = edt_2.6/preannotated/morph_extended/half_data_splits/initial_data +final_output_dir = edt_2.6/preannotated/morph_extended/half_data_splits + +# train half data models +[train_stanza_morph_extended_half_data] +input_dir = edt_2.6/preannotated/morph_extended/half_data_splits +experiment_type = half_data +train_file_pat = (?P\S+)_train.conllu +dev_file_pat = (?P\S+)_dev.conllu +predict_after = True +args = --batch_size 5000 +output_dir = edt_2.6/trained/morph_extended/stanza_half_data + +# predict using 10 models as an ensemble +[predict_stanza_morph_extended_half_data_ensemble_majority_voting] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended/train_full.conllu +test_file = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +models_dir = edt_2.6/trained/morph_extended/stanza_half_data +#tagger_path = ensemble_tagger.StanzaSyntaxEnsembleTagger +tagger_path = ensemble_tagger_with_entropy.StanzaSyntaxEnsembleTaggerWithEntropy +use_estnltk = True +use_ensemble = True +use_majority_voting = True +seed = 43 +scores_seed = 3 +use_gpu = True +morph_layer = morph_extended +output_file_prefix = predicted_stanzaensembletagger_majority_voting_ +output_dir = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data + +# Evaluate the ensemble model +[eval_stanza_morph_extended_half_data_ensemble_majority_voting] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data/predicted_stanzaensembletagger_majority_voting_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_ensemble_half_data/predicted_stanzaensembletagger_majority_voting_test.conllu \ No newline at end of file diff --git a/confs/conf_edt_v26_Stanza_ME_sketches_knockout_5_groups.ini b/confs/conf_edt_v26_Stanza_ME_sketches_knockout_5_groups.ini new file mode 100644 index 00000000..21a0aaec --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_sketches_knockout_5_groups.ini @@ -0,0 +1,94 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +[extract_clauses_morph_extended] +input_dir = edt_2.6/preannotated/morph_extended +skip_list = train_full.conllu +output_dir = edt_2.6/preannotated/morph_extended_clauses + +[make_sketches_table_morph_extended_top_50] +input_dir = edt_2.6/preannotated/morph_extended_clauses +top_n = 50 +output_csv_file = edt_2.6/preannotated/morph_extended_clauses/top_50_sketches.csv + +[prepare_knockout_morph_extended_top_50_sketches_5groups] +input_dir = edt_2.6/preannotated/morph_extended_clauses +top_sketches_file = edt_2.6/preannotated/morph_extended_clauses/top_50_sketches.csv +min_support = 50 +# distribute top_n_sketches randomly into 5 roughly same size groups +# prepare knockout on groups +random_groups = 5 +grouping_seed = 1 +initial_output_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_initial +final_output_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_splits + +[train_stanza_morph_extended_sketches_knockout_5groups] +input_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_splits +experiment_type = multi_experiment +train_file_pat = train_(?Pgroup\d+).conllu +dev_file_pat = dev_(?Pgroup\d+).conllu +test_file_pat = dev_(?Pgroup\d+).conllu +args = --batch_size 5000 +predict_after = False +output_dir = edt_2.6/trained/morph_extended_clauses/knockout_5groups +#dry_run = True + +[predict_stanza_morph_extended_sketches_knockout_5groups_matrix] +input_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_splits +experiment_type = multi_experiment +train_file_pat = train_(?Pgroup\d+).conllu +test_file = test_(?Pgroup\d+).conllu +test_file_is_pattern = True +# test matrix: evaluate all models on all test files +test_matrix = True +output_file_prefix = predicted_matrix_ +models_dir = edt_2.6/trained/morph_extended_clauses/knockout_5groups +output_dir = edt_2.6/evaluation/morph_extended_clauses/knockout_5groups +use_gpu = True +#dry_run = True + +[predict_stanza_morph_extended_sketches_knockout_5groups_single_test] +input_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_splits +experiment_type = multi_experiment +train_file_pat = train_(?Pgroup\d+).conllu +test_file = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_initial/test_50x50.conllu +test_file_is_pattern = False +models_dir = edt_2.6/trained/morph_extended_clauses/knockout_5groups +output_dir = edt_2.6/evaluation/morph_extended_clauses/knockout_5groups +use_gpu = True +#dry_run = True + +[eval_stanza_morph_extended_sketches_knockout_5groups_matrix] +experiment_type = multi_experiment +gold_splits_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_splits +gold_test = test_(?Pgroup\d+).conllu +test_file_is_pattern = True +train_file_pat = train_(?Pgroup\d+).conllu +test_matrix = True +skip_train = True +predictions_prefix = predicted_matrix_ +punct_tokens_file = background_data/edt_punct_tokens.txt +predictions_dir = edt_2.6/evaluation/morph_extended_clauses/knockout_5groups +macro_average = False +output_csv_file = edt_2.6/results_stanza_ME_sketches_5groups_knockout_matrix.csv + +[eval_stanza_morph_extended_sketches_knockout_5groups_single_file] +experiment_type = multi_experiment +gold_splits_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_splits +gold_test = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_initial/test_50x50.conllu +test_file_is_pattern = False +train_file_pat = train_(?Pgroup\d+).conllu +test_matrix = False +skip_train = True +predictions_prefix = predicted_ +punct_tokens_file = background_data/edt_punct_tokens.txt +predictions_dir = edt_2.6/evaluation/morph_extended_clauses/knockout_5groups +macro_average = False +output_csv_file = edt_2.6/results_stanza_ME_sketches_5groups_knockout.csv \ No newline at end of file diff --git a/confs/conf_edt_v26_Stanza_ME_sketches_knockout_5_groups_random.ini b/confs/conf_edt_v26_Stanza_ME_sketches_knockout_5_groups_random.ini new file mode 100644 index 00000000..43d611f1 --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_sketches_knockout_5_groups_random.ini @@ -0,0 +1,97 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +[extract_clauses_morph_extended] +input_dir = edt_2.6/preannotated/morph_extended +skip_list = train_full.conllu +output_dir = edt_2.6/preannotated/morph_extended_clauses + +[make_sketches_table_morph_extended_top_50] +input_dir = edt_2.6/preannotated/morph_extended_clauses +top_n = 50 +output_csv_file = edt_2.6/preannotated/morph_extended_clauses/top_50_sketches.csv + +[prepare_knockout_morph_extended_top_50_sketches_5randomgroups] +input_dir = edt_2.6/preannotated/morph_extended_clauses +top_sketches_file = edt_2.6/preannotated/morph_extended_clauses/top_50_sketches.csv +min_support = 50 +# distribute top_n_sketches randomly into 5 roughly same size groups +random_groups = 5 +grouping_seed = 1 +# Create control dataset: chose same amount of clauses as in knockout sketches randomly +# prepare knockout +create_control = True +create_control_seed = 5 +initial_output_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_initial +final_output_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_random + +[train_stanza_morph_extended_sketches_knockout_5randomgroups] +input_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_random +experiment_type = multi_experiment +train_file_pat = train_(?Pgroup\d+).conllu +dev_file_pat = dev_(?Pgroup\d+).conllu +test_file_pat = dev_(?Pgroup\d+).conllu +args = --batch_size 5000 +predict_after = False +output_dir = edt_2.6/trained/morph_extended_clauses/knockout_5groups_random +#dry_run = True + +[predict_stanza_morph_extended_sketches_knockout_5randomgroups_matrix] +input_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_random +experiment_type = multi_experiment +train_file_pat = train_(?Pgroup\d+).conllu +test_file = test_(?Pgroup\d+).conllu +test_file_is_pattern = True +# test matrix: evaluate all models on all test files +test_matrix = True +output_file_prefix = predicted_matrix_ +models_dir = edt_2.6/trained/morph_extended_clauses/knockout_5groups_random +output_dir = edt_2.6/evaluation/morph_extended_clauses/knockout_5groups_random +use_gpu = True +#dry_run = True + +[predict_stanza_morph_extended_sketches_knockout_5randomgroups_single_test] +input_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_random +experiment_type = multi_experiment +train_file_pat = train_(?Pgroup\d+).conllu +test_file = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_initial/test_50x50.conllu +test_file_is_pattern = False +models_dir = edt_2.6/trained/morph_extended_clauses/knockout_5groups_random +output_dir = edt_2.6/evaluation/morph_extended_clauses/knockout_5groups_random +use_gpu = True +#dry_run = True + +[eval_stanza_morph_extended_sketches_knockout_5randomgroups_matrix] +experiment_type = multi_experiment +gold_splits_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_random +gold_test = test_(?Pgroup\d+).conllu +test_file_is_pattern = True +train_file_pat = train_(?Pgroup\d+).conllu +test_matrix = True +skip_train = True +predictions_prefix = predicted_matrix_ +punct_tokens_file = background_data/edt_punct_tokens.txt +predictions_dir = edt_2.6/evaluation/morph_extended_clauses/knockout_5groups_random +macro_average = False +output_csv_file = edt_2.6/results_stanza_ME_sketches_5randomgroups_knockout_matrix.csv + +[eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file] +experiment_type = multi_experiment +gold_splits_dir = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_random +gold_test = edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_initial/test_50x50.conllu +test_file_is_pattern = False +train_file_pat = train_(?Pgroup\d+).conllu +test_matrix = False +skip_train = True +predictions_prefix = predicted_ +punct_tokens_file = background_data/edt_punct_tokens.txt +predictions_dir = edt_2.6/evaluation/morph_extended_clauses/knockout_5groups_random +macro_average = False +output_csv_file = edt_2.6/results_stanza_ME_sketches_5randomgroups_knockout.csv \ No newline at end of file diff --git a/confs/conf_edt_v26_Stanza_ME_smaller_data_01_only_form.ini b/confs/conf_edt_v26_Stanza_ME_smaller_data_01_only_form.ini new file mode 100644 index 00000000..c96d44ae --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_smaller_data_01_only_form.ini @@ -0,0 +1,69 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +# we need 10 smaller data splits +[split_morph_extended_smaller_data_01] +input_dir = edt_2.6/preannotated/morph_extended +concatenate = train, dev +split_type = smaller_data +block_count = 195 +split_count = 10 +seed = 9 +first_output_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/initial_split +final_output_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/02_keep_all + +# modify train/train_all/dev files +[modify_conllu_morph_extended_smaller_data_01_train_dev] +input_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/02_keep_all +gap_experiments = 08_only_wordforms +suppress_checks = True +conll_file_pat = (?P\S+)_(train|train_all|dev).conllu +output_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/01_only_form + +# modify test file +[modify_conllu_morph_extended_smaller_data_01_test] +input_dir = edt_2.6/preannotated/morph_extended +gap_experiments = 08_only_wordforms +suppress_checks = True +conll_file_pat = \S+-(?Ptest)-\S+\.conllu +output_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits +# --> Creates: 08_only_wordforms_test_test.conllu + +[train_stanza_morph_extended_smaller_data_01] +input_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/01_only_form +experiment_type = smaller_data +train_file_pat = (?P\S+)_train.conllu +dev_file_pat = (?P\S+)_dev.conllu +test_file_pat = (?P\S+)_dev.conllu +args = --batch_size 5000 +predict_after = True +output_dir = edt_2.6/trained/morph_extended/smaller_data/01_only_form +#dry_run = True + +[predict_stanza_morph_extended_smaller_data_01] +input_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/01_only_form +experiment_type = smaller_data +train_file_pat = (?P\S+)_train_all.conllu +test_file = edt_2.6/preannotated/morph_extended/smaller_data_splits/08_only_wordforms_test_test.conllu +models_dir = edt_2.6/trained/morph_extended/smaller_data/01_only_form +output_dir = edt_2.6/evaluation/morph_extended/smaller_data/01_only_form +use_gpu = True +#dry_run = True + +[eval_stanza_morph_extended_smaller_data_01] +experiment_type = smaller_data +gold_splits_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/01_only_form +gold_test = edt_2.6/preannotated/morph_extended/smaller_data_splits/08_only_wordforms_test_test.conllu +train_file_pat = (?P\S+)_train_all.conllu +# we need to specify punct listing, because xpos/upos have been deleted ... +punct_tokens_file = background_data/edt_punct_tokens.txt +predictions_dir = edt_2.6/evaluation/morph_extended/smaller_data/01_only_form +macro_average = False +count_words = True \ No newline at end of file diff --git a/confs/conf_edt_v26_Stanza_ME_smaller_data_02_keep_all.ini b/confs/conf_edt_v26_Stanza_ME_smaller_data_02_keep_all.ini new file mode 100644 index 00000000..0d2dc432 --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_smaller_data_02_keep_all.ini @@ -0,0 +1,50 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +# we need 10 smaller data splits +[split_morph_extended_smaller_data_02] +input_dir = edt_2.6/preannotated/morph_extended +concatenate = train, dev +split_type = smaller_data +block_count = 195 +split_count = 10 +seed = 9 +first_output_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/initial_split +final_output_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/02_keep_all + +[train_stanza_morph_extended_smaller_data_02_keep_all] +input_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/02_keep_all +experiment_type = smaller_data +train_file_pat = (?P\S+)_train.conllu +dev_file_pat = (?P\S+)_dev.conllu +test_file_pat = (?P\S+)_dev.conllu +args = --batch_size 5000 +predict_after = True +output_dir = edt_2.6/trained/morph_extended/smaller_data/02_keep_all +#dry_run = True + +[predict_stanza_morph_extended_smaller_data_02_keep_all] +input_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/02_keep_all +experiment_type = smaller_data +train_file_pat = (?P\S+)_train_all.conllu +test_file = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +models_dir = edt_2.6/trained/morph_extended/smaller_data/02_keep_all +output_dir = edt_2.6/evaluation/morph_extended/smaller_data/02_keep_all +use_gpu = True +#dry_run = True + +[eval_stanza_morph_extended_smaller_data_02_keep_all] +experiment_type = smaller_data +gold_splits_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/02_keep_all +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +train_file_pat = (?P\S+)_train_all.conllu +predictions_dir = edt_2.6/evaluation/morph_extended/smaller_data/02_keep_all +macro_average = False +count_words = True diff --git a/confs/conf_edt_v26_Stanza_ME_smaller_data_03_only_pos_feats.ini b/confs/conf_edt_v26_Stanza_ME_smaller_data_03_only_pos_feats.ini new file mode 100644 index 00000000..f7e88dff --- /dev/null +++ b/confs/conf_edt_v26_Stanza_ME_smaller_data_03_only_pos_feats.ini @@ -0,0 +1,67 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +# we need 10 smaller data splits +[split_morph_extended_smaller_data_03] +input_dir = edt_2.6/preannotated/morph_extended +concatenate = train, dev +split_type = smaller_data +block_count = 195 +split_count = 10 +seed = 9 +first_output_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/initial_split +final_output_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/02_keep_all + +# modify train/train_all/dev files +[modify_conllu_morph_extended_smaller_data_03_train_dev] +input_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/02_keep_all +gap_experiments = 09_only_pos_feats +suppress_checks = True +conll_file_pat = (?P\S+)_(train|train_all|dev).conllu +output_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/03_only_pos_feats + +# modify test file +[modify_conllu_morph_extended_smaller_data_03_test] +input_dir = edt_2.6/preannotated/morph_extended +gap_experiments = 09_only_pos_feats +suppress_checks = True +conll_file_pat = \S+-(?Ptest)-\S+\.conllu +output_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits +# --> Creates: 09_only_pos_feats_test_test.conllu + +[train_stanza_morph_extended_smaller_data_03] +input_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/03_only_pos_feats +experiment_type = smaller_data +train_file_pat = (?P\S+)_train.conllu +dev_file_pat = (?P\S+)_dev.conllu +test_file_pat = (?P\S+)_dev.conllu +args = --batch_size 5000 +predict_after = True +output_dir = edt_2.6/trained/morph_extended/smaller_data/03_only_pos_feats +#dry_run = True + +[predict_stanza_morph_extended_smaller_data_03_only_pos_feats] +input_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/03_only_pos_feats +experiment_type = smaller_data +train_file_pat = (?P\S+)_train_all.conllu +test_file = edt_2.6/preannotated/morph_extended/smaller_data_splits/09_only_pos_feats_test_test.conllu +models_dir = edt_2.6/trained/morph_extended/smaller_data/03_only_pos_feats +output_dir = edt_2.6/evaluation/morph_extended/smaller_data/03_only_pos_feats +use_gpu = True +#dry_run = True + +[eval_stanza_morph_extended_smaller_data_03_only_pos_feats] +experiment_type = smaller_data +gold_splits_dir = edt_2.6/preannotated/morph_extended/smaller_data_splits/03_only_pos_feats +gold_test = edt_2.6/preannotated/morph_extended/smaller_data_splits/09_only_pos_feats_test_test.conllu +train_file_pat = (?P\S+)_train_all.conllu +predictions_dir = edt_2.6/evaluation/morph_extended/smaller_data/03_only_pos_feats +macro_average = False +count_words = True \ No newline at end of file diff --git a/confs/conf_edt_v26_Stanza_UD_morph_Stanza_UD_syntax.ini b/confs/conf_edt_v26_Stanza_UD_morph_Stanza_UD_syntax.ini new file mode 100644 index 00000000..bd3fa4fa --- /dev/null +++ b/confs/conf_edt_v26_Stanza_UD_morph_Stanza_UD_syntax.ini @@ -0,0 +1,59 @@ +# predict with stanza's et morph tagger & lemmatizer and then train stanza syntax on it +[copy_ud_morph] +input_dir = UD_Estonian-EDT-r2.6 +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/ud_stanza_auto_morph + +[join_ud_stanza_morph_train_full] +input_dir = edt_2.6/preannotated/ud_stanza_auto_morph +concatenate = train, dev +output_dir = edt_2.6/preannotated/ud_stanza_auto_morph + +[predict_morph_feats_stanza_ud_1] +train_file = edt_2.6/preannotated/ud_stanza_auto_morph/et_edt-ud-train.conllu +test_file = edt_2.6/preannotated/ud_stanza_auto_morph/et_edt-ud-test.conllu +# we're using models from stanza's resources ver 1.4.1 (since we use stanza 1.4.2) +download_models = True +output_file_prefix = morph_predicted_ +output_dir = edt_2.6/preannotated/ud_stanza_auto_morph +use_gpu = True + +[predict_morph_feats_stanza_ud_2] +train_file = edt_2.6/preannotated/ud_stanza_auto_morph/train_full.conllu +test_file = edt_2.6/preannotated/ud_stanza_auto_morph/et_edt-ud-dev.conllu +# we're using models from stanza's resources ver 1.4.1 (since we use stanza 1.4.2) +download_models = True +output_file_prefix = morph_predicted_ +output_dir = edt_2.6/preannotated/ud_stanza_auto_morph +use_gpu = True + +[train_stanza_ud_on_stanza_auto_morph] +experiment_type = full_data +train_file = edt_2.6/preannotated/ud_stanza_auto_morph/morph_predicted_et_edt-ud-train.conllu +eval_file = edt_2.6/preannotated/ud_stanza_auto_morph/morph_predicted_et_edt-ud-dev.conllu +args = --batch_size 5000 +output_dir = edt_2.6/trained/ud_stanza_auto_morph/stanza_full_data +model_file = deprel_model_ud.pt +predict_after = True +#dry_run = True + +[predict_stanza_ud_on_stanza_auto_morph_full] +experiment_type = full_data +train_file = edt_2.6/preannotated/ud_stanza_auto_morph/morph_predicted_train_full.conllu +test_file = edt_2.6/preannotated/ud_stanza_auto_morph/morph_predicted_et_edt-ud-test.conllu +model_file = edt_2.6/trained/ud_stanza_auto_morph/stanza_full_data/deprel_model_ud.pt +output_dir = edt_2.6/evaluation/ud_stanza_auto_morph/stanza_full_data +#dry_run = True + +[eval_stanza_ud_on_stanza_auto_morph_full_data] +experiment_type = full_data +gold_train = edt_2.6/preannotated/ud_stanza_auto_morph/train_full.conllu +gold_test = edt_2.6/preannotated/ud_stanza_auto_morph/et_edt-ud-test.conllu +predicted_train = edt_2.6/evaluation/ud_stanza_auto_morph/stanza_full_data/predicted_train.conllu +predicted_test = edt_2.6/evaluation/ud_stanza_auto_morph/stanza_full_data/predicted_test.conllu + + + + diff --git a/confs/conf_edt_v26_UDPipe1_ME_full.ini b/confs/conf_edt_v26_UDPipe1_ME_full.ini new file mode 100644 index 00000000..9ae6d931 --- /dev/null +++ b/confs/conf_edt_v26_UDPipe1_ME_full.ini @@ -0,0 +1,65 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +# required by MaltOptimizer: +remove_metadata = True +output_dir = edt_2.6/preannotated/morph_extended_no_metadata + +[join_morph_extended_train_full] +input_dir = edt_2.6/preannotated/morph_extended_no_metadata +concatenate = train, dev +output_dir = edt_2.6/preannotated/morph_extended_no_metadata + +[train_udpipe1_default_morph_extended_full] +# Train UDPipe with default parameters, except learning_rate & number of iterations +experiment_type = full_data +input_dir = edt_2.6/preannotated/morph_extended_no_metadata +train_file = edt_2.6/preannotated/morph_extended_no_metadata/et_edt-ud-train-morph_extended.conllu +parser_options = learning_rate=0.01;iterations=30 +output_dir = edt_2.6/trained/morph_extended/udpipe1_full_data +model_file = model_morph_extended_default.udpipe + +[train_udpipe1_embeddings_morph_extended_full] +# Train UDPipe with embeddings +experiment_type = full_data +input_dir = edt_2.6/preannotated/morph_extended_no_metadata +train_file = edt_2.6/preannotated/morph_extended_no_metadata/et_edt-ud-train-morph_extended.conllu +parser_options = learning_rate=0.01;iterations=30 +create_embeddings_file = et_edt.skip.forms.50.vectors +output_dir = edt_2.6/trained/morph_extended/udpipe1_full_data +model_file = model_morph_extended_embed.udpipe + +[predict_udpipe1_default_morph_extended_full] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended_no_metadata/train_full.conllu +test_file = edt_2.6/preannotated/morph_extended_no_metadata/et_edt-ud-test-morph_extended.conllu +model_file = edt_2.6/trained/morph_extended/udpipe1_full_data/model_morph_extended_default.udpipe +output_file_prefix = predicted_udpipe1_default_ +output_dir = edt_2.6/evaluation/morph_extended/udpipe1_full_data + +[predict_udpipe1_embeddings_morph_extended_full] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended_no_metadata/train_full.conllu +test_file = edt_2.6/preannotated/morph_extended_no_metadata/et_edt-ud-test-morph_extended.conllu +model_file = edt_2.6/trained/morph_extended/udpipe1_full_data/model_morph_extended_embed.udpipe +output_file_prefix = predicted_udpipe1_embed_ +output_dir = edt_2.6/evaluation/morph_extended/udpipe1_full_data + +[eval_udpipe1_default_morph_extended] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended_no_metadata/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended_no_metadata/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended/udpipe1_full_data/predicted_udpipe1_default_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/udpipe1_full_data/predicted_udpipe1_default_test.conllu + +[eval_udpipe1_embeddings_morph_extended] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended_no_metadata/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended_no_metadata/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended/udpipe1_full_data/predicted_udpipe1_embed_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/udpipe1_full_data/predicted_udpipe1_embed_test.conllu diff --git a/confs/conf_edt_v26_stanza_MA_ensemble_full.ini b/confs/conf_edt_v26_stanza_MA_ensemble_full.ini new file mode 100644 index 00000000..d8f43aae --- /dev/null +++ b/confs/conf_edt_v26_stanza_MA_ensemble_full.ini @@ -0,0 +1,58 @@ +[preannotation_morph_analysis] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_analysis +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_analysis + +# Make train_full (for evaluation) +[join_morph_analysis_train_full] +input_dir = edt_2.6/preannotated/morph_analysis +concatenate = train, dev +output_dir = edt_2.6/preannotated/morph_analysis + +# we need crossvalidation data splits for ensemble models +[split_morph_analysis_crossvalidation] +input_dir = edt_2.6/preannotated/morph_analysis +concatenate = train, dev +split_type = crossvalidation +block_count = 195 +split_count = 10 +seed = 9 +first_output_dir = edt_2.6/preannotated/morph_analysis/crossvalidation_splits/initial_split +final_output_dir = edt_2.6/preannotated/morph_analysis/crossvalidation_splits + +# train 10 crossvalidation models +[train_stanza_morph_analysis_crossvalidation] +input_dir = edt_2.6/preannotated/morph_analysis/crossvalidation_splits +experiment_type = crossvalidation +predict_after = True +args = --batch_size 5000 +output_dir = edt_2.6/trained/morph_analysis/crossvalidation + +# predict using 10 models as an ensemble +[predict_stanza_ensemble_morph_analysis_default] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_analysis/train_full.conllu +test_file = edt_2.6/preannotated/morph_analysis/et_edt-ud-test-morph_analysis.conllu +models_dir = edt_2.6/trained/morph_analysis/crossvalidation +tagger_path = ensemble_tagger_with_entropy.StanzaSyntaxEnsembleTaggerWithEntropy +use_estnltk = True +use_ensemble = True +seed = 43 +scores_seed = 3 +use_gpu = True +morph_layer = morph_analysis +output_file_prefix = predicted_stanzaensembletagger_default_ +output_dir = edt_2.6/evaluation/morph_analysis/stanza_ensemble_full_data + +[eval_stanza_ensemble_morph_analysis_full_data_default] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_analysis/train_full.conllu +gold_test = edt_2.6/preannotated/morph_analysis/et_edt-ud-test-morph_analysis.conllu +predicted_train = edt_2.6/evaluation/morph_analysis/stanza_ensemble_full_data/predicted_stanzaensembletagger_default_train.conllu +predicted_test = edt_2.6/evaluation/morph_analysis/stanza_ensemble_full_data/predicted_stanzaensembletagger_default_test.conllu + diff --git a/confs/conf_edt_v26_stanza_MA_ensemble_majority_voting_full.ini b/confs/conf_edt_v26_stanza_MA_ensemble_majority_voting_full.ini new file mode 100644 index 00000000..7648932f --- /dev/null +++ b/confs/conf_edt_v26_stanza_MA_ensemble_majority_voting_full.ini @@ -0,0 +1,59 @@ +[preannotation_morph_analysis] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_analysis +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_analysis + +# Make train_full (for evaluation) +[join_morph_analysis_train_full] +input_dir = edt_2.6/preannotated/morph_analysis +concatenate = train, dev +output_dir = edt_2.6/preannotated/morph_analysis + +# we need crossvalidation data splits for ensemble models +[split_morph_analysis_crossvalidation] +input_dir = edt_2.6/preannotated/morph_analysis +concatenate = train, dev +split_type = crossvalidation +block_count = 195 +split_count = 10 +seed = 9 +first_output_dir = edt_2.6/preannotated/morph_analysis/crossvalidation_splits/initial_split +final_output_dir = edt_2.6/preannotated/morph_analysis/crossvalidation_splits + +# train 10 crossvalidation models +[train_stanza_morph_analysis_crossvalidation] +input_dir = edt_2.6/preannotated/morph_analysis/crossvalidation_splits +experiment_type = crossvalidation +predict_after = True +args = --batch_size 5000 +output_dir = edt_2.6/trained/morph_analysis/crossvalidation + +# predict using 10 models as an ensemble +[predict_stanza_ensemble_morph_analysis_majority_voting] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_analysis/train_full.conllu +test_file = edt_2.6/preannotated/morph_analysis/et_edt-ud-test-morph_analysis.conllu +models_dir = edt_2.6/trained/morph_analysis/crossvalidation +tagger_path = ensemble_tagger_with_entropy.StanzaSyntaxEnsembleTaggerWithEntropy +use_estnltk = True +use_ensemble = True +use_majority_voting = True +seed = 43 +scores_seed = 3 +use_gpu = True +morph_layer = morph_analysis +output_file_prefix = predicted_stanzaensembletagger_majority_voting_ +output_dir = edt_2.6/evaluation/morph_analysis/stanza_ensemble_full_data + +[eval_stanza_ensemble_morph_analysis_full_data_majority_voting] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_analysis/train_full.conllu +gold_test = edt_2.6/preannotated/morph_analysis/et_edt-ud-test-morph_analysis.conllu +predicted_train = edt_2.6/evaluation/morph_analysis/stanza_ensemble_full_data/predicted_stanzaensembletagger_majority_voting_train.conllu +predicted_test = edt_2.6/evaluation/morph_analysis/stanza_ensemble_full_data/predicted_stanzaensembletagger_majority_voting_test.conllu + diff --git a/confs/conf_edt_v26_stanza_ME_ensemble_full.ini b/confs/conf_edt_v26_stanza_ME_ensemble_full.ini new file mode 100644 index 00000000..7e25e43f --- /dev/null +++ b/confs/conf_edt_v26_stanza_ME_ensemble_full.ini @@ -0,0 +1,59 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +# Make train_full (for evaluation) +[join_morph_extended_train_full] +input_dir = edt_2.6/preannotated/morph_extended +concatenate = train, dev +output_dir = edt_2.6/preannotated/morph_extended + +# we need crossvalidation data splits for ensemble models +[split_morph_extended_crossvalidation] +input_dir = edt_2.6/preannotated/morph_extended +concatenate = train, dev +split_type = crossvalidation +block_count = 195 +split_count = 10 +seed = 9 +first_output_dir = edt_2.6/preannotated/morph_extended/crossvalidation_splits/initial_split +final_output_dir = edt_2.6/preannotated/morph_extended/crossvalidation_splits + +# train 10 crossvalidation models +[train_stanza_morph_extended_crossvalidation] +input_dir = edt_2.6/preannotated/morph_extended/crossvalidation_splits +experiment_type = crossvalidation +predict_after = True +args = --batch_size 5000 +output_dir = edt_2.6/trained/morph_extended/crossvalidation + +# predict using 10 models as an ensemble +[predict_stanza_ensemble_tagger_morph_extended_default] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended/train_full.conllu +test_file = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +models_dir = edt_2.6/trained/morph_extended/crossvalidation +#tagger_path = ensemble_tagger.StanzaSyntaxEnsembleTagger +tagger_path = ensemble_tagger_with_entropy.StanzaSyntaxEnsembleTaggerWithEntropy +use_estnltk = True +use_ensemble = True +seed = 43 +scores_seed = 3 +use_gpu = True +morph_layer = morph_extended +output_file_prefix = predicted_stanzaensembletagger_default_ +output_dir = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data + +[eval_stanza_ensemble_tagger_morph_extended_default_full_data] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data/predicted_stanzaensembletagger_default_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data/predicted_stanzaensembletagger_default_test.conllu + diff --git a/confs/conf_edt_v26_stanza_ME_ensemble_majority_voting_full.ini b/confs/conf_edt_v26_stanza_ME_ensemble_majority_voting_full.ini new file mode 100644 index 00000000..43dbf077 --- /dev/null +++ b/confs/conf_edt_v26_stanza_ME_ensemble_majority_voting_full.ini @@ -0,0 +1,60 @@ +[preannotation_morph_extended] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = morph_extended +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/morph_extended + +# Make train_full (for evaluation) +[join_morph_extended_train_full] +input_dir = edt_2.6/preannotated/morph_extended +concatenate = train, dev +output_dir = edt_2.6/preannotated/morph_extended + +# we need crossvalidation data splits for ensemble models +[split_morph_extended_crossvalidation] +input_dir = edt_2.6/preannotated/morph_extended +concatenate = train, dev +split_type = crossvalidation +block_count = 195 +split_count = 10 +seed = 9 +first_output_dir = edt_2.6/preannotated/morph_extended/crossvalidation_splits/initial_split +final_output_dir = edt_2.6/preannotated/morph_extended/crossvalidation_splits + +# train 10 crossvalidation models +[train_stanza_morph_extended_crossvalidation] +input_dir = edt_2.6/preannotated/morph_extended/crossvalidation_splits +experiment_type = crossvalidation +predict_after = True +args = --batch_size 5000 +output_dir = edt_2.6/trained/morph_extended/crossvalidation + +# predict using 10 models as an ensemble +[predict_stanza_ensemble_tagger_morph_extended_majority_voting] +experiment_type = full_data +train_file = edt_2.6/preannotated/morph_extended/train_full.conllu +test_file = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +models_dir = edt_2.6/trained/morph_extended/crossvalidation +#tagger_path = ensemble_tagger.StanzaSyntaxEnsembleTagger +tagger_path = ensemble_tagger_with_entropy.StanzaSyntaxEnsembleTaggerWithEntropy +use_estnltk = True +use_ensemble = True +use_majority_voting = True +seed = 43 +scores_seed = 3 +use_gpu = True +morph_layer = morph_extended +output_file_prefix = predicted_stanzaensembletagger_majority_voting_ +output_dir = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data + +[eval_stanza_morph_extended_full_data_ensemble_majority_voting] +experiment_type = full_data +gold_train = edt_2.6/preannotated/morph_extended/train_full.conllu +gold_test = edt_2.6/preannotated/morph_extended/et_edt-ud-test-morph_extended.conllu +predicted_train = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data/predicted_stanzaensembletagger_majority_voting_train.conllu +predicted_test = edt_2.6/evaluation/morph_extended/stanza_ensemble_full_data/predicted_stanzaensembletagger_majority_voting_test.conllu + diff --git a/confs/conf_edt_v26_stanza_auto_ud_morph.ini b/confs/conf_edt_v26_stanza_auto_ud_morph.ini new file mode 100644 index 00000000..5bb5de36 --- /dev/null +++ b/confs/conf_edt_v26_stanza_auto_ud_morph.ini @@ -0,0 +1,43 @@ +[preannotation_ud_morph_analysis] +input_dir = UD_Estonian-EDT-r2.6 +morph_layer = ud_morph_analysis +seed = 43 +dictionarize = True +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/ud_auto_morph + +[join_ud_auto_morph_train_full] +input_dir = edt_2.6/preannotated/ud_auto_morph +concatenate = train, dev +output_dir = edt_2.6/preannotated/ud_auto_morph + +[train_stanza_ud_auto_morph] +experiment_type = full_data +train_file = edt_2.6/preannotated/ud_auto_morph/et_edt-ud-train-ud_morph_analysis.conllu +eval_file = edt_2.6/preannotated/ud_auto_morph/et_edt-ud-dev-ud_morph_analysis.conllu +args = --batch_size 5000 +output_dir = edt_2.6/trained/ud_auto_morph/stanza_full_data +model_file = model_ud_morph_analysis.pt +predict_after = True +#dry_run = True + +[predict_stanza_ud_auto_morph_full] +experiment_type = full_data +train_file = edt_2.6/preannotated/ud_auto_morph/train_full.conllu +test_file = edt_2.6/preannotated/ud_auto_morph/et_edt-ud-test-ud_morph_analysis.conllu +model_file = edt_2.6/trained/ud_auto_morph/stanza_full_data/model_ud_morph_analysis.pt +output_dir = edt_2.6/evaluation/ud_auto_morph/stanza_full_data +#dry_run = True + +[eval_stanza_ud_auto_morph_full_data] +experiment_type = full_data +gold_train = edt_2.6/preannotated/ud_auto_morph/train_full.conllu +gold_test = edt_2.6/preannotated/ud_auto_morph/et_edt-ud-test-ud_morph_analysis.conllu +predicted_train = edt_2.6/evaluation/ud_auto_morph/stanza_full_data/predicted_train.conllu +predicted_test = edt_2.6/evaluation/ud_auto_morph/stanza_full_data/predicted_test.conllu + + + + diff --git a/confs/conf_edt_v26_stanza_gold_ud_morph.ini b/confs/conf_edt_v26_stanza_gold_ud_morph.ini new file mode 100644 index 00000000..395b8644 --- /dev/null +++ b/confs/conf_edt_v26_stanza_gold_ud_morph.ini @@ -0,0 +1,41 @@ +# train stanza on gold standard ud morph annotation +[copy_ud_morph] +input_dir = UD_Estonian-EDT-r2.6 +remove_empty_nodes = True +remove_deps = True +remove_misc = True +output_dir = edt_2.6/preannotated/ud_gold_morph + +[join_ud_morph_train_full] +input_dir = edt_2.6/preannotated/ud_gold_morph +concatenate = train, dev +output_dir = edt_2.6/preannotated/ud_gold_morph + +[train_stanza_ud_gold_morph] +experiment_type = full_data +train_file = edt_2.6/preannotated/ud_gold_morph/et_edt-ud-train.conllu +eval_file = edt_2.6/preannotated/ud_gold_morph/et_edt-ud-dev.conllu +args = --batch_size 5000 +output_dir = edt_2.6/trained/ud_gold_morph/stanza_full_data +model_file = model_ud.pt +predict_after = True +#dry_run = True + +[predict_stanza_ud_gold_morph_full] +experiment_type = full_data +train_file = edt_2.6/preannotated/ud_gold_morph/train_full.conllu +test_file = edt_2.6/preannotated/ud_gold_morph/et_edt-ud-test.conllu +model_file = edt_2.6/trained/ud_gold_morph/stanza_full_data/model_ud.pt +output_dir = edt_2.6/evaluation/ud_gold_morph/stanza_full_data +#dry_run = True + +[eval_stanza_ud_gold_morph_full_data] +experiment_type = full_data +gold_train = edt_2.6/preannotated/ud_gold_morph/train_full.conllu +gold_test = edt_2.6/preannotated/ud_gold_morph/et_edt-ud-test.conllu +predicted_train = edt_2.6/evaluation/ud_gold_morph/stanza_full_data/predicted_train.conllu +predicted_test = edt_2.6/evaluation/ud_gold_morph/stanza_full_data/predicted_test.conllu + + + + diff --git a/edt_2.11/results_stanza_basic.csv b/edt_2.11/results_stanza_basic.csv new file mode 100644 index 00000000..5f9ec231 --- /dev/null +++ b/edt_2.11/results_stanza_basic.csv @@ -0,0 +1,2 @@ +experiment,LAS_test,LAS_train,LAS_gap,UAS_test,UAS_train +eval_stanza_morph_extended_full_data,0.8484,0.9247,0.0764,0.8775,0.9430 diff --git a/edt_2.6/preannotated/morph_extended_clauses/top_50_sketches.csv b/edt_2.6/preannotated/morph_extended_clauses/top_50_sketches.csv new file mode 100644 index 00000000..3c78dc12 --- /dev/null +++ b/edt_2.6/preannotated/morph_extended_clauses/top_50_sketches.csv @@ -0,0 +1,51 @@ +,sketch,support +0,[V]nsubj(L),1918 +1,[S],1891 +2,[V]nsubj(L)obl(L),979 +3,[V],883 +4,[S]cop(L)nsubj:cop(L),853 +5,[V]nsubj(L)obj(L),760 +6,[V]advmod(L)nsubj(L),663 +7,[V]obj(L),594 +8,[V]obl(L),557 +9,[V]nsubj(L)obj(L)obl(L),534 +10,[V]obj(L)obl(L),489 +11,[S]advmod(L)cop(L)nsubj:cop(L),485 +12,[V]nsubj(L)obl(P),484 +13,[V]nsubj(P),477 +14,[V]advmod(L)nsubj(L)obl(L),476 +15,[S]nmod(L),423 +16,[V]nsubj(P)obl(L),418 +17,[V]nsubj(L)obj(P),396 +18,[V]advmod(L)nsubj(L)obj(L),366 +19,[S]cop(L)nsubj:cop(P),365 +20,[V]advmod(L),357 +21,[V]nsubj(L)obl(L)obl(L),338 +22,[X],311 +23,[V]nsubj(L)xcomp(P),305 +24,[V]obj(P),291 +25,[V]aux(L)nsubj(L),283 +26,[S]cop(L)nsubj:cop(L)obl(L),267 +27,[S]nummod(L),264 +28,[V]nsubj(L)xcomp(L),258 +29,[V]nsubj(L)obl(L)obl(P),248 +30,[V]advmod(L)obj(L),242 +31,[V]nsubj(P)obj(L),236 +32,[V]aux(L)nsubj(L)obj(L),234 +33,[V]xcomp(P),217 +34,[S]amod(L)cop(L)nsubj:cop(L),213 +35,[V]nsubj(P)obl(P),212 +36,[V]obl(P),207 +37,[V]aux(L)nsubj(L)obl(L),206 +38,[V]advmod(L)obl(L),203 +39,[S]amod(L),203 +40,[V]obj(P)obl(L),195 +41,[V]nsubj(L)obj(P)obl(L),189 +42,[S]cop(L)nmod(L)nsubj:cop(L),188 +43,[V]nsubj(L)obj(L)obl(P),187 +44,[V]advmod(L)nsubj(L)obj(L)obl(L),185 +45,[V]advmod(L)nsubj(L)obl(P),184 +46,[S]advmod(L),177 +47,[V]obj(L)obl(P),177 +48,[V]advmod(L)nsubj(P),173 +49,[V]aux(L),173 diff --git a/edt_2.6/preannotated/morph_extended_clauses/top_50_sketches_5_groups.csv b/edt_2.6/preannotated/morph_extended_clauses/top_50_sketches_5_groups.csv new file mode 100644 index 00000000..3845a1b2 --- /dev/null +++ b/edt_2.6/preannotated/morph_extended_clauses/top_50_sketches_5_groups.csv @@ -0,0 +1,6 @@ +,grouped_sketches,support +0,[V]nsubj(L)obl(L);[V]nsubj(L)obl(L)obl(P);[S]advmod(L)cop(L)nsubj:cop(L);[S]cop(L)nsubj:cop(P);[V]aux(L)nsubj(L)obl(L);[V]obj(L)obl(L);[V]nsubj(L)xcomp(P);[V]obj(P)obl(L);[V]obj(P);[V]obj(L),4157 +1,[S]nmod(L);[V]advmod(L)nsubj(P);[V]aux(L)nsubj(L)obj(L);[S]amod(L);[V]advmod(L);[V];[V]nsubj(L)obj(P);[S];[V]nsubj(L)obj(P)obl(L);[V]nsubj(P)obl(L),5167 +2,[V]obj(L)obl(P);[V]nsubj(L)obj(L);[S]amod(L)cop(L)nsubj:cop(L);[V]advmod(L)obl(L);[S]cop(L)nmod(L)nsubj:cop(L);[V]advmod(L)nsubj(L)obj(L);[V]nsubj(L)obj(L)obl(P);[V]advmod(L)nsubj(L)obj(L)obl(L);[V]advmod(L)obj(L);[S]cop(L)nsubj:cop(L),3374 +3,[V]nsubj(L)obj(L)obl(L);[S]advmod(L);[S]cop(L)nsubj:cop(L)obl(L);[V]nsubj(L)obl(P);[V]nsubj(P)obl(P);[V]advmod(L)nsubj(L)obl(P);[V]nsubj(L);[V]advmod(L)nsubj(L);[V]nsubj(L)xcomp(L);[V]obl(P),4904 +4,[V]nsubj(L)obl(L)obl(L);[V]advmod(L)nsubj(L)obl(L);[V]aux(L);[X];[V]xcomp(P);[V]aux(L)nsubj(L);[S]nummod(L);[V]nsubj(P);[V]nsubj(P)obj(L);[V]obl(L),3332 diff --git a/edt_2.6/preannotated/morph_extended_clauses/top_50_sketches_5_groups_preparation_log.txt b/edt_2.6/preannotated/morph_extended_clauses/top_50_sketches_5_groups_preparation_log.txt new file mode 100644 index 00000000..6c0f49d1 --- /dev/null +++ b/edt_2.6/preannotated/morph_extended_clauses/top_50_sketches_5_groups_preparation_log.txt @@ -0,0 +1,293 @@ +Performing make_sketches_table_morph_extended_top_50 ... +et_edt-ud-dev-morph_extended.conllu | #clauses: 5708 +et_edt-ud-test-morph_extended.conllu | #clauses: 6033 +et_edt-ud-train-morph_extended.conllu | #clauses: 43966 + +#clauses total: 55707 +Writing sketches freq table into edt_2.6/preannotated/morph_extended_clauses/top_50_sketches.csv. +Performing prepare_knockout_morph_extended_top_50_sketches_5randomgroups ... +et_edt-ud-dev-morph_extended.conllu | #clauses: 5708 +et_edt-ud-test-morph_extended.conllu | #clauses: 6033 +et_edt-ud-train-morph_extended.conllu | #clauses: 43966 +[V]nsubj(L) + extracted 193 clauses from test +[S] + extracted 247 clauses from test +[V]nsubj(L)obl(L) + extracted 88 clauses from test +[V] + extracted 102 clauses from test +[S]cop(L)nsubj:cop(L) + extracted 81 clauses from test +[V]nsubj(L)obj(L) + extracted 62 clauses from test +[V]advmod(L)nsubj(L) + extracted 65 clauses from test +[V]obj(L) + extracted 65 clauses from test +[V]obl(L) + extracted 51 clauses from test +[V]nsubj(L)obj(L)obl(L) + extracted 44 clauses from test + extracted 6 clauses from train +[V]obj(L)obl(L) + extracted 49 clauses from test + extracted 1 clauses from train +[S]advmod(L)cop(L)nsubj:cop(L) + extracted 33 clauses from test + extracted 17 clauses from train +[V]nsubj(L)obl(P) + extracted 35 clauses from test + extracted 15 clauses from train +[V]nsubj(P) + extracted 52 clauses from test +[V]advmod(L)nsubj(L)obl(L) + extracted 51 clauses from test +[S]nmod(L) + extracted 36 clauses from test + extracted 14 clauses from train +[V]nsubj(P)obl(L) + extracted 43 clauses from test + extracted 7 clauses from train +[V]nsubj(L)obj(P) + extracted 44 clauses from test + extracted 6 clauses from train +[V]advmod(L)nsubj(L)obj(L) + extracted 39 clauses from test + extracted 11 clauses from train +[S]cop(L)nsubj:cop(P) + extracted 42 clauses from test + extracted 8 clauses from train +[V]advmod(L) + extracted 39 clauses from test + extracted 11 clauses from train +[V]nsubj(L)obl(L)obl(L) + extracted 36 clauses from test + extracted 14 clauses from train +[X] + extracted 50 clauses from test +[V]nsubj(L)xcomp(P) + extracted 22 clauses from test + extracted 28 clauses from train +[V]obj(P) + extracted 28 clauses from test + extracted 22 clauses from train +[V]aux(L)nsubj(L) + extracted 25 clauses from test + extracted 25 clauses from train +[S]cop(L)nsubj:cop(L)obl(L) + extracted 39 clauses from test + extracted 11 clauses from train +[S]nummod(L) + extracted 24 clauses from test + extracted 26 clauses from train +[V]nsubj(L)xcomp(L) + extracted 32 clauses from test + extracted 18 clauses from train +[V]nsubj(L)obl(L)obl(P) + extracted 23 clauses from test + extracted 27 clauses from train +[V]advmod(L)obj(L) + extracted 24 clauses from test + extracted 26 clauses from train +[V]nsubj(P)obj(L) + extracted 28 clauses from test + extracted 22 clauses from train +[V]aux(L)nsubj(L)obj(L) + extracted 25 clauses from test + extracted 25 clauses from train +[V]xcomp(P) + extracted 17 clauses from test + extracted 33 clauses from train +[S]amod(L)cop(L)nsubj:cop(L) + extracted 29 clauses from test + extracted 21 clauses from train +[V]nsubj(P)obl(P) + extracted 12 clauses from test + extracted 38 clauses from train +[V]obl(P) + extracted 12 clauses from test + extracted 38 clauses from train +[V]aux(L)nsubj(L)obl(L) + extracted 18 clauses from test + extracted 32 clauses from train +[S]amod(L) + extracted 21 clauses from test + extracted 29 clauses from train +[V]advmod(L)obl(L) + extracted 15 clauses from test + extracted 35 clauses from train +[V]obj(P)obl(L) + extracted 22 clauses from test + extracted 28 clauses from train +[V]nsubj(L)obj(P)obl(L) + extracted 19 clauses from test + extracted 31 clauses from train +[S]cop(L)nmod(L)nsubj:cop(L) + extracted 21 clauses from test + extracted 29 clauses from train +[V]nsubj(L)obj(L)obl(P) + extracted 18 clauses from test + extracted 32 clauses from train +[V]advmod(L)nsubj(L)obj(L)obl(L) + extracted 19 clauses from test + extracted 31 clauses from train +[V]advmod(L)nsubj(L)obl(P) + extracted 18 clauses from test + extracted 32 clauses from train +[V]obj(L)obl(P) + extracted 16 clauses from test + extracted 34 clauses from train +[S]advmod(L) + extracted 23 clauses from test + extracted 27 clauses from train +[V]advmod(L)nsubj(P) + extracted 15 clauses from test + extracted 35 clauses from train +[V]aux(L) + extracted 19 clauses from test + extracted 31 clauses from train + Saved 3007 test clauses into edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_initial\test_50x50.conllu. + Saved 43087 train clauses into edt_2.6/preannotated/morph_extended_clauses/knockout_5groups_initial\train_pure.conllu. + +group: 0 +[V]nsubj(L)obl(L) +[V]obj(L) +[V]obj(L)obl(L) +[S]advmod(L)cop(L)nsubj:cop(L) +[S]cop(L)nsubj:cop(P) +[V]nsubj(L)xcomp(P) +[V]obj(P) +[V]nsubj(L)obl(L)obl(P) +[V]aux(L)nsubj(L)obl(L) +[V]obj(P)obl(L) + Saved 553 test clauses into test_group0.conllu. + Removed 403 clauses and saved remaining 5305 dev clauses into dev_group0.conllu. + Removed 3201 clauses and saved remaining 39886 train clauses into train_group0.conllu. +group: 1 +[S] +[V] +[S]nmod(L) +[V]nsubj(P)obl(L) +[V]nsubj(L)obj(P) +[V]advmod(L) +[V]aux(L)nsubj(L)obj(L) +[S]amod(L) +[V]nsubj(L)obj(P)obl(L) +[V]advmod(L)nsubj(P) + Saved 749 test clauses into test_group1.conllu. + Removed 599 clauses and saved remaining 5109 dev clauses into dev_group1.conllu. + Removed 3819 clauses and saved remaining 39268 train clauses into train_group1.conllu. +group: 2 +[S]cop(L)nsubj:cop(L) +[V]nsubj(L)obj(L) +[V]advmod(L)nsubj(L)obj(L) +[V]advmod(L)obj(L) +[S]amod(L)cop(L)nsubj:cop(L) +[V]advmod(L)obl(L) +[S]cop(L)nmod(L)nsubj:cop(L) +[V]nsubj(L)obj(L)obl(P) +[V]advmod(L)nsubj(L)obj(L)obl(L) +[V]obj(L)obl(P) + Saved 543 test clauses into test_group2.conllu. + Removed 340 clauses and saved remaining 5368 dev clauses into dev_group2.conllu. + Removed 2491 clauses and saved remaining 40596 train clauses into train_group2.conllu. +group: 3 +[V]nsubj(L) +[V]advmod(L)nsubj(L) +[V]nsubj(L)obj(L)obl(L) +[V]nsubj(L)obl(P) +[S]cop(L)nsubj:cop(L)obl(L) +[V]nsubj(L)xcomp(L) +[V]nsubj(P)obl(P) +[V]obl(P) +[V]advmod(L)nsubj(L)obl(P) +[S]advmod(L) + Saved 658 test clauses into test_group3.conllu. + Removed 477 clauses and saved remaining 5231 dev clauses into dev_group3.conllu. + Removed 3769 clauses and saved remaining 39318 train clauses into train_group3.conllu. +group: 4 +[V]obl(L) +[V]nsubj(P) +[V]advmod(L)nsubj(L)obl(L) +[V]nsubj(L)obl(L)obl(L) +[X] +[V]aux(L)nsubj(L) +[S]nummod(L) +[V]nsubj(P)obj(L) +[V]xcomp(P) +[V]aux(L) + Saved 504 test clauses into test_group4.conllu. + Removed 358 clauses and saved remaining 5350 dev clauses into dev_group4.conllu. + Removed 2467 clauses and saved remaining 40620 train clauses into train_group4.conllu. + +group: 0 +[V]nsubj(L)obl(L) +[V]obj(L) +[V]obj(L)obl(L) +[S]advmod(L)cop(L)nsubj:cop(L) +[S]cop(L)nsubj:cop(P) +[V]nsubj(L)xcomp(P) +[V]obj(P) +[V]nsubj(L)obl(L)obl(P) +[V]aux(L)nsubj(L)obl(L) +[V]obj(P)obl(L) + Saved 553 test clauses into test_group0.conllu. + Removed 403 clauses randomly and saved remaining 5305 dev clauses into dev_group0.conllu. + Removed 3201 clauses randomly and saved remaining 39886 train clauses into train_group0.conllu. +group: 1 +[S] +[V] +[S]nmod(L) +[V]nsubj(P)obl(L) +[V]nsubj(L)obj(P) +[V]advmod(L) +[V]aux(L)nsubj(L)obj(L) +[S]amod(L) +[V]nsubj(L)obj(P)obl(L) +[V]advmod(L)nsubj(P) + Saved 749 test clauses into test_group1.conllu. + Removed 599 clauses randomly and saved remaining 5109 dev clauses into dev_group1.conllu. + Removed 3819 clauses randomly and saved remaining 39268 train clauses into train_group1.conllu. +group: 2 +[S]cop(L)nsubj:cop(L) +[V]nsubj(L)obj(L) +[V]advmod(L)nsubj(L)obj(L) +[V]advmod(L)obj(L) +[S]amod(L)cop(L)nsubj:cop(L) +[V]advmod(L)obl(L) +[S]cop(L)nmod(L)nsubj:cop(L) +[V]nsubj(L)obj(L)obl(P) +[V]advmod(L)nsubj(L)obj(L)obl(L) +[V]obj(L)obl(P) + Saved 543 test clauses into test_group2.conllu. + Removed 340 clauses randomly and saved remaining 5368 dev clauses into dev_group2.conllu. + Removed 2491 clauses randomly and saved remaining 40596 train clauses into train_group2.conllu. +group: 3 +[V]nsubj(L) +[V]advmod(L)nsubj(L) +[V]nsubj(L)obj(L)obl(L) +[V]nsubj(L)obl(P) +[S]cop(L)nsubj:cop(L)obl(L) +[V]nsubj(L)xcomp(L) +[V]nsubj(P)obl(P) +[V]obl(P) +[V]advmod(L)nsubj(L)obl(P) +[S]advmod(L) + Saved 658 test clauses into test_group3.conllu. + Removed 477 clauses randomly and saved remaining 5231 dev clauses into dev_group3.conllu. + Removed 3769 clauses randomly and saved remaining 39318 train clauses into train_group3.conllu. +group: 4 +[V]obl(L) +[V]nsubj(P) +[V]advmod(L)nsubj(L)obl(L) +[V]nsubj(L)obl(L)obl(L) +[X] +[V]aux(L)nsubj(L) +[S]nummod(L) +[V]nsubj(P)obj(L) +[V]xcomp(P) +[V]aux(L) + Saved 504 test clauses into test_group4.conllu. + Removed 358 clauses randomly and saved remaining 5350 dev clauses into dev_group4.conllu. + Removed 2467 clauses randomly and saved remaining 40620 train clauses into train_group4.conllu. \ No newline at end of file diff --git a/edt_2.6/results_crossvalidation.csv b/edt_2.6/results_crossvalidation.csv new file mode 100644 index 00000000..3c9db58f --- /dev/null +++ b/edt_2.6/results_crossvalidation.csv @@ -0,0 +1,13 @@ +experiment,LAS_test,LAS_train,LAS_gap,UAS_test,UAS_train +eval_stanza_morph_extended_crossvalidation_01,0.8485,0.9292,0.0807,0.8778,0.9476 +eval_stanza_morph_extended_crossvalidation_02,0.8480,0.9217,0.0737,0.8767,0.9412 +eval_stanza_morph_extended_crossvalidation_03,0.8460,0.9274,0.0813,0.8760,0.9463 +eval_stanza_morph_extended_crossvalidation_04,0.8468,0.9263,0.0795,0.8763,0.9452 +eval_stanza_morph_extended_crossvalidation_05,0.8514,0.9308,0.0793,0.8797,0.9484 +eval_stanza_morph_extended_crossvalidation_06,0.8473,0.9203,0.0730,0.8772,0.9405 +eval_stanza_morph_extended_crossvalidation_07,0.8465,0.9201,0.0735,0.8759,0.9402 +eval_stanza_morph_extended_crossvalidation_08,0.8482,0.9297,0.0815,0.8779,0.9479 +eval_stanza_morph_extended_crossvalidation_09,0.8482,0.9241,0.0758,0.8782,0.9430 +eval_stanza_morph_extended_crossvalidation_10,0.8503,0.9322,0.0819,0.8800,0.9499 +eval_stanza_morph_extended_crossvalidation_AVG,0.8481,0.9262,0.0780,0.8776,0.9450 +eval_stanza_ensemble_tagger_morph_extended_default_full_data,0.8568,0.9337,0.0769,0.8851,0.9515 diff --git a/edt_2.6/results_ensemble_conf_intervals.csv b/edt_2.6/results_ensemble_conf_intervals.csv new file mode 100644 index 00000000..5272bb8e --- /dev/null +++ b/edt_2.6/results_ensemble_conf_intervals.csv @@ -0,0 +1,6 @@ +experiment,LAS_test,LAS_train,UAS_test,UAS_train,LAS_gap +eval_stanza_morph_extended_full_data_conf_intervals,0.8401; 0.8486; 0.8572,0.9161; 0.9176; 0.9191,0.8697; 0.8782; 0.8867,0.9363; 0.9378; 0.9393,0.0760; 0.0689; 0.0619 +eval_stanza_ensemble_morph_extended_full_data_las_coherence_conf_intervals,0.8488; 0.8568; 0.8649,0.9327; 0.9337; 0.9347,0.8774; 0.8852; 0.8929,0.9505; 0.9515; 0.9525,0.0839; 0.0769; 0.0698 +eval_stanza_ensemble_morph_extended_full_data_majority_voting_conf_intervals,0.8487; 0.8564; 0.8641,0.9327; 0.9337; 0.9347,0.8775; 0.8849; 0.8924,0.9507; 0.9517; 0.9527,0.0840; 0.0773; 0.0707 +eval_stanza_ensemble_morph_extended_half_data_las_coherence_conf_intervals,0.8383; 0.8447; 0.8510,0.9134; 0.9152; 0.9169,0.8699; 0.8761; 0.8822,0.9325; 0.9344; 0.9363,0.0751; 0.0705; 0.0659 +eval_stanza_ensemble_morph_extended_half_data_majority_voting_conf_intervals,0.8386; 0.8449; 0.8512,0.9109; 0.9126; 0.9143,0.8699; 0.8762; 0.8826,0.9306; 0.9325; 0.9344,0.0722; 0.0677; 0.0631 diff --git a/edt_2.6/results_ensemble_entropy.csv b/edt_2.6/results_ensemble_entropy.csv new file mode 100644 index 00000000..bdb16004 --- /dev/null +++ b/edt_2.6/results_ensemble_entropy.csv @@ -0,0 +1,5 @@ +experiment,LAS_test,LAS_train,UAS_test,UAS_train,LAS_gap,test_LAS_0_entropy,test_LAS_0_entropy_matches,test_LAS_0_entropy_total_words,test_LAS_0_entropy_total_words_%,test_LAS_vs_entropy_corr,test_LAS_vs_entropy_corr_pvalue,train_LAS_0_entropy,train_LAS_0_entropy_matches,train_LAS_0_entropy_total_words,train_LAS_vs_entropy_corr,train_LAS_vs_entropy_corr_pvalue,train_LAS_0_entropy_total_words_%,test_words,train_words +eval_stanza_ensemble_morph_extended_full_data_las_coherence_entropy,0.8568,0.9337,0.8851,0.9515,0.0769,0.9256,36904,39870,82.2214,0.4431,0.0000,0.9699,322359,332368,0.3948,0.0000,85.3806,48491,389278 +eval_stanza_ensemble_morph_extended_full_data_majority_voting_entropy,0.8564,0.9337,0.8849,0.9517,0.0773,0.9256,36904,39870,82.2214,0.4465,0.0000,0.9699,322359,332368,0.3963,0.0000,85.3806,48491,389278 +eval_stanza_ensemble_morph_extended_half_data_las_coherence_entropy,0.8446,0.9151,0.8760,0.9344,0.0705,0.9405,33555,35679,73.5786,0.4760,0.0000,0.9831,286232,291164,0.4616,0.0000,74.7959,48491,389278 +eval_stanza_ensemble_morph_extended_half_data_majority_voting_entropy,0.8449,0.9126,0.8762,0.9325,0.0677,0.9405,33555,35679,73.5786,0.4777,0.0000,0.9831,286232,291164,0.4743,0.0000,74.7959,48491,389278 diff --git a/edt_2.6/results_ensemble_majority_voting.csv b/edt_2.6/results_ensemble_majority_voting.csv new file mode 100644 index 00000000..534458fb --- /dev/null +++ b/edt_2.6/results_ensemble_majority_voting.csv @@ -0,0 +1,3 @@ +experiment,LAS_test,LAS_train,UAS_test,UAS_train,LAS_gap +eval_stanza_morph_extended_full_data_ensemble_majority_voting,0.8564,0.9337,0.8849,0.9517,0.0773 +eval_stanza_morph_extended_half_data_ensemble_majority_voting,0.8449,0.9126,0.8762,0.9325,0.0677 diff --git a/edt_2.6/results_full_data_malt_udpipe1.csv b/edt_2.6/results_full_data_malt_udpipe1.csv new file mode 100644 index 00000000..736bb26f --- /dev/null +++ b/edt_2.6/results_full_data_malt_udpipe1.csv @@ -0,0 +1,4 @@ +experiment,LAS_test,LAS_train,LAS_gap,UAS_test,UAS_train +eval_malt_morph_extended_full,0.7186,0.8446,0.1260,0.7681,0.8715 +eval_udpipe1_default_morph_extended,0.7560,0.8501,0.0941,0.8009,0.8811 +eval_udpipe1_embeddings_morph_extended,0.7713,0.9147,0.1434,0.8135,0.9330 diff --git a/edt_2.6/results_gap_experiments.csv b/edt_2.6/results_gap_experiments.csv new file mode 100644 index 00000000..6fa08606 --- /dev/null +++ b/edt_2.6/results_gap_experiments.csv @@ -0,0 +1,11 @@ +experiment,LAS_test,LAS_train,LAS_gap,UAS_test,UAS_train +eval_stanza_morph_extended_gap_experiments_01_no_wordforms,0.8396,0.9236,0.0840,0.8734,0.9461 +eval_stanza_morph_extended_gap_experiments_02_no_lemmas,0.8483,0.9308,0.0825,0.8789,0.9496 +eval_stanza_morph_extended_gap_experiments_02_no_pos,0.8492,0.9284,0.0792,0.8781,0.9471 +eval_stanza_morph_extended_gap_experiments_03_no_wordforms_adj_noun_lemmas,0.8200,0.9109,0.0909,0.8610,0.9396 +eval_stanza_morph_extended_gap_experiments_04_no_wordforms_verb_adpos_lemmas,0.7822,0.9106,0.1285,0.8181,0.9370 +eval_stanza_morph_extended_gap_experiments_05_only_cg_list_wordforms_lemmas,0.8158,0.9153,0.0995,0.8572,0.9433 +eval_stanza_morph_extended_gap_experiments_06_no_wordform_lemma_pos_keep_conj,0.6869,0.8100,0.1230,0.7572,0.8701 +eval_stanza_morph_extended_gap_experiments_07_no_wordform_lemma_pos,0.6723,0.7648,0.0925,0.7532,0.8351 +eval_stanza_morph_extended_gap_experiments_08_only_wordforms,0.8407,0.9202,0.0795,0.8742,0.9391 +eval_stanza_morph_extended_gap_experiments_09_only_pos_feats,0.7018,0.8213,0.1195,0.7709,0.8799 diff --git a/edt_2.6/results_gold_and_auto_ud_morph.csv b/edt_2.6/results_gold_and_auto_ud_morph.csv new file mode 100644 index 00000000..97754cbe --- /dev/null +++ b/edt_2.6/results_gold_and_auto_ud_morph.csv @@ -0,0 +1,3 @@ +experiment,LAS_test,LAS_train,LAS_gap,UAS_test,UAS_train +eval_stanza_ud_auto_morph_full_data,0.8523,0.9333,0.0810,0.8808,0.9508 +eval_stanza_ud_gold_morph_full_data,0.8810,0.9380,0.0570,0.8987,0.9502 diff --git a/edt_2.6/results_half_data.csv b/edt_2.6/results_half_data.csv new file mode 100644 index 00000000..fb467fc4 --- /dev/null +++ b/edt_2.6/results_half_data.csv @@ -0,0 +1,13 @@ +experiment,LAS_test,LAS_train,LAS_gap,UAS_test,UAS_train +eval_stanza_morph_extended_half_data_001,0.8295,0.9448,0.1153,0.8625,0.9564 +eval_stanza_morph_extended_half_data_002,0.8278,0.9203,0.0925,0.8607,0.9392 +eval_stanza_morph_extended_half_data_003,0.8276,0.9453,0.1177,0.8602,0.9568 +eval_stanza_morph_extended_half_data_004,0.8265,0.9384,0.1119,0.8580,0.9521 +eval_stanza_morph_extended_half_data_005,0.8264,0.9289,0.1024,0.8601,0.9443 +eval_stanza_morph_extended_half_data_006,0.8248,0.9255,0.1007,0.8586,0.9425 +eval_stanza_morph_extended_half_data_007,0.8275,0.9271,0.0996,0.8602,0.9438 +eval_stanza_morph_extended_half_data_008,0.8287,0.9353,0.1065,0.8617,0.9495 +eval_stanza_morph_extended_half_data_009,0.8304,0.9368,0.1065,0.8627,0.9504 +eval_stanza_morph_extended_half_data_010,0.8252,0.9275,0.1023,0.8579,0.9445 +eval_stanza_morph_extended_half_data_AVG,0.8274,0.9330,0.1055,0.8603,0.9480 +eval_stanza_morph_extended_half_data_ensemble,0.8446,0.9151,0.0705,0.8760,0.9344 diff --git a/edt_2.6/results_smaller_data.csv b/edt_2.6/results_smaller_data.csv new file mode 100644 index 00000000..f065157e --- /dev/null +++ b/edt_2.6/results_smaller_data.csv @@ -0,0 +1,31 @@ +experiment,LAS_test,LAS_train,LAS_gap,UAS_test,UAS_train,test_words,train_words +eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_010,0.6864,0.9491,0.2628,0.7605,0.9591,48491,39886 +eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_020,0.7490,0.9636,0.2146,0.8004,0.9700,48491,77759 +eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_030,0.7819,0.9365,0.1546,0.8271,0.9489,48491,117642 +eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_040,0.7989,0.9366,0.1377,0.8388,0.9502,48491,157491 +eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_050,0.8152,0.9234,0.1082,0.8543,0.9406,48491,195477 +eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_060,0.8250,0.9299,0.1049,0.8595,0.9458,48491,235468 +eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_070,0.8241,0.9179,0.0938,0.8600,0.9370,48491,273422 +eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_080,0.8327,0.9194,0.0867,0.8674,0.9383,48491,311383 +eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_090,0.8343,0.9151,0.0808,0.8680,0.9362,48491,349322 +eval_stanza_morph_extended_smaller_data_01_08_only_wordforms_100,0.8377,0.9104,0.0728,0.8723,0.9320,48491,389278 +eval_stanza_morph_extended_smaller_data_02_keep_all_010,0.7509,0.9286,0.1778,0.7995,0.9402,48491,39886 +eval_stanza_morph_extended_smaller_data_02_keep_all_020,0.7895,0.9552,0.1657,0.8297,0.9630,48491,77759 +eval_stanza_morph_extended_smaller_data_02_keep_all_030,0.8062,0.9268,0.1207,0.8439,0.9440,48491,117642 +eval_stanza_morph_extended_smaller_data_02_keep_all_040,0.8209,0.9414,0.1205,0.8545,0.9542,48491,157491 +eval_stanza_morph_extended_smaller_data_02_keep_all_050,0.8299,0.9360,0.1060,0.8616,0.9505,48491,195477 +eval_stanza_morph_extended_smaller_data_02_keep_all_060,0.8409,0.9350,0.0941,0.8723,0.9505,48491,235468 +eval_stanza_morph_extended_smaller_data_02_keep_all_070,0.8363,0.9248,0.0884,0.8675,0.9428,48491,273422 +eval_stanza_morph_extended_smaller_data_02_keep_all_080,0.8435,0.9295,0.0860,0.8739,0.9463,48491,311383 +eval_stanza_morph_extended_smaller_data_02_keep_all_090,0.8463,0.9261,0.0798,0.8756,0.9437,48491,349322 +eval_stanza_morph_extended_smaller_data_02_keep_all_100,0.8466,0.9236,0.0771,0.8763,0.9422,48491,389278 +eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_010,0.6285,0.8174,0.1889,0.7121,0.8568,48491,39886 +eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_020,0.6544,0.8542,0.1997,0.7336,0.8912,48491,77759 +eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_030,0.6652,0.7847,0.1195,0.7412,0.8445,48491,117642 +eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_040,0.6730,0.7889,0.1159,0.7479,0.8471,48491,157491 +eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_050,0.6802,0.7878,0.1076,0.7536,0.8479,48491,195477 +eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_060,0.6880,0.8124,0.1244,0.7595,0.8668,48491,235468 +eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_070,0.6915,0.8000,0.1085,0.7638,0.8594,48491,273422 +eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_080,0.6939,0.8001,0.1061,0.7653,0.8575,48491,311383 +eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_090,0.6952,0.8081,0.1128,0.7658,0.8664,48491,349322 +eval_stanza_morph_extended_smaller_data_03_only_pos_feats_09_only_pos_feats_100,0.6970,0.7874,0.0904,0.7670,0.8489,48491,389278 diff --git a/edt_2.6/results_stanza_MA_ensembles.csv b/edt_2.6/results_stanza_MA_ensembles.csv new file mode 100644 index 00000000..c0502833 --- /dev/null +++ b/edt_2.6/results_stanza_MA_ensembles.csv @@ -0,0 +1,3 @@ +experiment,LAS_test,LAS_train,UAS_test,UAS_train,LAS_gap +eval_stanza_ensemble_morph_analysis_full_data_default,0.8573,0.9330,0.8851,0.9510,0.0757 +eval_stanza_ensemble_morph_analysis_full_data_majority_voting,0.8573,0.9329,0.8858,0.9510,0.0756 diff --git a/edt_2.6/results_stanza_ME_conf_intervals.csv b/edt_2.6/results_stanza_ME_conf_intervals.csv new file mode 100644 index 00000000..686185a6 --- /dev/null +++ b/edt_2.6/results_stanza_ME_conf_intervals.csv @@ -0,0 +1,2 @@ +experiment,LAS_test,LAS_train,UAS_test,UAS_train,LAS_gap,test_words,train_words +eval_stanza_morph_extended_full_data_conf_intervals,0.8401; 0.8486; 0.8572,0.9161; 0.9176; 0.9191,0.8697; 0.8782; 0.8867,0.9363; 0.9378; 0.9393,0.0760; 0.0689; 0.0619,48491,389278 diff --git a/edt_2.6/results_stanza_ME_error_types.csv b/edt_2.6/results_stanza_ME_error_types.csv new file mode 100644 index 00000000..afab0e39 --- /dev/null +++ b/edt_2.6/results_stanza_ME_error_types.csv @@ -0,0 +1,2 @@ +experiment,E1,E2,E3,E1_impact,E2_impact,E3_impact,E1_rel_error,E2_rel_error,E3_rel_error,total_no_punct,correct,gold_in_clause,gold_out_of_clause,total_words,punct,unequal_length,E1_missed_root,E2_missed_root,E3_missed_root,E2_with_E3,E2_without_E3 +stanza_ME_error_types_on_test,4821,398,1162,0.7555,0.0624,0.1821,0.1413,0.0117,0.1733,40815,34434,34108,6707,48491,7676,0,0,0,254,360,38 diff --git a/edt_2.6/results_stanza_ME_full_predict_on_clauses.csv b/edt_2.6/results_stanza_ME_full_predict_on_clauses.csv new file mode 100644 index 00000000..4459222e --- /dev/null +++ b/edt_2.6/results_stanza_ME_full_predict_on_clauses.csv @@ -0,0 +1,2 @@ +experiment,LAS_test,LAS_train,UAS_test,UAS_train,LAS_gap +eval_stanza_morph_extended_full_predict_on_clauses,0.8544,0.9076,0.8873,0.9341,0.0532 diff --git a/edt_2.6/results_stanza_ME_full_predict_on_clauses_error_types.csv b/edt_2.6/results_stanza_ME_full_predict_on_clauses_error_types.csv new file mode 100644 index 00000000..b8a28663 --- /dev/null +++ b/edt_2.6/results_stanza_ME_full_predict_on_clauses_error_types.csv @@ -0,0 +1,2 @@ +experiment,E1,E2,E3,E1_impact,E2_impact,E3_impact,E1_rel_error,E2_rel_error,E3_rel_error,total_no_punct,correct,gold_in_clause,gold_out_of_clause,total_words,punct,unequal_length,E1_missed_root,E2_missed_root,E3_missed_root +stanza_ME_full_on_clauses_error_types_on_test,4774,352,355,0.8710,0.0642,0.0648,0.1490,0.0110,0.0585,38101,32620,32034,6067,39128,1027,0,0,0,352 diff --git a/edt_2.6/results_stanza_ME_on_clauses.csv b/edt_2.6/results_stanza_ME_on_clauses.csv new file mode 100644 index 00000000..d756dba3 --- /dev/null +++ b/edt_2.6/results_stanza_ME_on_clauses.csv @@ -0,0 +1,2 @@ +experiment,LAS_test,LAS_train,LAS_gap,UAS_test,UAS_train +eval_stanza_morph_extended_on_clauses,0.8623,0.9298,0.0674,0.8924,0.9492 diff --git a/edt_2.6/results_stanza_ME_on_clauses_error_types.csv b/edt_2.6/results_stanza_ME_on_clauses_error_types.csv new file mode 100644 index 00000000..a93d2914 --- /dev/null +++ b/edt_2.6/results_stanza_ME_on_clauses_error_types.csv @@ -0,0 +1,2 @@ +experiment,E1,E2,E3,E1_impact,E2_impact,E3_impact,E1_rel_error,E2_rel_error,E3_rel_error,total_no_punct,correct,gold_in_clause,gold_out_of_clause,total_words,punct,unequal_length,E1_missed_root,E2_missed_root,E3_missed_root +stanza_ME_on_clauses_error_types_on_test,4501,327,337,0.8714,0.0633,0.0652,0.1405,0.0102,0.0555,38101,32936,32034,6067,39128,1027,0,0,0,328 diff --git a/edt_2.6/results_stanza_ME_sketches_5groups_knockout.csv b/edt_2.6/results_stanza_ME_sketches_5groups_knockout.csv new file mode 100644 index 00000000..829f9faa --- /dev/null +++ b/edt_2.6/results_stanza_ME_sketches_5groups_knockout.csv @@ -0,0 +1,6 @@ +experiment,LAS_test,UAS_test +eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group0,0.9142,0.9392 +eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group1,0.9126,0.9356 +eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group2,0.9161,0.9403 +eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group3,0.9108,0.9373 +eval_stanza_morph_extended_sketches_knockout_5groups_single_file_group4,0.9129,0.9380 diff --git a/edt_2.6/results_stanza_ME_sketches_5groups_knockout_matrix.csv b/edt_2.6/results_stanza_ME_sketches_5groups_knockout_matrix.csv new file mode 100644 index 00000000..bb43559e --- /dev/null +++ b/edt_2.6/results_stanza_ME_sketches_5groups_knockout_matrix.csv @@ -0,0 +1,6 @@ +,group0,group1,group2,group3,group4 +group0,0.8931,0.9046,0.9107,0.9139,0.9093 +group1,0.9193,0.9047,0.9189,0.9154,0.9154 +group2,0.9293,0.9250,0.9131,0.9186,0.9246 +group3,0.9136,0.9122,0.9186,0.8903,0.9168 +group4,0.9194,0.9179,0.9204,0.9204,0.8947 diff --git a/edt_2.6/results_stanza_ME_sketches_5randomgroups_knockout.csv b/edt_2.6/results_stanza_ME_sketches_5randomgroups_knockout.csv new file mode 100644 index 00000000..c90ae5cc --- /dev/null +++ b/edt_2.6/results_stanza_ME_sketches_5randomgroups_knockout.csv @@ -0,0 +1,6 @@ +experiment,LAS_test,UAS_test +eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group0,0.9169,0.9393 +eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group1,0.9154,0.9372 +eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group2,0.9169,0.9399 +eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group3,0.9148,0.9388 +eval_stanza_morph_extended_sketches_knockout_5randomgroups_single_file_group4,0.9165,0.9396 diff --git a/edt_2.6/results_stanza_ME_sketches_5randomgroups_knockout_matrix.csv b/edt_2.6/results_stanza_ME_sketches_5randomgroups_knockout_matrix.csv new file mode 100644 index 00000000..fe7fcb8e --- /dev/null +++ b/edt_2.6/results_stanza_ME_sketches_5randomgroups_knockout_matrix.csv @@ -0,0 +1,6 @@ +,group0,group1,group2,group3,group4 +group0,0.9114,0.9057,0.9150,0.9114,0.9100 +group1,0.9129,0.9176,0.9154,0.9098,0.9154 +group2,0.9198,0.9218,0.9230,0.9186,0.9198 +group3,0.9179,0.9154,0.9136,0.9161,0.9182 +group4,0.9239,0.9184,0.9184,0.9184,0.9204 diff --git a/edt_2.6/results_stanza_UD_on_auto_UD_morph.csv b/edt_2.6/results_stanza_UD_on_auto_UD_morph.csv new file mode 100644 index 00000000..79fbc75b --- /dev/null +++ b/edt_2.6/results_stanza_UD_on_auto_UD_morph.csv @@ -0,0 +1,2 @@ +experiment,LAS_test,LAS_train,UAS_test,UAS_train,LAS_gap +eval_stanza_ud_on_stanza_auto_morph_full_data,0.8519,0.9291,0.8801,0.9467,0.0772 diff --git a/edt_2.6/results_stanza_basic.csv b/edt_2.6/results_stanza_basic.csv new file mode 100644 index 00000000..82908a21 --- /dev/null +++ b/edt_2.6/results_stanza_basic.csv @@ -0,0 +1,3 @@ +experiment,LAS_test,LAS_train,LAS_gap,UAS_test,UAS_train,test_words,train_words +eval_stanza_morph_analysis_full_data,0.8507,0.9231,0.0724,0.8802,0.9422,48491,389278 +eval_stanza_morph_extended_full_data,0.8486,0.9176,0.0689,0.8782,0.9378,48491,389278 diff --git a/ensemble_tagger_with_entropy.py b/ensemble_tagger_with_entropy.py new file mode 100644 index 00000000..fd3620e6 --- /dev/null +++ b/ensemble_tagger_with_entropy.py @@ -0,0 +1,450 @@ +# +# Tags dependency syntactic analysis with an ensemble of Stanza's models and records predictions' entropy. +# Requires estnltk 1.7.2 +# + +import os +from collections import defaultdict, Counter, OrderedDict +from decimal import Decimal, getcontext +from random import Random +from typing import Tuple +import warnings + +from scipy.stats import entropy + +from estnltk import Layer +from estnltk.taggers.standard.syntax.syntax_dependency_retagger import SyntaxDependencyRetagger +from estnltk.taggers.standard.syntax.ud_validation.deprel_agreement_retagger import DeprelAgreementRetagger +from estnltk.taggers.standard.syntax.ud_validation.ud_validation_retagger import UDValidationRetagger +from estnltk.taggers import Tagger +from estnltk.downloader import get_resource_paths + +from estnltk.converters.serialisation_modules import syntax_v0 + +from estnltk_neural.taggers.syntax.stanza_tagger.common_utils import prepare_input_doc +from estnltk_neural.taggers.syntax.stanza_tagger.common_utils import feats_to_ordereddict + +class StanzaSyntaxEnsembleTaggerWithEntropy(Tagger): + """ + Tags dependency syntactic analysis with an ensemble of Stanza's models and records predictions' entropy. + The tagger assumes that the segmentation to sentences and words is completed beforehand. When using default + models, the tagger assumes that extended morph analysis is completed with VabaMorf module. + + The tagger creates a syntax layer that features Universal Dependencies dependency-tags in attribute 'deprel'. + UPOS is the same as VabaMorf's part of speech tag and feats is based on VabaMorf's forms. + + Names of layers to use can be changed using parameters sentences_layer, words_layer and input_morph_layer, + if needed. To use GPU for parsing, parameter use_gpu must be set to True. + Parameter add_parents_and_children adds attributes that contain the parent and children of a word. + + When using models which are trained on some missing conllu fields (text, lemma, upos, xpos, feats), these + fields can be omitted by assigning a list of field names to parameter `remove_fields`. Fields can also be + replaced with a chosen string by assigning a tuple containing a list of field names as first element + and string as a second to parameter `replace_fields`. + + The input morph analysis layer can be ambiguous. In that case, StanzaSyntaxEnsembleTagger picks randomly one + morph analysis for each ambiguous word, and predicts from "unambiguous" input. + Important: as a result, by default, the output will not be deterministic: for ambiguous words, you will + get different 'lemma', 'upostag', 'xpostag', 'feats' values on each run, and this also affects the results + of dependency parsing. + Ambiguity can also rise in dependency parsing: ensemble models can give multiple dependency parses with + maximum score and one parse needs to be chosen -- this is again done via a random choice (using a different + random generator). + How to make the output deterministic: you can pass a seed value for picking one analysis from ambiguous + morph analyses via constructor parameter random_pick_seed (int, default value: None), and a seed value + for choosing one dependency result from results with maximum scores via constructor parameter + random_pick_max_score_seed (int, default value: None). + Note that seed values are fixed once at creating a new instance of StanzaSyntaxEnsembleTagger, and you only + get deterministic / repeatable results if you tag texts in exactly the same order. + Note: if you want to get the same deterministic output as in previous versions of the tagger, use + random_pick_seed=5 and random_pick_max_score_seed=3. + + Aggregation algorithm. For aggregating predictions from multiple models, there are currently 2 algorithms + available. The first / default method ('las_coherence') processes input sentence-wise and calculates LAS + scores between each model's sentence prediction and all other sentence predictions. The sentence prediction + with the highest average LAS will be chosen for the output. This method ensures valid output tree structure. + The second method ('majority_voting') processes input token-wise and records predicted head & deprel frequencies + for each token in a sentence. After that, it applies Chu–Liu/Edmonds' algorithm to construct a valid syntactic + tree of the sentence over high frequency heads of each token. Any remaining ambiguities (e.g. choices between + multiple different deprels for a head) will be resolved via random choice. + You can set the aggregation algorithm via constructor parameter aggregation_algorithm. + + Tutorial: + https://github.com/estnltk/estnltk/blob/main/tutorials/nlp_pipeline/C_syntax/03_syntactic_analysis_with_stanza.ipynb + """ + + conf_param = ['add_parent_and_children', 'aggregation_algorithm', 'syntax_dependency_retagger', + 'mark_syntax_error', 'mark_agreement_error', 'agreement_error_retagger', + 'ud_validation_retagger', 'use_gpu', 'gpu_max_words_in_sentence', 'model_paths', + 'taggers', 'remove_fields', 'replace_fields', 'find_entropy', 'random_pick_seed', + '_random1', 'random_pick_max_score_seed', '_random2'] + + def __init__(self, + output_layer: str = 'stanza_ensemble_syntax', + sentences_layer: str = 'sentences', + words_layer: str = 'words', + input_morph_layer: str = 'morph_extended', + aggregation_algorithm: str = 'las_coherence', + random_pick_seed: int = None, + random_pick_max_score_seed: int = None, + remove_fields: list = None, + replace_fields: Tuple[list, str] = None, + model_paths: list = None, + add_parent_and_children: bool = False, + mark_syntax_error: bool = False, + mark_agreement_error: bool = False, + find_entropy: bool = True, + use_gpu: bool = False, + gpu_max_words_in_sentence: int = 1000 + ): + # Make an internal import to avoid explicit stanza dependency + import stanza + + self.output_layer = output_layer + self.remove_fields = remove_fields + self.replace_fields = replace_fields + self.model_paths = model_paths + self.add_parent_and_children = add_parent_and_children + self.mark_syntax_error = mark_syntax_error + self.mark_agreement_error = mark_agreement_error + self.output_attributes = ('id', 'lemma', 'upostag', 'xpostag', 'feats', 'head', 'deprel', 'deps', 'misc') + if not isinstance(aggregation_algorithm, str) or \ + aggregation_algorithm.lower() not in ['las_coherence', 'majority_voting']: + raise ValueError(('(!) Unexpected aggregation_algorithm value {!r}. '+\ + 'Must be a value from set {"las_coherence", "majority_voting"}').format(aggregation_algorithm)) + self.aggregation_algorithm = aggregation_algorithm.lower() + self.use_gpu = use_gpu + # We may run into "CUDA out of memory" error when processing very long sentences + # with GPU. + # Set a reasonable default for max sentence length: if that gets exceeded, then a + # guarding exception will be thrown + self.gpu_max_words_in_sentence = gpu_max_words_in_sentence + + # Random generator for picking one analysis from ambiguous morph analyses: + self.random_pick_seed = random_pick_seed + self._random1 = Random() + if isinstance(self.random_pick_seed, int): + self._random1.seed(self.random_pick_seed) + # Random generator for choosing one dependency result from results with maximum scores: + self.random_pick_max_score_seed = random_pick_max_score_seed + self._random2 = Random() + if isinstance(self.random_pick_max_score_seed, int): + self._random2.seed(self.random_pick_max_score_seed) + + # Try to get the resources path for stanzasyntaxensembletagger. Attempt to download resources, if missing + resources_path = get_resource_paths("stanzasyntaxensembletagger", only_latest=True, download_missing=True) + if resources_path is None: + raise Exception('Models of StanzaSyntaxEnsembleTagger are missing. '+\ + 'Please use estnltk.download("stanzasyntaxensembletagger") to download the models.') + + if not model_paths: + self.model_paths = list() + ensemble_path = os.path.join(resources_path, 'et', 'depparse', 'ensemble_models') + if not os.path.isdir(ensemble_path): + raise ValueError('Missing models under the subdirectory `stanza_resources/et/depparse/ensemble_models.') + for model in os.listdir(ensemble_path): + self.model_paths.append(os.path.join(ensemble_path, model)) + + self.taggers = dict() # Save taggers + for i, model_path in enumerate(self.model_paths): + if not os.path.isfile(model_path): + raise ValueError('Invalid model path: {}'.format(model_path)) + + nlp = stanza.Pipeline(lang='et', processors='depparse', + dir=resources_path, + depparse_pretagged=True, + depparse_model_path=model_path, + use_gpu=self.use_gpu, + logging_level='WARN') + self.taggers[str(i)] = nlp + + self.input_layers = [sentences_layer, input_morph_layer, words_layer] + + self.syntax_dependency_retagger = None + if add_parent_and_children: + self.syntax_dependency_retagger = SyntaxDependencyRetagger(syntax_layer=output_layer) + self.output_attributes += ('parent_span', 'children') + + self.ud_validation_retagger = None + if mark_syntax_error: + self.ud_validation_retagger = UDValidationRetagger(output_layer=output_layer) + self.output_attributes += ('syntax_error', 'error_message') + + self.agreement_error_retagger = None + if mark_agreement_error: + if not add_parent_and_children: + raise ValueError('`add_parent_and_children` must be True for marking agreement errors.') + else: + self.agreement_error_retagger = DeprelAgreementRetagger(output_layer=output_layer) + self.output_attributes += ('agreement_deprel',) + + self.find_entropy = find_entropy + if self.find_entropy: + self.output_attributes += ('entropy', 'votes') + + def _make_layer_template(self): + """Creates and returns a template of the layer.""" + layer = Layer(name=self.output_layer, + text_object=None, + attributes=self.output_attributes, + parent=self.input_layers[1], + ambiguous=False ) + if self.add_parent_and_children: + layer.serialisation_module = syntax_v0.__version__ + return layer + + def _make_layer(self, text, layers, status): + # Make an internal import to avoid explicit stanza dependency + import numpy as np + from stanza import Document + from stanza.models.common.chuliu_edmonds import chuliu_edmonds_one_root + + sentences_layer = self.input_layers[0] + morph_layer = self.input_layers[1] + + sents_lases_table = defaultdict(dict) + parsed_texts = defaultdict() + + text_data = prepare_input_doc(layers, sentences_layer, morph_layer, remove_fields=self.remove_fields, + replace_fields=self.replace_fields, random_picker=self._random1) + + if self.use_gpu and self.gpu_max_words_in_sentence is not None: + # Check that sentences are not too long (for CUDA memory) + for sentence in text_data: + if len(sentence) > self.gpu_max_words_in_sentence: + raise Exception( ('(!) Encountered a sentence which length ({}) exceeds '+\ + 'gpu_max_words_in_sentence ({}). Are you sure GPU '+\ + 'has enough memory for processing this long sentence? '+\ + 'Either process this document with CPU or, if GPU '+\ + 'memory is ensured, pass parameter '+\ + 'gpu_max_words_in_sentence=None to this tagger '+\ + 'to disable this exception.').format(len(sentence), \ + self.gpu_max_words_in_sentence) ) + + for model, nlp in self.taggers.items(): + doc = Document(text_data) + nlp(doc) # Parsing documents + parsed_texts[model] = doc.to_dict() + + parent_layer = layers[self.input_layers[1]] + # Find predictions' uncertainty + words_entropy = None + if self.find_entropy: + words_entropy = \ + find_prediction_entropy(parent_layer, parsed_texts, add_model_votes=True) + assert len(words_entropy) == len(parent_layer) + extracted_words = [] + if self.aggregation_algorithm == 'las_coherence': + # 1) Compare predictions of each model against every other + # model, and find the prediction with the highest avg sentence + # LAS score. This method ensures valid tree structure. + extracted_data = [] + for model, parsed in parsed_texts.items(): + for model2, parsed2 in parsed_texts.items(): + text_sentence_lases = text_sentence_LAS(parsed, parsed2) + sents_lases_table[model][model2] = text_sentence_lases + + final_table = defaultdict(dict) # Scores by sentence indices. + for idx in range(len(layers[sentences_layer])): + for model, scores in sents_lases_table.items(): + if model not in final_table: + final_table[idx][model] = {} + for model2, sentence_scores in scores.items(): + score = sentence_scores[idx] + final_table[idx][model][model2] = score + + sent_scores = defaultdict(dict) + getcontext().prec = 4 + for sent, score_dict in final_table.items(): + if sent not in sent_scores: + sent_scores[sent] = Counter() + for base_model, score in score_dict.items(): + decimals = list(map(Decimal, score.values())) + avg_score = sum(decimals) / Decimal(len(self.taggers)) + sent_scores[sent][base_model] = avg_score + + chosen_sents = defaultdict(list) + for sent, score in sent_scores.items(): + max_score = max(score.values()) + max_score_count = 0 + max_score_models = [] + for s in score: + if score[s] == max_score: + max_score_count += 1 + max_score_models.append(s) + self._random2.shuffle(max_score_models) + chosen_sents[max_score_models[0]].append(sent) + + idxed_sents = {} + for model, sent_no in chosen_sents.items(): + content = parsed_texts[model] + sents_set = set(sent_no) + for idx in sents_set: + idxed_sents[idx] = content[idx] + + for idx in range(0, len(idxed_sents)): + extracted_data.append(idxed_sents[idx]) + + extracted_words = [word for sentence in extracted_data for word in sentence] + else: + assert self.aggregation_algorithm == "majority_voting" + # 2) Majority voting: pick the dependency relation with the + # highest number of votes over all model predictions. + word_id = 0 + sentence_id = 0 + while sentence_id < len(layers[sentences_layer]): + # 1) Collect words and votes for the current sentence + sentence_word_id = 0 + sent_len = len(layers[sentences_layer][sentence_id]) + voting_table = defaultdict(lambda: defaultdict(int)) + label_token_map = defaultdict(lambda: defaultdict(list)) + sent_matrix = np.zeros((sent_len+1, sent_len+1)) + np.fill_diagonal(sent_matrix, -float('inf')) + while sentence_word_id < sent_len: + # collect votes + for model, parsed_doc in parsed_texts.items(): + assert len(parsed_doc) == len(layers[sentences_layer]) + sentence = parsed_doc[sentence_id] + assert len(sentence) == sent_len + token = sentence[sentence_word_id] + label = '{}__{}'.format(token['deprel'], token['head']) + voting_table[sentence_word_id][label] += 1 + label_token_map[sentence_word_id][label].append(token) + head_int = int(token['head']) + sent_matrix[sentence_word_id+1, head_int] += 1.0 + sentence_word_id += 1 + word_id += 1 + # 2) use Chu–Liu/Edmonds' algorithm to find head_seq of a valid tree + valid_tree_head_seq = chuliu_edmonds_one_root(sent_matrix)[1:] + # 3) For each word, find maximum voting score and corresponding tokens + for wid in sorted(voting_table.keys()): + valid_head = valid_tree_head_seq[wid] + max_votes_valid = [] + for l, v in voting_table[wid].items(): + if l.endswith('__{}'.format(valid_head)): + max_votes_valid.append((l, v)) + if not max_votes_valid: + # If something went wrong, then fall back to unchecked tree. + word_str = layers[sentences_layer][sentence_id][wid] + sentence_str = layers[sentences_layer][sentence_id].enclosing_text + msg = ('(!) Unable to find a tree-bound head for '+\ + 'word {!r} in sentence {!r}. ').format(word_str, sentence_str) + msg += 'Falling back to unchecked tree construction, ' + msg += 'which may result in an invalid syntax tree.' + warnings.warn(msg) + max_votes_valid = voting_table[wid].items() + max_votes = max([v for (l, v) in max_votes_valid]) + max_votes_labels = [l for l, v in max_votes_valid if v==max_votes] + max_votes_tokens = [] + for label, tokens in label_token_map[wid].items(): + if label in max_votes_labels: + max_votes_tokens.extend(tokens) + # In case of a tie, pick a token randomly + self._random2.shuffle(max_votes_tokens) + extracted_words.append(max_votes_tokens[0]) + # Next sentence + sentence_id += 1 + assert len(extracted_words) == len(parent_layer) + + layer = self._make_layer_template() + layer.text_object=text + + global_word_id = 0 + for token, span in zip(extracted_words, parent_layer): + assert span.text == token['text'] + word_id = token['id'] + lemma = token['lemma'] + upostag = token['upos'] + xpostag = token['xpos'] + feats = OrderedDict() + if 'feats' in token.keys(): + feats = feats_to_ordereddict(token['feats']) + head = token['head'] + deprel = token['deprel'] + + attributes = {'id': word_id, 'lemma': lemma, 'upostag': upostag, 'xpostag': xpostag, + 'feats': feats, 'head': head, 'deprel': deprel, 'deps': '_', 'misc': '_'} + if words_entropy is not None: + w_entropy = words_entropy[global_word_id] + attributes['entropy'] = w_entropy['entropy'] + attributes['votes'] = w_entropy['votes'] + + layer.add_annotation(span, **attributes) + global_word_id += 1 + + if self.add_parent_and_children: + # Add 'parent_span' & 'children' to the syntax layer. + self.syntax_dependency_retagger.change_layer(text, {self.output_layer: layer}) + + if self.mark_syntax_error: + # Add 'syntax_error' & 'error_message' to the layer. + self.ud_validation_retagger.change_layer(text, {self.output_layer: layer}) + + if self.mark_agreement_error: + # Add 'agreement_deprel' to the layer. + self.agreement_error_retagger.change_layer(text, {self.output_layer: layer}) + + return layer + + +def find_prediction_entropy(words_layer, parsed_texts, add_model_votes=True, add_sentence_ids=False): + ''' + Calculates uncertainty/Shannon entropy for ensemble predictions of each word. + If add_model_votes, then adds frequencies of model votes to results (for debugging). + If add_sentence_ids, then adds sentence id-s to results. + ''' + word_id = 0 + sentence_id = 0 + sentence_word_id = 0 + results = [] + while word_id < len( words_layer ): + sentence = None + # Get (deprel,head) votes for the word token + votes = [] + for model, parsed_doc in parsed_texts.items(): + sentence = parsed_doc[sentence_id] + token = sentence[sentence_word_id] + label = '{}_{}'.format(token['deprel'], token['head']) + votes.append(label) + voting_table = Counter(votes) + normalized_voting_table = \ + [voting_table[k]/len(parsed_texts.items()) for k in voting_table.keys()] + e = entropy(normalized_voting_table) + r = {'entropy': e} + if add_model_votes: + r['votes'] = voting_table.most_common() + if add_sentence_ids: + r['sentence_id'] = sentence_id + results.append(r) + word_id += 1 + sentence_word_id += 1 + if sentence_word_id >= len(sentence): + # Next sentence + sentence_id += 1 + sentence_word_id = 0 + return results + + +def sentence_LAS(sent1, sent2): + wrong = 0 + correct = 0 + for tok1, tok2 in zip(sent1, sent2): + if tok1['xpos'] != 'Z': + if tok1['head'] == tok2['head'] and tok1['deprel'] == tok2['deprel']: + correct += 1 + else: + wrong += 1 + + if wrong == 0 and correct == 0: + return 1 + else: + return correct / (correct + wrong) + + +def text_sentence_LAS(sents1, sents2): + file_sentence_lases = [] + for sent1, sent2 in zip(sents1, sents2): + las = sentence_LAS(sent1, sent2) + file_sentence_lases.append(las) + return file_sentence_lases + diff --git a/py39_stanza_training_env.yml b/py39_stanza_training_env.yml new file mode 100644 index 00000000..41045a5b --- /dev/null +++ b/py39_stanza_training_env.yml @@ -0,0 +1,123 @@ +name: py39_stanza_training +channels: + - defaults +dependencies: + - _libgcc_mutex=0.1=main + - _openmp_mutex=5.1=1_gnu + - ca-certificates=2022.10.11=h06a4308_0 + - certifi=2022.12.7=py39h06a4308_0 + - ld_impl_linux-64=2.38=h1181459_1 + - libffi=3.4.2=h6a678d5_6 + - libgcc-ng=11.2.0=h1234567_1 + - libgomp=11.2.0=h1234567_1 + - libstdcxx-ng=11.2.0=h1234567_1 + - ncurses=6.3=h5eee18b_3 + - openssl=1.1.1s=h7f8727e_0 + - pip=22.3.1=py39h06a4308_0 + - python=3.9.15=h7a1cb2a_2 + - readline=8.2=h5eee18b_0 + - setuptools=65.6.3=py39h06a4308_0 + - sqlite=3.40.1=h5082296_0 + - tk=8.6.12=h1ccaba5_0 + - tzdata=2022g=h04d1e81_0 + - wheel=0.37.1=pyhd3eb1b0_0 + - xz=5.2.8=h5eee18b_0 + - zlib=1.2.13=h5eee18b_0 + - pip: + - absl-py==1.4.0 + - asttokens==2.2.1 + - astunparse==1.6.3 + - backcall==0.2.0 + - beautifulsoup4==4.11.1 + - bs4==0.0.1 + - cached-property==1.5.2 + - cachetools==5.2.1 + - charset-normalizer==2.1.1 + - click==8.1.3 + - conllu==4.5.2 + - contourpy==1.0.6 + - cycler==0.11.0 + - decorator==5.1.1 + - emoji==2.2.0 + - estnltk==1.7.2 + - estnltk-core==1.7.2 + - estnltk-neural==1.7.2 + - executing==1.2.0 + - filelock==3.9.0 + - flatbuffers==23.1.4 + - fonttools==4.38.0 + - gast==0.4.0 + - google-auth==2.16.0 + - google-auth-oauthlib==0.4.6 + - google-pasta==0.2.0 + - grpcio==1.51.1 + - h5py==3.7.0 + - html5lib==1.1 + - huggingface-hub==0.11.1 + - idna==3.4 + - importlib-metadata==6.0.0 + - ipython==8.8.0 + - jedi==0.18.2 + - joblib==1.2.0 + - keras==2.11.0 + - kiwisolver==1.4.4 + - libclang==15.0.6.1 + - lxml==4.9.2 + - markdown==3.4.1 + - markupsafe==2.1.2 + - matplotlib==3.6.2 + - matplotlib-inline==0.1.6 + - networkx==3.0 + - nltk==3.8.1 + - numpy==1.24.1 + - oauthlib==3.2.2 + - opt-einsum==3.3.0 + - packaging==23.0 + - pandas==1.5.2 + - parso==0.8.3 + - pexpect==4.8.0 + - pickleshare==0.7.5 + - pillow==9.4.0 + - prompt-toolkit==3.0.36 + - protobuf==3.19.6 + - ptyprocess==0.7.0 + - pure-eval==0.2.2 + - pyahocorasick==1.4.4 + - pyasn1==0.4.8 + - pyasn1-modules==0.2.8 + - pygments==2.14.0 + - pyparsing==3.0.9 + - python-crfsuite==0.9.8 + - python-dateutil==2.8.2 + - pytz==2022.7 + - pyyaml==6.0 + - regex==2022.10.31 + - requests==2.28.1 + - requests-oauthlib==1.3.1 + - rsa==4.9 + - six==1.16.0 + - soupsieve==2.3.2.post1 + - stack-data==0.6.2 + - stanza==1.4.2 + - tensorboard==2.11.2 + - tensorboard-data-server==0.6.1 + - tensorboard-plugin-wit==1.8.1 + - tensorflow==2.11.0 + - tensorflow-estimator==2.11.0 + - tensorflow-io-gcs-filesystem==0.29.0 + - termcolor==2.2.0 + - tokenizers==0.13.2 + - torch==1.13.1+cu116 + - torchaudio==0.13.1+cu116 + - torchvision==0.14.1+cu116 + - tqdm==4.64.1 + - traitlets==5.8.1 + - transformers==4.25.1 + - typing-extensions==4.4.0 + - urllib3==1.26.14 + - wcwidth==0.2.5 + - webencodings==0.5.1 + - werkzeug==2.2.2 + - wrapt==1.14.1 + - zipp==3.11.0 +prefix: /home/soras/.conda/envs/py39_stanza_training diff --git a/readme.md b/readme.md new file mode 100644 index 00000000..0347d82e --- /dev/null +++ b/readme.md @@ -0,0 +1,97 @@ +## Syntax ablation experiments + +This repository contains codebase and results of the experiments reported in the article ["Automatic dependency parsing of Estonian: what linguistic features to include?"](https://doi.org/10.1007/s10579-024-09779-z). + +### Pre-requisites + +* Install [estnltk](https://github.com/estnltk/estnltk) (version 1.7.2+ is required); +* Install [stanza](https://github.com/stanfordnlp/stanza) (we used version 1.4.2); +* Install [scikit-learn](https://scikit-learn.org/) (we used version 1.2.1); +* For some of the experiments, you'll also need [MaltParser](https://maltparser.org), [MaltOptimizer](http://nil.fdi.ucm.es/maltoptimizer), [UDPipe-1](https://ufal.mff.cuni.cz/udpipe/1) and [gensim](https://radimrehurek.com/gensim); +* For visualization of the results, you'll need [matplotlib](https://matplotlib.org/stable/), [seaborn](https://seaborn.pydata.org/), [plotnine](https://plotnine.readthedocs.io/en/stable/), [patchworklib](https://pypi.org/project/patchworklib/), [adjustText](https://pypi.org/project/adjustText/); +* Download and unpack [Estonian UD corpus](https://github.com/UniversalDependencies/UD_Estonian-EDT/tags) (most experiments were conducted with the corpus version 2.6, the version 2.11 was used for one experiment); + +### Configuration files + +Most important settings of data pre-processing, training and evaluation are defined in configuration INI files. You can find these files in [confs](confs/) folder. In order to run a processing step, pass name of an INI file as an argument to the script. + +### Processing steps (scripts) + +* `01_ud_preprocessing.py` -- Converts gold standard UD corpus to EstNLTK's format: overwrites values of `lemma`, `upos`, `xpos` and `feats` with EstNLTK's automatic morphological analyses (from layers `morph_analysis` / `morph_extended` / `ud_morph_analysis`). Alternatively, you can also skip the conversion altogether and just clean the gold standard files and copy to the experiments folder. Executes all sections starting with `preannotation_` and `copy_` in input configuration file. Example usage: + + * `python 01_ud_preprocessing.py confs/conf_edt_v26_Stanza_ME_full.ini` + +* `01b_extract_clauses.py` -- Splits sentences in CONLLU files into clauses (with EstNLTK's ClauseTagger). Cleans clauses (removes conjunctions and punctuation at the beginning and/or at the end of a clause), and exports cleaned clauses as CONLLU files. Executes all sections starting with `extract_clauses_` in input configuration file. This is a preprocessing step required by _syntax sketches knockout experiments_. Example usage: + + * `python 01b_extract_clauses.py confs/conf_edt_v26_Stanza_ME_sketches_knockout_5_groups.ini` + +* `01c_analyse_sketches.ipynb` -- computes sketches from the whole corpus and provides (descriptive) data analysis of sketches. Optional step in _syntax sketches knockout experiments_. + +* `01d_prepare_sketches.py` -- Creates frequency table of syntax sketches, and prepares datasets for sketches knockout experiments: removes clauses corresponding to sketches systematically from train, dev and test sets. Executes sections in the configuration starting with prefix `make_sketches_table_` and `prepare_knockout_`. This is a preprocessing step required by _syntax sketches knockout experiments_. Example usage: + + * `python 01d_prepare_sketches.py confs/conf_edt_v26_Stanza_ME_sketches_knockout_5_groups.ini` + +* `02_split_data.py` -- Creates data splits (or joins) for model training and evaluation. Executes all sections starting with `split_` and `join_` in input configuration file. Example usage: + + * `python 02_split_data.py confs/conf_edt_v26_Stanza_ME_full.ini` + +* `02b_make_gaps.py` -- Modifies conllu files for gap experiments: deletes a combination of fields `form`, `lemma`, `upos`, `xpos`, `feats` from files, and writes files with deletions to a new location. For implemented modifications, see the header comment of the script. Executes all sections starting with `modify_conllu_` in input configuration file. Example usage: + + * `python 02b_make_gaps.py confs/conf_edt_v26_Stanza_ME_gap_experiments.ini` + +* `03_predict_stanza_morph.py` -- Uses stanza's POS/morphological features tagger and lemmatizer models for predicting morphological annotations. Executes all sections starting with `predict_morph_feats_stanza_` in input configuration file. Example: + + * `python 03_predict_stanza_morph.py confs/conf_edt_v26_Stanza_UD_morph_Stanza_UD_syntax.ini` + +* `03_train_stanza.py` -- Trains stanza parser models. Executes all sections starting with `train_stanza_` in input configuration file. Example: + + * `python 03_train_stanza.py confs/conf_edt_v26_Stanza_ME_full.ini` + +* `03b_optimize_malt.py` -- Optimizes MaltParser before training: produces feature selection files. Requires Python 2.7. Executes all sections starting with `maltoptimize_` in input configuration file. Example: + + * `python 03b_optimize_malt.py confs/conf_edt_v26_MaltParser_ME_full.ini` + +* `03c_train_malt_udpipe.py` -- Trains MaltParser and/or UDPipe-1 models. Executes all sections starting with `train_malt_` and `train_udpipe1_` in input configuration file. Example: + + * `python 03c_train_malt_udpipe.py confs/conf_edt_v26_MaltParser_ME_full.ini` + +* `04_predict_stanza.py` -- Applies trained stanza parser models on evaluation data to get predictions. Writes predictions to conllu files. Executes all sections starting with `predict_stanza_` in input configuration file. Example: + + * `python 04_predict_stanza.py confs/conf_edt_v26_Stanza_ME_full.ini` + +* `04b_predict_malt_udpipe.py` -- Applies trained MaltParser and/or UDPipe-1 models on evaluation data to get predictions. Writes predictions to conllu files. Executes all sections starting with `predict_malt_` and `predict_udpipe1_` in input configuration file. Example: + + * `python 04b_predict_malt_udpipe.py confs/conf_edt_v26_MaltParser_ME_full.ini` + +* `05_evaluate.py` -- Evaluates predictions: compares predicted files to gold standard files and calculates LAS/UAS scores. Executes all sections starting with `eval_` in given configuration files (multiple INI files can be given as an input). Writes results into file `results.csv` in a sub directory closest to the execution directory (for given configurations, the path will be: `edt_2.6/results.csv`). You can also give name of the output csv file as an input argument of the script. Example usage: + + * `python 05_evaluate.py confs/conf_edt_v26_MaltParser_ME_full.ini results_maltparser.csv` + +Note: configurations also contain overlapping parts, e.g. once you've run UD preprocessing with `confs/conf_edt_v26_Stanza_ME_full.ini`, you do not need to run UD preprocessing again with `confs/conf_edt_v26_stanza_ME_ensemble_full.ini`; + +### Results and further studies + + +* [06_result_tables.ipynb](06_result_tables.ipynb) -- tables with the experiment results read from CSV files; + +* [07_smaller_data_exp_and_extrapolation.ipynb](07_smaller_data_exp_and_extrapolation.ipynb) -- draw figures about smaller data experiments (experiments where training set size is gradually increased) and extrapolate the results; + +* [08_results_clauses_and_sketches_knockout_5groups.ipynb](08_results_clauses_and_sketches_knockout_5groups.ipynb) -- results of the _clauses experiment_ and _syntax sketches knockout experiments_; + +### Extras + +* [X1_previously_reported_performances.ipynb](X1_previously_reported_performances.ipynb) -- a small recap of previously reported dependency parsing performances for Estonian and English; + +### Citation + +In case you use this codebase or results in your work, please cite us as follows: + + @article{laur2024automatic, + title={Automatic dependency parsing of Estonian: what linguistic features to include?}, + author={Laur, Sven and Orasmaa, Siim and Eiche, Sandra and Särg, Dage}, + journal={Language Resources and Evaluation}, + year={2024}, + publisher={Springer}, + url={https://doi.org/10.1007/s10579-024-09779-z}, + doi={10.1007/s10579-024-09779-z}, + } \ No newline at end of file diff --git a/syntax_sketches/__init__.py b/syntax_sketches/__init__.py new file mode 100644 index 00000000..5fec1e74 --- /dev/null +++ b/syntax_sketches/__init__.py @@ -0,0 +1,3 @@ +from .syntax_sketch import clean_clause +from .syntax_sketch import syntax_sketch +from .clause_export import export_cleaned_clause diff --git a/syntax_sketches/clause_export.py b/syntax_sketches/clause_export.py new file mode 100644 index 00000000..fac2db62 --- /dev/null +++ b/syntax_sketches/clause_export.py @@ -0,0 +1,83 @@ +from typing import Dict, List + + +def export_cleaned_clause(clause: Dict[str, list]) -> str: + """ + Exports a cleaned clause into the CoNLL-U format. This can be used to create various data sets. + The function assumes that the input is generated by the function clean_clause and that there is only one root node. + Returns a properly formatted table where each token is on the separate line and upos and xpos fields are the same. + """ + + assert len(clause['root_loc']) == 1, 'There can be only one root in a clause' + assert len(clause['ids']) == len(clause['wordforms']) == len(clause['lemmas']), 'Fields must be aligned' + assert len(clause['ids']) == len(clause['postags']) == len(clause['features']), 'Fields must be aligned' + assert len(clause['ids']) == len(clause['deprels']) == len(clause['heads']), 'Fields must be aligned' + + # As standard ids will be assigned to all tokens in a clause we need update heads + head_map = {clause_id: idx + 1 for idx, clause_id in enumerate(clause['ids'])} + + result = [] + row_template = '{id}\t{wordform}\t{lemma}\t{postag}\t{postag}\t{features}\t{head}\t{deprel}\t_\t_' + for i in range(len(clause['ids'])): + + head = head_map.get(clause['heads'][i], 0) + deprel = clause['deprels'][i] if head != 0 else 'root' + + if clause['features'][i]: + features = '|'.join([key + '=' + value for key, value in clause['features'][i].items()]) + else: + features = '_' + + result.append(row_template.format( + id=i + 1, + wordform=clause['wordforms'][i], + lemma=clause['lemmas'][i], + postag=clause['postags'][i], + features=features, + head=head, + deprel=deprel)) + + return '\n'.join(result) + + + +def remove_extracted_from_conllu_and_dicts(overall_conllu: List[str], + overall_dicts: List[Dict[str, list]], + extracted_conllu: List[str]): + ''' + Removes items of extracted_conllu from overall_conllu (and from corresponding + overall_dicts). + + Assumes that overall_conllu and extracted_conllu are lists of CoNLL-U format + clause strings, and that extracted_conllu is a sub list of overall_conllu. + overall_dicts should be a list of dictionaries with CoNLL features, each dict + corresponding to a clause with the same index in overall_conllu. + + Note that there can be repeating clauses in overall_conllu, e.g. clause "ma ei tea" + ('I don't know') may appear multiple times. If a repeating clause appears in + extracted_conllu, all of its instances will be deleted from overall_conllu and + overall_dicts. + This function keeps track of how many times each clause (conllu) was deleted and + returns a dictionary mapping extracted clause conllu-s to corresponding deletion + counts (for debugging purposes). + + Returns (new_clause_conllu, new_clause_dicts, deletion_counts) + ''' + assert len(overall_conllu) == len(overall_dicts) + new_clause_conllu = [] + new_clause_dicts = [] + deletion_counts = dict() + extracted_counts = \ + {conllu: extracted_conllu.count(conllu) for conllu in extracted_conllu} + for cid, conllu in enumerate(overall_conllu): + if conllu not in extracted_conllu: + # Preserve clause + new_clause_conllu.append(conllu) + new_clause_dicts.append(overall_dicts[cid]) + else: + # Delete clause + # Keep track of how many times clause was deleted + if conllu not in deletion_counts: + deletion_counts[conllu] = 0 + deletion_counts[conllu] += 1 + return new_clause_conllu, new_clause_dicts, deletion_counts \ No newline at end of file diff --git a/syntax_sketches/clause_import.py b/syntax_sketches/clause_import.py new file mode 100644 index 00000000..d37e9680 --- /dev/null +++ b/syntax_sketches/clause_import.py @@ -0,0 +1,98 @@ +import os, os.path +from typing import List + +# Mapping from conllu field name to key name in output dictionary +conllu2dictkey = \ + [('id', 'ids'), + ('wordform', 'wordforms'), + ('lemma', 'lemmas'), + ('upos', None), + ('xpos', 'postags'), + ('features', 'features'), + ('head', 'heads'), + ('deprel', 'deprels'), + ('deps', None), + ('misc', None)] + + +def import_clauses(input_file: str, as_dicts:bool=False) -> List[str]: + """ + Imports all clauses from a CoNLL-U format file. + + By default, returns a list of CoNLL-U strings, each corresponding + to a clause. If as_dicts==True, then returns a list of dicts, + each dict containing CoNLL features extracted from a single clause + (dict keys: ids, postags, deprels, heads, root_loc, wordforms, + lemmas, features). + + CoNLL-U strings output format is used in filtering/removing clauses, + dicts output format is a basis for creating syntax sketches for + clauses. + + The function assumes that clauses in the input file have been + generated by the function export_cleaned_clause. + """ + assert os.path.exists( input_file ), f'Non-existent input conllu file: {input_file}' + clause_dicts = [] + clause_strings = [] + with open(input_file, 'r', encoding='utf-8') as in_f: + clause_started = False + current_clause = [] + current_clause_dict = dict(ids=[], postags=[], deprels=[], + heads=[], root_loc=[], wordforms=[], + lemmas=[], features=[]) + for line in in_f: + if len(line.rstrip()) > 0: + if line[0].isnumeric(): + number_of_fields = len(line.split('\t')) + assert number_of_fields == len(conllu2dictkey), \ + f'Unexpected number of fields ({number_of_fields}) on conllu line: {line!r}' + # line corresponds to a word's analysis + if not as_dicts: + # extract only CoNLL line + current_clause.append( line.rstrip() ) + else: + # extract CoNLL field values to dict + parts = line.split('\t') + for field_id, (conllu_field, dict_key) in enumerate(conllu2dictkey): + if dict_key is not None: + value = parts[field_id] + current_clause_dict[dict_key].append(value) + clause_started = True + else: + if not line.startswith('#'): + raise ValueError(f'Unexpected conllu line: {line!r}') + else: + if clause_started: + # end of a clause + if not as_dicts: + # Save clause CoNLL lines + clause_strings.append('\n'.join(current_clause)) + current_clause = [] + else: + # Find indices of root nodes + ids = current_clause_dict['ids'] + heads = current_clause_dict['heads'] + root_locations = [i for i, head in enumerate(heads) if head not in ids] + current_clause_dict['root_loc'] = root_locations + # Save clause dict + clause_dicts.append(current_clause_dict) + current_clause_dict = dict(ids=[], postags=[], deprels=[], + heads=[], root_loc=[], wordforms=[], + lemmas=[], features=[]) + clause_started = False + # add the last remaining clause + if clause_started: + if not as_dicts: + # Save clause CoNLL lines + clause_strings.append('\n'.join(current_clause)) + else: + # Find indices of root nodes + ids = current_clause_dict['ids'] + heads = current_clause_dict['heads'] + root_locations = [i for i, head in enumerate(heads) if head not in ids] + current_clause_dict['root_loc'] = root_locations + # Save clause dict + clause_dicts.append(current_clause_dict) + return clause_strings if not as_dicts else clause_dicts + diff --git a/syntax_sketches/deprel_seq.py b/syntax_sketches/deprel_seq.py new file mode 100644 index 00000000..f558a2e3 --- /dev/null +++ b/syntax_sketches/deprel_seq.py @@ -0,0 +1,20 @@ +import conllu + +def collect_deprel_seqs(in_file: str): + ''' + Reads sentences from given conllu file (`in_file`) and + collects all `deprel` sequences corresponding to sentences. + Returns a list of sentences, each sentence represented as + a `deprel` sequence string, where word `deprels` are joined + via `|`. + This function is used only for data exploration. + ''' + with open(in_file, 'r', encoding='utf-8') as input_file: + conll_sentences = conllu.parse(input_file.read()) + all_deprel_seqs = [] + for sentence in conll_sentences: + deprel_seq = [] + for token in sentence: + deprel_seq.append(token["deprel"]) + all_deprel_seqs.append('|'.join(deprel_seq)) + return all_deprel_seqs \ No newline at end of file diff --git a/syntax_sketches/syntax_sketch.py b/syntax_sketches/syntax_sketch.py new file mode 100644 index 00000000..c4784752 --- /dev/null +++ b/syntax_sketches/syntax_sketch.py @@ -0,0 +1,344 @@ +import os, os.path +from random import Random + +from estnltk import Layer +from estnltk.converters.conll.conll_importer import conll_to_text + +from typing import List, Dict, Union, Any, Tuple, Optional + +# ===================================================== +# Creating syntax sketches +# ===================================================== + +def subtree_size(heads: List[int], tails: List[int], root: int) -> int: + """ + Computes the size of the subtree specified by the root node, i.e., the root is included into the subtree. + Arcs of a tree are specified as head, tail pairs, i.e., tails[i] -> heads[i] is an arc. + """ + + result = 0 + for i, dep_head in enumerate(heads): + if dep_head == root: + result += subtree_size(heads, tails, tails[i]) + return result + 1 + + +def clean_clause(clause: Layer) -> Dict[str, list]: + """ + Removes spurious words from clause and extracts relevant information from other layers. + Spurious words can occur at the beginning or at the end of the clause: + * conjunctions + * punctuation marks + + Returns a dictionary of aligned vectors for clause members: + * ids -- token numbers + * postags -- part-of-speech tags + * deprels -- dependency relations + * heads -- head of the node + * root_loc -- indices of root nodes + * wordforms -- complete text + * lemmas -- lemma + * features -- other syntactic features + + Syntax information is specified as in the syntax tree corresponding to the entire sentence. + As clause finding algorithm is not perfect there can be several roots in the clause. + The information about root can be found by fetching the corresponding field, e.g. ids[root_loc[0]]. + These fields contain enough information to store the cleaned clause in the conll-format + """ + + # Extract relevant fields + ids = list(clause.ud_syntax.id) + postags = list(clause.ud_syntax.xpostag) + deprels = list(clause.ud_syntax.deprel) + heads = list(clause.ud_syntax.head) + + wordforms = list(clause.ud_syntax.text) + lemmas = list(clause.ud_syntax.lemma) + features = list(clause.ud_syntax.feats) + + # Remove leading punctuation marks and conjunction + while postags and ('J' in postags[0] or 'Z' in postags[0]): + heads.pop(0) + ids.pop(0) + deprels.pop(0) + postags.pop(0) + wordforms.pop(0) + lemmas.pop(0) + features.pop(0) + + if not postags: + return dict(ids=[], postags=[], deprels=[], heads=[], root_loc=[], wordforms=[], lemmas=[], features=[]) + + # Remove trailing punctuation marks and conjunction + while 'J' in postags[-1] or 'Z' in postags[-1]: + heads.pop() + ids.pop() + deprels.pop() + postags.pop() + wordforms.pop() + lemmas.pop() + features.pop() + + # Find indices of root nodes + root_locations = [i for i, head in enumerate(heads) if head not in ids] + + return dict( + ids=ids, postags=postags, deprels=deprels, heads=heads, + root_loc=root_locations, + wordforms=wordforms, lemmas=lemmas, features=features) + + +def syntax_sketch(clause: Dict[str, list], ordered=True) -> str: + """ + Computes syntax sketch for a clause that encodes information about the root node and the first level child nodes. + By default the first level child nodes are lexicographically ordered in the sketch. + + Examples: + + wordforms: ['Ma', 'kaldun', 'arvama'] + ids: [1, 2, 3] + heads: [2, 0, 2] + postags: ['P', 'V', 'V'] + deprels: ['nsubj', 'root', 'xcomp'] + root_loc: [1] + output: '[V]nsubj(L)xcomp(L)' + + wordforms: ['Vermeeri', 'saatus', 'oli', 'teistsugune'] + ids: [6, 7, 8, 9] + heads: [7, 9, 9, 3] + postags: ['S', 'S', 'V', 'P'] + deprels: ['nmod', 'nsubj:cop', 'cop', 'ccomp'] + root_loc: [3] + output: '[S]cop(L)nsubj:cop(L)' + + wordforms: ['uus', 'ooper', 'tuleb', 'habras', 'ja', 'ilus'] + ids: [8, 9, 10, 11, 12, 13] + heads: [9, 10, 2, 10, 13, 11] + postags: ['A', 'S', 'V', 'A', 'J', 'A'] + deprels: ['amod', 'nsubj', 'ccomp', 'xcomp', 'cc', 'conj'] + root_loc: [2] + output: '[V]nsubj(L)xcomp(P)' + """ + + assert len(clause['root_loc']) == 1, "The clause must have a single root" + + # Compute root tag for the sketch + root_tag = clause['postags'][clause['root_loc'][0]] + if root_tag == 'V': + # group of verbs + sketch_root = 'V' + elif root_tag in ['S', 'P', 'A', 'Y', 'N']: + # non-verbs: substantives, pronouns, adjectives, + # acronyms/abbreviations, numerals + sketch_root = 'S' + else: + # remaining postags + sketch_root = 'X' + + # Compute sketches for child nodes + first_level = list() + root = clause['ids'][clause['root_loc'][0]] + for i, head in enumerate(clause['heads']): + if head != root: + continue + + length = subtree_size(clause['heads'], clause['ids'], clause['ids'][i]) + if length < 3: + subtree_cat = 'L' + elif length < 10: + subtree_cat = 'P' + else: + subtree_cat = 'ÜP' + + subtree = clause['deprels'][i] + '({})'.format(subtree_cat) + first_level.append(subtree) + + if ordered: + return '[{root}]{children}'.format(root=sketch_root, children=''.join(sorted(first_level))) + else: + return '[{root}]{children}'.format(root=sketch_root, children=''.join(first_level)) + + +def safe_sketch_name(sketch_name: str) -> str: + ''' + Makes sketch name safe so that it can be used as (a part of) file name. + Returns safe name. + ''' + safe_name = sketch_name.replace(':', 'COLON').replace(')', '').replace('(', '').replace('[', '').replace(']', '') + assert safe_name.isidentifier() + return safe_name + + +# ===================================================== +# Compute sketches for the whole corpus +# ===================================================== + +def compute_sketches(input_dir:str, skip_files:List[str]=['train_full.conllu'], verbose:bool=True) -> Tuple[List[str], int]: + ''' + Loads clauses from conllu files in the input_dir and computes syntax sketches + for all clauses that have a single root. + Assumes that all conllu files in the input_dir have been created via script + "01b_extract_clauses.py", that is, they contain clauses instead of sentences. + Optionally, you can skip some of the input files via parameter skip_files. + Returns tuple: (list_of_sketches, clauses_count_total) + ''' + # 1) Import data from conllu files, rename sentences -> clauses and validate + expected_layers = {'clauses', 'ud_syntax', 'words'} + whole_data = [] + for fname in os.listdir(input_dir): + if fname in skip_files: + continue + if fname.endswith('.conllu'): + text_obj = conll_to_text( os.path.join(input_dir, fname), + 'ud_syntax', + remove_empty_nodes=True) + text_obj.meta['file'] = fname + # Rename sentences layer + # (because it actually contains clauses, not sentences) + clauses_layer = text_obj.pop_layer('sentences') + clauses_layer.name = 'clauses' + text_obj.add_layer(clauses_layer) + # Validate text layers + assert text_obj.layers == expected_layers, \ + f'Unexpected layers {text_obj.layers!r}' + whole_data.append(text_obj) + # 2) Create sketches from the data + clauses_count_total = 0 + invalid_clauses_total = 0 + sketches = [] + for text_obj in whole_data: + clauses_count = 0 + for clause in text_obj.clauses: + cleaned_clause = clean_clause(clause) + if len(cleaned_clause['root_loc']) != 1: + # At this point, assuming input processed with + # "01b_extract_clauses.py", we actually should + # not encounter any invalid clauses ... + invalid_clauses_total += 1 + continue + sketches.append(syntax_sketch(cleaned_clause)) + clauses_count += 1 + if verbose: + print(text_obj.meta['file'], '|', f'#clauses: {clauses_count}') + clauses_count_total += clauses_count + if verbose: + print() + print(f'#clauses total: {clauses_count_total}') + if invalid_clauses_total > 0: + print(f'#invalid clauses total: {invalid_clauses_total}') + return sketches, clauses_count_total + + +# ===================================================== +# Distribute syntax sketches randomly into bins +# ===================================================== + +def rand_group_sketches(sketches: List[Union[str, List[Any]]], n:int, seed:int=5) \ + -> List[List[Union[str, List[Any]]]]: + ''' + Distributes given sketches randomly into n same size groups. + Returns list of lists of sketches, one sub list for each group. + ''' + result = [] + if not n <= len(sketches): + raise ValueError(f'(!) Number of sketches ({len(sketches)}) '+\ + f'is smaller than number of groups ({n}).') + rnd = Random(seed) + rnd.shuffle(sketches) + for i in range(n): + result.append([]) + for sid, sketch in enumerate(sketches): + result[sid % n].append(sketch) + assert len(sketches) == sum([len(g) for g in result]) + return result + + +# ===================================================== +# Filtering lists of clauses by sketches +# ===================================================== + +def extract_sketches(clause_conllus: List[str], clause_dicts: List[Dict[str, list]], + target_sketch:str, amount:Optional[int]=None, verbose:bool=False): + ''' + Extracts given amount of target_sketch from clause_conllus and clause_dicts. + Note that the extraction operation is virtual: the input lists clause_conllus + and clause_dicts are not affected. + Returns extracted items. + If amount is None (default), then extracts all clauses corresponding to the sketch. + Returns triple: (extracted_conllus, extracted_dicts, number_of_extracted_items) + ''' + assert len(clause_conllus) == len(clause_dicts), \ + 'Unexpectedly, numers of conllu clauses and corresponding clause dicts differ: '+\ + f' {len(clause_conllus)} vs {len(clause_dicts)}' + extracted = [] + extracted_dicts = [] + for clause_id, clause_conllu in enumerate(clause_conllus): + clause_dict = clause_dicts[clause_id] + sketch = syntax_sketch(clause_dict) + if sketch == target_sketch: + if amount is None or len(extracted) < amount: + extracted.append( clause_conllu ) + extracted_dicts.append( clause_dict ) + if verbose: + print('Extracted {} instances of sketch {}'. format(len(extracted), target_sketch)) + return extracted, extracted_dicts, len(extracted) + + +def remove_sketches(clause_conllus: List[str], clause_dicts: List[Dict[str, list]], + target_sketch:str, amount:Optional[int]=None, verbose:bool=False): + ''' + Removes given amount of target_sketch from clause_conllus and clause_dicts. + Note that the removal operation is virtual: the input lists clause_conllus and + clause_dicts are not affected. + Returns preserved items after removal (and count of removed items). + If amount is None (default), then removes all clauses corresponding to the sketch. + Returns triple: (preserved_conllus, preserved_dicts, number_of_removed_items) + ''' + assert len(clause_conllus) == len(clause_dicts), \ + 'Unexpectedly, numers of conllu clauses and corresponding clause dicts differ: '+\ + f' {len(clause_conllus)} vs {len(clause_dicts)}' + preserved = [] + preserved_dicts = [] + removed = 0 + for clause_id, clause_conllu in enumerate(clause_conllus): + clause_dict = clause_dicts[clause_id] + sketch = syntax_sketch(clause_dict) + if sketch == target_sketch: + if amount is None or removed < amount: + removed += 1 + continue + preserved.append( clause_conllu ) + preserved_dicts.append( clause_dict ) + if verbose: + print('Removed {} instances of sketch {}'. format(removed, target_sketch)) + return preserved, preserved_dicts, removed + + +def remove_sketches_group(clause_conllus: List[str], clause_dicts: List[Dict[str, list]], + target_sketches:List[str], verbose:bool=False): + ''' + Removes all target_sketches from clause_conllus and clause_dicts. + Note that the removal operation is virtual: the input lists clause_conllus and + clause_dicts are not affected. + Returns preserved items after the removal (and total count of removed items). + Returns triple: (preserved_conllus, preserved_dicts, number_of_removed_items) + ''' + assert len(clause_conllus) == len(clause_dicts), \ + 'Unexpectedly, numers of conllu clauses and corresponding clause dicts differ: '+\ + f' {len(clause_conllus)} vs {len(clause_dicts)}' + assert len(target_sketches) > 0, 'Unexpectedly, got an empty target_sketches list' + preserved = [] + preserved_dicts = [] + removed = 0 + target_sketches_set = set(target_sketches) + for clause_id, clause_conllu in enumerate(clause_conllus): + clause_dict = clause_dicts[clause_id] + sketch = syntax_sketch(clause_dict) + if sketch in target_sketches_set: + removed += 1 + continue + preserved.append( clause_conllu ) + preserved_dicts.append( clause_dict ) + if verbose: + print('Removed {} instances of sketches {}'. format(removed, target_sketches)) + return preserved, preserved_dicts, removed \ No newline at end of file