【MindSpore学习打卡】应用实践-自然语言处理-基于RNN的情感分类:使用MindSpore实现IMDB影评分类

情感分类是自然语言处理(NLP)中的一个经典任务,广泛应用于社交媒体分析、市场调研和客户反馈等领域。本篇博客将带领大家使用MindSpore框架,基于RNN(循环神经网络)实现一个情感分类模型。我们将详细介绍数据准备、模型构建、训练与评估等步骤,并最终实现对自定义输入的情感预测。

数据准备

下载和加载数据集

  • 为什么要使用requeststqdm库? requests库提供了简洁的HTTP请求接口,方便我们从网络上下载数据。tqdm库则用于显示下载进度条,帮助我们实时了解下载进度,提高用户体验。
  • 为什么要使用临时文件和shutil库? 使用临时文件可以确保下载的数据在出现意外中断时不会影响最终保存的文件。shutil库提供了高效的文件操作方法,确保数据能正确地从临时文件复制到最终保存路径。

我们使用IMDB影评数据集,这是一个经典的情感分类数据集,包含积极(Positive)和消极(Negative)两类影评。为了方便数据下载和处理,我们首先设计一个数据下载模块。

import os
import shutil
import requests
import tempfile
from tqdm import tqdm
from typing import IO
from pathlib import Path# 指定保存路径
cache_dir = Path.home() / '.mindspore_examples'def http_get(url: str, temp_file: IO):req = requests.get(url, stream=True)content_length = req.headers.get('Content-Length')total = int(content_length) if content_length is not None else Noneprogress = tqdm(unit='B', total=total)for chunk in req.iter_content(chunk_size=1024):if chunk:progress.update(len(chunk))temp_file.write(chunk)progress.close()def download(file_name: str, url: str):if not os.path.exists(cache_dir):os.makedirs(cache_dir)cache_path = os.path.join(cache_dir, file_name)cache_exist = os.path.exists(cache_path)if not cache_exist:with tempfile.NamedTemporaryFile() as temp_file:http_get(url, temp_file)temp_file.flush()temp_file.seek(0)with open(cache_path, 'wb') as cache_file:shutil.copyfileobj(temp_file, cache_file)return cache_path

下载IMDB数据集并进行解压和加载:

import re
import six
import string
import tarfileclass IMDBData():label_map = {"pos": 1, "neg": 0}def __init__(self, path, mode="train"):self.mode = modeself.path = pathself.docs, self.labels = [], []self._load("pos")self._load("neg")def _load(self, label):pattern = re.compile(r"aclImdb/{}/{}/.*\.txt$".format(self.mode, label))with tarfile.open(self.path) as tarf:tf = tarf.next()while tf is not None:if bool(pattern.match(tf.name)):self.docs.append(str(tarf.extractfile(tf).read().rstrip(six.b("\n\r")).translate(None, six.b(string.punctuation)).lower()).split())self.labels.append([self.label_map[label]])tf = tarf.next()def __getitem__(self, idx):return self.docs[idx], self.labels[idx]def __len__(self):return len(self.docs)

加载训练数据集进行测试,输出数据集数量:

imdb_path = download('aclImdb_v1.tar.gz', 'https://mindspore-website.obs.myhuaweicloud.com/notebook/datasets/aclImdb_v1.tar.gz')
imdb_train = IMDBData(imdb_path, 'train')
print(len(imdb_train))

加载预训练词向量

为什么要使用预训练词向量? 预训练词向量(如Glove)是基于大规模语料库训练得到的,能够捕捉到丰富的语义信息。使用预训练词向量可以提高模型的性能,尤其是在数据量较小的情况下。

  • 为什么要添加<unk><pad>标记符? <unk>标记符用于处理词汇表中未出现的单词,避免模型在遇到新词时无法处理。<pad>标记符用于填充不同长度的文本序列,使其能够被打包为一个batch进行并行计算,提高训练效率。

我们使用Glove预训练词向量对文本进行编码。以下是加载Glove词向量的代码:

