简介这份资源是一套以机器学习与自然语言处理为核心的古诗自动生成与情感分析项目面向具备基础编程能力的自然语言处理学习者、高校学生及竞赛参赛者覆盖古诗网站语料爬取、数据去重与清洗、分词去停用词、词频统计与关键主题分析、基于格律韵脚的规则作诗以及神经网络模型训练写诗等完整流程。压缩包共一百五十六个文件包含爬虫与模型训练的脚本、古诗文本语料、可视化图表、模型检查点与参数文件、词向量文件、说明文档等多种类型整体约三百三十七兆字节目录结构清晰便于按阶段复现。目前已有二百九十三人学习下载。读者不仅能获得可直接运行的代码与语料还能借助模型检查点、参数文件及配套文档深入理解古诗格律特征提取、情感标签处理和生成式模型的训练调优思路同时从数据采集到模型部署的完整链路也为课程设计、毕业设计或自然语言处理综合项目提供了可靠参考。1. 拆一套古诗生成与情感分析系统从词向量到 checkpoint 的完整链路拿到这套资源的时候大部分人会被里面十几个 checkpoint 文件吓住以为模型很复杂。拆完才发现真正决定项目能不能跑起来的是vectors_poem.bin这一个词向量文件和三个关键训练节点model.ckpt-4000、model.ckpt-52000、model.ckpt-53493。这套系统走的是一条非常标准的机器学习与自然语言处理落地路线爬虫抓语料、清洗分词、词频统计、规则作诗做基准再用 LSTM 训练生成模型最后用情感分析给生成结果打分。它不炫技但胜在链路完整特别适合正在做 NLP 课程设计、毕业设计或者刚入门自然语言处理想找个能跑通全流程项目的人。你不需要自己从零搭环境重点是把数据流理清楚知道每个文件是谁产出的、谁消费的然后照着下面的步骤把模型加载起来。2. 语料爬取与数据清洗从网页到可控训练集的三步实操2.1 爬虫脚本怎么写才不会被封请求参数与解析策略我从这套系统里最常被问到的第一步开始。古诗语料不像新闻语料有现成的公开数据集多数情况要自己爬。爬虫部分用requests加BeautifulSoup就够了Scrapy 对几百首到几万首的规模来说有点重。我一般会把请求封装成一个带重试和超时的函数避免单页请求失败直接中断整个任务import requests from bs4 import BeautifulSoup import time import random headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36, Referer: https://example-poetry-site.com/ } def fetch_page(url, retry3): for attempt in range(retry): try: resp requests.get(url, headersheaders, timeout8) resp.encoding utf-8 if resp.status_code 200: return resp.text except requests.RequestException: time.sleep(2) time.sleep(random.uniform(1, 2)) return None这里timeout8是底线古诗网站响应普遍不快超过 8 秒直接放弃重试比死等更高效。random.uniform(1, 2)的随机休眠是防止请求频率太规律被服务端识别爬少量语料时这个习惯也要养成。headers里的Referer有些老站点会校验漏了可能返回 403。需要说明的是爬取前先看站点根目录有没有robots.txt控制抓取频率这是行业基本规矩。拿到 HTML 之后解析逻辑要针对目标站点的页面结构来写。古诗页面通常每个p是一句诗标题在h1里作者和朝代散落在页面信息区。解析示例如下def parse_poem(html): soup BeautifulSoup(html, html.parser) title soup.find(h1).get_text().strip() author soup.select_one(.author).get_text().strip() dynasty soup.select_one(.dynasty).get_text().strip() lines [p.get_text().strip() for p in soup.select(.poem-content p)] # 过滤空行,保留诗句本身的行结构 lines [ln for ln in lines if ln and not ln.startswith(注释)] return { title: title, author: author, dynasty: dynasty, lines: lines, emotion: }.author、.dynasty这些 class 名称要以实际页面为准我只是给一个结构参考。解析结果按 jsonlines 格式追加保存每行一条完整诗歌记录方便后面清洗和训练时逐行读取。这一步的坑主要在编码上个别老页面用 GBK 编码resp.encoding utf-8之后中文会乱码稳妥做法是用resp.apparent_encoding做一次自动探测。2.2 清洗与去重古诗比现代文本多踩的三个坑语料清洗看着简单古诗场景下有三个坑是处理现代文本时不会遇到的。第一个坑是全角半角混用和多余空白。爬下来的诗句经常带着全角空格和奇怪的缩进直接分词会产出大量无意义 token。第二个坑是 HTML 转义符比如nbsp;和amp;处理前得先反转义否则明月nbsp;这种碎片会被当成独立词。第三个坑最隐蔽不能像处理现代新闻那样把标点全删掉。古诗的句读位置是格律信息的一部分韵脚的识别依赖每句最后一个字删了标点等于丢掉了天然的句子边界。我一般写一个分层清洗函数第一层处理转义和空白第二层只做轻度标点归一化import re import html def clean_poem_line(raw_line): line html.unescape(raw_line) line re.sub(r\s, , line) # 全角逗号、句号统一转成半角,便于后面按句切分 line line.replace(, ,).replace(。, .) # 古诗不删标点,但去掉生僻控制符 line re.sub(r[\x00-\x08\x0b-\x1f], , line) return line.strip(。,. )清洗之后做去重。古诗语料去重不能只看全文同一首诗在不同网站排版可能差一个标点或空格所以要用归一化后的全文做 MD5 或直接比较清洗后的字符串集合seen set() unique_poems [] with open(poems_raw.jsonl, encodingutf-8) as f: for line in f: poem json.loads(line) key .join(clean_poem_line(l) for l in poem[lines]) if key not in seen: seen.add(key) poem[lines] [clean_poem_line(l) for l in poem[lines]] unique_poems.append(poem)这个集合判重方案对十几万条语料完全够用。去重后把结果落成poems_clean.jsonl这套系统后面所有模块都从这份干净语料读取。我在这一步会用len(set())和原始条数做个比值如果重复率超过 30%说明爬取时分页逻辑可能有误需要回头检查。2.3 分词、词频统计与关键词提取为规则引擎和情感分析做准备数据清洗完就开始分词。古诗分词和现代汉语分词不完全一样难点在文言词和专名上比如“澹澹”“将进酒”“谢公”标准词典里没有。解决办法是准备一个带词频的自定义词典用jieba.load_userdict加载词典一行一个词格式是“词 词频 词性”澹澹 10 n 将进酒 20 nz 谢公 5 nr然后对清洗后的诗句做分词和词频统计import jieba import jieba.analyse from collections import Counter jieba.load_userdict(poem_dict.txt) lines [] with open(poems_clean.jsonl, encodingutf-8) as f: for line in f: poem json.loads(line) lines.extend(poem[lines]) tokens [] for line in lines: tokens.extend(jieba.lcut(line)) freq Counter(tokens) print(freq.most_common(50))词频统计结果会暴露一个规律古诗里高频词集中在“明月”“春风”“青山”“流水”这类意象词上这种统计结果就是后面规则作诗选词库的依据。关键词提取用jieba.analyse.extract_tags更直接它基于 TF-IDFtopK20表示取权重最高的前 20 个词corpus .join(lines) tags jieba.analyse.extract_tags(corpus, topK20, withWeightTrue) for word, weight in tags: print(word, weight)分析结果如果要接 SPSS 做后续统计把词频表导出成 CSV 就行字段设计成word,freq,pos三列SPSS 能直接读取做描述统计。这一步产出三个中间文件poem_dict.txt自定义词典、word_freq.csv词频表、keywords.txt关键词表。后面规则作诗引擎直接从word_freq.csv里挑主题词机器学习部分从keywords.txt里选情感标签候选。3. 规则作诗与词向量搭建先跑通一个能压住场面的基准模型3.1 格律约束与韵脚过滤五言七言绝句的规则设计规则作诗在这套系统里不是摆设它是机器学习模型的基准线——用来对照后面 LSTM 写出来的诗到底有没有进步。规则引擎最重要的一点是先把格律框架搭死再往里填词。我习惯以绝句为最小单元因为它只有四句五言和七言的结构约束最清晰。设计上拆成三步定句长、押韵和选意象词。句长直接决定每句字数五言绝句每句 5 字、七言每句 7 字超出或不足都要在候选词阶段处理。押韵是规则引擎的核心筛选条件绝句一般二四句押韵。要判断押韵得先把每个字映射到韵部平水韵有 106 韵部对规则引擎来说太细实操中我常用简化方案按现代汉语拼音韵母聚类in/en归一类ang/an归一类够用但别指望它写出严格的平水韵作品def simplify_rhyme(char): 返回简化韵部,基于拼音韵母做粗糙聚类 pinyin get_pinyin(char) # 需要pypinyin库 if not pinyin: return None final pinyin[-1] # 常见近韵合并,具体规则按自己语料情况调整 groups [ ([in, en, un, ün], en), ([ang, eng, ing, ong], eng), ([an, ian, uan], an), ([a, ia, ua], a), ] for finals, key in groups: if final in finals: return key return final这个简化韵部表不是学术结论是工程取舍。规则作诗的押韵要求本来就是为了让生成结果读起来顺口不是参加诗词大赛。有了韵部映射后第四句的末字要从第二句末字的同韵部候选里选否则判定为不押韵。选意象词时我准备一个主题词表比如“思乡”对应“明月、故乡、秋风”“离别”对应“杨柳、长亭、孤帆”。生成流程是从句库中按主题筛选名词短语先填第一句再用对仗关系推第二句。整个引擎不需要太复杂能稳定产出结构完整、韵脚正确的句子就算合格def generate_rule_poem(subject, style五绝): max_len 5 if style 五绝 else 7 first pick_theme_line(subject, lengthmax_len) second pick_antithesis_line(first, lengthmax_len) rhyme simplify_rhyme(second[-1]) third pick_theme_line(subject, avoidfirst, lengthmax_len) fourth pick_rhyme_line(rhyme, lengthmax_len) return [first, second, third, fourth]pick_antithesis_line从对仗词库里找和第一句对应位置词性相同的词pick_rhyme_line强制要求末字落在rhyme韵部。这个引擎的问题是词与词之间没有语义连贯性经常出“明月对黄河”这种对仗但意象割裂的句子这正是机器学习模型要改进的地方。3.2 从语料中抽取对仗词库与韵脚表规则引擎的词库不是手写的应该是从清洗好的语料里自动抽取的。对仗词库的抽取逻辑并不玄学绝句的第一二句通常是对仗关系所以我按相邻两句的相同位置抽取共现词对。比如第一句第二个字是“明”第二句第二个字是“清”就记一对。统计所有词对的出现次数过滤掉低频对子留下的就是候选对仗词库from collections import Counter pair_counter Counter() poem_sentences [] with open(poems_clean.jsonl, encodingutf-8) as f: for line in f: poem json.loads(line) if len(poem[lines]) 2: poem_sentences.append(poem[lines]) for lines in poem_sentences: s1, s2 lines[0], lines[1] for pos in range(min(len(s1), len(s2))): c1, c2 s1[pos], s2[pos] if c1 and c2 and c1 ! c2: pair_counter[(c1, c2)] 1 common_pairs [pair for pair, cnt in pair_counter.items() if cnt 3]cnt 3这个阈值我调过几次太低了全是噪声太高了词库稀疏到选不出对子。语料规模在几千首时3 次共现是个平衡点。韵脚表也从语料里构建遍历每句的末字建立韵部 - 字集合的映射规则引擎生成第四句时直接从对应集合里取样。3.3 训练并加载 vectors_poem.bin词向量的两种用途这套资源里的vectors_poem.bin是用 Gensim 的 Word2Vec 训练出来的。古诗语料规模通常不大几万句都算多的所以向量维度不用太高我一般设 128 维。min_count设 2 可以滤掉只出现一次的生僻字sg1表示用 skip-gram对稀疏语料的效果通常优于 CBOWfrom gensim.models import Word2Vec sentences [jieba.lcut(line) for line in all_lines] model Word2Vec( sentences, vector_size128, window5, min_count2, epochs10, sg1, workers4 ) model.save(vectors_poem.bin)提一句 Gensim 老版本参数名是size新版改成vector_size如果你用的是库版本较新需要做对应调整。加载方式直接Word2Vec.load(vectors_poem.bin)或加载后取.wv键值向量wv Word2Vec.load(vectors_poem.bin).wv print(wv.most_similar(明月, topn10))词向量在这个系统里有两个用途。第一是给规则作诗引擎做近义词扩展比如主题词是“明月”取向量最近的前 15 个词作为候选意象能大大扩充规则引擎的词库。第二是给机器学习写诗模型做 Embedding 初始化不过要注意如果 LSTM 模型是按字训练的而词向量是按词训练的维度对不上。常见的做法是字向量随机初始化再额外把句子的词向量平均池化作为辅助特征如果你的vectors_poem.bin本身就是按字训练的直接就能用。4. 机器学习写诗与情感分析序列生成、标签训练与模型恢复4.1 字级 LSTM 写诗模型结构设计与输入序列构建机器学习写诗部分我用的是字级 LSTM。选字级而不是词级是因为古诗的生成本质上是在固定格律内逐字选择字级模型能更直接地控制句长和韵脚词级模型在七言句里容易出现分词边界和格律冲突。输入序列的构造方式是从每句诗里切出前缀到后缀的所有子序列让模型学习“看到前面几个字预测下一个字”def build_sequences(lines, max_len16): X, y [], [] for line in lines: chars list(line) if len(chars) 3: continue for i in range(2, len(chars)): seq chars[:i] if len(seq) max_len: seq seq[-max_len:] X.append(seq) y.append(chars[i]) return X, ymax_len是截断长度七言诗一句最多 7 个字按理 max_len 设 7 就够但实际训练时我看的是前两三个字对当前字的约束保留 16 的窗口是为了让模型在长语料上看到更多历史信息。模型结构用单层 LSTM 加一个全连接输出层就够古诗生成不是复杂翻译任务过深的网络在小语料上只会过拟合。Embedding 维度设 128LSTM 隐层 256dropout 挂 0.3import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout model Sequential([ Embedding(vocab_size, 128, mask_zeroTrue), LSTM(256, dropout0.3, recurrent_dropout0.2), Dense(vocab_size, activationsoftmax) ]) model.compile(optimizeradam, losssparse_categorical_crossentropy)sparse_categorical_crossentropy省去了手工做 one-hot 的步骤整数标签直接喂进去。这部分的训练脚本看模型的保存格式就知道是 TensorFlow 1.x 时代产物——资源里是model.ckpt-4000.data-00000-of-00001这种命名。如果直接用 TensorFlow 2.x 重训上面的 Keras 代码没问题如果要去加载资源里给的 checkpoint就得用兼容模式下面详细讲。4.2 checkpoint 训练与恢复三个文件命名背后的训练节奏资源里的 checkpoint 有 4000、52000、53493 三个步数这说明作者在训练过程中多次保存了中间状态。model.ckpt-4000可能是早期试验model.ckpt-52000和model.ckpt-53493间隔 3493 步更像是一轮训练的尾部节点。这种多步数保存的习惯我在实践里也保持原因是生成模型训练的 loss 曲线不能完全代表生成质量留多个节点方便回溯。在 TensorFlow 1.x 的模型恢复场景里checkpoint 由三部分文件组成.meta保存图结构.index保存变量名索引.data-00000-of-00001保存具体变量值。如果你的目录里只有data文件——这套资源大概率就是这样——恢复模型时先要有个图结构通常是重跑一遍训练时的模型定义代码然后指定 checkpoint 路径恢复import tensorflow.compat.v1 as tf tf.disable_v2_behavior() # 需要先重建一模一样的图结构 inputs tf.placeholder(tf.int32, shape[None, None]) embedding tf.get_variable(embedding, [vocab_size, 128]) ... saver tf.train.Saver() with tf.Session() as sess: # latest_checkpoint 会自动找目录下最新的 ckpt ckpt_path tf.train.latest_checkpoint(./checkpoints/) saver.restore(sess, ckpt_path)tf.train.latest_checkpoint会扫描目录下的 checkpoint 文件并选取步数最大的那个如果你想加载 52000 而不是 53493直接手动拼路径传进去。变量名必须完全对齐差一个字母都会报Cannot find match for key错误。每次恢复成功后我都会打印一遍模型里所有变量的名字和 shape确认张量维度不是预期值reader tf.train.NewCheckpointReader(./checkpoints/model.ckpt-53493) for name, shape in reader.get_variable_to_shape_map().items(): print(name, shape)这行命令是排查恢复问题最快的工具能让你一眼看到 checkpoint 里到底存了哪些层、维度是多少避免对着报错瞎猜。4.3 情感分析从标签到分类模型的最小闭环情感分析部分和生成模型共用同一份语料所以数据层面不用额外采集。设计上有两个要点标签体系怎么定、数据不均衡怎么办。古诗的情感标签我建议控制在 56 类比如“思乡、离别、山水、怀古、闺怨、其他”。类别太多会让标注成本飙升分类模型在小样本上也会失衡。如果语料本身没有情感标签就需要人工标注实操上先粗标 2000 条左右覆盖所有类别再训练一个初版模型去辅助标注剩余数据这叫主动学习式打标能省下大量人工。分类模型用 TextCNN 比 LSTM 更稳古诗短句场景下 CNN 的局部 n-gram 特征抓得又快又准。模型结构是 Embedding 加多组卷积核3、4、5 三个窗口宽度各配一组最后接全局池化和 softmaxfrom tensorflow.keras.layers import Input, Embedding, Conv1D, GlobalMaxPooling1D, Dense, Dropout, Concatenate inputs Input(shape(max_len,)) x Embedding(vocab_size, 128, mask_zeroTrue)(inputs) convs [] for kernel_size in [3, 4, 5]: conv Conv1D(filters128, kernel_sizekernel_size, activationrelu)(x) pooled GlobalMaxPooling1D()(conv) convs.append(pooled) x Concatenate()(convs) if len(convs) 1 else convs[0] x Dropout(0.5)(x) outputs Dense(num_classes, activationsoftmax)(x) model tf.keras.Model(inputs, outputs) model.compile(optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy])多组卷积核把相邻字构成的局部特征都捞了一遍“明-月-光”这种连续三字组合在kernel_size3的卷积里会直接形成一个局部模式。情感分析和生成模型在同一套词表上构建Embedding 层也可以复用vectors_poem.bin的向量做初始化。评估指标不能只看 accuracy类别不均衡时 F1 才是真实水平这点放在避坑章展开。5. 避坑指南语料、训练与 checkpoint 恢复的九个翻车现场5.1 checkpoint 与词向量加载的高频报错先说最容易卡住的 checkpoint 加载。现象是saver.restore直接抛异常提示某个变量找不到匹配常见报错是Cannot find match for key embedding in checkpoint。原因是重建的图和 checkpoint 保存时的图结构不一致比如你在 Keras 里定义的 Embedding 层变量名可能叫embedding_1而 checkpoint 里叫embedding。解决方式是先列出 checkpoint 里的变量清单再按名字对齐重建图reader tf.train.NewCheckpointReader(./checkpoints/model.ckpt-53493) var_map reader.get_variable_to_shape_map() for name, shape in var_map.items(): print(name, shape)如果变量名对不上可以在tf.train.Saver(var_list...)里做一层 name 映射把新变量名指向 checkpoint 里的旧名字。另一个常见问题是 checkpoint 文件缺.meta文件只有 data 文件。这种情况下没法直接import_meta_graph必须重跑一遍训练时的模型定义代码让图先存在内存里再用saver.restore加载变量值。词向量加载也有专属坑。现象是Word2Vec.load(vectors_poem.bin)报UnpicklingError因为 Gensim 的save方法保存的是完整 pickled 对象不是裸的二进制向量矩阵。如果这份vectors_poem.bin是用model.wv.save_word2vec_format导出的纯文本词向量格式加载方式完全不同要用load_word2vec_format。我现在拿到陌生的 bin 文件会按四种方式依次尝试from gensim.models import Word2Vec, KeyedVectors try: model Word2Vec.load(vectors_poem.bin) wv model.wv except Exception: try: wv KeyedVectors.load(vectors_poem.bin) except Exception: try: wv KeyedVectors.load_word2vec_format(vectors_poem.bin, binaryTrue) except Exception: raise RuntimeError(词向量文件格式无法识别)这个 try 链每次都能在报错前定位到正确格式省去反复试错。5.2 训练 loss 在降、生成诗却读不通的常见原因有个特别迷惑的情况LSTM 训练 loss 从 5.2 降到 1.8验证集准确率看着还行一生成就是“月月月月月”或者“床前床前床前”这种死循环。第一反应不要怪模型结构先看训练数据量。古诗语料如果只有几百首LSTM 基本是在背诗而不是学语言规律它学到的是高频字的局部共现一旦遇到没见过的 prefix 就反复输出训练集里出现次数最多的字。解决方向是加数据先把语料扩到 5000 首以上再看循环情况。第二个常见原因是采样方式。生成时直接argmax会选概率最高的字连续选最可能字容易陷入重复因为古诗字与字之间的转移概率峰值非常集中。我一般用 temperature 采样缓解把 logits 除以一个温度系数再算概率分布温度越高低概率字被采到的机会越大def sample_with_temperature(logits, temperature0.8): logits logits / temperature probs tf.nn.softmax(logits, axis-1) return tf.random.categorical(probs, num_samples1)temperature 从 0.6 起步0.60.9 区间内生成结果通常能兼顾流畅度和多样性。如果调完仍循环再看训练和验证 loss 差差值超过 0.8 基本可以断定过拟合加大 dropout 或者提前停止。词向量的使用方式也有坑。如果把vectors_poem.bin的词向量直接用来初始化字表 Embedding而语料里的字在词向量里缺失会导致 Embedding 矩阵出现大量随机行训练时这些行还会反向传播污染邻近向量。所以我现在的习惯是先统计词向量覆盖率和未登录字比例覆盖率低于 90% 就直接随机初始化 Embedding把词向量仅作候选词的语义扩展不做权重绑定。5.3 数据标签缺失与分布不均衡时的替代方案古诗情感分析最常见的翻车点是语料根本没有情感标签。第一步不是补标而是看能不能用无监督方法来粗标。我的做法是建一个古诗情感词典——“泪、孤、寒、愁”归“闺怨/悲愁”“山、水、云、松”归“山水”——基于词典对每首诗打分取分值最高的类别作为伪标签。这套方案精度有限但能让你先把 TextCNN 训练起来再人工抽检纠错。标签分布不均衡时模型会把所有诗都判成“山水”因为这类样本最多整体 accuracy 显得很高但 F1 惨不忍睹。先打印各类别样本数class_counts [1200, 300, 250, 180, 70, 400] # 按类别顺序排列 class_weight {i: 1.0 / max(cnt, 20) for i, cnt in enumerate(class_counts)}class_weight会放大少数类在 loss 里的权重让模型不能只靠多数类刷分。评估时用 macro F1 而不是 accuracymacro F1 对每个类一视同仁任何一个类别拉胯都会直接反映在指标上。标签严重不足的类别甚至可以合并到“其他”五分类九十分的模型比七分类六十分的模型在演示场景下更有说服力。6. 生成控制技巧temperature、top-k 与韵脚约束的配合6.1 temperature 与 top-k一对儿配合着调的参数如果只调 temperature 不调 top-k低概率的烂字还是有机会冒出来只调 top-k 不调 temperature又会显得千篇一律。我的习惯是先固定 temperature0.8再试 top-k 的取值。top-k 指的是每次只从概率最高的 k 个字里采样k 从 10 到 50 递增观察输出参数组合生成效果适用场景temperature0.6, top_k10句子稳但词汇重复率高规则感强的五言绝句temperature0.8, top_k30流畅和变化比较均衡七言、长句生成temperature1.0, top_k50意象跳跃大偶尔出彩找灵感人工润色每组参数我固定随机种子跑 50 首抽 10 首人工读比看 loss 值靠谱得多。生成模型的质量判断本身带着审美判断不要只盯着数值。6.2 韵脚强制约束生成后修正比生成时约束更省事在 LSTM 生成过程中强约束韵脚很难实现因为模型逐字生成不到最后一个字不知道这句押不押韵。我的技巧是先放开韵脚生成整句再强制替换句末字为同韵部候选字def force_rhyme(line, rhyme_char): target_rhyme simplify_rhyme(rhyme_char) candidates rhyme_table.get(target_rhyme, [rhyme_char]) new_line line[:-1] random.choice(candidates) return new_line替换后语句大概率会损失一点流畅度但作为演示系统韵脚正确比个别字别扭重要得多。实际使用时可以多生成几十句只保留替换后仍然通顺的句子比逐句硬改效果好。从那以后我每次跑生成类 NLP 项目都会强制把“随机种子固定 关键词表 韵脚表 采样参数”写在一个配置里保证同一份 checkpoint 复现出几乎一致的结果。这套系统折腾下来最大的收获就是规则模型负责兜底机器学习模型负责出彩情感分析负责把关三者用一个词向量文件串联起来整个项目就活了。希望帮到你。本文还有配套的精品资源点击获取