# Copyright 2016 Google Inc. All Rights Reserved. Licensed under the Apache
# License, Version 2.0 (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

"""Define a Wide + Deep model for classification on structured data."""

# Obtained from: https://github.com/GoogleCloudPlatform/cloudml-samples/blob/master/census/estimator/trainer/model.py
# Modified to use a Wide and Deep Model over the Credit Card Fraud Dataset

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import multiprocessing

import tensorflow as tf


# Define the format of your input data including unused columns
CSV_COLUMNS = [ 'time', 'v1', 'v2','v3','v4','v5','v6','v7','v8','v9','v10','v11',
               'v12','v13','v14','v15','v16','v17','v18','v19','v20','v21','v22','v23','v24',
               'v25','v26','v27', 'v28','amount','class']               

# We include these defaults in order to force float castings during the CSV parsing
CSV_COLUMN_DEFAULTS = [[0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],
                       [-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],
                       [-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[0.3],['']]


LABEL_COLUMN = 'class'

LABELS = ['fraud', 'notfraud']

# Define the initial ingestion of each feature used by your model.
# Additionally, provide metadata about the feature.
INPUT_COLUMNS = [


    # Continuous base columns.
    tf.feature_column.numeric_column('time'),
    tf.feature_column.numeric_column('v1'),
    tf.feature_column.numeric_column('v2'),
    tf.feature_column.numeric_column('v3'),
    tf.feature_column.numeric_column('v4'),
    tf.feature_column.numeric_column('v5'),    
    tf.feature_column.numeric_column('v6'),
    tf.feature_column.numeric_column('v7'),
    tf.feature_column.numeric_column('v8'),
    tf.feature_column.numeric_column('v9'),
    tf.feature_column.numeric_column('v10'),
    tf.feature_column.numeric_column('v11'),
    tf.feature_column.numeric_column('v12'),
    tf.feature_column.numeric_column('v13'),
    tf.feature_column.numeric_column('v14'),
    tf.feature_column.numeric_column('v15'),    
    tf.feature_column.numeric_column('v16'),
    tf.feature_column.numeric_column('v17'),
    tf.feature_column.numeric_column('v18'),
    tf.feature_column.numeric_column('v19'),
    tf.feature_column.numeric_column('v20'),
    tf.feature_column.numeric_column('v21'),
    tf.feature_column.numeric_column('v22'),
    tf.feature_column.numeric_column('v23'),
    tf.feature_column.numeric_column('v24'),
    tf.feature_column.numeric_column('v25'),    
    tf.feature_column.numeric_column('v26'),
    tf.feature_column.numeric_column('v27'),
    tf.feature_column.numeric_column('v28'),
    tf.feature_column.numeric_column('amount'),

]

UNUSED_COLUMNS = set(CSV_COLUMNS) - {col.name for col in INPUT_COLUMNS} - \
    {LABEL_COLUMN}


def build_estimator(config, embedding_size=8, hidden_units=None):
  """Build a wide and deep model for predicting income category.

  Wide and deep models use deep neural nets to learn high level abstractions
  about complex features or interactions between such features.
  These models then combined the outputs from the DNN with a linear regression
  performed on simpler features. This provides a balance between power and
  speed that is effective on many structured data problems.

  You can read more about wide and deep models here:
  https://research.googleblog.com/2016/06/wide-deep-learning-better-together-with.html

  To define model we can use the prebuilt DNNCombinedLinearClassifier class,
  and need only define the data transformations particular to our dataset, and
  then
  assign these (potentially) transformed features to either the DNN, or linear
  regression portion of the model.

  Args:
    config: tf.contrib.learn.RunConfig defining the runtime environment for the
      estimator (including model_dir).
    embedding_size: int, the number of dimensions used to represent categorical
      features when providing them as inputs to the DNN.
    hidden_units: [int], the layer sizes of the DNN (input layer first)
    learning_rate: float, the learning rate for the optimizer.
  Returns:
    A DNNCombinedLinearClassifier
  """
  (time, v1, v2, v3, v4,
   v5, v6, v7, v8, v9, 
   v10, v11, v12, v13, v14, 
   v15,v16,v17,v18,v19,v20,v21,
   v22,v23,v24,v25,v26,v27,v28, amount) = INPUT_COLUMNS
  """Build an estimator."""
  
  # Reused Transformations.
  # Continuous columns can be converted to categorical via bucketization
  # We use the (bucketized) amount column in the Wide part
  amount_buckets = tf.feature_column.bucketized_column(amount, boundaries=[4,8,12,15,35,75,100, 200, 300, 1000])

  # Wide columns and deep columns.
  wide_columns = [amount_buckets]

  # All the other CCF features will be used in the deep part
  deep_columns = [
   time, v1, v2, v3, v4,
   v5, v6, v7, v8, v9, 
   v10, v11, v12, v13, v14, 
   v15,v16,v17,v18,v19,v20,v21,
   v22,v23,v24,v25,v26,v27,v28
  ]
  
  # We hardcode here the models in order to avoid the exponential decaying model which is already implemented
  hidden_units = [20,15]

  # We can try either Wide and Deep models or Deep Neural Networks (DNN)
  #"""
  return tf.contrib.learn.DNNLinearCombinedClassifier(
      config=config,
      linear_feature_columns=wide_columns,
      dnn_feature_columns=deep_columns,
      dnn_hidden_units=hidden_units or [100, 70, 50, 25],
      dnn_optimizer=tf.train.AdamOptimizer(),
      fix_global_step_increment_bug=True
  )

  """
  deep_columns = deep_columns + [amount]
  return tf.contrib.learn.DNNClassifier(
      config=config,
      feature_columns=deep_columns,
      hidden_units=hidden_units or [100, 70, 50, 25],
      optimizer=tf.train.AdamOptimizer()
      #optimizer=tf.train.ProximalAdagradOptimizer(
      #learning_rate=0.1,
      #l2_regularization_strength=0.001
	  #)
      )
  #"""



def parse_label_column(label_string_tensor):
  """Parses a string tensor into the label tensor
  Args:
    label_string_tensor: Tensor of dtype string. Result of parsing the
    CSV column specified by LABEL_COLUMN
  Returns:
    A Tensor of the same shape as label_string_tensor, should return
    an int64 Tensor representing the label index for classification tasks,
    and a float32 Tensor representing the value for a regression task.
  """
  # Build a Hash Table inside the graph
  table = tf.contrib.lookup.index_table_from_tensor(tf.constant(LABELS))

  # Use the hash table to convert string labels to ints and one-hot encode
  return table.lookup(label_string_tensor)


# ************************************************************************
# YOU NEED NOT MODIFY ANYTHING BELOW HERE TO ADAPT THIS MODEL TO YOUR DATA
# ************************************************************************


def csv_serving_input_fn():
  """Build the serving inputs."""
  csv_row = tf.placeholder(
      shape=[None],
      dtype=tf.string
  )
  features = parse_csv(csv_row)
  features.pop(LABEL_COLUMN)
  return tf.contrib.learn.InputFnOps(features, None, {'csv_row': csv_row})


def example_serving_input_fn():
  """Build the serving inputs."""
  example_bytestring = tf.placeholder(
      shape=[None],
      dtype=tf.string,
  )
  feature_scalars = tf.parse_example(
      example_bytestring,
      tf.feature_column.make_parse_example_spec(INPUT_COLUMNS)
  )
  features = {
      key: tf.expand_dims(tensor, -1)
      for key, tensor in feature_scalars.iteritems()
  }
  return tf.contrib.learn.InputFnOps(
      features,
      None,  # labels
      {'example_proto': example_bytestring}
  )


def json_serving_input_fn():
  """Build the serving inputs."""
  inputs = {}
  for feat in INPUT_COLUMNS:
    inputs[feat.name] = tf.placeholder(shape=[None], dtype=feat.dtype)

  features = {
      key: tf.expand_dims(tensor, -1)
      for key, tensor in inputs.iteritems()
  }
  return tf.contrib.learn.InputFnOps(features, None, inputs)


SERVING_FUNCTIONS = {
    'JSON': json_serving_input_fn,
    'EXAMPLE': example_serving_input_fn,
    'CSV': csv_serving_input_fn
}


def parse_csv(rows_string_tensor):
  """Takes the string input tensor and returns a dict of rank-2 tensors."""

  # Takes a rank-1 tensor and converts it into rank-2 tensor
  # Example if the data is ['csv,line,1', 'csv,line,2', ..] to
  # [['csv,line,1'], ['csv,line,2']] which after parsing will result in a
  # tuple of tensors: [['csv'], ['csv']], [['line'], ['line']], [[1], [2]]
  row_columns = tf.expand_dims(rows_string_tensor, -1)
  columns = tf.decode_csv(row_columns, record_defaults=CSV_COLUMN_DEFAULTS)
  features = dict(zip(CSV_COLUMNS, columns))

  # Remove unused columns
  for col in UNUSED_COLUMNS:
    features.pop(col)
  return features


def generate_input_fn(filenames,
                      num_epochs=None,
                      shuffle=True,
                      skip_header_lines=0,
                      batch_size=200):
  """Generates an input function for training or evaluation.
  This uses the input pipeline based approach using file name queue
  to read data so that entire data is not loaded in memory.

  Args:
      filenames: [str] list of CSV files to read data from.
      num_epochs: int how many times through to read the data.
        If None will loop through data indefinitely
      shuffle: bool, whether or not to randomize the order of data.
        Controls randomization of both file order and line order within
        files.
      skip_header_lines: int set to non-zero in order to skip header lines
        in CSV files.
      batch_size: int First dimension size of the Tensors returned by
        input_fn
  Returns:
      A function () -> (features, indices) where features is a dictionary of
        Tensors, and indices is a single Tensor of label indices.
  """
  filename_queue = tf.train.string_input_producer(
      filenames, num_epochs=num_epochs, shuffle=shuffle)
  reader = tf.TextLineReader(skip_header_lines=skip_header_lines)

  _, rows = reader.read_up_to(filename_queue, num_records=batch_size)

  # Parse the CSV File
  features = parse_csv(rows)

  # This operation builds up a buffer of parsed tensors, so that parsing
  # input data doesn't block training
  # If requested it will also shuffle
  if shuffle:
    features = tf.train.shuffle_batch(
        features,
        batch_size,
        min_after_dequeue=2 * batch_size + 1,
        capacity=batch_size * 10,
        num_threads=multiprocessing.cpu_count(),
        enqueue_many=True,
        allow_smaller_final_batch=True
    )
  else:
    features = tf.train.batch(
        features,
        batch_size,
        capacity=batch_size * 10,
        num_threads=multiprocessing.cpu_count(),
        enqueue_many=True,
        allow_smaller_final_batch=True
    )

  return features, parse_label_column(features.pop(LABEL_COLUMN))

bef90d7733809e4507684f356b8827a88163a64a
<mask token>


def build_estimator(config, embedding_size=8, hidden_units=None):
    """Build a wide and deep model for predicting income category.

  Wide and deep models use deep neural nets to learn high level abstractions
  about complex features or interactions between such features.
  These models then combined the outputs from the DNN with a linear regression
  performed on simpler features. This provides a balance between power and
  speed that is effective on many structured data problems.

  You can read more about wide and deep models here:
  https://research.googleblog.com/2016/06/wide-deep-learning-better-together-with.html

  To define model we can use the prebuilt DNNCombinedLinearClassifier class,
  and need only define the data transformations particular to our dataset, and
  then
  assign these (potentially) transformed features to either the DNN, or linear
  regression portion of the model.

  Args:
    config: tf.contrib.learn.RunConfig defining the runtime environment for the
      estimator (including model_dir).
    embedding_size: int, the number of dimensions used to represent categorical
      features when providing them as inputs to the DNN.
    hidden_units: [int], the layer sizes of the DNN (input layer first)
    learning_rate: float, the learning rate for the optimizer.
  Returns:
    A DNNCombinedLinearClassifier
  """
    (time, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15,
        v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, amount
        ) = INPUT_COLUMNS
    """Build an estimator."""
    amount_buckets = tf.feature_column.bucketized_column(amount, boundaries
        =[4, 8, 12, 15, 35, 75, 100, 200, 300, 1000])
    wide_columns = [amount_buckets]
    deep_columns = [time, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
        v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
        v26, v27, v28]
    hidden_units = [20, 15]
    return tf.contrib.learn.DNNLinearCombinedClassifier(config=config,
        linear_feature_columns=wide_columns, dnn_feature_columns=
        deep_columns, dnn_hidden_units=hidden_units or [100, 70, 50, 25],
        dnn_optimizer=tf.train.AdamOptimizer(),
        fix_global_step_increment_bug=True)
    """
  deep_columns = deep_columns + [amount]
  return tf.contrib.learn.DNNClassifier(
      config=config,
      feature_columns=deep_columns,
      hidden_units=hidden_units or [100, 70, 50, 25],
      optimizer=tf.train.AdamOptimizer()
      #optimizer=tf.train.ProximalAdagradOptimizer(
      #learning_rate=0.1,
      #l2_regularization_strength=0.001
	  #)
      )
  #"""


<mask token>


def csv_serving_input_fn():
    """Build the serving inputs."""
    csv_row = tf.placeholder(shape=[None], dtype=tf.string)
    features = parse_csv(csv_row)
    features.pop(LABEL_COLUMN)
    return tf.contrib.learn.InputFnOps(features, None, {'csv_row': csv_row})


def example_serving_input_fn():
    """Build the serving inputs."""
    example_bytestring = tf.placeholder(shape=[None], dtype=tf.string)
    feature_scalars = tf.parse_example(example_bytestring, tf.
        feature_column.make_parse_example_spec(INPUT_COLUMNS))
    features = {key: tf.expand_dims(tensor, -1) for key, tensor in
        feature_scalars.iteritems()}
    return tf.contrib.learn.InputFnOps(features, None, {'example_proto':
        example_bytestring})


def json_serving_input_fn():
    """Build the serving inputs."""
    inputs = {}
    for feat in INPUT_COLUMNS:
        inputs[feat.name] = tf.placeholder(shape=[None], dtype=feat.dtype)
    features = {key: tf.expand_dims(tensor, -1) for key, tensor in inputs.
        iteritems()}
    return tf.contrib.learn.InputFnOps(features, None, inputs)


<mask token>


def generate_input_fn(filenames, num_epochs=None, shuffle=True,
    skip_header_lines=0, batch_size=200):
    """Generates an input function for training or evaluation.
  This uses the input pipeline based approach using file name queue
  to read data so that entire data is not loaded in memory.

  Args:
      filenames: [str] list of CSV files to read data from.
      num_epochs: int how many times through to read the data.
        If None will loop through data indefinitely
      shuffle: bool, whether or not to randomize the order of data.
        Controls randomization of both file order and line order within
        files.
      skip_header_lines: int set to non-zero in order to skip header lines
        in CSV files.
      batch_size: int First dimension size of the Tensors returned by
        input_fn
  Returns:
      A function () -> (features, indices) where features is a dictionary of
        Tensors, and indices is a single Tensor of label indices.
  """
    filename_queue = tf.train.string_input_producer(filenames, num_epochs=
        num_epochs, shuffle=shuffle)
    reader = tf.TextLineReader(skip_header_lines=skip_header_lines)
    _, rows = reader.read_up_to(filename_queue, num_records=batch_size)
    features = parse_csv(rows)
    if shuffle:
        features = tf.train.shuffle_batch(features, batch_size,
            min_after_dequeue=2 * batch_size + 1, capacity=batch_size * 10,
            num_threads=multiprocessing.cpu_count(), enqueue_many=True,
            allow_smaller_final_batch=True)
    else:
        features = tf.train.batch(features, batch_size, capacity=batch_size *
            10, num_threads=multiprocessing.cpu_count(), enqueue_many=True,
            allow_smaller_final_batch=True)
    return features, parse_label_column(features.pop(LABEL_COLUMN))

<mask token>


def build_estimator(config, embedding_size=8, hidden_units=None):
    """Build a wide and deep model for predicting income category.

  Wide and deep models use deep neural nets to learn high level abstractions
  about complex features or interactions between such features.
  These models then combined the outputs from the DNN with a linear regression
  performed on simpler features. This provides a balance between power and
  speed that is effective on many structured data problems.

  You can read more about wide and deep models here:
  https://research.googleblog.com/2016/06/wide-deep-learning-better-together-with.html

  To define model we can use the prebuilt DNNCombinedLinearClassifier class,
  and need only define the data transformations particular to our dataset, and
  then
  assign these (potentially) transformed features to either the DNN, or linear
  regression portion of the model.

  Args:
    config: tf.contrib.learn.RunConfig defining the runtime environment for the
      estimator (including model_dir).
    embedding_size: int, the number of dimensions used to represent categorical
      features when providing them as inputs to the DNN.
    hidden_units: [int], the layer sizes of the DNN (input layer first)
    learning_rate: float, the learning rate for the optimizer.
  Returns:
    A DNNCombinedLinearClassifier
  """
    (time, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15,
        v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, amount
        ) = INPUT_COLUMNS
    """Build an estimator."""
    amount_buckets = tf.feature_column.bucketized_column(amount, boundaries
        =[4, 8, 12, 15, 35, 75, 100, 200, 300, 1000])
    wide_columns = [amount_buckets]
    deep_columns = [time, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
        v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
        v26, v27, v28]
    hidden_units = [20, 15]
    return tf.contrib.learn.DNNLinearCombinedClassifier(config=config,
        linear_feature_columns=wide_columns, dnn_feature_columns=
        deep_columns, dnn_hidden_units=hidden_units or [100, 70, 50, 25],
        dnn_optimizer=tf.train.AdamOptimizer(),
        fix_global_step_increment_bug=True)
    """
  deep_columns = deep_columns + [amount]
  return tf.contrib.learn.DNNClassifier(
      config=config,
      feature_columns=deep_columns,
      hidden_units=hidden_units or [100, 70, 50, 25],
      optimizer=tf.train.AdamOptimizer()
      #optimizer=tf.train.ProximalAdagradOptimizer(
      #learning_rate=0.1,
      #l2_regularization_strength=0.001
	  #)
      )
  #"""


def parse_label_column(label_string_tensor):
    """Parses a string tensor into the label tensor
  Args:
    label_string_tensor: Tensor of dtype string. Result of parsing the
    CSV column specified by LABEL_COLUMN
  Returns:
    A Tensor of the same shape as label_string_tensor, should return
    an int64 Tensor representing the label index for classification tasks,
    and a float32 Tensor representing the value for a regression task.
  """
    table = tf.contrib.lookup.index_table_from_tensor(tf.constant(LABELS))
    return table.lookup(label_string_tensor)


def csv_serving_input_fn():
    """Build the serving inputs."""
    csv_row = tf.placeholder(shape=[None], dtype=tf.string)
    features = parse_csv(csv_row)
    features.pop(LABEL_COLUMN)
    return tf.contrib.learn.InputFnOps(features, None, {'csv_row': csv_row})


def example_serving_input_fn():
    """Build the serving inputs."""
    example_bytestring = tf.placeholder(shape=[None], dtype=tf.string)
    feature_scalars = tf.parse_example(example_bytestring, tf.
        feature_column.make_parse_example_spec(INPUT_COLUMNS))
    features = {key: tf.expand_dims(tensor, -1) for key, tensor in
        feature_scalars.iteritems()}
    return tf.contrib.learn.InputFnOps(features, None, {'example_proto':
        example_bytestring})


def json_serving_input_fn():
    """Build the serving inputs."""
    inputs = {}
    for feat in INPUT_COLUMNS:
        inputs[feat.name] = tf.placeholder(shape=[None], dtype=feat.dtype)
    features = {key: tf.expand_dims(tensor, -1) for key, tensor in inputs.
        iteritems()}
    return tf.contrib.learn.InputFnOps(features, None, inputs)


<mask token>


def parse_csv(rows_string_tensor):
    """Takes the string input tensor and returns a dict of rank-2 tensors."""
    row_columns = tf.expand_dims(rows_string_tensor, -1)
    columns = tf.decode_csv(row_columns, record_defaults=CSV_COLUMN_DEFAULTS)
    features = dict(zip(CSV_COLUMNS, columns))
    for col in UNUSED_COLUMNS:
        features.pop(col)
    return features


def generate_input_fn(filenames, num_epochs=None, shuffle=True,
    skip_header_lines=0, batch_size=200):
    """Generates an input function for training or evaluation.
  This uses the input pipeline based approach using file name queue
  to read data so that entire data is not loaded in memory.

  Args:
      filenames: [str] list of CSV files to read data from.
      num_epochs: int how many times through to read the data.
        If None will loop through data indefinitely
      shuffle: bool, whether or not to randomize the order of data.
        Controls randomization of both file order and line order within
        files.
      skip_header_lines: int set to non-zero in order to skip header lines
        in CSV files.
      batch_size: int First dimension size of the Tensors returned by
        input_fn
  Returns:
      A function () -> (features, indices) where features is a dictionary of
        Tensors, and indices is a single Tensor of label indices.
  """
    filename_queue = tf.train.string_input_producer(filenames, num_epochs=
        num_epochs, shuffle=shuffle)
    reader = tf.TextLineReader(skip_header_lines=skip_header_lines)
    _, rows = reader.read_up_to(filename_queue, num_records=batch_size)
    features = parse_csv(rows)
    if shuffle:
        features = tf.train.shuffle_batch(features, batch_size,
            min_after_dequeue=2 * batch_size + 1, capacity=batch_size * 10,
            num_threads=multiprocessing.cpu_count(), enqueue_many=True,
            allow_smaller_final_batch=True)
    else:
        features = tf.train.batch(features, batch_size, capacity=batch_size *
            10, num_threads=multiprocessing.cpu_count(), enqueue_many=True,
            allow_smaller_final_batch=True)
    return features, parse_label_column(features.pop(LABEL_COLUMN))

<mask token>
CSV_COLUMNS = ['time', 'v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7', 'v8', 'v9',
    'v10', 'v11', 'v12', 'v13', 'v14', 'v15', 'v16', 'v17', 'v18', 'v19',
    'v20', 'v21', 'v22', 'v23', 'v24', 'v25', 'v26', 'v27', 'v28', 'amount',
    'class']
CSV_COLUMN_DEFAULTS = [[0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3
    ], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-
    0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [
    -0.3], [-0.3], [-0.3], [-0.3], [-0.3], [0.3], ['']]
LABEL_COLUMN = 'class'
LABELS = ['fraud', 'notfraud']
INPUT_COLUMNS = [tf.feature_column.numeric_column('time'), tf.
    feature_column.numeric_column('v1'), tf.feature_column.numeric_column(
    'v2'), tf.feature_column.numeric_column('v3'), tf.feature_column.
    numeric_column('v4'), tf.feature_column.numeric_column('v5'), tf.
    feature_column.numeric_column('v6'), tf.feature_column.numeric_column(
    'v7'), tf.feature_column.numeric_column('v8'), tf.feature_column.
    numeric_column('v9'), tf.feature_column.numeric_column('v10'), tf.
    feature_column.numeric_column('v11'), tf.feature_column.numeric_column(
    'v12'), tf.feature_column.numeric_column('v13'), tf.feature_column.
    numeric_column('v14'), tf.feature_column.numeric_column('v15'), tf.
    feature_column.numeric_column('v16'), tf.feature_column.numeric_column(
    'v17'), tf.feature_column.numeric_column('v18'), tf.feature_column.
    numeric_column('v19'), tf.feature_column.numeric_column('v20'), tf.
    feature_column.numeric_column('v21'), tf.feature_column.numeric_column(
    'v22'), tf.feature_column.numeric_column('v23'), tf.feature_column.
    numeric_column('v24'), tf.feature_column.numeric_column('v25'), tf.
    feature_column.numeric_column('v26'), tf.feature_column.numeric_column(
    'v27'), tf.feature_column.numeric_column('v28'), tf.feature_column.
    numeric_column('amount')]
UNUSED_COLUMNS = set(CSV_COLUMNS) - {col.name for col in INPUT_COLUMNS} - {
    LABEL_COLUMN}


def build_estimator(config, embedding_size=8, hidden_units=None):
    """Build a wide and deep model for predicting income category.

  Wide and deep models use deep neural nets to learn high level abstractions
  about complex features or interactions between such features.
  These models then combined the outputs from the DNN with a linear regression
  performed on simpler features. This provides a balance between power and
  speed that is effective on many structured data problems.

  You can read more about wide and deep models here:
  https://research.googleblog.com/2016/06/wide-deep-learning-better-together-with.html

  To define model we can use the prebuilt DNNCombinedLinearClassifier class,
  and need only define the data transformations particular to our dataset, and
  then
  assign these (potentially) transformed features to either the DNN, or linear
  regression portion of the model.

  Args:
    config: tf.contrib.learn.RunConfig defining the runtime environment for the
      estimator (including model_dir).
    embedding_size: int, the number of dimensions used to represent categorical
      features when providing them as inputs to the DNN.
    hidden_units: [int], the layer sizes of the DNN (input layer first)
    learning_rate: float, the learning rate for the optimizer.
  Returns:
    A DNNCombinedLinearClassifier
  """
    (time, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15,
        v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, amount
        ) = INPUT_COLUMNS
    """Build an estimator."""
    amount_buckets = tf.feature_column.bucketized_column(amount, boundaries
        =[4, 8, 12, 15, 35, 75, 100, 200, 300, 1000])
    wide_columns = [amount_buckets]
    deep_columns = [time, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
        v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
        v26, v27, v28]
    hidden_units = [20, 15]
    return tf.contrib.learn.DNNLinearCombinedClassifier(config=config,
        linear_feature_columns=wide_columns, dnn_feature_columns=
        deep_columns, dnn_hidden_units=hidden_units or [100, 70, 50, 25],
        dnn_optimizer=tf.train.AdamOptimizer(),
        fix_global_step_increment_bug=True)
    """
  deep_columns = deep_columns + [amount]
  return tf.contrib.learn.DNNClassifier(
      config=config,
      feature_columns=deep_columns,
      hidden_units=hidden_units or [100, 70, 50, 25],
      optimizer=tf.train.AdamOptimizer()
      #optimizer=tf.train.ProximalAdagradOptimizer(
      #learning_rate=0.1,
      #l2_regularization_strength=0.001
	  #)
      )
  #"""


def parse_label_column(label_string_tensor):
    """Parses a string tensor into the label tensor
  Args:
    label_string_tensor: Tensor of dtype string. Result of parsing the
    CSV column specified by LABEL_COLUMN
  Returns:
    A Tensor of the same shape as label_string_tensor, should return
    an int64 Tensor representing the label index for classification tasks,
    and a float32 Tensor representing the value for a regression task.
  """
    table = tf.contrib.lookup.index_table_from_tensor(tf.constant(LABELS))
    return table.lookup(label_string_tensor)


def csv_serving_input_fn():
    """Build the serving inputs."""
    csv_row = tf.placeholder(shape=[None], dtype=tf.string)
    features = parse_csv(csv_row)
    features.pop(LABEL_COLUMN)
    return tf.contrib.learn.InputFnOps(features, None, {'csv_row': csv_row})


def example_serving_input_fn():
    """Build the serving inputs."""
    example_bytestring = tf.placeholder(shape=[None], dtype=tf.string)
    feature_scalars = tf.parse_example(example_bytestring, tf.
        feature_column.make_parse_example_spec(INPUT_COLUMNS))
    features = {key: tf.expand_dims(tensor, -1) for key, tensor in
        feature_scalars.iteritems()}
    return tf.contrib.learn.InputFnOps(features, None, {'example_proto':
        example_bytestring})


def json_serving_input_fn():
    """Build the serving inputs."""
    inputs = {}
    for feat in INPUT_COLUMNS:
        inputs[feat.name] = tf.placeholder(shape=[None], dtype=feat.dtype)
    features = {key: tf.expand_dims(tensor, -1) for key, tensor in inputs.
        iteritems()}
    return tf.contrib.learn.InputFnOps(features, None, inputs)


SERVING_FUNCTIONS = {'JSON': json_serving_input_fn, 'EXAMPLE':
    example_serving_input_fn, 'CSV': csv_serving_input_fn}


def parse_csv(rows_string_tensor):
    """Takes the string input tensor and returns a dict of rank-2 tensors."""
    row_columns = tf.expand_dims(rows_string_tensor, -1)
    columns = tf.decode_csv(row_columns, record_defaults=CSV_COLUMN_DEFAULTS)
    features = dict(zip(CSV_COLUMNS, columns))
    for col in UNUSED_COLUMNS:
        features.pop(col)
    return features


def generate_input_fn(filenames, num_epochs=None, shuffle=True,
    skip_header_lines=0, batch_size=200):
    """Generates an input function for training or evaluation.
  This uses the input pipeline based approach using file name queue
  to read data so that entire data is not loaded in memory.

  Args:
      filenames: [str] list of CSV files to read data from.
      num_epochs: int how many times through to read the data.
        If None will loop through data indefinitely
      shuffle: bool, whether or not to randomize the order of data.
        Controls randomization of both file order and line order within
        files.
      skip_header_lines: int set to non-zero in order to skip header lines
        in CSV files.
      batch_size: int First dimension size of the Tensors returned by
        input_fn
  Returns:
      A function () -> (features, indices) where features is a dictionary of
        Tensors, and indices is a single Tensor of label indices.
  """
    filename_queue = tf.train.string_input_producer(filenames, num_epochs=
        num_epochs, shuffle=shuffle)
    reader = tf.TextLineReader(skip_header_lines=skip_header_lines)
    _, rows = reader.read_up_to(filename_queue, num_records=batch_size)
    features = parse_csv(rows)
    if shuffle:
        features = tf.train.shuffle_batch(features, batch_size,
            min_after_dequeue=2 * batch_size + 1, capacity=batch_size * 10,
            num_threads=multiprocessing.cpu_count(), enqueue_many=True,
            allow_smaller_final_batch=True)
    else:
        features = tf.train.batch(features, batch_size, capacity=batch_size *
            10, num_threads=multiprocessing.cpu_count(), enqueue_many=True,
            allow_smaller_final_batch=True)
    return features, parse_label_column(features.pop(LABEL_COLUMN))

<mask token>
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import multiprocessing
import tensorflow as tf
CSV_COLUMNS = ['time', 'v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7', 'v8', 'v9',
    'v10', 'v11', 'v12', 'v13', 'v14', 'v15', 'v16', 'v17', 'v18', 'v19',
    'v20', 'v21', 'v22', 'v23', 'v24', 'v25', 'v26', 'v27', 'v28', 'amount',
    'class']
CSV_COLUMN_DEFAULTS = [[0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3
    ], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-
    0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3], [
    -0.3], [-0.3], [-0.3], [-0.3], [-0.3], [0.3], ['']]
