ner_lad_code.md 16 KB

好的,我们可以使用spaCy进行中文命名实体识别(NER),然后利用jieba进行分词(主要用于处理NER未识别的部分,或者作为备选方案),最后使用gensim库进行LDA主题挖掘。

以下是具体的步骤和代码示例:

核心思路:

  1. NER提取关键信息: 首先对每篇专利文本进行NER,提取出预定义的实体类型(如:组织机构、地点、产品、技术术语等)。这些实体通常是文本中最具信息量的部分。
  2. 准备LDA输入: 将提取出的实体作为主要的“词语”来构建文档。这使得LDA模型更关注这些关键实体之间的共现模式,而不是普通词汇。也可以结合jieba分词结果,但优先保留实体。
  3. LDA建模: 使用gensim对处理后的文档(主要由实体构成)进行LDA训练,发现潜在主题。
  4. 结果解释: 分析LDA模型输出的主题及其包含的代表性实体(或词语)。

所需库安装:

pip install spacy jieba gensim pandas
# 下载spaCy中文模型 (选择一个,sm最小,md中等,lg最大,效果通常更好但需要更多资源)
python -m spacy download zh_core_web_sm
# 或者 python -m spacy download zh_core_web_md
# 或者 python -m spacy download zh_core_web_lg

Python代码示例:

import spacy
import jieba
import re
import pandas as pd
from gensim import corpora, models
from gensim.models.coherencemodel import CoherenceModel
import time

# --- 1. 配置与加载 ---

# 加载spaCy中文模型 (确保已下载)
# nlp = spacy.load("zh_core_web_sm")
nlp = spacy.load("zh_core_web_md") # 推荐使用md或lg模型以获得更好的NER效果
print("spaCy中文模型加载成功。")

# 加载中文停用词 (需要自行准备一个停用词文件 'chinese_stopwords.txt')
# 你可以从网上找到通用的中文停用词表,例如:https://github.com/goto456/stopwords
# 确保文件编码为 UTF-8
stopwords_path = 'chinese_stopwords.txt' # <--- 请将此路径替换为你的停用词文件路径
try:
    with open(stopwords_path, 'r', encoding='utf-8') as f:
        stopwords = {line.strip() for line in f if line.strip()}
    print(f"停用词表加载成功,共 {len(stopwords)} 个词。")
except FileNotFoundError:
    print(f"警告:停用词文件 {stopwords_path} 未找到,将不使用停用词过滤。")
    stopwords = set()

# --- 2. 准备示例数据 ---
# 假设这是你的专利数据,实际应用中应从文件或数据库加载
patent_texts = [
    "本发明公开了一种预制混凝土结构及其施工方法,涉及建筑工程技术领域。该结构包括预制梁和预制柱,通过特定的连接节点实现快速装配。采用了高强度钢筋和自密实混凝土,提高了结构的承载能力和耐久性。施工方法简化了现场作业,缩短了工期,特别适用于高层建筑和桥梁工程。合作单位包括同济大学和中建集团。",
    "一种新型的绿色建筑材料,主要成分为改性粉煤灰和再生骨料。本专利技术通过优化配合比和添加特殊外加剂,显著提升了材料的抗压强度和抗渗性能。该材料可替代传统水泥混凝土,用于道路路面、墙体砌块等,具有低碳环保、成本低廉的优点。由清华大学建筑学院研发。",
    "公开了一种基于BIM技术的智能施工管理系统。该系统集成了进度管理、成本控制、质量监控和安全预警功能。通过三维模型与实时数据的结合,实现了施工过程的可视化和精细化管理。系统应用在北京某大型公共建筑项目中,有效提升了管理效率,降低了沟通成本。开发商为广联达软件股份有限公司。",
    "涉及一种深基坑支护结构体系及其优化设计方法。该体系采用地下连续墙与多道内支撑相结合的方式。优化设计方法考虑了土压力、水压力以及周边环境影响,利用有限元软件进行模拟分析,确保基坑的稳定性和安全性。适用于软土地基条件下的复杂基坑工程。研究机构为中国建筑科学研究院。",
    "本发明涉及一种装配式钢结构住宅的连接节点。该节点设计独特,采用高强度螺栓连接,安装便捷,并能有效传递地震作用。节点构造考虑了防火和防腐要求。通过实验验证,该节点的抗震性能优于传统焊接节点。适用于多层及高层钢结构住宅建筑。专利权人是宝钢集团。"
]

