【学习】Batch Normalization、seq2seq——transformer


一、Batch Normalization

Batch Normalization在影像处理的时候会带来很大的帮助。

1、changing landscape

为什么我们需要Batch Normalization呢?Batch Normalization是一个能把崎岖的error surface的“大山”铲平的方法。
在下图中,w1轴方向的斜率变化比较缓慢,而在w2方向的斜率变化相对比较快。这样就会引来一个问题:在w1轴和w2轴之间的斜率差距很大,如果我的learning rate是固定的,那我就很难得到很好的结果。所以我们需要adaptive的learning rate,或者需要用adam等进阶的optimization的方法才能够得到很好的结果。现在我们需要从另一个方向想,我们能不能把这个难做的error surface改掉,而不是用更好optimization的方法去解决我们的问题。如果可以的话,我们的第一个问题就是,w1和w2之间斜率差距很大,到底是为什么呢?
在这里插入图片描述

2、为什么error surface崎岖?

以下我们有一个非常简单的模型,输入是x1和x2,分别乘以权重w1和w2之后,加上偏置b求和得到y,计算y和label_y之间的 差距e,然后把所有的数据的的e加起来,得到L。当我们的w1有一个小小变化的时候,这时候我们会分别改变y、e、L,所以当w1改变就会影响L。那什么时候我们的w1改变很小的时候会对L的影响也比较小?我们的输出(x1)很小(比如x1=1、2)的时候。如果我们的x2变化很大(2000)的时候,改变w2(这个跟x1的例子变化类似啊),我们最后的L就会变化很大。这两个x1和x2的变化很大是很有可能的,因为我们也不可能限制我们的数据在一个很一致的数值范围内,所以产生error surface崎岖的情况。那么我们应该怎么做呢?我们应该怎么让不同的feature里面不同的维度有同样的数值的范围呢(如下图)?如果我们有办法解决这个问题的话,我们就可能让我们的训练变的容易点。
在这里插入图片描述

3、feature normalization

此方法有很多,我们可以用来解决上面的问题。我们有以下的所有训练资料的feature vector,每一个xi vector都有很多个维度,对于每一个维度的不同feature vector的数值里面取出来(如图的绿色方框)求平均(mean mi),同时我们也计算出这一列数据的标准差standard deviation:Oi。
在这里插入图片描述
接下来我们就可以做一中normalization,称为standardization。我们应该怎么做呢?

方法

对于每一个绿色方框内的数据,我们都单独取一个出来,计算这个数据减去均值mi再除标准差Oi的值,然后把这个值放回去。做完这个normalization之后,这个维度(绿色方框内)的总和为0,所以mean也是0,他们的variance差异都是1,变化后的数据就会在0上下波动。那么我们就可能会做一个error surface好一点,在做gradient decent的时候快一点,训练更顺利一点。这个是feature normalization。
在这里插入图片描述

4、在深度学习里面

我们同样在之前作业用到feature normalization,如下图。我们在对数据进行特征标准化之后,经过一层layer W1得到z1,然后通过激活函数sigmoid得到a1,然后再经过下一层。但是我们反过来想想,我们不同的z和a也是不同的输入和输出,那么我们在输入第二层的时候,我们的数据可能变化很大。对于第二层的来说,z或者a都是一些feature vector,我们应该也对这些进行标准化。那么我们是对a还是对z做标准化呢?实际上都是差不多的。如果我们选用sigmoid的激活函数,推荐对z做标准化。因为我们的sigmoid激活函数在0附近的斜率很大,那么我们在对z做标准化,把z都做成0附近的数值,那么我们经过sigmoid之后的数据就会非常大。
在这里插入图片描述
在这里我们考虑对z做特征标准化。把所有的z取出来算mean(u),然后再算标准差(o),以下是公式。我们假设只有三个数据!!
在这里插入图片描述
然后我们经过类似上面的变化得到新的z^。
在这里插入图片描述
这边有一个有趣的事情,就是我们的z1会影响很多数据。比如说,z1改变的时候,z~ 1 、a1、z~ 2、a2、z~ 3、a3都会改变。我们之前输入进w1的x~ 1,x~ 2和x~3是独立处理的,但是这里会变成一个蝴蝶效应。现在我们可以把这个模型看成一个大的网络,输入多个数据,产生多个输出。在这里插入图片描述
当然我们不可能把整个数据集丢进去算,这样太大了。那么我们怎么做呢?这里我们可以只考虑一个batch里面的数据。这个方法叫做batch normalization,这个方法也应该适用于那些batch size比较大的情况。这里我们就把对整个数据的处理变成对每个batch做处理的应用模拟。在这里我们一般用这种方法:
对于每个改变的z~ ,我们把每个数据的z~ 乘γ再加上β,下面的矩阵乘法不再赘述。我们把γ和β看成模型的参数,是应该被机器学习出来的。我们在做标准化的时候,z~的平均都是0。但是我们怕这种平均会带来什么负面的影响,所以需要加上这两个参数来计算,让他们的平均可以不为0也可以为0。如果不为0,那他就需要自己去学习β和γ来调整z^ 。在初始化时,β会设置为1,γ设置为0.我们会让刚开始的数据比较接近,然后在之后的训练再加入β和γ。
在这里插入图片描述

