Build the whole neural network for the QA model.

def build_net(self, is_training):
        """Build the whole neural network for the QA model."""
        cfg = self.cfg
        with tf.device('/cpu:0'):
            word_embed = tf.get_variable(
                name='word_embed', initializer=self.embed, dtype=tf.float32, trainable=False)
            char_embed = tf.get_variable(name='char_embed',
                                         shape=[cfg.char_vcb_size,
                                                cfg.char_embed_dim],
                                         dtype=tf.float32)

        # [query_length, batch_size]
        self.query_word = tf.placeholder(dtype=tf.int32,
                                         shape=[None, None],
                                         name='query_word')
        self.query_mask = tf.placeholder(dtype=tf.float32,
                                         shape=[None, None],
                                         name='query_mask')
        # [batch_size]
        self.query_lengths = tf.placeholder(
            dtype=tf.int32, shape=[None], name='query_lengths')

        # [passage_length, batch_size]
        self.passage_word = tf.placeholder(
            dtype=tf.int32, shape=[None, None], name='passage_word')
        self.passage_mask = tf.placeholder(
            dtype=tf.float32, shape=[None, None], name='passage_mask')
        # [batch_size]
        self.passage_lengths = tf.placeholder(
            dtype=tf.int32, shape=[None], name='passage_lengths')

        if is_training:
            self.answer_begin = tf.placeholder(
                dtype=tf.int32, shape=[None], name='answer_begin')
            self.answer_end = tf.placeholder(
                dtype=tf.int32, shape=[None], name='answer_end')

        self.query_char_ids = tf.placeholder(dtype=tf.int32,
                                             shape=[
                                                 self.cfg.max_char_length, None, None],
                                             name='query_char_ids')
        # sequence_length, batch_size
        self.query_char_lengths = tf.placeholder(
            dtype=tf.int32, shape=[None, None], name='query_char_lengths')

        self.passage_char_ids = tf.placeholder(dtype=tf.int32,
                                               shape=[
                                                   self.cfg.max_char_length, None, None],
                                               name='passage_char_ids')
        # sequence_length, batch_size
        self.passage_char_lengths = tf.placeholder(dtype=tf.int32,
                                                   shape=[None, None],
                                                   name='passage_char_lengths')

        query_char_states = self.build_char_states(char_embed=char_embed,
                                                   is_training=is_training,
                                                   reuse=False,
                                                   char_ids=self.query_char_ids,
                                                   char_lengths=self.query_char_lengths)

        passage_char_states = self.build_char_states(char_embed=char_embed,
                                                     is_training=is_training,
                                                     reuse=True,
                                                     char_ids=self.passage_char_ids,
                                                     char_lengths=self.passage_char_lengths)

        with tf.variable_scope("encoding") as scope:
            query_states = tf.concat([tf.nn.embedding_lookup(
                word_embed, self.query_word), query_char_states], axis=2)
            scope.reuse_variables()
            passage_states = tf.concat([tf.nn.embedding_lookup(
                word_embed, self.passage_word), passage_char_states], axis=2)
        passage_states = tf.transpose(passage_states, perm=[1, 0, 2])
        query_states = tf.transpose(query_states, perm=[1, 0, 2])
        self.passage_states = passage_states
        self.query_states = query_states

        output, output2 = graph_to_network(passage_states, query_states,
                                           self.passage_lengths, self.query_lengths,
                                           self.graph, self.cfg.dropout,
                                           is_training, num_heads=cfg.num_heads,
                                           rnn_units=cfg.rnn_units)

        passage_att_mask = self.passage_mask
        batch_size_x = tf.shape(self.query_lengths)
        answer_h = tf.zeros(
            tf.concat([batch_size_x, tf.constant([cfg.ptr_dim], dtype=tf.int32)], axis=0))

        answer_context = tf.reduce_mean(output2, axis=1)

        query_init_w = tf.get_variable(
            'query_init_w', shape=[output2.get_shape().as_list()[-1], cfg.ptr_dim])
        self.query_init = query_init_w
        answer_context = tf.matmul(answer_context, query_init_w)

        output = tf.transpose(output, perm=[1, 0, 2])

        with tf.variable_scope('answer_ptr_layer'):
            ptr_att = DotAttention('ptr',
                                   hidden_dim=cfg.ptr_dim,
                                   is_vanilla=self.cfg.att_is_vanilla,
                                   is_identity_transform=self.cfg.att_is_id,
                                   need_padding=self.cfg.att_need_padding)
            answer_pre_compute = ptr_att.get_pre_compute(output)
            ptr_gru = XGRUCell(hidden_dim=cfg.ptr_dim)
            begin_prob, begin_logits = ptr_att.get_prob(output, answer_context, passage_att_mask,
                                                        answer_pre_compute, True)
            att_state = ptr_att.get_att(output, begin_prob)
            (_, answer_h) = ptr_gru.call(inputs=att_state, state=answer_h)
            answer_context = answer_h
            end_prob, end_logits = ptr_att.get_prob(output, answer_context,
                                                    passage_att_mask, answer_pre_compute,
                                                    True)

        self.begin_prob = tf.transpose(begin_prob, perm=[1, 0])
        self.end_prob = tf.transpose(end_prob, perm=[1, 0])
        begin_logits = tf.transpose(begin_logits, perm=[1, 0])
        end_logits = tf.transpose(end_logits, perm=[1, 0])

        if is_training:
            def label_smoothing(inputs, masks, epsilon=0.1):
                """Modify target for label smoothing."""
                epsilon = cfg.labelsmoothing
                num_of_channel = tf.shape(inputs)[-1]  # number of channels
                inputs = tf.cast(inputs, tf.float32)
                return (((1 - epsilon) * inputs) + (epsilon /
                                                    tf.cast(num_of_channel, tf.float32))) * masks
            cost1 = tf.reduce_mean(
                tf.losses.softmax_cross_entropy(label_smoothing(
                    tf.one_hot(self.answer_begin,
                               depth=tf.shape(self.passage_word)[0]),
                    tf.transpose(self.passage_mask, perm=[1, 0])), begin_logits))
            cost2 = tf.reduce_mean(
                tf.losses.softmax_cross_entropy(
                    label_smoothing(tf.one_hot(self.answer_end,
                                               depth=tf.shape(self.passage_word)[0]),
                                    tf.transpose(self.passage_mask, perm=[1, 0])), end_logits))

            reg_ws = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
            l2_loss = tf.reduce_sum(reg_ws)
            loss = cost1 + cost2 + l2_loss
            self.loss = loss

            optimizer = tf.train.AdamOptimizer(learning_rate=cfg.learning_rate)
            self.train_op = optimizer.minimize(self.loss)

        return tf.stack([self.begin_prob, self.end_prob])