# 实际应用中可能需要读取大量文本
# df = pd.read_csv('your_patents.csv') # 假设专利在CSV文件的 'abstract' 列
# patent_texts = df['abstract'].tolist()

print(f"载入了 {len(patent_texts)} 条示例专利文本。")

# --- 3. NER 提取与文本预处理 ---

def preprocess_text_with_ner(text, nlp_model, stop_words):
    """
    使用spaCy进行NER,并结合jieba分词进行预处理。
    优先保留识别出的实体,对其余文本进行分词和过滤。
    """
    # 清洗文本,去除特殊字符和数字(可选,根据需求调整)
    text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z]', ' ', text) # 保留中文和英文字母
    text = re.sub(r'\s+', ' ', text).strip()

    doc = nlp_model(text)

    entities = []
    # 选择你认为重要的实体类型 (可以根据spaCy模型支持的标签调整)
    # 常见标签: ORG(组织), PERSON(人名), GPE(地缘政治实体,如城市/国家),
    #          LOC(非GPE位置), PRODUCT(产品), EVENT(事件), NORP(民族、宗教、政治团体)
    #          WORK_OF_ART(艺术品), LAW(法律), LANGUAGE(语言)
    #          DATE(日期), TIME(时间), PERCENT(百分比), MONEY(货币), QUANTITY(数量), ORDINAL(序数), CARDINAL(基数)
    # 对于建筑专利,ORG, PRODUCT, GPE, LOC, NORP 可能比较相关,但也可能需要自定义模型识别技术术语
    relevant_entity_labels = {'ORG', 'PRODUCT', 'GPE', 'LOC', 'NORP', 'LAW'}

    entity_texts = set() # 用set去重
    for ent in doc.ents:
        if ent.label_ in relevant_entity_labels:
            # 清理实体文本,去除内部多余空格,并转小写(可选)
            clean_ent = ent.text.strip().lower()
            if clean_ent and clean_ent not in stop_words and len(clean_ent) > 1: # 过滤掉空实体、停用词实体和单字实体
               entities.append(clean_ent)
               entity_texts.add(clean_ent) # 记录已被识别为实体的文本

    # 对非实体部分进行分词 (可选策略)
    # 策略 A: 只使用实体 (如果实体覆盖度足够)
    # processed_tokens = entities

    # 策略 B: 结合分词,但避免重复切分实体
    # (这个策略较复杂,这里采用一个简化版:对全文分词,然后替换回实体,再过滤)
    # 使用jieba分词
    words = jieba.lcut(text, cut_all=False)

    processed_tokens = []
    # 先加入识别出的实体
    processed_tokens.extend(entities)

    # 再处理分词结果,过滤停用词、单字词,并确保不是实体的一部分
    current_text = text.lower() # 用于检查是否是实体的一部分
    for word in words:
        word = word.strip().lower()
        # 检查这个词是否已经是实体的一部分,或者是否是停用词/单字词
        is_part_of_entity = False
        for ent_text in entity_texts:
            if word in ent_text: # 简化判断,可能不够精确
                is_part_of_entity = True
                break

        if word and word not in stop_words and len(word) > 1 and not is_part_of_entity:
            processed_tokens.append(word)

    # 去重 (如果需要)
    processed_tokens = list(dict.fromkeys(processed_tokens))

    return processed_tokens


print("\n开始文本预处理 (结合NER)...")
start_time = time.time()
processed_docs = [preprocess_text_with_ner(text, nlp, stopwords) for text in patent_texts]
end_time = time.time()
print(f"文本预处理完成,耗时: {end_time - start_time:.2f} 秒。")

# 打印一些预处理结果样例
print("\n预处理结果样例 (前2条):")
for i in range(min(2, len(processed_docs))):
    print(f"专利 {i+1}: {processed_docs[i]}")