LABEL_COLUMN = 'class'
LABELS = ['fraud', 'notfraud']
INPUT_COLUMNS = [tf.feature_column.numeric_column('time'), tf.
    feature_column.numeric_column('v1'), tf.feature_column.numeric_column(
    'v2'), tf.feature_column.numeric_column('v3'), tf.feature_column.
    numeric_column('v4'), tf.feature_column.numeric_column('v5'), tf.
    feature_column.numeric_column('v6'), tf.feature_column.numeric_column(
    'v7'), tf.feature_column.numeric_column('v8'), tf.feature_column.
    numeric_column('v9'), tf.feature_column.numeric_column('v10'), tf.
    feature_column.numeric_column('v11'), tf.feature_column.numeric_column(
    'v12'), tf.feature_column.numeric_column('v13'), tf.feature_column.
    numeric_column('v14'), tf.feature_column.numeric_column('v15'), tf.
    feature_column.numeric_column('v16'), tf.feature_column.numeric_column(
    'v17'), tf.feature_column.numeric_column('v18'), tf.feature_column.
    numeric_column('v19'), tf.feature_column.numeric_column('v20'), tf.
    feature_column.numeric_column('v21'), tf.feature_column.numeric_column(
    'v22'), tf.feature_column.numeric_column('v23'), tf.feature_column.
    numeric_column('v24'), tf.feature_column.numeric_column('v25'), tf.
    feature_column.numeric_column('v26'), tf.feature_column.numeric_column(
    'v27'), tf.feature_column.numeric_column('v28'), tf.feature_column.
    numeric_column('amount')]
