Computes log probabilities using numerically stable trick .

def logits_to_log_prob(logits):
  """Computes log probabilities using numerically stable trick.

  This uses two numerical stability tricks:
  1) softmax(x) = softmax(x - c) where c is a constant applied to all
  arguments. If we set c = max(x) then the softmax is more numerically
  stable.
  2) log softmax(x) is not numerically stable, but we can stabilize it
  by using the identity log softmax(x) = x - log sum exp(x)

  Args:
    logits: Tensor of arbitrary shape whose last dimension contains logits.

  Returns:
    A tensor of the same shape as the input, but with corresponding log
    probabilities.
  """

  with tf.variable_scope('log_probabilities'):
    reduction_indices = len(logits.shape.as_list()) - 1
    max_logits = tf.reduce_max(
        logits, reduction_indices=reduction_indices, keep_dims=True)
    safe_logits = tf.subtract(logits, max_logits)
    sum_exp = tf.reduce_sum(
        tf.exp(safe_logits),
        reduction_indices=reduction_indices,
        keep_dims=True)
    log_probs = tf.subtract(safe_logits, tf.log(sum_exp))
  return log_probs

def logits_to_log_prob ( logits ) : with tf . variable_scope ( 'log_probabilities' ) : reduction_indices = len ( logits . shape . as_list ( ) ) - 1 max_logits = tf . reduce_max ( logits , reduction_indices = reduction_indices , keep_dims = True ) safe_logits = tf . subtract ( logits , max_logits ) sum_exp = tf . reduce_sum ( tf . exp ( safe_logits ) , reduction_indices = reduction_indices , keep_dims = True ) log_probs = tf . subtract ( safe_logits , tf . log ( sum_exp ) ) return log_probs


Returns a list of variables to restore for the specified list of methods .

def variables_to_restore(scope=None, strip_scope=False):
  """Returns a list of variables to restore for the specified list of methods.

  It is supposed that variable name starts with the method's scope (a prefix
  returned by _method_scope function).

  Args:
    methods_names: a list of names of configurable methods.
    strip_scope: if True will return variable names without method's scope.
      If methods_names is None will return names unchanged.
    model_scope: a scope for a whole model.

  Returns:
    a dictionary mapping variable names to variables for restore.
  """
  if scope:
    variable_map = {}
    method_variables = slim.get_variables_to_restore(include=[scope])
    for var in method_variables:
      if strip_scope:
        var_name = var.op.name[len(scope) + 1:]
      else:
        var_name = var.op.name
      variable_map[var_name] = var

    return variable_map
  else:
    return {v.op.name: v for v in slim.get_variables_to_restore()}

def variables_to_restore ( scope = None , strip_scope = False ) : if scope : variable_map = { } method_variables = slim . get_variables_to_restore ( include = [ scope ] ) for var in method_variables : if strip_scope : var_name = var . op . name [ len ( scope ) + 1 : ] else : var_name = var . op . name variable_map [ var_name ] = var return variable_map else : return { v . op . name : v for v in slim . get_variables_to_restore ( ) }


def define():
  """Define common flags."""
  # yapf: disable
  flags.DEFINE_integer('batch_size', 32,
                       'Batch size.')

  flags.DEFINE_integer('crop_width', None,
                       'Width of the central crop for images.')

  flags.DEFINE_integer('crop_height', None,
                       'Height of the central crop for images.')

  flags.DEFINE_string('train_log_dir', '/home/ucmed/opt/python/models-master/research/attention_ocr/python/logs',
                      'Directory where to write event logs.')

  flags.DEFINE_string('dataset_name', 'fsns',
                      'Name of the dataset. Supported: fsns')

  flags.DEFINE_string('split_name', 'train',
                      'Dataset split name to run evaluation for: test,train.')

  flags.DEFINE_string('dataset_dir', None,
                      'Dataset root folder.')

  flags.DEFINE_string('checkpoint', '',
                      'Path for checkpoint to restore weights from.')

  flags.DEFINE_string('master',
                      '',
                      'BNS name of the TensorFlow master to use.')

  # Model hyper parameters
  flags.DEFINE_float('learning_rate', 0.004,
                     'learning rate')

  flags.DEFINE_string('optimizer', 'momentum',
                      'the optimizer to use')

  flags.DEFINE_string('momentum', 0.9,
                      'momentum value for the momentum optimizer if used')

  flags.DEFINE_bool('use_augment_input', True,
                    'If True will use image augmentation')

  # Method hyper parameters
  # conv_tower_fn
  flags.DEFINE_string('final_endpoint', 'Mixed_5d',
                      'Endpoint to cut inception tower')

  # sequence_logit_fn
  flags.DEFINE_bool('use_attention', True,
                    'If True will use the attention mechanism')

  flags.DEFINE_bool('use_autoregression', True,
                    'If True will use autoregression (a feedback link)')

  flags.DEFINE_integer('num_lstm_units', 256,
                       'number of LSTM units for sequence LSTM')

  flags.DEFINE_float('weight_decay', 0.00004,
                     'weight decay for char prediction FC layers')

  flags.DEFINE_float('lstm_state_clip_value', 10.0,
                     'cell state is clipped by this value prior to the cell'
                     ' output activation')

  # 'sequence_loss_fn'
  flags.DEFINE_float('label_smoothing', 0.1,
                     'weight for label smoothing')

  flags.DEFINE_bool('ignore_nulls', True,
                    'ignore null characters for computing the loss')

  flags.DEFINE_bool('average_across_timesteps', False,
                    'divide the returned cost by the total label weight')


Returns sparse or dense loss function depending on the label_smoothing .

def get_softmax_loss_fn(label_smoothing):
  """Returns sparse or dense loss function depending on the label_smoothing.

    Args:
      label_smoothing: weight for label smoothing

    Returns:
      a function which takes labels and predictions as arguments and returns
      a softmax loss for the selected type of labels (sparse or dense).
    """
  if label_smoothing > 0:

    def loss_fn(labels, logits):
      return (tf.nn.softmax_cross_entropy_with_logits(
        logits=logits, labels=labels))
  else:

    def loss_fn(labels, logits):
      return tf.nn.sparse_softmax_cross_entropy_with_logits(
        logits=logits, labels=labels)

  return loss_fn


def __init__(self, charset, default_character='?'):
    """Creates a lookup table.

    Args:
      charset: a dictionary with id-to-character mapping.
    """
    mapping_strings = tf.constant(_dict_to_array(charset, default_character))
    self.table = tf.contrib.lookup.index_to_string_table_from_tensor(
      mapping=mapping_strings, default_value=default_character)

def __init__ ( self , charset , default_character = '?' ) : mapping_strings = tf . constant ( _dict_to_array ( charset , default_character ) ) self . table = tf . contrib . lookup . index_to_string_table_from_tensor ( mapping = mapping_strings , default_value = default_character )


Returns a string corresponding to a sequence of character ids .

def get_text(self, ids):
    """Returns a string corresponding to a sequence of character ids.

        Args:
          ids: a tensor with shape [batch_size, max_sequence_length]
        """
    return tf.reduce_join(
      self.table.lookup(tf.to_int64(ids)), reduction_indices=1)

def get_text ( self , ids ) : return tf . reduce_join ( self . table . lookup ( tf . to_int64 ( ids ) ) , reduction_indices = 1 )


def __init__(self,
               num_char_classes,
               seq_length,
               num_views,
               null_code,
               mparams=None,
               charset=None):
    """Initialized model parameters.

    Args:
      num_char_classes: size of character set.
      seq_length: number of characters in a sequence.
      num_views: Number of views (conv towers) to use.
      null_code: A character code corresponding to a character which
        indicates end of a sequence.
      mparams: a dictionary with hyper parameters for methods,  keys -
        function names, values - corresponding namedtuples.
      charset: an optional dictionary with a mapping between character ids and
        utf8 strings. If specified the OutputEndpoints.predicted_text will
        utf8 encoded strings corresponding to the character ids returned by
        OutputEndpoints.predicted_chars (by default the predicted_text contains
        an empty vector).
        NOTE: Make sure you call tf.tables_initializer().run() if the charset
        specified.
    """
    super(Model, self).__init__()
    self._params = ModelParams(
      num_char_classes=num_char_classes,
      seq_length=seq_length,
      num_views=num_views,
      null_code=null_code)
    self._mparams = self.default_mparams()
    if mparams:
      self._mparams.update(mparams)
    self._charset = charset

def __init__ ( self , num_char_classes , seq_length , num_views , null_code , mparams = None , charset = None ) : super ( Model , self ) . __init__ ( ) self . _params = ModelParams ( num_char_classes = num_char_classes , seq_length = seq_length , num_views = num_views , null_code = null_code ) self . _mparams = self . default_mparams ( ) if mparams : self . _mparams . update ( mparams ) self . _charset = charset


Computes convolutional features using the InceptionV3 model .

def conv_tower_fn(self, images, is_training=True, reuse=None):
    """Computes convolutional features using the InceptionV3 model.

    Args:
      images: A tensor of shape [batch_size, height, width, channels].
      is_training: whether is training or not.
      reuse: whether or not the network and its variables should be reused. To
        be able to reuse 'scope' must be given.

    Returns:
      A tensor of shape [batch_size, OH, OW, N], where OWxOH is resolution of
      output feature map and N is number of output features (depends on the
      network architecture).
    """
    mparams = self._mparams['conv_tower_fn']
    logging.debug('Using final_endpoint=%s', mparams.final_endpoint)
    with tf.variable_scope('conv_tower_fn/INCE'):
      if reuse:
        tf.get_variable_scope().reuse_variables()
      with slim.arg_scope(inception.inception_v3_arg_scope()):
        with slim.arg_scope([slim.batch_norm, slim.dropout],
                            is_training=is_training):
          net, _ = inception.inception_v3_base(
            images, final_endpoint=mparams.final_endpoint)
      return net

