rostcm6情感分析案例分析_卷积情感分析

这是一个面向小白(比如,本人)的关于情感分析的系列教程 [1]。老鸽子整理了“4 - Convolutional Sentiment Analysis.ipynb”中的内容。

本文任务:使用卷积神经网络(CNN)来实现句子分类。

简介

CNN用于分析图像,包含一个或多个卷积层,紧跟一个或多个线性层。在卷积层中,使用滤波器扫描图像。扫描后的图像送入另一个卷积层或线性层。每个滤波器都有大小,如:每次使用大小的滤波器来扫描大小的图像区域,滤波器有9个对应的权重。

类似于使用滤波器查看图像区域,使用大小的滤波器来查看文本中两个连续的词(bi-gram)。

准备数据

不同于FastText模型,不用明确创建bi-grams并添加到句尾。

因为卷积层的第一个维度是batch,所以可以设置field中的batch_first为True。

划分训练集和验证集;构建词汇表;创建迭代器。

import torch
from torchtext import data
from torchtext import datasets
import random
import numpy as np

SEED = 1234

random.seed(SEED)
np.random.seed(SEED)
torch.manual_seed(SEED)
torch.backends.cudnn.deterministic = True

TEXT = data.Field(tokenize='spacy', batch_first=True)
LABEL = data.LabelField(dtype=torch.float)

train_data, test_data = datasets.IMDB.splits(TEXT, LABEL)
train_data, valid_data = train_data.split(random_state = random.seed(SEED))
#-----------------------------

MAX_VOCAB_SIZE = 25_000

TEXT.build_vocab(
    train_data, 
    max_size = MAX_VOCAB_SIZE, 
    vectors = "glove.6B.100d", 
    unk_init = torch.Tensor.normal_
    )
LABEL.build_vocab(train_data)
#-----------------------------

BATCH_SIZE = 64

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
train_iterator, valid_iterator, test_iterator = data.BucketIterator.splits(
    (train_data, valid_data, test_data), 
    batch_size=BATCH_SIZE, 
    device=device
    )

构建模型

在二维中可视化句中的词。每个词沿一个轴,嵌入沿另一个轴。

使用一个大小的滤波器,每次扫描n个词。

下图有4个词,每个词有5维嵌入,因此得到一个大小的“图像”张量。使用一个大小的滤波器,每次扫描两个词(黄色)。滤波器的每个元素包含一个权重,它的输出为10个嵌入元素的加权和。滤波器向下扫描(跨句)到下一个bi-gram,输出另一个加权和。滤波器再次向下扫描,计算最后的加权和。

滤波器的宽度等于“图像”的宽度,输出一个向量,它的长度等于“图像”的高度减滤波器的高度加1:。

854e2571747f6d2f56f97f73949c2080.png

本文所用的模型将有不同大小的滤波器,它们的高度分别为3、4和5,每个大小有100个,从而能够寻找与评论的情感相关的不同的tri-grams、4-grams和5-grams。

为了确定与情绪相关的最重要的n-gram,最大池化卷积层的输出。

因为模型有300个不同且重要的n-grams,所以可以认为全连接层用于加权这些n-grams,从而给出最终的判断。

应用细节

使用nn.Conv2d来实现卷积层:in_channels为输入通道数;图像的通道数为3(红-绿-蓝),而文本的通道数为1;out_channels为输出通道数,kernel_size为滤波器大小:,其中的为n-grams的大小。

RNN的batch位于第二维,而CNN的batch位于第一维。CNN的第二维为通道数(为嵌入添加大小为1的新维度,与卷积层的in_channels=1一致)。

卷积后的激活函数为ReLU。池化层可以处理不同长度的句子,卷积层的输出取决于它的输入,不同的batches包含了不同长度的句子。当没有最大池化层时,线性层的输入取决于输入句子的大小。为了解决该问题,可以修剪或填充所有句子到等长。当有最大池化层时,线性层的输入数等于滤波器个数。

当句子比最大的滤波器还短时,必须填充句子到最大的滤波器长度。因为IMDb数据中的评论都大于5个字长,所以不用担心该情况。

对级联的滤波器输出使用Dropout,再经过线性层得到预测结果。

import torch.nn as nn
import torch.nn.functional as F

