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…

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

【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轮 。 如果您是新手,但不确定要怎么做,请查看我以前的文章或关于开发JavaFX应用程序时节省内存的第一篇文章 。 作为Java开发人员,我主要关心的是在开发JavaFX域模型时在性能 &#xff0c…

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

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

【Android OpenGL ES 开发 (四)】纹理相关(一)

纹理贴图的原理 1.作用:可以用来渲染视频。 2.纹理坐标 生成OpenGL中的纹理对象 1.像素数据想要绘制出来需要先变成纹理 2.创建纹理放在GPU上 GLuint CreateTexture2D(unsigned char *pixelData,int width,int height,GLenum type) {GLuint texture;glGenTextu…

jmx 替代_使用JMX作为Ganglia的现代替代品进行CLDB监视

jmx 替代有许多选项可用于监视MapR集群的性能和运行状况。 在本文中,我将介绍使用Java管理扩展(JMX)监视CLDB的鲜为人知的方法。 据最受尊敬的MapR数据工程师之一,Akihiko Kusanagi称,与使用Ganglia相比,使…

red hat安装宝塔_如何在几分钟内安装Red Hat Container Development Kit(CDK)

red hat安装宝塔作为负责开发容器化应用程序提供的可能性的应用程序开发人员或架构师,将所有工具组合在一起以帮助您入门时几乎没有帮助。 到现在。 红帽容器开发套件(CDK) 安装变得简单! 红帽提供了一个容器开发套件&#xf…

threejs 影子属性_影子场vs.属性访问器接口第2轮

threejs 影子属性如果你们还没有注意到Dirk Lemmerman和我之间的(轻松) 摊牌 ,那么让我快速提及一下我们是如何做到这一点的。 首先,Dirk创建了JavaFX技巧23:“ 为属性保存内存阴影字段 ”,以帮助应用程序开…

【OpenGL从入门到精通】Shader专题

详解GPU的工作流程 1.shader通常称为着色器,作用是把CPU上的点渲染出来。 2.shader是并行的。 3.流程:数据data (顶点数据) ----->VS(输入:data的顶点数据,输出:gl_Position的 vec4 顶点数据)----->光栅化处理…

【OpenGL从入门到精通(六)】纹理对象与纹理坐标

1.在OpenGL想要显示一张图片,需要先绘制一个自定义的几何体。 2.把图片加载到纹理对象中 3.当进行纹理贴图时候,使用纹理坐标来设置纹理对象。 2.