You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NLTK中自定义分词标签?及命名实体识别结果异常问题

嘿,我来帮你搞定这两个NLTK的问题,都是实际使用中常见的痛点:

1. 如何在NLTK工具中为数据分词设置自定义标签?

NLTK默认的词性标注器用的是预训练好的通用模型,要添加自定义标签,主要有两种方式,看你的需求场景选:

方式一:手动修改标注结果(适合少量文本)

如果只是个别词需要自定义标签,可以先跑默认的词性标注,再手动替换目标词的标签。举个例子:

from nltk import word_tokenize, pos_tag

sentence = "My custom term needs a special tag"
tokens = word_tokenize(sentence)
# 先获取默认标注结果
default_tagged = pos_tag(tokens)
# 替换指定词的标签为自定义的CUSTOM_TAG
custom_tagged = [(word, 'CUSTOM_TAG') if word == 'custom' else (word, tag) for word, tag in default_tagged]
print(custom_tagged)

方式二:训练自定义词性标注器(适合大量领域文本)

如果你的场景有很多需要自定义标签的词汇,最好训练自己的标注器。可以基于NLTK的UnigramTagger或BigramTagger,结合自定义语料和预训练模型来提升准确率:

from nltk.tag import UnigramTagger
from nltk.corpus import treebank

# 准备带自定义标签的语料,格式是[[(词1, 标签1), (词2, 标签2)], ...]
custom_corpus = [
    [("Antacids", "MEDICATION"), ("is", "VBZ"), ("given", "VBN"), ("to", "TO")],
    [("Sodium", "MEDICATION"), ("Bicarbonate", "MEDICATION"), ("is", "VBZ"), ("given", "VBN")]
]
# 用预训练的treebank标注器作为回退,避免未见过的词标注错误
base_tagger = UnigramTagger(treebank.tagged_sents())
# 训练自定义标注器,优先用自定义语料, fallback到预训练模型
custom_tagger = UnigramTagger(custom_corpus, backoff=base_tagger)

# 测试效果
tokens = word_tokenize("Antacids is given to John")
print(custom_tagger.tag(tokens))

2. 修正NLTK命名实体识别(NER)的错误

你遇到的问题很典型——NLTK默认的ne_chunk用的是基于通用语料训练的模型,对医学术语、特定人名这类领域实体的识别能力很有限,容易把药物名、人名误判为GPE(地理实体)。这里有几种解决思路:

方案一:手动修正NER树结构(适合少量测试文本)

可以解析ne_chunk返回的树结构,根据已知的实体列表手动替换错误的标签:

from nltk import word_tokenize, pos_tag, ne_chunk
from nltk.tree import Tree

sentence = "Antacids is given to Jhon,Sodium Bicarbonate is given to Carl,Folic Acid to Jeery all works at Google "
tokens = word_tokenize(sentence)
tagged = pos_tag(tokens)
ner_tree = ne_chunk(tagged)

def correct_ner(tree):
    corrected_nodes = []
    # 定义已知的实体和正确标签
    entity_map = {
        "Antacids": "MEDICATION",
        "Sodium Bicarbonate": "MEDICATION",
        "Folic Acid": "MEDICATION",
        "Jhon": "PERSON",
        "Carl": "PERSON",
        "Jeery": "PERSON",
        "Google": "ORGANIZATION"
    }
    
    for node in tree:
        if isinstance(node, Tree):
            # 提取实体文本
            entity_text = " ".join([word for word, _ in node.leaves()])
            # 如果在映射表里,替换标签;否则保留原标签
            if entity_text in entity_map:
                corrected_nodes.append(Tree(entity_map[entity_text], node.leaves()))
            else:
                corrected_nodes.append(node)
        else:
            corrected_nodes.append(node)
    return Tree('S', corrected_nodes)

# 得到修正后的NER树
corrected_ner_tree = correct_ner(ner_tree)
print(corrected_ner_tree)

方案二:训练自定义NER模型(适合大量领域数据)

如果有足够多的标注好的领域语料,训练自己的NER模型是长期解决方案。NLTK可以用基于分类器的chunk parser来实现,这里用MaxEnt分类器举个例子:

from nltk.classify import MaxentClassifier
from nltk.tag import ClassifierBasedTagger
from nltk.chunk import ChunkParserI
from nltk import word_tokenize, pos_tag
from nltk.tree import Tree

# 准备训练数据,用IOB标注格式(B-开头表示实体起始,I-表示实体延续,O表示非实体)
training_data = [
    (["Antacids", "is", "given", "to", "Jhon"], ["B-MEDICATION", "O", "O", "O", "B-PERSON"]),
    (["Sodium", "Bicarbonate", "is", "given", "to", "Carl"], ["B-MEDICATION", "I-MEDICATION", "O", "O", "O", "B-PERSON"]),
    (["Folic", "Acid", "to", "Jeery", "all", "works", "at", "Google"], ["B-MEDICATION", "I-MEDICATION", "O", "B-PERSON", "O", "O", "O", "B-ORGANIZATION"])
]

# 定义特征提取函数,给分类器提供词的上下文信息
def ner_features(tokens, index, history):
    word = tokens[index]
    pos_tag_val = pos_tag(tokens)[index][1]
    return {
        'word': word.lower(),
        'pos': pos_tag_val,
        'is_capitalized': word.istitle(),
        'prev_word': tokens[index-1].lower() if index > 0 else '',
        'next_word': tokens[index+1].lower() if index < len(tokens)-1 else ''
    }

# 自定义NER解析器类
class CustomNERParser(ChunkParserI):
    def __init__(self, training_data):
        train_set = []
        for tokens, tags in training_data:
            tagged_tokens = pos_tag(tokens)
            history = []
            for i, _ in enumerate(tagged_tokens):
                features = ner_features(tokens, i, history)
                train_set.append((features, tags[i]))
                history.append(tags[i])
        # 训练MaxEnt分类器
        self.classifier = MaxentClassifier.train(train_set, trace=0)
    
    def parse(self, tagged_tokens):
        tokens = [word for word, _ in tagged_tokens]
        history = []
        predicted_tags = []
        for i, _ in enumerate(tagged_tokens):
            features = ner_features(tokens, i, history)
            tag = self.classifier.classify(features)
            predicted_tags.append(tag)
            history.append(tag)
        # 把IOB标签转换为NLTK的Tree结构
        return self._iob_to_tree(tagged_tokens, predicted_tags)
    
    def _iob_to_tree(self, tagged_tokens, iob_tags):
        tree_nodes = []
        current_chunk = []
        current_entity_type = None
        
        for (word, pos), tag in zip(tagged_tokens, iob_tags):
            if tag.startswith('B-'):
                # 结束上一个chunk(如果有的话)
                if current_chunk:
                    tree_nodes.append(Tree(current_entity_type, current_chunk))
                    current_chunk = []
                current_entity_type = tag.split('-')[1]
                current_chunk.append((word, pos))
            elif tag.startswith('I-') and current_entity_type == tag.split('-')[1]:
                # 延续当前chunk
                current_chunk.append((word, pos))
            else:
                # 非实体,结束当前chunk(如果有的话)
                if current_chunk:
                    tree_nodes.append(Tree(current_entity_type, current_chunk))
                    current_chunk = []
                tree_nodes.append((word, pos))
        # 处理最后一个chunk
        if current_chunk:
            tree_nodes.append(Tree(current_entity_type, current_chunk))
        return Tree('S', tree_nodes)

# 训练并测试
parser = CustomNERParser(training_data)
test_tokens = word_tokenize(sentence)
test_tagged = pos_tag(test_tokens)
corrected_tree = parser.parse(test_tagged)
print(corrected_tree)

额外提示

如果你的场景对NER准确率要求很高,也可以考虑结合spaCy这类工具的领域预训练模型(比如医学领域的en_med7_lg),不过如果必须用NLTK,上面的两种方案完全可以解决你的问题。

内容的提问来源于stack exchange,提问作者Meta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:10:39