class CNN(nn.Module):
    def __init__(self, vocab_size, embedding_dim, n_filters, filter_sizes, output_dim, dropout, pad_idx):
        super().__init__()
        
        self.embedding = nn.Embedding(
            vocab_size, 
            embedding_dim, 
            padding_idx=pad_idx
            )
        self.conv_0 = nn.Conv2d(
            in_channels=1, 
            out_channels=n_filters, 
            kernel_size=(filter_sizes[0], embedding_dim)
            )
        self.conv_1 = nn.Conv2d(
            in_channels=1, 
            out_channels=n_filters, 
            kernel_size=(filter_sizes[1], embedding_dim)
            )
        self.conv_2 = nn.Conv2d(
            in_channels=1, 
            out_channels=n_filters, 
            kernel_size=(filter_sizes[2], embedding_dim)
            )
        self.fc = nn.Linear(len(filter_sizes) * n_filters, output_dim)
        self.dropout = nn.Dropout(dropout)

    def forward(self, text):      
        # text:     [batch size, sent len]
        # embedded: [batch size, sent len, emb dim]
        embedded = self.embedding(text)
        # embedded: [batch size, 1, sent len, emb dim]
        embedded = embedded.unsqueeze(1)
        
        # conved_n: [batch size, n_filters, sent len - filter_sizes[n] + 1]
        conved_0 = F.relu(self.conv_0(embedded).squeeze(3))
        conved_1 = F.relu(self.conv_1(embedded).squeeze(3))
        conved_2 = F.relu(self.conv_2(embedded).squeeze(3))
        # pooled_n: [batch size, n_filters]
        pooled_0 = F.max_pool1d(conved_0, conved_0.shape[2]).squeeze(2)
        pooled_1 = F.max_pool1d(conved_1, conved_1.shape[2]).squeeze(2)
        pooled_2 = F.max_pool1d(conved_2, conved_2.shape[2]).squeeze(2)

        # cat:  [batch size, n_filters * len(filter_sizes)]
        cat = self.dropout(torch.cat((pooled_0, pooled_1, pooled_2), dim=1))
        return self.fc(cat)

CNN模型仅用了3种不同大小的滤波器。为了使用任意数量的滤波器,将所有的卷积层放入nn.ModuleList。

class CNN2d(nn.Module):
    def __init__(self, vocab_size, embedding_dim, n_filters, filter_sizes, output_dim, dropout, pad_idx):
        super().__init__()
                
        self.embedding = nn.Embedding(
            vocab_size, 
            embedding_dim, 
            padding_idx=pad_idx
            )
        self.convs = nn.ModuleList([
            nn.Conv2d(
                in_channels=1, 
                out_channels=n_filters, 
                kernel_size=(fs, embedding_dim)
                ) 
                for fs in filter_sizes
            ])
        self.fc = nn.Linear(len(filter_sizes) * n_filters, output_dim)
        self.dropout = nn.Dropout(dropout)
        
    def forward(self, text):
        # text:     [batch size, sent len]
        # embedded: [batch size, sent len, emb dim]
        embedded = self.embedding(text)
        # embedded: [batch size, 1, sent len, emb dim]
        embedded = embedded.unsqueeze(1)
        
        # conved_n: [batch size, n_filters, sent len - filter_sizes[n] + 1]
        conved = [F.relu(conv(embedded)).squeeze(3) for conv in self.convs]
        # pooled_n: [batch size, n_filters]
        pooled = [F.max_pool1d(conv, conv.shape[2]).squeeze(2) for conv in conved]
        
        # cat:      [batch size, n_filters * len(filter_sizes)]
        cat = self.dropout(torch.cat(pooled, dim=1))
        return self.fc(cat)

使用一维卷积层,滤波器的“深度”和宽分别为嵌入的维度和句中的词数。

class CNN1d(nn.Module):
    def __init__(self, vocab_size, embedding_dim, n_filters, filter_sizes, output_dim, dropout, pad_idx):
        super().__init__()
        
        self.embedding = nn.Embedding(
            vocab_size, 
            embedding_dim, 
            padding_idx=pad_idx
            )
        self.convs = nn.ModuleList([
            nn.Conv1d(
                in_channels=embedding_dim, 
                out_channels=n_filters, 
                kernel_size=fs
                )
                for fs in filter_sizes
            ])
        self.fc = nn.Linear(len(filter_sizes) * n_filters, output_dim)
        self.dropout = nn.Dropout(dropout)
        
    def forward(self, text):
        # text:     [batch size, sent len]
        # embedded: [batch size, sent len, emb dim]
        embedded = self.embedding(text)
        # embedded: [batch size, emb dim, sent len]
        embedded = embedded.permute(0, 2, 1)
        
        # conved_n: [batch size, n_filters, sent len - filter_sizes[n] + 1]
        conved = [F.relu(conv(embedded)) for conv in self.convs]
        # pooled_n: [batch size, n_filters]
        pooled = [F.max_pool1d(conv, conv.shape[2]).squeeze(2) for conv in conved]
        
        # cat:  [batch size, n_filters * len(filter_sizes)]
        cat = self.dropout(torch.cat(pooled, dim=1))
        return self.fc(cat)

