自然语言处理应用(一):情感分析

情感分析

随着在线社交媒体和评论平台的快速发展,大量评论的数据被记录下来。这些数据具有支持决策过程的巨大潜力。 情感分析(sentiment analysis)研究人们在文本中 (如产品评论、博客评论和论坛讨论等)“隐藏”的情绪。 它在广泛应用于政治(如公众对政策的情绪分析)、 金融(如市场情绪分析)和营销(如产品研究和品牌管理)等领域。

由于情感可以被分类为离散的极性或尺度(例如,积极的和消极的),我们可以将情感分析看作一项文本分类任务,它将可变长度的文本序列转换为固定长度的文本类别。在本章中,我们将使用斯坦福大学的大型电影评论数据集(large movie review dataset)进行情感分析。它由一个训练集和一个测试集组成,其中包含从IMDb下载的25000个电影评论。在这两个数据集中,“积极”和“消极”标签的数量相同,表示不同的情感极性。

文章内容来自李沐大神的《动手学深度学习》并加以我的理解,感兴趣可以去https://zh-v2.d2l.ai/查看完整书籍


文章目录

  • 情感分析
  • 数据集
    • 读取数据集
    • 预处理数据集
    • 创建数据迭代器
    • 整合代码
  • 使用循环神经网络实现
    • 使用循环神经网络表示单个文本
    • 加载预训练的词向量
    • 训练和评估模型
  • 使用卷积神经网络实现
    • 一维卷积
    • 最大时间汇聚层
    • textCNN模型
      • 定义模型
      • 加载预训练词向量
      • 训练和评估模型


数据集

读取数据集

读取训练和测试数据集。每个样本都是一个评论及其标签:1表示“积极”,0表示“消极”。

import os
import torch
from torch import nn
from d2l import torch as d2l
#@save
d2l.DATA_HUB['aclImdb'] = ('http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz','01ada507287d82875905620988597833ad4e0903')data_dir = d2l.download_extract('aclImdb', 'aclImdb')
#@save
def read_imdb(data_dir, is_train):"""读取IMDb评论数据集文本序列和标签"""data, labels = [], []for label in ('pos', 'neg'):folder_name = os.path.join(data_dir, 'train' if is_train else 'test',label)for file in os.listdir(folder_name):with open(os.path.join(folder_name, file), 'rb') as f:review = f.read().decode('utf-8').replace('\n', '')data.append(review)labels.append(1 if label == 'pos' else 0)return data, labelstrain_data = read_imdb(data_dir, is_train=True)
print('训练集数目:', len(train_data[0]))
for x, y in zip(train_data[0][:3], train_data[1][:3]):print('标签:', y, 'review:', x[0:60])

在这里插入图片描述

预处理数据集

将每个单词作为一个词元,过滤掉出现不到5次的单词,我们从训练数据集中创建一个词表。

train_tokens = d2l.tokenize(train_data[0], token='word')
vocab = d2l.Vocab(train_tokens, min_freq=5, reserved_tokens=['<pad>'])

在词元化之后,让我们绘制评论词元长度的直方图。

d2l.set_figsize()
d2l.plt.xlabel('# tokens per review')
d2l.plt.ylabel('count')
d2l.plt.hist([len(line) for line in train_tokens], bins=range(0, 1000, 50));

在这里插入图片描述
正如我们所料,评论的长度各不相同。为了每次处理一小批量这样的评论,我们通过截断和填充将每个评论的长度设置为500。

def truncate_pad(line, num_steps, padding_token):if len(line) > num_steps:return line[:num_steps]  # Truncatereturn line + [padding_token] * (num_steps - len(line))  # Pad

如果长度大于num_steps的,我们将其截断,小于num_steps的,我们将其填充为<pad>

num_steps = 500  # 序列长度
train_features = torch.tensor([truncate_pad(vocab[line], num_steps, vocab['<pad>']) for line in train_tokens])
print(train_features.shape)

在这里插入图片描述

创建数据迭代器

现在我们可以创建数据迭代器了。在每次迭代中,都会返回一小批量样本。

train_iter = d2l.load_array((train_features,torch.tensor(train_data[1])), 64)for X, y in train_iter:print('X:', X.shape, ', y:', y.shape)break
print('小批量数目:', len(train_iter))