import zipfile
import numpy as npdef load_glove(glove_path):glove_100d_path = os.path.join(cache_dir, 'glove.6B.100d.txt')if not os.path.exists(glove_100d_path):glove_zip = zipfile.ZipFile(glove_path)glove_zip.extractall(cache_dir)embeddings = []tokens = []with open(glove_100d_path, encoding='utf-8') as gf:for glove in gf:word, embedding = glove.split(maxsplit=1)tokens.append(word)embeddings.append(np.fromstring(embedding, dtype=np.float32, sep=' '))embeddings.append(np.random.rand(100))embeddings.append(np.zeros((100,), np.float32))vocab = ds.text.Vocab.from_list(tokens, special_tokens=["<unk>", "<pad>"], special_first=False)embeddings = np.array(embeddings).astype(np.float32)return vocab, embeddingsglove_path = download('glove.6B.zip', 'https://mindspore-website.obs.myhuaweicloud.com/notebook/datasets/glove.6B.zip')
vocab, embeddings = load_glove(glove_path)
print(len(vocab.vocab()))

数据集预处理

为什么要对文本进行分词和去除标点? 分词和去除标点是文本预处理的基础步骤,有助于模型更好地理解文本的语义。将文本转为小写可以减少词汇表的大小,提高模型的泛化能力。

对加载的数据集进行预处理,包括将文本转为index id序列,并进行序列填充。

import mindspore as ms
import mindspore.dataset as dslookup_op = ds.text.Lookup(vocab, unknown_token='<unk>')
pad_op = ds.transforms.PadEnd([500], pad_value=vocab.tokens_to_ids('<pad>'))
type_cast_op = ds.transforms.TypeCast(ms.float32)imdb_train = imdb_train.map(operations=[lookup_op, pad_op], input_columns=['text'])
imdb_train = imdb_train.map(operations=[type_cast_op], input_columns=['label'])imdb_test = imdb_test.map(operations=[lookup_op, pad_op], input_columns=['text'])
imdb_test = imdb_test.map(operations=[type_cast_op], input_columns=['label'])imdb_train, imdb_valid = imdb_train.split([0.7, 0.3])
imdb_valid = imdb_valid.batch(64, drop_remainder=True)

模型构建

接下来,我们构建用于情感分类的RNN模型。模型主要包括以下几层:

  1. Embedding层:将单词的index id转为词向量。
  2. RNN层:使用LSTM进行特征提取。
  3. 全连接层:将LSTM的输出特征映射到分类结果。

为什么选择LSTM而不是经典RNN? LSTM通过引入门控机制,有效地缓解了经典RNN中存在的梯度消失问题,能够更好地捕捉长距离依赖信息,提高模型的效果。

import math
import mindspore as ms
import mindspore.nn as nn
import mindspore.ops as ops
from mindspore.common.initializer import Uniform, HeUniformclass RNN(nn.Cell):def __init__(self, embeddings, hidden_dim, output_dim, n_layers, bidirectional, pad_idx):super().__init__()vocab_size, embedding_dim = embeddings.shapeself.embedding = nn.Embedding(vocab_size, embedding_dim, embedding_table=ms.Tensor(embeddings), padding_idx=pad_idx)self.rnn = nn.LSTM(embedding_dim, hidden_dim, num_layers=n_layers, bidirectional=bidirectional, batch_first=True)weight_init = HeUniform(math.sqrt(5))bias_init = Uniform(1 / math.sqrt(hidden_dim * 2))self.fc = nn.Dense(hidden_dim * 2, output_dim, weight_init=weight_init, bias_init=bias_init)def construct(self, inputs):embedded = self.embedding(inputs)_, (hidden, _) = self.rnn(embedded)hidden = ops.concat((hidden[-2, :, :], hidden[-1, :, :]), axis=1)output = self.fc(hidden)return output

损失函数与优化器

我们使用二分类交叉熵损失函数nn.BCEWithLogitsLoss和Adam优化器。

为什么使用二分类交叉熵损失函数和Adam优化器? 二分类交叉熵损失函数适用于二分类任务,能够衡量模型预测结果与真实标签之间的差异。Adam优化器结合了动量和自适应学习率的优点,具有较快的收敛速度和较好的效果,广泛应用于深度学习模型的训练。