def
conv_tower_fn
(
self
,
images
,
is_training
=
True
,
reuse
=
)
:
mparams
=
self
.
_mparams
[
'conv_tower_fn'
]
logging
.
debug
(
'Using final_endpoint=%s'
,
mparams
.
final_endpoint
)
with
tf
.
variable_scope
(
'conv_tower_fn/INCE'
)
:
if
reuse
:
tf
.
get_variable_scope
(
)
.
reuse_variables
(
)
with
slim
.
arg_scope
(
inception
.
inception_v3_arg_scope
(
)
)
:
with
slim
.
arg_scope
(
[
slim
.
batch_norm
,
slim
.
dropout
]
,
is_training
=
is_training
)
:
net
,
_
=
inception
.
inception_v3_base
(
images
,
final_endpoint
=
mparams
.
final_endpoint
)
return
net


Splits an input tensor into a list of tensors ( features ) .

def _create_lstm_inputs(self, net):
    """Splits an input tensor into a list of tensors (features).

    Args:
      net: A feature map of shape [batch_size, num_features, feature_size].

    Raises:
      AssertionError: if num_features is less than seq_length.

    Returns:
      A list with seq_length tensors of shape [batch_size, feature_size]
    """
    num_features = net.get_shape().dims[1].value
    if num_features < self._params.seq_length:
      raise AssertionError('Incorrect dimension #1 of input tensor'
                           ' %d should be bigger than %d (shape=%s)' %
                           (num_features, self._params.seq_length,
                            net.get_shape()))
    elif num_features > self._params.seq_length:
      logging.warning('Ignoring some features: use %d of %d (shape=%s)',
                      self._params.seq_length, num_features, net.get_shape())
      net = tf.slice(net, [0, 0, 0], [-1, self._params.seq_length, -1])

    return tf.unstack(net, axis=1)


Max pool across all nets in spatial dimensions .

def max_pool_views(self, nets_list):
    """Max pool across all nets in spatial dimensions.

    Args:
      nets_list: A list of 4D tensors with identical size.

    Returns:
      A tensor with the same size as any input tensors.
    """
    batch_size, height, width, num_features = [
      d.value for d in nets_list[0].get_shape().dims
    ]
    xy_flat_shape = (batch_size, 1, height * width, num_features)
    nets_for_merge = []
    with tf.variable_scope('max_pool_views', values=nets_list):
      for net in nets_list:
        nets_for_merge.append(tf.reshape(net, xy_flat_shape))
      merged_net = tf.concat(nets_for_merge, 1)
      net = slim.max_pool2d(
        merged_net, kernel_size=[len(nets_list), 1], stride=1)
      net = tf.reshape(net, (batch_size, height, width, num_features))
    return net

def max_pool_views ( self , nets_list ) : batch_size , height , width , num_features = [ d . value for d in nets_list [ 0 ] . get_shape ( ) . dims ] xy_flat_shape = ( batch_size , 1 , height * width , num_features ) nets_for_merge = [ ] with tf . variable_scope ( 'max_pool_views' , values = nets_list ) : for net in nets_list : nets_for_merge . append ( tf . reshape ( net , xy_flat_shape ) ) merged_net = tf . concat ( nets_for_merge , 1 ) net = slim . max_pool2d ( merged_net , kernel_size = [ len ( nets_list ) , 1 ] , stride = 1 ) net = tf . reshape ( net , ( batch_size , height , width , num_features ) ) return net


Combines output of multiple convolutional towers into a single tensor .

def pool_views_fn(self, nets):
    """Combines output of multiple convolutional towers into a single tensor.

    It stacks towers one on top another (in height dim) in a 4x1 grid.
    The order is arbitrary design choice and shouldn't matter much.

    Args:
      nets: list of tensors of shape=[batch_size, height, width, num_features].

    Returns:
      A tensor of shape [batch_size, seq_length, features_size].
    """
    with tf.variable_scope('pool_views_fn/STCK'):
      net = tf.concat(nets, 1)
      batch_size = net.get_shape().dims[0].value
      feature_size = net.get_shape().dims[3].value
      return tf.reshape(net, [batch_size, -1, feature_size])

def pool_views_fn ( self , nets ) : with tf . variable_scope ( 'pool_views_fn/STCK' ) : net = tf . concat ( nets , 1 ) batch_size = net . get_shape ( ) . dims [ 0 ] . value feature_size = net . get_shape ( ) . dims [ 3 ] . value return tf . reshape ( net , [ batch_size , - 1 , feature_size ] )


Returns confidence scores ( softmax values ) for predicted characters .

def char_predictions(self, chars_logit):
    """Returns confidence scores (softmax values) for predicted characters.

    Args:
      chars_logit: chars logits, a tensor with shape
        [batch_size x seq_length x num_char_classes]

    Returns:
      A tuple (ids, log_prob, scores), where:
        ids - predicted characters, a int32 tensor with shape
          [batch_size x seq_length];
        log_prob - a log probability of all characters, a float tensor with
          shape [batch_size, seq_length, num_char_classes];
        scores - corresponding confidence scores for characters, a float
        tensor
          with shape [batch_size x seq_length].
    """
    log_prob = utils.logits_to_log_prob(chars_logit)
    ids = tf.to_int32(tf.argmax(log_prob, axis=2), name='predicted_chars')
    mask = tf.cast(
      slim.one_hot_encoding(ids, self._params.num_char_classes), tf.bool)
    all_scores = tf.nn.softmax(chars_logit)
    selected_scores = tf.boolean_mask(all_scores, mask, name='char_scores')
    scores = tf.reshape(selected_scores, shape=(-1, self._params.seq_length))
    return ids, log_prob, scores

def char_predictions ( self , chars_logit ) : log_prob = utils . logits_to_log_prob ( chars_logit ) ids = tf . to_int32 ( tf . argmax ( log_prob , axis = 2 ) , name = 'predicted_chars' ) mask = tf . cast ( slim . one_hot_encoding ( ids , self . _params . num_char_classes ) , tf . bool ) all_scores = tf . nn . softmax ( chars_logit ) selected_scores = tf . boolean_mask ( all_scores , mask , name = 'char_scores' ) scores = tf . reshape ( selected_scores , shape = ( - 1 , self . _params . seq_length ) ) return ids , log_prob , scores


Adds one - hot encoding of coordinates to different views in the networks .

def encode_coordinates_fn(self, net):
    """Adds one-hot encoding of coordinates to different views in the networks.

    For each "pixel" of a feature map it adds a onehot encoded x and y
    coordinates.

    Args:
      net: a tensor of shape=[batch_size, height, width, num_features]

    Returns:
      a tensor with the same height and width, but altered feature_size.
    """
    mparams = self._mparams['encode_coordinates_fn']
    if mparams.enabled:
      batch_size, h, w, _ = net.shape.as_list()
      x, y = tf.meshgrid(tf.range(w), tf.range(h))
      w_loc = slim.one_hot_encoding(x, num_classes=w)
      h_loc = slim.one_hot_encoding(y, num_classes=h)
      loc = tf.concat([h_loc, w_loc], 2)
      loc = tf.tile(tf.expand_dims(loc, 0), [batch_size, 1, 1, 1])
      return tf.concat([net, loc], 3)
    else:
      return net

def encode_coordinates_fn ( self , net ) : mparams = self . _mparams [ 'encode_coordinates_fn' ] if mparams . enabled : batch_size , h , w , _ = net . shape . as_list ( ) x , y = tf . meshgrid ( tf . range ( w ) , tf . range ( h ) ) w_loc = slim . one_hot_encoding ( x , num_classes = w ) h_loc = slim . one_hot_encoding ( y , num_classes = h ) loc = tf . concat ( [ h_loc , w_loc ] , 2 ) loc = tf . tile ( tf . expand_dims ( loc , 0 ) , [ batch_size , 1 , 1 , 1 ] ) return tf . concat ( [ net , loc ] , 3 ) else : return net


Creates a base part of the Model ( no gradients losses or summaries ) .

def create_base(self,
                  images,
                  labels_one_hot,
                  scope='AttentionOcr_v1',
                  reuse=None):
    """Creates a base part of the Model (no gradients, losses or summaries).

    Args:
      images: A tensor of shape [batch_size, height, width, channels].
      labels_one_hot: Optional (can be None) one-hot encoding for ground truth
        labels. If provided the function will create a model for training.
      scope: Optional variable_scope.
      reuse: whether or not the network and its variables should be reused. To
        be able to reuse 'scope' must be given.

    Returns:
      A named tuple OutputEndpoints.
    """
    logging.debug('images: %s', images)
    is_training = labels_one_hot is not None

    with tf.variable_scope(scope, reuse=reuse):
      views = tf.split(
        value=images, num_or_size_splits=self._params.num_views, axis=2)
      logging.debug('Views=%d single view: %s', len(views), views[0])

      nets = [
        self.conv_tower_fn(v, is_training, reuse=(i != 0))
        for i, v in enumerate(views)
      ]
      logging.debug('Conv tower: %s', nets[0])

      nets = [self.encode_coordinates_fn(net) for net in nets]
      logging.debug('Conv tower w/ encoded coordinates: %s', nets[0])

      net = self.pool_views_fn(nets)
      logging.debug('Pooled views: %s', net)

      chars_logit = self.sequence_logit_fn(net, labels_one_hot)
      logging.debug('chars_logit: %s', chars_logit)

      predicted_chars, chars_log_prob, predicted_scores = (
        self.char_predictions(chars_logit))
      if self._charset:
        character_mapper = CharsetMapper(self._charset)
        predicted_text = character_mapper.get_text(predicted_chars)
      else:
        predicted_text = tf.constant([])
    return OutputEndpoints(
      chars_logit=chars_logit,
      chars_log_prob=chars_log_prob,
      predicted_chars=predicted_chars,
      predicted_scores=predicted_scores,
      predicted_text=predicted_text)

