Pytorch 自然语言处理基础

人生苦短 我用 pytorch

文章基于该 notebook,加入自己的理解。

<一>.Pytorch 基础

Pytorch 从实践角度来说,相比 tensorflow,keras 使用起来方便不少(其他的框架我也没用过,尴尬脸)。话不多说,开始上代码。

# 全家桶 使用 Pytorch 必备 具体功能且看下文
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable

那么大千世界的数据,如何在 Pytorch 中使用呢?

# 以下期双色球号码为例
haoma = [10, 12, 13, 14, 15, 16, 2]
haoma_in_numpy = np.array(haoma)
# Tensor's type: 一共八种 常用 FloatTensor 和 LongTensor
# 另外的写法: torch.LongTensor() 和下面本质一样
haoma_in_pytorch = torch.Tensor(haoma).type(torch.LongTensor)
haoma_in_numpy_to_pytorch_tensor = torch.from_numpy(haoma_in_numpy)
haoma_in_pytorch_to_numpy_array = haoma_in_pytorch.numpy()

计算图和自动微分,计算图在 CS231n 有清楚的讲解。

自动微分呢,往简单说就是 BP 算法,再简单的说就是带缓存的链式求导,结合计算图,自我体会 逃:)

# 以预测下下期彩票双色球篮球号码为例
haoma_lanse_xiaqi_in_pytorch = haoma_in_pytorch[6]
# 把数值 放入变量
var_lanse_xiaqi_in_pytorch = Variable(torch.LongTensor([haoma_lanse_xiaqi_in_pytorch]), requires_grad=True)
haoma_lanse_xiaxiaqi_in_pytorch = 2 * var_lanse_xiaqi_in_pytorch  + 1
# 打印下下期蓝色球号码
print(haoma_lanse_xiaxiaqi_in_pytorch)

# Variable containing:
# 5
# [torch.LongTensor of size 1]

haoma_lanse_xiaxiaqi_in_pytorch.grad_fn
# 这里可以理解成计算图上的一个点
# <torch.autograd.function.AddConstantBackward object at 0x7f9ed6fa3a98>

# 自动微分
haoma_lanse_xiaxiaqi_in_pytorch.backward()
# 得到 dy/dx 结果
print(var_lanse_xiaqi_in_pytorch.grad)
# Variable containing:
# 2
# [torch.LongTensor of size 1]

<二>.词袋模型(Bag-of-Words),Word Embedding 以及 Word2vec

看过了 Pytorch 基础后,我们正式进入 NPL 部分。以 Bag-of-Words,Word Embedding 以及 Word2vec 为切入点。对 Pytorch 中的自然语言处理进行探索。

# 用词袋模型表示一句话
import jieba
from collections import Counter
import numpy as np

sentences = [
    "我有一个很久远的梦想它让我魂牵梦绕我希望有朝一日我可以中彩票", 
    "除此之外我还有一个特别小的梦想就是我希望这个世界和平"
]
total_counts = Counter()
for i in range(len(sentences)):
    for word in list(jieba.cut(sentences[i])):
        total_counts[word] += 1

vocab = set(total_counts.keys())
# vocab_size 为 23
vocab_size = len(vocab)
BOWs = np.zeros((1,vocab_size))

# 把第一句话转换成词袋模型的向量
word2index = {}
for i,word in enumerate(vocab):
    word2index[word] = i
    
word2index

def to_bows(review):
     
    global BOWs
    
    BOWs *= 0
    
    for word in review:
        if not BOWs[0][word2index[word]]:
            BOWs[0][word2index[word]] += 1
            
to_bows(list(jieba.cut(sentences[0])))

print(BOWs)
# [[ 0. 1. 0. 1. 0. 1. 1. 1. 1. 0. 1. 1. 0. 1. 1. 1. 1. 0.
# 1. 1. 1. 0. 0.]]
# 缺点很明显 当 vocab_size 很大时,维度会很大,可怕的维度灾难!

单词嵌入,一种深度神经网络方法,可以用大量的类更高效地表示数据,即通过用更低维的向量表示数据。

word_to_ix = { "我": 0, "喜欢": 1, "她": 2}
embeds = nn.Embedding(3, 5) # 3 words in vocab, 5 dimensional embeddings
lookup_tensor = torch.LongTensor([word_to_ix["我"]])
me_embed = embeds(Variable(lookup_tensor) )
print(hello_embed)

# Variable containing:
# -1.8008 0.1119 -1.1834 0.4894 -0.4060
# [torch.FloatTensor of size 1x5]

# 这里的 embedding 只是随机生成的向量
# 未经过训练,下面看如何使用 word2vec 训练词向量

使用 word2vec 训练词向量

CONTEXT_SIZE = 2
raw_text = """我知道生活是十分艰难的不论我走到天南还是地北 我总是见到人性的虚伪亲爱的神伟大的神良心仿佛在笑我"""

word_to_ix = { word: i for i, word in enumerate(set(jieba.cut(raw_text))) }
data = []
raw_text = list(jieba.cut(raw_text))
for i in range(2, len(raw_text) - 2):
    target = [ raw_text[i-2], raw_text[i-1], raw_text[i+1], raw_text[i+2] ]
    context = raw_text[i]
    data.append( (context, target) )
