这里对《ElitesAI·动手学深度学习PyTorch版》Task2的学习做个简短的总结。
Task2中总共分为3个部分:文本预处理、语言模型、RNN基础。
主要学习了分词,n-gram,随机采样与相邻采样还有基础的RNN知识。
文本预处理
1. 关于re正则
推荐学习链接:https://blog.youkuaiyun.com/qq_41185868/article/details/96422320#3%E3%80%81%E6%A3%80%E7%B4%A2%E5%92%8C%E6%9B%BF%E6%8D%A2
lines = [re.sub('[^a-z]+', ' ', line.strip().lower()) for line in f]
# 上面这行代码的正则部分为
re.sub('[^a-z]+', ' ', str)
# re.sub()函数是用来字符串替换的函数
# '[^a-z]+' 注意这里的^是非的意思,就是说非a-z字符串
# 上面句子的含义是:将字符串str中的非小写字母开头的字符串以空格代替
2. 关于建立词典
这里是对Vocab类实现的理解,首先是这个类想干什么?
Vocab类想实现将词映射成一个索引,既然是索引那么相同的词就应该具有相同的索引,所以这里对于输入的文本还会进行一个去重的操作。
此外,Vocab还想方便的获取给定某个词对应的索引,以及给定一个索引获取这个索引所对应的词。除了上面说的两个功能,还有一个就是统计了每一个词的词频。
3. 常用的分词工具
- Spacy
- NLTK
语言模型
1. n-gram语言模型
假设序列w1,w2,…,wTw_1, w_2, \ldots, w_Tw1,w2,…,wT中的每个词是依次生成的,我们有
KaTeX parse error: No such environment: align* at position 8: \begin{̲a̲l̲i̲g̲n̲*̲}̲ P(w_1, w_2, \l…
例如,一段含有4个词的文本序列的概率
P(w1,w2,w3,w4)=P(w1)P(w2∣w1)P(w3∣w1,w2)P(w4∣w1,w2,w3). P(w_1, w_2, w_3, w_4) = P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_1, w_2) P(w_4 \mid w_1, w_2, w_3). P(w1,w2,w3,w4)=P(w1)P(w2∣w1)P(w3∣w1,w2)P(w4∣w1,w2,w3).
语言模型的参数就是词的概率以及给定前几个词情况下的条件概率。设训练数据集为一个大型文本语料库,如维基百科的所有条目,词的概率可以通过该词在训练数据集中的相对词频来计算,例如,w1w_1w1的概率可以计算为:
P^(w1)=n(w1)n \hat P(w_1) = \frac{n(w_1)}{n} P^(w1)=nn(w1)
其中n(w1)n(w_1)n(w1)为语料库中以w1w_1w1作为==第一个词==的文本的数量,nnn为语料库中文本的总数量。
此处需要再斟酌,按照书上所说,n(w1)n(w_1)n(w1)表示分词后的w1w_1w1出现的次数
类似的,给定w1w_1w1情况下,w2w_2w2的条件概率可以计算为:
P^(w2∣w1)=n(w1,w2)n(w1) \hat P(w_2 \mid w_1) = \frac{n(w_1, w_2)}{n(w_1)} P^(w2∣w1)=n(w1)n(w1,w2)
其中n(w1,w2)n(w_1, w_2)n(w1,w2)为语料库中以w1w_1w1作为第一个词,w2w_2w2作为第二个词的文本的数量。
2. 采样方式
随机采样:与mini_batch类似,对整个序列shuffle后,每次训练从训练集indices中选取batch_size个样本,组成1个batch进行训练。
相邻采样:其原理如下图所示,下图中batch_size为3,可见整个序列被拆分为3份,每次iteration取3份中相同位置的样本,然后组合为1个batch进行训练。

RNN基础
下图展示了如何基于循环神经网络实现语言模型。我们的目的是基于当前的输入与过去的输入序列,预测序列的下一个字符。循环神经网络引入一个隐藏变量HHH,用HtH_{t}Ht表示HHH在时间步ttt的值。HtH_{t}Ht的计算基于XtX_{t}Xt和Ht−1H_{t-1}Ht−1,可以认为HtH_{t}Ht记录了到当前字符为止的序列信息,利用HtH_{t}Ht对序列的下一个字符进行预测。