def
create_base
(
self
,
images
,
labels_one_hot
,
scope
=
'AttentionOcr_v1'
,
reuse
=
)
:
logging
.
debug
(
'images: %s'
,
images
)
is_training
=
labels_one_hot
is
not
with
tf
.
variable_scope
(
scope
,
reuse
=
reuse
)
:
views
=
tf
.
split
(
value
=
images
,
num_or_size_splits
=
self
.
_params
.
num_views
,
axis
=
2
)
logging
.
debug
(
'Views=%d single view: %s'
,
len
(
views
)
,
views
[
0
]
)
nets
=
[
self
.
conv_tower_fn
(
v
,
is_training
,
reuse
=
(
i
!=
0
)
)
for
i
,
v
in
enumerate
(
views
)
]
logging
.
debug
(
'Conv tower: %s'
,
nets
[
0
]
)
nets
=
[
self
.
encode_coordinates_fn
(
net
)
for
net
in
nets
]
logging
.
debug
(
'Conv tower w/ encoded coordinates: %s'
,
nets
[
0
]
)
net
=
self
.
pool_views_fn
(
nets
)
logging
.
debug
(
'Pooled views: %s'
,
net
)
chars_logit
=
self
.
sequence_logit_fn
(
net
,
labels_one_hot
)
logging
.
debug
(
'chars_logit: %s'
,
chars_logit
)
predicted_chars
,
chars_log_prob
,
predicted_scores
=
(
self
.
char_predictions
(
chars_logit
)
)
if
self
.
_charset
:
character_mapper
=
CharsetMapper
(
self
.
_charset
)
predicted_text
=
character_mapper
.
get_text
(
predicted_chars
)
else
:
predicted_text
=
tf
.
constant
(
[
]
)
return
OutputEndpoints
(
chars_logit
=
chars_logit
,
chars_log_prob
=
chars_log_prob
,
predicted_chars
=
predicted_chars
,
predicted_scores
=
predicted_scores
,
predicted_text
=
predicted_text
)


Creates all losses required to train the model .

def create_loss(self, data, endpoints):
    """Creates all losses required to train the model.

    Args:
      data: InputEndpoints namedtuple.
      endpoints: Model namedtuple.

    Returns:
      Total loss.
    """
    # NOTE: the return value of ModelLoss is not used directly for the
    # gradient computation because under the hood it calls slim.losses.AddLoss,
    # which registers the loss in an internal collection and later returns it
    # as part of GetTotalLoss. We need to use total loss because model may have
    # multiple losses including regularization losses.
    self.sequence_loss_fn(endpoints.chars_logit, data.labels)
    total_loss = slim.losses.get_total_loss()
    tf.summary.scalar('TotalLoss', total_loss)
    return total_loss


Applies a label smoothing regularization .

def label_smoothing_regularization(self, chars_labels, weight=0.1):
    """Applies a label smoothing regularization.

    Uses the same method as in https://arxiv.org/abs/1512.00567.

    Args:
      chars_labels: ground truth ids of charactes,
        shape=[batch_size, seq_length];
      weight: label-smoothing regularization weight.

    Returns:
      A sensor with the same shape as the input.
    """
    one_hot_labels = tf.one_hot(
      chars_labels, depth=self._params.num_char_classes, axis=-1)
    pos_weight = 1.0 - weight
    neg_weight = weight / self._params.num_char_classes
    return one_hot_labels * pos_weight + neg_weight

def label_smoothing_regularization ( self , chars_labels , weight = 0.1 ) : one_hot_labels = tf . one_hot ( chars_labels , depth = self . _params . num_char_classes , axis = - 1 ) pos_weight = 1.0 - weight neg_weight = weight / self . _params . num_char_classes return one_hot_labels * pos_weight + neg_weight


def sequence_loss_fn(self, chars_logits, chars_labels):
    """Loss function for char sequence.

    Depending on values of hyper parameters it applies label smoothing and can
    also ignore all null chars after the first one.

    Args:
      chars_logits: logits for predicted characters,
        shape=[batch_size, seq_length, num_char_classes];
      chars_labels: ground truth ids of characters,
        shape=[batch_size, seq_length];
      mparams: method hyper parameters.

    Returns:
      A Tensor with shape [batch_size] - the log-perplexity for each sequence.
    """
    mparams = self._mparams['sequence_loss_fn']
    with tf.variable_scope('sequence_loss_fn/SLF'):
      if mparams.label_smoothing > 0:
        smoothed_one_hot_labels = self.label_smoothing_regularization(
          chars_labels, mparams.label_smoothing)
        labels_list = tf.unstack(smoothed_one_hot_labels, axis=1)
      else:
        # NOTE: in case of sparse softmax we are not using one-hot
        # encoding.
        labels_list = tf.unstack(chars_labels, axis=1)

      batch_size, seq_length, _ = chars_logits.shape.as_list()
      if mparams.ignore_nulls:
        weights = tf.ones((batch_size, seq_length), dtype=tf.float32)
      else:
        # Suppose that reject character is the last in the charset.
        reject_char = tf.constant(
          self._params.num_char_classes - 1,
          shape=(batch_size, seq_length),
          dtype=tf.int64)
        known_char = tf.not_equal(chars_labels, reject_char)
        weights = tf.to_float(known_char)

      logits_list = tf.unstack(chars_logits, axis=1)
      weights_list = tf.unstack(weights, axis=1)
      loss = tf.contrib.legacy_seq2seq.sequence_loss(
        logits_list,
        labels_list,
        weights_list,
        softmax_loss_function=get_softmax_loss_fn(mparams.label_smoothing),
        average_across_timesteps=mparams.average_across_timesteps)
      tf.losses.add_loss(loss)
      return loss


def create_summaries(self, data, endpoints, charset, is_training):
    """Creates all summaries for the model.

    Args:
      data: InputEndpoints namedtuple.
      endpoints: OutputEndpoints namedtuple.
      charset: A dictionary with mapping between character codes and
        unicode characters. Use the one provided by a dataset.charset.
      is_training: If True will create summary prefixes for training job,
        otherwise - for evaluation.

    Returns:
      A list of evaluation ops
    """

    def sname(label):
      prefix = 'train' if is_training else 'eval'
      return '%s/%s' % (prefix, label)

    max_outputs = 4
    # TODO(gorban): uncomment, when tf.summary.text released.
    charset_mapper = CharsetMapper(charset)
    pr_text = charset_mapper.get_text(
    endpoints.predicted_chars[:max_outputs,:])
    tf.summary.text(sname('text/pr'), pr_text)
    gt_text = charset_mapper.get_text(data.labels[:max_outputs,:])
    tf.summary.text(sname('text/gt'), gt_text)
    tf.summary.image(sname('image'), data.images, max_outputs=max_outputs)

    if is_training:
      tf.summary.image(
        sname('image/orig'), data.images_orig, max_outputs=max_outputs)
      for var in tf.trainable_variables():
        tf.summary.histogram(var.op.name, var)
      return None

    else:
      names_to_values = {}
      names_to_updates = {}

      def use_metric(name, value_update_tuple):
        names_to_values[name] = value_update_tuple[0]
        names_to_updates[name] = value_update_tuple[1]

      use_metric('CharacterAccuracy',
                 metrics.char_accuracy(
                   endpoints.predicted_chars,
                   data.labels,
                   streaming=True,
                   rej_char=self._params.null_code))
      # Sequence accuracy computed by cutting sequence at the first null char
      use_metric('SequenceAccuracy',
                 metrics.sequence_accuracy(
                   endpoints.predicted_chars,
                   data.labels,
                   streaming=True,
                   rej_char=self._params.null_code))

      for name, value in names_to_values.items():
        summary_name = 'eval/' + name
        tf.summary.scalar(summary_name, tf.Print(value, [value], summary_name))
      return list(names_to_updates.values())


Creates an init operations to restore weights from various checkpoints .

def create_init_fn_to_restore(self, master_checkpoint,
                                inception_checkpoint=None):
    """Creates an init operations to restore weights from various checkpoints.

    Args:
      master_checkpoint: path to a checkpoint which contains all weights for
        the whole model.
      inception_checkpoint: path to a checkpoint which contains weights for the
        inception part only.

    Returns:
      a function to run initialization ops.
    """
    all_assign_ops = []
    all_feed_dict = {}

    def assign_from_checkpoint(variables, checkpoint):
      logging.info('Request to re-store %d weights from %s',
                   len(variables), checkpoint)
      if not variables:
        logging.error('Can\'t find any variables to restore.')
        sys.exit(1)
      assign_op, feed_dict = slim.assign_from_checkpoint(checkpoint, variables)
      all_assign_ops.append(assign_op)
      all_feed_dict.update(feed_dict)

    logging.info('variables_to_restore:\n%s' % utils.variables_to_restore().keys())
    logging.info('moving_average_variables:\n%s' % [v.op.name for v in tf.moving_average_variables()])
    logging.info('trainable_variables:\n%s' % [v.op.name for v in tf.trainable_variables()])
    if master_checkpoint:
      assign_from_checkpoint(utils.variables_to_restore(), master_checkpoint)

    if inception_checkpoint:
      variables = utils.variables_to_restore(
        'AttentionOcr_v1/conv_tower_fn/INCE', strip_scope=True)
      assign_from_checkpoint(variables, inception_checkpoint)

    def init_assign_fn(sess):
      logging.info('Restoring checkpoint(s)')
      sess.run(all_assign_ops, all_feed_dict)

    return init_assign_fn


Generates orthonormal matrices with random values .