5、test(inference)

我们在测试的时候会有点问题:如果我们需要在线的训练的时候,不想要一个batch size的数据都进来的时候才做运算,我们应该对每个数据都进行一次运算。那我们怎么算它的均值和标准差呢?pytorch在训练的时候,每一个batch都计算出来的均值和标准差都会拿出来算moving average。我们把每次算出的均值求个平均u_,乘上一个超参数p,然后加上(1-p)乘每个u,得到的值作为更新参数。那么我们在做训练的时候就用这种更新的数值来取代上面的均值和标准差。

在这里插入图片描述
这个是原始的batch normalization上的方法比较。横轴是训练过程,纵轴是验证集上的准确度。可以看出在没有用BN(黑色虚线)的时候在达到高精度的时候比较慢,但是红色的虚线(用了BN)只用了一半的时间就跑到同样的准确度了。其他的曲线,紫色的线,就算用难搞的sigmoid很快,但是要达到高精度还是不行。而用了五倍learn rate的蓝色虚线会比用了30倍learning rate的蓝色实线会好。error surface平滑可以让learning rate大一点。
在这里插入图片描述

6、internal convariate shift

在我们更新了A为A’,更新B为B’,但是我们发现了一个问题,我们的a的输入是A,a’的输入A’,但是我们这样直接更新B,B’和a’没有关系呀。updata也许不适合用在a’上。但是我们坐标准化的时候会让a和a’比较接近,就可能会对我们的训练有帮助。但是下面的论文跟这个说法相悖。在这里插入图片描述

在这里插入图片描述
除了这个方法之外,我们还有别的方法。

二、transformer(sequence-to-sequence (seq2seq) )

transformer是什么呢?transformer就是一个sequence-to-sequence (seq2seq)的模型。seq2seq模型的输入是一个sequence,输出长度由模型自己决定。
在这里插入图片描述
如果要做一个台语翻译,我们可以不考虑背景音乐和噪声,也不用考虑嘈杂的转录等等,就直接把语音放到模型里面训练。
在这里插入图片描述
这个是可以实现的,如以下例子:
在这里插入图片描述
但是对于倒装或者有些难以听懂的就很难识别出来了。
text-to-speech synthesis:
语音识别:输入语音输出文字
语音合成:输入文字输出语音

除了语音合成领域,seq2seq也可以用在NLP。NLP中经常需要seq2seq应用在QA问题中。虽然seq2seq可以用在很多任务上,但是我们如果用特制的模型能做的更好。

虽然很多任务都不认为可以用seq2seq解决,但是我们是可以用seq2seq模型的。比如说语法解析:我们要做的就是输入一个句子,找到这个句子的语法解析树。虽然是树状结构,但是我们也可以用sequence来表示。
在这里插入图片描述
以下的论文就是上面的例子。
在这里插入图片描述
multi-class classification这个方法是:我们不止一个class,机器要从多个class里面选择一个class出来。
multi-label classification这个方法是:同一个东西可以属于多个class。比如下面,一个文章可以属于多个class。
在这里插入图片描述
如果我们用classifier限制threshold的时候直接输出分数最好的前三名,我们可能不会得到最好的结果。那怎么办呢?seq2seq。
在object detection领域,我们要想给机器一张图片,让他识别出图片的物体,也可以用seq2seq模型来做。
在这里插入图片描述

