[Практика НЛП 02] Практика простых трансформеров задача распознавания именованных объектов

искусственный интеллект

«Это третий день моего участия в ноябрьском испытании обновлений. Подробную информацию об этом событии см.:Вызов последнего обновления 2021 г."

[Практика 02] Распознавание именованных объектов

Основное содержание: Используйте SimpleTransformers для выполнения задач NER (распознавание именованных объектов).
命名实体识别(Named Entity Recognition,简称NER)Это найти соответствующий объект из предложения и определить его местоположение.Определение объекта очень широкое, это может быть имя человека, учреждение, местонахождение или пол, модель продукта и тому подобное в соответствии с потребностями бизнеса.

Например: Лю Юаньюань поступил в Университет Цинхуа. здесь刘媛媛Является人名,清华大学Является机构.

Ссылка на установку SimpleTransformers:[Практика НЛП 01] установка простых трансформеров и простая реализация классификации текста

набор данных

Автор выбираетCLUEдействоватьbenchmarkнабор данных

Выберите набор данных:

(1) ПОДСКАЗКА Мелкозернистый НЭР

Тест понимания китайского языка (CLUE)

Woohoo.Стратегические тесты.com/DataSet_sea…

Эти данные основаны на наборе данных классификации текстов с открытым исходным кодом THUCTC Университета Цинхуа и выбирают некоторые данные для детальной аннотации именованных объектов.Исходные данные взяты из Sina News RSS.

Набор для обучения: 10748, Набор для проверки: 1343, Категории меток: 10

Этикетки:

  • адрес
  • Заголовок книги
  • Компания
  • игра
  • правительство
  • кино
  • название
  • организация
  • должность
  • Аттракцион (сцена)

Ссылка на скачивание Клинера:загрузка данных

Детали миссии:CLUENER2020

{"text": "浙商银行企业信贷部叶老桂博士则从另一个角度对五道门槛进行了解读。叶老桂认为,对目前国内商业银行而言,", 
 "label": {
   "name": {"叶老桂": [[9, 11]]}, 
   "company": {"浙商银行": [[0, 3]]}}
}

{"text": "生生不息CSOL生化狂潮让你填弹狂扫", 
 "label": {
   "game": {"CSOL": [[4, 7]]}
 }
}

Определения и правила маркировки:

地址(address): **省**市**区**街**号,**路,**街道,**村等(如单独出现也标记),注意:地址需要标记完全, 标记到最细。

书名(book): 小说,杂志,习题集,教科书,教辅,地图册,食谱,书店里能买到的一类书籍,包含电子书。

公司(company): **公司,**集团,**银行(央行,中国人民银行除外,二者属于政府机构), 如:新东方,包含新华网/中国军网等。

游戏(game): 常见的游戏,注意有一些从小说,电视剧改编的游戏,要分析具体场景到底是不是游戏。

政府(goverment): 包括中央行政机关和地方行政机关两级。 中央行政机关有国务院、国务院组成部门(包括各部、委员会、中国人民银行和审计署)、国务院直属机构(如海关、税务、工商、环保总局等),军队等。

电影(movie): 电影,也包括拍的一些在电影院上映的纪录片,如果是根据书名改编成电影,要根据场景上下文着重区分下是电影名字还是书名。

姓名(name): 一般指人名,也包括小说里面的人物,宋江,武松,郭靖,小说里面的人物绰号:及时雨,花和尚,著名人物的别称,通过这个别称能对应到某个具体人物。

组织机构(organization): 篮球队,足球队,乐团,社团等,另外包含小说里面的帮派如:少林寺,丐帮,铁掌帮,武当,峨眉等。

职位(position): 古时候的职称:巡抚,知州,国师等。现代的总经理,记者,总裁,艺术家,收藏家等。

景点(scene): 常见旅游景点如:长沙公园,深圳动物园,海洋馆,植物园,黄河,长江等。

обработка данных

SimpleTransformers требуют, чтобы данные содержались в кадрах данных Pandas как минимум с тремя столбцами.

Назовите идентификатор предложения столбца, текст и метку, и SimpleTransformers обработает данные.
Первый столбец содержит идентификатор предложения, который имеет тип int. Второй столбец содержит слова и имеет тип str. Второй столбец содержит метки и имеет тип int.

import json
import pandas as pd

def load_cluener2020_data(path):
    """适应simpletransformer的加载方式"""
    data = []
    labels_list = []
    with open(path, "r", encoding="utf-8") as f:
        for idx, line in enumerate(f):
            line = json.loads(line.strip())
            text = line["text"]
            label_entities = line.get("label", None)
            words = list(text)
            labels = ['O'] * len(words)
            if label_entities:
                for key, value in label_entities.items():
                    for sub_name, sub_index in value.items():
                        for start_index, end_index in sub_index:
                            assert "".join(words[start_index:end_index+1]) == sub_name
                            if start_index == end_index:
                                labels[start_index] = "S-" + key
                            else:
                                labels[start_index] = "B-" + key
                                labels[start_index+1:end_index+1] = ["I-"+key] * (len(sub_name) - 1)
            for word, label in zip(words, labels):
                data.append([idx, word, label])
                if label not in labels_list:
                    labels_list.append(label)
    data_df = pd.DataFrame(data, columns=["sentence_id", "words", "labels"])
    return data_df, labels_list

