ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

从数学建模到数字人文:基于LDA主题模型与情感分析的唐宋诗定量研究

从数学建模到数字人文:基于LDA主题模型与情感分析的唐宋诗定量研究 1. 项目背景与核心目标从一道赛题看数据驱动的文史研究去年在整理过往的数学建模竞赛资料时我又翻到了2022年认证杯SPSSPRO杯数学建模B题第一阶段的题目——“唐宋诗的定量分析与比较研究”。这道题当时给我留下了很深的印象因为它完美地架起了一座横跨文理学科的桥梁让冰冷的算法和代码去解读温热的唐诗宋词。很多同学初次接触这类题目可能会发懵觉得用数学去分析诗歌是不是有点“不近人情”但恰恰相反这正是当下数字人文研究的一个经典缩影。这道题的核心目标就是要求我们扮演一个“数据侦探”的角色利用SPSSPRO、Python等工具对海量的唐宋诗歌文本进行量化处理通过统计特征、主题模型等手段客观地揭示两个伟大朝代在诗歌创作上的异同比如用词习惯、情感倾向、主题分布等从而为文学研究提供一个全新的、可量化的视角。简单来说它不是一个纯数学推导题而是一个典型的数据科学综合应用题。你需要处理非结构化的文本数据诗歌将其转化为结构化的数字特征然后运用合适的数学模型进行分析和比较最后给出有数据支撑的文学见解。整个过程涉及数据爬取或获取、文本预处理、特征工程、建模分析和可视化呈现等多个环节。对于参赛者而言这不仅考验编程和建模能力更考验将抽象问题转化为具体分析框架的能力。接下来我将结合这道赛题拆解其完整的求解思路、技术细节以及我在实操中积累的一些心得希望能为对数字人文或数学建模感兴趣的朋友提供一个详尽的参考范本。2. 解题框架设计如何将诗歌比较转化为可计算的模型面对“唐宋诗的定量分析与比较研究”这样一个开放性问题第一步也是最关键的一步就是构建一个清晰、可操作的分析框架。我们不能漫无目的地统计字数而是需要设计一系列有文学研究意义且可量化的指标。我的整体框架主要围绕四个维度展开词汇特征、句法特征、情感特征和主题特征。这个框架确保了分析既能触及诗歌的表面形式也能深入其内在意蕴。2.1 分析维度的确立与指标定义首先我们需要明确比较什么。我将其分解为以下几个可计算的层面词汇丰富度与复杂性这是最基础的层面。我们可以计算每首诗的平均句长、平均词长、唯一词占比型例比等。一个初步假设是宋诗说理成分增多可能在词汇多样性上与唐诗的意象密集有所不同。高频词与特色词分析统计唐宋诗各自的高频词如名词、动词、形容词并找出那些在其中一个朝代显著多于另一个朝代的“特色词”。例如唐诗中“明月”、“孤帆”、“黄河”等意象词频率可能极高而宋诗中“人生”、“道理”、“清风”等抽象词或说理词可能更突出。这里需要使用到词频-逆文档频率TF-IDF或者卡方检验等方法来量化“特色”。情感倾向计算利用情感词典如知网Hownet、大连理工大学情感词汇本体等对每一首诗歌进行情感打分。我们可以分析两个朝代诗歌的整体情感基调积极、消极、中性比例以及情感强度的分布差异。例如唐诗的边塞诗可能充满豪迈积极高强度而宋诗的许多作品可能带有更沉郁的感伤消极中强度。主题模型挖掘这是探究深层内容差异的利器。使用LDALatent Dirichlet Allocation主题模型我们可以无监督地从诗歌集合中提取出若干个主题如“山水田园”、“边塞征战”、“怀古咏史”、“哲理思辨”等然后计算每个朝代诗歌在不同主题上的分布比例。这能直观地告诉我们唐宋诗关注的核心话题有何不同。2.2 技术路线图基于以上维度我规划的技术路线如下数据准备获取高质量的唐宋诗全文本数据集并进行清洗去除标题、作者、注释等非诗句部分。文本预处理使用jieba进行分词去除停用词如“之”、“乎”、“者”、“也”等无实义的虚词。特征提取词汇句法特征通过统计计算。情感特征基于情感词典匹配计算。主题特征将分词后的文本转化为词袋模型输入LDA模型训练。比较分析描述性统计对比各项指标的均值、分布。可视化使用柱状图、词云、主题分布饼图等进行展示。统计检验如使用T检验或曼-惠特尼U检验判断唐宋诗在某些指标上的差异是否具有统计学显著性。综合阐释将数据分析结果与文学史知识相结合给出合理的解释和结论。这个框架的优势在于层层递进从形式到内容从浅层到深层形成了一个完整的证据链。在具体实施时SPSSPRO可以很好地承担描述性统计、检验和基础可视化的任务而Python则在数据爬取、文本预处理、复杂建模LDA和高级可视化方面发挥核心作用。3. 数据获取与预处理构建干净的分析语料库巧妇难为无米之炊数据分析的第一步永远是数据。对于本题我们需要一个尽可能全面的唐宋诗电子文本库。理想的数据源应包含诗歌正文、作者、朝代等信息。3.1 数据来源与爬取策略当时我主要考虑了以下几个来源公开数据集如Chinese-poetry一个著名的开源唐诗宋词数据库它提供了结构化的JSON数据包含标题、作者、朝代和段落诗句。这是最推荐的方式质量高且规范。专业网站爬取如古诗文网。需要注意的是爬取时应遵守网站的robots.txt协议并控制请求频率避免对服务器造成压力。使用Python的requests库获取页面用BeautifulSoup或lxml进行解析提取诗歌正文和元数据。注意在实际竞赛中如果组委会提供了数据包则应优先使用。若需自行获取必须在论文中注明数据来源并简要说明清洗过程这是学术规范。3.2 文本清洗与分词实战原始数据往往包含大量“噪音”必须清洗后才能用于分析。以下是我用Python进行预处理的关键步骤及代码示例import jieba import re # 假设一首诗的内容存储在变量 raw_text 中 # 示例raw_text 《静夜思》 李白 床前明月光疑是地上霜。举头望明月低头思故乡。 def preprocess_poem(raw_text): # 1. 去除标题、作者信息这里简化处理实际需根据数据格式定制 # 例如用正则匹配并去除《》及其中的内容以及常见作者名 text re.sub(r《.*?》, , raw_text) # 去除书名号及其中内容 text re.sub(r李白|杜甫|苏轼|王安石, , text) # 简单去除作者名实际应用需更完善的名单 # 2. 去除标点符号和数字 text re.sub(r[^\u4e00-\u9fa5], , text) # 只保留汉字 # 3. 使用jieba进行精确模式分词 words jieba.lcut(text) # 4. 加载停用词表并过滤 with open(stopwords.txt, r, encodingutf-8) as f: stopwords [line.strip() for line in f] filtered_words [word for word in words if word not in stopwords and len(word) 1] # 同时过滤单字 return filtered_words # 处理示例 cleaned_words preprocess_poem(《静夜思》 李白 床前明月光疑是地上霜。举头望明月低头思故乡。) print(cleaned_words) # 输出[床前, 明月, 光, 疑是, 地上, 霜, 举头, 望明月, 低头, 思故乡]3.3 停用词表的定制经验通用的中文停用词表如哈工大停用词表是基础但对于古诗词分析必须进行定制。除了“的”、“了”、“在”等现代汉语停用词还需要额外去除古典虚词之、乎、者、也、矣、焉、哉。常用方位词/副词上、下、东、西、何、岂。高频但分析意义不大的实词根据初步的词频统计如果“千里”、“万里”、“何处”等词在所有诗歌中都极高频且对区分朝代主题贡献不大可以考虑加入停用词表。但这需要谨慎最好在初步分析后决定。预处理阶段的质量直接决定了后续所有分析的可信度。一个常见的坑是分词不准确导致意象词被割裂比如“明月光”被切成“明月”和“光”虽然“光”单独看意义不大但在“明月光”这个特定意象中割裂可能影响分析。因此对于古诗词可以考虑使用jieba的全模式切分后再人工审核或基于词典调整或者直接使用更适配古汉语的分词工具如果存在。在实际操作中我通常先用小样本测试预处理效果反复调整清洗规则和停用词表直到分词结果看起来“顺眼”且符合语言学直觉。4. 多维特征计算与对比分析数据准备好后就可以开始计算我们之前设计的各项指标了。这部分工作可以在Python中完成然后将汇总结果导入SPSSPRO进行统计检验和可视化也可以全部在Python的pandas、scipy、matplotlib生态中完成。4.1 词汇与句法特征计算我们为每一首诗计算以下特征poem_length: 诗歌总字数清洗后。avg_sentence_length: 平均句长总字数/句数。需要先以句号、问号、感叹号等为界进行分句。unique_word_ratio: 唯一词占比独特词数/总词数即型例比TTR反映词汇丰富度。content_word_ratio: 实词名词、动词、形容词占比。这需要词性标注jieba可以配合posseg模块实现。计算完成后我们将所有唐诗和宋诗分别作为两个样本集合计算每个特征的平均值、中位数、标准差等。然后可以使用SPSSPRO中的独立样本T检验若数据符合正态分布或曼-惠特尼U检验非参数检验来判断两个朝代在这些特征上的差异是否显著。4.2 情感分析实现情感分析的核心是情感词典。我以大连理工大学的情感词汇本体为例它包含了词语、词性、情感类别如乐、好、怒、哀、惧、恶、惊、情感强度等信息。# 假设已加载情感词典到一个字典中格式如sentiment_dict {明月: (Pos, 5), 孤寂: (Neg, 3), ...} # Pos/Neg表示积极/消极数字表示强度 def calculate_sentiment(word_list, sentiment_dict): total_score 0 pos_count neg_count 0 for word in word_list: if word in sentiment_dict: category, intensity sentiment_dict[word] if category Pos: total_score intensity pos_count 1 elif category Neg: total_score - intensity neg_count 1 # 情感倾向总得分 0 为积极 0 为消极 sentiment_orientation 积极 if total_score 0 else (消极 if total_score 0 else 中性) # 情感丰富度情感词占比 emotion_richness (pos_count neg_count) / len(word_list) if word_list else 0 return total_score, sentiment_orientation, emotion_richness, pos_count, neg_count # 对每首诗计算情感值计算完每首诗的情感得分后我们可以从两个层面比较整体情感分布统计唐宋诗中积极、消极、中性诗歌的数量和比例绘制成堆叠柱状图。情感强度对比比较两个朝代诗歌情感得分绝对值的均值或分布看哪个朝代的诗歌情感表达更强烈。实操心得古汉语情感词典并不完美很多词汇的情感色彩在现代和古代可能有差异甚至相反。例如“憔悴”在现代多为消极但在某些古诗语境中可能是一种中性的状态描写。因此情感分析的结果需要谨慎解读最好能结合人工抽样校验。一个改进方法是使用基于预训练模型如BERT的情感分析但这对竞赛环境和算力要求较高。4.3 高频词与特色词挖掘这是发现朝代用词习惯差异最直观的方法。from collections import Counter import pandas as pd # 假设 tang_words_list 和 song_words_list 分别是所有唐诗和宋诗分词后的总列表 tang_freq Counter(tang_words_list) song_freq Counter(song_words_list) # 获取各自的前50高频词 tang_top50 tang_freq.most_common(50) song_top50 song_freq.most_common(50) # 使用TF-IDF思想寻找特色词简化版比较相对频率 def find_distinctive_words(freq_dict1, freq_dict2, top_n20): total1 sum(freq_dict1.values()) total2 sum(freq_dict2.values()) word_ratios {} for word in set(freq_dict1.keys()) | set(freq_dict2.keys()): freq1 freq_dict1.get(word, 0) / total1 freq2 freq_dict2.get(word, 0) / total2 if freq1 freq2 0: # 避免除零 # 计算比值比值远大于1说明在第一个集合中更特色 ratio freq1 / freq2 if freq2 ! 0 else float(inf) word_ratios[word] ratio # 找出在第一个集合中相对频率最高的词 distinctive_for_1 sorted([(w, r) for w, r in word_ratios.items() if r 1], keylambda x: x[1], reverseTrue)[:top_n] # 找出在第二个集合中相对频率最高的词 distinctive_for_2 sorted([(w, 1/r) for w, r in word_ratios.items() if r 1 and r 0], keylambda x: x[1], reverseTrue)[:top_n] return distinctive_for_1, distinctive_for_2 tang_distinctive, song_distinctive find_distinctive_words(tang_freq, song_freq) print(唐诗特色词:, tang_distinctive[:10]) print(宋诗特色词:, song_distinctive[:10])通过这个方法我们可能会发现唐诗的特色词集中在“明月”、“青山”、“流水”、“孤帆”、“征战”等自然意象和宏大场景上而宋诗的特色词则可能出现“人生”、“世事”、“读书”、“清风”、“闲适”等更具哲理性和个人化色彩的词汇。将这些结果用词云图可视化效果非常直观。5. LDA主题模型探析唐宋诗深层主题差异LDA主题模型是本次分析的重头戏它能帮助我们超越单个词汇发现诗歌中隐藏的主题结构。其基本原理是认为每篇文档这里是一首诗由多个主题以一定比例混合而成而每个主题又是词汇表上的一组概率分布。5.1 LDA建模流程与参数选择我们使用gensim库来实现LDA。from gensim import corpora, models import gensim # 1. 准备文档列表每个元素是一首诗的过滤后词汇列表 tang_poems_words [[床前,明月,光, ...], [白日,依山,尽, ...], ...] # 唐诗语料 song_poems_words [[明月,几时,有, ...], [横看,成岭,侧成,峰, ...], ...] # 宋词语料 # 合并所有诗歌用于构建统一的词典和语料库 all_poems_words tang_poems_words song_poems_words # 2. 创建词典和语料库 dictionary corpora.Dictionary(all_poems_words) # 可选过滤极端词出现太少或太多 dictionary.filter_extremes(no_below5, no_above0.5) # 至少出现在5首诗最多出现在50%的诗中 corpus [dictionary.doc2bow(text) for text in all_poems_words] # 词袋模型表示 # 3. 训练LDA模型 # 关键参数num_topics主题数, passes迭代次数 lda_model gensim.models.LdaModel(corpuscorpus, id2worddictionary, num_topics10, # 主题数量K需要调优 random_state42, passes20, # 迭代次数确保收敛 alphaauto, # 让模型学习文档-主题分布的稀疏性 etaauto) # 让模型学习主题-词汇分布的稀疏性5.2 主题数K的选择与模型评估主题数num_topics的选择是个艺术活。K太小主题过于笼统K太大主题过于琐碎且可能过拟合。我通常采用以下方法综合确定一致性分数Coherence Score计算不同K值下的主题一致性选择分数较高的点。gensim提供了CoherenceModel。主题可解释性人工审视K5,8,10,12,15等不同设置下生成的主题。一个好的主题应该能用一个简短的短语概括如“山水隐逸”、“边塞豪情”。研究需求根据对唐宋诗的基本了解预设一个大概的范围如8-12个主题。from gensim.models import CoherenceModel coherence_scores [] for k in range(5, 16, 2): lda gensim.models.LdaModel(corpuscorpus, id2worddictionary, num_topicsk, passes10, random_state42) coherence_model CoherenceModel(modellda, textsall_poems_words, dictionarydictionary, coherencec_v) coherence_score coherence_model.get_coherence() coherence_scores.append((k, coherence_score)) print(f主题数 K{k}, 一致性分数: {coherence_score:.4f})在我的实验中K10通常能取得较好的平衡得到诸如“田园山水”、“思乡怀人”、“咏史怀古”、“哲理禅意”、“宴饮酬唱”、“边塞战争”、“闺怨爱情”、“时序感伤”、“咏物言志”、“羁旅愁思”等有区分度的主题。5.3 主题可视化与朝代对比训练好模型后我们可以查看每个主题下的核心词汇并计算每首诗的主题分布。# 查看主题 topics lda_model.print_topics(num_words10) # 每个主题显示前10个词 for topic in topics: print(topic) # 获取单首诗的主题分布 def get_topic_distribution(poem_words): bow dictionary.doc2bow(poem_words) topic_dist lda_model.get_document_topics(bow, minimum_probability0.0) # topic_dist 是一个列表元素为 (主题id, 概率) return topic_dist # 计算整个朝代在主题上的平均分布 def get_dynasty_topic_profile(poems_words_list): dynasty_profile [0] * lda_model.num_topics for poem_words in poems_words_list: topic_dist get_topic_distribution(poem_words) for tid, prob in topic_dist: dynasty_profile[tid] prob # 归一化 total sum(dynasty_profile) dynasty_profile [p/total for p in dynasty_profile] return dynasty_profile tang_profile get_dynasty_topic_profile(tang_poems_words) song_profile get_dynasty_topic_profile(song_poems_words)最后将tang_profile和song_profile这两个列表用堆叠柱状图或雷达图进行对比。你会发现唐诗可能在“边塞战争”、“山水田园”主题上占比更高而宋诗则在“哲理禅意”、“咏史怀古”主题上更为突出。这个发现与文学史中“唐诗主情宋诗主理”的定性描述形成了有趣的量化印证。踩坑提醒LDA的结果具有随机性由random_state控制每次运行可能略有不同。为了结果稳定可以多次运行取平均或者设置固定的随机种子。另外预处理特别是停用词对LDA结果影响巨大不合适的停用词会导致主题混杂无意义。6. 结果综合与论文撰写要点完成所有计算和分析后最后一步是将散点的发现整合成一个连贯的故事并撰写成数学建模论文。这部分往往比建模本身更能体现水平。6.1 如何组织你的发现论文的结果部分不应是图表和数字的堆砌而应有逻辑地呈现基础特征对比首先展示词汇、句法、情感等基础指标的对比结果用表格和简洁的统计检验结果p值说明哪些差异是显著的。例如“数据显示宋诗的平均句长显著长于唐诗p0.01且唯一词占比略低这可能暗示宋诗语言更趋平实、说理而唐诗更凝练跳跃。”词汇景观展示接着用词云图展示各自的高频词用表格列出前10的“特色词”并加以文学化的解读。例如“唐诗高频词云中‘山’、‘月’、‘云’、‘水’等自然意象占据视觉中心而宋诗词云中‘人’、‘风’、‘年’、‘心’等字眼更为突出反映了从外向自然到内向心灵的关注点转移。”主题深度洞察这是论文的亮点。用雷达图清晰展示两个朝代在10个主题上的分布差异。结合具体诗例进行解释。例如“主题模型显示在‘边塞战争’主题上唐诗的占比15%远高于宋诗5%。我们随机抽取该主题下概率最高的唐诗如王昌龄的《出塞》其‘秦时明月汉时关万里长征人未还’的雄浑悲凉正是此主题的典型体现。而宋诗在‘哲理禅意’主题上的优势12% vs 5%则可在苏轼《题西林壁》‘不识庐山真面目只缘身在此山中’得到验证。”综合讨论将以上所有线索串联起来提出一个综合性的结论。例如“定量分析从多个维度支持了文学史上的经典论断。唐诗在词汇上更具意象的跳跃性和丰富性情感表达更为强烈外放主题上偏爱对自然与边塞的宏大叙事而宋诗则表现出更强的说理性和内省性用词更趋日常化和理性化主题上更关注历史、人生哲理与个人心境。这种差异可能与唐宋两代不同的社会文化背景、士人心态密切相关。”6.2 建模论文的“加分项”与常见误区加分项敏感性分析讨论一下你的结论是否稳健。例如改变LDA的主题数K比如从8调到12主要结论是否依然成立调整情感词典或停用词表关键指标的趋势是否不变模型局限性主动指出你方法的不足。例如情感词典对古汉语的适应性、LDA模型无法捕捉词序和语法关系、数据集的代表性可能存在的偏差等。这体现了批判性思维。可视化美学图表要清晰、专业、美观。使用一致的配色方案如唐诗用红色系宋诗用蓝色系给图表加上完整的标题、坐标轴标签和图例。代码与数据在附录中提供核心代码的片段注意不要暴露个人信息或违规内容并说明完整代码和数据的获取方式如果允许。常见误区只有数据没有解读这是最大的忌讳。每一个数字和图表都必须配有文字解释说明它“意味着什么”。结论绝对化避免使用“证明”、“完全”等绝对化词汇。使用“支持”、“表明”、“暗示”、“可能反映了”等更严谨的表述。忽视统计显著性在比较差异时一定要进行统计检验不能只看平均值的大小就下结论。技术细节堆砌论文主体应聚焦于分析思路、结果和结论。将复杂的公式推导、冗长的代码放入附录。回顾整个解题过程从数据清洗到模型构建再到最后的论文呈现其核心思想是一致的将人文问题转化为可计算、可验证的科学问题。这道题的魅力在于它要求你既要有理工科的严谨逻辑又要有文科的阐释能力。在实际操作中最大的挑战往往不是代码怎么写而是如何设计有意义的分析维度以及如何让数据“说话”讲出一个令人信服的故事。我个人的体会是在开始编码前花足够的时间进行文献调研和框架设计与队友充分讨论每一个指标的含义往往能事半功倍。最后别忘了享受这个过程当你用代码跑出那个印证了千年文学史常识的图表时那种跨学科探索的乐趣是无与伦比的。
返回列表