news 2026/8/7 8:53:27

python神经网络编程入门(二十五)——IMDB 数据集预处理与词汇表构建

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
python神经网络编程入门(二十五)——IMDB 数据集预处理与词汇表构建

引言:模型点好了菜,厨房还得有食材

上一章把"词 → 向量"这最后一层翻译官请到位了:词嵌入就是一张查表,查表就能把词变成向量。可查表之前,得先有一张词表——字典都没有,上哪儿查?

从本章开始,系列进入"实战准备篇"的第二站:给模型准备一顿真材实料的饭。第 14 章要训练的情感分类器,吃的不是玩具数据,而是真实的电影评论——IMDB 数据集。真实世界的文本,远比第 11 章那 8 句极简语料野蛮:有 HTML 标签残留、有大小写、有生僻词、有长有短。本章的任务,就是把"野生的"影评加工成模型能下咽的整数序列,并搭好一张词表,供第 14 章的词嵌入层查。

🎯本章目标

  1. 看清 IMDB 数据集长什么样(整数序列 + 词表);
  2. 统计词频,看懂"长尾分布",明白为什么 5000 个词就够用;
  3. 亲手构建 word2idx 词表,留好 PAD 和 UNK 两个特殊位;
  4. 把一条真实影评从文字变成整数 ID 序列。

一、认识 IMDB:25000 条影评的"仓库"

IMDB 是全球最大的电影资料库,自带一个经典的二分类数据集:每条样本是一条电影评论,标签 0 表示差评、1 表示好评。规模是:训练集 25000 条,测试集 25000 条,好评差评各占一半——正好 12500 比 12500,模型想靠"全都猜好评"蒙混过关是行不通的。

数据的存储方式很特别:不是文本,而是一串整数。每一条影评已经变成了一个整数列表,每个整数对应词表里的一个词。这种"预编号"的格式省去了重复分词,让注意力集中在"怎么利用这些数字"上。

# 读取数据的部分已封装好,这里只看结构:# x 是影评(整数序列),y 是标签(0=差评,1=好评)(x_train,y_train),(x_test,y_test)=load_imdb()print(x_train.shape)# (25000,):25000 条训练影评print(x_test.shape)# (25000,):25000 条测试影评print(set(y_train))# {0, 1}:0=差评,1=好评print((y_train==1).sum())# 12500:好评数量print((y_train==0).sum())# 12500:差评数量,正好一半

看一眼第一条影评长什么样:

review0=x_train[0]print(len(review0))# 138:这条影评有 138 个词print(review0[:10])# [23022, 309, 6, 3, 1069, 209, 9, 2175, 30, 1]

一串数字,看不出任何感情色彩。数字背后是什么词?需要一张词表来"翻译"。文字到数字的整条流水线,用一张图概括:

二、词表:数字和单词之间的"翻译官"

数据自带一张词表 word_index:单词 → 编号。把它的方向反过来,就能把整数序列还原成英文:

word_index=load_word_index()# 词表:单词 → 编号idx2word={v:kfork,vinword_index.items()}# 反过来:编号 → 单词words0=[idx2word[i]foriinreview0]print(' '.join(words0[:40]))# bromwell high is a cartoon comedy it ran at the same time as some# other programs about school life such as teachers my 35 years in the# teaching profession lead me to believe that bromwell high's satire

还原出来的是一条动画剧集《Bromwell High》的短评,标签是 1(好评)。词表一共多大?

print(len(word_index))# 88584:近 9 万个不同的词

88584 个词。如果全收进词表,词嵌入层就要维护一张 88584 行的查表,其中大部分词一年都出现不了一次。下一步:数一数每个词到底被用了多少次,看看能不能瘦身。


三、词频统计:谁在刷存在感

把训练集里所有影评的所有词,逐个计数:

fromcollectionsimportCounter counter=Counter()forreviewinx_train:counter.update(review)# 每个词的编号计数 +1print(len(counter))# 88584:训练集共 88584 个不同词total=sum(counter.values())# 5942841:所有词出现次数合计(约 594 万次)forwid,cntincounter.most_common(10):print(idx2word[wid],cnt)# the 336148# and 164097# a 163040# of 145847# to 135708# is 107313# br 101871# in 93934# it 79058# i 77142

