动手学习深度学习 | 语言模型和循环神经网络笔记

0.文本处理整体概况

step1:对原始数据进行分词
step2:对分词后的数据进行去重编号,得到[词语to序号]的列表,和[序号to词语]的字典。将这两部分用作后续训练循环神经网络的数据集。
step3:通过一些采样方法对构建的数据集进行采样,得到训练的批次。常见的采样方法有随机采样和相邻采样。
step4:利用语言模型对上述的数据集进行训练,得到一个nlp模型。语言模型有n元语法模型,RNN模型,LSTM模型等。

1.使用spacy可以进行语言分词

达到很好的直观效果,相较于自己构建的逻辑,更加符合语言本身词意的分词操作,且可以将分词对应的idx对应输出。

import spacy
text = "Mr. Chen doesn't agree with my suggestion."
nlp = spacy.load('en_core_web_sm')
doc = nlp(text)
print([token.text for token in doc])
print([token.idx for token in doc])

#------------------
['Mr.', 'Chen', 'does', "n't", 'agree', 'with', 'my', 'suggestion', '.']
[0, 4, 9, 13, 17, 23, 28, 31, 41]
2.随机采样和相邻采样
2.1 随机采样

下面的代码每次从数据里随机采样一个小批量。其中批量大小batch_size是每个小批量的样本数,num_steps是每个样本所包含的时间步数。 在随机采样中,每个样本是原始序列上任意截取的一段序列,相邻的两个随机小批量在原始序列上的位置不一定相毗邻。

import torch
import random
def data_iter_random(corpus_indices, batch_size, num_steps, device=None):
    # 减1是因为对于长度为n的序列,X最多只有包含其中的前n - 1个字符
    num_examples = (len(corpus_indices) - 1) // num_steps  # 下取整,得到不重叠情况下的样本个数
    example_indices = [i * num_steps for i in range(num_examples)]  # 每个样本的第一个字符在corpus_indices中的下标
    random.shuffle(example_indices)

    def _data(i):
        # 返回从i开始的长为num_steps的序列
        return corpus_indices[i: i + num_steps]
    if device is None:
        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    
    for i in range(0, num_examples, batch_size):
        # 每次选出batch_size个随机样本
        batch_indices = example_indices[i: i + batch_size]  # 当前batch的各个样本的首字符的下标
        X = [_data(j) for j in batch_indices]
        Y = [_data(j + 1) for j in batch_indices]
        yield torch.tensor(X, device=device), torch.tensor(Y, device=device)
2.2 相邻采样


在相邻采样中,相邻的两个随机小批量在原始序列上的位置相毗邻。

def data_iter_consecutive(corpus_indices, batch_size, num_steps, device=None):
    if device is None:
        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    corpus_len = len(corpus_indices) // batch_size * batch_size  # 保留下来的序列的长度
    corpus_indices = corpus_indices[: corpus_len]  # 仅保留前corpus_len个字符
    indices = torch.tensor(corpus_indices, device=device)
    indices = indices.view(batch_size, -1)  # resize成(batch_size, )
    batch_num = (indices.shape[1] - 1) // num_steps
    for i in range(batch_num):
        i = i * num_steps
        X = indices[:, i: i + num_steps]
        Y = indices[:, i + 1: i + num_steps + 1]
        yield X, Y
3.语言模型

语言模型的目标就是给定一个通过分词得到的词序列,并且评估该序列是否合理,即计算该序列的概率。语言模型从最初的n-gram方法受限于模型参数大,参数稀疏,计算效率低等不足,发展到现在的基于深度学习的RNN,LSTM等方法,大大的提高了语言模型的能力。

3.1 n-gram

如下描述所示,n-gram语言模型是一种基于统计的方法。该方法通过分词在语料库中的比重以及条件概率构建概率模型。由于条件概率的存在,该方法的最终计算需要基于n阶马尔科夫假设。且该方法由于基于分词在语料库中的比重,从而导致很多分词的出现频次很少甚至为零,导致得到的值的稀疏性太强,计算效率低。

3.2 RNN递归神经网络模型

如下描述所示,递归神经网络RNN是一种基于参数学习的语言模型。该类模型通过中间状态位的保留,隐式的实现了上述概率模型中的条件概率模型。而且此种方法学习友好,使用方便。且如下代码所示,RNN模型的参数与输入输出的时间步数无关,因此模型的复用性很强。

vocab_size = 1027
num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
# num_inputs: d
# num_hiddens: h, 隐藏单元的个数是超参数
# num_outputs: q

def get_params():
    def _one(shape):
        param = torch.zeros(shape, device=device, dtype=torch.float32)
        nn.init.normal_(param, 0, 0.01)
        return torch.nn.Parameter(param)

    # 隐藏层参数
    W_xh = _one((num_inputs, num_hiddens))
    print(W_xh.shape)
    W_hh = _one((num_hiddens, num_hiddens))
    print(W_hh.shape)
    b_h = torch.nn.Parameter(torch.zeros(num_hiddens, device=device))
    # 输出层参数
    W_hq = _one((num_hiddens, num_outputs))
    print(W_hq.shape)
    b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device))
    return (W_xh, W_hh, b_h, W_hq, b_q)
get_params()
print(num_inputs, num_hiddens, num_outputs)

#--------------------

torch.Size([1027, 256])
torch.Size([256, 256])
torch.Size([256, 1027])
1027 256 1027
发布了233 篇原创文章 · 获赞 187 · 访问量 40万+

猜你喜欢

转载自blog.csdn.net/qiu931110/article/details/104292998