UNUSED_COLUMNS = set(CSV_COLUMNS) - {col.name for col in INPUT_COLUMNS} - {
    LABEL_COLUMN}


def build_estimator(config, embedding_size=8, hidden_units=None):
    """Build a wide and deep model for predicting income category.

  Wide and deep models use deep neural nets to learn high level abstractions
  about complex features or interactions between such features.
  These models then combined the outputs from the DNN with a linear regression
  performed on simpler features. This provides a balance between power and
  speed that is effective on many structured data problems.

  You can read more about wide and deep models here:
  https://research.googleblog.com/2016/06/wide-deep-learning-better-together-with.html

  To define model we can use the prebuilt DNNCombinedLinearClassifier class,
  and need only define the data transformations particular to our dataset, and
  then
  assign these (potentially) transformed features to either the DNN, or linear
  regression portion of the model.

  Args:
    config: tf.contrib.learn.RunConfig defining the runtime environment for the
      estimator (including model_dir).
    embedding_size: int, the number of dimensions used to represent categorical
      features when providing them as inputs to the DNN.
    hidden_units: [int], the layer sizes of the DNN (input layer first)
    learning_rate: float, the learning rate for the optimizer.
  Returns:
    A DNNCombinedLinearClassifier
  """
    (time, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15,
        v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, amount
        ) = INPUT_COLUMNS
    """Build an estimator."""
    amount_buckets = tf.feature_column.bucketized_column(amount, boundaries
        =[4, 8, 12, 15, 35, 75, 100, 200, 300, 1000])
    wide_columns = [amount_buckets]
    deep_columns = [time, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
        v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
        v26, v27, v28]
    hidden_units = [20, 15]
    return tf.contrib.learn.DNNLinearCombinedClassifier(config=config,
        linear_feature_columns=wide_columns, dnn_feature_columns=
        deep_columns, dnn_hidden_units=hidden_units or [100, 70, 50, 25],
        dnn_optimizer=tf.train.AdamOptimizer(),
        fix_global_step_increment_bug=True)
    """
  deep_columns = deep_columns + [amount]
  return tf.contrib.learn.DNNClassifier(
      config=config,
      feature_columns=deep_columns,
      hidden_units=hidden_units or [100, 70, 50, 25],
      optimizer=tf.train.AdamOptimizer()
      #optimizer=tf.train.ProximalAdagradOptimizer(
      #learning_rate=0.1,
      #l2_regularization_strength=0.001
	  #)
      )
  #"""