hidden_size = 256
output_size = 1
num_layers = 2
bidirectional = True
lr = 0.001
pad_idx = vocab.tokens_to_ids('<pad>')model = RNN(embeddings, hidden_size, output_size, num_layers, bidirectional, pad_idx)
loss_fn = nn.BCEWithLogitsLoss(reduction='mean')
optimizer = nn.Adam(model.trainable_params(), learning_rate=lr)

训练逻辑

设计训练一个epoch的函数,用于训练过程和loss的可视化。

def forward_fn(data, label):logits = model(data)loss = loss_fn(logits, label)return lossgrad_fn = ms.value_and_grad(forward_fn, None, optimizer.parameters)def train_step(data, label):loss, grads = grad_fn(data, label)optimizer(grads)return lossdef train_one_epoch(model, train_dataset, epoch=0):model.set_train()total = train_dataset.get_dataset_size()loss_total = 0step_total = 0with tqdm(total=total) as t:t.set_description('Epoch %i' % epoch)for i in train_dataset.create_tuple_iterator():loss = train_step(*i)loss_total += loss.asnumpy()step_total += 1t.set_postfix(loss=loss_total/step_total)t.update(1)

评估指标和逻辑

设计评估逻辑,计算模型在验证集上的准确率。

def binary_accuracy(preds, y):rounded_preds = np.around(ops.sigmoid(preds).asnumpy())correct = (rounded_preds == y).astype(np.float32)acc = correct.sum() / len(correct)return accdef evaluate(model, test_dataset, criterion, epoch=0):total = test_dataset.get_dataset_size()epoch_loss = 0epoch_acc = 0step_total = 0model.set_train(False)with tqdm(total=total) as t:t.set_description('Epoch %i' % epoch)for i in test_dataset.create_tuple_iterator():predictions = model(i[0])loss = criterion(predictions, i[1])epoch_loss += loss.asnumpy()acc = binary_accuracy(predictions, i[1])epoch_acc += accstep_total += 1t.set_postfix(loss=epoch_loss/step_total, acc=epoch_acc/step_total)t.update(1)return epoch_loss / total

模型训练与保存

进行模型训练,并保存最优模型。

num_epochs = 2
best_valid_loss = float('inf')
ckpt_file_name = os.path.join(cache_dir, 'sentiment-analysis.ckpt')for epoch in range(num_epochs):train_one_epoch(model, imdb_train, epoch)valid_loss = evaluate(model, imdb_valid, loss_fn, epoch)if valid_loss < best_valid_loss:best_valid_loss = valid_lossms.save_checkpoint(model, ckpt_file_name)

模型加载与测试

加载已保存的最优模型,并在测试集上进行评估。

param_dict = ms.load_checkpoint(ckpt_file_name)
ms.load_param_into_net(model, param_dict)
imdb_test = imdb_test.batch(64)
evaluate(model, imdb_test, loss_fn)

自定义输入测试

设计一个预测函数,实现对自定义输入的情感预测。

score_map = {1: "Positive",0: "Negative"
}def predict_sentiment(model, vocab, sentence):model.set_train(False)tokenized = sentence.lower().split()indexed = vocab.tokens_to_ids(tokenized)tensor = ms.Tensor(indexed, ms.int32)tensor = tensor.expand_dims(0)prediction = model(tensor)

通过本文的学习,我们成功地使用MindSpore框架实现了一个基于RNN的情感分类模型。我们从数据准备开始,详细讲解了如何加载和处理IMDB影评数据集,以及使用预训练的Glove词向量对文本进行编码。然后,我们构建了一个包含Embedding层、LSTM层和全连接层的情感分类模型,并使用二分类交叉熵损失函数和Adam优化器进行训练。最后,我们评估了模型在测试集上的性能,并实现了对自定义输入的情感预测。希望这篇博客能帮助你更好地理解RNN在自然语言处理中的应用,并激发你在NLP领域的更多探索和实践。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/41027.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

