def save_model(model, path, tmp_files_path, new_model): """ Guarda el modelo en el path solicitado. :model: [SpacyModelRef] - Referencia a un modelo de spacy. :path: [String] - Ruta en la cual guardar el modelo. :tmp_files_path: [String] - Ruta donde se encuentran los archivos temporales del modelo. :new_model: [Model] - Objeto que representa el nuevo modelo. """ Logger.log('L-0030') base_path = build_path(MODEL_MANAGER_ROOT_DIR, path) if check_dir_existence(base_path): ErrorHandler.raise_error('E-0030') model_storage_path = get_absoulute_path(base_path) model.to_disk(model_storage_path) custom_model_files_path = build_path(base_path, MODEL_MANAGER_CUSTOM_FILES_DIR) create_dir_if_not_exist(custom_model_files_path) model_seed_path = build_path(tmp_files_path, TOKEN_RULES_GEN_MODEL_SEED_FILENAME) model_seed_copy_path = build_path(custom_model_files_path, TOKEN_RULES_GEN_MODEL_SEED_FILENAME, add_absolute_root=True) copy_file(model_seed_path, model_seed_copy_path, is_absolute_path=True) cfg_file_path = build_path(model_storage_path, MODEL_CONFIG_FILE_NAME) dictionary_to_disk(cfg_file_path, { 'model_name': new_model.get_model_name(), 'description': new_model.get_description(), 'author': new_model.get_author(), 'analyzer_rule_set': new_model.get_analyser_rules_set() }, True) Logger.log('L-0032')
def __create_custom_verb_token_rules(self, verb, base_verb, person, time_key, max_dist): """ A partir de un verbo conjugado, utiliza el modulo WordProcessor para deformarlo. Para cada deformación que cumpla con la distancia máxima deseada, se crea una regla. :verb: [String] - Cadena de caracteres con la forma "bien escrita" de la palabra a deformar. :base_verb: [String] - Forma base de la palabra (Ej. vende --> vender) :person: [int] - Indice de la persona (0 - 5) :time_key: [String] - Clave del tiempo verbal. :max_dist: Distancia de levenshtein máxima para que una variación de una palabra sea tomada como válida. """ Logger.log('L-0014', [{'text': verb, 'color': HIGHLIGHT_COLOR}]) fuzzy_token_set = self.__word_processor.get_fuzzy_set(verb, max_dist) tokenizer_exceptions_set = list([]) for fuzzy_token in fuzzy_token_set: token_exception = self.__get_verb_token_rule( fuzzy_token, base_verb, person, time_key) tokenizer_exceptions_set.append(token_exception) return tokenizer_exceptions_set
def approve_traning_examples(self, examples_id_list): """ Aprueba un conjunto de ejemplos de entrenamiento cuyos ids se encuentran en la lista de ids provista. Si alguno de los ids en la lista no existe la operación no se realizará para ningún ejemplo. :examples_id_list: [List(int)] - Lista con los ids de los ejemplos de entrenamiento a aprobar. :return: [List(dict)] - Listado indicando los ejemplos de entrenamiento que se han podido aceptar y los que no. """ Logger.log('L-0305') results = list([]) for example_id in examples_id_list: status = False error = None try: self.__train_data_manager.approve_example(example_id) status = True except Exception as e: error = ErrorHandler.get_error_dict(e) finally: results.append({ 'example_id': example_id, 'status': status, 'error': error }) Logger.log('L-0306') return results
def __create_task(self, task, is_able_to_start): """ Crea una tarea, la agrega al administrador y si es posible la inicializa. :task: [Task] - Tarea a crear. :is_able_to_start: [boolean] - Indica si la tarea se puede inicializar inmediatamente después de creada. """ try: self.__lock.acquire() Logger.log('L-0227') self.__active_tasks.append(task) task.add_observer(self) Logger.log('L-0228') running_tasks_count = len([ task for task in self.__active_tasks if task.get_task_status_data()['status'] == TASK_STATUS_RUNNING ]) if is_able_to_start and running_tasks_count <= MAX_CONCURRENT_TASKS and not task.is_alive( ): task.init() except: pass finally: self.__lock.release()
def discard_training_examples(self, examples_id_list): """ Rechaza el conjunto de ejemplos de entrenamiento que tienen los ids especificados en la lista de ids provista. Todos los ids provistos deben existir, de lo contrario, no se descartará ninguno. :examples_id_list: [List(int)] - Listado con los ids de los ejemplos a rechazar. :return: [List(dict)] - Listado indicando los ejemplos de entrenamiento que se han podido rechazar y los que no. """ Logger.log('L-0314') results = list([]) for example_id in examples_id_list: status = False error = None try: self.__train_data_manager.discard_example(example_id) status = True except Exception as e: error = ErrorHandler.get_error_dict(e) finally: results.append({ 'example_id': example_id, 'status': status, 'error': error }) Logger.log('L-0315') return results
def create_model_creation_task(self, model_id, model_name, description, author, tokenizer_exceptions, max_dist): """ Crea y agrega a la cola una nueva tarea de creación de modelo. :model_id: [String] - Id del modelo a crear. :model_name: [String] - Nombre del modelo a crear. :description: [String] - Descripcion del modelo a crear. :author: [String] - Autor del modelo a crear. :tokenizer_exceptions: [Dict] - Datos de las excepciones al tokenizer a aplicar al nuevo modelo. :max_dist: [int] - Distancia de demerau levenshtein máxima. :return: [int] - Id. de la tarea creada. """ Logger.log('L-0225') next_task_id = self.__get_next_task_id() new_task = ModelCreationTask(next_task_id, model_id, model_name, description, author, tokenizer_exceptions, max_dist) is_able_to_init = not self.__check_active_status_for_model( model_id, model_name) self.__create_task(new_task, is_able_to_init) Logger.log('L-0226') return next_task_id
def generate_model_data(self, model_seed, path, max_dist): """ A partir de una model_seed, crea los archivos de configuración para modificar el tokenizer de un modelo de spacy. :model_seed: [Dict] - Semilla para la creación del modelo. :base_path: [String] - Directorio base del modelo. :max_dist: [int] - Distancia de demerau levenshtein máxima para las deformaciones a los token. """ base_path = build_path(TOKEN_RULES_GEN_TMP_ROOT_PATH, path) if check_dir_existence(base_path): ErrorHandler.raise_error('E-0027') Logger.log('L-0003') create_dir_if_not_exist(base_path) self.__save_model_seed(model_seed, base_path) nouns_path = build_path(base_path, TOKEN_RULES_GEN_TYPE_NOUN) create_dir_if_not_exist(nouns_path) verbs_path = build_path(base_path, TOKEN_RULES_GEN_TYPE_VERB) create_dir_if_not_exist(verbs_path) self.__generate_noun_rules(model_seed['nouns'], max_dist, nouns_path) self.__generate_verb_rules(model_seed['verbs'], max_dist, verbs_path) Logger.log('L-0004') return get_absoulute_path(base_path)
def edit_model_data(self, model_id, new_model_name=None, new_description=None): """ Edita los datos de un modelo existente. Si el modelo no existe u ocurre algún error durante la edición de sus datos devolverá false. :model_id: [String] - Nombre actual del modelo. :new_model_name: [String] - Nuevo nombre a asignar al modelo. :new_description: [String] - Nueva descripción para el modelo. """ Logger.log('L-0074') current_model = self.__model_manager.get_model(model_id) if current_model is None: ErrorHandler.raise_error('E-0074') current_model_name = current_model.get_model_name() edited_model_name = new_model_name current_description = current_model.get_description() edited_description = new_description if new_model_name is None or new_model_name == '': edited_model_name = current_model_name if new_description is None or new_description == '': edited_description = current_description if edited_model_name == current_model_name and edited_description == current_description: ErrorHandler.raise_error('E-0075') self.__model_manager.edit_model(model_id, edited_model_name, edited_description)
def save_model_data(model_id, model_name, description, author, path, analyser_rules_set): """ Guarda información de un modelo. :model_id: [String] - Id del modelo. :model_name: [String] - Nombre del modelo (actua como id). :description: [String] - Descripción del modelo. :author: [String] - Nombre del creador del modelo. :path: [String] - Ruta relativa para encontrar el modelo. :analyser_rules_set: [List(Dict)] - Lista de reglas para el analizador """ Logger.log('L-0026') if ModelDataManager.check_existing_model(model_id): ErrorHandler.raise_error('E-0029') data_dict = { 'model_id': model_id, 'model_name': model_name, 'description': description, 'author': author, 'path': path, 'analyzer_rules_set': analyser_rules_set } db_insert_item(MODEL_MANAGER_DB, MODEL_MANAGER_MODELS_COLLECTION, data_dict) Logger.log('L-0029')
def import_model(model_id, source): """ Obtiene los archivos de modelo, los descomprime y obtiene el diccionario de configuración del modelo remoto solicitado. :model_id: [String] - Id del modelo a importar. :return: [Dict] - Dicionario con la configuración del modelo. """ try: local_path = '%s/%s/%s' % (CURRENT_BASE_PATH, MODEL_MANAGER_ROOT_DIR, model_id) model_cfg_file = '%s/%s' % (local_path, MODEL_CONFIG_FILE_NAME) if source['remote']: remote_repo_url = '%s/%s%s' % (source['path'], model_id, MODEL_IMPORT_EXT) Logger.log('L-0134', [{'text': remote_repo_url, 'color': HIGHLIGHT_COLOR}]) Repo.clone_from(remote_repo_url, local_path) else: Logger.log('L-0140', [{'text': source['path'], 'color': HIGHLIGHT_COLOR}]) copy_dir(source['path'], local_path) Logger.log('L-0147') Logger.log('L-0154') unzip_model(local_path, model_id) remove_files(local_path, '%s%s' % (model_id, MODEL_PACKAGING_EXTENSION)) remove_files(local_path, '%s' % MODEL_TMP_JOINT_FILE_NAME) Logger.log('L-0161') return load_json_file(model_cfg_file) except Exception as e: ErrorHandler.raise_error('E-0119', [{'text': e, 'color': ERROR_COLOR}])
def delete_model_files(path): """ Elimina los archivos de un modelo del disco. :path: [String] - Ruta a eliminar. """ Logger.log('L-0070') full_path = build_path(MODEL_MANAGER_ROOT_DIR, path) remove_dir(full_path)
def log_error(error_data, log_data): """ Loggea los datos de un error :error_data: [Dict] - Datos del error :log_data: [List] - Datos adicionales de log """ log_code = error_data['log'] Logger.log(log_code, log_data)
def get_available_models(self): """ Devuelve una lista con todos los modelos disponibles en el sistema (esten cargados o no). :return: [List] - Listado de los modelos disponibles en el sistema. """ if not self.__init_success: Logger.log('L-0095') return None return self.__model_manager.get_available_models_dict()
def run(self): """ Inicia la ejecución del thread """ Logger.log('L-0229', [{'text': self.__id, 'color': HIGHLIGHT_COLOR}]) self.__init_time = datetime.datetime.now() self.__status = TASK_STATUS_RUNNING self.task_init_hook() self.__end_time = datetime.datetime.now() self.__status = TASK_STATUS_FINISHED self.notify(self) Logger.log('L-0230')
def __init(self): """ Inicializa el módulo. """ Logger.log('L-0243') self.__init_success = False self.__model_trainer = ModelTrainerManager() self.__model_manager = ModelManagerController() self.__model_manager.add_observer(self) available_models = self.__model_manager.get_available_models() self.__train_data_manager = TrainDataManager(available_models) Logger.log('L-0245') self.__init_success = True
def add_ner_labels(ner, training_data): """ Agrega las nuevas etiquetas al ner. :ner: [SpacyNER] - NER al cual aplicar las nuevas etiquetas :training_data: [List(Dict)] - Set de datos de entrenamiento. """ Logger.log('L-0345') for _, annotations in training_data: for ent in annotations.get('entities'): ner.add_label(ent[2]) Logger.log('L-0346')
def load_model(self, model_id): """ Carga un modelo en memoria para poder tener un acceso más rapido al mismo. Solo se aconseja su uso para la realización de pruebas. :model_id: [String] - Nombre del modelo a cargar. :return: [bool] - True si el modelo ha sido exitosamente cargado, False en caso contrario. """ if not self.__init_success: Logger.log('L-0096') return None return self.__model_manager.load_model(model_id)
def get_examples_history(self, model_id): """ Retorna un listado con todos los ejemplos, sin importar su estado, para un determinado modelo. :model_id: [String] - Id del modelo. :return: [List] - Listado de todos los ejemplos para el modelo solicitado. """ if not self.__find_model(model_id): ErrorHandler.raise_error('E-0082') results = list([]) Logger.log('L-0326') examples_data = db_get_items(TRAIN_MANAGER_DB, TRAIN_DATA_EXAMPLES_COLLECTION, {'model_id': model_id}) Logger.log('L-0327') Logger.log('L-0328') for example_data in examples_data: example = TrainExample(example_data['example_id'], example_data['sentence'], example_data['tags'], example_data['type'], example_data['status']) results.append(example) Logger.log('L-0329') return results
def discard_example(self, example_id): """ Rechaza el ejemplo de entrenamiento. El ejemplo solicitado debe existir y no estar ya rechazado o aplicado. :example_id: [int] - Id del ejemplo de entrenamiento. """ Logger.log('L-0316', [{'text': example_id, 'color': HIGHLIGHT_COLOR}]) example = self.__find_example(example_id) if example is None: ErrorHandler.raise_error('E-0104') if example.get_status() != TRAIN_EXAMPLE_STATUS_SUBMITTED: ErrorHandler.raise_error('E-0105') Logger.log('L-0318') updated_items = db_update_item( TRAIN_MANAGER_DB, TRAIN_DATA_EXAMPLES_COLLECTION, { 'example_id': example_id }, { 'status': TRAIN_EXAMPLE_STATUS_REJECTED }).modified_count if updated_items <= 0: ErrorHandler.raise_error('E-0106') Logger.log('L-0319') example.reject() Logger.log('L-0320', [{'text': example_id, 'color': HIGHLIGHT_COLOR}])
def import_model(self, model_id, source=None): """ Importa un modelo existente desde el repositorio de modelos. No debe existir un modelo local con dicho id y, además, el módelo debe existir en el repositorio remoto de modelos. :model_id: [String] - Id del modelo a importar. :source: [Dict] - Fuente de donde obtener el modelo, puede ser un repositorio git o un directorio local. """ Logger.log('L-0116') model = self.get_model(model_id) if model: ErrorHandler.raise_error('E-0118') if not source: source = REMOTE_MODEL_SOURCE cfg = ModelLoader.import_model(model_id, source) analyzer_exceptions_set_data = cfg['analyzer_exceptions_set'] Logger.log('L-0179') ModelDataManager.save_model_data(model_id, cfg['model_name'], cfg['description'], cfg['author'], model_id, cfg['analyzer_rules_set']) remote_model = Model(model_id, cfg['model_name'], cfg['description'], cfg['author'], model_id, cfg['analyzer_rules_set'], []) self.__models.append(remote_model) for exception in analyzer_exceptions_set_data: self.add_analyzer_exception(model_id, exception['base_form'], exception['token_text'], exception['enabled']) Logger.log('L-0188') Logger.log('L-0197')
def __build_annotations(self, examples): """ Crea el listado de ejemplos a partir de los ejemplos de entrenamiento. :examples: [List(TrainExample)] - Lista de ejemplos de entrenamiento. :return: [List] - Listado con la anotaciones. """ Logger.log('L-0338') annotations = list([]) for training_example in examples: annotations.append(training_example.get_annotations()) Logger.log('L-0339') return annotations
def edit_custom_tag_entity(self, name, description): """ Edita un tag personalizado. El tag debe existir, solamente se puede modificar la descripción. :name: [String] - Nombre del tag a actualizar. :description: [String] - Descripción actualizada del tag. """ Logger.log('L-0284') entity = self.__get_entity(name) if entity is None: ErrorHandler.raise_error('E-0101') if entity.get_description() == description: ErrorHandler.raise_error('E-0102') Logger.log('L-0287') updated_entries = db_update_item(TRAIN_MANAGER_DB, CUSTOM_ENTITY_MANAGER_COLLECTION, { 'name': name }, { 'description': description }).modified_count if updated_entries <= 0: ErrorHandler.raise_error('E-103') Logger.log('L-0288') entity.set_description(description) Logger.log('L-0290')
def approve_example(self, example_id): """ Aprueba el ejemplo de entrenamiento identificado on el id solicitado. El ejemplo debe existir y no estar ya aprobado o aplicado. :example_id: [int] - Id del ejemplo de entrenamiento. """ Logger.log('L-0307', [{'text': example_id, 'color': HIGHLIGHT_COLOR}]) example = self.__find_example(example_id) if example is None: ErrorHandler.raise_error('E-0077') if example.get_status() != TRAIN_EXAMPLE_STATUS_SUBMITTED: ErrorHandler.raise_error('E-0078') example_id = example.get_example_id() Logger.log('L-0309') updated_items = db_update_item( TRAIN_MANAGER_DB, TRAIN_DATA_EXAMPLES_COLLECTION, { 'example_id': example_id }, { 'status': TRAIN_EXAMPLE_STATUS_APPROVED }).modified_count if updated_items <= 0: ErrorHandler.raise_error('E-0079') Logger.log('L-0310') example.approve() Logger.log('L-0311', [{'text': example_id, 'color': HIGHLIGHT_COLOR}])
def create_model_training_task(self, model_id): """ Crea y agrega a la cola una nueva tarea de entrenamiento de modelo. :model_id: [String] - Id del modelo a utilizar. :return: [int] - Id. de la tarea creada. """ Logger.log('L-0231') next_task_id = self.__get_next_task_id() new_task = ModelTrainingTask(next_task_id, model_id) is_able_to_init = not self.__check_active_status_for_model(model_id) self.__create_task(new_task, is_able_to_init) Logger.log('L-0232') return next_task_id
def edit_model(self, model_id, model_name, description): """ Permite editar la descripción de un modelo. El modelo debe existir. :model_id: [String] - Id del modelo. :model_name: [String] - Nuevo nombre para el modelo. :description: [String] - Nueva descripción para el modelo. """ selected_model = self.get_model(model_id) ModelDataManager.modify_model_data(model_id, model_name, description) selected_model.set_model_name(model_name) selected_model.set_description(description) Logger.log('L-0082')
def __apply_tokenizer_exceptions(self, model, tokenizer_exceptions_path): """ Aplica todas las excepciones contenidas en el directorio de excepciones al modelo. :model: [SpacyModelRef] - Modelo sobre el cual aplicar las excepc¡ones. :tokenizer_exceptions_path: [String] - Ruta al directorio de excepciones para el modelo. """ Logger.log('L-0023') tokenizer_exceptions_files = get_files_in_dir(tokenizer_exceptions_path, TOKEN_RULES_GEN_RULES_EXT) for source_file in tokenizer_exceptions_files: rule_set = load_json_file(source_file) for key in rule_set: model.add_tokenizer_rule_set(rule_set[key]) Logger.log('L-0024')
def remove_model_data(model_id): """ Elimina la entrada para un modelo. :model_id: [String] - Id del modelo a eliminar. """ Logger.log('L-0066') model_delete_count = db_delete_item(MODEL_MANAGER_DB, MODEL_MANAGER_MODELS_COLLECTION, { 'model_id': model_id }).deleted_count if model_delete_count <= 0: ErrorHandler.raise_error('E-0072') db_delete_items(MODEL_MANAGER_DB, ANALYZER_EXCEPTIONS_COLLECTION, {'model_id': model_id})
def add_training_examples(self, model_id, examples_list): """ Agrega una lista de ejemplos de entrenamiento. Para que la operación sea exitosa todos los ejemplos deben poder ser validados correctamente, en caso contrario no se añadirá ninguno. :model_id: [String] - Id del modelo al cual se aplicará el ejemplo. :examples_list: [List(Dict)] - Listado de ejemplos de entrenamiento. """ Logger.log('L-0292') model = self.__model_manager.get_model(model_id) if model is None: ErrorHandler.raise_error('E-0084') self.__train_data_manager.add_training_examples( model_id, examples_list) Logger.log('L-0295')
def get_model_ner(model_ref): """ Obtiene el NER de un determinado modelo. Si el mismo no existe, crea uno nuevo y lo agrega al pipeline del modelo. :model_reference: [SpacyModelRef] - Referencia al modelo de spacy """ Logger.log('L-0343') ner = None if MODEL_NER in model_ref.pipe_names: ner = model_ref.get_pipe(MODEL_NER) else: ner = model_ref.create_pipe(MODEL_NER) model_ref.add_pipe(ner, last=True) Logger.log('L-0344') return ner
def __initialize(self, is_retry=False): """ Inicializa el modulo. Si la inicialización es exitosa, setea el atributo ready a True. En caso contrario se pasará (si no estuviese ya) a False :is_retry: [Bool] - Indica si es un reintento de inicialización """ Logger.log('L-0036') self.__init_success = False self.__word_processor = WordProcessorController() if is_retry: self.__word_processor.retry_initialization() self.__tokenizer_rules_generator = TokenizerRulesGenerator() self.__model_manager = ModelManagerController() self.__train_manager = ModelTrainingController() self.__analyzer_rules_generator = AnalyzerRulesGerator() self.__init_success = True Logger.log('L-0037')