1、seq2seq模型

一般的seq2seq会分成两块,分别是encoder和decoder。输入一个sequence,由encoder负责处理,然后把结果传到decoder里面,由decoder决定输出什么样的sequence。
在这里插入图片描述
seq2seq模型有一个很流行的模型transformer:
在这里插入图片描述

(1)encoder

encoder要做的事情就是给一排向量输出另外一排向量。这种事情很多模型也能做到,比如rnn或者cnn。
在这里插入图片描述
在transformer里面的encoder就是self-attention。
在这里插入图片描述
现在的encoder里面会分成很多的block。每个block都是输入一排向量再输出另外一排向量。其实每个block并不是神经网络的一层,每个block在做的事情是好几个layer在做的事情。在transformer的encoder里面的每个block做的事情其实是,输入一排vector到self-attention输出另一排向量,然后把输出放入FC里面再输出另外一排vector,这个输出就是block的输出。在transformer里面会更复杂一点,我们输入一排向量到self-attention里面,考虑了所有的输入数据,输出一排向量。此时我们还没结束,我们还要把原来的输入加上,得到新的输出,这个结构称为residual connection。residual connection广泛应用于深度学习领域。得到residual的结果之后我们需要再进行normalization,但是不是batch normalization,而是 layer normalization。layer normalization做的事情比batch normalization更简单。在layer normalization里面,我们输入一个向量,输出另外一个向量,不用考虑batch的资讯。layer normalization做的事情是,将输入计算mean m和标准差O。这里要注意,我们在做batch normalization的时候是对不同feature的同一个维度计算均值和标准差,但是layer normalization是对同一个feature里面不同的维度计算均值和标准差。
在这里插入图片描述
得到了norm的输出x’之后,才是FC的输入。在FC里面,也有residual connection的结构。在FC做完normalization之后的输出,才是一个block的输出。
在这里插入图片描述
在输入的时候,还可以加上positional encoding。我们如果只用self-attention,我们没有位置的资讯,所以需要加上positional encoding。以下的multi-head attention就是self -attention 的block。而上面的add&norm就是residual +layer normalization。feed forward就是FC。
在这里插入图片描述
而著名的模型bert就是transformer的encoder。
其实上面原始的结构也不是说一定是最好的,还可以是以下的变形,也能得到更好的结果:
在这里插入图片描述

(2)decoder

autoregressive(AT)

decoder的一种常见的方法是autoregressive(AT)。decoder要把encoder的输出先输入进去,如果是语音识别的系统,那decoder怎么产生一段文字呢?首先给他一个特殊的符号,这个符号代表开始(begin of sentence =BOS)。begin可以用1-hot vector表示(由1和0构成)。decoder经过softmax之后会输出一个vector(向量distribution所有的可能分数加起来是1,分数最高的一个就是输出最有可能的那个字),这个vector的长度跟我们的vocabulary的长度是一样的。假设我们输出的是中文,那我们的vocabulary就是我们需要输出中文的数字(常用中文数)。在这里,我们输入begin的时候,会输出机;输入机的时候,会输出器;输入器的时候,会输出学;……在encoder里面的输出,之后会考虑。这里有个关键的:decoder的输入,其实是前一个时间点自己的输出,这样有可能会看错!这样会不会造成连锁的错误呢?有可能。这里先无视这个问题。

在这里插入图片描述

decoder结构

decoder的结构更复杂了!但是其实除了中间的一块,encoder跟decoder是差不多的!输出是不一样的decoder会在最后加一个softmax使得输出成为一个几率。在multi-head attention这个结构里面还多一个masked。
在这里插入图片描述
原始的self-attention是这样的
在这里插入图片描述
但是加上masked 之后的masked self-attention是这样的:我们在产生输出的时候不能考虑右边的东西。产生b1的时候,不能考虑a2、a3、a4;产生b2的时候,不能考虑b3、b4;产生b3的时候,不能考虑a4.
在这里插入图片描述
输出b2的时候,我们不能涉及a3和a4:

