Word Averaging模型做文本分类 稳定效果好模型简单

利用WORDAVG模型做文本分类   模型简单 效果号

简单思想就是  将每个词向量在 句子维度进行求平均  可以用avgpool来做平均池化 

然后用平均后的向量作为句子向量 进行文本分类 

后面我们还会介绍将rnn表示的句子向量进行文本分类 

也可以用cnn进行文本分类 

我们这篇文章代码和注释  主要是介绍 WORDEMage进行文本分类 

"""
准备数据
TorchText中的一个重要概念是Field。Field决定了你的数据会被怎样处理。在我们的情感分类任务中,
我们所需要接触到的数据有文本字符串和两种情感,"pos"或者"neg"。
Field的参数制定了数据会被怎样处理。
我们使用TEXT field来定义如何处理电影评论,使用LABEL field来处理两个情感类别。
我们的TEXT field带有tokenize='spacy',这表示我们会用spaCy tokenizer来tokenize英文句子。
如果我们不特别声明tokenize这个参数,那么默认的分词方法是使用空格。
安装spaCy
pip install -U spacy
python -m spacy download en
LABEL由LabelField定义。这是一种特别的用来处理label的Field。我们后面会解释dtype。
更多关于Fields,参见https://github.com/pytorch/text/blob/master/torchtext/data/field.py
和之前一样,我们会设定random seeds使实验可以复现。
"""
import torch
from torchtext import data

SEED=1234
torch.manual_seed(SEED)
torch.cuda.manual_seed(SEED)
torch.backends.cudnn.deterministic=True
"""
先决定怎么处理数据  Field是决定如何处理数据的  
默认进行空格分词
spacy 是根据点进行分词
spaCy是世界上最快的工业级自然语言处理工具。 支持多种自然语言处理基本功能。
官网地址:https://spacy.io/
spaCy主要功能包括分词、词性标注、词干化、命名实体识别、名词短语提取等等。


"""
TEXT=data.Field(tokenize='spacy')
LABEL=data.LabelField(dtype=torch.float)

"""
TorchText支持很多常见的自然语言处理数据集。
下面的代码会自动下载IMDb数据集,然后分成train/test两个torchtext.datasets类别。
数据被前面的Fields处理。IMDb数据集一共有50000电影评论,每个评论都被标注为正面的或负面的。
"""
from torchtext import datasets
train_data,test_data=datasets.IMDB.splits(TEXT,LABEL)

