南邮NLP实验一:Anaconda环境、jieba分词与词频统计实战
2026/9/23 1:36:07 网站建设 项目流程

简介:南邮自然语言处理课程实验一资源,围绕词典分词与二元语法分词两大核心任务,整理了一份可逐项对照学习的实验报告。内容覆盖HanLP的命令行分词、取消词性标注参数、文件输入输出分词、句法分析指令,并给出商品和服务、结婚的和尚未结婚的等典型句子的分词结果;同时整理了前向最长匹配、后向最长匹配、双向最长匹配三种词典分词算法的Python代码与结果对比,讲解三种算法的区别与适用性。资源还附有教材第27页相关Python实现示例和HanLP核心词典路径说明,便于在Windows、Python3.7/3.8环境下复现。资源为doc文档1份,压缩包大小232KB,内容精炼、层级清楚,适合南京邮电大学计算机相关专业学生及NLP初学者快速掌握实验要点。已有421人学习下载,对完成课程实验和巩固自然语言处理基础具有实用参考价值。

1. 南邮自然语言处理实验一:第一个实验在动手之前要认清什么

第一次做南邮自然语言处理实验一的人,最容易把时间耗在“装环境”而不是“跑通任务”上。这个实验名义上是自然语言处理入门,实际考察的却是一个最小闭环:拿到一份中文语料,完成分词、过滤、统计,最后输出一组能被解释的词频结果。很多人在这一步就卡住,往往不是算法不会,而是编码没处理、分词模式选错、停用词没过滤。这篇内容适合第一次打开 Anaconda、第一次写中文文本处理脚本的读者,也适合已经写完但结果和预期对不上的情况——实验一的坑不在模型,而在数据处理的基本功。

2. 先把工具链理清楚:Anaconda、Jupyter 与实验一依赖

2.1 为什么实验一普遍要求 Anaconda 而不是裸 Python

自然语言处理课程从实验一开始就引导安装 Anaconda,不是因为 Python 本身跑不了中文处理,而是 Anaconda 解决了进实验室的第一道障碍——依赖和路径。Windows 上裸装 Python 再逐个 pip install,最容易在 numpy、scipy 这类带二进制扩展的包上踩编译错误;而 Anaconda 自带 conda 包管理器和预编译的科学计算栈,能在不碰系统 Python 的前提下隔离出一个干净的实验环境。

从实验一的角度看,Anaconda 的第二个价值是 Jupyter Notebook。NLP 实验的典型工作流是“读语料、看分词结果、调参数、再读一段”,这种交互式迭代恰好是 Jupyter 的强项:单元格级执行让每一步的输出都能立刻被检查。相比之下,写一个完整 .py 再整体运行,遇到分词结果不理想时来回改成本高很多。

第三个理由和可复现性有关。实验报告要求截图和输出,Jupyter 天然把代码、输出、说明文字放在同一个文件里,导出的 HTML 或 PDF 可以直接作为实验报告素材。南邮实验一如果要求提交代码和结果分析,用 Notebook 组织会比零散脚本更接近评分标准里的“实验过程完整”这一项。

2.2 创建独立环境与安装 jieba 的完整命令

不建议直接在 base 环境里装包。实验一后续还有二、三、四,不同实验可能依赖不同版本的库,独立环境能把互相污染的风险降到最低。常见做法是创建命名环境,比如叫 nlp-lab:

conda create -n nlp-lab python=3.10 -y conda activate nlp-lab conda install jupyter -y pip install jieba

逐条说明这几条命令的作用。第一条用 conda 创建名为 nlp-lab 的环境,指定 Python 3.10,-y 跳过确认提示;第二条激活环境,激活后命令行前缀会变成 (nlp-lab);第三条在当前环境里装 Jupyter,第四条用 pip 装 jieba。为什么用 pip 而不是 conda 装 jieba?因为 jieba 是纯 Python 包,没有二进制依赖,pip 和 conda 效果一致,但 jieba 在 conda 默认源里的更新往往滞后,pip 拿到的版本更新。

装完之后验证环境是否正常:

conda env list

输出里带有 * 号的就是当前激活的环境。确认是 nlp-lab 后,再启动 Jupyter:

jupyter notebook

如果浏览器没有自动打开,把终端里输出的带 token 的 URL复制到浏览器即可。这是实验一最常见的环境卡点之一,多数情况下不是装失败,而是没看清终端里的 URL。

2.3 环境验证:跑通第一段 NLP 代码

环境装好不等于能用,先用最小代码验证 jieba 是否可导入、中文是否能正确分词:

import jieba text = "南邮自然语言处理实验一" print(list(jieba.cut(text)))