在这里插入图片描述

整合代码

#@save
def load_data_imdb(batch_size, num_steps=500):"""返回数据迭代器和IMDb评论数据集的词表"""data_dir = d2l.download_extract('aclImdb', 'aclImdb')train_data = read_imdb(data_dir, True)test_data = read_imdb(data_dir, False)train_tokens = d2l.tokenize(train_data[0], token='word')test_tokens = d2l.tokenize(test_data[0], token='word')vocab = d2l.Vocab(train_tokens, min_freq=5)train_features = torch.tensor([d2l.truncate_pad(vocab[line], num_steps, vocab['<pad>']) for line in train_tokens])test_features = torch.tensor([d2l.truncate_pad(vocab[line], num_steps, vocab['<pad>']) for line in test_tokens])train_iter = d2l.load_array((train_features, torch.tensor(train_data[1])),batch_size)test_iter = d2l.load_array((test_features, torch.tensor(test_data[1])),batch_size,is_train=False)return train_iter, test_iter, vocab

使用循环神经网络实现

与词相似度和类比任务一样,我们也可以将预先训练的词向量应用于情感分析。由于IMDb评论数据集不是很大,使用在大规模语料库上预训练的文本表示可以减少模型的过拟合。作为图中所示的具体示例,我们将使用预训练的GloVe模型来表示每个词元,并将这些词元表示送入多层双向循环神经网络以获得文本序列表示,该文本序列表示将被转换为情感分析输出 (Maas et al., 2011)。对于相同的下游应用,我们稍后将考虑不同的架构选择。
在这里插入图片描述

import torch
from torch import nn
from d2l import torch as d2lbatch_size = 64
train_iter, test_iter, vocab = d2l.load_data_imdb(batch_size)

使用循环神经网络表示单个文本

在文本分类任务(如情感分析)中,可变长度的文本序列将被转换为固定长度的类别。在下面的BiRNN类中,虽然文本序列的每个词元经由嵌入层(self.embedding)获得其单独的预训练GloVe表示,但是整个序列由双向循环神经网络(self.encoder)编码。更具体地说,双向长短期记忆网络在初始和最终时间步的隐状态(在最后一层)被连结起来作为文本序列的表示。然后,通过一个具有两个输出(“积极”和“消极”)的全连接层(self.decoder),将此单一文本表示转换为输出类别。

class BiRNN(nn.Module):def __init__(self, vocab_size, embed_size, num_hiddens,num_layers, **kwargs):super(BiRNN, self).__init__(**kwargs)self.embedding = nn.Embedding(vocab_size, embed_size)# 将bidirectional设置为True以获取双向循环神经网络self.encoder = nn.LSTM(embed_size, num_hiddens, num_layers=num_layers,bidirectional=True)self.decoder = nn.Linear(4 * num_hiddens, 2)def forward(self, inputs):# inputs的形状是(批量大小,时间步数)# 因为长短期记忆网络要求其输入的第一个维度是时间维,# 所以在获得词元表示之前,输入会被转置。# 输出形状为(时间步数,批量大小,词向量维度)embeddings = self.embedding(inputs.T)self.encoder.flatten_parameters()# 返回上一个隐藏层在不同时间步的隐状态,# outputs的形状是(时间步数,批量大小,2*隐藏单元数)outputs, _ = self.encoder(embeddings)# 连结初始和最终时间步的隐状态,作为全连接层的输入,# 其形状为(批量大小,4*隐藏单元数)encoding = torch.cat((outputs[0], outputs[-1]), dim=1)outs = self.decoder(encoding)return outs

让我们构造一个具有两个隐藏层的双向循环神经网络来表示单个文本以进行情感分析。

embed_size, num_hiddens, num_layers = 100, 100, 2
devices = d2l.try_all_gpus()
net = BiRNN(len(vocab), embed_size, num_hiddens, num_layers)def init_weights(m):if type(m) == nn.Linear:nn.init.xavier_uniform_(m.weight)if type(m) == nn.LSTM:for param in m._flat_weights_names:if "weight" in param:nn.init.xavier_uniform_(m._parameters[param])
net.apply(init_weights);

加载预训练的词向量

下面,我们为词表中的单词加载预训练的100维(需要与embed_size一致)的GloVe嵌入。

