当前位置: 首页 > news >正文

循环网络RNN--评论内容情感分析

一、构建字表

基于微博语料库构建中文字表,通过统计字频筛选有效字符,为每个字符分配唯一索引,并加入未知字符<UNK>和填充字符<PAD>,最终将词表保存为 pickle 文件

代码:

from tqdm import tqdm import pickle as pkl MAX_VOCAB_SIZE=4760 UNK,PAD='<UNK>','<PAD>' def build_vocab(file_path,max_size,min_frep): '''功能:基于文本内容建立词表vocab,vocab中包含语料库中的字 参数file_path:需要读取的语料库的路径 max_size:获取词频最高的前max_size个词 min_freq:剔除字频低于min_freq个的词 ''' tokenizer = lambda x: [y for y in x]# 简单的函数定义了一个函数tokenizer,功能为分字 vocab_dic = {} with open(file_path, 'r',encoding='UTF-8') as f: i = 0 for line in tqdm(f):# 用来显示循环的进度条 if i == 0: # 跳过文件种第1行表头内容 i += 1 continue lin = line[2:].strip() # 获取评论内容,剔除标签。不用split分割,因为评论内容中可能会存在逗号。 if not lin: continue for word in tokenizer(lin): vocab_dic[word] = vocab_dic.get(word,0)+1 vocab_list = sorted([_ for _ in vocab_dic.items() if _[1] > min_frep],key = lambda x: x[1],reverse = True)[:max_size] vocab_dic = {word_count[0]: idx for idx,word_count in enumerate(vocab_list)} vocab_dic.update({UNK: len(vocab_dic),PAD: len(vocab_dic) + 1}) # {"<UNK>':4760,"<PAD>':4761} print(vocab_dic) pkl.dump(vocab_dic,open('simplifyweibo_4_moods.pkl','wb')) # 现在我完成:统计了所有的文字,并将每一个独一无二的文 print(f"Vocabsize:{len(vocab_dic)}") return vocab_dic if __name__=="__main__": vocab=build_vocab('simplifyweibo_4_moods.csv',MAX_VOCAB_SIZE,3)

1. 分字器tokenizer = lambda x: [y for y in x]

作用:中文分字(因中文无空格分隔单词,NLP 基础任务常按字符处理)

原理:匿名函数lambda接收字符串x,通过列表推导式将字符串拆分为单个字符的列表

示例:输入"我喜欢NLP",输出["我", "喜", "欢", "N", "L", "P"]

2. 字频统计vocab_dic.get(word, 0) + 1

核心:利用字典get方法实现高效字频统计,无需先判断字符是否在字典中

如果word已在vocab_dic中,取其当前值(已出现次数)加 1

如果word不在vocab_dic中,默认返回 0,加 1 后即为首次出现(次数 = 1)

3. 词表排序与截取

sorted([_ for _ in vocab_dic.items() if _[1] > min_freq], key=lambda x: x[1], reverse=True)[:max_size]

筛选:_[1] > min_freq 保留字频超过阈值的字符(_[1]为元组中的字频值)
排序:key=lambda x: x[1] 按字频排序,reverse=True 降序(高频在前)
截取:[:max_size] 仅保留前MAX_VOCAB_SIZE个高频字符,控制词表容量

4. UNK 和 PAD 的作用

<UNK>(未知字符):词表中未收录的生僻字、外来字、特殊符号,统一映射到该索引,避免模型因未见过的字符报错

<PAD>(填充字符):NLP 模型要求输入文本长度一致,需用该字符将短文本补全到指定长度,长文本截断,保证输入维度统一

索引分配:在基础词表后连续分配,确保索引唯一且无冲突(示例:基础词表 4760 个字符→UNK=4760,PAD=4761,总词表大小 4762)

5. pickle 保存词表pkl.dump(vocab_dic, open('xxx.pkl', 'wb'))

原因:pickle 是 Python 专用的序列化格式,能完整保留字典的键值对结构,且二进制写入节省存储空间

使用:在模型训练 / 推理时,可通过pkl.load(open('xxx.pkl', 'rb'))快速加载词表,无需重新统计

二、数据集加载与预处理

1.函数功能:

  • 加载之前构建的字表文件,实现字符→数值索引的映射;

  • 读取原始 CSV 语料,提取文本标签和评论内容,并对文本进行中文分字;

  • 对所有文本做长度统一处理(短文本填充、长文本截断),适配模型固定输入长度要求;

  • 将字符序列转换为数值索引序列(模型可处理的输入格式);

  • 对所有数据随机打乱,按8:1:1比例切分训练集、验证集、测试集;

  • 输出字表和切分后的三类数据集,为后续模型训练做准备。

代码:

