nlp(3)自然语言分类

本系列是七月算法nlp就业班学习笔记。


三种分类方式:Word Averaging模型、RNN、CNN。

1 数据准备

第一步是准备数据。代码中用到的类库有spacy、torchtext。
torchtext中有data.Field用来处理每个文本字段。dataLabelFiled处理标签字段。

1.1 数据集拆分

将数据集分为训练集、验证集和测试集。了解每种数据集的数据量,查看每一条数据的样子。
每一条句子是一个样本。

1.2 创建词库vocabulary

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

设置词表大小,用词向量初始化词表。

1.3 batch数据,创建Iterator

训练的时候是一个batch,一个batch训练的。torchtext会将短句子pad到和最长的句子长度相同。

train_iterator, valid_iterator, test_iterator = data.BucketIterator.splits(
    (train_data, valid_data, test_data), 
    batch_size=BATCH_SIZE,
    device=device)

数据准备好了,就开始用模型训练吧。

2 Word Averaging模型

我们首先介绍一个简单的Word Averaging模型。这个模型非常简单,我们把每个单词都通过Embedding层投射成word embedding vector,然后把一句话中的所有word vector做个平均,就是整个句子的vector表示了。
接下来把这个sentence vector传入一个Linear层,做分类即可。
怎么做平均呢?我们使用avg_pool2d来做average pooling。我们的目标是把sentence length那个维度平均成1,然后保留embedding这个维度。
avg_pool2d的kernel size是 (embedded.shape[1], 1),所以句子长度的那个维度会被压扁。
在这里插入图片描述

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

class WordAVGModel(nn.Module):
    def __init__(self, vocab_size, embedding_dim, output_dim, pad_idx):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embedding_dim, padding_idx=pad_idx)
        self.fc = nn.Linear(embedding_dim, output_dim)
        
    def forward(self, text):
        embedded = self.embedding(text) # [sent len, batch size, emb dim]
        embedded = embedded.permute(1, 0, 2) # [batch size, sent len, emb dim]
        pooled = F.avg_pool2d(embedded, (embedded.shape[1], 1)).squeeze(1) # [batch size, embedding_dim]
        return self.fc(pooled)

接下来就是模型训练、评价,。

3 RNN模型

在这里插入图片描述

我们使用最后一个hidden state h T h_T hT来表示整个句子。
然后我们把 h T h_T hT通过一个线性变换f,然后用来预测句子的情感。

class RNN(nn.Module):
    def __init__(self, vocab_size, embedding_dim, hidden_dim, output_dim, 
                 n_layers, bidirectional, dropout, pad_idx):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embedding_dim, padding_idx=pad_idx)
        self.rnn = nn.LSTM(embedding_dim, hidden_dim, num_layers=n_layers, 
                           bidirectional=bidirectional, dropout=dropout)
        self.fc = nn.Linear(hidden_dim*2, output_dim)
        self.dropout = nn.Dropout(dropout)
        
    def forward(self, text):
        embedded = self.dropout(self.embedding(text)) #[sent len, batch size, emb dim]
        output, (hidden, cell) = self.rnn(embedded)
        #output = [sent len, batch size, hid dim * num directions]
        #hidden = [num layers * num directions, batch size, hid dim]
        #cell = [num layers * num directions, batch size, hid dim]
        
        #concat the final forward (hidden[-2,:,:]) and backward (hidden[-1,:,:]) hidden layers
        #and apply dropout
        hidden = self.dropout(torch.cat((hidden[-2,:,:], hidden[-1,:,:]), dim=1)) # [batch size, hid dim * num directions]
        return self.fc(hidden.squeeze(0))

4 CNN

CNN可以把每个局域的特征提出来。在用于句子情感分类的时候,其实是做了一个ngram的特征抽取。
首先做一个embedding,得到每个词的词向量,是一个k维的向量。每个词的词向量结合到一起,得到一个nxk的矩阵。n是单词个数。
在这里插入图片描述

其次卷积层filter。现在相当于一个hgram。最后转化为h个单词。
在这里插入图片描述
w是hxk的。
每一个h单词的窗口都会被这个filter转化。 c = [ c 1 , c 2 , . . . c n − h − 1 ] c=[c_1,c_2,...c_{n-h-1}] c=[c1,c2,...cnh1]

第三步,做一个max-over-time pooling。 C ^ = m a x { c } \widehat{C}=max\{c\} C =max{ c}

如果有m个filter,我们会得到 z = [ C 1 ^ , C 2 ^ , . . . C m ^ ] z=[\widehat{C_1},\widehat{C_2},...\widehat{C_m}] z=[C1 ,C2 ,...Cm ]

第四步,对z做一个线性变换,得到分类。

猜你喜欢

转载自blog.csdn.net/flying_all/article/details/115317403
今日推荐