lecture 9 Transformers
Transformer是对自然语言处理研究领域的一场革新,几乎目前NLP中所有的先进模型都离不开Transformer。典中典的Attention Is All You Need,很多人都有写过Transformer的原理解析,这里不赘述。
lecture 10 更多关于Transformers的内容以及预训练
本次作业不确定性较大,因为缺少计算资源无法完全跑通所有代码。
- ( a ) (a)(a ) 提示:参考[slides]中注意力机制的相关内容。
- ( 1 ) (1)(1 ) 作业中式( 1 ) (1)(1 )已经写得很明白了,这是一个模糊查询,我们不能直接通过查询向量q q q精确匹配到某个键向量k k k,只能赋予每个键一定的概率分布权重(即α i j \alpha_{ij}αi j ),得到最终的输出结果。
- ( 2 ) (2)(2 ) 根据式( 2 ) (2)(2 )的计算方法,如果查询向量q q q与某个键k i k_i k i 的相似度非常高(点积值很大),且q q q与其他的键基本垂直(点积值为零),那么就会使得α i \alpha_i αi 极大。
- ( 3 ) (3)(3 ) 此时c c c基本近似等于v i v_i v i
- ( 4 ) (4)(4 ) 直觉上就是单词的表示越相近,注意力权重就会越高,得到的注意力输出就越接近那个单词。(感觉在把一句废话换着方式说了好几遍)
- ( b ) (b)(b ) 只考虑两个值向量的特殊情况,探究注意力机制的深层含义。
- ( 1 ) (1)(1 ) 有人可能会觉得如果只是将值向量根据注意力得分取加权和,很难从这个结果中挖掘原先值向量的信息,事实上不然,但是这里做了一个非常强的假定,即两个值向量v a , v b v_a,v_b v a ,v b 是来自相互垂直的向量空间的:
v a ∈ span { a 1 , a 2 , . . . , a m } ⇒ v a = ∑ i = 1 m c i a i v b ∈ span { b 1 , b 2 , . . . , b p } ⇒ v b = ∑ j = 1 p d i b i where { a i ⊤ b j = 0 ∀ i = 1 , . . . , m ; ∀ j = 1 , . . . , p a i ⊤ a j = 0 ∀ i = 1 , . . . , m b i ⊤ b j = 0 ∀ j = 1 , . . . , p (a5.1.1) v_a\in\text{span}{a_1,a_2,…,a_m}\Rightarrow v_a=\sum_{i=1}^mc_ia_i\ v_b\in\text{span}{b_1,b_2,…,b_p}\Rightarrow v_b=\sum_{j=1}^pd_ib_i\ \text{where }\left{\begin{aligned} &a_i^\top b_j=0&&\forall i=1,…,m;\forall j=1,…,p\ &a_i^\top a_j=0&&\forall i=1,…,m\ &b_i^\top b_j=0&&\forall j=1,…,p \end{aligned}\right.\tag{a5.1.1}v a ∈span {a 1 ,a 2 ,…,a m }⇒v a =i =1 ∑m c i a i v b ∈span {b 1 ,b 2 ,…,b p }⇒v b =j =1 ∑p d i b i where ⎩⎪⎪⎨⎪⎪⎧a i ⊤b j =0 a i ⊤a j =0 b i ⊤b j =0 ∀i =1 ,…,m ;∀j =1 ,…,p ∀i =1 ,…,m ∀j =1 ,…,p (a 5 .1 .1 )
根据秩一矩阵的构造方法,假定M M M具有如下的形式:
M = ∑ i = 1 m λ i a i a i ⊤ (a5.1.2) M=\sum_{i=1}^m\lambda_ia_ia^\top_i\tag{a5.1.2}M =i =1 ∑m λi a i a i ⊤(a 5 .1 .2 )
其中λ i , i = 1 , . . . , m \lambda_i,i=1,…,m λi ,i =1 ,…,m是待定系数,则有如下推导:
M s = v a ⟺ M ( v a + v b ) = v a ⟺ ( ∑ i = 1 m λ i a i a i ⊤ ) ( ∑ i = 1 m c i a i + ∑ j = 1 p d i b i ) = ∑ i = 1 m c i a i ⟺ ∑ i = 1 m λ i c i a i a i ⊤ a i = ∑ i = 1 m c i a i (orthogonal property) ⟺ ∑ i = 1 m ( λ i c i a i ⊤ a i ) a i = ∑ i = 1 m c i a i ⟹ λ i c i a i ⊤ a i = c i ⟹ λ i = 1 a i ⊤ a i i = 1 , . . . , m (a5.1.3) \begin{aligned} Ms=v_a&\Longleftrightarrow M(v_a+v_b)=v_a\ &\Longleftrightarrow\left(\sum_{i=1}^m\lambda_ia_ia^\top_i\right)\left(\sum_{i=1}^mc_ia_i+\sum_{j=1}^pd_ib_i\right)=\sum_{i=1}^mc_ia_i\ &\Longleftrightarrow\sum_{i=1}^m\lambda_ic_ia_ia_i^\top a_i=\sum_{i=1}^mc_ia_i\quad\text{(orthogonal property)}\ &\Longleftrightarrow\sum_{i=1}^m(\lambda_ic_ia_i^\top a_i)a_i=\sum_{i=1}^mc_ia_i\ &\Longrightarrow\lambda_ic_ia_i^\top a_i=c_i\ &\Longrightarrow\lambda_i=\frac{1}{a_i^\top a_i}\quad i=1,…,m \end{aligned}\tag{a5.1.3}M s =v a ⟺M (v a +v b )=v a ⟺(i =1 ∑m λi a i a i ⊤)(i =1 ∑m c i a i +j =1 ∑p d i b i )=i =1 ∑m c i a i ⟺i =1 ∑m λi c i a i a i ⊤a i =i =1 ∑m c i a i (orthogonal property)⟺i =1 ∑m (λi c i a i ⊤a i )a i =i =1 ∑m c i a i ⟹λi c i a i ⊤a i =c i ⟹λi =a i ⊤a i 1 i =1 ,…,m (a 5 .1 .3 )
综上所述:
M = ∑ i = 1 m a i a i ⊤ a i ⊤ a i (a5.1.4) M=\sum_{i=1}^m\frac{a_ia_i^\top}{a_i^\top a_i}\tag{a5.1.4}M =i =1 ∑m a i ⊤a i a i a i ⊤(a 5 .1 .4 ) - 本质上就是找一个q q q使得k a ⊤ q = k b ⊤ q k_a^\top q=k_b^\top q k a ⊤q =k b ⊤q,则可知q ⊤ ( k a − k b ) = 0 q^\top (k_a-k_b)=0 q ⊤(k a −k b )=0,找一个与k a − k b k_a-k_b k a −k b 垂直的q q q就完事了(表达式应该怎么写呢?)。
- ( c ) (c)(c ) 探究单头注意力机制的缺陷:
- ( 1 ) (1)(1 ) 因为协方差矩阵很小,因此可以近似用μ i \mu_i μi 来替换k i k_i k i ,因此等价于找一个q q q与( μ a − μ b ) (\mu_a-\mu_b)(μa −μb )垂直即可。
- ( 2 ) (2)(2 ) 容易想到,如果存在一个明显很大的键向量k a k_a k a ,那么单头注意力机制得到的权重就没有什么意义了,因为加权和之后基本就还是指向k a k_a k a 的方向。
- ( d ) (d)(d ) 探究多头注意力机制的优势: 这里的意思是说,给两个查询向量q 1 q_1 q 1 和q 2 q_2 q 2 ,分别计算单头注意力得到权重c 1 c_1 c 1 和c 2 c_2 c 2 ,然后取c = ( c 1 + c 2 ) / 2 c=(c_1+c_2)/2 c =(c 1 +c 2 )/2作为最终结果即可。
- ( 1 ) (1)(1 ) 这个就没那么显然了,要求有下式的条件成立:
α 1 a + α 2 a = α 1 b + α 2 b ⟺ exp ( k a ⊤ q 1 ) exp ( k a ⊤ q 1 ) + exp ( k b ⊤ q 1 ) + exp ( k a ⊤ q 2 ) exp ( k a ⊤ q 2 ) + exp ( k b ⊤ q 2 ) = exp ( k b ⊤ q 1 ) exp ( k a ⊤ q 1 ) + exp ( k b ⊤ q 1 ) + exp ( k b ⊤ q 2 ) exp ( k a ⊤ q 2 ) + exp ( k b ⊤ q 2 ) ⟺ exp ( k a ⊤ q 1 ) − exp ( k b ⊤ q 1 ) exp ( k a ⊤ q 1 ) + exp ( k b ⊤ q 1 ) + exp ( k a ⊤ q 2 ) − exp ( k b ⊤ q 2 ) exp ( k a ⊤ q 2 ) + exp ( k b ⊤ q 2 ) = 0 ⟺ [ exp ( k a ⊤ ( q 1 + q 2 ) ) + exp ( k a ⊤ q 1 + k b ⊤ q 2 ) − exp ( k b ⊤ q 1 + k a ⊤ q 2 ) − exp ( k b ⊤ ( q 1 + q 2 ) ) ] + [ exp ( k a ⊤ ( q 1 + q 2 ) ) + exp ( k b ⊤ q 1 + k a ⊤ q 2 ) − exp ( k a ⊤ q 1 + k b ⊤ q 2 ) − exp ( k b ⊤ ( q 1 + q 2 ) ) ] = 0 ⟺ exp ( k a ⊤ ( q 1 + q 2 ) ) = exp ( k b ⊤ ( q 1 + q 2 ) ) ⟺ k a ⊤ ( q 1 + q 2 ) = k b ⊤ ( q 1 + q 2 ) ⟺ ( k a − k b ) ⊤ ( q 1 + q 2 ) = 0 (a5.1.5) \begin{aligned} &\alpha_{1}^a+\alpha_2^a=\alpha_1^b+\alpha_2^b\ \Longleftrightarrow&\frac{\exp(k_a^\top q_1)}{\exp(k_a^\top q_1)+\exp(k_b^\top q_1)}+\frac{\exp(k_a^\top q_2)}{\exp(k_a^\top q_2)+\exp(k_b^\top q_2)}=\frac{\exp(k_b^\top q_1)}{\exp(k_a^\top q_1)+\exp(k_b^\top q_1)}+\frac{\exp(k_b^\top q_2)}{\exp(k_a^\top q_2)+\exp(k_b^\top q_2)}\ \Longleftrightarrow&\frac{\exp(k_a^\top q_1)-\exp(k_b^\top q_1)}{\exp(k_a^\top q_1)+\exp(k_b^\top q_1)}+\frac{\exp(k_a^\top q_2)-\exp(k_b^\top q_2)}{\exp(k_a^\top q_2)+\exp(k_b^\top q_2)}=0\ \Longleftrightarrow&[\exp(k_a^\top(q_1+q_2))+\exp(k_a^\top q_1+k_b^\top q_2)-\exp(k_b^\top q_1+k_a^\top q_2)-\exp(k_b^\top(q_1+q_2))]\ &+[\exp(k_a^\top(q_1+q_2))+\exp(k_b^\top q_1+k_a^\top q_2)-\exp(k_a^\top q_1+k_b^\top q_2)-\exp(k_b^\top(q_1+q_2))]=0\ \Longleftrightarrow&\exp(k_a^\top(q_1+q_2))=\exp(k_b^\top(q_1+q_2))\ \Longleftrightarrow&k_a^\top(q_1+q_2)=k_b^\top(q_1+q_2)\ \Longleftrightarrow&(k_a-k_b)^\top(q_1+q_2)=0 \end{aligned}\tag{a5.1.5}⟺⟺⟺⟺⟺⟺α1 a +α2 a =α1 b +α2 b exp (k a ⊤q 1 )+exp (k b ⊤q 1 )exp (k a ⊤q 1 )+exp (k a ⊤q 2 )+exp (k b ⊤q 2 )exp (k a ⊤q 2 )=exp (k a ⊤q 1 )+exp (k b ⊤q 1 )exp (k b ⊤q 1 )+exp (k a ⊤q 2 )+exp (k b ⊤q 2 )exp (k b ⊤q 2 )exp (k a ⊤q 1 )+exp (k b ⊤q 1 )exp (k a ⊤q 1 )−exp (k b ⊤q 1 )+exp (k a ⊤q 2 )+exp (k b ⊤q 2 )exp (k a ⊤q 2 )−exp (k b ⊤q 2 )=0 [exp (k a ⊤(q 1 +q 2 ))+exp (k a ⊤q 1 +k b ⊤q 2 )−exp (k b ⊤q 1 +k a ⊤q 2 )−exp (k b ⊤(q 1 +q 2 ))]+[exp (k a ⊤(q 1 +q 2 ))+exp (k b ⊤q 1 +k a ⊤q 2 )−exp (k a ⊤q 1 +k b ⊤q 2 )−exp (k b ⊤(q 1 +q 2 ))]=0 exp (k a ⊤(q 1 +q 2 ))=exp (k b ⊤(q 1 +q 2 ))k a ⊤(q 1 +q 2 )=k b ⊤(q 1 +q 2 )(k a −k b )⊤(q 1 +q 2 )=0 (a 5 .1 .5 )
刚好消掉了交叉项,那么结论就是找到q 1 , q 2 q_1,q_2 q 1 ,q 2 使得它们的和与k a − k b k_a-k_b k a −k b 垂直,这里用μ a \mu_a μa 和μ 2 \mu_2 μ2 近似,就是跟μ a − μ b \mu_a-\mu_b μa −μb 垂直。 - ( 2 ) (2)(2 ) 实话说没怎么搞明白是什么意思,虽然增加了协方差,但是μ a − μ b \mu_a-\mu_b μa −μb 依然可以近似表示k a − k b k_a-k_b k a −k b ,而且理论上偏差值比没有协方差的情况要小一些(因为协方差都是正数,所以相减相当于抵消了一些偏差)。 我觉得可能就是想说在多头注意力的情况下,可以缓解( c . 2 ) (c.2)(c .2 )的问题,因为对输出的注意力权重进行了均衡。
本次代码实验是GPT \text{GPT}GPT模型的预训练和微调,GPT \text{GPT}GPT模型定义的代码已经完全写好了,要完成的只是数据处理、注意力机制定义、运行与报告部分的代码。
注意代码里有不少读取文件的默认代码可能出错,需要设置文件编码类型。
实话说这个任务有点离谱,居然是根据人名预测出生地,虽说的确不同地区的人名是可以做一些区分,但未免也太牵强了。
本题的代码借鉴自GitHub@Mr-maoge的解法,需要至少8 G 8\text{G}8 G以上的显存才能跑通,因为缺少计算资源无法跑通代码(经测试,可以调小batch size \text{batch size}batch size使得在低显存耗用的情况下通过代码测试,但是无法获得正确的结果)。
虽然代码很难跑通得到结果,但是其中的GPT \text{GPT}GPT模型代码以及两种注意力机制的实现代码是值得学习的。
- ( a ) (a)(a ) 阅读
play_char.ipynb
,看代码说明里应该还有play_math.ipynb
,play_image.ipynb
,play_word.ipynb
,有谁知道几个在哪儿可以找到,到时候踢我一下。 - ( b ) (b)(b ) 运行
python src/dataset.py namedata
得到以下输出:
data has 418352 characters, 256 unique.
x: Where was Khatchig Mouradian born?⁇Lebanon⁇□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
y: □□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□⁇Lebanon⁇□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
x: Where was Jacob Henry Studer born?⁇Columbus⁇□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
y: □□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□⁇Columbus⁇□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
x: Where was John Stephen born?⁇Glasgow⁇□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
y: □□□□□□□□□□□□□□□□□□□□□□□□□□□⁇Glasgow⁇□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
x: Where was Georgina Willis born?⁇Australia⁇□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
y: □□□□□□□□□□□□□□□□□□□□□□□□□□□□□□⁇Australia⁇□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
双问号表示 MASK_CHAR
,正方形表示 PAD_CHAR
。
* ( c ) (c)(c ) 编写 run.py
中相关代码块,注意如果出现 trainer.py
中有 pipeline
的报错信息,将 num_workers
取0 0 0来避免。(从这边往下PC \text{PC}PC机就跑不通了)
* ( d ) (d)(d ) 运行下面的脚本:
python src/run.py finetune vanilla wiki.txt --writing_params_path vanilla.model.params --finetune_corpus_path birth_places_train.tsv
python src/run.py evaluate vanilla wiki.txt --reading_params_path vanilla.model.params --eval_corpus_path birth_dev.tsv --outputs_path vanilla.nopretrain.dev.predictions
python src/run.py evaluate vanilla wiki.txt --reading_params_path vanilla.model.params --eval_corpus_path birth_test_inputs.tsv --outputs_path vanilla.nopretrain.test.predictions
- ( e ) (e)(e ) 运行
python src/dataset.py charcorruption
- ( f ) (f)(f ) 运行下面的脚本:
python src/run.py pretrain vanilla wiki.txt --writing_params_path vanilla.pretrain.params
python src/run.py finetune vanilla wiki.txt --reading_params_path vanilla.pretrain.params --writing_params_path vanilla.finetune.params --finetune_corpus_path birth_places_train.tsv
python src/run.py evaluate vanilla wiki.txt --reading_params_path vanilla.finetune.params --eval_corpus_path birth_dev.tsv --outputs_path vanilla.pretrain.dev.predictions
python src/run.py evaluate vanilla wiki.txt --reading_params_path vanilla.finetune.params --eval_corpus_path birth_test_inputs.tsv --outputs_path vanilla.pretrain.test.predictions
- ( g ) (g)(g ) 运行下面的脚本:
python src/run.py pretrain synthesizer wiki.txt --writing_params_path synthesizer.pretrain.params
python src/run.py finetune synthesizer wiki.txt --reading_params_path synthesizer.pretrain.params --writing_params_path synthesizer.finetune.params --finetune_corpus_path birth_places_train.tsv
python src/run.py evaluate synthesizer wiki.txt --reading_params_path synthesizer.finetune.params --eval_corpus_path birth_dev.tsv --outputs_path synthesizer.pretrain.dev.predictions
python src/run.py evaluate synthesizer wiki.txt --reading_params_path synthesizer.finetune.params --eval_corpus_path birth_test_inputs.tsv --outputs_path synthesizer.pretrain.test.predictions
记录一下synthesizer \text{synthesizer}synthesizer注意力(提出论文)的原理:
– 设X ∈ R l × d X\in\R^{l\times d}X ∈R l ×d,其中l l l的块大小(序列长度),d d d是词向量温度,d / h d/h d /h是每个注意力头的维度,Q , K , V ∈ R d × d / h Q,K,V\in\R^{d\times d/h}Q ,K ,V ∈R d ×d /h跟自注意力中的三个矩阵一样,则自注意力头的输出为:
Y i = softmax ( ( X Q i ) ( X K i ) ⊤ d / h ) ( X V i ) ∈ R l × d / h (a5.2.1) Y_i=\text{softmax}\left(\frac{(XQ_i)(XK_i)^\top}{\sqrt{d/h}}\right)(XV_i)\in\R^{l\times d/h}\tag{a5.2.1}Y i =softmax (d /h (X Q i )(X K i )⊤)(X V i )∈R l ×d /h (a 5 .2 .1 )
接着将各个自注意力头拼接起来:
Y = [ Y 1 ; . . . ; Y h ] A ∈ R l × d (a5.2.2) Y=[Y_1;…;Y_h]A\in\R^{l\times d}\tag{a5.2.2}Y =[Y 1 ;…;Y h ]A ∈R l ×d (a 5 .2 .2 )
– 本题实现的是上面的一个变体:
Y i = softmax ( ReLU ( X A i + b 1 ) B i + b 2 ) ( X V i ) (a5.2.3) Y_i=\text{softmax}(\text{ReLU}(XA_i+b_1)B_i+b_2)(XV_i)\tag{a5.2.3}Y i =softmax (ReLU (X A i +b 1 )B i +b 2 )(X V i )(a 5 .2 .3 )
其中A i ∈ R d × d / h , B ∈ R d / h × l , V i ∈ R d × d / h A_i\in\R^{d\times d/h},B\in\R^{d/h\times l},V_i\in\R^{d\times d/h}A i ∈R d ×d /h ,B ∈R d /h ×l ,V i ∈R d ×d /h 可以作这样的解释: ① ( X Q i ) ( X K i ) ⊤ ∈ R l × l (XQ_i)(XK_i)^\top\in\R^{l\times l}(X Q i )(X K i )⊤∈R l ×l是注意力得分; ② synthesizer \text{synthesizer}synthesizer变体则避免计算所有成对的这种点积,而是直接通过将每个自注意力头的d d d维向量映射到l × l l\times l l ×l的注意力得分矩阵。
- ( a ) (a)(a ) 预训练模型结果比非预训练模型结果好不是理所当然的吗,硬要说就是首先找到了一个比较好的初始解开始迭代,因而可以收敛到更好地解。实际情况,不微调只有0.02 0.02 0 .0 2,微调了之后是0.22 0.22 0 .2 2
- ( b ) (b)(b ) 人无法辨别出机器到底是检索还是在瞎猜,这可能会使得机器的可解释性下降,无法用于实际应用。测试集中几乎所有人名都没有在训练集中出现过,但是只看姓氏或者名字的话还是有迹可循的,所以机器也并非完全是在瞎猜。
- ( c ) (c)(c ) 模型瞎猜肯定会导致应用的可信度下降呗,不是很能理解这种应用有啥用。
Original: https://blog.csdn.net/CY19980216/article/details/125072701
Author: 囚生CY
Title: CS224N WINTER 2022(五)Transformers详解(附Assignment5答案)
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/527779/
转载文章受原作者版权保护。转载请注明原作者出处!