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

📅 发布时间:2026/7/12 22:40:41 👁️ 浏览次数:
循环网络RNN--评论内容情感分析
一、构建字表基于微博语料库构建中文字表通过统计字频筛选有效字符为每个字符分配唯一索引并加入未知字符UNK和填充字符PAD最终将词表保存为 pickle 文件代码from tqdm import tqdm import pickle as pkl MAX_VOCAB_SIZE4760 UNK,PADUNK,PAD def build_vocab(file_path,max_size,min_frep): 功能基于文本内容建立词表vocabvocab中包含语料库中的字 参数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,encodingUTF-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(fVocabsize{len(vocab_dic)}) return vocab_dic if __name____main__: vocabbuild_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 后即为首次出现次数 13. 词表排序与截取sorted([_ for _ in vocab_dic.items() if _[1] min_freq], keylambda x: x[1], reverseTrue)[:max_size]筛选_[1] min_freq 保留字频超过阈值的字符_[1]为元组中的字频值排序keylambda x: x[1] 按字频排序reverseTrue 降序高频在前截取[:max_size] 仅保留前MAX_VOCAB_SIZE个高频字符控制词表容量4. UNK 和 PAD 的作用UNK未知字符词表中未收录的生僻字、外来字、特殊符号统一映射到该索引避免模型因未见过的字符报错PAD填充字符NLP 模型要求输入文本长度一致需用该字符将短文本补全到指定长度长文本截断保证输入维度统一索引分配在基础词表后连续分配确保索引唯一且无冲突示例基础词表 4760 个字符→UNK4760PAD4761总词表大小 47625. 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,PADUNK,PAD def load_dataset(path, pad_size70): contents [] # 用来存储转换为数值标号的句子 vocab pkl.load(open(simplifyweibo_4_moods.pkl,rb)) # 读取vocab文件 tokenizer lambda x: [y for y in x] with open(path,r,encodingUTF-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_data1.文本长度统一填充 / 截断pad_size70 # 目标固定长度所有文本最终统一为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_size2.字符→数值索引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 任务中经典的数据集切分比例兼顾训练集的数据量和验证 / 测试集的评估有效性