# --- 4. LDA 模型训练 ---

print("\n开始构建LDA模型...")
start_time = time.time()

# 创建Gensim字典
id2word = corpora.Dictionary(processed_docs)
print(f"创建字典完成,字典大小: {len(id2word)}")

# 创建语料库 (BoW格式)
corpus = [id2word.doc2bow(doc) for doc in processed_docs]
print("创建语料库 (BoW) 完成。")

# 确定主题数量 (这是一个超参数,通常需要尝试不同的值)
# 可以通过计算困惑度(Perplexity)或一致性分数(Coherence Score)来辅助选择
num_topics = 3 # 假设我们想挖掘3个主题 (根据你的数据量和领域知识调整)
print(f"设置主题数量为: {num_topics}")

# 训练LDA模型 (使用多核版本 LdaMulticore 加速)
# passes 控制训练遍数,iterations 控制每次迭代的最大次数
# alpha 和 eta 是先验参数,'auto' 让gensim自动学习
# random_state 保证结果可复现
try:
    lda_model = models.LdaMulticore(
        corpus=corpus,
        id2word=id2word,
        num_topics=num_topics,
        random_state=42,
        chunksize=100, # 每次处理的文档数
        passes=15,     # 整个语料库的训练遍数
        iterations=100,# 对每个文档的迭代次数
        alpha='auto',  # 或者设置为一个浮点数 e.g., 0.1
        eta='auto',    # 或者设置为一个浮点数 e.g., 0.01
        workers=max(1, spacy.util.cpu_count() - 1) # 使用CPU核心数-1
    )
    print("LDA模型训练成功。")

    end_time = time.time()
    print(f"LDA模型训练耗时: {end_time - start_time:.2f} 秒。")

    # --- 5. 结果展示与评估 ---

    print("\n--- LDA 主题结果 ---")
    # 打印每个主题的代表性词语 (实体优先)
    # num_words 控制每个主题显示多少个词
    topics = lda_model.print_topics(num_topics=num_topics, num_words=10)
    for i, topic in enumerate(topics):
        print(f"主题 {i+1}: {topic[1]}") # topic[1] 是主题词字符串

    # (可选) 计算模型的一致性分数 (Coherence Score)
    # C_v 一致性是比较常用和推荐的指标
    print("\n计算模型一致性分数 (C_v)...")
    coherence_model_lda = CoherenceModel(model=lda_model, texts=processed_docs, dictionary=id2word, coherence='c_v')
    coherence_lda = coherence_model_lda.get_coherence()
    print(f'LDA 模型一致性分数 (C_v): {coherence_lda:.4f}')
    # 一致性分数越高,通常表示主题的可解释性越好 (范围一般在0.3到0.7之间较好)

    # (可选) 查看某篇专利的主题分布
    # print("\n查看第一篇专利的主题分布:")
    # doc_lda = lda_model[corpus[0]] # 获取第一篇文档的主题分布
    # print(doc_lda) # 输出格式为 [(topic_id, probability), ...]

except Exception as e:
    print(f"LDA模型训练或评估过程中发生错误: {e}")
    print("可能的原因:数据量过少、文本预处理后内容为空、参数设置问题等。")

print("\n--- 流程结束 ---")