glove_embedding = d2l.TokenEmbedding('glove.6b.100d')

打印词表中所有词元向量的形状。

embeds = glove_embedding[vocab.idx_to_token]
embeds.shape

在这里插入图片描述
我们使用这些预训练的词向量来表示评论中的词元,并且在训练期间不要更新这些向量。

net.embedding.weight.data.copy_(embeds)
net.embedding.weight.requires_grad = False

训练和评估模型

lr, num_epochs = 0.01, 5
trainer = torch.optim.Adam(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss(reduction="none")
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,devices)

在这里插入图片描述我们定义以下函数来使用训练好的模型net预测文本序列的情感。

#@save
def predict_sentiment(net, vocab, sequence):"""预测文本序列的情感"""sequence = torch.tensor(vocab[sequence.split()], device=d2l.try_gpu())label = torch.argmax(net(sequence.reshape(1, -1)), dim=1)return 'positive' if label == 1 else 'negative'

最后,让我们使用训练好的模型对简单的句子进行情感预测。

predict_sentiment(net, vocab, 'this movie is so great')

在这里插入图片描述

使用卷积神经网络实现

虽然卷积神经网络最初是为计算机视觉设计的,但它也被广泛用于自然语言处理。简单地说,只要将任何文本序列想象成一维图像即可。通过这种方式,一维卷积神经网络可以处理文本中的局部特征,例如 n n n元语法。

本节将使用textCNN模型来演示如何设计一个表示单个文本 (Kim, 2014)的卷积神经网络架构。

在这里插入图片描述

一维卷积

在介绍该模型之前,让我们先看看一维卷积是如何工作的。请记住,这只是基于互相关运算的二维卷积的特例。
在这里插入图片描述
如图中所示,在一维情况下,卷积窗口在输入张量上从左向右滑动。在滑动期间,卷积窗口中某个位置包含的输入子张量和核张量按元素相乘。这些乘法的总和在输出张量的相应位置给出单个标量值

我们在下面的corr1d函数中实现了一维互相关。给定输入张量X和核张量K,它返回输出张量Y。

def corr1d(X, K):w = K.shape[0]Y = torch.zeros((X.shape[0] - w + 1))for i in range(Y.shape[0]):Y[i] = (X[i: i + w] * K).sum()return Y
X, K = torch.tensor([0, 1, 2, 3, 4, 5, 6]), torch.tensor([1, 2])
corr1d(X, K)

在这里插入图片描述
对于任何具有多个通道的一维输入,卷积核需要具有相同数量的输入通道。然后,对于每个通道,对输入的一维张量和卷积核的一维张量执行互相关运算,将所有通道上的结果相加以产生一维输出张量。 下图演示了具有3个输入通道的一维互相关操作。
在这里插入图片描述
我们可以实现多个输入通道的一维互相关运算

def corr1d_multi_in(X, K):# 首先,遍历'X'和'K'的第0维(通道维)。然后,把它们加在一起return sum(corr1d(x, k) for x, k in zip(X, K))X = torch.tensor([[0, 1, 2, 3, 4, 5, 6],[1, 2, 3, 4, 5, 6, 7],[2, 3, 4, 5, 6, 7, 8]])
K = torch.tensor([[1, 2], [3, 4], [-1, -3]])
corr1d_multi_in(X, K)

在这里插入图片描述
注意,多输入通道的一维互相关等同于单输入通道的二维互相关。
在这里插入图片描述

最大时间汇聚层

类似地,我们可以使用汇聚层从序列表示中提取最大值,作为跨时间步的最重要特征。textCNN中使用的最大时间汇聚层的工作原理类似于一维全局汇聚 (Collobert et al., 2011)。对于每个通道在不同时间步存储值的多通道输入,每个通道的输出是该通道的最大值。请注意,最大时间汇聚允许在不同通道上使用不同数量的时间步。

textCNN模型

使用一维卷积和最大时间汇聚,textCNN模型将单个预训练的词元表示作为输入,然后获得并转换用于下游应用的序列表示。