1. 循环神经网络的构造
我们先看循环神经网络的具体构造。假设Xt∈Rn×d\boldsymbol{X}_t \in \mathbb{R}^{n \times d}Xt∈Rn×d是时间步ttt的小批量输入,Ht∈Rn×h\boldsymbol{H}_t \in \mathbb{R}^{n \times h}Ht∈Rn×h是该时间步的隐藏变量,则:
Ht=ϕ(XtWxh+Ht−1Whh+bh) \boldsymbol{H}_t = \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hh} + \boldsymbol{b}_h) Ht=ϕ(XtWxh+Ht−1Whh+bh)
其中,Wxh∈Rd×h\boldsymbol{W}_{xh} \in \mathbb{R}^{d \times h}Wxh∈Rd×h,Whh∈Rh×h\boldsymbol{W}_{hh} \in \mathbb{R}^{h \times h}Whh∈Rh×h,bh∈R1×h\boldsymbol{b}_{h} \in \mathbb{R}^{1 \times h}bh∈R1×h,ϕ\phiϕ函数是非线性激活函数。由于引入了Ht−1Whh\boldsymbol{H}_{t-1} \boldsymbol{W}_{hh}Ht−1Whh,HtH_{t}Ht能够捕捉截至当前时间步的序列的历史信息,就像是神经网络当前时间步的状态或记忆一样。由于HtH_{t}Ht的计算基于Ht−1H_{t-1}Ht−1,上式的计算是循环的,使用循环计算的网络即循环神经网络(recurrent neural network)。
在时间步ttt,输出层的输出为:
Ot=HtWhq+bq. \boldsymbol{O}_t = \boldsymbol{H}_t \boldsymbol{W}_{hq} + \boldsymbol{b}_q. Ot=HtWhq+bq.
其中Whq∈Rh×q\boldsymbol{W}_{hq} \in \mathbb{R}^{h \times q}Whq∈Rh×q,bq∈R1×q\boldsymbol{b}_q \in \mathbb{R}^{1 \times q}bq∈R1×q。
2. 总结
在代码实现中,H1\boldsymbol{H}_1H1 前面往往还会输入一个H0\boldsymbol{H}_0H0,一方面保证程序和公式的通用性,另一方面在遇到相邻采样时,可以利用到前面相邻样本的先验信息。
如果采取随机采样策略,训练每1个batch时,会将H0\boldsymbol{H}_0H0 初始化为0。
如果采取相邻采样策略,训练第i个batch时,有H0i=Hti−1\boldsymbol{H}^i_0=\boldsymbol{H}^{i-1}_tH0i=Hti−1 其中t=num_stepst=num\_stepst=num_steps。其代码如下:
for X, Y in data_iter:
if is_random_iter: # 如使用随机采样,在每个小批量更新前初始化隐藏状态
state = init_rnn_state(batch_size, num_hiddens, device)
else: # 否则需要使用detach函数从计算图分离隐藏状态
for s in state:
s.detach_()
此处需要注意.detach_()的用法,在昨天的笔记中对.data()和.detach()进行了区分,.detach_()的功能和.detach()基本一致,唯一的区别在于.detach_()是一个in-place operation,即在代码中可以这样进行等效:
# 下方代码等效于for s in state: s.detach_()
for i, s in enumerate(state):
state[i] = s.detach()
在代码中,s是上一个batch训练完得到的HtH_{t}Ht,存储着上一个batch的历史信息,且在计算图中是与上一个batch的计算变量相关联的,Ht=ϕ(XtWxh+Ht−1Whh+bh){H}_t = \phi({X}_t {W}_{xh} + {H}_{t-1} {W}_{hh} + {b}_h)Ht=ϕ(XtWxh+Ht−1Whh+bh),此处需要调用.detach_()改变计算图,将上个batch的Ht{H}_tHt也就是这个batch的H0H_0H0搞成一个叶子结点。
1459

被折叠的 条评论
为什么被折叠?