Обработанная форма выглядит следующим образом:

Построение модели и обучение

Сначала настройте параметры. У Simple Transformers есть аргументы dict. Подробное описание каждого аргумента см. по адресу:простые трансформеры.love/docs/tips - ааа...

1) Конфигурация параметров

# 配置config
import argparse
def data_config(parser):
    parser.add_argument("--trainset_path", type=str, default="data/CLUENER2020/train.json",
                        help="训练集路径")
    parser.add_argument("--devset_path", type=str, default="data/CLUENER2020/dev.json",
                        help="验证集路径")
    parser.add_argument("--testset_path", type=str, default="data/CLUENER2020/test.json",
                        help="测试集路径")
    parser.add_argument("--reprocess_input_data", type=bool, default=True,
                        help="如果为True,则即使cache_dir中存在输入数据的缓存文件,也将重新处理输入数据")
    parser.add_argument("--overwrite_output_dir", type=bool, default=True,
                        help="如果为True,则训练后的模型将保存到ouput_dir,并将覆盖同一目录中的现有已保存模型")
    parser.add_argument("--use_cached_eval_features", type=bool, default=True,
                        help="训练期间的评估使用缓存特征,将此设置为False将导致在每个评估步骤中重新计算特征")
    parser.add_argument("--output_dir", type=str, default="outputs/",
                        help="存储所有输出,包括模型checkpoints和评估结果")
    parser.add_argument("--best_model_dir", type=str, default="outputs/best_model/",
                        help="保存评估过程中的最好模型")
    return parser

def model_config(parser):
    parser.add_argument("--max_seq_length", type=int, default=200,
                        help="模型支持的最大序列长度")
    parser.add_argument("--model_type", type=str, default="bert",
                        help="模型类型bert/roberta")
    parser.add_argument("--model_name", type=str, default="../pretrainmodel/bert",
                        help="选择使用哪个预训练模型")
    parser.add_argument("--manual_seed", type=int, default=2021,
                        help="为了产生可重现的结果,需要设置随机种子")
    return parser

def train_config(parser):
    parser.add_argument("--evaluate_during_training", type=bool, default=True,
                        help="设置为True以在训练模型时执行评估,确保评估数据已传递到训练方法")
    parser.add_argument("--num_train_epochs", type=int, default=3,
                        help="模型训练迭代数")
    parser.add_argument("--evaluate_during_training_steps", type=int, default=100,
                        help="在每个指定的step上执行评估,checkpoint和评估结果将被保存")
    parser.add_argument("--save_eval_checkpoints", type=bool, default=True)
    parser.add_argument("--save_model_every_epoch", type=bool, default=True,
                        help="每次epoch保存模型")
    parser.add_argument("--n_gpu", type=int, default=1,
                        help="训练时使用的GPU个数")
    parser.add_argument("--train_batch_size", type=int, default=16)
    parser.add_argument("--eval_batch_size", type=int, default=8)
    return parser

def set_args():
    parser = argparse.ArgumentParser()
    parser = data_config(parser)
    parser = model_config(parser)
    parser = train_config(parser)

    args,unknown = parser.parse_known_args()
    return args

2) Построение модели и обучение

import logging
from simpletransformers.ner import NERModel

# 可从训练集获取
labels_list = ["B-company", "I-company", 'O', "B-name", "I-name", 
               "B-game", "I-game", "B-organization", "I-organization",
               "B-movie", "I-movie", "B-position", "I-position",
               "B-address", "I-address", "B-government", "I-government",
               "B-scene", "I-scene", "B-book", "I-book",
               "S-company", "S-address", "S-name", "S-position"]
# 训练
args = set_args()
logging.basicConfig(level=logging.INFO)
transformers_logger = logging.getLogger("transformers")
transformers_logger.setLevel(logging.WARNING)
# 读取数据
train_df, labels_list = load_cluener2020_data(args.trainset_path)
dev_df, _ = load_cluener2020_data(args.devset_path)

# 创建命名实体识别模型
model = NERModel(args.model_type, args.model_name, labels=labels_list, args=vars(args))
model.save_model(model=model.model)  # 可以将预训练模型下载到output_dir

# 训练模型,并在训练时评估
model.train_model(train_df, eval_data=dev_df)

результат прогноза

3 раунда обучения, лучшее значение F10.772964

полный код

import json
import argparse
import numpy as np
import pandas as pd
import logging
from simpletransformers.ner import NERModel