在这里插入图片描述
对于decoder来说,输入相当于是一个个顺序加进去的,所以只能考虑左边的东西,不是一下子加进去的,跟encoder有所区别!
decoder必须自己决定输出序列的长度。如果decoder不能决定输出序列的长度的话,可能decoder就一直输出下去,比较decoder是用前面输出的内容作为输入的嘛!所以我们还需要一个字符表示结束,这里可以用END(也可以跟begin的符号是一样的)。在decoder输出机器学习(我们输入encoder的输入是机器学习)之后,我们要知道输出end。
在这里插入图片描述

non-autoregressive(NAT)模型

AT和NAT的工作方式: AT首先输入begin,输出w1;输入w1,输出w2;...;输入w3,输出end。AT是一个个输入然后一个个输出的! NAT一次性把begin输入到四个通道里面,然后一次性输出四个输出(token)。相当于一个步骤!
在这里插入图片描述

那我们怎么知道NAT要输入几个begin呢?下面有几个方法:
(1)我们可以另外训练一个predictor去决定decoder的长度。这个predictor输入是encoder的输入,然后输出是一个数字(代表decoder应该输出的长度)。
(2)我们直接输入一个很长的begin,输出同样长度的token,然后看看哪里是end,end右边就忽视。

NAT的decoder有什么好处呢?平行化。AT的输出是一个个字产生的,那我们要输出多个字的时候就需要进行很多次的decoder。而NAT是一次decoder产生所有的字,所以NAT是比AT快的。NAT的decoder是self-attention之后产生的,也成为了一个热门的主题。NAT还有个好处就是比较能控制输出长度的。比如在语音识别里面,我们如果想要二倍速的话,就可以把classifier的输出除以2,那就可以加快速度了。虽然NAT有以上的好处,但是AT的performance更好。
在这里插入图片描述

encoder和decoder之间的联系

红色框是cross attention,它是连接encoder和decoder的桥梁。encoder提供左边两个蓝色箭头,decoder提供右边一个绿色箭头。decoder从左边的两个两个箭头可以读到encoder的输出。

在这里插入图片描述
把self-attention输出的向量乘上一个矩阵做transform得到q(query),encoder的a产生k,q和k们分别计算分数得到a’,也可以做softmax。然后把a’与v相乘相加得到v,v作为FC的输入进行后续处理。q来自decoder,v来自encoder。这个就是cross attention架构:
在这里插入图片描述
如下是cross attention的分数
在这里插入图片描述
当然我们也可以变换一下cross attention的模型,可以不用encoder的最后一层的输出作为输入到decoder
在这里插入图片描述

训练

在我们训练的模型里面,我们会有ground truth(1-hot vector)表示每个输入向量应该输出什么样的输出向量。在这里如果我们输入是机的向量,那么我们的ground truth里面的机的概率就是1.这时候我希望decoder输出的distribution跟ground truth越接近越好。所以我们会计算ground truth和distribution的minimize cross entropy,我们希望minimize cross entropy越小越好。这个跟分类很像!
在这里插入图片描述
所以对于我们的训练来说,对于每个输出向量,我们都希望输出跟ground truth的minimize cross entropy总和最小。但是不要忘了还有begin和end!我们会在训练的时候给机器看正确的答案,也就是作为输入,这个事情叫做teacher forcing。那么测试的时候,机器不能看见正确答案啊,那就会出现一些mismatch。在这里插入图片描述

tips

(1)copy mechanism

在刚才的讨论,我们要求decoder自己产生输出,也许decoder可以不用自己产生输出而是采用输入的东西。那我们怎么复制输入呢?比如下面的例子:
在这里插入图片描述
在这里插入图片描述
文章摘要:(需要百万篇文章!)很多词汇都是从文章中复制出来的!
在这里插入图片描述
那么seq2seq有没有这种具有复制能力的模型?pointer network、copying mechanism in sequence-tosequence。