创建CNN2d(或CNN、CNN1d)类的实例;打印模型中可训练的参数量;清0“”和“”的初始权重。

INPUT_DIM = len(TEXT.vocab)
EMBEDDING_DIM = 100
N_FILTERS = 100
FILTER_SIZES = [3, 4, 5]
OUTPUT_DIM = 1
DROPOUT = 0.5
PAD_IDX = TEXT.vocab.stoi[TEXT.pad_token]

model = CNN2d(INPUT_DIM, EMBEDDING_DIM, N_FILTERS, FILTER_SIZES, OUTPUT_DIM, DROPOUT, PAD_IDX)
#-----------------------------

def count_parameters(model):
    return sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f'The model has {count_parameters(model):,} trainable parameters')
#-----------------------------

pretrained_embeddings = TEXT.vocab.vectors
model.embedding.weight.data.copy_(pretrained_embeddings)

UNK_IDX = TEXT.vocab.stoi[TEXT.unk_token]
model.embedding.weight.data[UNK_IDX] = torch.zeros(EMBEDDING_DIM)
model.embedding.weight.data[PAD_IDX] = torch.zeros(EMBEDDING_DIM)

训练模型、测试和用户输入

该部分与前面几节对应的代码相同。当测试模型时,损失为0.337,准确率为85.69%。

用户的预测结果分别为0.11990132927894592(负面情绪)和0.9671174883842468(正面情绪)。

import torch.optim as optim

optimizer = optim.Adam(model.parameters())
criterion = nn.BCEWithLogitsLoss()

model = model.to(device)
criterion = criterion.to(device)

def binary_accuracy(preds, y):
    # round predictions to the closest integer
    rounded_preds = torch.round(torch.sigmoid(preds))
    correct = (rounded_preds == y).float() # convert into float for division 
    acc = correct.sum() / len(correct)
    return acc
#-----------------------------

def train(model, iterator, optimizer, criterion):
    epoch_loss = 0
    epoch_acc = 0
    
    model.train()

    for batch in iterator:
        optimizer.zero_grad()
        predictions = model(batch.text).squeeze(1)
        loss = criterion(predictions, batch.label)
        acc = binary_accuracy(predictions, batch.label)
        
        loss.backward()
        optimizer.step()
        
        epoch_loss += loss.item()
        epoch_acc += acc.item()
        
    return epoch_loss / len(iterator), epoch_acc / len(iterator)
#-----------------------------

def evaluate(model, iterator, criterion):
    epoch_loss = 0
    epoch_acc = 0
    
    model.eval()
    
    with torch.no_grad():
        for batch in iterator:
            predictions = model(batch.text).squeeze(1)
            loss = criterion(predictions, batch.label)
            acc = binary_accuracy(predictions, batch.label)

            epoch_loss += loss.item()
            epoch_acc += acc.item()
        
    return epoch_loss / len(iterator), epoch_acc / len(iterator)
#-----------------------------

import time

def epoch_time(start_time, end_time):
    elapsed_time = end_time - start_time
    elapsed_mins = int(elapsed_time / 60)
    elapsed_secs = int(elapsed_time - (elapsed_mins * 60))
    return elapsed_mins, elapsed_secs
#-----------------------------

N_EPOCHS = 5
best_valid_loss = float('inf')

for epoch in range(N_EPOCHS):
    start_time = time.time()
    train_loss, train_acc = train(model, train_iterator, optimizer, criterion)
    valid_loss, valid_acc = evaluate(model, valid_iterator, criterion)
    end_time = time.time()
    epoch_mins, epoch_secs = epoch_time(start_time, end_time)
    
    if valid_loss         best_valid_loss = valid_loss
        torch.save(model.state_dict(), 'tut4-model.pt')
    
    print(f'Epoch: {epoch+1:02} | Epoch Time: {epoch_mins}m {epoch_secs}s')
    print(f'\tTrain Loss: {train_loss:.3f} | Train Acc: {train_acc*100:.2f}%')
    print(f'\t Val. Loss: {valid_loss:.3f} |  Val. Acc: {valid_acc*100:.2f}%')
#=============================

# Test.

model.load_state_dict(torch.load('tut4-model.pt'))
test_loss, test_acc = evaluate(model, test_iterator, criterion)
print(f'Test Loss: {test_loss:.3f} | Test Acc: {test_acc*100:.2f}%')
#=============================

# User input.

import spacy
nlp = spacy.load('en')