"""
查看每个数据split 有多少条数据 
"""
print(f'number of training examples:{len(train_data)}')
print(f"number of  testing examples:{len(test_data)}")
"""
查看一个example  

{'text': ['Brilliant', 'adaptation', 'of', 'the', 'novel', 'that', 'made', 'famous', 'the', 'relatives', 'of', 'Chilean', 'President', 'Salvador', 'Allende', '
killed', '.', 'In', 'the', 'environment', 'of', 'a', 'large', 'estate', 'that', 'arises', 'from', 'the', 'ruins', ',', 'becoming', 'a', 'force', 'to', 'abuse', 'and',
'exploitation', 'of', 'outrage', ',', 'a', 'luxury', 'estate', 'for', 'the', 'benefit', 'of', 'the', 'upstart', 'Esteban', 'Trueba', 'and', 'his', 'undeserved',
'family', ',', 'the', 'brilliant', 'Danish', 'director', 'Bille', 'August', 'recreates', ',', 'in', 'micro', ',', 'which', 'at', 'the', 'time', 'would', 'be', 'the', 
'process', 'leading', 'to', 'the', 'greatest', 'infamy', 'of', 'his', 'story', 'to', 'the', 'hardened', 'Chilean', 'nation', ',', 'and', 'whose', 'main',
'character', 'would', 'Augusto', 'Pinochet', '(', 'Stephen', 'similarities', 'with', 'it', 'are', 'inevitable', ':', 'recall', ',', 'as', 'an', 'example', ',',
'that', 'image', 'of', 'the', 'senator', 'with', 'dark', 'glasses', 'that', 'makes', 'him', 'the', 'wink', 'to', 'the', 'general', 'to', 'begin', 'making', 
'the', 'palace).Bille', 'August', 'attends', 'an', 'exceptional', 'cast', 'in', 'the', 'Jeremy', 'protruding', 'Irons', ',', 'whose', 
'character', 'changes', 'from', 'arrogance', 'and', 'extreme', 'cruelty', ',', 'the', 'hard', 'lesson', 'that', 'life', 'always', 'brings', 'us', 'to',
'almost', 'force', 'us', 'to', 'change', '.', 'In', 'Esteban', 'fully', 'applies', 'the', 'law', 'of', 'resonance', ',', 'with', 'great', 'wisdom', ',', 
'Solomon', 'describes', 'in', 'these', 'words:"The', 'things', 'that', 'freckles', 'are', 'the', 'same', 'punishment', 'that', 'will', 'serve', 
'you', '.', '"', '<', 'br', '/>Unforgettable', 'Glenn', 'Close', 'playing', 'splint', ',', 'the', 'tainted', 'sister', 'of', 'Stephen', ',',
'whose', 'sin', ',', 'driven', 'by', 'loneliness', ',', 'spiritual', 'and', 'platonic', 'love', 'was', 'the', 'wife', 'of', 'his', 'cruel', 'snowy', '
brother', '.', 'Meryl', 'Streep', 'also', 'brilliant', ',', 'a', 'woman', 'whose', 'name', 'came', 'to', 'him', 'like', 'a', 'glove', 'Clara', '.', 
'With', 'telekinetic', 'powers', ',', 'cognitive', 'and', 'mediumistic', ',', 'this', 'hardened', 'woman', ',', 'loyal', 'to', 'his', 'blunt', ',', 
'conservative', 'husband', ',', 'is', 'an', 'indicator', 'of', 'character', 'and', 'self', '-', 'control', 'that', 'we', 'wish', 'for', 'ourselves', 
'and', 'for', 'all', 'human', 'beings', '.', '<', 'br', '/>Every', 'character', 'is', 'a', 'portrait', 'of', 'virtuosity', '(', 'as', 'Blanca', 
'worthy', 'rebel', 'leader', 'Pedro', 'Segundo', 'unhappy', '...', ')', 'or', 'a', 'portrait', 'of', 'humiliation', ',', 'like', 'Stephen', 'Jr.', ',', 
'the', 'bastard', 'child', 'of', 'Senator', ',', 'who', 'serves', 'as', 'an', 'instrument', 'for', 'the', 'return', 'of', 'the', 'boomerang', '.',
'<', 'br', '/>The', 'film', 'moves', 'the', 'bowels', ',', 'we', 'recreated', 'some', 'facts', 'that', 'should', 'not', 'ever', 'be',
'repeated', ',', 'but', 'that', 'absurdly', 'still', 'happen', '(', 'Colombia', 'is', 'a', 'sad', 'example', ')', 'and', 'another', 'reminder', 'that', ',', 'against', 'all', ',',
'life', 'is', 'wonderful', 'because', 'there', 'are', 'always', 'people', 'like', 'Isabel', 'Allende', 'and', 'immortalize', 'just', 'Bille', 'August', '.'], 'label': 'pos'}
"""

print(vars(train_data.examples[0]))

"""
由于我们现在只有train/test这两个分类,所以我们需要创建一个新的validation set。我们可以使用.split()创建新的分类。
默认的数据分割是 70、30,如果我们声明split_ratio,可以改变split之间的比例,split_ratio=0.8表示80%的数据是训练集,20%是验证集。
我们还声明random_state这个参数,确保我们每次分割的数据集都是一样的。

"""
import random
train_data,valid_data=train_data.split(random_state=random.seed(SEED))
#检查一下现在每个部分有多少条数据 
print(f'number of training examples:{len(train_data)}')
print(f'number of  validation examples:{len(valid_data)}')
print(f'number if testing examples:{len(test_data)}')