对于具有由 d d d维向量表示的 n n n个词元的单个文本序列,输入张量的宽度、高度和通道数分别为 n n n 1 1 1 d d d。textCNN模型将输入转换为输出,如下所示:

  1. 定义多个一维卷积核,并分别对输入执行卷积运算。具有不同宽度的卷积核可以捕获不同数目的相邻词元之间的局部特征。

  2. 在所有输出通道上执行最大时间汇聚层,然后将所有标量汇聚输出连结为向量。

  3. 使用全连接层将连结后的向量转换为输出类别。Dropout可以用来减少过拟合。

在这里插入图片描述

定义模型

我们在下面的类中实现textCNN模型。除了用卷积层代替循环神经网络层外,我们还使用了两个嵌入层:一个是可训练权重,另一个是固定权重。

class TextCNN(nn.Module):def __init__(self, vocab_size, embed_size, kernel_sizes, num_channels,**kwargs):super(TextCNN, self).__init__(**kwargs)self.embedding = nn.Embedding(vocab_size, embed_size)# 这个嵌入层不需要训练self.constant_embedding = nn.Embedding(vocab_size, embed_size)self.dropout = nn.Dropout(0.5)self.decoder = nn.Linear(sum(num_channels), 2)# 最大时间汇聚层没有参数,因此可以共享此实例self.pool = nn.AdaptiveAvgPool1d(1)self.relu = nn.ReLU()# 创建多个一维卷积层self.convs = nn.ModuleList()for c, k in zip(num_channels, kernel_sizes):self.convs.append(nn.Conv1d(2 * embed_size, c, k))def forward(self, inputs):# 沿着向量维度将两个嵌入层连结起来,# 每个嵌入层的输出形状都是(批量大小,词元数量,词元向量维度)连结起来embeddings = torch.cat((self.embedding(inputs), self.constant_embedding(inputs)), dim=2)# 根据一维卷积层的输入格式,重新排列张量,以便通道作为第2维embeddings = embeddings.permute(0, 2, 1)# 每个一维卷积层在最大时间汇聚层合并后,获得的张量形状是(批量大小,通道数,1)# 删除最后一个维度并沿通道维度连结encoding = torch.cat([torch.squeeze(self.relu(self.pool(conv(embeddings))), dim=-1)for conv in self.convs], dim=1)outputs = self.decoder(self.dropout(encoding))return outputs

让我们创建一个textCNN实例。它有3个卷积层,卷积核宽度分别为3、4和5,均有100个输出通道。

embed_size, kernel_sizes, nums_channels = 100, [3, 4, 5], [100, 100, 100]
devices = d2l.try_all_gpus()
net = TextCNN(len(vocab), embed_size, kernel_sizes, nums_channels)def init_weights(m):if type(m) in (nn.Linear, nn.Conv1d):nn.init.xavier_uniform_(m.weight)net.apply(init_weights);

加载预训练词向量

glove_embedding = d2l.TokenEmbedding('glove.6b.100d')
embeds = glove_embedding[vocab.idx_to_token]
net.embedding.weight.data.copy_(embeds)
net.constant_embedding.weight.data.copy_(embeds)
net.constant_embedding.weight.requires_grad = False

训练和评估模型

现在我们可以训练textCNN模型进行情感分析。

lr, num_epochs = 0.001, 5
trainer = torch.optim.Adam(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss(reduction="none")
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)

在这里插入图片描述
下面,我们使用训练好的模型来预测两个简单句子的情感。

d2l.predict_sentiment(net, vocab, 'this movie is so great')

在这里插入图片描述

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

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

相关文章

云原生服务无状态(Stateless)特性的实现

文章目录 为何要使用无状态服务&#xff1f;无状态服务的实现方法1. 会话状态外部化2. 负载均衡3. 自动伸缩4. 容器编排5. 数据存储6. 安全性 示例&#xff1a;使用Spring Boot实现无状态服务结论 &#x1f389;欢迎来到云计算技术应用专栏~云原生服务无状态&#xff08;Statel…

地理地形sdk:Tatuk GIS Developer Kernel for .NET Crack

Tatuk GIS Developer Kernel for .NET 是一个变体&#xff0c;它是受控代码和 .NET GIS SDK&#xff0c;用于为用户 Windows 操作系统创建专业 GIS 软件的过程。它被认为是一个完全针对Win Forms 的.NET CIL&#xff0c;WPF 框架是针对C# 以及VB.NET、VC、Oxy 以及最终与.NET 的…

