def pp_from_raw_generator(raw_file: str, chunk_size: int, **pp_settings) -> pd.DataFrame: """ Генератор предобработанных текстов :param raw_file: путь к файлу исходных текстов :param chunk_size: размер чанка для предобработки :param pp_settings: настройки препроцессора """ pp = Preprocessor() for n, chunk in enumerate( pd.read_csv(raw_file, encoding="cp1251", quoting=3, sep="\t", chunksize=chunk_size)): try: pp_chunk = pp.preprocess_dataframe(df=chunk, **pp_settings) except Exception as e: logger = get_logger("ecs.data_tools.pp_from_raw_generator") error_ps( logger, f"Error occurred during preprocessing chunk {n} of file {raw_file}: {e}" ) exit(1) else: yield pp_chunk
def __init__(self, preproc: str, language: str): self.language = language self.preproc = preproc self.map_config = ConfigParser() self.map_config.read_file(open(join(dirname(__file__), "map.ini"), 'r')) self.logger = get_logger("ecs.Preprocessor2.Normalizer") section = f"Supported{self.language.capitalize()}Models" if section not in self.map_config.keys(): raise ValueError( 'Preprocessing is not supported for this language') if self.preproc != "no": if self.preproc not in list(self.map_config[section].keys()): raise ValueError( f'The preprocessing technique is not supported for this language' ) components = self.map_config['Supported' + self.language.capitalize() + 'Models'][self.preproc].split('.') module_name = ".".join(components[:-1]) try: module = import_module(module_name) self.class_type = getattr(module, components[-1]) except ImportError: self.logger.error(f"Package '{module_name}' is not installed!") exit(1) else: self.class_type = None
def preprocess(self, text: str, remove_stopwords: bool, remove_formulas: bool, normalization: str, language="auto", default_lang="error") -> str: """ Предобработка одного текста. ---------------------------- Аргументы: text: Строка с текстом для предобработки. remove_stopwords: Удалять стоп-слова. Возможные варианты: [True, False]. remove_formulas: Удалять TeX-формулы, заключенные в $...$ Формулы вида $$...$$ удаляются всегда. Возможные варианты: [True, False]. normalizaion: Метод нормализации слова. Возможные варианты: ["no", "lemmatization", "stemming"]. language: Язык текста. По умолчанию язык определяется автоматически. Возможные варианты: ["auto", "ru", "en"]. Автоопределение занимает время (особенно на больших текстах), поэтому лучше задать определенный язык. default_lang: Действия в случае несоответствия текста ни одному из языков. Аргумент используется только при language="auto". Варианты: - "random": Попробовать угадать язык случайным образом - "error" : Вызвать ошибку - "none" : Вернуть None (default) """ lang = language if language == "auto": lang = self.recognize_language(text.lower(), default_lang) if lang is None: raise ValueError("Unable to recognize language!") self.last_language = lang res = text res = self.__remove_email(res) if remove_formulas: res = self.__remove_single_formulas(res) res = self.__remove_md(res) if remove_stopwords: res = self.__dense(res) res = self.__remove_stopwords(res.lower(), lang) res = self.__beautify(res) if normalization != "no": try: res = Normalizer(normalization, lang).normalize(res) res = re.sub(" ".join(self.delim).strip(), self.delim, res) except ValueError: logger = get_logger("ecs.Preprocessor2.preprocess") logger.info(f"\n{lang_norm_hint(lang, normalization)}") exit() return res
def __trace_time(self, description: str): if self.DEBUG: if description.lower() == "init": self.timer.start() # print("Time recording started") else: logger = get_logger("ecs.Preprocessor2.__trace_time") logger.info(description, "has taken {} sec".format(self.timer.check()))
def vectorize_text(text: str, w2v_model: Word2Vec, conv_type: str) -> np.ndarray: tokens = text.split() text_matrix = [] for word in tokens: try: word_vector = w2v_model[word] except KeyError: word_vector = np.zeros(w2v_model.vector_size) text_matrix.append(word_vector) text_matrix = np.vstack(text_matrix) if conv_type == "sum": return text_matrix.sum(axis=0) elif conv_type == "mean": return text_matrix.mean(axis=0) elif conv_type == "max": return text_matrix.max(axis=0) else: get_logger("ecs.data_tools.vectorize_text").error( f"Conv type '{conv_type}' is not supported") exit(1)
def aggregate_full_dataset(vector_gen) -> pd.DataFrame: logger = get_logger("ecs.data_tools.aggregate_full_dataset") rubricators = ["subj", "ipv", "rgnti"] full_df = pd.DataFrame(columns=["vectors", *rubricators]) try: for chunk in vector_gen: chunk[rubricators] = chunk[rubricators].astype(str) full_df = pd.concat([full_df, chunk], ignore_index=True) except Exception as e: error_ps(logger, f"Error occurred during loading the dataset in memory: {e}") exit(1) return full_df
def create_report(model, x_test: np.ndarray, y_test: list, ignore_rubrics=()): # Для обратной совместимости используется старый код logger = get_logger("ecs.model_tool.create_report") pred = [] try: for p in model.predict_proba(x_test): all_prob = pd.Series(p, index=model.classes_) for code in ignore_rubrics: try: all_prob.drop(code) except KeyError: pass pred.append(list(all_prob.sort_values(ascending=False).index)) except Exception as e: error_ps(logger, f"Error occurred during model testing: {e}") exit(1) else: return count_stats(predicts=pred, y_test=y_test, amounts=[1, 2, 3, 4, 5, -1])
def create_w2v(pp_sources: list, vector_dim: int, window_size: int, train_alg: str) -> Word2Vec: """ Создать модель Word2Vec для предобработанных текстов. :param window_size: размер окна контекста :param vector_dim: размерность векторной модели :param train_alg: тип алгоритма обучения word2vec (cbow или skip-gram) :param pp_sources: список инстансов генератора предобработанных данных (pp_generator или caching_pp_generator) :returns обученная модель Word2Vec """ train_algoritm = { "cbow": 0, "skip-gram": 1 } # to transform into word2vec param logger = get_logger("ecs.data_tools.create_w2v") w2v = Word2Vec(size=vector_dim, min_count=3, workers=3, window=window_size, sg=train_algoritm[train_alg]) init = False for n_source, pp_source in enumerate(pp_sources, start=1): info_ps(logger, f"Training W2V on source {n_source}/{len(pp_sources)}") for n_chunk, pp_chunk in enumerate(pp_source, start=1): try: if n_chunk % N_CHUNKS_INTERVAL == 0: info_ps(logger, f"\nChunk {n_chunk}") sentence = [text.split() for text in pp_chunk["text"].values] w2v.build_vocab(sentence, update=init) # TODO: вынести количество эпох в параметры w2v.train(sentence, epochs=20, total_examples=len(sentence)) init = True except Exception as e: error_ps( logger, f"An error occurred during training W2V: {e}. Source {n_source}, chunk {n_chunk} " f"(~{(n_chunk - 1) * len(pp_chunk) + 1}{n_chunk * len(pp_chunk)} " f"lines in clear file)") exit(1) return w2v
def run_grid_search(model_instance, hyperparameters: dict, x_train: list, y_train: list, binary: bool, n_folds: int, n_jobs: int) -> dict: """ Запустить поиск по сетке параметров для модели для поиска лучшей комбинации, чтобы затем обучить ее на полной выборке без CV :param model_instance: инстанс модели, например MLPClassifier() :param hyperparameters: словарь гиперпараметров, где ключи - названия из документации, а значения - списки значений гиперпараметров :param x_train: array-like список обучающих векторов :param y_train: array-like список меток :param binary: использовать ли OneVsAll :param n_folds: количество фолдов кросс-валидации :param n_jobs: количество параллельных потоков :return: словарь лучших параметров в совместимом формате """ # TODO: Возможно, стоит добавить таймер scoring = 'f1_weighted' skf = StratifiedKFold(shuffle=True, n_splits=n_folds) hypers_copy = hyperparameters.copy() logger = get_logger("ecs.model_tools.run_grid_search") if binary: model_instance = OneVsRestClassifier(model_instance) for key in hyperparameters: hypers_copy["estimator__" + key] = hypers_copy.pop(key) grid_searcher = GridSearchCV(estimator=model_instance, param_grid=hypers_copy, n_jobs=n_jobs, scoring=scoring, cv=skf, verbose=0) info_ps(logger, "Fitting grid-searcher...") grid_searcher.fit(x_train, y_train) best_params = grid_searcher.best_params_ # # Убираем "estimator__" из ключей # best_params = {} # for key, value in grid_searcher.best_params_.items(): # best_params[key.split("estimator__")[-1]] = value return best_params
def create_clear_generators(base_dir: str, clear_filter: dict, chunk_size: int, training_fpath: str, test_fpath: str, experiment_title: str, pp_params: dict) -> dict: """ Создает подходящий генератор чистых текстов. Если найден кэш, то создает читающий генератор, если нет - предобрабатывает сырые данные :param base_dir: базовая директория для поиска кэша :param clear_filter: словарь с настройками препроцессора для поиска подходящего кэша :param chunk_size: размер чанка :param training_fpath: путь к сырому обучающему файлу :param test_fpath: путь к сырому тест-файлу (может быть равен '') :param experiment_title: название эксперимента :param pp_params: словарь с полными настройками препроцессора :return: словарь вида {'train': генератор, ['test': генератор]} """ try: pp_sources = {} cached_clear = find_cached_clear(base_dir=base_dir, metadata_filter=clear_filter) clear_file_list = [] # Если мы нашли кэш, список станет непустым # А если нет, то мы не зайдем в условие if len(cached_clear) > 0: # Если нашли больше, чем одну папку с чистыми текстами, # берем случайную _, clear_file_list = cached_clear.popitem() # Если список был пустым, то find_cache_for source # вернет пустую строку и будет создан кэширующий генератор train_pp_cache = find_cache_for_source(clear_file_list, training_fpath) if train_pp_cache: pp_sources[train_pp_cache] = pp_from_csv_generator( train_pp_cache, chunk_size) else: pp_sources[training_fpath] = create_caching_pp_gen( raw_path=training_fpath, exp_name=experiment_title, chunk_size=chunk_size, pp_params=pp_params) # Тут возможно 2 ситуации: # 1. Тестового файла не предусмотрено (ничего не делать) # 2. Тестовый кэш не найден (создать кэширующий генератор) if test_fpath: test_pp_cache = find_cache_for_source(clear_file_list, test_fpath) if test_pp_cache: pp_sources[test_pp_cache] = pp_from_csv_generator( test_pp_cache, chunk_size) else: pp_sources[test_fpath] = create_caching_pp_gen( raw_path=test_fpath, exp_name=experiment_title, chunk_size=chunk_size, pp_params=pp_params) return train_test_match(pp_sources, train_raw_path=training_fpath, test_raw_path=test_fpath) except Exception as e: logger = get_logger("ecs.create_clear_generators") error_ps(logger, f"Error occurred during creation of clear generators: {e}") exit(1)
def remove_empty_items(lst: list): try: return list(filter(lambda x: len(x.strip()) > 0, lst)) except AttributeError: logger = get_logger("ecs.Preprocessor2.remove_empty_items") logger.error(f"Error occurred during processing the list {lst}")
import warnings warnings.filterwarnings("ignore") import sys from ECS.interface.logging_tools import get_logger val_logger = get_logger("ecs.validation") # Функции для парсинга типов данных настроек # (См. докстринг ValidConfig) def is_int(value: str) -> bool: try: int(value) return True except ValueError: return False def is_float(value: str) -> bool: try: float(value) return True except ValueError: return False def is_bool(value: str) -> bool: return value.lower() in ["true", "false"]