boyu《动手学深度学习Pytorch版》(02) -- 文本预处理, 语言模型, 循环神经网络基础

文本预处理

  • 最简单的文本预处理方法,就是按照字母来给序号a–>1, b–>2, … z–>26。但是这样得到的编码结果本身没有太多的信息,网络需要对其中的信息进行更深入的挖掘
  • 先分词处理,对词进行编码,比如“我爱你中国”可以分词为<我> <爱> <你> <中国>。这样编码的结果富含更多的信息,网络在学习的时候更高效。但是这样会导致编码库膨胀,比如汉子单用3500个,但是词语得有几十万了吧?
  • word2vec就是来对词语空间进行压缩编码的,用一个1xN的向量来表示一个词语
  • 常用分词工具spacy, nltk
  • <unk> unknow这一个特殊符号在编码的时候是一定要有的,因为我们的字典不能保证含有所有的词。

语言模型

  • 已知前t-1个字符,推测第t个字符的分布概率(这也是RNN要做的工作):
    P ( w T ∣ w 1 w 2 ⋯ w T − 1 ) P(w_T \mid w_1w_2\cdots w_{T-1}) P(wTw1w2wT1)

  • 一个序列出现的概率:
    P ( w 1 , w 2 , … , w T ) = ∏ t = 1 T P ( w t ∣ w 1 , … , w t − 1 ) = P ( w 1 ) P ( w 2 ∣ w 1 ) ⋯ P ( w T ∣ w 1 w 2 ⋯ w T − 1 ) \begin{matrix} P(w_1, w_2, \ldots, w_T) &= \prod_{t=1}^T P(w_t \mid w_1, \ldots, w_{t-1})\\ &= P(w_1)P(w_2 \mid w_1) \cdots P(w_T \mid w_1w_2\cdots w_{T-1}) \end{matrix} P(w1,w2,,wT)=t=1TP(wtw1,,wt1)=P(w1)P(w2w1)P(wTw1w2wT1)

  • 由马尔科夫假设,字符的出现只与前面的n个字符有关.极端情况下就是只与前面的一个字符有关:
    P ( w 1 , w 2 , w 3 , w 4 ) = P ( w 1 ) P ( w 2 ∣ w 1 ) P ( w 3 ∣ w 2 ) P ( w 4 ∣ w 3 ) , P(w_1, w_2, w_3, w_4) = P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_2) P(w_4 \mid w_3) ,\\ P(w1,w2,w3,w4)=P(w1)P(w2w1)P(w3w2)P(w4w3),

时序数据的采样

  • 主要分为“随机采样”,“相邻采样”两种

假设序列长度为num_steps

  • 随机采样:
  1. 先把文本整个序列按照num_steps进行切分,得到序列[0, 1, …, n-1], [n, n+1, …, 2n-1], …
  2. 序列头{0, n, 2n, …}
  3. 从序列头结合中随机抽取batch_size个,有序列头往后找num_step个字符就是一条数据

随机采样中每个样本只包含局部的时间序列信息,因为样本不完整所以每个批量需要重新初始化隐藏状态

  • 相邻采样
  1. 切分成batch_size份,每份长度相同,多余的字符抛弃
  2. reshape层(batch_size, K)的矩阵
  3. 第i个batch就是[num_step*i: num_step*(i+1)]列数据组成

循环神经网络基础

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))
    W_hh = _one((num_hiddens, num_hiddens))
    b_h = torch.nn.Parameter(torch.zeros(num_hiddens, device=device))
    # 输出层参数
    W_hq = _one((num_hiddens, num_outputs))
    b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device))
    return (W_xh, W_hh, b_h, W_hq, b_q)

注意其中的num_hiddens并不是下图中H1, H2, …H5总共5个node,而是一个H1中feature的维度
在这里插入图片描述
RNN节点:

def rnn(inputs, state, params):
    # inputs和outputs皆为num_steps个形状为(batch_size, vocab_size)的矩阵
    W_xh, W_hh, b_h, W_hq, b_q = params
    H, = state
    outputs = []
    for X in inputs:
        H = torch.tanh(torch.matmul(X, W_xh) + torch.matmul(H, W_hh) + b_h)
        Y = torch.matmul(H, W_hq) + b_q
        outputs.append(Y)
    return outputs, (H,)

预测函数

def predict_rnn(prefix, num_chars, rnn, params, init_rnn_state,
                num_hiddens, vocab_size, device, idx_to_char, char_to_idx):
    state = init_rnn_state(1, num_hiddens, device)
    output = [char_to_idx[prefix[0]]]   # output记录prefix加上预测的num_chars个字符
    for t in range(num_chars + len(prefix) - 1):
        # 将上一时间步的输出作为当前时间步的输入
        X = to_onehot(torch.tensor([[output[-1]]], device=device), vocab_size)
        # 计算输出和更新隐藏状态
        (Y, state) = rnn(X, state, params)
        # 下一个时间步的输入是prefix里的字符或者当前的最佳预测字符
        if t < len(prefix) - 1:
            output.append(char_to_idx[prefix[t + 1]])
        else:
            output.append(Y[0].argmax(dim=1).item())
    return ''.join([idx_to_char[i] for i in output])

使用举例,注意output的前两个字符就是输入的prefix,后面的8个是模型预测的。而输入只在第一步用了输入的‘分’字,后面都是用上一次的output,所以‘开’字没有被用作输入x

predict_rnn('分开', 10, rnn, params, init_rnn_state, num_hiddens, vocab_size,
            device, idx_to_char, char_to_idx)

梯度裁剪防止梯度暴涨或衰减

min ⁡ ( θ ∥ g ∥ , 1 ) g \min\left(\frac{\theta}{\|\boldsymbol{g}\|}, 1\right)\boldsymbol{g} min(gθ,1)g

def grad_clipping(params, theta, device):
    norm = torch.tensor([0.0], device=device)
    for param in params:
        norm += (param.grad.data ** 2).sum()
    norm = norm.sqrt().item()
    if norm > theta:
        for param in params:
            param.grad.data *= (theta / norm)

困惑度

  • 最佳情况下,模型总是把标签类别的概率预测为1,此时困惑度为1;
  • 最坏情况下,模型总是把标签类别的概率预测为0,此时困惑度为正无穷;
  • 基线情况下,模型总是预测所有类别的概率都相同,此时困惑度为类别个数。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值