"""
下一步我们需要创建 vocabulary 。vocabulary 就是把每个单词一一映射到一个数字。
我们使用最常见的25k个单词来构建我们的单词表,用max_size这个参数可以做到这一点。
所有其他的单词都用来表示。

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

print(f'unique tokens in TEXT vocabulary:{len(TEXT.vocab)}')
print(f'unique tokens in LABEL vocabulary:{len(LABEL.vocab)}')


"""
当我们把句子传进模型的时候,我们是按照一个个 batch 穿进去的,也就是说,
我们一次传入了好几个句子,而且每个batch中的句子必须是相同的长度。为了确保句子的长度相同,TorchText会把短的句子pad到和最长的句子等长。
下面我们来看看训练数据集中最常见的单词。
[('the', 201455), (',', 192552), ('.', 164402), ('a', 108963), ('and', 108649), ('of', 100010), ('to', 92873), ('is', 76046), 
('in', 60904), ('I', 54486), ('it', 53405), ('that', 49155), 
('"', 43890), ("'s", 43151), ('this', 42454), ('-', 36769), ('/>', '', 'the', ',', '.', 'a', 'and', 'of', 'to', 'is']
"""
print(TEXT.vocab.itos[:10])

"""
查看labels。
defaultdict(, {'neg': 0, 'pos': 1})
"""
print(LABEL.vocab.stoi)

"""
最后一步数据的准备是创建iterators。每个itartion都会返回一个batch的examples。
我们会使用BucketIterator。BucketIterator会把长度差不多的句子放到同一个batch中,确保每个batch中不出现太多的padding。
严格来说,我们这份notebook中的模型代码都有一个问题,也就是我们把也当做了
模型的输入进行训练。更好的做法是在模型中把由产生的输出给消除掉。在这节课中我们简单处理,
这个可以  pack_pad处理即可及解决这个问题 稍后我们对模型进行简单的修改  去掉padd的 值  
那么就要知道每个句子的长度 
直接把也用作模型输入了。由于数量不多,模型的效果也不差。
如果我们有GPU,还可以指定每个iteration返回的tensor都在GPU上。
"""
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
)

"""
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(WordAVGModel,self).__init__()
        self.embedding_dim=nn.Embedding(vocab_size,embedding_dim,padding_idx=pad_idx)
        
        self.fc=nn.Linear(embedding_dim,output_dim)
        
    def forward(self,text):
        """
        text [seten_len,batch_size]>>>[seten_len,batch_Size,emb_dim]
        """
        embedded=self.embedding(text)
        """
        将bacth_size 的维度放在前面
        [batch_size,seten_len,embed_size] 也可以用transpose
        """
        embedded=embedded.permute(1,0,2)
        """
        沿着seq_len维度 句子进行平均此话  得到句子的平局词向量
        [batch_size,1,embed_size]>>>[batch_Size,embed_Size]
        
        """
        pooled=F.avg_pool2d(embedded,(embedded.shape(1),1)).squeeze()
        """
        将平局之后的句子向量放到线性层 进行分类   
        [batch_Size,embed_Size]>>>[batch_Size,output_Size]
        """
        return self.fc(pooled)
    

    
"""
定义超参数
初始化模型
"""
INPUT_DIM=len(TEXT.vocab)
EMNEDDING_DIM=100
OUTPUT_DIM=1
PAD_IDX=TEXT.vocab.stoi[TEXT.pad_token]

model=WordAVGModel(INPUT_DIM,EMNEDDING_DIM,OUTPUT_DIM,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')


"""
将Glove 100维的词向量  作为模型的初始化参数  可以加速的收敛

tensor([[-0.1117, -0.4966,  0.1631,  ...,  1.2647, -0.2753, -0.1325],
        [-0.8555, -0.7208,  1.3755,  ...,  0.0825, -1.1314,  0.3997],
        [-0.0382, -0.2449,  0.7281,  ..., -0.1459,  0.8278,  0.2706],
        ...,
        [-0.7244, -0.0186,  0.0996,  ...,  0.0045, -1.0037,  0.6646],
        [-1.1243,  1.2040, -0.6489,  ..., -0.7526,  0.5711,  1.0081],
        [ 0.0860,  0.1367,  0.0321,  ..., -0.5542, -0.4557, -0.0382]])
"""
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(EMNEDDING_DIM)
model.embedding.weight.data[PAD_IDX]=torch.zeros(EMNEDDING_DIM)

"""
训练模型 
"""
import torch.optim as optim

optimizer=optim.Adam(model.parameters())
"""
这是一个而计算二分类分布之间差别的 损失函数  
"""
criterion=nn.BCEWithLogitsLoss()
model=model.to(device)
criterion=criterion.to(device)

"""
计算预测的准确率 
"""

def  binary_accuacry(preds,y):
    """
    returns accuacry per batch  ie if you get 8/10 right this returns  0.8 NOT 8
    """
    # round predictions to the closest integer
    """
    将输出值进行 sigmoid 到0-1之间 然后进行四舍五入
    """
    rounded_preds=torch.round(torch.sigmoid(preds))
    correct=(rounded_preds==y).float()
    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()
        """
         将平局之后的句子向量放到线性层 进行分类   
        [batch_Size,embed_Size]>>>[batch_Size,output_Size]
        output_size=1
        
        要对形状进行变化 
        """
        predictions=model(batch.text).squeeze(1)
        loss=criterion(predictions,batch.label)
        
        acc=binary_accuacry(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).suqeeze(1)
            
            loss=criterion(predictions,batch.label)
            acc=binary_accuacry(predictions,batch.label)
            epoch_loss+=loss.item()
            epoch_acc+=loss.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)
    
    elased_secs=int(elapsed_time-(elapsed_mins*60))
    
    return  elapsed_mins,elased_secs


N_EPOCH=10
best_valid_loss=float('inf')

for epoch in range(N_EPOCH):
    start_time=time.time()
    train_loss,train_acc=train(model,train_iteratorn,optimizer,criterion)
    
    valid_loss,valid_acc=evaluate(moedl,valid_iterator.criterion)
    
    end_time=time.time()
    epoch_mins,epoch_secs=epoch_time(start_time,end_time)
    
    
    if valid_loss

 

你可能感兴趣的:(pytorch官方教程)