def orthogonal_initializer(shape, dtype=tf.float32, *args, **kwargs):
  """Generates orthonormal matrices with random values.

  Orthonormal initialization is important for RNNs:
    http://arxiv.org/abs/1312.6120
    http://smerity.com/articles/2016/orthogonal_init.html

  For non-square shapes the returned matrix will be semi-orthonormal: if the
  number of columns exceeds the number of rows, then the rows are orthonormal
  vectors; but if the number of rows exceeds the number of columns, then the
  columns are orthonormal vectors.

  We use SVD decomposition to generate an orthonormal matrix with random
  values. The same way as it is done in the Lasagne library for Theano. Note
  that both u and v returned by the svd are orthogonal and random. We just need
  to pick one with the right shape.

  Args:
    shape: a shape of the tensor matrix to initialize.
    dtype: a dtype of the initialized tensor.
    *args: not used.
    **kwargs: not used.

  Returns:
    An initialized tensor.
  """
  del args
  del kwargs
  flat_shape = (shape[0], np.prod(shape[1:]))
  w = np.random.randn(*flat_shape)
  u, _, v = np.linalg.svd(w, full_matrices=False)
  w = u if u.shape == flat_shape else v
  return tf.constant(w.reshape(shape), dtype=dtype)

def orthogonal_initializer ( shape , dtype = tf . float32 , * args , * * kwargs ) : del args del kwargs flat_shape = ( shape [ 0 ] , np . prod ( shape [ 1 : ] ) ) w = np . random . randn ( * flat_shape ) u , _ , v = np . linalg . svd ( w , full_matrices = False ) w = u if u . shape == flat_shape else v return tf . constant ( w . reshape ( shape ) , dtype = dtype )


A convenience function to get a layer class based on requirements .

def get_layer_class(use_attention, use_autoregression):
  """A convenience function to get a layer class based on requirements.

  Args:
    use_attention: if True a returned class will use attention.
    use_autoregression: if True a returned class will use auto regression.

  Returns:
    One of available sequence layers (child classes for SequenceLayerBase).
  """
  if use_attention and use_autoregression:
    layer_class = AttentionWithAutoregression
  elif use_attention and not use_autoregression:
    layer_class = Attention
  elif not use_attention and not use_autoregression:
    layer_class = NetSlice
  elif not use_attention and use_autoregression:
    layer_class = NetSliceWithAutoregression
  else:
    raise AssertionError('Unsupported sequence layer class')

  logging.debug('Use %s as a layer class', layer_class.__name__)
  return layer_class

def
get_layer_class
(
use_attention
,
use_autoregression
)
:
if
use_attention
and
use_autoregression
:
layer_class
=
AttentionWithAutoregression
elif
use_attention
and
not
use_autoregression
:
layer_class
=
Attention
elif
not
use_attention
and
not
use_autoregression
:
layer_class
=
NetSlice
elif
not
use_attention
and
use_autoregression
:
layer_class
=
NetSliceWithAutoregression
else
:
raise
AssertionError
(
'Unsupported sequence layer class'
)
logging
.
debug
(
'Use %s as a layer class'
,
layer_class
.
__name__
)
return
layer_class


Stores argument in member variable for further use .

def __init__(self, net, labels_one_hot, model_params, method_params):
    """Stores argument in member variable for further use.

    Args:
      net: A tensor with shape [batch_size, num_features, feature_size] which
        contains some extracted image features.
      labels_one_hot: An optional (can be None) ground truth labels for the
        input features. Is a tensor with shape
        [batch_size, seq_length, num_char_classes]
      model_params: A namedtuple with model parameters (model.ModelParams).
      method_params: A SequenceLayerParams instance.
    """
    self._params = model_params
    self._mparams = method_params
    self._net = net
    self._labels_one_hot = labels_one_hot
    self._batch_size = net.get_shape().dims[0].value

    # Initialize parameters for char logits which will be computed on the fly
    # inside an LSTM decoder.
    self._char_logits = {}
    regularizer = slim.l2_regularizer(self._mparams.weight_decay)
    self._softmax_w = slim.model_variable(
        'softmax_w',
        [self._mparams.num_lstm_units, self._params.num_char_classes],
        initializer=orthogonal_initializer,
        regularizer=regularizer)
    self._softmax_b = slim.model_variable(
        'softmax_b', [self._params.num_char_classes],
        initializer=tf.zeros_initializer(),
        regularizer=regularizer)


Returns a sample to be used to predict a character during training .

def get_train_input(self, prev, i):
    """Returns a sample to be used to predict a character during training.

    This function is used as a loop_function for an RNN decoder.

    Args:
      prev: output tensor from previous step of the RNN. A tensor with shape:
        [batch_size, num_char_classes].
      i: index of a character in the output sequence.

    Returns:
      A tensor with shape [batch_size, ?] - depth depends on implementation
      details.
    """
    pass

def get_train_input ( self , prev , i ) : pass


Returns a sample to be used to predict a character during inference .

def get_eval_input(self, prev, i):
    """Returns a sample to be used to predict a character during inference.

    This function is used as a loop_function for an RNN decoder.

    Args:
      prev: output tensor from previous step of the RNN. A tensor with shape:
        [batch_size, num_char_classes].
      i: index of a character in the output sequence.

    Returns:
      A tensor with shape [batch_size, ?] - depth depends on implementation
      details.
    """
    raise AssertionError('Not implemented')

def
get_eval_input
(
self
,
prev
,
i
)
:
raise
AssertionError
(
'Not implemented'
)


def unroll_cell(self, decoder_inputs, initial_state, loop_function, cell):
    """Unrolls an RNN cell for all inputs.

    This is a placeholder to call some RNN decoder. It has a similar to
    tf.seq2seq.rnn_decode interface.

    Args:
      decoder_inputs: A list of 2D Tensors* [batch_size x input_size]. In fact,
        most of existing decoders in presence of a loop_function use only the
        first element to determine batch_size and length of the list to
        determine number of steps.
      initial_state: 2D Tensor with shape [batch_size x cell.state_size].
      loop_function: function will be applied to the i-th output in order to
        generate the i+1-st input (see self.get_input).
      cell: rnn_cell.RNNCell defining the cell function and size.

    Returns:
      A tuple of the form (outputs, state), where:
        outputs: A list of character logits of the same length as
        decoder_inputs of 2D Tensors with shape [batch_size x num_characters].
        state: The state of each cell at the final time-step.
          It is a 2D Tensor of shape [batch_size x cell.state_size].
    """
    pass

def unroll_cell ( self , decoder_inputs , initial_state , loop_function , cell ) : pass


Returns True if the layer is created for training stage .

def is_training(self):
    """Returns True if the layer is created for training stage."""
    return self._labels_one_hot is not None

def is_training ( self ) : return self . _labels_one_hot is not None


Creates logits for a character if required .

def char_logit(self, inputs, char_index):
    """Creates logits for a character if required.

    Args:
      inputs: A tensor with shape [batch_size, ?] (depth is implementation
        dependent).
      char_index: A integer index of a character in the output sequence.

    Returns:
      A tensor with shape [batch_size, num_char_classes]
    """
    if char_index not in self._char_logits:
      self._char_logits[char_index] = tf.nn.xw_plus_b(inputs, self._softmax_w,
                                                      self._softmax_b)
    return self._char_logits[char_index]

def char_logit ( self , inputs , char_index ) : if char_index not in self . _char_logits : self . _char_logits [ char_index ] = tf . nn . xw_plus_b ( inputs , self . _softmax_w , self . _softmax_b ) return self . _char_logits [ char_index ]


Creates one hot encoding for a logit of a character .

def char_one_hot(self, logit):
    """Creates one hot encoding for a logit of a character.

    Args:
      logit: A tensor with shape [batch_size, num_char_classes].

    Returns:
      A tensor with shape [batch_size, num_char_classes]
    """
    prediction = tf.argmax(logit, axis=1)
    return slim.one_hot_encoding(prediction, self._params.num_char_classes)

def char_one_hot ( self , logit ) : prediction = tf . argmax ( logit , axis = 1 ) return slim . one_hot_encoding ( prediction , self . _params . num_char_classes )


A wrapper for get_train_input and get_eval_input .

def get_input(self, prev, i):
    """A wrapper for get_train_input and get_eval_input.

    Args:
      prev: output tensor from previous step of the RNN. A tensor with shape:
        [batch_size, num_char_classes].
      i: index of a character in the output sequence.

    Returns:
      A tensor with shape [batch_size, ?] - depth depends on implementation
      details.
    """
    if self.is_training():
      return self.get_train_input(prev, i)
    else:
      return self.get_eval_input(prev, i)

def get_input ( self , prev , i ) : if self . is_training ( ) : return self . get_train_input ( prev , i ) else : return self . get_eval_input ( prev , i )


Creates character sequence logits for a net specified in the constructor .

def create_logits(self):
    """Creates character sequence logits for a net specified in the constructor.

    A "main" method for the sequence layer which glues together all pieces.

    Returns:
      A tensor with shape [batch_size, seq_length, num_char_classes].
    """
    with tf.variable_scope('LSTM'):
      first_label = self.get_input(prev=None, i=0)
      decoder_inputs = [first_label] + [None] * (self._params.seq_length - 1)
      lstm_cell = tf.contrib.rnn.LSTMCell(
          self._mparams.num_lstm_units,
          use_peepholes=False,
          cell_clip=self._mparams.lstm_state_clip_value,
          state_is_tuple=True,
          initializer=orthogonal_initializer)
      lstm_outputs, _ = self.unroll_cell(
          decoder_inputs=decoder_inputs,
          initial_state=lstm_cell.zero_state(self._batch_size, tf.float32),
          loop_function=self.get_input,
          cell=lstm_cell)

    with tf.variable_scope('logits'):
      logits_list = [
          tf.expand_dims(self.char_logit(logit, i), dim=1)
          for i, logit in enumerate(lstm_outputs)
      ]

    return tf.concat(logits_list, 1)