X86和ARM架构的服务器 的区别

X86和ARM架构的服务器各有其优缺点,并适用于不同的应用场景。 一、X86架构服务器的优缺点及应用场景: 优点: 1. 易于获取和成本较低:X86服务器在市场上品牌和型号众多,价格相对较低,适合中小型企业。 2. 处理能力强大:X86服务器通常具有强大的处理器性能,支持多核心…

CLIP-EBC:通过增强的逐块分类,CLIP能够准确计数

摘要 https://arxiv.org/pdf/2403.09281v1 CLIP&#xff08;Contrastive Language-Image Pretraining&#xff0c;对比语言-图像预训练&#xff09;模型在识别问题中表现出了卓越的性能&#xff0c;如零样本图像分类和对象检测。然而&#xff0c;由于其固有的挑战——即将计数…

Nettyの参数优化简单RPC框架实现

本篇介绍Netty调优&#xff0c;在上篇聊天室的案例中进行改造&#xff0c;手写一个简单的RPC实现。 1、超时时间参数 CONNECT_TIMEOUT_MILLIS 是Netty的超时时间参数&#xff0c;属于客户端SocketChannel的参数&#xff0c;客户端连接时如果一定时间没有连接上&#xff0c;就会…

Spring Cloud 是什么?(Spring Cloud 组件介绍)

什么是 Spring Cloud&#xff1f; Spring Cloud 是微服务系统架构的一站式解决方案&#xff0c;是各个微服务架构落地技术的集合体&#xff0c;让架构师、 开发者在使用微服务理念构建应用系统的时候&#xff0c; 面对各个环节的问题都可以找到相应的组件来处理&#xff0c;比…

二叉树的遍历算法:前序、中序与后序遍历

在数据结构与算法中&#xff0c;二叉树的遍历是基础且重要的操作之一&#xff0c;它允许我们按照某种顺序访问树中的每个节点。常见的二叉树遍历方式有前序遍历&#xff08;Preorder Traversal&#xff09;、中序遍历&#xff08;Inorder Traversal&#xff09;和后序遍历&…

React 19 竞态问题解决

竞态问题/竞态条件 指的是&#xff0c;当我们在交互过程中&#xff0c;由于各种原因导致同一个接口短时间之内连续发送请求&#xff0c;后发送的请求有可能先得到请求结果&#xff0c;从而导致数据渲染出现预期之外的错误。 因为防止重复执行可以有效的解决竞态问题&#xff0…

聊天广场(Vue+WebSocket+SpringBoot)

由于心血来潮想要做个聊天室项目 &#xff0c;但是仔细找了一下相关教程&#xff0c;却发现这么多的WebSocket教程里面&#xff0c;很多都没有介绍详细&#xff0c;代码都有所残缺&#xff0c;所以这次带来一个比较完整得使用WebSocket的项目。 目录 一、效果展示 二、准备工…

html+css+js图片手动轮播

源代码在界面图片后面 轮播演示用的几张图片是Bing上的&#xff0c;直接用的几张图片的URL&#xff0c;谁加载可能需要等一下&#xff0c;现实中替换成自己的图片即可 关注一下点个赞吧&#x1f604; 谢谢大佬 界面图片 源代码 <!DOCTYPE html> <html lang&quo…

内存对齐宏ALIGN的理解

内存对齐宏ALIGN的理解 在Android Camera HAL代码中经常看到ALIGN这个宏&#xff0c;主要用来进行内存对齐&#xff0c;下面是v4l2_wrapper.cpp中ALIGN的一些定义 //v4l2_wrapper.cpp中内存分配进行对其的操作和定义#define ALIGN( num, to ) (((num) (to-1)) & (~(to-1)…

【Android】自定义换肤框架03之自定义LayoutInflaterFactory

AppCompatActivity是如何创建View的 Activity通过LayoutInflater解析出XmlLayout相关信息LayoutInflater内部维护了一个InflaterFactory对象InflaterFactory接口包含了一个onCreateView方法&#xff0c;用于创建View将解析出的Xml信息转为AttributeSet&#xff0c;交给Inflate…

