copy net
来源:互联网 发布:puppy linux 安装软件 编辑:程序博客网 时间:2024/06/16 05:28
输入数据处理
每个batch包含source和target两个矩阵,source矩阵第二维代表输入句子长度,句子长度不同时用0填充到相同的长度,同时在最后加一个全0列,比如:
[
[1,2,0],
[1,0,0]
]
第一个句子的长度为2,第二个句子的长度为1. target矩阵和source一样的处理方法。
target矩阵在求出embeding后的shape为[batch,sentence_len, embeding_zise], 需要在每个句子的开头加上一个起始符号0,同时将句子最后面一个0的emebding删除掉得到的形状还是[batch,sentence_len, embeding_zise]
测试阶段解码时,每一个step的计算方式:
先求出词的概率分布:
p(j) = p[0:MAX_VOC] (生成概率) 串联上 p[source sentence len](copy概率)。
源句子中位置j处的词如果属于[0:MAX_VOC], 则这个词实际概率为:p[s[j]]+p[MAX_VOC+j], 由生成概率和copy概率两部分组成 其中s[j]代表词的ID;然后将p[MAX_VOC+j]设置为0. 其他词的概率不用变。
本文只包含decoder网络
decoder数据准备
识别target句子中的每个词和源句子中哪些位置的词相同,在返回的cc矩阵里标注
返回值 的shape为 [source.shape[0], target.shape[1], source.shape[1]]
- source.shape[0]:为一个batch里,源句子数;
- target.shape[1] :每个目标句子的长度
- source.shape[1]:每个源句子的长度
代码如下:
def cc_martix(source, target): cc = np.zeros((source.shape[0], target.shape[1], source.shape[1]), dtype='float32') for k in xrange(source.shape[0]): for j in xrange(target.shape[1]): for i in xrange(source.shape[1]): if (source[k, i] == target[k, j]) and (source[k, i] > 0): cc[k][j][i] = 1. return cc
对source和target用下面这个函数处理一下。将data中大于config[‘voc_size’]的元素都设置为1,其他保持不变。词典按照频率从大到小的顺序排列,所以index越大,频率越小。
def unk_filter(data): if config['voc_size'] == -1: return copy.copy(data) else: mask = (np.less(data, config['voc_size'])).astype(dtype='int32') data = copy.copy(data * mask + (1 - mask)) return data
下面函数的target代表解码时一个batch的输入,这个函数的作用是准备好数据用来解码
def prepare_xy(self, target, cc_matrix): ''' target: (nb_samples, index_seq) nb_samples代表每个batch里有多少个句子 cc_matrix: (nb_samples, maxlen_t, maxlen_s)目标词和源句子中哪些位置的词相同 context: (nb_samples) return: Y_mask 其实是一个mask,标记target中哪些位置是真实的词(标记为1),哪些位置的值是填充的值(然后标记为0) Y的shape (nb_samples, maxlen_t, embedding_dim) X: 就是target句子的embeding结果,在每个句子的开头加上了起始符号,也就是全0 X_mask:在Y_mask的第二维的开始插入一个1,同时去掉第二维最后一个词,形状为[nb_samples, maxlen_t] LL :就是CC矩阵,没变化 XL_mask:形状为[nb_samples, maxlen_t],标记目标句子中的词是否和源句子中的某个词相同 Count:记录了每个句子的长度,shape为[nb_samples,1] ''' Y, Y_mask = self.Embed(target, True) #Y[:, :-1, :] 这里面-1表示不包含数组的最后一个元素 #在Y的第二维的第一个位置插入一个全0,同时去掉第二维最后一个词,得到形状为(nb_samples, maxlen_t, embedding_dim) X = T.concatenate([alloc_zeros_matrix(Y.shape[0], 1, Y.shape[2]), Y[:, :-1, :]], axis=1) LL = cc_matrix #T.gt(a,b)将a里值大于b的位置标记为true,其他的位置标记为false #XL_mask的形状为[nb_samples, maxlen_t],得到目标句子中的每个词是否和源句子中的某个词相同 XL_mask = T.cast(T.gt(T.sum(LL, axis=2), 0), dtype='float32') if not self.config['use_input']: X *= 0 #在Y_mask的第二维的开始插入一个1,同时去掉第二维最后一个词,形状为[nb_samples, maxlen_t] X_mask = T.concatenate([T.ones((Y.shape[0], 1)), Y_mask[:, :-1]], axis=1) #Count里记录了每个句子的长度[nb_samples,1] Count = T.cast(T.sum(X_mask, axis=1), dtype=theano.config.floatX) return X, X_mask, LL, XL_mask, Y_mask, Count
decoder attention
计算一个step的attention权重,返回shape 为(nb_samples, maxlen_s), 其中nb_samples就是batch size。
这里用到了coverage机制 可以参考 Get To The Point: Summarization with Pointer-Generator Networks 一文中的Coverage mechanism。大体的意思是:在解码step t 时,对之前每个解码step的attention权重求和。
def __call__(self, X, S, Smask=None, return_log=False, Cov=None): assert X.ndim + 1 == S.ndim, 'source should be one more dimension than target.' # X is the key: (nb_samples, x_dim) 解码时一个step的hidden state # S (nb_samples, maxlen_s, ctx_dim) encoder里每一个step的hidden state # Cov is the coverage vector (nb_samples, maxlen_s) # (nb_samples, source_num, hidden_dims) Eng = dot(X[:, None, :], self.Wa) + dot(S, self.Ua) Eng = self.tanh(Eng) # location aware: if self.coverage: # (nb_samples, source_num, hidden_dims) Eng += dot(Cov[:, :, None], self.Ca) #(nb_samples, source_num, hidden_dims) * (hidden_dims*1) 得到(nb_samples, source_num, 1) Eng = dot(Eng, self.va) Eng = Eng[:, :, 0] # 降维为 (nb_samples, source_num) if Smask is not None: # I want to use mask! EngSum = logSumExp(Eng, axis=1, mask=Smask) if return_log: return (Eng - EngSum) * Smask else: return T.exp(Eng - EngSum) * Smask else: if return_log: return T.log(self.softmax(Eng)) else: return self.softmax(Eng)
计算decoder
def build_decoder(self, target, cc_matrix, context, c_mask, return_count=False, train=True): """ Build the Computational Graph ::> Context is essential c_mask :二维数组[nb_samples, max_len_s] context 保存encoder里每一步的hidden state """ # context: (nb_samples, max_len, contxt_dim),输入到一个全连接层,改变最后一维的长度,得到(h_j * W_c) #后面用来计算update context_A = self.Is(context) # (nb_samples, max_len, embed_dim) X, X_mask, LL, XL_mask, Y_mask, Count = self.prepare_xy(target, cc_matrix) # input drop-out if any. if self.dropout > 0: X = self.D(X, train=train) # Initial state of RNN 第二维第一个位置代表句子最后一个词的隐藏状态? Init_h = self.Initializer(context[:, 0, :]) # default order -> Init_a = T.zeros((context.shape[0], context.shape[1]), dtype='float32') coverage = T.zeros((context.shape[0], context.shape[1]), dtype='float32') X = X.dimshuffle((1, 0, 2)) X_mask = X_mask.dimshuffle((1, 0)) LL = LL.dimshuffle((1, 0, 2)) # (maxlen_t, nb_samples, maxlen_s) maxlen_t锛歮ax target size XL_mask = XL_mask.dimshuffle((1, 0)) # (maxlen_t, nb_samples) def _recurrence(x, x_mask, ll, xl_mask, prev_h, prev_a, cov, cc, cm, ca): """ x: (nb_samples, embed_dims) x_mask: (nb_samples, ) ll: (nb_samples, maxlen_s) xl_mask:(nb_samples, ) ----------------------------------------- prev_h: (nb_samples, hidden_dims) prev_a: (nb_samples, maxlen_s) cov: (nb_samples, maxlen_s) *** coverage *** ----------------------------------------- cc: (nb_samples, maxlen_s, cxt_dim) cm: c_mask (nb_samples, maxlen_s) ca: (nb_samples, maxlen_s, ebd_dim) context_A 用来计算copy时的评分 """ #根据上一步的h,计算下一步的c_i prob = self.attention_reader(prev_h, cc, Smask=cm, Cov=cov) #更新coverage分布向量 ncov = cov + prob #c_i 代表上一步的attention vector,会由于这一步的RNN输入 cxt = T.sum(cc * prob[:, :, None], axis=1) # compute input word embedding (mixed). ca * prev_a[:, :, None]得到的是update x_in = T.concatenate([x, T.sum(ca * prev_a[:, :, None], axis=1)], axis=-1) # compute the current hidden states of the RNN. x_out = self.RNN(x_in, mask=x_mask, C=cxt, init_h=prev_h, one_step=True) # compute the current readout vector. r_in = [x_out] # copynet decoding (nb_samples, out_put_dim+context_dim) #根据x_out,计算取vocabulary里每个词的概率 r_out = self.hidden_readout(x_out) # (nb_samples, voc_size) #将r_in最后一维的长度变为和cc最后一维的长度相同,这样的话两者的最后一维上就可以做element-wise乘法了 key = self.Os(r_in) # (nb_samples, cxt_dim) :: key #计算key和encoder里每个step的相关性,即得到每个位置的权重 Eng = T.sum(key[:, None, :] * cc, axis=-1) #下面两步其实相当于求softmax,在后面会具体讲一下 EngSum = logSumExp(Eng, axis=-1, mask=cm, c=r_out) next_p = T.concatenate([T.exp(r_out - EngSum), T.exp(Eng - EngSum) * cm], axis=-1) #copy模式下的概率. 对于一个target词,只留下源句子中和其相同的位置的概率 next_c = next_p[:, self.config['dec_voc_size']:] * ll # (nb_samples, maxlen_s) #生成模式下的概率 next_b = next_p[:, :self.config['dec_voc_size']] #下面两项计算update值 sum_a = T.sum(next_c, axis=1, keepdims=True) # (nb_samples,1) next_a = (next_c / (sum_a + err)) * xl_mask[:, None] # numerically consideration return x_out, next_a, ncov, sum_a, next_b #代入参数时,顺序为sequences, outputs_info, non_sequences outputs, _ = theano.scan( _recurrence, sequences=[X, X_mask, LL, XL_mask], outputs_info=[Init_h, Init_a, coverage, None, None],#None的值不传入函数 non_sequences=[context, c_mask, context_A] ) X_out, source_prob, coverages, source_sum, prob_dist = [z.dimshuffle((1, 0, 2)) for z in outputs] X = X.dimshuffle((1, 0, 2)) X_mask = X_mask.dimshuffle((1, 0)) XL_mask = XL_mask.dimshuffle((1, 0)) ''' 当词是unk并且这个词在源句中时,在target里是用1代替的,1就代表UNK,同时在XL_mask中标记这个位置的词是和源句中的某个词相同。 所以下面两行的功能是:只留下target矩阵中非填充的词,且当target词大于了voc即UNK词,且没在原句中出现时,才标记为1。 得到的shape为[nb_samples, maxlen_t], ''' U_mask = T.ones_like(target) * (1 - T.eq(target, 1)) U_mask += (1 - U_mask) * (1 - XL_mask) ''' 概率计算分四种: 1、当target词属于vocabulary且target词不在原句中时,用生成概率; 2、当target词属于vocabulary且target词在原句中时,用生成概率加上这个词在x中的copy概率和; 3、当target词为UNK,且不在源句子中时,用生成UNK的概率 4、当target词为UNK,且在源句中时,用原句中每个和UNK词相同的位置的概率和 self._grab_prob(prob_dist, target) * U_mask :计算1、2中的生成概率、3 source_sum.sum(axis=-1): source_sum的shape为[nb_samples, maxlen_t, 1]。计算2中的copy概率以及第四条 ''' #prob_dist :[nb_samples, maxlen_t, voc_size] self._grab_prob(prob_dist, target) * U_mask #log_prob : shape (nb_samples,)是一个矩阵 log_prob = T.sum(T.log( self._grab_prob(prob_dist, target) * U_mask + source_sum.sum(axis=-1) + err ) * X_mask, axis=1) #(nb_samples,)每个句子对应的perplex,即每个词的平均概率 log_ppl = log_prob / (Count + err) if return_count: return log_prob, Count else: return log_prob, log_ppl
The log-sum-exp trick
参考The log-sum-exp trick in Machine Learning
Let’s say we have an n-dimensional vector and want to calculate:
if you try to calculate it naively, you quite quickly will encounter underflows or overflows, depending on the scale of
We can show, that the following equation holds:
其中a取x中的最大值,如果用上式右边替代y来计算,就不会出现上面的问题. 这样计算softmax就可以按照下面的方法:
这张图就对应上面代码的
EngSum = logSumExp(Eng, axis=-1, mask=cm, c=r_out) next_p = T.concatenate([T.exp(r_out - EngSum), T.exp(Eng - EngSum) * cm], axis=-1)
- copy net
- C# .net Directory Copy
- .net copy与clone的用法
- asp.net 程序copy SSH文件
- Asp.net MVC2.0 简介(copy)
- copy
- copy
- copy
- copy.
- copy
- copy
- copy
- copy
- copy
- copy
- Copy
- copy
- copy
- 表单添加 删除 搜索
- 设计模式------1.六大原则
- 阻塞队列Qeue在take的状态下推出多线程循环(笔记)
- 图像识别中的深度学习
- 《大数据分析原理与实践》——第3章 关联分析模型
- copy net
- Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[.....报错的问题
- Vue使用echarts数据动态更新,Y轴刻度固定或者自适应
- SQL查询的艺术学习笔记--连接符数值运算函数
- angularJS路由跳转
- openstack-dashboard安装
- React Native组件的生命周期及回调函数
- linux 基于alsa 使用libmad 解码库实现MP3文件的播放
- socket阻塞与非阻塞,同步与异步