第一名是 the,出现 33.6 万次,毫无悬念。有意思的是第 7 名:br。这不是英文单词,它是 HTML 换行标签<br />的残留——影评最初是网页,抓取时标签没清干净,<br />被当成一个"词"混了进来。真实数据往往带着这种杂质,数据清洗的重要性在这里看得一清二楚。

再看看榜单构成:除了 br,其余全是 the、and、a、of、to、is、in、it、i 这类冠词、介词、代词——功能词刷了海量存在感,却几乎不带感情色彩。真正的情绪词(great、awful、boring)都排在几百名开外。这就是词频的典型形态——长尾分布:极少数词出现极多次,绝大多数词只出现几次甚至一次。

把排名和词频画在图上,横轴是词频排名、纵轴是出现次数,两条轴都用对数刻度,尾巴一眼可见:

尾巴到底有多长?训练集里,出现次数不超过 9 次的词有 67987 个,占了全部 88584 个词的 77%。这些"一辈子没露几次脸"的词全收进词表纯属浪费——词嵌入的维度都花在它们身上,却学不到任何语义。


四、截断词表:前 5000 个词就够用

那么词表留多大合适?算一笔账:按词频从高到低累加,前NNN个词覆盖了多少文本:

CN=∑i=1Nfi∑i=1VfiC_N = \frac{\sum_{i=1}^{N} f_i}{\sum_{i=1}^{V} f_i}CN=i=1Vfii=1Nfi

其中fif_ifi是第iii高频词的出现次数,V=88584V = 88584V=88584是全部不同词数。用代码算几个截断点:

defcoverage(n):returnsum(cfor_,cincounter.most_common(n))/totalprint(round(coverage(1000),4))# 0.7637:前 1000 词覆盖 76.4%print(round(coverage(2000),4))# 0.8285:前 2000 词覆盖 82.9%print(round(coverage(5000),4))# 0.9004:前 5000 词覆盖 90.0%print(round(coverage(10000),4))# 0.9425:前 1 万词覆盖 94.3%

前 5000 个词就覆盖了 90% 的文本;再翻一倍到 1 万个词,只多覆盖 4 个百分点。这就像一座城市的人口:最大的几个城市装走了大部分人,剩下几千个小城镇分零头。收 5000 个词,词嵌入表只要 5000 行,既够用又不臃肿。

生活里也有现成的类比:字典收字上万,但常用字表只有 3500 个字,日常阅读已经覆盖 99%。词表截断就是这个道理。


五、构建词表 word2idx:留好两个"特殊座位"

现在动手构建自己的词表。除了 5000 个高频词,还要预留两个特殊位:

  • <PAD>= 0:填充位。影评长短不一,下一章要把它们对齐成等长,多出来的位置用 PAD 补齐;
  • <UNK>= 1:未知位。词表外的生僻词统一归入"未知"口袋,避免程序查表时直接报错。
VOCAB_SIZE=5000word2idx={'<PAD>':0,'<UNK>':1}# 两个特殊位占 0 和 1forwid,_incounter.most_common(VOCAB_SIZE):word2idx[idx2word[wid]]=len(word2idx)# 高频词依次排 2, 3, 4, ...print(len(word2idx))# 5002:5000 个词 + 2 个特殊位print(word2idx['the'])# 2:最高频的词排第一print(word2idx['movie'])# 18print(word2idx['great'])# 85print(word2idx.get('bromwell'))# None:生僻词没资格进词表

“the” 排 2、“movie” 排 18、“great” 排 85——编号越小,词越常见,这是一张"按热度排座"的座位表。生僻词(比如剧名 bromwell)查不到,就归<UNK>


六、把影评变成整数序列:encode 一行搞定

有了词表,编码函数就一行:

defencode(words):return[word2idx.get(w,1)forwinwords]# 查不到 → 1(UNK)

把第一条影评完整走一遍:

words0=[idx2word[i]foriinx_train[0]]# 先还原成单词ids0=encode(words0)print(len(ids0))# 138:词数不变,一一对应print(ids0[:20])# [1, 310, 7, 4, 1070, 210, 10, 2162, 31, 2, 170, 56, 15, 47, 83, 1, 42, 393, 111, 139]print(ids0.count(1))# 19:138 个词里有 19 个是生僻词 → UNK

注意第一位就是 1:bromwell 是生僻词,直接进了<UNK>口袋。138 个词里 19 个 UNK,占比约 14%——因为这是一条评小众剧集的影评,专有名词多,很正常。

再拿一个普通句子验证:

