Я создаю новую модель для распознавания именованных объектов. У меня есть тренировочный набор данных, который выглядит следующим образом:
[[
"world leading global energy trading company has an exciting
opportunity for a senior csharp application platform developer to
join its systematic trading division developing new trading
applications tools and solutions for its successful front office
trading team",
{"entities": [[80, 86, "RNK"]]}
]]
Поэтому я запускаю следующую функцию для обучения модели.
@plac.annotations(
model=("Model name. Defaults to blank 'en' model.", "option", "m",
str),
new_model_name=("New model name for model meta.", "option", "nm",
str),
output_dir=("Optional output directory", "option", "o", Path),
n_iter=("Number of training iterations", "option", "n", int),
entity=("Name of the entity to be trained", "option", "e", str),
label=("The label to be given to the trained entity", "option", "l",
str),
)
def main(model = None, new_model_name=("ner%s" % str(datetime.now)),
output_dir= None, n_iter=20, entity=None, label=None):
if entity is None or label is None:
log.info("Entity and Label must both be supplied")
log.info("Bailing out as nothing to do ...... :-(")
return
log.info("Fetching training data for entity [%s] to be trained with
label [%s]" % (entity, label))
log.info("Training data retrieved and the first row is : ")
log.info(TRAIN_DATA[0])
log.info("There are %d rows to be trained" % len(TRAIN_DATA))
if model is not None:
nlp = spacy.load(output_dir) # load existing spaCy model
print("Loaded model '%s'" % model)
else:
nlp = spacy.blank("en") # create blank Language class
print("Created blank 'en' model")
if "ner" not in nlp.pipe_names:
log.info("ner not in pipe names, adding it in now ....")
ner = nlp.create_pipe("ner")
nlp.add_pipe(ner)
# otherwise, get it, so we can add labels to it
else:
log.info("retrieving previous ner pipe now ....")
ner = nlp.get_pipe("ner")
# add labels
for _, annotations in TRAIN_DATA:
for ent in annotations.get('entities'):
ner.add_label(ent[2])
move_names = list(ner.move_names)
# get names of other pipes to disable them during training
pipe_exceptions = ["ner", "trf_wordpiecer", "trf_tok2vec"]
other_pipes = [pipe for pipe in nlp.pipe_names if pipe not in
pipe_exceptions]
with nlp.disable_pipes(*other_pipes): # only train NER
# reset and initialize the weights randomly – but only if we're
# training a new model
if model is None:
optimizer=nlp.begin_training()
else:
optimizer=nlp.resume_training()
for itn in range(n_iter):
random.shuffle(TRAIN_DATA)
losses = {}
# batch up the examples using spaCy's minibatch
batches = minibatch(TRAIN_DATA, size=compounding(4.0, 32.0,
1.001)) для партий в пакетах: тексты, аннотации = zip (* batch) nlp.update (тексты, # пакет аннотаций текстов, # пакет аннотаций drop = 0.5, # dropout - затруднить запоминание данных sgd = оптимизатор, loss = loss,) print ("Потери", потери)
# test the trained model
for text, _ in TRAIN_DATA:
doc = nlp(text)
print("Entities", [(ent.text, ent.label_) for ent in doc.ents])
print("Tokens", [(t.text, t.ent_type_, t.ent_iob) for t in doc])
if output_dir is not None:
output_dir = Path(output_dir)
if not output_dir.exists():
output_dir.mkdir()
nlp.meta["name"] = new_model_name # rename model
nlp.to_disk(output_dir)
print("Saved model to", output_dir)
# test the saved model
print("Loading from", output_dir)
nlp2 = spacy.load(output_dir)
# Check the classes have loaded back consistently
assert nlp2.get_pipe("ner").move_names == move_names
return
if __name__ == "__main__":
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.DEBUG)
log.addHandler(consoleHandler)
plac.call(main)
Это работает хорошо, и я могу затем успешно протестировать модель, используя следующую функцию
@plac.annotations(
model_dir=("Optional output directory", "option", "o", Path),
test_text=("The test text to be used to test the model","option",
"t", str),
entity=("Name of the entity to be trained", "option", "e", str),
label=("The label to be given to the trained entity", "option", "l",
str),
)
def main(model_dir= None,test_text=None, entity=None, label=None):
if entity is None or label is None:
log.info("Entity and Label must both be supplied")
log.info("Bailing out as nothing to do ...... :-(")
return
if test_text is None:
test_text = ("Using default test string which is not optimal to
look for %s" % entity)
nlp = spacy.load(model_dir)
log.info("Loaded model %s" % nlp.meta["name"])
log.info("Testing the string %s" % test_text)
ner = nlp.get_pipe("ner")
for label in ner.labels:
log.info("NER Label : %s found in model" % label)
doc = nlp(test_text)
print("Entities", [(ent.text, ent.label_) for ent in doc.ents])
print("Tokens", [(t.text, t.ent_type_, t.ent_iob) for t in doc])
if __name__ == "__main__":
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.DEBUG)
log.addHandler(consoleHandler)
plac.call(main)
Однако, если я снова запустил код, чтобы тренироваться против нового пометить, сказать «SKILL» и предоставить новые обучающие данные, он успешно загружает старую модель, извлекает конвейер Ner и настраивает возобновление работы оптимизатора, но когда я проверяю его снова, он забывает все свои тренировки по метке RNK.
Я предполагаю, что резюме каким-то образом подхватит предыдущее состояние модели и сохранит ранее изученные аннотации. Это, безусловно, сохраняет метку NER.
Почему это происходит?
Думая, что это, возможно, связано с катастрофой c Забывая проблему, я создал один большой набор обучающих данных с примерами из обеих категорий , так что это:
'TOKEN' >>> 'NER Annotation'
-----------------------------
'senior' >>> 'RNK'
'csharp' >>> 'SKILL'
'sql' >>> 'SKILL'
Это потери
Losses {'ner': 721.8737016180717}
Losses {'ner': 5.999976082008388}
Losses {'ner': 5.970323057037423}
Losses {'ner': 5.996330579093365}
Losses {'ner': 6.028536462566022}
Losses {'ner': 12.043830573641666}
Losses {'ner': 10.001897952651317}
Losses {'ner': 6.016950026187274}
Losses {'ner': 6.624311646328313}
Losses {'ner': 10.602919933949224}
Losses {'ner': 6.1062697231067995}
Losses {'ner': 8.792055106010444}
Losses {'ner': 13.302123281119345}
Losses {'ner': 6.068028368915684}
Losses {'ner': 8.026694430880903}
Losses {'ner': 8.961434860193798}
Losses {'ner': 6.02721516249698}
Losses {'ner': 9.714660156853073}
Losses {'ner': 4.108544494319015}
Losses {'ner': 6.023105974059858}
Losses {'ner': 7.357760648981275}
Losses {'ner': 6.295292869532734}
Losses {'ner': 3.8088561052881995}
Losses {'ner': 6.059279332644757}
Losses {'ner': 7.024559462190113}
Losses {'ner': 4.784358718788942}
Losses {'ner': 5.935101364429172}
Losses {'ner': 4.027772727507415}
Losses {'ner': 2.1748163004265884}
Losses {'ner': 5.993975825343896}
Я включил свои тренировочные данные по пастбину, которые можно найти здесь:
https://pastebin.com/JmQv7HRS