数据格式支持
eznlp支持多种NER任务常用的数据格式:
CoNLL格式 (最常用)
中 B-ORG
国 I-ORG
石 B-ORG
化 I-ORG
集 I-ORG
团 I-ORG
公 I-ORG
司 I-ORG
主 O
营 O
汽 O
油 O
# 加载CoNLL格式数据
from eznlp.io import ConllIO
io = ConllIO(scheme="BIO2") # 支持BIO1, BIO2, BIOES等标注方案
data = io.read("data/MSRA/train.tsv")
Brat格式
文本文件(example.txt):
中国石油化工集团公司主营汽油、煤油、柴油等石油产品的生产和销售。
注解文件(example.ann):
T1 Organization 0 8 中国石油化工集团公司
T2 Product 11 13 汽油
T3 Product 14 16 煤油
# 加载Brat格式数据
from eznlp.io import BratIO
io = BratIO(tokenize_callback=lambda x: list(x)) # 字符级分词
data = io.read("data/brat_folder/")
JSON格式
{
"tokens": ["中", "国", "石", "化"],
"entities": [
{"type": "Organization", "start": 0, "end": 4}
]
}
# 加载JSON格式数据
from eznlp.io import JsonIO
io = JsonIO()
data = io.read("data/json_data.json")
数据处理流程
数据加载与Token化
# scripts/utils.py
def load_data(args):
"""加载NER数据集"""
if args.dataset.lower() == "msra":
from eznlp.io import ConllIO
io = ConllIO(scheme="BIO2")
train_data = io.read("data/MSRA/train.tsv")
dev_data = io.read("data/MSRA/dev.tsv")
test_data = io.read("data/MSRA/test.tsv")
return train_data, dev_data, test_data
TokenSequence结构
TokenSequence是eznlp中处理文本的基本单位,它将文本和标注信息封装在一个对象中。
TokenSequence对象结构:
├── text: ['中', '国', '石', '化', '集', '团']
├── entities: [('Organization', 0, 6)]
├── char: ['中', '国', '石', '化', '集', '团']
├── isupper: [False, False, False, False, False, False]
└── shape_features: ['CN', 'CN', 'CN', 'CN', 'CN', 'CN']
各个组成部分的作用如下:
- text:这是TokenSequence的核心部分,存储了经过分词后的文本序列。在NER任务中,这通常是字符级别的序列(如示例中的每个汉字),但也可以是词级别的序列。这个字段是模型处理的主要输入。
- entities:存储了文本中的实体标注信息,采用
(实体类型, 起始位置, 结束位置)
的格式。在示例中,('Organization', 0, 6)
表示从位置0到位置6(不包括6)的字符序列"中国石化集团"是一个组织机构名称实体。这是NER任务需要预测的目标。 - char:存储每个token的字符级表示。对于字符级的文本(如中文),这个字段与text字段相同;对于词级别的文本(如英文),这个字段存储每个词的字符组成。这对于字符级特征提取和某些模型(如基于字符的嵌入)非常重要。
- isupper:这是一个形状特征,用于指示每个token是否为大写形式。在处理英文文本时特别有用,可以帮助模型识别专有名词等。对于中文文本,这个特征通常为False。
- shape_features:存储更丰富的形状特征,可以包括是否包含数字、是否包含标点符号、是否为标题格式等多种特征。这些特征可以帮助模型更好地理解文本的结构和语义信息,特别是在处理混合语言或包含特殊格式的文本时。
# eznlp/token.py
class TokenSequence(object):
def __init__(self, text=None, entities=None, **kwargs):
self.text = text or [] # 分词结果
self.entities = entities or [] # 实体标注
for key, value in kwargs.items():
setattr(self, key, value)
特征工程
基本特征
eznlp自动构建多种文本特征,这些特征能够帮助模型更好地理解文本的结构和语义信息:
# 自动构建的特征
token_sequence.build_basic_features()
# 包括: 字符特征、大小写特征、数字特征等
基本特征主要包括以下几类:
- 文本特征(text):这是最基础的特征,即token的文本内容。对于中文NER任务,通常是字符级别的文本,如"中"、"国"等。
- 前缀和后缀特征(prefix/suffix):提取token的前n个字符和后n个字符作为特征,包括前2、3、4、5个字符的前缀和后缀。这些特征有助于模型识别具有相似前缀或后缀的词汇,如英文中具有相同后缀的动词形式。
- 数字标记特征(num_mark):将数字转换为统一的标记格式,如将"123"转换为"int3",将"12.5%"转换为"percent2"。这种处理有助于模型识别数字的类型和数量级,而不被具体的数值干扰。
- 英文模式特征(en_pattern):将英文文本转换为模式表示,如将"Word123"转换为"Aaaa000",其中"A"代表大写字母,"a"代表小写字母,"0"代表数字。这种特征有助于模型识别文本的结构模式。
- 英文模式摘要特征(en_pattern_sum):对英文模式特征进行进一步压缩,将连续相同的字符合并,如"Aaaa000"变为"Aa0"。这有助于减少特征空间的复杂度。
- 英文形状特征(en_shape_features):一组布尔特征,描述token的各种形状属性,如是否包含大写字母、是否包含数字、是否为标题格式等。这些特征提供了更细粒度的文本形状信息。
中文NER特殊特征
针对中文NER,eznlp提供软词典特征,这是中文NER任务中的重要特征:
# 构建软词典特征(用于SoftLexicon嵌入)
token_sequence.build_softlexicons(lexicon_tokenizer)
# 结构: [[词1, 词2, ...], [词3, 词4, ...], ...]
软词典特征的原理是利用分词器从字符序列中识别出可能的词汇,并将这些词汇信息作为特征提供给模型。对于每个字符,系统会识别它在不同位置(开始B、中间M、结束E、单独S)上可能参与构成的词汇,并将这些词汇组成一个词典。这种特征能够为模型提供词汇级别的语义信息,即使在字符级别处理时也能利用词汇知识,显著提升中文NER的性能。
软词典特征的结构是一个三维列表,第一维对应文本中的每个字符,第二维对应字符在词汇中的位置标记(B/M/E/S),第三维是该位置上的词汇列表。例如,对于文本"中国石化",字符"化"可能会关联到词汇"石化"(作为词汇的开始B)和"中国石化"(作为词汇的结束E)等。
数据集构建
Dataset类
Dataset类是eznlp中用于组织和管理训练数据的核心组件,它继承自PyTorch的Dataset类,为模型训练提供了标准的数据接口:
# eznlp/dataset.py
class Dataset(torch.utils.data.Dataset):
def __init__(self, data, config, training=True):
self.data = data
self.config = config
# 将数据转换为模型可处理的示例
self.examples = [config.exemplify(entry, training) for entry in data]
主要功能:
- 数据存储:存储经过预处理的数据,每个数据条目都包含TokenSequence对象和对应的标签信息
- 示例生成:根据模型配置将原始数据转换为模型可处理的示例格式,这一步会将文本转换为ID序列,将标签转换为模型所需的格式
- 批处理支持:提供collate方法用于将多个示例组合成批次,这是模型训练中的关键步骤
- 数据统计:通过summary属性提供数据集的基本统计信息,如序列数量、平均长度、标签类型数等
examples结构:
examples是一个列表,其中每个元素都是一个字典,代表一个训练样本。字典的键值根据模型配置而变化,但通常包括以下几部分:
- ohots:基本的one-hot特征,如文本token的ID序列
- nested_ohots:嵌套的one-hot特征,如软词典特征
- 预训练模型特征:如BERT、ELMo等预训练模型的输出
- 标签对象:如序列标注任务中的tags_obj,包含实体标签的ID序列
例如,对于一个使用序列标注的NER任务,一个example可能如下所示:
{
"ohots": {
"text": tensor([ 45, 467, 89, 1024, 12, 5]) # 文本token的ID序列
},
"nested_ohots": {
"softlexicon": tensor([[[1, 2], [3, 4], [5], [6]]]) # 软词典特征
},
"tags_obj": Tags对象 # 包含标签信息的对象
}
配置与示例化
在构建Dataset之前,需要先配置模型参数,这决定了数据如何被转换为模型可处理的格式:
# 模型配置
config = ExtractorConfig(
ohots=ConfigDict({
"text": OneHotConfig(field="text", emb_dim=100)
}),
nested_ohots=ConfigDict({
"softlexicon": SoftLexiconConfig(emb_dim=50)
}),
decoder=SequenceTaggingDecoderConfig(scheme="BIOES", use_crf=True)
)
# 构建数据集
train_set = Dataset(train_data, config, training=True)
train_set.build_vocabs_and_dims(dev_data, test_data)
关键组件
- 嵌入配置(ohots/nested_ohots):定义了如何将文本特征转换为向量表示,如OneHotConfig定义了基本文本特征的嵌入维度,SoftLexiconConfig定义了软词典特征的嵌入设置
- 编码器配置(intermediate1/intermediate2):定义了特征的编码方式,通常使用LSTM或Transformer等结构
- 解码器配置(decoder):定义了如何从序列表示中解码出实体标签,如SequenceTaggingDecoderConfig使用序列标注方法,支持BIO、BIOES等标注方案,并可选择是否使用CRF层
build_vocabs_and_dims方法用于构建词汇表和计算维度信息,这是模型训练前的必要步骤。通过传入开发集和测试集,确保词汇表能够覆盖所有数据集中的token。
数据处理流程
eznlp框架中的NER数据处理流程是一个完整的从原始数据到模型输入的转换过程。整个流程可以分为以下几个关键阶段:
数据读取与解析阶段
数据处理的第一步是将存储在文件中的原始数据读取到内存中,并解析为统一的内部表示格式。eznlp通过eznlp.io模块支持多种数据格式:
- CoNLL格式:最常见的NER数据格式,以行为单位组织,每行包含一个token及其标签
- Brat格式:由文本文件和标注文件组成,标注文件中每行描述一个实体
- JSON格式:结构化的数据格式,易于程序处理
IO类(如ConllIO、BratIO、JsonIO)负责将这些不同格式的数据统一转换为TokenSequence对象列表。TokenSequence是eznlp中处理文本的基本单位,它封装了文本序列和对应的实体标注信息,为后续处理提供统一的数据接口。
数据集构建与词汇表生成
解析后的数据需要进一步处理才能被模型使用。这个任务由eznlp.dataset.Dataset类完成。在这一阶段,系统会执行以下关键操作:
- 词汇表构建:将文本中的token映射为数字ID,这是神经网络模型处理的必要步骤。不同的特征(如文本token、字符等)会构建各自的词汇表。
- 维度计算:根据数据统计信息计算各种特征的维度,如词汇表大小、字符集大小等,这些信息用于模型配置。
- 示例生成:根据模型配置将每个数据条目转换为模型可处理的示例(example),包括将文本token转换为对应的ID序列,将实体标注转换为模型训练所需的标签格式。
批处理与张量转换
在训练或推理时,需要将单个示例组合成批次以提高处理效率:
- 批处理准备:多个示例会被组合成批次(batch),这个过程会对序列进行填充(padding)以使同一批次中的序列长度一致。
- 掩码构建:构建相应的掩码(mask)来标识真实数据和填充数据,确保模型能够正确处理变长序列。
- 张量转换:将处理好的批次数据转换为PyTorch张量,包括token IDs、各种mask矩阵和标签。这些张量可以直接输入到神经网络模型中进行前向传播计算。
模型前向传播
最后,模型接收批处理张量作为输入,通过以下层次的处理生成最终的预测结果:
- 嵌入层:将ID转换为向量表示,可能包括词嵌入、字符嵌入、位置嵌入等
- 编码层:捕获上下文信息,常用的有LSTM、Transformer等结构
- 解码层:将序列表示转换为实体标签预测,对于NER任务通常使用序列标注或CRF等方法
整个数据处理流程设计合理,各步骤职责明确,既保证了处理效率,又具有良好的扩展性,能够适应不同格式的NER数据和不同结构的模型需求。
实际使用示例
# 完整的数据处理流程
from eznlp.io import ConllIO
from eznlp.dataset import Dataset
from eznlp.model import ExtractorConfig, OneHotConfig, SoftLexiconConfig, SequenceTaggingDecoderConfig
# 1. 加载数据
io = ConllIO(scheme="BIO2")
train_data = io.read("data/MSRA/train.tsv")
# 2. 配置模型
config = ExtractorConfig(
ohots={"text": OneHotConfig(field="text", emb_dim=100)},
nested_ohots={"softlexicon": SoftLexiconConfig(emb_dim=50)},
decoder=SequenceTaggingDecoderConfig(scheme="BIOES", use_crf=True)
)
# 3. 构建数据集
train_set = Dataset(train_data, config, training=True)
train_set.build_vocabs_and_dims() # 构建词汇表
# 4. 查看处理结果
print(f"数据集大小: {len(train_set)}")
example = train_set[0]
print(f"示例结构: {example.keys()}")
文章评论