Как улучшить модель классификации немецкого текста в spaCy - PullRequest
0 голосов
/ 29 мая 2020

Я работаю над проектом классификации текста и использую для этого spacy. Сейчас у меня точность почти 70%, но этого недостаточно. Я пытался улучшить модель последние две недели, но пока безуспешных результатов. И тут я ищу совета, что мне делать или пробовать. Любая помощь будет высоко ценится!

Итак, вот что я делаю до сих пор:

1) Подготовка данных:

У меня несбалансированный набор данных немецких новостей с 21 категории (например, POLITICS, ECONOMY, SPORT, CELEBRITIES et c). Чтобы сделать категории равными, я дублирую небольшие классы. В результате у меня есть 21 файл с почти 700 000 строками текста. Затем я нормализую эти данные, используя следующий код:

import spacy
from charsplit import Splitter

POS = ['NOUN', 'VERB', 'PROPN', 'ADJ', 'NUM']  # allowed parts of speech

nlp_helper = spacy.load('de_core_news_sm')
splitter = Splitter()

def normalizer(texts):
    arr = []  # list of normalized texts (will be returned from the function as a result of normalization)

    docs = nlp_helper.pipe(texts)  # creating doc objects for multiple lines
    for doc in docs:  # iterating through each doc object
        text = []  # list of words in normalized text
        for token in doc:  # for each word in text
            token = token.lemma_.lower()

            if token not in stop_words and token.pos_ in POS:  # deleting stop words and some parts of speech
                if len(word) > 8 and token.pos_ == 'NOUN':  # only nouns can be splitted
                    _, word1, word2 = splitter.split_compound(word)[0]  # checking only the division with highest prob
                    word1 = word1.lower()
                    word2 = word2.lower()
                    if word1 in german and word2 in german:
                        text.append(word1)
                        text.append(word2)
                    elif word1[:-1] in german and word2 in german:  # word[:-1] - checking for 's' that joins two words
                        text.append(word1[:-1])
                        text.append(word2)
                    else:
                        text.append(word)
                else:
                    text.append(word)
        arr.append(re.sub(r'[.,;:?!"()-=_+*&^@/\']', ' ', ' '.join(text))) # delete punctuation
    return arr

Некоторые пояснения к приведенному выше коду:

POS - список разрешенных частей речи. Если слово, с которым я работаю в данный момент, является частью речи, которой нет в этом списке -> я удаляю ее.

stop_words - просто список слов, которые я удаляю.

splitter.split_compound(word)[0] - возвращает кортеж с наиболее вероятным разделением составного слова (я использую его для разделения длинных немецких слов на более короткие и широко используемые). Вот ссылка на репозиторий с этой функциональностью.

Подводя итог: я нахожу лемму слова, делаю его строчными буквами, удаляю стоп-слова и некоторые части речи, делю сложные слова, удалите знаки препинания. Затем я соединяю все слова и возвращаю массив нормализованных строк.

2) Обучение модели

Я обучаю свою модель, используя de_core_news_sm (чтобы сделать это возможным в будущем использовать эту модель не только для классификации, но и для нормализации). Вот код для обучения:

nlp = spacy.load('de_core_news_sm')

textcat = nlp.create_pipe('textcat', config={"exclusive_classes": False, "architecture": 'simple_cnn'})
nlp.add_pipe(textcat, last=True)
for category in categories:
    textcat.add_label(category)

pipe_exceptions = ["textcat"]
other_pipes = [pipe for pipe in nlp.pipe_names if pipe not in pipe_exceptions]

with nlp.disable_pipes(*other_pipes):
    optimizer = nlp.begin_training()

    for i in range(n_iter):
        shuffle(data)
        batches = spacy.util.minibatch(data)

        for batch in batches:
            texts, annotations = zip(*batch)
            nlp.update(texts, annotations, sgd=optimizer, drop=0.25)

Некоторые пояснения к приведенному выше коду:

data - список списков, где каждый список включает строку текста и словарь с категориями (как и в docs )

'Categories' - список категорий

'n_iter' - количество итераций для обучения

3) В конце я просто сохраняю модель методом to_disk.

С помощью приведенного выше кода мне удалось обучить модель с точностью 70%. Вот список того, что я пытался улучшить до сих пор:

1) Использование другой архитектуры (ensemble) - не дало никаких улучшений

2 ) Обучение на ненормализованных данных - результат был намного хуже

3) Используя предварительно обученную модель BERT - не смог ( здесь - мой вопрос без ответа по этому поводу)

4) Обучение de_core_news_md вместо de_core_news_sm - не дало никаких улучшений (пробовал, потому что согласно docs может быть улучшение благодаря векторам (если я правильно понял). Поправьте меня, если я ошибаюсь)

5) Обучение на данных, нормализованных несколько иначе (без нижнего регистра и удаления знаков препинания) - улучшений не дало

6) Изменение выпадение - не помогло

Итак, сейчас я немного не знаю, что делать дальше. Буду очень признателен за любую подсказку или совет.

Заранее спасибо за помощь!

1 Ответ

2 голосов
/ 29 мая 2020

Первое, что я хотел бы предложить, это увеличить размер вашей партии. После этого ваш оптимизатор (если возможно, Адам) и скорость обучения, для которой я не вижу здесь кода. Наконец, вы можете попробовать изменить отсев.

Кроме того, если вы пытаетесь использовать нейронные сети и планируете многое изменить, было бы лучше, если бы вы могли переключиться на PyTorch или TensorFlow. В PyTorch у вас будет библиотека HuggingFace, в которую встроен BERT.

Надеюсь, это вам поможет!

...