输出应该是 ['南邮', '自然语言', '处理', '实验', '一'] 这样的分词结果。这里有两个值得注意的点:第一,jieba.cut 返回的是生成器,必须用 list() 包裹才能一次性看到全部词;第二,如果输出出现 \u5357 之类转义字符,说明终端编码有问题,Python 3 下通常在代码开头不需要额外处理,但 Windows 终端建议设置 chcp 65001 切换到 UTF-8。

依赖包用途实验一中的角色
jieba中文分词核心依赖,所有文本切分都靠它
jupyter交互式执行代码与报告的组织载体
numpy数值计算构建 n-gram 计数矩阵时需要
pandas表格处理词频结果整理与导出

这张表里的前三项是实验一装完就必须能 import 的,pandas 可以等做到实验三、四再补,但如果实验一要求把词频结果整理成 CSV,直接用 pandas 会更省事。

3. 实验一核心代码:中文分词、停用词过滤与词频统计

3.1 读入语料的编码问题

实验一的语料通常以 .txt 形式给出,第一个坑就是编码。绝大多数中文语料是 UTF-8 编码,但偶尔会遇到 GBK 或 GB2312,尤其是在 Windows 下另存的文本。用错误编码读取,轻则出现乱码,重则直接 UnicodeDecodeError。

稳妥的读法是指定 encoding,并处理可能的异常:

def load_text(path): for enc in ["utf-8", "gbk", "utf-8-sig"]: try: with open(path, "r", encoding=enc) as f: return f.read() except UnicodeDecodeError: continue raise ValueError(f"无法识别文件编码: {path}")

这个函数依次尝试三种常见编码。utf-8-sig 放在最后是因为它会自动处理 BOM 头;如果文件是 UTF-8 无 BOM,用 utf-8-sig 读取也不会出错,只是多一次尝试。实际使用中,可以先读出前几个字节判断编码,但这个顺序尝试版本足够应对实验一的所有情况。

3.2 jieba 分词的三种模式与参数选择

jieba 提供三种分词模式:精确模式、全模式和搜索引擎模式。精确模式是最常用的默认模式,适合做词频统计,因为它把句子切分成最合理的一组词,没有冗余;全模式会把所有可能成词的组合都切出来,比如“南京市”会同时切出“南京”“南京市”“京市”,这种冗余对词频统计是灾难;搜索引擎模式在全模式基础上做合并,主要用于搜索场景。

实验一选择精确模式即可,对应的调用是 jieba.cut(text, cut_all=False),这也是默认值。有几个参数值得注意:

import jieba # 精确模式,默认 cut_all=False words = jieba.cut(text) # 保留词性,便于后续按词性过滤 words_with_pos = jieba.posseg.cut(text) # 加载自定义词典,补充领域词 jieba.load_userdict("userdict.txt")

cut_all 参数控制是否全模式;jieba.posseg.cut 返回带词性的对偶,比如 ("南邮", "nt"),如果实验要求统计动词或名词,就用到这个接口;load_userdict 用于把“自然语言处理”这类 jieba 可能切错的词强制切成一个整体,词典格式是每行一个词,后面可以跟词频和词性,例如“自然语言处理 5 n”。

分词结果的质量直接影响后续统计。一个常见的问题是:包含标点、数字、英文混排的文本,分出来的“词”里有大量噪声。这时候需要在分词前做清洗,或者分词后过滤。

3.3 用 Counter 统计词频并输出 Top-K

词频统计在 Python 里最顺手的工具是 collections.Counter。它接收一个可迭代对象,自动统计每个元素出现的次数:

from collections import Counter import re import jieba def clean_and_cut(text): # 只保留中文字符,去掉标点与英文数字 text = re.sub(r"[^\u4e00-\u9fa5]", " ", text) return [w.strip() for w in jieba.cut(text) if w.strip()] def top_k_words(text, k=20, stopwords=None): words = clean_and_cut(text) if stopwords: words = [w for w in words if w not in stopwords] counter = Counter(words) return counter.most_common(k)

正则替换把非中文字符统一换成空格,这样标点和数字不会混进分词结果。停用词过滤放在分词之后、统计之前,顺序错了会导致停用词被拆分,出现“我 是 南邮 学生”这种片段里混入单字的情况。停用词表可以直接用网上的通用中文停用词表,也可以根据自己的语料手动维护,核心是保留“南邮”“自然语言”这类领域词,过滤“的、了、是、在”这类高频虚词。

输出时,建议同时打印词频和占比,而不是只打印次数:

total = sum(counter.values()) for word, count in counter.most_common(20): print(f"{word}\t{count}\t{count/total:.4f}")

占比比绝对次数更有解释力。比如“南邮”出现 120 次,如果总词数是 8000,占比 1.5%,这个信息在实验报告里可以直接说明文本的主题集中度。