def predict_sentiment(model, sentence, min_len=5):
    model.eval()
    tokenized = [tok.text for tok in nlp.tokenizer(sentence)]
    if len(tokenized)         tokenized += [''] * (min_len - len(tokenized))
    indexed = [TEXT.vocab.stoi[t] for t in tokenized]
    tensor = torch.LongTensor(indexed).to(device)
    tensor = tensor.unsqueeze(0)
    prediction = torch.sigmoid(model(tensor))
    return prediction.item()

print(predict_sentiment(model, "This film is terrible"))
print(predict_sentiment(model, "This film is great"))

[1]https://github.com/bentrevett/pytorch-sentiment-analysis

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

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

相关文章

java jigsaw_60秒内使用Java 9的Project Jigsaw的JavaFX HelloWorld

java jigsaw到目前为止,您可能已经听说过Java 9的新模块系统,即项目Jigsaw。 如果您不了解Java 9的新模块系统,则应该访问Mark Reinhold的论文“模块系统的状态” 。 另外,您应该在nipafx上查看 Nicolai Parlog的优秀博客&#xf…

C++ 11 深度学习(三)范围for、new内存动态分配、nullptr

1.范围for 作用&#xff1a;用于遍历一个序列&#xff0c;例&#xff1a; int v[]{ 1,2,3,4,5,6 };for (auto x : v)//数组v中每个元素&#xff0c;依次放入x中;cout << x << endl; 2.new和delete new和delete是C中的两个关键字&#xff0c;其中new 先执行mallo…

opencv python 多帧降噪算法_OpenCV-Python中用于视频跟踪的Meanshift和Camshift算法介绍...

学习目标在本章中&#xff0c;我们将学习用于跟踪视频中对象的Meanshift和Camshift算法。MeanshiftMeanshift背后的直觉很简单&#xff0c;假设你有点的集合。(它可以是像素分布&#xff0c;例如直方图反投影)。你会得到一个小窗口(可能是一个圆形)&#xff0c;并且必须将该窗口…

【视频码率==文件传输大小等通用计算】

1字节&#xff08;Byte&#xff09; 8位&#xff08;bit&#xff09;&#xff0c;我们通常称比特为小b 1千字节&#xff08;KB,Kilobyte&#xff09;1024字节&#xff08;2的10次方字节&#xff09;&#xff08;1KB1024B&#xff09;&#xff0c;我们经常称字节为大B 通用计算…

vue.jsr入门_JSR-308和Checker框架为jOOQ 3.9添加了更多类型安全性

vue.jsr入门Java 8引入了JSR-308&#xff0c;它为Java语言添加了新的注释功能。 最重要的是&#xff1a;键入注释。 现在可以像下面这样设计怪物了&#xff1a; 比注解更疯狂的是类型注解。 在数组上。 谁认为这是有效的Java代码&#xff1f; pic.twitter.com/M9fSRRerAD — …

数组索引必须为正整数或逻辑值_Office 365函数新世界——动态数组

革新一个旧的函数体系&#xff0c;无外乎从这么几个方面入手&#xff0c;函数的运行效率、函数的编写方式以及扩展新的函数功能。今天咱们就学习一下&#xff0c;看看365新函数是如何通过这三个方面打破旧函数条条框框的。365函数系列推出了一个新的概念&#xff0c;叫做动态数…

【音视频安卓开发 (零)】用 Android NDK 编译 FFmpeg 与 X264

下载相关构建和工具链 1.先下载NDK Google Android 2.在Windows端下载使用msys2,msys2支持Linux msys2 安装相关源&#xff0c;等待更新要很久.....慢慢等 pacman -S mingw-w64-x86_64-toolchain 然后执行以下命令安装 pacman -S base-devel 如果下载过慢或者不成功可…

jax-rs jax-ws_极端懒惰:使用Spring Boot开发JAX-RS服务

jax-rs jax-ws我认为可以公平地说&#xff0c;作为软件开发人员&#xff0c;我们一直在寻找编写更少的代码的方法&#xff0c;这些代码可以自动地或不能自动地完成更多的工作。 考虑到这一点&#xff0c;作为Spring产品组合的骄傲成员的Spring Boot项目中断了传统方法&#xff…

python内置类型_Python内置对象类型

核心数字类型&#xff1a;数字&#xff1a;int&#xff0c;long&#xff0c;float&#xff0c;complex&#xff0c;bool字符&#xff1a;str&#xff0c;unicode列表&#xff1a;list字典&#xff1a;dict元组&#xff1a;tuple文件&#xff1a;file其他类型&#xff1a;集合(s…

【Android OpenGL ES 开发 (零)】创建一个新的工程