def create_logits ( self ) : with tf . variable_scope ( 'LSTM' ) : first_label = self . get_input ( prev = None , i = 0 ) decoder_inputs = [ first_label ] + [ None ] * ( self . _params . seq_length - 1 ) lstm_cell = tf . contrib . rnn . LSTMCell ( self . _mparams . num_lstm_units , use_peepholes = False , cell_clip = self . _mparams . lstm_state_clip_value , state_is_tuple = True , initializer = orthogonal_initializer ) lstm_outputs , _ = self . unroll_cell ( decoder_inputs = decoder_inputs , initial_state = lstm_cell . zero_state ( self . _batch_size , tf . float32 ) , loop_function = self . get_input , cell = lstm_cell ) with tf . variable_scope ( 'logits' ) : logits_list = [ tf . expand_dims ( self . char_logit ( logit , i ) , dim = 1 ) for i , logit in enumerate ( lstm_outputs ) ] return tf . concat ( logits_list , 1 )


Returns a subset of image features for a character .

def get_image_feature(self, char_index):
    """Returns a subset of image features for a character.

    Args:
      char_index: an index of a character.

    Returns:
      A tensor with shape [batch_size, ?]. The output depth depends on the
      depth of input net.
    """
    batch_size, features_num, _ = [d.value for d in self._net.get_shape()]
    slice_len = int(features_num / self._params.seq_length)
    # In case when features_num != seq_length, we just pick a subset of image
    # features, this choice is arbitrary and there is no intuitive geometrical
    # interpretation. If features_num is not dividable by seq_length there will
    # be unused image features.
    net_slice = self._net[:, char_index:char_index + slice_len, :]
    feature = tf.reshape(net_slice, [batch_size, -1])
    logging.debug('Image feature: %s', feature)
    return feature


See SequenceLayerBase . get_eval_input for details .

def get_eval_input(self, prev, i):
    """See SequenceLayerBase.get_eval_input for details."""
    del prev
    return self.get_image_feature(i)

def get_eval_input ( self , prev , i ) : del prev return self . get_image_feature ( i )


See SequenceLayerBase . get_train_input for details .

def get_train_input(self, prev, i):
    """See SequenceLayerBase.get_train_input for details."""
    return self.get_eval_input(prev, i)

def get_train_input ( self , prev , i ) : return self . get_eval_input ( prev , i )


See SequenceLayerBase . unroll_cell for details .

def unroll_cell(self, decoder_inputs, initial_state, loop_function, cell):
    """See SequenceLayerBase.unroll_cell for details."""
    return tf.contrib.legacy_seq2seq.rnn_decoder(
        decoder_inputs=decoder_inputs,
        initial_state=initial_state,
        cell=cell,
        loop_function=self.get_input)

def unroll_cell ( self , decoder_inputs , initial_state , loop_function , cell ) : return tf . contrib . legacy_seq2seq . rnn_decoder ( decoder_inputs = decoder_inputs , initial_state = initial_state , cell = cell , loop_function = self . get_input )


def get_eval_input(self, prev, i):
    """See SequenceLayerBase.get_eval_input for details."""
    if i == 0:
      prev = self._zero_label
    else:
      logit = self.char_logit(prev, char_index=i - 1)
      prev = self.char_one_hot(logit)
    image_feature = self.get_image_feature(char_index=i)
    return tf.concat([image_feature, prev], 1)

def get_eval_input ( self , prev , i ) : if i == 0 : prev = self . _zero_label else : logit = self . char_logit ( prev , char_index = i - 1 ) prev = self . char_one_hot ( logit ) image_feature = self . get_image_feature ( char_index = i ) return tf . concat ( [ image_feature , prev ] , 1 )


def get_train_input(self, prev, i):
    """See SequenceLayerBase.get_train_input for details."""
    if i == 0:
      prev = self._zero_label
    else:
      prev = self._labels_one_hot[:, i - 1, :]
    image_feature = self.get_image_feature(i)
    return tf.concat([image_feature, prev], 1)

def get_train_input ( self , prev , i ) : if i == 0 : prev = self . _zero_label else : prev = self . _labels_one_hot [ : , i - 1 , : ] image_feature = self . get_image_feature ( i ) return tf . concat ( [ image_feature , prev ] , 1 )


def get_eval_input(self, prev, i):
    """See SequenceLayerBase.get_eval_input for details."""
    del prev, i
    # The attention_decoder will fetch image features from the net, no need for
    # extra inputs.
    return self._zero_label


def get_train_input(self, prev, i):
    """See SequenceLayerBase.get_train_input for details."""
    if i == 0:
      return self._zero_label
    else:
      # TODO(gorban): update to gradually introduce gt labels.
      return self._labels_one_hot[:, i - 1, :]


def get_eval_input(self, prev, i):
    """See SequenceLayerBase.get_eval_input for details."""
    if i == 0:
      return self._zero_label
    else:
      logit = self.char_logit(prev, char_index=i - 1)
      return self.char_one_hot(logit)

def get_eval_input ( self , prev , i ) : if i == 0 : return self . _zero_label else : logit = self . char_logit ( prev , char_index = i - 1 ) return self . char_one_hot ( logit )


Creates optimized based on the specified flags .

def create_optimizer(hparams):
  """Creates optimized based on the specified flags."""
  if hparams.optimizer == 'momentum':
    optimizer = tf.train.MomentumOptimizer(
        hparams.learning_rate, momentum=hparams.momentum)
  elif hparams.optimizer == 'adam':
    optimizer = tf.train.AdamOptimizer(hparams.learning_rate)
  elif hparams.optimizer == 'adadelta':
    optimizer = tf.train.AdadeltaOptimizer(hparams.learning_rate)
  elif hparams.optimizer == 'adagrad':
    optimizer = tf.train.AdagradOptimizer(hparams.learning_rate)
  elif hparams.optimizer == 'rmsprop':
    optimizer = tf.train.RMSPropOptimizer(
        hparams.learning_rate, momentum=hparams.momentum)
  return optimizer

def create_optimizer ( hparams ) : if hparams . optimizer == 'momentum' : optimizer = tf . train . MomentumOptimizer ( hparams . learning_rate , momentum = hparams . momentum ) elif hparams . optimizer == 'adam' : optimizer = tf . train . AdamOptimizer ( hparams . learning_rate ) elif hparams . optimizer == 'adadelta' : optimizer = tf . train . AdadeltaOptimizer ( hparams . learning_rate ) elif hparams . optimizer == 'adagrad' : optimizer = tf . train . AdagradOptimizer ( hparams . learning_rate ) elif hparams . optimizer == 'rmsprop' : optimizer = tf . train . RMSPropOptimizer ( hparams . learning_rate , momentum = hparams . momentum ) return optimizer


Wraps slim . learning . train to run a training loop .

def train(loss, init_fn, hparams):
  """Wraps slim.learning.train to run a training loop.

  Args:
    loss: a loss tensor
    init_fn: A callable to be executed after all other initialization is done.
    hparams: a model hyper parameters
  """
  optimizer = create_optimizer(hparams)

  if FLAGS.sync_replicas:
    replica_id = tf.constant(FLAGS.task, tf.int32, shape=())
    optimizer = tf.LegacySyncReplicasOptimizer(
        opt=optimizer,
        replicas_to_aggregate=FLAGS.replicas_to_aggregate,
        replica_id=replica_id,
        total_num_replicas=FLAGS.total_num_replicas)
    sync_optimizer = optimizer
    startup_delay_steps = 0
  else:
    startup_delay_steps = 0
    sync_optimizer = None

  train_op = slim.learning.create_train_op(
      loss,
      optimizer,
      summarize_gradients=True,
      clip_gradient_norm=FLAGS.clip_gradient_norm)

  slim.learning.train(
      train_op=train_op,
      logdir=FLAGS.train_log_dir,
      graph=loss.graph,
      master=FLAGS.master,
      is_chief=(FLAGS.task == 0),
      number_of_steps=FLAGS.max_number_of_steps,
      save_summaries_secs=FLAGS.save_summaries_secs,
      save_interval_secs=FLAGS.save_interval_secs,
      startup_delay_steps=startup_delay_steps,
      sync_optimizer=sync_optimizer,
      init_fn=init_fn)

def train ( loss , init_fn , hparams ) : optimizer = create_optimizer ( hparams ) if FLAGS . sync_replicas : replica_id = tf . constant ( FLAGS . task , tf . int32 , shape = ( ) ) optimizer = tf . LegacySyncReplicasOptimizer ( opt = optimizer , replicas_to_aggregate = FLAGS . replicas_to_aggregate , replica_id = replica_id , total_num_replicas = FLAGS . total_num_replicas ) sync_optimizer = optimizer startup_delay_steps = 0 else : startup_delay_steps = 0 sync_optimizer = None train_op = slim . learning . create_train_op ( loss , optimizer , summarize_gradients = True , clip_gradient_norm = FLAGS . clip_gradient_norm ) slim . learning . train ( train_op = train_op , logdir = FLAGS . train_log_dir , graph = loss . graph , master = FLAGS . master , is_chief = ( FLAGS . task == 0 ) , number_of_steps = FLAGS . max_number_of_steps , save_summaries_secs = FLAGS . save_summaries_secs , save_interval_secs = FLAGS . save_interval_secs , startup_delay_steps = startup_delay_steps , sync_optimizer = sync_optimizer , init_fn = init_fn )


Augmentation the image with a random modification .