4. 从词频到 n-gram:让实验一多拿分的特征工程

4.1 为什么词频只是第一步:bi-gram 的构建

词频统计给出了“每个词出现多少次”的一维视角,但自然语言的重要信息恰恰在词与词的顺序关系里。“南京”和“大学”分开看是两个高频词,合在一起“南京大学”才是一个实体。实验一如果只交词频统计,能及格,但要拿高分,通常要证明你理解了 n-gram 的基本思想——把相邻词作为一个整体来统计。

n-gram 的做法是把文本切成长度为 n 的滑动窗口。n=1 就是 unigram,对应词频统计;n=2 是 bigram,统计相邻两个词的组合;n=3 是 trigram。bigram 能捕捉“自然语言”“语言处理”这类局部搭配,而这些往往是主题词。对实验一来说,实现到 bigram 已经足够展示对上下文的理解,再往上 trigram 的数据会变得稀疏,统计意义反而下降。

4.2 实现一个最小 bigram 模型

在分词结果上构建 bigram 非常简单,不需要额外安装库:

from collections import Counter from itertools import islice def build_bigrams(words): # 相邻两词组成一个二元组 return Counter(zip(words, islice(words, 1, None))) bigram_counter = build_bigrams(words) print(bigram_counter.most_common(10))

zip 把 words 和去掉首元素后的 words 逐对拼接,islice 生成偏移一个位置的迭代器,避免显式写索引循环。这个实现的效率足够处理几十万字的语料,因为生成器是惰性求值的,内存占用可控。拿到 bigram 统计后,可以进一步筛选:只保留出现次数大于等于 3 的组合,减少噪声,比如“的 时候”“在 这里”这类高频但无信息的搭配。

4.3 用困惑度(perplexity)粗略验证

建好 bigram 后,一个自然的验证方式是计算困惑度。困惑度越低,说明模型对语料的拟合越好。对实验一来说,不需要实现平滑算法,用最简单的相对频率估计即可:

import math def bigram_perplexity(bigram_counter, unigram_counter, test_words): total_bigrams = sum(bigram_counter.values()) total_unigrams = sum(unigram_counter.values()) log_prob_sum = 0.0 n = 0 for w1, w2 in zip(test_words, islice(test_words, 1, None)): prob_bigram = bigram_counter.get((w1, w2), 0) / total_bigrams prob_unigram = unigram_counter.get(w1, 0) / total_unigrams # 如果 bigram 没出现,退化为 unigram 概率,避免 log(0) prob = prob_bigram if prob_bigram > 0 else prob_unigram if prob > 0: log_prob_sum += math.log(prob) n += 1 if n == 0: return float("inf") return math.exp(-log_prob_sum / n)

这里用了一个最简单的回退策略:bigram 没出现时退回到 unigram 概率。真正的实验报告里不需要实现完整的 Kneser-Ney 平滑,但能说明这个回退逻辑,已经比只交一个词频统计高出一个层次。可以用语料的前 80% 构建计数,后 20% 计算困惑度,对比 unigram 和 bigram 的困惑度差异,数字越小说明 n-gram 确实捕获了词序信息。

5. 提交前自查:结果验证与四个常见扣分点

5.1 用内置语料做端到端回归

实验一交上去之前,先用一段你知道标准答案的小文本跑一遍,确认每个环节没有静默出错。比如用“南京市长江大桥欢迎你”这句话,期望的分词结果是“南京市/长江大桥/欢迎/你”,而不是“南京/市长/江大桥”。如果分错了,用 load_userdict 把“长江大桥”加进词典,再重新跑。这个检查只需要十几秒,但能避免最尴尬的“程序跑完、结果全错”的情况。

5.2 编码、随机性与可复现性

四个常见扣分点:第一,读文件没指定 encoding,在 Windows 上偶发乱码,截图时输出是坏的;第二,没有固定随机种子——如果实验涉及随机采样,比如从语料中抽取子集,建议 random.seed(42),否则每次运行结果不同,报告里写的数据对应不上;第三,停用词过滤不完整,词频 Top-K 里全是“的”“了”这类虚词,看起来像是没做预处理;第四,输出没有保存,只在 Jupyter 里有展示,提交时临时重新跑,结果有出入。

提示:Jupyter 里跑完后,用 df.to_csv("result.csv", index=False) 把中间结果落盘,确保报告里的截图和数据文件能对应上,这比提交前临时截图可靠得多。

最后的一个实用技巧是给代码加一个“最小语料”测试:把一份只有几千字的样本和期望输出硬编码进测试函数,跑通后再处理全量语料。这个习惯能让实验一从“写完碰运气”变成“每步可验证”——评分看的不只是结果对不对,更是这个过程是否经得起追问。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询