sentence='the movie is great and i loved it'.split()print(sentence)# ['the', 'movie', 'is', 'great', 'and', 'i', 'loved', 'it']print(encode(sentence))# [2, 18, 7, 85, 3, 11, 445, 10]

“the movie is great” 变成[2, 18, 7, 85]。第 14 章的词嵌入层拿到这串编号,查表取出对应行向量喂给 GRU——整条链路从此打通。


七、影评有多长?长短差出 400 倍

再统计一个"身材"指标:影评长度。这直接决定下一章的难度。

lens=[len(r)forrinx_train]+[len(r)forrinx_test]print(min(lens))# 6:最短的影评只有 6 个词print(max(lens))# 2493:最长的有 2493 个词print(round(sum(lens)/len(lens),1))# 233.8:平均 234 词

最短 6 词、最长 2493 词,差了 400 多倍。RNN 按时间步展开,一条 6 词的影评展开 6 步,一条 2493 词的展开 2493 步——长短不一的序列根本没法打包成一批训练。解决办法是下一章的主角:填充(Padding)与掩码(Masking)。先看一眼长度分布的形状:

print(round(sum(1forlinlensifl<=500)/len(lens),4))# 0.9199:92% 的影评不超过 500 词

92% 的影评在 500 词以内,把 500 定为填充上限,只损失不到 8% 的数据——这就是下一章 max_len 的来历。


小结与预告

本章把"野生的影评"加工成了模型能吃的整数序列:

  • 数据:25000 条训练 + 25000 条测试,好评差评各半,格式是整数序列 + 词表;
  • 词频:88584 个不同词、合计约 594 万次,the 以 33.6 万次登顶,br 是 HTML 标签残留;
  • 长尾:前 5000 高频词覆盖 90% 文本,词表只留 5000 词;
  • 词表<PAD>=0<UNK>=1两个特殊位 + 5000 高频词,“the movie is great” →[2, 18, 7, 85]
  • 长度:最短 6 词、最长 2493 词、平均 234 词,92% 影评不超过 500 词。

全流程用一张图收个尾:

下一步,长短不一的整数序列要装进统一大小的"模具"里——第 13 章处理变长序列:填充(Padding)与掩码(Masking),让模型知道哪些位置是真实词、哪些是凑数的空气。

下一篇(二十六):变长序列处理——填充与掩码

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/7 8:46:33

深圳知名网站建设价格解析与实战避坑指南

说到深圳,很多人的第一反应就是“搞钱”,是速度,是创新,是那种走在时代最前沿的紧迫感。在这座被称为“中国硅谷”的城市里,互联网行业如雨后春笋般爆发,大大小小的企业都在忙着建官网、做小程序、搞APP。对于老板或者市场部门负责人来说,当需要找一家靠谱的团队来建设官…

作者头像 李华
网站建设 2026/8/7 8:38:52

从Arduino到STM32:IIC通信协议详解与四位数码管驱动实战

1. 从 Arduino 到 STM32&#xff0c;IIC 通信到底解决了什么问题 如果你刚开始接触单片机&#xff0c;从 Arduino 玩到 STM32&#xff0c;一定会遇到一个坎&#xff1a;怎么让不同的芯片、不同的模块互相“说话”&#xff1f;比如&#xff0c;你想用 Arduino Uno 读取一个温湿度…

作者头像 李华
网站建设 2026/8/7 8:38:35

西湖区外贸网站建设怎么做才地道?深度解析中小企业出海的核心策略与避坑指南

本文关键词:西湖区外贸网站建设在这个互联网信息爆炸的时代,很多企业老板、市场负责人,甚至是刚入行的外贸小白,提到“西湖区外贸网站建设”这个话题,第一反应往往是:找一家公司做几个页面,放几张产品图,搞定SEO,能搜到就行。这种想法在过去或许能行得通,但在今天,尤…

作者头像 李华
网站建设 2026/8/7 8:35:35

超越printf:嵌入式系统高效调试策略与实战工具指南

1. 先搞清楚“只会printf”在电赛调试里到底有多要命 如果你参加过电子设计竞赛&#xff0c;或者做过嵌入式项目&#xff0c;肯定听过“调试全靠 printf ”这句话。这听起来像一句自嘲&#xff0c;但在四天三夜的极限赛程里&#xff0c;它可能直接决定你是能顺利调通系统&…

作者头像 李华