创建符合要求的AndroidApp 使用Android Studio开发&#xff0c;Android Studio下载地址 1.先创建Native C 2.项目相关添加 3.使用C11标准 学习使用JNI为Java提供API 1.在cpp中定义相关接口&#xff0c;格式如下 2.外部调用这个API 把so改成自己想要的名字 1.需要在cmake文件…

java方法参数类型不确定_一个Java方法能有多少个参数类型?这个好奇coder做了个实验...

选自 justinblank机器之心编译参与&#xff1a;李志伟、张倩在 JVM 中&#xff0c;一个 Java 方法&#xff0c;最多能定义多少参数呢&#xff1f;这是一个很无聊的问题&#xff0c;即使能定义一万个&#xff0c;十万个&#xff0c;谁又会真的去这么做呢。但是作为一个 coder&am…

lagom的微服务框架_您的第一个Lagom服务– Java Microservices入门

lagom的微服务框架在撰写我的下一份OReilly报告时&#xff0c;我一直很沮丧&#xff0c;并且一段时间内没有足够的时间来撰写博客。 是时候赶快来这里&#xff0c;让您真正快速地开始使用名为Lagom的新微服务框架。 它与您从Java EE或其他应用程序框架中可能了解到的有所不同。…

【Android OpenGL ES 开发 (一)】使用c++开发opengles 与 日志功能 及 加载assets

创建OpenGLES视口 1.App窗口改成OpenGL窗口,是通过java调用C&#xff0c;在以下位置修改如下内容 package com.example.learnogles;import androidx.appcompat.app.AppCompatActivity;import android.content.Context; import android.opengl.GLSurfaceView; import android.o…

水印相机定位不准确怎么办_禄来的广角双反相机(2020版)

点击上方胶卷迷俱乐部快速关注&#xff0c;胶卷迷们坚实的阵地内容主要原创&#xff0c;配图来自群友和网络&#xff0c;所有水印保留最下方为微信自带广告&#xff0c;支持请点击(本号可获微量收入)4.0德国禄来公司以双反相机名震天下&#xff0c;其中的2.8&#xff0c;3.5两个…

【Android OpenGL ES 开发 (二)】渲染管线与Shader

计算出每一帧耗费的时间 1.在头文件中加入time.h,cpp中实现如下计算时间接口 float GetFrameTime(){static unsigned long long lastTime0,currentTime0;timeval current;gettimeofday(&current, nullptr);//取当前时间currentTime current.tv_sec * 1000 current.tv…

tibco汉化包6.3.0_TIBCO BusinessWorks 6和Container Edition与BW5的比较

tibco汉化包6.3.0[本文已于一段时间前发布在TIBCO博客上 。 我还在适当的地方添加了一些有关BusinessWorks Container Edition&#xff08;BW CE&#xff09;的信息 。 下面定义的大多数特征对于BW6和BW CE都是正确的] TIBCO ActiveMatrix BusinessWorks 6&#xff08;BW6&…

python地图散点图_Python中基于Basemap的三维散点图

我正在尝试绘制一个三维散点图&#xff0c;图中是Python身上的烟羽&#xff0c;下面是北美的地图。我试过各种方法&#xff0c;但似乎都有缺点。我现在的代码是&#xff1a;fig plt.figure(figsize(22,4))ax Axes3D(fig)M Basemap(llcrnrlon-140,llcrnrlat10,urcrnrlon-40,u…

【Android OpenGL ES 开发 (三)】Shader 扩展

编译Shader代码 1.封装一个编译shader的接口 GLuint CompileShader(GLenum shaderType,const char *shaderCode){GLuint shaderglCreateShader(shaderType);glShaderSource(shader,1,&shaderCode,NULL);glCompileShader(shader);GLint compileResultGL_TRUE;glGetShaderi…

css阴影属性_第三场阴影场与属性访问器接口

css阴影属性这是“ 影子字段与属性访问器”界面的 第3轮 。 如果您是新手&#xff0c;但不确定要怎么做&#xff0c;请查看我以前的文章或关于开发JavaFX应用程序时节省内存的第一篇文章 。 作为Java开发人员&#xff0c;我主要关心的是在开发JavaFX域模型时在性能 &#xff0c…

js if判断多个条件_JS条件判断小技巧(一)

经常code review&#xff0c;我发现JS newbie很容易写出一堆冗长的代码。今天就列几个比较常见的“解决之道”&#xff0c;看看如何减少JS里的条件判断。提前返回&#xff0c;少用if...else“if...else是编程语言的精华。——鲁迅”但是过多的嵌套&#xff0c;还是挺令人抓狂的…