def augment_image(image):
  """Augmentation the image with a random modification.

  Args:
    image: input Tensor image of rank 3, with the last dimension
           of size 3.

  Returns:
    Distorted Tensor image of the same shape.
  """
  with tf.variable_scope('AugmentImage'):
    height = image.get_shape().dims[0].value
    width = image.get_shape().dims[1].value

    # Random crop cut from the street sign image, resized to the same size.
    # Assures that the crop is covers at least 0.8 area of the input image.
    bbox_begin, bbox_size, _ = tf.image.sample_distorted_bounding_box(
        tf.shape(image),
        bounding_boxes=tf.zeros([0, 0, 4]),
        min_object_covered=0.8,
        aspect_ratio_range=[0.8, 1.2],
        area_range=[0.8, 1.0],
        use_image_if_no_bounding_boxes=True)
    distorted_image = tf.slice(image, bbox_begin, bbox_size)

    # Randomly chooses one of the 4 interpolation methods
    distorted_image = inception_preprocessing.apply_with_random_selector(
        distorted_image,
        lambda x, method: tf.image.resize_images(x, [height, width], method),
        num_cases=4)
    distorted_image.set_shape([height, width, 3])

    # Color distortion
    distorted_image = inception_preprocessing.apply_with_random_selector(
        distorted_image,
        functools.partial(
            inception_preprocessing.distort_color, fast_mode=False),
        num_cases=4)
    distorted_image = tf.clip_by_value(distorted_image, -1.5, 1.5)

  return distorted_image


Returns a central crop for the specified size of an image .

def central_crop(image, crop_size):
  """Returns a central crop for the specified size of an image.

  Args:
    image: A tensor with shape [height, width, channels]
    crop_size: A tuple (crop_width, crop_height)

  Returns:
    A tensor of shape [crop_height, crop_width, channels].
  """
  with tf.variable_scope('CentralCrop'):
    target_width, target_height = crop_size
    image_height, image_width = tf.shape(image)[0], tf.shape(image)[1]
    assert_op1 = tf.Assert(
        tf.greater_equal(image_height, target_height),
        ['image_height < target_height', image_height, target_height])
    assert_op2 = tf.Assert(
        tf.greater_equal(image_width, target_width),
        ['image_width < target_width', image_width, target_width])
    with tf.control_dependencies([assert_op1, assert_op2]):
      offset_width = (image_width - target_width) / 2
      offset_height = (image_height - target_height) / 2
      return tf.image.crop_to_bounding_box(image, offset_height, offset_width,
                                           target_height, target_width)

def
central_crop
(
image
,
crop_size
)
:
with
tf
.
variable_scope
(
'CentralCrop'
)
:
target_width
,
target_height
=
crop_size
image_height
,
image_width
=
tf
.
shape
(
image
)
[
0
]
,
tf
.
shape
(
image
)
[
1
]
assert_op1
=
tf
.
Assert
(
tf
.
greater_equal
(
image_height
,
target_height
)
,
[
'image_height < target_height'
,
image_height
,
target_height
]
)
assert_op2
=
tf
.
Assert
(
tf
.
greater_equal
(
image_width
,
target_width
)
,
[
'image_width < target_width'
,
image_width
,
target_width
]
)
with
tf
.
control_dependencies
(
[
assert_op1
,
assert_op2
]
)
:
offset_width
=
(
image_width
target_width
)
/
2
offset_height
=
(
image_height
target_height
)
/
2
return
tf
.
image
.
crop_to_bounding_box
(
image
,
offset_height
,
offset_width
,
target_height
,
target_width
)


Normalizes image to have values in a narrow range around zero .

def preprocess_image(image, augment=False, central_crop_size=None,
                     num_towers=4):
  """Normalizes image to have values in a narrow range around zero.

  Args:
    image: a [H x W x 3] uint8 tensor.
    augment: optional, if True do random image distortion.
    central_crop_size: A tuple (crop_width, crop_height).
    num_towers: optional, number of shots of the same image in the input image.

  Returns:
    A float32 tensor of shape [H x W x 3] with RGB values in the required
    range.
  """
  with tf.variable_scope('PreprocessImage'):
    image = tf.image.convert_image_dtype(image, dtype=tf.float32)
    if augment or central_crop_size:
      if num_towers == 1:
        images = [image]
      else:
        images = tf.split(value=image, num_or_size_splits=num_towers, axis=1)
      if central_crop_size:
        view_crop_size = (central_crop_size[0] / num_towers,
                          central_crop_size[1])
        images = [central_crop(img, view_crop_size) for img in images]
      if augment:
        images = [augment_image(img) for img in images]
      image = tf.concat(images, 1)

    image = tf.subtract(image, 0.5)
    image = tf.multiply(image, 2.5)

  return image

def preprocess_image ( image , augment = False , central_crop_size = None , num_towers = 4 ) : with tf . variable_scope ( 'PreprocessImage' ) : image = tf . image . convert_image_dtype ( image , dtype = tf . float32 ) if augment or central_crop_size : if num_towers == 1 : images = [ image ] else : images = tf . split ( value = image , num_or_size_splits = num_towers , axis = 1 ) if central_crop_size : view_crop_size = ( central_crop_size [ 0 ] / num_towers , central_crop_size [ 1 ] ) images = [ central_crop ( img , view_crop_size ) for img in images ] if augment : images = [ augment_image ( img ) for img in images ] image = tf . concat ( images , 1 ) image = tf . subtract ( image , 0.5 ) image = tf . multiply ( image , 2.5 ) return image


Wraps calls to DatasetDataProviders and shuffle_batch .

def get_data(dataset,
             batch_size,
             augment=False,
             central_crop_size=None,
             shuffle_config=None,
             shuffle=True):
  """Wraps calls to DatasetDataProviders and shuffle_batch.

  For more details about supported Dataset objects refer to datasets/fsns.py.

  Args:
    dataset: a slim.data.dataset.Dataset object.
    batch_size: number of samples per batch.
    augment: optional, if True does random image distortion.
    central_crop_size: A CharLogittuple (crop_width, crop_height).
    shuffle_config: A namedtuple ShuffleBatchConfig.
    shuffle: if True use data shuffling.

  Returns:

  """
  if not shuffle_config:
    shuffle_config = DEFAULT_SHUFFLE_CONFIG

  provider = slim.dataset_data_provider.DatasetDataProvider(
      dataset,
      shuffle=shuffle,
      common_queue_capacity=2 * batch_size,
      common_queue_min=batch_size)
  image_orig, label = provider.get(['image', 'label'])

  image = preprocess_image(
      image_orig, augment, central_crop_size, num_towers=dataset.num_of_views)
  label_one_hot = slim.one_hot_encoding(label, dataset.num_char_classes)

  images, images_orig, labels, labels_one_hot = (tf.train.shuffle_batch(
      [image, image_orig, label, label_one_hot],
      batch_size=batch_size,
      num_threads=shuffle_config.num_batching_threads,
      capacity=shuffle_config.queue_capacity,
      min_after_dequeue=shuffle_config.min_after_dequeue))

  return InputEndpoints(
      images=images,
      images_orig=images_orig,
      labels=labels,
      labels_one_hot=labels_one_hot)

def get_data ( dataset , batch_size , augment = False , central_crop_size = None , shuffle_config = None , shuffle = True ) : if not shuffle_config : shuffle_config = DEFAULT_SHUFFLE_CONFIG provider = slim . dataset_data_provider . DatasetDataProvider ( dataset , shuffle = shuffle , common_queue_capacity = 2 * batch_size , common_queue_min = batch_size ) image_orig , label = provider . get ( [ 'image' , 'label' ] ) image = preprocess_image ( image_orig , augment , central_crop_size , num_towers = dataset . num_of_views ) label_one_hot = slim . one_hot_encoding ( label , dataset . num_char_classes ) images , images_orig , labels , labels_one_hot = ( tf . train . shuffle_batch ( [ image , image_orig , label , label_one_hot ] , batch_size = batch_size , num_threads = shuffle_config . num_batching_threads , capacity = shuffle_config . queue_capacity , min_after_dequeue = shuffle_config . min_after_dequeue ) ) return InputEndpoints ( images = images , images_orig = images_orig , labels = labels , labels_one_hot = labels_one_hot )


def char_accuracy(predictions, targets, rej_char, streaming=False):
  """Computes character level accuracy.

  Both predictions and targets should have the same shape
  [batch_size x seq_length].

  Args:
    predictions: predicted characters ids.
    targets: ground truth character ids.
    rej_char: the character id used to mark an empty element (end of sequence).
    streaming: if True, uses the streaming mean from the slim.metric module.

  Returns:
    a update_ops for execution and value tensor whose value on evaluation
    returns the total character accuracy.
  """
  with tf.variable_scope('CharAccuracy'):
    predictions.get_shape().assert_is_compatible_with(targets.get_shape())

    targets = tf.to_int32(targets)
    const_rej_char = tf.constant(rej_char, shape=targets.get_shape())
    weights = tf.to_float(tf.not_equal(targets, const_rej_char))
    correct_chars = tf.to_float(tf.equal(predictions, targets))
    accuracy_per_example = tf.div(
        tf.reduce_sum(tf.multiply(correct_chars, weights), 1),
        tf.reduce_sum(weights, 1))
    if streaming:
      return tf.contrib.metrics.streaming_mean(accuracy_per_example)
    else:
      return tf.reduce_mean(accuracy_per_example)

def char_accuracy ( predictions , targets , rej_char , streaming = False ) : with tf . variable_scope ( 'CharAccuracy' ) : predictions . get_shape ( ) . assert_is_compatible_with ( targets . get_shape ( ) ) targets = tf . to_int32 ( targets ) const_rej_char = tf . constant ( rej_char , shape = targets . get_shape ( ) ) weights = tf . to_float ( tf . not_equal ( targets , const_rej_char ) ) correct_chars = tf . to_float ( tf . equal ( predictions , targets ) ) accuracy_per_example = tf . div ( tf . reduce_sum ( tf . multiply ( correct_chars , weights ) , 1 ) , tf . reduce_sum ( weights , 1 ) ) if streaming : return tf . contrib . metrics . streaming_mean ( accuracy_per_example ) else : return tf . reduce_mean ( accuracy_per_example )


