本文目录导读:

我将为您展示如何使用CRF(条件随机场)进行命名实体识别(NER)的完整案例。
环境准备
首先安装必要的库:
pip install sklearn-crfsuite scikit-learn nltk
基础CRF NER实现
import sklearn_crfsuite
from sklearn_crfsuite import metrics
from sklearn.model_selection import train_test_split
import nltk
from nltk.corpus import conll2002
import numpy as np
# 下载数据
nltk.download('conll2002')
nltk.download('averaged_perceptron_tagger')
# 加载数据
train_sents = list(conll2002.iob_sents('esp.train'))
test_sents = list(conll2002.iob_sents('esp.testb'))
print(f"训练样本数: {len(train_sents)}")
print(f"测试样本数: {len(test_sents)}")
print(f"第一个句子样本:\n{train_sents[0][:10]}")
特征提取函数
def word2features(sent, i):
"""提取单个词的上下文特征"""
word = sent[i][0]
postag = sent[i][1]
features = {
'bias': 1.0, # 偏置特征
'word.lower()': word.lower(),
'word[-3:]': word[-3:],
'word[-2:]': word[-2:],
'word.isupper()': word.isupper(),
'word.istitle()': word.istitle(),
'word.isdigit()': word.isdigit(),
'postag': postag,
'postag[:2]': postag[:2], # 词性标签的前两个字符
}
# 前一个词的特征
if i > 0:
word1 = sent[i-1][0]
postag1 = sent[i-1][1]
features.update({
'-1:word.lower()': word1.lower(),
'-1:postag': postag1,
'-1:postag[:2]': postag1[:2],
})
else:
features['BOS'] = True # 句子开始标记
# 后一个词的特征
if i < len(sent)-1:
word1 = sent[i+1][0]
postag1 = sent[i+1][1]
features.update({
'+1:word.lower()': word1.lower(),
'+1:postag': postag1,
'+1:postag[:2]': postag1[:2],
})
else:
features['EOS'] = True # 句子结束标记
return features
def sent2features(sent):
"""将句子转换为特征序列"""
return [word2features(sent, i) for i in range(len(sent))]
def sent2labels(sent):
"""提取句子的标签序列"""
return [label for token, pos, label in sent]
def sent2tokens(sent):
"""提取句子的词序列"""
return [token for token, pos, label in sent]
准备训练数据
# 准备特征和标签
X_train = [sent2features(s) for s in train_sents]
y_train = [sent2labels(s) for s in train_sents]
X_test = [sent2features(s) for s in test_sents]
y_test = [sent2labels(s) for s in test_sents]
print("特征示例:")
print(f"第一个句子的第一个词特征: {X_train[0][0]}")
print(f"第一个句子的标签: {y_train[0][:10]}")
训练CRF模型
# 创建CRF模型
crf = sklearn_crfsuite.CRF(
algorithm='lbfgs', # 优化算法
c1=0.1, # L1正则化系数
c2=0.1, # L2正则化系数
max_iterations=100, # 最大迭代次数
all_possible_transitions=True # 考虑所有可能的转移
)
# 训练模型
print("开始训练CRF模型...")
crf.fit(X_train, y_train)
print("训练完成!")
模型评估
# 预测
y_pred = crf.predict(X_test)
# 计算准确率
labels = list(crf.classes_)
labels.remove('O') # 移除O标签用于计算准确率
# 评估指标
print("\n=== 模型评估结果 ===")
print(f"F1-score (宏观平均): {metrics.flat_f1_score(y_test, y_pred, average='weighted', labels=labels):.4f}")
print(f"准确率: {metrics.flat_accuracy_score(y_test, y_pred):.4f}")
# 分类报告
sorted_labels = sorted(labels, key=lambda name: (name[1:], name[0]))
print("\n分类报告:")
print(metrics.flat_classification_report(y_test, y_pred, labels=sorted_labels, digits=3))
使用模型进行预测
def predict_entities(text, crf_model):
"""对输入文本进行命名实体识别"""
# 分词和词性标注
tokens = nltk.word_tokenize(text)
pos_tags = nltk.pos_tag(tokens, lang='spa') # 这里用西班牙语模型
# 转换为特征格式
sent = [(token, pos, 'O') for token, pos in pos_tags]
features = sent2features(sent)
# 预测
predicted_tags = crf_model.predict([features])[0]
# 提取实体
entities = []
current_entity = []
current_label = None
for token, tag in zip(tokens, predicted_tags):
if tag.startswith('B-'):
if current_entity:
entities.append((current_label, ' '.join(current_entity)))
current_entity = [token]
current_label = tag[2:]
elif tag.startswith('I-'):
if current_label == tag[2:]:
current_entity.append(token)
else:
if current_entity:
entities.append((current_label, ' '.join(current_entity)))
current_entity = [token]
current_label = tag[2:]
else:
if current_entity:
entities.append((current_label, ' '.join(current_entity)))
current_entity = []
current_label = None
# 添加最后一个实体
if current_entity:
entities.append((current_label, ' '.join(current_entity)))
return entities
# 测试预测
test_text = "Juan visitó Madrid y Barcelona la semana pasada."
entities = predict_entities(test_text, crf)
print("\n输入文本:", test_text)
print("识别的实体:", entities)
完整的中文NER示例
# 使用自建数据集进行中文NER
import jieba
import jieba.posseg as pseg
# 创建示例训练数据
train_data = [
("我在北京工作", "O O B-LOC O"),
("李明是个医生", "B-PER O O O O"),
("我去了北京大学", "O O O B-ORG I-ORG"),
("王小明在上海读书", "B-PER I-PER O B-LOC O O O"),
]
def prepare_chinese_data(data):
"""准备中文CRF训练数据"""
sentences = []
labels = []
for text, label_str in data:
# 分词
words = list(jieba.cut(text))
label_seq = label_str.split()
# 词性标注
pos_tags = [word.flag for word in pseg.cut(text)]
# 构建句子格式 (word, pos, label)
sent = [(word, pos, label) for word, pos, label in zip(words, pos_tags, label_seq)]
sentences.append(sent)
return sentences
# 准备中文数据
chinese_sents = prepare_chinese_data(train_data)
# 特征提取(中文适配)
def chinese_word2features(sent, i):
"""中文特征提取"""
word = sent[i][0]
postag = sent[i][1]
features = {
'bias': 1.0,
'word': word,
'word[:1]': word[:1] if len(word) > 0 else '',
'word[-1:]': word[-1:] if len(word) > 0 else '',
'postag': postag,
'word.isdigit()': word.isdigit(),
'word.isalpha()': word.isalpha(),
}
# 上下文特征
if i > 0:
word1 = sent[i-1][0]
features.update({
'-1:word': word1,
'-1:postag': sent[i-1][1],
})
if i < len(sent) - 1:
word1 = sent[i+1][0]
features.update({
'+1:word': word1,
'+1:postag': sent[i+1][1],
})
return features
# 训练中文CRF模型
def train_chinese_crf(sentences):
"""训练中文NER模型"""
X = []
y = []
for sent in sentences:
X.append([chinese_word2features(sent, i) for i in range(len(sent))])
y.append([label for word, pos, label in sent])
# 训练模型
crf_chinese = sklearn_crfsuite.CRF(
algorithm='lbfgs',
c1=0.1,
c2=0.1,
max_iterations=50,
)
crf_chinese.fit(X, y)
return crf_chinese
# 训练中文模型
print("\n训练中文NER模型...")
chinese_crf = train_chinese_crf(chinese_sents)
print("训练完成!")
# 中文预测函数
def predict_chinese_ner(text, crf_model):
"""中文命名实体识别预测"""
# 分词和词性标注
words = []
pos_tags = []
for word, flag in pseg.cut(text):
words.append(word)
pos_tags.append(flag)
# 构建特征
sent = [(word, pos, 'O') for word, pos in zip(words, pos_tags)]
features = [chinese_word2features(sent, i) for i in range(len(sent))]
# 预测
predicted = crf_model.predict([features])[0]
# 提取实体
entities = []
entity = []
label = None
for word, tag in zip(words, predicted):
if tag.startswith('B-'):
if entity:
entities.append((label, ''.join(entity)))
entity = [word]
label = tag[2:]
elif tag.startswith('I-'):
if label == tag[2:]:
entity.append(word)
else:
if entity:
entities.append((label, ''.join(entity)))
entity = [word]
label = tag[2:]
else:
if entity:
entities.append((label, ''.join(entity)))
entity = []
label = None
if entity:
entities.append((label, ''.join(entity)))
return entities, list(zip(words, predicted))
# 测试中文预测
test_texts = [
"我在北京工作",
"李明是一名优秀的医生",
"王小明在北京大学学习",
]
print("\n中文NER预测结果:")
for text in test_texts:
entities, tagged = predict_chinese_ner(text, chinese_crf)
print(f"\n输入: {text}")
print(f"实体: {entities}")
print(f"标注: {tagged}")
模型保存与加载
import pickle
# 保存模型
def save_model(model, filename='crf_ner_model.pkl'):
with open(filename, 'wb') as f:
pickle.dump(model, f)
print(f"模型已保存到 {filename}")
# 加载模型
def load_model(filename='crf_ner_model.pkl'):
with open(filename, 'rb') as f:
model = pickle.load(f)
print(f"模型已从 {filename} 加载")
return model
# 使用示例
save_model(crf, 'crf_ner_model.pkl')
loaded_model = load_model('crf_ner_model.pkl')
模型优化建议
def optimize_crf_parameters(X_train, y_train, X_test, y_test):
"""CRF参数优化示例"""
from sklearn.metrics import make_scorer
from sklearn.model_selection import GridSearchCV
# 定义参数网格
param_grid = {
'c1': [0.01, 0.1, 1.0],
'c2': [0.01, 0.1, 1.0],
'max_iterations': [50, 100],
'algorithm': ['lbfgs', 'l2sgd']
}
# 创建CRF
crf = sklearn_crfsuite.CRF()
# 网格搜索
gs = GridSearchCV(
crf,
param_grid,
cv=3,
verbose=True,
n_jobs=-1
)
# 训练
gs.fit(X_train, y_train)
print(f"最佳参数: {gs.best_params_}")
print(f"最佳分数: {gs.best_score_:.4f}")
return gs.best_estimator_
# 注意:网格搜索可能需要较长时间,仅在数据量大时使用
这个完整案例展示了如何使用CRF进行命名实体识别,包括特征工程、模型训练、评估和预测的全流程,您可以根据实际需求调整特征提取函数和模型参数。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播