//  [python]
def mi_loss(self, h_states, click_seq, noclick_seq,  mask, stag='NCE'):
        exp = 'random_1'
        if exp == 'random_1':
            shape = tf.shape(h_states)
            batch, len_seq, dim = shape[0], shape[1], shape[2]
            Wk_ct = []
            x = tf.layers.dense(click_seq, 256, activation=None, name='pos_enc')
            x = tf.unstack(x, axis=1)
            neg = tf.layers.dense(noclick_seq, 256, activation=None, name='neg_enc')
            neg = tf.unstack(neg, axis=1)
            c_t = tf.unstack(h_states, axis=1)
            with tf.name_scope(stag):
                for i in range(len(c_t)):
                    Wk_ct.append(tf.layers.dense(c_t[i], 256, activation=None, name='W{}'.format(i)))
            #nce = 0        
            nce = []
            for i in range(len(c_t)):
                s_p = tf.reduce_sum(x[i] * Wk_ct[i], axis=1, keep_dims=True) # shape=[batch,1]
                s_n = tf.reduce_sum(neg[i] * Wk_ct[i], axis=1, keep_dims=True)
                score = tf.concat([s_p, s_n], axis=1)
                score = tf.nn.log_softmax(tf.exp(score), dim=1)
                score = tf.reshape(score[:, 0], [-1])
                nce.append(score)
            nce = tf.stack(nce, axis=1) * mask
            nce = tf.reduce_sum(nce)
            nce /= -1.0 * tf.cast(batch*len_seq, tf.float32)
            return nce
        elif exp == 'random_all':
            shape = tf.shape(h_states)
            batch, len_seq, dim = shape[0], shape[1], shape[2]
            Wk_ct = []
            x = tf.layers.dense(click_seq, 256, activation=None, name='pos_enc')
            x = tf.unstack(x, axis=1)
            neg = tf.layers.dense(noclick_seq, 256, activation=None, name='neg_enc')
            neg = tf.unstack(neg, axis=1)
            c_t = tf.unstack(h_states, axis=1)
            with tf.name_scope(stag):
                for i in range(len(c_t)):
                    Wk_ct.append(tf.layers.dense(c_t[i], 256, activation=None, name='W{}'.format(i)))
            nce = []
            for i in range(len(c_t)):
                s_p = tf.reduce_sum(x[i] * Wk_ct[i], axis=1, keep_dims=True) # shape=[batch,1]
                s_n = []
                for j in range(len(neg)):
                    s_n.append(tf.reduce_sum(neg[j] * Wk_ct[i], axis=1, keep_dims=True))
                score = tf.concat([s_p] + s_n, axis=1)
                score = tf.nn.log_softmax(tf.exp(score), dim=1)
                score = tf.reshape(score[:, 0], [-1])
                nce.append(score)
            nce = tf.stack(nce, axis=1) * mask
            nce = tf.reduce_sum(nce)
            nce /= -1.0 * tf.cast(batch*len_seq, tf.float32)
            return nce

        elif exp == 'batch_1':
            shape = tf.shape(click_seq)
            batch, len_seq, dim = shape[0], shape[1], shape[2]
            x = tf.layers.dense(click_seq, 256, activation=None, name='pos_enc')
            x = tf.unstack(x, axis=1)
            c_t = tf.unstack(h_states, axis=1)
            # different W for every step
            rand_idx = 12
            Wk_ct = []
            with tf.name_scope(stag):
                for i in range(len(c_t)):
                    Wk_ct.append(tf.layers.dense(c_t[i], 256, activation=None, name='W{}'.format(i)))
            nce = []
            for i in range(len(c_t)):
                x_i = tf.tile(x[i], [2,1])
                s_p = tf.reduce_sum(x_i[0:128, :] * Wk_ct[i], axis=1, keep_dims=True) # shape=[batch,1]
                s_n = tf.reduce_sum(x_i[rand_idx:rand_idx+128] * Wk_ct[i], axis=1, keep_dims=True) # shape=[batch,1]
                score = tf.concat([s_p, s_n], axis=1)
                score = tf.nn.log_softmax(tf.exp(score), dim=1) # softmax over batch
                score = tf.reshape(score[:, 0], [-1])
                nce.append(score)
            nce =tf.stack(nce, axis=1) * mask
            nce = tf.reduce_sum(nce)
            nce /= -1.0*tf.cast(batch*len_seq, tf.float32)
            return nce

        elif exp == 'batch_all':
            shape = tf.shape(click_seq)
            batch, len_seq, dim = shape[0], shape[1], shape[2]
            x = tf.layers.dense(click_seq, 256, activation=None, name='pos_enc')
            x = tf.unstack(x, axis=1)
            c_t = tf.unstack(h_states, axis=1)
            # different W for every step
            Wk_ct = []
            with tf.name_scope(stag):
                for i in range(len(c_t)):
                    Wk_ct.append(tf.layers.dense(c_t[i], 256, activation=None, name='W{}'.format(i)))
            nce = []
            for i in range(len(c_t)):
                score = tf.exp(tf.matmul(x[i], tf.transpose(Wk_ct[i])))
                score = tf.nn.log_softmax(score, dim=0) # softmax over batch
                nce.append(tf.linalg.diag_part(score))
                #nce += tf.reduce_sum(tf.linalg.diag_part(score))
            nce = tf.stack(nce, axis=1)  * mask
            nce = tf.reduce_sum(nce)
            nce /= -1.0*tf.cast(batch*len_seq, tf.float32)
            return nce