def sequence_accuracy(predictions, targets, rej_char, streaming=False):
  """Computes sequence level accuracy.

  Both input tensors should have the same shape: [batch_size x seq_length].

  Args:
    predictions: predicted character classes.
    targets: ground truth character classes.
    rej_char: the character id used to mark empty element (end of sequence).
    streaming: if True, uses the streaming mean from the slim.metric module.

  Returns:
    a update_ops for execution and value tensor whose value on evaluation
    returns the total sequence accuracy.
  """

  with tf.variable_scope('SequenceAccuracy'):
    predictions.get_shape().assert_is_compatible_with(targets.get_shape())

    targets = tf.to_int32(targets)
    const_rej_char = tf.constant(
        rej_char, shape=targets.get_shape(), dtype=tf.int32)
    include_mask = tf.not_equal(targets, const_rej_char)
    include_predictions = tf.to_int32(
        tf.where(include_mask, predictions,
                 tf.zeros_like(predictions) + rej_char))
    correct_chars = tf.to_float(tf.equal(include_predictions, targets))
    correct_chars_counts = tf.cast(
        tf.reduce_sum(correct_chars, reduction_indices=[1]), dtype=tf.int32)
    target_length = targets.get_shape().dims[1].value
    target_chars_counts = tf.constant(
        target_length, shape=correct_chars_counts.get_shape())
    accuracy_per_example = tf.to_float(
        tf.equal(correct_chars_counts, target_chars_counts))
    if streaming:
      return tf.contrib.metrics.streaming_mean(accuracy_per_example)
    else:
      return tf.reduce_mean(accuracy_per_example)

def sequence_accuracy ( predictions , targets , rej_char , streaming = False ) : with tf . variable_scope ( 'SequenceAccuracy' ) : predictions . get_shape ( ) . assert_is_compatible_with ( targets . get_shape ( ) ) targets = tf . to_int32 ( targets ) const_rej_char = tf . constant ( rej_char , shape = targets . get_shape ( ) , dtype = tf . int32 ) include_mask = tf . not_equal ( targets , const_rej_char ) include_predictions = tf . to_int32 ( tf . where ( include_mask , predictions , tf . zeros_like ( predictions ) + rej_char ) ) correct_chars = tf . to_float ( tf . equal ( include_predictions , targets ) ) correct_chars_counts = tf . cast ( tf . reduce_sum ( correct_chars , reduction_indices = [ 1 ] ) , dtype = tf . int32 ) target_length = targets . get_shape ( ) . dims [ 1 ] . value target_chars_counts = tf . constant ( target_length , shape = correct_chars_counts . get_shape ( ) ) accuracy_per_example = tf . to_float ( tf . equal ( correct_chars_counts , target_chars_counts ) ) if streaming : return tf . contrib . metrics . streaming_mean ( accuracy_per_example ) else : return tf . reduce_mean ( accuracy_per_example )


Computes func ( x sel ) with sel sampled from [ 0 ... num_cases - 1 ] .

def apply_with_random_selector(x, func, num_cases):
  """Computes func(x, sel), with sel sampled from [0...num_cases-1].

  Args:
    x: input Tensor.
    func: Python function to apply.
    num_cases: Python int32, number of cases to sample sel from.

  Returns:
    The result of func(x, sel), where func receives the value of the
    selector as a python integer, but sel is sampled dynamically.
  """
  sel = tf.random_uniform([], maxval=num_cases, dtype=tf.int32)
  # Pass the real x only to one of the func calls.
  return control_flow_ops.merge([
      func(control_flow_ops.switch(x, tf.equal(sel, case))[1], case)
      for case in range(num_cases)
  ])[0]


def distort_color(image, color_ordering=0, fast_mode=True, scope=None):
  """Distort the color of a Tensor image.

  Each color distortion is non-commutative and thus ordering of the color ops
  matters. Ideally we would randomly permute the ordering of the color ops.
  Rather then adding that level of complication, we select a distinct ordering
  of color ops for each preprocessing thread.

  Args:
    image: 3-D Tensor containing single image in [0, 1].
    color_ordering: Python int, a type of distortion (valid values: 0-3).
    fast_mode: Avoids slower ops (random_hue and random_contrast)
    scope: Optional scope for name_scope.
  Returns:
    3-D Tensor color-distorted image on range [0, 1]
  Raises:
    ValueError: if color_ordering not in [0, 3]
  """
  with tf.name_scope(scope, 'distort_color', [image]):
    if fast_mode:
      if color_ordering == 0:
        image = tf.image.random_brightness(image, max_delta=32. / 255.)
        image = tf.image.random_saturation(image, lower=0.5, upper=1.5)
      else:
        image = tf.image.random_saturation(image, lower=0.5, upper=1.5)
        image = tf.image.random_brightness(image, max_delta=32. / 255.)
    else:
      if color_ordering == 0:
        image = tf.image.random_brightness(image, max_delta=32. / 255.)
        image = tf.image.random_saturation(image, lower=0.5, upper=1.5)
        image = tf.image.random_hue(image, max_delta=0.2)
        image = tf.image.random_contrast(image, lower=0.5, upper=1.5)
      elif color_ordering == 1:
        image = tf.image.random_saturation(image, lower=0.5, upper=1.5)
        image = tf.image.random_brightness(image, max_delta=32. / 255.)
        image = tf.image.random_contrast(image, lower=0.5, upper=1.5)
        image = tf.image.random_hue(image, max_delta=0.2)
      elif color_ordering == 2:
        image = tf.image.random_contrast(image, lower=0.5, upper=1.5)
        image = tf.image.random_hue(image, max_delta=0.2)
        image = tf.image.random_brightness(image, max_delta=32. / 255.)
        image = tf.image.random_saturation(image, lower=0.5, upper=1.5)
      elif color_ordering == 3:
        image = tf.image.random_hue(image, max_delta=0.2)
        image = tf.image.random_saturation(image, lower=0.5, upper=1.5)
        image = tf.image.random_contrast(image, lower=0.5, upper=1.5)
        image = tf.image.random_brightness(image, max_delta=32. / 255.)
      else:
        raise ValueError('color_ordering must be in [0, 3]')

    # The random_* ops do not necessarily clamp.
    return tf.clip_by_value(image, 0.0, 1.0)


Generates cropped_image using a one of the bboxes randomly distorted .

def distorted_bounding_box_crop(image,
                                bbox,
                                min_object_covered=0.1,
                                aspect_ratio_range=(0.75, 1.33),
                                area_range=(0.05, 1.0),
                                max_attempts=100,
                                scope=None):
  """Generates cropped_image using a one of the bboxes randomly distorted.

  See `tf.image.sample_distorted_bounding_box` for more documentation.

  Args:
    image: 3-D Tensor of image (it will be converted to floats in [0, 1]).
    bbox: 3-D float Tensor of bounding boxes arranged [1, num_boxes, coords]
      where each coordinate is [0, 1) and the coordinates are arranged
      as [ymin, xmin, ymax, xmax]. If num_boxes is 0 then it would use the
      whole image.
    min_object_covered: An optional `float`. Defaults to `0.1`. The cropped
      area of the image must contain at least this fraction of any bounding box
      supplied.
    aspect_ratio_range: An optional list of `floats`. The cropped area of the
      image must have an aspect ratio = width / height within this range.
    area_range: An optional list of `floats`. The cropped area of the image
      must contain a fraction of the supplied image within in this range.
    max_attempts: An optional `int`. Number of attempts at generating a cropped
      region of the image of the specified constraints. After `max_attempts`
      failures, return the entire image.
    scope: Optional scope for name_scope.
  Returns:
    A tuple, a 3-D Tensor cropped_image and the distorted bbox
  """
  with tf.name_scope(scope, 'distorted_bounding_box_crop', [image, bbox]):
    # Each bounding box has shape [1, num_boxes, box coords] and
    # the coordinates are ordered [ymin, xmin, ymax, xmax].

    # A large fraction of image datasets contain a human-annotated bounding
    # box delineating the region of the image containing the object of interest.
    # We choose to create a new bounding box for the object which is a randomly
    # distorted version of the human-annotated bounding box that obeys an
    # allowed range of aspect ratios, sizes and overlap with the human-annotated
    # bounding box. If no box is supplied, then we assume the bounding box is
    # the entire image.
    sample_distorted_bounding_box = tf.image.sample_distorted_bounding_box(
        tf.shape(image),
        bounding_boxes=bbox,
        min_object_covered=min_object_covered,
        aspect_ratio_range=aspect_ratio_range,
        area_range=area_range,
        max_attempts=max_attempts,
        use_image_if_no_bounding_boxes=True)
    bbox_begin, bbox_size, distort_bbox = sample_distorted_bounding_box

    # Crop the image to the specified bounding box.
    cropped_image = tf.slice(image, bbox_begin, bbox_size)
    return cropped_image, distort_bbox


Distort one image for training a network .