print(data[:5])
# 为了训练把数据改成如下形式
data_t = []
for i in range(2, len(raw_text) - 2):
    target = [ raw_text[i-2], raw_text[i-1], raw_text[i+1], raw_text[i+2] ]
    context = raw_text[i]
    for i in range(len(target)):
        data_t.append( (context, target[i]))
print(data_t[:20])

# data
# [('生活', ['我', '知道', '是', '十分']), ('是', ['知道', '生活', '十分', '艰难']), ('十分', ['生活', '是', '艰难', '的']), ('艰难', ['是', '十分', '的', '不论']), ('的', ['十分', '艰难', '不论', '我'])]
# data_t
# [('生活', '我'), ('生活', '知道'), ('生活', '是'), ('生活', '十分'), ('是', '知道'), ('是', '生活'), ('是', '十分'), ('是', '艰难'), ('十分', '生活'), ('十分', '是'), ('十分', '艰难'), ('十分', '的'), ('艰难', '是'), ('艰难', '十分'), ('艰难', '的'), ('艰难', '不论'), ('的', '十分'), ('的', '艰难'), ('的', '不论'), ('的', '我')]

# Word2Vec Model.
class Word2Vec(nn.Module):
    def __init__(self, vocab_size, embedding_dim):
        super(Word2Vec, self).__init__()
        self.embeddings = nn.Embedding(vocab_size, embedding_dim)
        self.linear = nn.Linear(embedding_dim, vocab_size)
        
    def forward(self, inputs):
        embeds = self.embeddings(inputs).view((1, -1))
        out = self.linear(embeds)
        log_probs = F.log_softmax(out)
        return log_probs

那么我们接下来来看 NPL 常用模型 RNN 的 Pytorch 实现,Pytorch 的好用之处之一,是实现 RNN 以及自然语言处理的一些机制(如:Attention, teacher forcing…)特别方便,和 Tensorflow 和 Keras 相比。如果你想弄明白 RNN 的机制,移步如下链接:

https://www.youtube.com/watch?v=Keqep_PKrY8&index=8&list=PL3FW7Lu3i5Jsnh1rnUwq_TcylNr7EkRe6

https://www.youtube.com/watch?v=QuELiw8tbx8&list=PL3FW7Lu3i5Jsnh1rnUwq_TcylNr7EkRe6&index=9

http://colah.github.io/posts/2015-08-Understanding-LSTMs/

<三>.RNN 模型

代码实现 RNN

class RNN(nn.Module):
  
    def __init__(self, batch_size, num_tokens, embed_size, hidden):        
        super(RNN, self).__init__()
        
        self.batch_size = batch_size
        self.num_tokens = num_tokens
        self.embed_size = embed_size
        self.hidden = hidden
        
        self.lookup = nn.Embedding(num_tokens, embed_size)
        self.rnn = nn.RNN(embed_size, hidden)
        
    def forward(self, embed, state_word):
        embedded = self.lookup(embed)
        output_word, state_word = self.rnn(embedded, state_word)       
        return output_word, state_word
    
    def init_hidden(self):
        return Variable(torch.zeros(1, self.batch_size, self.hidden))

<四>.LSTM 模型

RNN unit 改进版: LSTM 实现( GRU, SRU 同理)

# 实现起来很简单 只需把 nn.RNN 替换成 nn.LSTM
class RNN(nn.Module):
  
    def __init__(self, batch_size, num_tokens, embed_size, hidden):        
        super(RNN, self).__init__()
        
        self.batch_size = batch_size
        self.num_tokens = num_tokens
        self.embed_size = embed_size
        self.hidden = hidden
        
        self.lookup = nn.Embedding(num_tokens, embed_size)
        self.LSTM = nn.LSTM(embed_size, hidden)
        
    def forward(self, embed, state_word):
        embedded = self.lookup(embed)
        output_word, state_word = self.LSTM(embedded, state_word)       
        return output_word, state_word
    
    def init_hidden(self):
        return Variable(torch.zeros(1, self.batch_size, self.hidden))

<五>.Bi-LSTM 模型

# 实现起来很简单 只需把 nn.RNN 替换成 nn.LSTM 以及
# 添加属性 bidirectional= True
# 倍化 hidden_size
class RNN(nn.Module):
  
    def __init__(self, batch_size, num_tokens, embed_size, hidden):        
        super(RNN, self).__init__()
        
        self.batch_size = batch_size
        self.num_tokens = num_tokens
        self.embed_size = embed_size
        self.hidden = hidden
        
        self.lookup = nn.Embedding(num_tokens, embed_size)
        self.bi_LSTM = nn.LSTM(embed_size, word_gru_hidden, bidirectional= True)
        
    def forward(self, embed, state_word):
        embedded = self.lookup(embed)
        output_word, state_word = self.bi_LSTM(embedded, state_word)       
        return output_word, state_word
    
    def init_hidden(self):
        return Variable(torch.zeros(2, self.batch_size, self.hidden))

好了,文章到这里就结束了。希望大家看完后有所收获。有哪里写错的话,欢迎指正。最后,祝中秋快乐~(完)

    原文作者:quoniammm
    原文地址: https://zhuanlan.zhihu.com/p/29751885
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