def data_config(parser):
    parser.add_argument("--trainset_path", type=str, default="data/CLUENER2020/train.json",
                        help="训练集路径")
    parser.add_argument("--devset_path", type=str, default="data/CLUENER2020/dev.json",
                        help="验证集路径")
    parser.add_argument("--testset_path", type=str, default="data/CLUENER2020/test.json",
                        help="测试集路径")
    parser.add_argument("--reprocess_input_data", type=bool, default=True,
                        help="如果为True,则即使cache_dir中存在输入数据的缓存文件,也将重新处理输入数据")
    parser.add_argument("--overwrite_output_dir", type=bool, default=True,
                        help="如果为True,则训练后的模型将保存到ouput_dir,并将覆盖同一目录中的现有已保存模型")
    parser.add_argument("--use_cached_eval_features", type=bool, default=True,
                        help="训练期间的评估使用缓存特征,将此设置为False将导致在每个评估步骤中重新计算特征")
    parser.add_argument("--output_dir", type=str, default="outputs/",
                        help="存储所有输出,包括模型checkpoints和评估结果")
    parser.add_argument("--best_model_dir", type=str, default="outputs/best_model/",
                        help="保存评估过程中的最好模型")
    return parser

def model_config(parser):
    parser.add_argument("--max_seq_length", type=int, default=200,
                        help="模型支持的最大序列长度")
    parser.add_argument("--model_type", type=str, default="bert",
                        help="模型类型bert/roberta")
    parser.add_argument("--model_name", type=str, default="../pretrainmodel/bert",
                        help="选择使用哪个预训练模型")
    parser.add_argument("--manual_seed", type=int, default=2021,
                        help="为了产生可重现的结果,需要设置随机种子")
    return parser

def train_config(parser):
    parser.add_argument("--evaluate_during_training", type=bool, default=True,
                        help="设置为True以在训练模型时执行评估,确保评估数据已传递到训练方法")
    parser.add_argument("--num_train_epochs", type=int, default=3,
                        help="模型训练迭代数")
    parser.add_argument("--evaluate_during_training_steps", type=int, default=100,
                        help="在每个指定的step上执行评估,checkpoint和评估结果将被保存")
    parser.add_argument("--save_eval_checkpoints", type=bool, default=True)
    parser.add_argument("--save_model_every_epoch", type=bool, default=True,
                        help="每次epoch保存模型")
    parser.add_argument("--n_gpu", type=int, default=1,
                        help="训练时使用的GPU个数")
    parser.add_argument("--train_batch_size", type=int, default=16)
    parser.add_argument("--eval_batch_size", type=int, default=8)
    return parser

def set_args():
    parser = argparse.ArgumentParser()
    parser = data_config(parser)
    parser = model_config(parser)
    parser = train_config(parser)

    args,unknown = parser.parse_known_args()
    return args

def load_cluener2020_data(path):
    """适应simpletransformer的加载方式"""
    data = []
    labels_list = []
    with open(path, "r", encoding="utf-8") as f:
        for idx, line in enumerate(f):
            line = json.loads(line.strip())
            text = line["text"]
            label_entities = line.get("label", None)
            words = list(text)
            labels = ['O'] * len(words)
            if label_entities:
                for key, value in label_entities.items():
                    for sub_name, sub_index in value.items():
                        for start_index, end_index in sub_index:
                            assert "".join(words[start_index:end_index+1]) == sub_name
                            if start_index == end_index:
                                labels[start_index] = "S-" + key
                            else:
                                labels[start_index] = "B-" + key
                                labels[start_index+1:end_index+1] = ["I-"+key] * (len(sub_name) - 1)
            for word, label in zip(words, labels):
                data.append([idx, word, label])
                if label not in labels_list:
                    labels_list.append(label)
    data_df = pd.DataFrame(data, columns=["sentence_id", "words", "labels"])
    return data_df, labels_list

def train_model():
    # 可从训练集获取
    labels_list = ["B-company", "I-company", 'O', "B-name", "I-name", 
                   "B-game", "I-game", "B-organization", "I-organization",
                   "B-movie", "I-movie", "B-position", "I-position",
                   "B-address", "I-address", "B-government", "I-government",
                   "B-scene", "I-scene", "B-book", "I-book",
                   "S-company", "S-address", "S-name", "S-position"]
    # 训练
    args = set_args()
    logging.basicConfig(level=logging.INFO)
    transformers_logger = logging.getLogger("transformers")
    transformers_logger.setLevel(logging.WARNING)
    # 读取数据
    train_df, labels_list = load_cluener2020_data(args.trainset_path)
    dev_df, _ = load_cluener2020_data(args.devset_path)
    print(train_df.head(10))
    print(dev_df.head(10))

    # 创建命名实体识别模型
    model = NERModel(args.model_type, args.model_name, labels=labels_list, args=vars(args))
    model.save_model(model=model.model)  # 可以将预训练模型下载到output_dir

    # 训练模型,并在训练时评估
    model.train_model(train_df, eval_data=dev_df)

if __name__ == '__main__':
    train_model()

Ссылаться на:nuggets.capable/post/684490…
Ссылка на код:GitHub.com/поделиться через/s…

Простые трансформеры быстро начинают работать, но они имеют тенденцию только к быстрому применению или написанию базовых линий.Необходимо изменить структуру модели и гибкие методы комбинирования или освоить библиотеки Python с высокими степенями свободы, такие как трансформеры.

НЛП новое, талант и обучение поверхностны, есть ошибки или несовершенства, прошу критиковать и исправлять! !