def parse_label_column(label_string_tensor):
    """Parses a string tensor into the label tensor
  Args:
    label_string_tensor: Tensor of dtype string. Result of parsing the
    CSV column specified by LABEL_COLUMN
  Returns:
    A Tensor of the same shape as label_string_tensor, should return
    an int64 Tensor representing the label index for classification tasks,
    and a float32 Tensor representing the value for a regression task.
  """
    table = tf.contrib.lookup.index_table_from_tensor(tf.constant(LABELS))
    return table.lookup(label_string_tensor)


def csv_serving_input_fn():
    """Build the serving inputs."""
    csv_row = tf.placeholder(shape=[None], dtype=tf.string)
    features = parse_csv(csv_row)
    features.pop(LABEL_COLUMN)
    return tf.contrib.learn.InputFnOps(features, None, {'csv_row': csv_row})


def example_serving_input_fn():
    """Build the serving inputs."""
    example_bytestring = tf.placeholder(shape=[None], dtype=tf.string)
    feature_scalars = tf.parse_example(example_bytestring, tf.
        feature_column.make_parse_example_spec(INPUT_COLUMNS))
    features = {key: tf.expand_dims(tensor, -1) for key, tensor in
        feature_scalars.iteritems()}
    return tf.contrib.learn.InputFnOps(features, None, {'example_proto':
        example_bytestring})


