Вопрос или проблема
Я реализовал пользовательское NER с обученными данными ниже в первый раз, и это даёт мне хорошие прогнозы по Имени и НазваниеПродукта. Я указал код ниже.
if __name__ == '__main__':
TRAIN_DATA = [
('Меня зовут Раджеш', {'entities': [(11, 17, 'Имя')]}),
('Меня зовут Бауль', {'entities': [(11, 16, 'Имя')]}),
('Меня зовут Притам', {'entities': [(11, 17, 'Имя')]}),
('Меня зовут Ракеш', {'entities': [(11, 17, 'Имя')]}),
('Меня зовут Джаита', {'entities': [(11, 18, 'Имя')]}),
('это цена сумки', {'entities': [(21, 24, 'НазваниеПродукта')]}),
('какова цена мяча?', {'entities': [(21, 25, 'НазваниеПродукта')]}),
('какова цена леггинсов?', {'entities': [(21, 28, 'НазваниеПродукта')]}),
('какова цена футболки?', {'entities': [(21, 28, 'НазваниеПродукта')]}),
]
iterations = 20
try:
model="live_ner_model"
nlp = spacy.load(model) # загрузить существующую модель spacy
except:
model = None
print("Исключение")
nlp = spacy.blank('en') # создать пустой класс языка
print("Создана пустая модель 'en'")
if 'ner' not in nlp.pipe_names:
ner = nlp.create_pipe('ner')
nlp.add_pipe(ner)
print("Создан NER")
else:
ner = nlp.get_pipe('ner')
print("Существующий NER")
# Добавить новые метки сущностей в распознаватель сущностей
for _, annotations in TRAIN_DATA:
for ent in annotations.get('entities'):
ner.add_label(ent[2])
# получить имена других трубопроводов, чтобы отключить их во время обучения
other_pipes = [pipe for pipe in nlp.pipe_names if pipe != 'ner']
with nlp.disable_pipes(*other_pipes): # обучать только NER
optimizer = nlp.begin_training()
for itn in range(iterations):
print("Начата итерация " + str(itn))
random.shuffle(TRAIN_DATA)
losses = {}
for text, annotations in TRAIN_DATA:
nlp.update(
[text], # партия текстов
[annotations], # партия аннотаций
drop=0.2, # дроп-аут - усложняет запоминание данных
sgd=optimizer, # вызываемая для обновления весов
losses=losses)
print(losses)
# Сохранить модель
output_dir="live_ner_model"
if output_dir is not None:
output_dir = Path(output_dir)
if not output_dir.exists():
output_dir.mkdir()
nlp.meta['name'] = model # переименовать модель
nlp.to_disk(output_dir)
print("Модель сохранена в", output_dir)
# Протестировать сохранённую модель
output_dir="live_ner_model"
print("Загрузка из", output_dir)
nlp2 = spacy.load('live_ner_model')
test_text = """
какова цена чашки. Меня зовут Рахим
"""
doc2 = nlp2(test_text)
for ent in doc2.ents:
print(ent.label_, ent.text)
Но когда я пытаюсь обучить с некоторыми новыми данными, которые содержат сущность только с НазваниеПродукта или любую другую новую сущность, исключая Имя в существующей модели.
Тогда предсказание сущности Имя идет не так. Я думаю, что эта проблема возникает, когда я обновил обученные данные, исключив Имя
сущность.
Есть ли способ, как мы можем улучшить обучение, не затрагивая существующее обучение? Может кто-то поделиться идеей? Если возможно, пожалуйста, поделитесь образцом кода.
Среда: Anaconda, spacy=v2.0.1, python=3.7
Модель полностью зависит от обучающих данных: если вы обучаете на данных, в которых только PrdName является меткой, модель знает только эту метку и может предсказывать только эту метку. Вам нужно предоставить как можно больше обучающих данных, содержащих все возможные метки.
Для справки, NER обычно обучаются с тысячами предложений, чтобы учесть разнообразие случаев, когда может появиться NE.
Ответ или решение
Для улучшения обучения существующей модели Named Entity Recognition (NER) в Spacy 2.0.1 и обеспечения ее надежности при добавлении новых сущностей, таких как PrdName
, без ущерба для уже имеющихся сущностей, таких как Name
, необходимо следовать нескольким ключевым стратегиям.
1. Поддержание Полноты Данных
Первое и, возможно, самое важное правило заключается в том, чтобы ваше обучающее множество данных всегда включало примеры для всех сущностей, которые вы хотите распознавать. Если вы обучаете модель только на новых данных, содержащих одну сущность, она может потерять способность правильно определять существующие сущности. Следовательно, обновляя данные, необходимо учитывать все уже существующие сущности, добавляя не только новые примеры для PrdName
, но и примеры для Name
.
Пример Дополнения Данных
NEW_TRAIN_DATA = [
('My Name is Rajesh', {'entities': [(11, 17, 'Name')]}),
# Ваши новые примеры для PrdName
('this is the price of cup', {'entities': [(21, 24, 'PrdName')]}),
('My Name is Rahim', {'entities': [(11, 17, 'Name')]}),
# Включите другие примеры для обоих типов сущностей
]
2. Использование Рыночных Правил
При использовании метода nlp.update
, убедитесь, что вы применяете регулярные обновления к существующим данным. Это помогает модели не "забывать" ранее обученные сущности. Используйте технику, известную как "тренировка на смешанном наборе данных", где вы чередуете примеры для различных сущностей.
3. Контроль Параметров Обучения
В Spacy параметры, такие как drop
(дроп-аут) и sgd
(обновления весов), могут быть отрегулированы для улучшения обучения. Попробуйте поиграть с коэффициентом дроп-аутов, так как это может помочь модели сохранять более высокую обобщающую способность.
4. Подход с Использованием Регуляризации
Вы можете применять регуляризацию во время обучения, что может помочь с проблемами переноса обучения, особенно если у вас ограниченный набор данных. Например, попробуйте уменьшить скорость обучения, чтобы модель делала более мелкие шаги в корректировке своих весов.
Пример Кода
Вот обновленный фрагмент кода, который объединяет различные советы.
import spacy
import random
from pathlib import Path
if __name__ == '__main__':
TRAIN_DATA = [...] # Объедините данные, которые охватывают все сущности
iterations = 20
try:
model = "live_ner_model"
nlp = spacy.load(model)
except Exception as e:
model = None
nlp = spacy.blank('en')
if 'ner' not in nlp.pipe_names:
ner = nlp.create_pipe('ner')
nlp.add_pipe(ner)
else:
ner = nlp.get_pipe('ner')
# Добавление новых меток сущностей
for _, annotations in TRAIN_DATA:
for ent in annotations.get('entities'):
ner.add_label(ent[2])
other_pipes = [pipe for pipe in nlp.pipe_names if pipe != 'ner']
with nlp.disable_pipes(*other_pipes):
optimizer = nlp.begin_training()
for itn in range(iterations):
random.shuffle(TRAIN_DATA)
losses = {}
for text, annotations in TRAIN_DATA:
nlp.update(
[text],
[annotations],
drop=0.5, # Измените дроп-аут по мере необходимости
sgd=optimizer,
losses=losses
)
print(losses)
nlp.to_disk(Path("live_ner_model"))
Заключение
Внедрение вышеизложенных методов поможет вам улучшить качество обучения вашей модели NER без потери точности уже обученных сущностей. Основная идея заключается в обеспечении многообразия и полноты обучающих данных, а также оптимизации процесса обучения. Поддержание этого баланса между старым и новым – ключ к успешному обновлению вашей модели.