list of list
每一个list,由N个tuple组成,第一个tuple是开始的,
(["<s>", "<d>", "</s>"] ,0, None)其他的每个tuple,
第一项是list of str(就是一个句子的所有单词)第二项是int,是否是B说的,第三项是 tuple, 第一个元素是str,消息类型(下面统称为dialog_act),第二项是list of float,向量(1*4的数组)list of tuple
每一个tuple也有三项,
第一项和第二项都是说话人的信息,一个list,年龄、教育、性别第三项是str,topiclist of list,
就是所有的消息,和dialog不同的是没有说话人和消息向量这两个。
统计训练语料中所有单词的词频,只保留词频在前10000个。OOV在0.008035
vocab:list of str,就是所有的单词,再加上两个 <pad> 和 <unk>(未知单词)
rev_vocab:dict,key是单词,value是在词表的位置
也是按照topic频率建立,一共有67个topic
同上,一共有42个dialog_act
算出一共可以分成几个batch,记录每个batch包含的训练数据。
每一batch返回前,要做如下处理
每一个数据单元都是一轮会话,把最后一句作为out,前面的都是 context
返回的是一批输入context,一批out
相关变量: context_lens, list of int 每个训练样本有几句输入 context_utts, list of list 每个训练样本中的输入句子(补齐长度) floors, list of [0/1] 每个训练样本中的输入句子,和`out`是不是同一个人 out_utts, list of list 每个`out` out_lens, list of int 每个`out` 长度 out_floors, list of int,每个`out`说话人 out_das, list of list,每个`out`的语料 Returns: vec_context: 一个np矩阵,第一维`batch_id`, 第二三维就是`context`向量,用0补齐 vec_context_lens:np数组, vec_floors:np数组,第一维`batch_id` my_profiles = np.array([meta[out_floors[idx]] for idx, meta in enumerate(meta_rows)]) ot_profiles = np.array([meta[1-out_floors[idx]] for idx, meta in enumerate(meta_rows)])后来重新看了一下meta的内容,my_profiles 和 ot_profiles 是说话两方
out_floors[idx] 取值0,1,1-out_floors[idx] 取值1,0每一个meta是一个list,
[m_meta, o_meta, topic_idx]每一个dialog是一个list
第一项:每一句话中,每个词在词表的位置utt_idx,第二项:每一句话是谁说的0或者1,floor第三项:[dialog_act_idx, x,x,x],比如 ['statement-non-opinion', [0.149, 0.851, 0.0, -0.4215]][ μ log ( σ 2 ) ] = W r [ x c ] + b r \left[\begin{array}{c}{\mu} \\ {\log \left(\sigma^{2}\right)}\end{array}\right]=W_{r}\left[\begin{array}{l}{x} \\ {c}\end{array}\right]+b_{r} [μlog(σ2)]=Wr[xc]+br 对应的实现代码如下:
with variable_scope.variable_scope("recognitionNetwork"): recog_input = tf.concat([cond_embedding, output_embedding, attribute_fc1], 1) recog_mulogvar = layers.fully_connected(recog_input, config.latent_size * 2, activation_fn=None, scope="muvar") recog_mu, recog_logvar = tf.split(recog_mulogvar, 2, axis=1) # 从公式看出是将两个参数一起训练的,所以用tf.split得到训练后的结果。[ μ ′ log ( σ ′ 2 ) ] = MLP p ( c ) \left[\begin{array}{c}{\mu^{\prime}} \\ {\log \left(\sigma^{\prime 2}\right)}\end{array}\right]=\operatorname{MLP}_{p}(c) [μ′log(σ′2)]=MLPp(c)
with variable_scope.variable_scope("priorNetwork"): # P(XYZ)=P(Z|X)P(X)P(Y|X,Z) prior_fc1 = layers.fully_connected(cond_embedding, np.maximum(config.latent_size * 2, 100), activation_fn=tf.tanh, scope="fc1") prior_mulogvar = layers.fully_connected(prior_fc1, config.latent_size * 2, activation_fn=None, scope="muvar") prior_mu, prior_logvar = tf.split(prior_mulogvar, 2, axis=1) # use sampled Z or posterior Z latent_sample = tf.cond(self.use_prior, lambda: sample_gaussian(prior_mu, prior_logvar), lambda: sample_gaussian(recog_mu, recog_logvar)) # 这里, self.use_prior 是bool的False,所以执行的后面那个函数,就是trick,见下面其实很简单,就是我们要从 $p(Z|X_{k})$ 中采样一个 Z k Z_{k} Zk出来,尽管我们知道了 p ( Z ∣ X k ) p(Z|X_{k}) p(Z∣Xk) 是正态分布,但是均值方差都是靠模型算出来的,我们要靠这个过程反过来优化均值方差的模型,但是“采样”这个操作是不可导的,而采样的结果是可导的,于是我们利用了一个事实:
从 N ( μ , σ 2 ) \mathcal{N}\left(\mu, \sigma^{2}\right) N(μ,σ2)中采样一个 Z Z Z,相当于从 N ( 0 , I ) \mathcal{N}\left(0, I\right) N(0,I)中采样一个 ϵ \epsilon ϵ,然后让 Z = μ + ε × σ Z=\mu+\varepsilon \times \sigma Z=μ+ε×σ
这样一来,“采样”这个操作就不用参与梯度下降了,改为采样的结果参与,使得整个模型可训练了
对应的代码实现
def sample_gaussian(mu, logvar): epsilon = tf.random_normal(tf.shape(logvar), name="epsilon") std = tf.exp(0.5 * logvar) z= mu + tf.multiply(std, epsilon) return z在前面的讨论中,我们希望 $X$ 经过编码后,$Z$ 的分布都具有零均值和单位方差,这个“希望”是通过加入了 KL loss 来实现的。
如果现在多了类别信息 $Y$,我们可以希望同一个类的样本都有一个专属的均值 $\mu^{Y}$(方差不变,还是单位方差),这个 $\mu^{Y}$ 让模型自己训练出来。
这样的话,有多少个类就有多少个正态分布,而在生成的时候,我们就可以通过控制均值来控制生成图像的类别。
事实上,这样可能也是在 VAE 的基础上加入最少的代码来实现 CVAE 的方案了,因为这个“新希望”也只需通过修改 KL loss 实现:
L μ , σ 2 = 1 2 ∑ i = 1 d [ ( μ ( i ) − μ ( i ) Y ) 2 + σ ( i ) 2 − log σ ( i ) 2 − 1 ] \mathcal{L}_{\mu, \sigma^{2}}=\frac{1}{2} \sum_{i=1}^{d}\left[\left(\mu_{(i)}-\mu_{(i)}^{Y}\right)^{2}+\sigma_{(i)}^{2}-\log \sigma_{(i)}^{2}-1\right] Lμ,σ2=21i=1∑d[(μ(i)−μ(i)Y)2+σ(i)2−logσ(i)2−1]
def gaussian_kld(recog_mu, recog_logvar, prior_mu, prior_logvar): kld = -0.5 * tf.reduce_sum(1 + (recog_logvar - prior_logvar) - tf.div(tf.pow(prior_mu - recog_mu, 2), tf.exp(prior_logvar)) - tf.div(tf.exp(recog_logvar), tf.exp(prior_logvar)), reduction_indices=1) return kld模型训练中,计算loss时有一处不是很明白
self.log_p_z = norm_log_liklihood(latent_sample, prior_mu, prior_logvar) self.log_q_z_xy = norm_log_liklihood(latent_sample, recog_mu, recog_logvar) self.est_marginal = tf.reduce_mean(rc_loss + bow_loss - self.log_p_z + self.log_q_z_xy)