Я работаю над проектом классификации текста и использую для этого 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) Изменение выпадение - не помогло
Итак, сейчас я немного не знаю, что делать дальше. Буду очень признателен за любую подсказку или совет.
Заранее спасибо за помощь!