guided attention

在处理短句子的时候会有点问题(missing an input character漏字)。怎么办呢?我们可以让机器把输入的每个东西都看过!这个方法叫guided attention。
我们的attention可能会出现下面第二层的问题(顺序混乱),一般我们读句子会从左往右读(像下面第一层)。
在这里插入图片描述

beam search

加上我们的只有两个字A和B,我们每次decoder都进行选择。每次都找分数最高的作为输出就是greedy decoding。
在这里插入图片描述
但是如果我们选了绿色的话,以后都是比较好的!
在这里插入图片描述
我们怎么找到绿色的路呢?总不能爆搜这个吧,太多啦!那么有一种方法就是beam search。
beam search有时候有用,有时候没用。为什么呢?因为beam search会对有目的的任务比较有效(答案很明确)。假如需要很多个可能的时候,beam search就没什么用。
TTS在测试的时候加入噪声居然会让结果更好!如果要产生好的声音就要加入可能性。

在这里插入图片描述

schedule sampling

如果没有mismatch的情况怎么办呢(exposure bias)?我们可以在decoder的时候加入一些错误,不用全给正确的答案。这种方法叫做schedule sampling。不过她会影响到平行化的能力。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是基于seq2seq的对二值图像进行折叠处理的Transformer代码,代码中注释详尽,希望对你有所帮助: ```python import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers # 定义Transformer结构中的Multi-Head Attention层 class MultiHeadAttention(layers.Layer): def __init__(self, embed_dim, num_heads): super(MultiHeadAttention, self).__init__() self.embed_dim = embed_dim self.num_heads = num_heads # 对输入的query、key、value进行线性变换,得到Q、K、V self.query_dense = layers.Dense(units=embed_dim) self.key_dense = layers.Dense(units=embed_dim) self.value_dense = layers.Dense(units=embed_dim) # 将多头注意力的输出进行线性变换,得到最终输出 self.dense = layers.Dense(units=embed_dim) def split_heads(self, inputs, batch_size): inputs = tf.reshape( inputs, shape=(batch_size, -1, self.num_heads, self.embed_dim // self.num_heads) ) return tf.transpose(inputs, perm=[0, 2, 1, 3]) def call(self, inputs): query, key, value, mask = inputs['query'], inputs['key'], inputs['value'], inputs['mask'] batch_size = tf.shape(query)[0] # 对query、key、value进行线性变换 query = self.query_dense(query) key = self.key_dense(key) value = self.value_dense(value) # 对query、key、value进行分割 query = self.split_heads(query, batch_size) key = self.split_heads(key, batch_size) value = self.split_heads(value, batch_size) # 计算注意力权重 scaled_attention_logits = tf.matmul(query, key, transpose_b=True) scaled_attention_logits += tf.cast((mask * -1e9), dtype=scaled_attention_logits.dtype) attention_weights = tf.nn.softmax(scaled_attention_logits, axis=-1) attention_output = tf.matmul(attention_weights, value) # 合并多头注意力的输出 attention_output = tf.transpose(attention_output, perm=[0, 2, 1, 3]) concat_attention = tf.reshape(attention_output, (batch_size, -1, self.embed_dim)) outputs = self.dense(concat_attention) return outputs # 定义Transformer结构中的Positional Encoding层 class PositionalEncoding(layers.Layer): def __init__(self, position, embed_dim): super(PositionalEncoding, self).__init__() self.position = position self.embed_dim = embed_dim self.pos_encoding = self.positional_encoding(position, embed_dim) def get_angles(self, pos, i, embed_dim): angle_rates = 1 / tf.pow(10000, (2 * (i // 2)) / tf.cast(embed_dim, tf.float32)) return pos * angle_rates def positional_encoding(self, position, embed_dim): angle_rads = self.get_angles( tf.range(position, dtype=tf.float32)[:, tf.newaxis], tf.range(embed_dim, dtype=tf.float32)[tf.newaxis, :], embed_dim, ) # 将sin应用于偶数索引(从0开始),将cos应用于奇数索引(从1开始) sines = tf.math.sin(angle_rads[:, 0::2]) cosines = tf.math.cos(angle_rads[:, 1::2]) pos_encoding = tf.concat([sines, cosines], axis=-1) pos_encoding = pos_encoding[tf.newaxis, ...] return tf.cast(pos_encoding, tf.float32) def call(self, inputs): return inputs + self.pos_encoding[:, :tf.shape(inputs)[1], :] # 定义Transformer结构中的Encoder层 def encoder_layer(units, d_model, num_heads, dropout, name="encoder_layer"): inputs = keras.Input(shape=(None, d_model), name="inputs") # 为了保证模型的学习能力,需要在输入层和输出层之间添加多头注意力层和前向传递层 attention = MultiHeadAttention(d_model, num_heads)(inputs={"query": inputs, "key": inputs, "value": inputs}) attention = layers.Dropout(rate=dropout)(attention) attention = layers.LayerNormalization(epsilon=1e-6)(inputs + attention) outputs = layers.Dense(units=units, activation="relu")(attention) outputs = layers.Dense(units=d_model)(outputs) outputs = layers.Dropout(rate=dropout)(outputs) outputs = layers.LayerNormalization(epsilon=1e-6)(attention + outputs) return keras.Model(inputs=inputs, outputs=outputs, name=name) # 定义Transformer结构中的Decoder层 def decoder_layer(units, d_model, num_heads, dropout, name="decoder_layer"): inputs = keras.Input(shape=(None, d_model), name="inputs") enc_outputs = keras.Input(shape=(None, d_model), name="encoder_outputs") # 为了保证模型的学习能力,需要在输入层和输出层之间添加多头注意力层和前向传递层,以及编码器-解码器注意力层 attention1 = MultiHeadAttention(d_model, num_heads)(inputs={"query": inputs, "key": inputs, "value": inputs}) attention1 = layers.Dropout(rate=dropout)(attention1) attention1 = layers.LayerNormalization(epsilon=1e-6)(attention1 + inputs) attention2 = MultiHeadAttention(d_model, num_heads)(inputs={"query": attention1, "key": enc_outputs, "value": enc_outputs}) attention2 = layers.Dropout(rate=dropout)(attention2) attention2 = layers.LayerNormalization(epsilon=1e-6)(attention2 + attention1) outputs = layers.Dense(units=units, activation="relu")(attention2) outputs = layers.Dense(units=d_model)(outputs) outputs = layers.Dropout(rate=dropout)(outputs) outputs = layers.LayerNormalization(epsilon=1e-6)(outputs + attention2) return keras.Model(inputs=[inputs, enc_outputs], outputs=outputs, name=name) # 定义Transformer结构 def transformer(vocab_size, num_layers, units, d_model, num_heads, dropout, name="transformer"): inputs = keras.Input(shape=(None,), name="inputs") # 定义掩码,用于将填充的部分掩盖掉 padding_mask = keras.layers.Lambda(lambda x: tf.cast(tf.equal(x, 0), dtype=tf.float32))(inputs) # 嵌入层,将输入的单词ID转换为对应的向量表示 embeddings = layers.Embedding(vocab_size, d_model)(inputs) embeddings *= tf.math.sqrt(tf.cast(d_model, tf.float32)) embeddings = PositionalEncoding(vocab_size, d_model)(embeddings) outputs = layers.Dropout(rate=dropout)(embeddings) # 定义多个Encoder层 for i in range(num_layers): outputs = encoder_layer(units=units, d_model=d_model, num_heads=num_heads, dropout=dropout, name="encoder_layer_{}".format(i),)(outputs=[outputs, padding_mask]) # 定义多个Decoder层 for i in range(num_layers): outputs = decoder_layer(units=units, d_model=d_model, num_heads=num_heads, dropout=dropout, name="decoder_layer_{}".format(i),)(inputs=[outputs, padding_mask]) # 添加一个全连接层,输出二值图像折叠后的结果 outputs = layers.Dense(units=vocab_size, activation="softmax")(outputs) return keras.Model(inputs=inputs, outputs=outputs, name=name) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值