【动手学习深度学习----循环神经网络笔记】

第一步:文本预处理

一般的文本的常见预处理包括四个步骤:

  1. 将文本作为字符串加载到内存中。

  2. 将字符串拆分为词元(如单词和字符)。

  3. 建立一个词表,将拆分的词元映射到数字索引。

  4. 将文本转换为数字索引序列,方便模型操作。

1.读取数据集

  1. 进行忽略标点符号,和大写处理。
    with open(d2l.download('time_machine'), 'r') as f:
        lines = f.readlines()
    return [re.sub('[^A-Za-z]+', ' ', line).strip().lower() for line in lines]

2.词元化(tokenize)

token的意思是词元,而-ize的后缀则是“使动用法”可以翻译为化字。

    if token == 'word':
        return [line.split() for line in lines]
    elif token == 'char':
        return [list(line) for line in lines]
    else:
        print('错误:未知词元类型:' + token)

3.词表(vocabular)

词表是一个字典,用来把字符串类型的词元映射到从0开始的数字索引中。训练集中的所有文档合并在一起,对它们的唯一词元进行统计, 得到的统计结果称之为语料(corpus),然后根据词元出现的频率分配索引,很少出现的词元通常被移除,这可以降低复杂性。

第一步:统计词元频率并排序

def count_corpus(tokens):  #@save
    """统计词元的频率"""
    # 这里的tokens是1D列表或2D列表
    if len(tokens) == 0 or isinstance(tokens[0], list):
        # 将词元列表展平成一个列表
        tokens = [token for line in tokens for token in line]
    return collections.Counter(tokens)

# 按出现频率排序
        counter = count_corpus(tokens)
        self._token_freqs = sorted(counter.items(), key=lambda x: x[1],
                                   reverse=True)

第二步:根据排序后的token进行构建词表
对于Vocab类,有两个成员,一个idx_to_token,通过索引找token(列表的形式),一个是token_to_idx,通过token找id(字典的形式)

tips:每一条文字行转换成一个数字索引列表

for i in [0, 10]:
    print('文本:', tokens[i])
    print('索引:', vocab[tokens[i]]) # vocab['the'] = vocab.token_to_idx.items()

第二步:读取长序列数据

序列数据一般是长并且连续的,可以通过使用随机采样和顺序分区两种方法进行读取。
下面的引例中,减去1,是因为我们需要考虑标签。

下面我们生成一个从 0 到 34 的序列。 假设批量大小为 2 ,时间步数为 5 ,这意味着可以生成 ⌊(35−1)/5⌋=6 个“特征-标签”子序列对。 如果设置小批量大小为 2 ,我们只能得到 3 个小批量。

第三步:循环神经网络

n为批量大小,q为词表大小(因为X转换为one-hot编码了)

模型介绍

在这里插入图片描述
该图展现了RNN在三个相邻时间步的计算逻辑,在任意时间步t,隐状态的计算可以被看为:

拼接当前时间步 t 的输入 Xt 和前一时间步 t−1 的隐状态 Ht−1
将拼接的结果送入带有激活函数 ϕ 的全连接层。 全连接层的输出是当前时间步 t 的隐状态 Ht

数学公式:
X为n x d大小的矩阵,Ht为n x h大小的矩阵其中Ht:
在这里插入图片描述
Ot的大小为n x q:
在这里插入图片描述
注意,输出的形状有时候我发现有(时间步数 x 批量大小,词表大小),X一般为(时间步数量,批量大小,词表大小)

模型评价方法:困惑度(perplexity)

在这里插入图片描述

通过时间的反向传播

先略了。

1.独热码

因为在train_iter中每个词元都表示为一个数字索引,但直接输入网络会很困难,一般把词元表示为更有表现力的特征向量,最简单的表示就是独热码(one-hot),

简言之,将每个索引映射为相互不同的单位向量:假设词表中不同词元的数目为 N (即len(vocab)), 词元索引的范围为 0 到 N−1 。 如果词元的索引是整数 i , 那么我们将创建一个长度为 N 的全 0 向量, 并将第 i 处的元素设置为 1 。 此向量是原始词元的一个独热向量。

2.RNN模型

  1. 初始化模型返回隐状态(H0
  2. 定义rnn网络
  3. 整合为RNNModelScratch类方便调用
num_hiddens = 512
net = RNNModelScratch(len(vocab), num_hiddens, d2l.try_gpu(), get_params,
                      init_rnn_state, rnn)
state = net.begin_state(X.shape[0], d2l.try_gpu())
Y, new_state = net(X.to(d2l.try_gpu()), state)
Y.shape, len(new_state), new_state[0].shape

3.梯度裁剪

通过下面的公式进行梯度裁剪
在这里插入图片描述
代码实现

def grad_clipping(net, theta):  #@save
    """裁剪梯度"""
    if isinstance(net, nn.Module):
        params = [p for p in net.parameters() if p.requires_grad]
    else:
        params = net.params
    norm = torch.sqrt(sum(torch.sum((p.grad ** 2)) for p in params)) # 一个参数可能会有很多个偏导,所以求和。
    if norm > theta:
        for param in params:
            param.grad[:] *= theta / norm

4.训练

  1. 序列数据的不同采样方法(随机采样和顺序分区)将导致隐状态初始化的差异。
  2. 我更新模型参数之前裁剪梯度。 这样的操作的目的是:即使训练过程中某个点上发生了梯度爆炸,也能保证模型不会发散。
  3. 用困惑度来评价模型。

5.预测

定义预测函数来生成prefix之后的新字符, 其中的prefix是一个用户提供的包含多个字符的字符串,在预测前有一个预热期。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瞲_大河弯弯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值