from tqdm import tqdm import pickle as pkl import random import torch UNK,PAD='<UNK>','<PAD>' def load_dataset(path, pad_size=70): contents =[] # 用来存储转换为数值标号的句子 vocab = pkl.load(open('simplifyweibo_4_moods.pkl','rb')) # 读取vocab文件 tokenizer = lambda x: [y for y in x] with open(path,'r',encoding='UTF-8') as f: i = 0 for line in tqdm(f): if i == 0: i += 1 continue if not line: continue label = int(line[0]) content = line[2:].strip('\n') words_line = [] token = tokenizer(content) # 将每一行的内容进行分字 seq_len = len(token) # 获取一行实际内容的长度 if pad_size: # 判断每条评论是否超过70个字 if len(token) < pad_size: # 如果一行的字少于70,则补充<PAD> token.extend([PAD] * (pad_size - len(token))) else : # 如果一行的字大于70,则只取前70个字 token = token[:pad_size]# 如果一条评论种的字大于或等于70个字,索引的切分 seq_len = pad_size# 当前评论的长度 # word to id for word in token: words_line.append(vocab.get(word,vocab.get(UNK))) # 把每一条评论转换为独热编码 contents.append((words_line,int(label),seq_len)) random.shuffle(contents)# 打乱顺序 train_data = contents[:int(len(contents) * 0.8)] # 前80%的评论数据作为训练集 dev_data = contents[int(len(contents) * 0.8):int(len(contents) * 0.9)] # 把80%~90%的评论数据集作为验证数据 test_data = contents[int(len(contents)*0.9):] # 90%~最后的数据作为测试数据集 return vocab, train_data, dev_data, test_data

1.文本长度统一(填充 / 截断)

pad_size=70 # 目标固定长度:所有文本最终统一为70个字符 token = tokenizer(content) # 对评论内容分字,得到字符列表 seq_len = len(token) # 记录文本**原始实际长度**(后续模型可利用该信息,避免填充字符干扰) if pad_size: # 若指定了固定长度,则执行填充/截断 if len(token) < pad_size: # 短文本:填充<PAD> token.extend([PAD] * (pad_size - len(token))) # 末尾补充PAD,直到长度为70 else: # 长文本:截断 token = token[:pad_size] # 只取前70个字符,截断多余部分 seq_len = pad_size # 截断后实际有效长度为pad_size

2.字符→数值索引

words_line = [] for word in token: # 核心映射逻辑:存在则取字符索引,不存在则取UNK的索引 words_line.append(vocab.get(word, vocab.get(UNK)))
  • 若字符在字表中(高频基础字符),直接取其对应的唯一索引

  • 若字符不在字表中(生僻字、特殊符号、外来字),统一映射为<UNK>的索引

3.将单条数据的「数值序列、标签、原始长度」封装为元组,存入列表统一管理、

contents.append((words_line,int(label),seq_len))

4.数据集打乱与切分

random.shuffle(contents) # 随机打乱所有数据的顺序,破坏原始数据的分布规律 # 按8:1:1比例切分,无重叠、全覆盖 train_data = contents[:int(len(contents) * 0.8)] # 前80%:训练集(模型参数学习) dev_data = contents[int(len(contents) * 0.8):int(len(contents) * 0.9)] # 80%-90%:验证集(超参数调优、早停) test_data = contents[int(len(contents)*0.9):] # 后10%:测试集(模型最终性能评估)

8:1:1是 NLP 任务中经典的数据集切分比例,兼顾训练集的数据量和验证 / 测试集的评估有效性

http://www.jsqmd.com/news/343703/

相关文章:

  • STM32F103C8T6最小系统板控制Nano-Banana生成模型展示
  • PostgreSQL 核心原理:一文掌握数据库的热数据缓存池(共享缓冲区)
  • 阿里云Qwen3-ASR-1.7B实战:会议录音转文字保姆级教程
  • 再互动:加多宝如何用一物一码织就情感与数据的营销双网
  • PostgreSQL 核心原理:一文掌握 WAL 缓冲区与刷盘策略(性能与数据安全的权衡)
  • VMware虚拟机部署Qwen2.5-VL:隔离环境搭建
  • 【实习】钉钉端银行经理新增与二维码功能开发复盘
  • 《镖人》将映《金吾不禁》杀青00后景瓷双屏绽放未来可期
  • translategemma-27b-it作品分享:中文书法印章图→英文收藏级描述+文化溯源
  • 气象监测设备如何助力精细化环境管理?金叶仪器智能气象站方案探讨
  • 范桢复古写真曝光 清冷氛围诠释复古浪漫
  • vLLM vs Ollama:大模型本地与生产部署如何选型?一文讲透
  • STM32CubeProgrammer 的隐藏功能:从命令行到自动化脚本的进阶玩法
  • RMBG-2.0与微信小程序开发:移动端图像处理解决方案
  • 我没想到 CSS if 函数这么强
  • 【IEEE出版】第二届能源系统与电气工程国际学术会议(ESEE 2026)
  • 造相-Z-Image参数详解:VAE分片解码机制与显存压力缓解原理
  • 【EV 录屏】电脑录屏神器!高效录屏神器 | 大学生及职场必备好用工具(十一)——EV录屏上手指南
  • 选品别只看“需求”,更要看“供给”:亚马逊新思路——用“供给断层”挑出更好打的品
  • 计算机组成原理 (二) 计算机硬件设计思想及软件
  • YOLOv12在安防监控中的应用:实时目标检测实战
  • KaiwuDB 3.1.0 社区版发布,安装部署体验焕新升级,多维度优化增强
  • Gemma-3-270m模型压缩技术:减小体积提升效率
  • 计算机组成原理 (三)计算机硬件组成
  • FT61E13x家族解析(FT61E131/3F/32/33/35)8位AD型MCU之间的区别
  • 软件测试实战:RMBG-2.0质量保障方案
  • Qwen3-4B开源模型部署指南:免编译、免依赖、一键启动
  • GLM-4-9B-Chat-1M新手指南:百万上下文模型本地运行全流程
  • lychee-rerank-mm保姆级教程:WebUI多语言切换与中文界面优化
  • 网站内容巡查制度有哪几种类型?