安全测试之使用Docker搭建SQL注入安全测试平台sqli-labs

1 搜索镜像 docker search sqli-labs 2 拉取镜像 docker pull acgpiano/sqli-labs 3 创建docker容器 docker run -d --name sqli-labs -p 10012:80 acgpiano/sqli-labs 4 访问测试平台网站 若直接使用虚拟机&#xff0c;则直接通过ip端口号访问若通过配置域名&#xff0…

PyQt多线程详解

PyQt多线程是在PyQt框架中利用多线程技术来提高应用程序的响应性和性能的一种方法。下面将详细说明PyQt多线程的基本概念、应用场景以及实现方式。 一、PyQt多线程的基本概念 在PyQt中&#xff0c;多线程指的是在单个程序实例内同时运行多个线程。每个线程都可以执行不同的任…

第十五章 Nest Pipe(内置及自定义)

NestJS的Pipe是一个用于数据转换和验证的特殊装饰器。Pipe可以应用于控制器&#xff08;Controller&#xff09;的处理方法&#xff08;Handler&#xff09;和中间件&#xff08;Middleware&#xff09;&#xff0c;用于处理传入的数据。它可以用来转换和验证数据&#xff0c;确…

【Linux进阶】文件系统5——ext2文件系统(inode)

1.再谈inode (1) 理解inode&#xff0c;要从文件储存说起。 文件储存在硬盘上&#xff0c;硬盘的最小存储单位叫做"扇区"&#xff08;Sector&#xff09;。每个扇区储存512字节&#xff08;相当于0.5KB&#xff09;。操作系统读取硬盘的时候&#xff0c;不会一个个…

记录excel表生成一列按七天一个周期的方法

使用excel生成每七天一个周期的列。如下图所示&#xff1a; 针对第一列的生成办法&#xff0c;使用如下函数&#xff1a; TEXT(DATE(2024,1,1)(ROW()-2)*7,"yyyy/m/d")&" - "&TEXT(DATE(2024,1,1)(ROW()-1)*7-1,"yyyy/m/d") 特此记录。…

charles使用教程

安装与配置 下载链接&#xff1a;https://www.charlesproxy.com/download/ 进行移动端抓包&#xff1a; 电脑端配置&#xff1a; 关闭防火墙 Proxy–>勾选 macOS Proxy Proxy–>Proxy Setting–>填入代理端口8888–>勾选Enable transparent http proxying 安装c…

俄罗斯方块的python实现

俄罗斯方块游戏是一种经典的拼图游戏&#xff0c;玩家需要将不同形状的方块拼接在一起&#xff0c;使得每一行都被完全填满&#xff0c;从而清除这一行并获得积分。以下是该游戏的算法描述&#xff1a; 1. 初始化 初始化游戏界面&#xff0c;设置屏幕大小、方块大小、网格大小…

昇思25天学习打卡营第1天|初识MindSpore

# 打卡 day1 目录 # 打卡 day1 初识MindSpore 昇思 MindSpore 是什么&#xff1f; 昇思 MindSpore 优势|特点 昇思 MindSpore 不足 官方生态学习地址 初识MindSpore 昇思 MindSpore 是什么&#xff1f; 昇思MindSpore 是全场景深度学习架构&#xff0c;为开发者提供了全…

女生学计算机好不好?感觉计算机分有点高……?

众所周知&#xff0c;在国内的高校里&#xff0c;计算机专业的女生是非常少的&#xff0c;很多小班30人左右&#xff0c;但是每个班女生人数只有个位数。这就给很多人一个感觉&#xff0c;是不是女生天生就不适合学这个东西呢&#xff1f;女生是不是也应该放弃呢&#xff1f;当…

ubuntu 进入命令行

在Ubuntu中&#xff0c;有几种方法可以进入命令行界面&#xff1a; 启动时选择命令行模式&#xff1a; 在计算机启动时&#xff0c;如果安装了GRUB引导加载器&#xff0c;可以通过GRUB菜单选择进入命令行模式。这通常涉及到在启动时按下Shift键或其他指定键来显示GRUB菜单&…