text: import argparse
  import glob
  
  import numpy as np
  import pandas as pd
  
  import tensorflow as tf
  
  from keras.preprocessing import sequence
  from tensorflow.contrib import rnn
  
  from sklearn.metrics import classification_report, precision_recall_fscore_support
  
  
  from evidencedetection.vectorizer import TokenizingEmbeddingVectorizer
  
  def parse_arguments():
      parser = argparse.ArgumentParser("Trains a simple BiLSTM to detect sentential arguments across multiple topics.")
  
      parser.add_argument("--embeddings", type=str, help="The path to the embedding folder.")
      parser.add_argument("--train", type=str, help="The training data file.")
      parser.add_argument("--test", type=str, help="The testing data file.")
      parser.add_argument("--epochs", type=int)
      parser.add_argument("--seed", type=int)
  
      return parser.parse_args()
  
  
  def read_data(data_path):
      data = pd.read_csv(data_path)
      return data
  
  
  def create_model(embeddings, max_length, seed):
  
      tf.set_random_seed(seed)
  
      # Based on https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/3_NeuralNetworks/bidirectional_rnn.py
      num_classes = 2
      dims = 100
      learning_rate = 0.001
      X_t = tf.placeholder(tf.int32, [None, max_length], name="topic_input")
      X_s = tf.placeholder(tf.int32, [None, max_length], name="sentence_input")
      Y = tf.placeholder(tf.float32, [None, num_classes], name="target")
  
      def BiRNN(x, layer):
  
          with tf.variable_scope('encoder_{}'.format(layer),reuse=False):
              # Prepare data shape to match `rnn` function requirements
              # Current data input shape: (batch_size, timesteps, n_input)
              # Required shape: 'timesteps' tensors list of shape (batch_size, num_input)
  
              # Unstack to get a list of 'timesteps' tensors of shape (batch_size, num_input)
              x = tf.unstack(x, max_length, 1)
  
              # Define lstm cells with tensorflow
              # Forward direction cell
              lstm_fw_cell = rnn.BasicLSTMCell(dims, forget_bias=1.0)
              # Backward direction cell
              lstm_bw_cell = rnn.BasicLSTMCell(dims, forget_bias=1.0)
  
              # Get lstm cell output
              outputs, _, _ = rnn.static_bidirectional_rnn(lstm_fw_cell, lstm_bw_cell, x, dtype=tf.float32)
  
              print("BiLSTM lengths: ", len(outputs))
              # Linear activation, using rnn inner loop last output
              return outputs[-1]
  
  
      topic_word_embeddings = tf.Variable(embeddings, dtype=tf.float32, name="topic_embeddings")
      topic_embedded_word_id = tf.nn.embedding_lookup(topic_word_embeddings, X_t)
  
      sentence_word_embeddings = tf.Variable(embeddings, dtype=tf.float32, name="sentence_embeddings")
      sentence_embedded_word_id = tf.nn.embedding_lookup(sentence_word_embeddings, X_s)
  
      topic_bilstm_out = BiRNN(topic_embedded_word_id, "topic")
      sentence_bilstm_out = BiRNN(sentence_embedded_word_id, "sentence") 
      output = tf.concat((topic_bilstm_out, sentence_bilstm_out), axis=1)
      logits = tf.layers.dense(output, 2)
      prediction = tf.nn.softmax(logits, name="output")
  
      # Define loss and optimizer
      loss_op = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=Y))
      optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
      train_op = optimizer.minimize(loss_op, name="train")
  
      # Initialize the variables (i.e. assign their default value)
      init = tf.global_variables_initializer()
      return X_t, X_s, Y, prediction, train_op
  
  
  if "__main__"==__name__:
  
      args = parse_arguments()
      train_data = read_data(args.train)
  
      train_links = train_data[["topic", "candidate"]].values
      train_labels = train_data["label"].values
      two_d_train_labels = np.zeros((train_labels.shape[0], 2))
      two_d_train_labels[np.where(train_labels==0), 0] = 1
      two_d_train_labels[np.where(train_labels==1), 1] = 1
  
      hypotheses = train_links[:, 0]
      sentences = train_links[:, 1]
  
      vectorizer = TokenizingEmbeddingVectorizer(args.embeddings)
  
      tokenized_hypotheses = vectorizer.tokenize_sentences(hypotheses)
      tokenized_sentences = vectorizer.tokenize_sentences(sentences)
  
      hypothesis_max_length = max(map(lambda s: len(s.split(" ")), hypotheses))
      sentence_max_length = max(map(lambda s: len(s.split(" ")), sentences))
  
      vectorized_hypotheses = vectorizer.sentences_to_padded_indices(hypotheses, sentence_max_length)
      vectorized_sentences = vectorizer.sentences_to_padded_indices(sentences, sentence_max_length)
  
      print("hypotheses.shape: ", vectorized_hypotheses.shape)
      print("hypotheses: ", vectorized_hypotheses)
      print("sentences.shape: ", vectorized_sentences.shape)
      print("sentences: ", vectorized_sentences)
  
      # Train model
      X_t, X_s, Y, output, train_op = create_model(vectorizer.embeddings, sentence_max_length, args.seed)
      # initialise graph
      init = tf.global_variables_initializer()
  
      session_conf = tf.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1)
      sess = tf.Session(graph=tf.get_default_graph(), config=session_conf)
      sess.run(init)
  
      batch_size = 32
  
      for i in range(args.epochs):
          # Run batches, because...
          num_batches = (len(train_data) + batch_size -1) // batch_size  # Add one to get the remaining samples
          print("Training epoch {0}/{1} with {2} batches for {3} samples.".format(i+1, args.epochs, num_batches, len(train_data)))
          for batch in range(num_batches):
              begin_idx =  batch * batch_size
              end_idx = min((batch+1)*batch_size, len(train_data))  # make sure to not go beyond number of samples
              print("\tRunning batch {0} of {1} with indices [{2}:{3}]".format(batch, num_batches, begin_idx, end_idx))
              feed_dict = {X_t: vectorized_hypotheses[begin_idx:end_idx], X_s: vectorized_sentences[begin_idx:end_idx], Y:two_d_train_labels[begin_idx:end_idx]}
              sess.run(train_op, feed_dict=feed_dict)
  
      test_data = read_data(args.test)
      test_links = test_data[["topic", "candidate"]].values
      test_labels = test_data["label"].values
  
      test_hypotheses = test_links[:, 0]
      test_sentences = test_links[:, 1]
  
      test_vectorized_hypotheses = vectorizer.sentences_to_padded_indices(test_hypotheses, sentence_max_length)
      test_vectorized_sentences = vectorizer.sentences_to_padded_indices(test_sentences, sentence_max_length)
  
  
      test_feed_dict = {X_t: test_vectorized_hypotheses, X_s: test_vectorized_sentences}
      raw_preds = sess.run(output, test_feed_dict)
      preds = np.argmax(raw_preds, axis=1)
      print(preds)
      print(classification_report(test_labels, preds, target_names=["no-link", "link"]))
      print("Macro: ", precision_recall_fscore_support(test_labels, preds, average="macro"))
  
  
      # builder = tf.saved_model.builder.SavedModelBuilder("evidencelinking-en-trainable")
      # builder.add_meta_graph_and_variables(
      #     sess,
      #     [tf.saved_model.tag_constants.SERVING],
      #     signature_def_map= {
      #         "magic_model": tf.saved_model.signature_def_utils.predict_signature_def({"topic_input": X_t, "sentence_input": X_s, "target": Y}, {"output": output})
      #     })
      # builder.save()
  