代码说明与注意事项:

  1. spacy.load(): 选择合适的中文模型。zh_core_web_mdzh_core_web_lg 通常比 sm 模型有更好的NER效果,但需要更多内存和下载时间。
  2. stopwords_path: 你需要提供一个中文停用词表文件 (.txt格式,每行一个词,UTF-8编码)。没有停用词表,代码也能运行,但效果会受影响。
  3. patent_texts: 这是你的输入数据。实际应用中,你需要从文件(如CSV, Excel, TXT)或数据库加载你的专利文本列表。
  4. preprocess_text_with_ner() 函数:
    • NER实体选择 (relevant_entity_labels): 这个集合定义了你认为重要的实体类型。你需要根据 spaCy 模型能识别的标签以及你的具体需求来调整。对于特定领域(如建筑),标准的NER模型可能无法识别所有关键技术术语(如“钢筋混凝土”、“预应力张拉”)。这时,理想情况是训练自定义的NER模型,或者采用更复杂的策略(如结合词典、规则)。当前代码优先保留识别出的相关实体。
    • 实体与分词结合策略: 代码中提供了两种思路(只用实体 或 实体+分词)。当前实现的是策略B的简化版:先提取实体,然后对全文分词,最后组合两者并进行过滤。你可以根据需要调整这个逻辑,例如,如果你觉得NER效果足够好,可以只用实体(注释掉分词部分)。
    • 清洗: 使用正则表达式 re.sub 去除非中文字符和字母,并合并多余空格。你可以根据需要调整这个规则。
    • 过滤: 过滤掉空词、停用词和单字词(长度小于等于1)。单字词在中文里通常意义不大,除非是特定的缩写。
  5. gensim.corpora.Dictionary: 创建词语到ID的映射。
  6. corpus: 将文档转换为词袋(Bag-of-Words)表示,这是LDA的输入格式。
  7. num_topics: 主题数量是一个关键超参数。需要根据你的数据量、领域知识以及后续的评估指标(如一致性分数)来调整和选择最佳值。可以写一个循环尝试不同的 num_topics 值,并计算对应的 Coherence Score
  8. models.LdaMulticore: 使用 gensim 的多核LDA实现进行训练。参数如 passes, iterations, alpha, eta 都会影响模型效果,可以根据 gensim 文档进行调优。
  9. lda_model.print_topics(): 显示每个主题下最可能的词语(在这里主要是实体和关键词)。num_words 控制显示词的数量。
  10. CoherenceModel: 计算 C_v 一致性分数,是评估主题模型质量的一个常用指标。分数越高通常越好。
  11. 错误处理: 添加了简单的 try...except 块来捕获LDA训练中可能出现的错误,例如数据太少或预处理后文档变空。

如何使用和改进:

  1. 替换数据: 将 patent_texts 列表换成你自己的专利数据列表。
  2. 停用词表: 确保提供一个合适的中文停用词表。
  3. NER模型与标签: 尝试不同的spaCy模型 (sm, md, lg),并仔细调整 relevant_entity_labels 以匹配建筑行业的重要概念。如果标准模型不够用,考虑:
    • 规则匹配: 结合 spaCyMatcherPhraseMatcher 添加基于规则的实体识别(例如,识别特定的材料名称、工艺流程)。
    • 自定义NER: 如果有标注数据,可以训练一个针对建筑领域的自定义NER模型。
  4. 预处理策略: 调整 preprocess_text_with_ner 函数中的逻辑,比如是否只用实体、如何处理数字、是否保留英文缩写等。
  5. LDA参数调优: 调整 num_topics, passes, iterations, alpha, eta 等参数,并通过一致性分数 (Coherence Score) 或人工评估来选择最佳模型。
  6. 结果可视化: 可以使用 pyLDAvis 库对LDA结果进行交互式可视化,有助于更直观地理解主题。

    # (需要在LDA模型训练成功后运行)
    # pip install pyldavis
    import pyLDAvis
    import pyLDAvis.gensim_models as gensimvis
    
    try:
    print("\n准备LDA可视化...")
    # 注意:pyLDAvis 可能与最新版pandas/numpy有兼容性问题,安装时可能需要指定版本
    vis_data = gensimvis.prepare(lda_model, corpus, id2word, mds='mmds') # 或者 mds='tsne'
    pyLDAvis.display(vis_data)
    # 在Jupyter Notebook 或类似环境中会直接显示交互式图表
    # 如果在脚本中运行,可以保存为HTML文件:
    # pyLDAvis.save_html(vis_data, 'lda_visualization.html')
    # print("LDA可视化数据已生成,可查看 lda_visualization.html 文件。")
    except Exception as e:
    print(f"生成LDA可视化时出错: {e}")
    print("请检查 pyLDAvis 及其依赖是否正确安装,并与当前环境兼容。")
    
    

这个流程结合了NER来聚焦关键信息,有望提取出比单纯基于分词的LDA更有针对性和可解释性的主题。