Commit 22eabcd1 authored by Amir MOHAMMADI's avatar Amir MOHAMMADI

Add keras-based models

parent 9d279cac
This diff is collapsed.
import tensorflow as tf
class ConvDiscriminator(tf.keras.Model):
"""A discriminator that can sit on top of DenseNet 161's transition 1 block.
The output of that block given 224x224 inputs is 14x14x384."""
def __init__(self, data_format="channels_last", n_classes=1, **kwargs):
super().__init__(**kwargs)
self.data_format = data_format
self.n_classes = n_classes
act = "sigmoid" if n_classes == 1 else "softmax"
self.sequential_layers = [
tf.keras.layers.Conv2D(200, 1, data_format=data_format),
tf.keras.layers.Activation("relu"),
tf.layers.AveragePooling2D(3, 2, data_format=data_format),
tf.keras.layers.Conv2D(100, 1, data_format=data_format),
tf.keras.layers.Activation("relu"),
tf.layers.AveragePooling2D(3, 2, data_format=data_format),
tf.keras.layers.Flatten(data_format=data_format),
tf.keras.layers.Dense(n_classes),
tf.keras.layers.Activation(act),
]
def call(self, x, training=None):
for l in self.sequential_layers:
x = l(x)
return x
class ConvDiscriminator2(tf.keras.Model):
"""A discriminator that can sit on top of DenseNet 161's transition 1 block.
The output of that block given 224x224 inputs is 14x14x384. Here we want to output
15x15x128 features which is going to match the output of encoder in mcae.py given
these layers::
ENCODER_LAYERS = (
(32, 5, 1, 2),
(64, 5, 1, 2),
(128, 3, 1, 2),
(128, 3, 1, 2)
)
DECODER_LAYERS = (
(64, 3, 2, 1),
(32, 3, 2, 1),
(16, 5, 2, 2),
(8, 5, 2, 2),
(3, 2, 1, 1),
)
"""
def __init__(self, data_format="channels_last", **kwargs):
super().__init__(**kwargs)
self.data_format = data_format
self.sequential_layers = [
tf.keras.layers.ZeroPadding2D(
padding=((1, 0), (1, 0)), data_format=data_format
),
tf.keras.layers.Conv2D(256, 5, data_format=data_format, padding="same"),
tf.keras.layers.Activation("relu"),
tf.keras.layers.Conv2D(128, 5, data_format=data_format, padding="same"),
tf.keras.layers.Activation("relu"),
tf.keras.layers.Conv2D(128, 1, data_format=data_format, padding="same"),
tf.keras.layers.Activation("relu"),
]
def call(self, x, training=None):
for l in self.sequential_layers:
x = l(x)
return x
"""Multi-channel autoencoder network used in:
@inproceedings{NikisinsICB2019,
author = {Olegs Nikisins, Anjith George, Sebastien Marcel},
title = {Domain Adaptation in Multi-Channel Autoencoder based Features for Robust Face Anti-Spoofing},
year = {2019},
booktitle = {ICB 2019},
}
"""
import tensorflow as tf
def get_l2_kw(weight_decay):
l2_kw = {}
if weight_decay is not None:
l2_kw = {"kernel_regularizer": tf.keras.regularizers.l2(weight_decay)}
return l2_kw
class ConvEncoder(tf.keras.Model):
"""The encoder part"""
def __init__(
self,
encoder_layers,
data_format="channels_last",
weight_decay=1e-5,
name="Encoder",
**kwargs,
):
super().__init__(name=name, **kwargs)
self.data_format = data_format
l2_kw = get_l2_kw(weight_decay)
layers = []
for i, (filters, kernel_size, strides, padding) in enumerate(encoder_layers):
pad_kw = {}
# if i == 0:
# pad_kw["input_shape"] = input_shape
pad = tf.keras.layers.ZeroPadding2D(
padding=padding, data_format=data_format, name=f"pad_{i}", **pad_kw
)
conv = tf.keras.layers.Conv2D(
filters,
kernel_size,
strides,
data_format=data_format,
name=f"conv_{i}",
**l2_kw,
)
act = tf.keras.layers.Activation("relu", name=f"relu_{i}")
pool = tf.keras.layers.MaxPooling2D(
data_format=data_format, name=f"pool_{i}"
)
layers.extend([pad, conv, act, pool])
self.sequential_layers = layers
def call(self, x, training=None):
for l in self.sequential_layers:
x = l(x)
return x
class ConvDecoder(tf.keras.Model):
"""The encoder part"""
def __init__(
self,
decoder_layers,
data_format="channels_last",
weight_decay=1e-5,
name="Decoder",
**kwargs,
):
super().__init__(name=name, ** kwargs)
self.data_format = data_format
l2_kw = get_l2_kw(weight_decay)
layers = []
for i, (filters, kernel_size, strides, cropping) in enumerate(decoder_layers):
dconv_kw = {}
dconv_kw.update(l2_kw)
# if i == 0:
# dconv_kw["input_shape"] = embedding_shape
dconv = tf.keras.layers.Conv2DTranspose(
filters,
kernel_size,
strides=strides,
data_format=data_format,
name=f"dconv_{i}",
**dconv_kw,
)
crop = tf.keras.layers.Cropping2D(
cropping=cropping, data_format=data_format, name=f"crop_{i}"
)
if i == len(decoder_layers) - 1:
act = tf.keras.layers.Activation("tanh", name="tanh")
else:
act = tf.keras.layers.Activation("relu", name=f"relu_{i}")
layers.extend([dconv, crop, act])
self.sequential_layers = layers
def call(self, x, training=None):
for l in self.sequential_layers:
x = l(x)
return x
class ConvAutoencoder(tf.keras.Model):
"""
A class defining a simple convolutional autoencoder.
Multi-channel autoencoder network used in::
@inproceedings{NikisinsICB2019,
author = {Olegs Nikisins, Anjith George, Sebastien Marcel},
title = {Domain Adaptation in Multi-Channel Autoencoder based Features for Robust Face Anti-Spoofing},
year = {2019},
booktitle = {ICB 2019},
}
Attributes
----------
data_format : str
Either channels_last or channels_first
decoder : object
The encoder part
encoder : object
The decoder part
"""
def __init__(
self,
data_format="channels_last",
encoder_layers=((16, 5, 1, 2), (16, 5, 1, 2), (16, 3, 1, 2), (16, 3, 1, 2)),
decoder_layers=(
(16, 3, 2, 1),
(16, 3, 2, 1),
(16, 5, 2, 2),
(3, 5, 2, 2),
(3, 2, 1, 1),
),
weight_decay=1e-5,
name="ConvAutoencoder",
**kwargs,
):
super().__init__(name=name, **kwargs)
self.data_format = data_format
self.weight_decay = weight_decay
self.encoder = ConvEncoder(
encoder_layers,
data_format=data_format,
weight_decay=weight_decay,
name="Encoder",
)
self.decoder = ConvDecoder(
decoder_layers,
data_format=data_format,
weight_decay=weight_decay,
name="Decoder",
)
def call(self, x, training=None):
x = self.encoder(x, training=training)
self.encoder_output = x
x = self.decoder(x, training=training)
return x
from .util import *
from .singleton import Singleton
from .session import Session
from . import hooks
from . import eval
from .hooks import *
from .eval import *
from .keras import *
from .train import *
from .graph import *
import tensorflow as tf
def call_on_frozen_graph(
graph_def_path,
input,
return_elements,
input_name,
name=None,
**kwargs
):
"""Loads a frozen graph def file (.pb) and replaces its input with the given input
and return the requested output tensors.
Parameters
----------
graph_def_path : str
Path to the graph definition file
input : object
Input tensor
return_elements : [str]
A list of strings which corresponds to operations in the graph.
input_name : str, optional
The name of input in the graph that will be replaced by input.
name : str, optional
The scope of the imported operations. Defaults to "import".
**kwargs
Extra arguments to be passed to tf.import_graph_def
Returns
-------
list
List of requested operations. Normally you would use
``returned_operations[0].outputs[0]``
"""
with tf.gfile.GFile(graph_def_path, "rb") as f:
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
input_map = {input_name: input}
return tf.import_graph_def(
graph_def,
input_map=input_map,
return_elements=return_elements,
name=name,
**kwargs
)
import tensorflow.keras.backend as K
from .network import is_trainable
import tensorflow as tf
import logging
logger = logging.getLogger(__name__)
def keras_channels_index():
return -3 if K.image_data_format() == "channels_first" else -1
def keras_model_weights_as_initializers_for_variables(model):
"""Changes the initialization operations of variables in the model to take the
current value as the initial values.
This is useful when you want to restore a pre-trained Keras model inside the
model_fn of an estimator.
Parameters
----------
model : object
A Keras model.
"""
sess = K.get_session()
n = len(model.variables)
logger.debug("Initializing %d variables with their current weights", n)
for variable in model.variables:
value = variable.eval(sess)
initial_value = tf.constant(value=value, dtype=value.dtype.name)
variable._initializer_op = variable.assign(initial_value)
variable._initial_value = initial_value
def apply_trainable_variables_on_keras_model(model, trainable_variables, mode):
"""Changes the trainable status of layers in a keras model.
It can only turn off the trainable status of layer.
Parameters
----------
model : object
A Keras model
trainable_variables : list or None
See bob.learn.tensorflow.estimators.Logits
mode : str
One of tf.estimator.ModeKeys
"""
for layer in model.layers:
trainable = is_trainable(layer.name, trainable_variables, mode=mode)
if layer.trainable:
layer.trainable = trainable
def restore_model_variables_from_checkpoint(model, checkpoint, session=None):
if session is None:
session = tf.keras.backend.get_session()
# removes duplicates
var_list = set(model.variables)
assert len(var_list)
saver = tf.train.Saver(var_list=var_list)
ckpt_state = tf.train.get_checkpoint_state(checkpoint)
logger.info("Loading checkpoint %s", ckpt_state.model_checkpoint_path)
saver.restore(session, ckpt_state.model_checkpoint_path)
def initialize_model_from_checkpoint(model, checkpoint, normalizer=None):
if normalizer is None:
def normalizer(name):
return name.split(":")[0]
assignment_map = {normalizer(v.name): v for v in model.variables}
assert len(assignment_map)
tf.train.init_from_checkpoint(checkpoint, assignment_map=assignment_map)
import tensorflow as tf
import tensorflow.contrib.slim as slim
def append_logits(graph,
n_classes,
reuse=False,
l2_regularizer=5e-05,
weights_std=0.1, trainable_variables=None,
name='Logits'):
trainable = is_trainable(name, trainable_variables)
return slim.fully_connected(
graph,
n_classes,
activation_fn=None,
weights_initializer=tf.truncated_normal_initializer(
stddev=weights_std),
weights_regularizer=slim.l2_regularizer(l2_regularizer),
scope=name,
reuse=reuse,
trainable=trainable,
)
def is_trainable(name, trainable_variables, mode=tf.estimator.ModeKeys.TRAIN):
"""
Check if a variable is trainable or not
Parameters
----------
name: str
Layer name
trainable_variables: list
List containing the variables or scopes to be trained.
If None, the variable/scope is trained
"""
# if mode is not training, so we shutdown
if mode != tf.estimator.ModeKeys.TRAIN:
return False
# If None, we train by default
if trainable_variables is None:
return True
# Here is my choice to shutdown the whole scope
return name in trainable_variables
'''Helps training reproducible networks.
'''
"""Helps training reproducible networks.
"""
import os
import random as rn
import numpy as np
import tensorflow as tf
from tensorflow.core.protobuf import rewriter_config_pb2
def set_seed(seed=0, python_hash_seed=0, log_device_placement=False):
def set_seed(
seed=0, python_hash_seed=0, log_device_placement=False, allow_soft_placement=False,
arithmetic_optimization=None,
):
"""Sets the seeds in python, numpy, and tensorflow in order to help
training reproducible networks.
......@@ -38,7 +42,7 @@ def set_seed(seed=0, python_hash_seed=0, log_device_placement=False):
# See these references for further details:
# https://docs.python.org/3.4/using/cmdline.html#envvar-PYTHONHASHSEED
# https://github.com/fchollet/keras/issues/2280#issuecomment-306959926
os.environ['PYTHONHASHSEED'] = '{}'.format(python_hash_seed)
os.environ["PYTHONHASHSEED"] = "{}".format(python_hash_seed)
# The below is necessary for starting Numpy generated random numbers
# in a well-defined initial state.
......@@ -56,7 +60,13 @@ def set_seed(seed=0, python_hash_seed=0, log_device_placement=False):
session_config = tf.ConfigProto(
intra_op_parallelism_threads=1,
inter_op_parallelism_threads=1,
log_device_placement=log_device_placement)
log_device_placement=log_device_placement,
allow_soft_placement=allow_soft_placement,
)
if arithmetic_optimization == 'off':
off = rewriter_config_pb2.RewriterConfig.OFF
session_config.graph_options.rewrite_options.arithmetic_optimization = off
# The below tf.set_random_seed() will make random number generation
# in the TensorFlow backend have a well-defined initial state.
......
import tensorflow as tf
def check_features(features):
if "data" not in features or "key" not in features:
raise ValueError(
"The input function needs to contain a dictionary with the keys `data` and `key` "
)
return True
def get_trainable_variables(extra_checkpoint, mode=tf.estimator.ModeKeys.TRAIN):
"""
Given the extra_checkpoint dictionary provided to the estimator,
extract the content of "trainable_variables".
If trainable_variables is not provided, all end points are trainable by
default.
If trainable_variables==[], all end points are NOT trainable.
If trainable_variables contains some end_points, ONLY these endpoints will
be trainable.
Attributes
----------
extra_checkpoint: dict
The extra_checkpoint dictionary provided to the estimator
mode:
The estimator mode. TRAIN, EVAL, and PREDICT. If not TRAIN, None is
returned.
Returns
-------
Returns `None` if **trainable_variables** is not in extra_checkpoint;
otherwise returns the content of extra_checkpoint .
"""
if mode != tf.estimator.ModeKeys.TRAIN:
return None
# If you don't set anything, everything is trainable
if extra_checkpoint is None or "trainable_variables" not in extra_checkpoint:
return None
return extra_checkpoint["trainable_variables"]
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment