如何使用NLTK Naive Bayes实现命名实体识别?附自有语料示例
嘿,我来帮你搞定用NLTK朴素贝叶斯做命名实体识别(NER)的事儿,结合你给的那种带
步骤1:先把你的标注语料转成NLTK能处理的格式
你给的语料是带XML风格O(表示Outside Entity,不属于任何实体)的标签。我用正则来做解析,简单直接:
import re def parse_enamex_corpus(text): # 匹配<ENAMEX TYPE="XXX">内容</ENAMEX>的正则表达式 enamex_pattern = r'<ENAMEX TYPE="([^"]+)">([^<]+)</ENAMEX>' processed_tokens = [] last_position = 0 # 遍历所有匹配的实体标签 for match in re.finditer(enamex_pattern, text): # 先处理标签前的普通文本 if match.start() > last_position: ordinary_words = text[last_position:match.start()].split() processed_tokens.extend([(word, 'O') for word in ordinary_words]) # 处理实体内部的词,每个词打上对应的实体类型标签 entity_words = match.group(2).split() entity_type = match.group(1) processed_tokens.extend([(word, entity_type) for word in entity_words]) last_position = match.end() # 处理最后一段普通文本 if last_position < len(text): remaining_words = text[last_position:].split() processed_tokens.extend([(word, 'O') for word in remaining_words]) return processed_tokens # 用你的示例文本测试一下 sample_text = 'Sementara itu Pengamat Pasar Modal <ENAMEX TYPE="PERSON">Dandossi Matram</ENAMEX> mengatakan, sulit bagi sebuah <ENAMEX TYPE="ORGANIZATION">kantor akuntan publik</ENAMEX> (<ENAMEX TYPE="ORGANIZATION">KAP</ENAMEX>) untuk dapat menyelesaikan audit perusahaan sebesar <ENAMEX TYPE="ORGANIZATION">Telkom</ENAMEX> dalam waktu 3 b...' print(parse_enamex_corpus(sample_text))
运行这段代码后,你会得到一个类似[('Sementara', 'O'), ('itu', 'O'), ('Dandossi', 'PERSON'), ...]的列表,每个词都对应了正确的标签。
步骤2:定义NER的特征函数
朴素贝叶斯分类器需要基于词的特征来判断它的实体类型,除了词本身,我们还可以加入上下文、大小写、前缀后缀这些常用的NER特征,比如:
def word_features(word, prev_word, next_word): return { 'word_lower': word.lower(), 'is_capitalized': word[0].isupper(), 'is_title_case': word.istitle(), 'is_digit': word.isdigit(), 'prefix_1': word[:1], 'prefix_2': word[:2], 'suffix_1': word[-1:], 'suffix_2': word[-2:], 'prev_word_lower': prev_word.lower(), 'next_word_lower': next_word.lower() }
这些特征能帮分类器更好地区分实体(比如人名通常首字母大写,机构名可能有特定后缀)。
步骤3:准备训练数据
我们需要把预处理后的语料转换成「特征集+标签」的格式,还要处理句子首尾的边界情况(比如第一个词没有前一个词,就用<START>代替,最后一个词用<END>):
def prepare_training_data(processed_corpus): training_examples = [] total_tokens = len(processed_corpus) for idx in range(total_tokens): word, label = processed_corpus[idx] # 获取前一个词,边界用特殊标记 prev_word = processed_corpus[idx-1][0] if idx > 0 else '<START>' # 获取后一个词,边界用特殊标记 next_word = processed_corpus[idx+1][0] if idx < total_tokens - 1 else '<END>' # 提取特征 features = word_features(word, prev_word, next_word) training_examples.append((features, label)) return training_examples # 把你的全部语料都处理成训练集 # 这里只是示例,实际要替换成你所有的标注语料 all_processed_data = parse_enamex_corpus(sample_text) training_set = prepare_training_data(all_processed_data)
步骤4:训练Naive Bayes分类器
现在就可以用NLTK自带的朴素贝叶斯分类器来训练了,还能查看分类准确率和最有信息量的特征:
from nltk.classify import NaiveBayesClassifier from nltk.classify.util import accuracy # 训练分类器 classifier = NaiveBayesClassifier.train(training_set) # 如果有测试集,可以看准确率(把测试集也用同样方法处理成(test_features, label)格式) # test_accuracy = accuracy(classifier, test_set) # print(f"分类器准确率: {test_accuracy:.2f}") # 查看最有信息量的特征,帮你调试模型 classifier.show_most_informative_features(10)
步骤5:用训练好的分类器做NER预测
写个简单的函数,输入句子就能返回每个词的实体标签:
def ner_predict(sentence, classifier): words = sentence.split() tagged_result = [] total_words = len(words) for idx in range(total_words): word = words[idx] prev_word = words[idx-1] if idx > 0 else '<START>' next_word = words[idx+1] if idx < total_words - 1 else '<END>' features = word_features(word, prev_word, next_word) predicted_label = classifier.classify(features) tagged_result.append((word, predicted_label)) return tagged_result # 测试一下 test_sentence = "Dandossi Matram bekerja di Telkom" print(ner_predict(test_sentence, classifier))
一些小提示
- 语料规模很重要:朴素贝叶斯是统计模型,语料越多效果越好,如果你的标注数据少,可以考虑做些数据增强(比如同义词替换、句子改写)
- 针对印尼语优化:你的示例是印尼语,可以加入印尼语词干提取(NLTK有印尼语词干器
nltk.stem.snowball.IndonesianStemmer)来优化特征 - 标签细化:如果你的实体有更细的分类(比如地点、时间),只要语料里有对应的
<ENAMEX TYPE>,代码不用改就能支持
内容的提问来源于stack exchange,提问作者Irfan HP
相关产品推荐
相关产品推荐