def preprocess_for_train(image,
                         height,
                         width,
                         bbox,
                         fast_mode=True,
                         scope=None):
  """Distort one image for training a network.

  Distorting images provides a useful technique for augmenting the data
  set during training in order to make the network invariant to aspects
  of the image that do not effect the label.

  Additionally it would create image_summaries to display the different
  transformations applied to the image.

  Args:
    image: 3-D Tensor of image. If dtype is tf.float32 then the range should be
      [0, 1], otherwise it would converted to tf.float32 assuming that the range
      is [0, MAX], where MAX is largest positive representable number for
      int(8/16/32) data type (see `tf.image.convert_image_dtype` for details).
    height: integer
    width: integer
    bbox: 3-D float Tensor of bounding boxes arranged [1, num_boxes, coords]
      where each coordinate is [0, 1) and the coordinates are arranged
      as [ymin, xmin, ymax, xmax].
    fast_mode: Optional boolean, if True avoids slower transformations (i.e.
      bi-cubic resizing, random_hue or random_contrast).
    scope: Optional scope for name_scope.
  Returns:
    3-D float Tensor of distorted image used for training with range [-1, 1].
  """
  with tf.name_scope(scope, 'distort_image', [image, height, width, bbox]):
    if bbox is None:
      bbox = tf.constant(
          [0.0, 0.0, 1.0, 1.0], dtype=tf.float32, shape=[1, 1, 4])
    if image.dtype != tf.float32:
      image = tf.image.convert_image_dtype(image, dtype=tf.float32)
    # Each bounding box has shape [1, num_boxes, box coords] and
    # the coordinates are ordered [ymin, xmin, ymax, xmax].
    image_with_box = tf.image.draw_bounding_boxes(
        tf.expand_dims(image, 0), bbox)
    tf.summary.image('image_with_bounding_boxes', image_with_box)

    distorted_image, distorted_bbox = distorted_bounding_box_crop(image, bbox)
    # Restore the shape since the dynamic slice based upon the bbox_size loses
    # the third dimension.
    distorted_image.set_shape([None, None, 3])
    image_with_distorted_box = tf.image.draw_bounding_boxes(
        tf.expand_dims(image, 0), distorted_bbox)
    tf.summary.image('images_with_distorted_bounding_box',
                     image_with_distorted_box)

    # This resizing operation may distort the images because the aspect
    # ratio is not respected. We select a resize method in a round robin
    # fashion based on the thread number.
    # Note that ResizeMethod contains 4 enumerated resizing methods.

    # We select only 1 case for fast_mode bilinear.
    num_resize_cases = 1 if fast_mode else 4
    distorted_image = apply_with_random_selector(
        distorted_image,
        lambda x, method: tf.image.resize_images(x, [height, width], method=method),
        num_cases=num_resize_cases)

    tf.summary.image('cropped_resized_image',
                     tf.expand_dims(distorted_image, 0))

    # Randomly flip the image horizontally.
    distorted_image = tf.image.random_flip_left_right(distorted_image)

    # Randomly distort the colors. There are 4 ways to do it.
    distorted_image = apply_with_random_selector(
        distorted_image,
        lambda x, ordering: distort_color(x, ordering, fast_mode),
        num_cases=4)

    tf.summary.image('final_distorted_image',
                     tf.expand_dims(distorted_image, 0))
    distorted_image = tf.subtract(distorted_image, 0.5)
    distorted_image = tf.multiply(distorted_image, 2.0)
    return distorted_image


def preprocess_for_eval(image,
                        height,
                        width,
                        central_fraction=0.875,
                        scope=None):
  """Prepare one image for evaluation.

  If height and width are specified it would output an image with that size by
  applying resize_bilinear.

  If central_fraction is specified it would cropt the central fraction of the
  input image.

  Args:
    image: 3-D Tensor of image. If dtype is tf.float32 then the range should be
      [0, 1], otherwise it would converted to tf.float32 assuming that the range
      is [0, MAX], where MAX is largest positive representable number for
      int(8/16/32) data type (see `tf.image.convert_image_dtype` for details)
    height: integer
    width: integer
    central_fraction: Optional Float, fraction of the image to crop.
    scope: Optional scope for name_scope.
  Returns:
    3-D float Tensor of prepared image.
  """
  with tf.name_scope(scope, 'eval_image', [image, height, width]):
    if image.dtype != tf.float32:
      image = tf.image.convert_image_dtype(image, dtype=tf.float32)
    # Crop the central region of the image with an area containing 87.5% of
    # the original image.
    if central_fraction:
      image = tf.image.central_crop(image, central_fraction=central_fraction)

    if height and width:
      # Resize the image to the specified height and width.
      image = tf.expand_dims(image, 0)
      image = tf.image.resize_bilinear(
          image, [height, width], align_corners=False)
      image = tf.squeeze(image, [0])
    image = tf.subtract(image, 0.5)
    image = tf.multiply(image, 2.0)
    return image


Pre - process one image for training or evaluation .

def preprocess_image(image,
                     height,
                     width,
                     is_training=False,
                     bbox=None,
                     fast_mode=True):
  """Pre-process one image for training or evaluation.

  Args:
    image: 3-D Tensor [height, width, channels] with the image.
    height: integer, image expected height.
    width: integer, image expected width.
    is_training: Boolean. If true it would transform an image for train,
      otherwise it would transform it for evaluation.
    bbox: 3-D float Tensor of bounding boxes arranged [1, num_boxes, coords]
      where each coordinate is [0, 1) and the coordinates are arranged as
      [ymin, xmin, ymax, xmax].
    fast_mode: Optional boolean, if True avoids slower transformations.

  Returns:
    3-D float Tensor containing an appropriately scaled image

  Raises:
    ValueError: if user does not provide bounding box
  """
  if is_training:
    return preprocess_for_train(image, height, width, bbox, fast_mode)
  else:
    return preprocess_for_eval(image, height, width)

def preprocess_image ( image , height , width , is_training = False , bbox = None , fast_mode = True ) : if is_training : return preprocess_for_train ( image , height , width , bbox , fast_mode ) else : return preprocess_for_eval ( image , height , width )


Reads a charset definition from a tab separated text file .

def read_charset(filename, null_character=u'\u2591'):

  """Reads a charset definition from a tab separated text file.

  charset file has to have format compatible with the FSNS dataset.

  Args:
    filename: a path to the charset file.
    null_character: a unicode character used to replace '<null>' character. the
      default value is a light shade block '░'.

  Returns:
    a dictionary with keys equal to character codes and values - unicode
    characters.
  """
  pattern = re.compile(r'(\d+)\t(.+)')
  charset = {}
  with tf.gfile.GFile(filename) as f:
    for i, line in enumerate(f):
      m = pattern.match(line)
      if m is None:
        logging.warning('incorrect charset file. line #%d: %s', i, line)
        continue
      code = int(m.group(1))
      #char = m.group(2).decode('utf-8')
      char = m.group(2)
      #print(char)
      if char == '<nul>':
        char = null_character
      charset[code] = char
  return charset

def
read_charset
(
filename
,
null_character
=
u'\u2591'
)
:
pattern
=
re
.
compile
(
r'(\d+)\t(.+)'
)
charset
=
{
}
with
tf
.
gfile
.
GFile
(
filename
)
as
f
:
for
i
,
line
in
enumerate
(
f
)
:
m
=
pattern
.
match
(
line
)
if
m
is
:
logging
.
warning
(
'incorrect charset file. line #%d: %s'
,
i
,
line
)
continue
code
=
int
(
m
.
group
(
1
)
)
#char = m.group(2).decode('utf-8')
char
=
m
.
group
(
2
)
#print(char)
if
char
==
'<nul>'
:
char
=
null_character
charset
[
code
]
=
char
return
charset


Returns a dataset tuple for FSNS dataset .

def get_split(split_name, dataset_dir=None, config=None):
  """Returns a dataset tuple for FSNS dataset.

  Args:
    split_name: A train/test split name.
    dataset_dir: The base directory of the dataset sources, by default it uses
      a predefined CNS path (see DEFAULT_DATASET_DIR).
    config: A dictionary with dataset configuration. If None - will use the
      DEFAULT_CONFIG.

  Returns:
    A `Dataset` namedtuple.

  Raises:
    ValueError: if `split_name` is not a valid train/test split.
  """
  if not dataset_dir:
    dataset_dir = DEFAULT_DATASET_DIR

  if not config:
    config = DEFAULT_CONFIG

  if split_name not in config['splits']:
    raise ValueError('split name %s was not recognized.' % split_name)

  logging.info('Using %s dataset split_name=%s dataset_dir=%s', config['name'],
               split_name, dataset_dir)

  # Ignores the 'image/height' feature.
  zero = tf.zeros([1], dtype=tf.int64)
  keys_to_features = {
      'image/encoded':
      tf.FixedLenFeature((), tf.string, default_value=''),
      'image/format':
      tf.FixedLenFeature((), tf.string, default_value='png'),
      'image/width':
      tf.FixedLenFeature([1], tf.int64, default_value=zero),
      'image/orig_width':
      tf.FixedLenFeature([1], tf.int64, default_value=zero),
      'image/class':
      tf.FixedLenFeature([config['max_sequence_length']], tf.int64),
      'image/unpadded_class':
      tf.VarLenFeature(tf.int64),
      'image/text':
      tf.FixedLenFeature([1], tf.string, default_value=''),
  }
  items_to_handlers = {
      'image':
      slim.tfexample_decoder.Image(
          shape=config['image_shape'],
          image_key='image/encoded',
          format_key='image/format'),
      'label':
      slim.tfexample_decoder.Tensor(tensor_key='image/class'),
      'text':
      slim.tfexample_decoder.Tensor(tensor_key='image/text'),
      'num_of_views':
      _NumOfViewsHandler(
          width_key='image/width',
          original_width_key='image/orig_width',
          num_of_views=config['num_of_views'])
  }
  decoder = slim.tfexample_decoder.TFExampleDecoder(keys_to_features,
                                                    items_to_handlers)
  charset_file = os.path.join(dataset_dir, config['charset_filename'])
  charset = read_charset(charset_file)

  print(charset)
  file_pattern = os.path.join(dataset_dir,
                              config['splits'][split_name]['pattern'])
  return slim.dataset.Dataset(
      data_sources=file_pattern,
      reader=tf.TFRecordReader,
      decoder=decoder,
      num_samples=config['splits'][split_name]['size'],
      items_to_descriptions=config['items_to_descriptions'],
      #  additional parameters for convenience.
      charset=charset,
      num_char_classes=len(charset),
      num_of_views=config['num_of_views'],
      max_sequence_length=config['max_sequence_length'],
      null_code=config['null_code'])