def json_serving_input_fn():
    """Build the serving inputs."""
    inputs = {}
    for feat in INPUT_COLUMNS:
        inputs[feat.name] = tf.placeholder(shape=[None], dtype=feat.dtype)
    features = {key: tf.expand_dims(tensor, -1) for key, tensor in inputs.
        iteritems()}
    return tf.contrib.learn.InputFnOps(features, None, inputs)


SERVING_FUNCTIONS = {'JSON': json_serving_input_fn, 'EXAMPLE':
    example_serving_input_fn, 'CSV': csv_serving_input_fn}


def parse_csv(rows_string_tensor):
    """Takes the string input tensor and returns a dict of rank-2 tensors."""
    row_columns = tf.expand_dims(rows_string_tensor, -1)
    columns = tf.decode_csv(row_columns, record_defaults=CSV_COLUMN_DEFAULTS)
    features = dict(zip(CSV_COLUMNS, columns))
    for col in UNUSED_COLUMNS:
        features.pop(col)
    return features


def generate_input_fn(filenames, num_epochs=None, shuffle=True,
    skip_header_lines=0, batch_size=200):
    """Generates an input function for training or evaluation.
  This uses the input pipeline based approach using file name queue
  to read data so that entire data is not loaded in memory.

  Args:
      filenames: [str] list of CSV files to read data from.
      num_epochs: int how many times through to read the data.
        If None will loop through data indefinitely
      shuffle: bool, whether or not to randomize the order of data.
        Controls randomization of both file order and line order within
        files.
      skip_header_lines: int set to non-zero in order to skip header lines
        in CSV files.
      batch_size: int First dimension size of the Tensors returned by
        input_fn
  Returns:
      A function () -> (features, indices) where features is a dictionary of
        Tensors, and indices is a single Tensor of label indices.
  """
    filename_queue = tf.train.string_input_producer(filenames, num_epochs=
        num_epochs, shuffle=shuffle)
    reader = tf.TextLineReader(skip_header_lines=skip_header_lines)
    _, rows = reader.read_up_to(filename_queue, num_records=batch_size)
    features = parse_csv(rows)
    if shuffle:
        features = tf.train.shuffle_batch(features, batch_size,
            min_after_dequeue=2 * batch_size + 1, capacity=batch_size * 10,
            num_threads=multiprocessing.cpu_count(), enqueue_many=True,
            allow_smaller_final_batch=True)
    else:
        features = tf.train.batch(features, batch_size, capacity=batch_size *
            10, num_threads=multiprocessing.cpu_count(), enqueue_many=True,
            allow_smaller_final_batch=True)
    return features, parse_label_column(features.pop(LABEL_COLUMN))

