Source code for texar.tf.modules.classifiers.conv_classifiers

# Copyright 2018 The Texar Authors. 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.
"""
Various classifier classes.
"""

# pylint: disable=not-context-manager, too-many-arguments, too-many-locals

import tensorflow as tf

from texar.tf.utils.exceptions import TexarError
from texar.tf.modules.classifiers.classifier_base import ClassifierBase
from texar.tf.modules.encoders.conv_encoders import Conv1DEncoder
from texar.tf.utils import utils
from texar.tf.hyperparams import HParams

__all__ = [
    "Conv1DClassifier"
]


[docs]class Conv1DClassifier(ClassifierBase): """Simple Conv-1D classifier. This is a combination of the :class:`~texar.tf.modules.Conv1DEncoder` with a classification layer. Args: hparams (dict, optional): Hyperparameters. Missing hyperparamerter will be set to default values. See :meth:`default_hparams` for the hyperparameter sturcture and default values. Example: .. code-block:: python clas = Conv1DClassifier(hparams={'num_classes': 10}) inputs = tf.random_uniform([64, 20, 256]) logits, pred = clas(inputs) # logits == Tensor of shape [64, 10] # pred == Tensor of shape [64] .. document private functions .. automethod:: _build """ def __init__(self, hparams=None): ClassifierBase.__init__(self, hparams) with tf.variable_scope(self.variable_scope): encoder_hparams = utils.dict_fetch( hparams, Conv1DEncoder.default_hparams()) self._encoder = Conv1DEncoder(hparams=encoder_hparams) # Add an additional dense layer if needed self._num_classes = self._hparams.num_classes if self._num_classes > 0: if self._hparams.num_dense_layers <= 0: self._encoder.append_layer({"type": "Flatten"}) logit_kwargs = self._hparams.logit_layer_kwargs if logit_kwargs is None: logit_kwargs = {} elif not isinstance(logit_kwargs, HParams): raise ValueError( "hparams['logit_layer_kwargs'] must be a dict.") else: logit_kwargs = logit_kwargs.todict() logit_kwargs.update({"units": self._num_classes}) if 'name' not in logit_kwargs: logit_kwargs['name'] = "logit_layer" self._encoder.append_layer( {"type": "Dense", "kwargs": logit_kwargs})
[docs] @staticmethod def default_hparams(): """Returns a dictionary of hyperparameters with default values. .. code-block:: python { # (1) Same hyperparameters as in Conv1DEncoder ... # (2) Additional hyperparameters "num_classes": 2, "logit_layer_kwargs": { "use_bias": False }, "name": "conv1d_classifier" } Here: 1. Same hyperparameters as in :class:`~texar.tf.modules.Conv1DEncoder`. See the :meth:`~texar.tf.modules.Conv1DEncoder.default_hparams`. An instance of Conv1DEncoder is created for feature extraction. 2. Additional hyperparameters: "num_classes": int Number of classes: - If **`> 0`**, an additional :tf_main:`Dense <layers/Dense>` \ layer is appended to the encoder to compute the logits over \ classes. - If **`<= 0`**, no dense layer is appended. The number of \ classes is assumed to be the final dense layer size of the \ encoder. "logit_layer_kwargs": dict Keyword arguments for the logit Dense layer constructor, except for argument "units" which is set to "num_classes". Ignored if no extra logit layer is appended. "name": str Name of the classifier. """ hparams = Conv1DEncoder.default_hparams() hparams.update({ "name": "conv1d_classifier", "num_classes": 2, # set to <=0 to avoid appending output layer "logit_layer_kwargs": {"use_bias": False} }) return hparams
[docs] def _build(self, # pylint: disable=arguments-differ inputs, sequence_length=None, dtype=None, mode=None): """Feeds the inputs through the network and makes classification. The arguments are the same as in :class:`~texar.tf.modules.Conv1DEncoder`. The predictions of binary classification ("num_classes"=1) and multi-way classification ("num_classes">1) are different, as explained below. Args: inputs: The inputs to the network, which is a 3D tensor. See :class:`~texar.tf.modules.Conv1DEncoder` for more details. sequence_length (optional): An int tensor of shape `[batch_size]` containing the length of each element in :attr:`inputs`. If given, time steps beyond the length will first be masked out before feeding to the layers. dtype (optional): Type of the inputs. If not provided, infers from inputs automatically. mode (optional): A tensor taking value in :tf_main:`tf.estimator.ModeKeys <estimator/ModeKeys>`, including `TRAIN`, `EVAL`, and `PREDICT`. If `None`, :func:`texar.tf.global_mode` is used. Returns: A tuple `(logits, pred)`, where - **`logits`** is a Tensor of shape `[batch_size, num_classes]`\ for `num_classes` >1, and `[batch_size]` for `num_classes` =1 \ (i.e., binary classification). - **`pred`** is the prediction, a Tensor of shape `[batch_size]` \ and type `tf.int64`. For binary classification, the standard \ sigmoid function is used for prediction, and the class labels are \ `{0, 1}`. """ logits = self._encoder(inputs, sequence_length, dtype, mode) num_classes = self._hparams.num_classes is_binary = num_classes == 1 is_binary = is_binary or (num_classes <= 0 and logits.shape[1] == 1) if is_binary: pred = tf.greater(logits, 0) logits = tf.reshape(logits, [-1]) else: pred = tf.argmax(logits, 1) pred = tf.cast(tf.reshape(pred, [-1]), tf.int64) self._built = True return logits, pred
@property def trainable_variables(self): """The list of trainable variables of the module. """ if not self._built: raise TexarError( "Attempting to access trainable_variables before module %s " "was fully built. The module is built once it is called, " "e.g., with `%s(...)`" % (self.name, self.name)) return self._encoder.trainable_variables @property def num_classes(self): """The number of classes. """ return self._num_classes @property def nn(self): # pylint: disable=invalid-name """The classifier neural network. """ return self._encoder
[docs] def has_layer(self, layer_name): """Returns `True` if the network with the name exists. Returns `False` otherwise. Args: layer_name (str): Name of the layer. """ return self._encoder.has_layer(layer_name)
[docs] def layer_by_name(self, layer_name): """Returns the layer with the name. Returns 'None' if the layer name does not exist. Args: layer_name (str): Name of the layer. """ return self._encoder.layer_by_name(layer_name)
@property def layers_by_name(self): """A dictionary mapping layer names to the layers. """ return self._encoder.layers_by_name @property def layers(self): """A list of the layers. """ return self._encoder.layers @property def layer_names(self): """A list of uniquified layer names. """ return self._encoder.layer_names
[docs] def layer_outputs_by_name(self, layer_name): """Returns the output tensors of the layer with the specified name. Returns `None` if the layer name does not exist. Args: layer_name (str): Name of the layer. """ return self._encoder.layer_outputs_by_name(layer_name)
@property def layer_outputs(self): """A list containing output tensors of each layer. """ return self._encoder.layer_outputs