VUE之滚动条参数设置

/*css主要部分的样式*/ /*定义滚动条宽高及背景&#xff0c;宽高分别对应横竖滚动条的尺寸*/ ::-webkit-scrollbar {width: 6px; /*对垂直流动条有效*/height: 6px; /*对水平流动条有效*/ }/*定义滚动条的轨道颜色、内阴影及圆角*/ ::-webkit-scrollbar-track{border-radius: 4…

删除安装Google Chrome浏览器时捆绑安装的Google 文档、表格、幻灯片、Gmail、Google 云端硬盘、YouTube网址链接(Mac)

删除安装Google Chrome浏览器时捆绑安装的Google 文档、表格、幻灯片、Gmail、Google 云端硬盘、YouTube网址链接(Mac) Mac mini操作系统&#xff0c;安装完 Google Chrome 浏览器以后&#xff0c;单击 启动台 桌面左下角的“显示应用程序”&#xff0c;我们发现捆绑安装了 Goo…

MySQL

文章目录 1.Mysql1.1 为什么学习数据库1.2 什么是数据库1.3 数据库分类1.4 Mysql简介1.5 安装Mysql1.6 安装Sqlyog1.7 连接数据库 2.操作数据库2.1 操作数据库2.2 数据库的列类型2.3 数据库字段属性&#xff08;重点&#xff09;2.4 创建数据库表&#xff08;重点&#xff09;2…

Java 使用 EMQX 实现物联网 MQTT 通信

一、介绍 1、MQTT MQTT(Message Queuing Telemetry Transport, 消息队列遥测传输协议)&#xff0c;是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议&#xff0c;该协议构建于TCP/IP协议上&#xff0c;由IBM在1999年发布。MQTT最大优点在于&#xff…

Leetcode算法入门与数组丨3. 数组基础

文章目录 前言1 数组简介2 数组的基本操作2.1 访问元素2.2 查找元素2.3 插入元素2.4 改变元素2.5 删除元素 3 总结task03task04 前言 Datawhale组队学习丨9月Leetcode算法入门与数组丨打卡笔记 这篇博客是一个 入门型 的文章&#xff0c;主要是自己学习的一个记录。 内容会参…

13-RocketMQ主从同步(HA实现)源码原理

目录 HAClient端 Master端 AcceptSocketService实现原理 HAConnection实现原理 ReadSocketService WriteSocketService GroupTransferService实现原理 五大线程的协调关系 HAClient端 首先要去connect一下master&#xff0c;从而建立一个SocketChannel连接通道&#x…

不知道有用没用的Api

encodeURIComponent(https://www.baidu.com/?name啊啊啊) decodeURIComponent(https%3A%2F%2Fwww.baidu.com%2F%3Fname%3D%E5%95%8A%E5%95%8A%E5%95%8A) encodeURI(https://www.baidu.com/?name啊啊啊) decodeURI(https://www.baidu.com/?name%E5%95%8A%E5%95%8A%E5%95%8A) …

面试核心技巧--spring篇

答题技巧&#xff1a; 总&#xff1a;当前问题的是那些具体点 分&#xff1a;1、2、3、4突出重点&#xff0c; 避重就轻:没有重点 一个问题能占用面试官多少时间?问的越多可能露馅越多 当面试官问到一个你熟悉的点的时候&#xff0c;一定要尽量拖时间 什么是底层实现&#…

真空腔体的设计要点

真空腔体是保持内部为真空状态的容器&#xff0c;真空腔体设计制作要考虑容积、材质和形状。 1、根据应用需求选择腔体形状。几种代表性的真空腔体包括垂直真空腔体、水平真空腔体、立方真空腔体和球形真空腔体。 2、根据获得真空度选择腔体材质。钛用于极高真空&#xff1b;…

旋转偏心裁切刀切向跟踪及半径补偿

1 裁刀半径补偿问题的提出 偏心裁刀一般皮革和纸箱行业用的比较多&#xff0c;它适用于裁切比较厚的材料。对于如图1所示的偏心裁刀&#xff0c;它的刀尖和旋转轴(也就是刀心)存在一个距离&#xff0c;设为半径r。由于改刀刀刃有方向&#xff0c;所以用该刀去切割直线时&#…