# Copyright 2016 Google Inc. All Rights Reserved. Licensed under the Apache
# License, Version 2.0 (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

"""Define a Wide + Deep model for classification on structured data."""

# Obtained from: https://github.com/GoogleCloudPlatform/cloudml-samples/blob/master/census/estimator/trainer/model.py
# Modified to use a Wide and Deep Model over the Credit Card Fraud Dataset

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import multiprocessing

import tensorflow as tf


# Define the format of your input data including unused columns
CSV_COLUMNS = [ 'time', 'v1', 'v2','v3','v4','v5','v6','v7','v8','v9','v10','v11',
               'v12','v13','v14','v15','v16','v17','v18','v19','v20','v21','v22','v23','v24',
               'v25','v26','v27', 'v28','amount','class']               

# We include these defaults in order to force float castings during the CSV parsing
CSV_COLUMN_DEFAULTS = [[0.3], [-0.3], [-0.3], [-0.3], [-0.3], [-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],
                       [-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],
                       [-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[-0.3],[0.3],['']]


LABEL_COLUMN = 'class'

LABELS = ['fraud', 'notfraud']

# Define the initial ingestion of each feature used by your model.
# Additionally, provide metadata about the feature.
INPUT_COLUMNS = [


    # Continuous base columns.
    tf.feature_column.numeric_column('time'),
    tf.feature_column.numeric_column('v1'),
    tf.feature_column.numeric_column('v2'),
    tf.feature_column.numeric_column('v3'),
    tf.feature_column.numeric_column('v4'),
    tf.feature_column.numeric_column('v5'),    
    tf.feature_column.numeric_column('v6'),
    tf.feature_column.numeric_column('v7'),
    tf.feature_column.numeric_column('v8'),
    tf.feature_column.numeric_column('v9'),
    tf.feature_column.numeric_column('v10'),
    tf.feature_column.numeric_column('v11'),
    tf.feature_column.numeric_column('v12'),
    tf.feature_column.numeric_column('v13'),
    tf.feature_column.numeric_column('v14'),
    tf.feature_column.numeric_column('v15'),    
    tf.feature_column.numeric_column('v16'),
    tf.feature_column.numeric_column('v17'),
    tf.feature_column.numeric_column('v18'),
    tf.feature_column.numeric_column('v19'),
    tf.feature_column.numeric_column('v20'),
    tf.feature_column.numeric_column('v21'),
    tf.feature_column.numeric_column('v22'),
    tf.feature_column.numeric_column('v23'),
    tf.feature_column.numeric_column('v24'),
    tf.feature_column.numeric_column('v25'),    
    tf.feature_column.numeric_column('v26'),
    tf.feature_column.numeric_column('v27'),
    tf.feature_column.numeric_column('v28'),
    tf.feature_column.numeric_column('amount'),

]

UNUSED_COLUMNS = set(CSV_COLUMNS) - {col.name for col in INPUT_COLUMNS} - \
    {LABEL_COLUMN}


def build_estimator(config, embedding_size=8, hidden_units=None):
  """Build a wide and deep model for predicting income category.

  Wide and deep models use deep neural nets to learn high level abstractions
  about complex features or interactions between such features.
  These models then combined the outputs from the DNN with a linear regression
  performed on simpler features. This provides a balance between power and
  speed that is effective on many structured data problems.

  You can read more about wide and deep models here:
  https://research.googleblog.com/2016/06/wide-deep-learning-better-together-with.html

  To define model we can use the prebuilt DNNCombinedLinearClassifier class,
  and need only define the data transformations particular to our dataset, and
  then
  assign these (potentially) transformed features to either the DNN, or linear
  regression portion of the model.

  Args:
    config: tf.contrib.learn.RunConfig defining the runtime environment for the
      estimator (including model_dir).
    embedding_size: int, the number of dimensions used to represent categorical
      features when providing them as inputs to the DNN.
    hidden_units: [int], the layer sizes of the DNN (input layer first)
    learning_rate: float, the learning rate for the optimizer.
  Returns:
    A DNNCombinedLinearClassifier
  """
  (time, v1, v2, v3, v4,
   v5, v6, v7, v8, v9, 
   v10, v11, v12, v13, v14, 
   v15,v16,v17,v18,v19,v20,v21,
   v22,v23,v24,v25,v26,v27,v28, amount) = INPUT_COLUMNS
  """Build an estimator."""
  
  # Reused Transformations.
  # Continuous columns can be converted to categorical via bucketization
  # We use the (bucketized) amount column in the Wide part
  amount_buckets = tf.feature_column.bucketized_column(amount, boundaries=[4,8,12,15,35,75,100, 200, 300, 1000])

  # Wide columns and deep columns.
  wide_columns = [amount_buckets]

  # All the other CCF features will be used in the deep part
  deep_columns = [
   time, v1, v2, v3, v4,
   v5, v6, v7, v8, v9, 
   v10, v11, v12, v13, v14, 
   v15,v16,v17,v18,v19,v20,v21,
   v22,v23,v24,v25,v26,v27,v28
  ]
  
  # We hardcode here the models in order to avoid the exponential decaying model which is already implemented
  hidden_units = [20,15]

  # We can try either Wide and Deep models or Deep Neural Networks (DNN)
  #"""
  return tf.contrib.learn.DNNLinearCombinedClassifier(
      config=config,
      linear_feature_columns=wide_columns,
      dnn_feature_columns=deep_columns,
      dnn_hidden_units=hidden_units or [100, 70, 50, 25],
      dnn_optimizer=tf.train.AdamOptimizer(),
      fix_global_step_increment_bug=True
  )

  """
  deep_columns = deep_columns + [amount]
  return tf.contrib.learn.DNNClassifier(
      config=config,
      feature_columns=deep_columns,
      hidden_units=hidden_units or [100, 70, 50, 25],
      optimizer=tf.train.AdamOptimizer()
      #optimizer=tf.train.ProximalAdagradOptimizer(
      #learning_rate=0.1,
      #l2_regularization_strength=0.001
	  #)
      )
  #"""



def parse_label_column(label_string_tensor):
  """Parses a string tensor into the label tensor
  Args:
    label_string_tensor: Tensor of dtype string. Result of parsing the
    CSV column specified by LABEL_COLUMN
  Returns:
    A Tensor of the same shape as label_string_tensor, should return
    an int64 Tensor representing the label index for classification tasks,
    and a float32 Tensor representing the value for a regression task.
  """
  # Build a Hash Table inside the graph
  table = tf.contrib.lookup.index_table_from_tensor(tf.constant(LABELS))

  # Use the hash table to convert string labels to ints and one-hot encode
  return table.lookup(label_string_tensor)


# ************************************************************************
# YOU NEED NOT MODIFY ANYTHING BELOW HERE TO ADAPT THIS MODEL TO YOUR DATA
# ************************************************************************


def csv_serving_input_fn():
  """Build the serving inputs."""
  csv_row = tf.placeholder(
      shape=[None],
      dtype=tf.string
  )
  features = parse_csv(csv_row)
  features.pop(LABEL_COLUMN)
  return tf.contrib.learn.InputFnOps(features, None, {'csv_row': csv_row})


def example_serving_input_fn():
  """Build the serving inputs."""
  example_bytestring = tf.placeholder(
      shape=[None],
      dtype=tf.string,
  )
  feature_scalars = tf.parse_example(
      example_bytestring,
      tf.feature_column.make_parse_example_spec(INPUT_COLUMNS)
  )
  features = {
      key: tf.expand_dims(tensor, -1)
      for key, tensor in feature_scalars.iteritems()
  }
  return tf.contrib.learn.InputFnOps(
      features,
      None,  # labels
      {'example_proto': example_bytestring}
  )


def json_serving_input_fn():
  """Build the serving inputs."""
  inputs = {}
  for feat in INPUT_COLUMNS:
    inputs[feat.name] = tf.placeholder(shape=[None], dtype=feat.dtype)

  features = {
      key: tf.expand_dims(tensor, -1)
      for key, tensor in inputs.iteritems()
  }
  return tf.contrib.learn.InputFnOps(features, None, inputs)


SERVING_FUNCTIONS = {
    'JSON': json_serving_input_fn,
    'EXAMPLE': example_serving_input_fn,
    'CSV': csv_serving_input_fn
}


def parse_csv(rows_string_tensor):
  """Takes the string input tensor and returns a dict of rank-2 tensors."""

  # Takes a rank-1 tensor and converts it into rank-2 tensor
  # Example if the data is ['csv,line,1', 'csv,line,2', ..] to
  # [['csv,line,1'], ['csv,line,2']] which after parsing will result in a
  # tuple of tensors: [['csv'], ['csv']], [['line'], ['line']], [[1], [2]]
  row_columns = tf.expand_dims(rows_string_tensor, -1)
  columns = tf.decode_csv(row_columns, record_defaults=CSV_COLUMN_DEFAULTS)
  features = dict(zip(CSV_COLUMNS, columns))

  # Remove unused columns
  for col in UNUSED_COLUMNS:
    features.pop(col)
  return features


def generate_input_fn(filenames,
                      num_epochs=None,
                      shuffle=True,
                      skip_header_lines=0,
                      batch_size=200):
  """Generates an input function for training or evaluation.
  This uses the input pipeline based approach using file name queue
  to read data so that entire data is not loaded in memory.

  Args:
      filenames: [str] list of CSV files to read data from.
      num_epochs: int how many times through to read the data.
        If None will loop through data indefinitely
      shuffle: bool, whether or not to randomize the order of data.
        Controls randomization of both file order and line order within
        files.
      skip_header_lines: int set to non-zero in order to skip header lines
        in CSV files.
      batch_size: int First dimension size of the Tensors returned by
        input_fn
  Returns:
      A function () -> (features, indices) where features is a dictionary of
        Tensors, and indices is a single Tensor of label indices.
  """
  filename_queue = tf.train.string_input_producer(
      filenames, num_epochs=num_epochs, shuffle=shuffle)
  reader = tf.TextLineReader(skip_header_lines=skip_header_lines)

  _, rows = reader.read_up_to(filename_queue, num_records=batch_size)

  # Parse the CSV File
  features = parse_csv(rows)

  # This operation builds up a buffer of parsed tensors, so that parsing
  # input data doesn't block training
  # If requested it will also shuffle
  if shuffle:
    features = tf.train.shuffle_batch(
        features,
        batch_size,
        min_after_dequeue=2 * batch_size + 1,
        capacity=batch_size * 10,
        num_threads=multiprocessing.cpu_count(),
        enqueue_many=True,
        allow_smaller_final_batch=True
    )
  else:
    features = tf.train.batch(
        features,
        batch_size,
        capacity=batch_size * 10,
        num_threads=multiprocessing.cpu_count(),
        enqueue_many=True,
        allow_smaller_final_batch=True
    )

  return features, parse_label_column(features.pop(LABEL_COLUMN))
