这里对《ElitesAI·动手学深度学习PyTorch版》Task2的学习做个简短的总结。
Task2中总共分为3个部分:文本预处理、语言模型、RNN基础。
主要学习了分词,n-gram,随机采样与相邻采样还有基础的RNN知识。
文本预处理
1. 关于re正则
推荐学习链接:https://blog.csdn.net/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语言模型
假设序列 w 1 , w 2 , … , w T w_1, w_2, \ldots, w_T w1,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 ( w 1 , w 2 , w 3 , w 4 ) = P ( w 1 ) P ( w 2 ∣ w 1 ) P ( w 3 ∣ w 1 , w 2 ) P ( w 4 ∣ w 1 , w 2 , 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_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).
语言模型的参数就是词的概率以及给定前几个词情况下的条件概率。设训练数据集为一个大型文本语料库,如维基百科的所有条目,词的概率可以通过该词在训练数据集中的相对词频来计算,例如, w 1 w_1 w1的概率可以计算为:
P ^ ( w 1 ) = n ( w 1 ) n \hat P(w_1) = \frac{n(w_1)}{n} P^(w1)=nn(w1)
其中 n ( w 1 ) n(w_1) n(w1)为语料库中以 w 1 w_1 w1作为==第一个词==的文本的数量, n n n为语料库中文本的总数量。
此处需要再斟酌,按照书上所说, n ( w 1 ) n(w_1) n(w1)表示分词后的 w 1 w_1 w1出现的次数
类似的,给定 w 1 w_1 w1情况下, w 2 w_2 w2的条件概率可以计算为:
P ^ ( w 2 ∣ w 1 ) = n ( w 1 , w 2 ) n ( w 1 ) \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 ( w 1 , w 2 ) n(w_1, w_2) n(w1,w2)为语料库中以 w 1 w_1 w1作为第一个词, w 2 w_2 w2作为第二个词的文本的数量。
2. 采样方式
随机采样:与mini_batch类似,对整个序列shuffle后,每次训练从训练集indices中选取batch_size个样本,组成1个batch进行训练。
相邻采样:其原理如下图所示,下图中batch_size为3,可见整个序列被拆分为3份,每次iteration取3份中相同位置的样本,然后组合为1个batch进行训练。
RNN基础
下图展示了如何基于循环神经网络实现语言模型。我们的目的是基于当前的输入与过去的输入序列,预测序列的下一个字符。循环神经网络引入一个隐藏变量
H
H
H,用
H
t
H_{t}
Ht表示
H
H
H在时间步
t
t
t的值。
H
t
H_{t}
Ht的计算基于
X
t
X_{t}
Xt和
H
t
−
1
H_{t-1}
Ht−1,可以认为
H
t
H_{t}
Ht记录了到当前字符为止的序列信息,利用
H
t
H_{t}
Ht对序列的下一个字符进行预测。
1. 循环神经网络的构造
我们先看循环神经网络的具体构造。假设 X t ∈ R n × d \boldsymbol{X}_t \in \mathbb{R}^{n \times d} Xt∈Rn×d是时间步 t t t的小批量输入, H t ∈ R n × h \boldsymbol{H}_t \in \mathbb{R}^{n \times h} Ht∈Rn×h是该时间步的隐藏变量,则:
H t = ϕ ( X t W x h + H t − 1 W h h + b h ) \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)
其中, W x h ∈ R d × h \boldsymbol{W}_{xh} \in \mathbb{R}^{d \times h} Wxh∈Rd×h, W h h ∈ R h × h \boldsymbol{W}_{hh} \in \mathbb{R}^{h \times h} Whh∈Rh×h, b h ∈ R 1 × h \boldsymbol{b}_{h} \in \mathbb{R}^{1 \times h} bh∈R1×h, ϕ \phi ϕ函数是非线性激活函数。由于引入了 H t − 1 W h h \boldsymbol{H}_{t-1} \boldsymbol{W}_{hh} Ht−1Whh, H t H_{t} Ht能够捕捉截至当前时间步的序列的历史信息,就像是神经网络当前时间步的状态或记忆一样。由于 H t H_{t} Ht的计算基于 H t − 1 H_{t-1} Ht−1,上式的计算是循环的,使用循环计算的网络即循环神经网络(recurrent neural network)。
在时间步 t t t,输出层的输出为:
O t = H t W h q + b q . \boldsymbol{O}_t = \boldsymbol{H}_t \boldsymbol{W}_{hq} + \boldsymbol{b}_q. Ot=HtWhq+bq.
其中 W h q ∈ R h × q \boldsymbol{W}_{hq} \in \mathbb{R}^{h \times q} Whq∈Rh×q, b q ∈ R 1 × q \boldsymbol{b}_q \in \mathbb{R}^{1 \times q} bq∈R1×q。
2. 总结
在代码实现中, H 1 \boldsymbol{H}_1 H1 前面往往还会输入一个 H 0 \boldsymbol{H}_0 H0,一方面保证程序和公式的通用性,另一方面在遇到相邻采样时,可以利用到前面相邻样本的先验信息。
如果采取随机采样策略,训练每1个batch时,会将 H 0 \boldsymbol{H}_0 H0 初始化为0。
如果采取相邻采样策略,训练第i个batch时,有 H 0 i = H t i − 1 \boldsymbol{H}^i_0=\boldsymbol{H}^{i-1}_t H0i=Hti−1 其中 t = n u m _ s t e p s t=num\_steps t=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训练完得到的 H t H_{t} Ht,存储着上一个batch的历史信息,且在计算图中是与上一个batch的计算变量相关联的, H t = ϕ ( X t W x h + H t − 1 W h h + b h ) {H}_t = \phi({X}_t {W}_{xh} + {H}_{t-1} {W}_{hh} + {b}_h) Ht=ϕ(XtWxh+Ht−1Whh+bh),此处需要调用.detach_()改变计算图,将上个batch的 H t {H}_t Ht也就是这个batch的 H 0 H_0 H0搞成一个叶子结点。