1
0
Fork 0
mirror of https://github.com/deepfakes/faceswap synced 2025-06-08 20:13:52 -04:00
faceswap/lib/model/session.py
torzdf d8557c1970
Faceswap 2.0 (#1045)
* Core Updates
    - Remove lib.utils.keras_backend_quiet and replace with get_backend() where relevant
    - Document lib.gpu_stats and lib.sys_info
    - Remove call to GPUStats.is_plaidml from convert and replace with get_backend()
    - lib.gui.menu - typofix

* Update Dependencies
Bump Tensorflow Version Check

* Port extraction to tf2

* Add custom import finder for loading Keras or tf.keras depending on backend

* Add `tensorflow` to KerasFinder search path

* Basic TF2 training running

* model.initializers - docstring fix

* Fix and pass tests for tf2

* Replace Keras backend tests with faceswap backend tests

* Initial optimizers update

* Monkey patch tf.keras optimizer

* Remove custom Adam Optimizers and Memory Saving Gradients

* Remove multi-gpu option. Add Distribution to cli

* plugins.train.model._base: Add Mirror, Central and Default distribution strategies

* Update tensorboard kwargs for tf2

* Penalized Loss - Fix for TF2 and AMD

* Fix syntax for tf2.1

* requirements typo fix

* Explicit None for clipnorm if using a distribution strategy

* Fix penalized loss for distribution strategies

* Update Dlight

* typo fix

* Pin to TF2.2

* setup.py - Install tensorflow from pip if not available in Conda

* Add reduction options and set default for mirrored distribution strategy

* Explicitly use default strategy rather than nullcontext

* lib.model.backup_restore documentation

* Remove mirrored strategy reduction method and default based on OS

* Initial restructure - training

* Remove PingPong
Start model.base refactor

* Model saving and resuming enabled

* More tidying up of model.base

* Enable backup and snapshotting

* Re-enable state file
Remove loss names from state file
Fix print loss function
Set snapshot iterations correctly

* Revert original model to Keras Model structure rather than custom layer
Output full model and sub model summary
Change NNBlocks to callables rather than custom keras layers

* Apply custom Conv2D layer

* Finalize NNBlock restructure
Update Dfaker blocks

* Fix reloading model under a different distribution strategy

* Pass command line arguments through to trainer

* Remove training_opts from model and reference params directly

* Tidy up model __init__

* Re-enable tensorboard logging
Suppress "Model Not Compiled" warning

* Fix timelapse

* lib.model.nnblocks - Bugfix residual block
Port dfaker
bugfix original

* dfl-h128 ported

* DFL SAE ported

* IAE Ported

* dlight ported

* port lightweight

* realface ported

* unbalanced ported

* villain ported

* lib.cli.args - Update Batchsize + move allow_growth to config

* Remove output shape definition
Get image sizes per side rather than globally

* Strip mask input from encoder

* Fix learn mask and output learned mask to preview

* Trigger Allow Growth prior to setting strategy

* Fix GUI Graphing

* GUI - Display batchsize correctly + fix training graphs

* Fix penalized loss

* Enable mixed precision training

* Update analysis displayed batch to match input

* Penalized Loss - Multi-GPU Fix

* Fix all losses for TF2

* Fix Reflect Padding

* Allow different input size for each side of the model

* Fix conv-aware initialization on reload

* Switch allow_growth order

* Move mixed_precision to cli

* Remove distrubution strategies

* Compile penalized loss sub-function into LossContainer

* Bump default save interval to 250
Generate preview on first iteration but don't save
Fix iterations to start at 1 instead of 0
Remove training deprecation warnings
Bump some scripts.train loglevels

* Add ability to refresh preview on demand on pop-up window

* Enable refresh of training preview from GUI

* Fix Convert
Debug logging in Initializers

* Fix Preview Tool

* Update Legacy TF1 weights to TF2
Catch stats error on loading stats with missing logs

* lib.gui.popup_configure - Make more responsive + document

* Multiple Outputs supported in trainer
Original Model - Mask output bugfix

* Make universal inference model for convert
Remove scaling from penalized mask loss (now handled at input to y_true)

* Fix inference model to work properly with all models

* Fix multi-scale output for convert

* Fix clipnorm issue with distribution strategies
Edit error message on OOM

* Update plaidml losses

* Add missing file

* Disable gmsd loss for plaidnl

* PlaidML - Basic training working

* clipnorm rewriting for mixed-precision

* Inference model creation bugfixes

* Remove debug code

* Bugfix: Default clipnorm to 1.0

* Remove all mask inputs from training code

* Remove mask inputs from convert

* GUI - Analysis Tab - Docstrings

* Fix rate in totals row

* lib.gui - Only update display pages if they have focus

* Save the model on first iteration

* plaidml - Fix SSIM loss with penalized loss

* tools.alignments - Remove manual and fix jobs

* GUI - Remove case formatting on help text

* gui MultiSelect custom widget - Set default values on init

* vgg_face2 - Move to plugins.extract.recognition and use plugins._base base class
cli - Add global GPU Exclude Option
tools.sort - Use global GPU Exlude option for backend
lib.model.session - Exclude all GPUs when running in CPU mode
lib.cli.launcher - Set backend to CPU mode when all GPUs excluded

* Cascade excluded devices to GPU Stats

* Explicit GPU selection for Train and Convert

* Reduce Tensorflow Min GPU Multiprocessor Count to 4

* remove compat.v1 code from extract

* Force TF to skip mixed precision compatibility check if GPUs have been filtered

* Add notes to config for non-working AMD losses

* Rasie error if forcing extract to CPU mode

* Fix loading of legace dfl-sae weights + dfl-sae typo fix

* Remove unused requirements
Update sphinx requirements
Fix broken rst file locations

* docs: lib.gui.display

* clipnorm amd condition check

* documentation - gui.display_analysis

* Documentation - gui.popup_configure

* Documentation - lib.logger

* Documentation - lib.model.initializers

* Documentation - lib.model.layers

* Documentation - lib.model.losses

* Documentation - lib.model.nn_blocks

* Documetation - lib.model.normalization

* Documentation - lib.model.session

* Documentation - lib.plaidml_stats

* Documentation: lib.training_data

* Documentation: lib.utils

* Documentation: plugins.train.model._base

* GUI Stats: prevent stats from using GPU

* Documentation - Original Model

* Documentation: plugins.model.trainer._base

* linting

* unit tests: initializers + losses

* unit tests: nn_blocks

* bugfix - Exclude gpu devices in train, not include

* Enable Exclude-Gpus in Extract

* Enable exclude gpus in tools

* Disallow multiple plugin types in a single model folder

* Automatically add exclude_gpus argument in for cpu backends

* Cpu backend fixes

* Relax optimizer test threshold

* Default Train settings - Set mask to Extended

* Update Extractor cli help text
Update to Python 3.8

* Fix FAN to run on CPU

* lib.plaidml_tools - typofix

* Linux installer - check for curl

* linux installer - typo fix
2020-08-12 10:36:41 +01:00

207 lines
8.9 KiB
Python

#!/usr/bin python3
""" Settings manager for Keras Backend """
import logging
import numpy as np
import tensorflow as tf
# pylint:disable=no-name-in-module,import-error
from keras.layers import Activation
from keras.models import load_model as k_load_model, Model
from lib.utils import get_backend
logger = logging.getLogger(__name__) # pylint:disable=invalid-name
class KSession():
""" Handles the settings of backend sessions for inference models.
This class acts as a wrapper for various :class:`keras.Model()` functions, ensuring that
actions performed on a model are handled consistently and can be performed in parallel in
separate threads.
This is an early implementation of this class, and should be expanded out over time
with relevant `AMD`, `CPU` and `NVIDIA` backend methods.
Notes
-----
The documentation refers to :mod:`keras`. This is a pseudonym for either :mod:`keras` or
:mod:`tensorflow.keras` depending on the backend in use.
Parameters
----------
name: str
The name of the model that is to be loaded
model_path: str
The path to the keras model file
model_kwargs: dict, optional
Any kwargs that need to be passed to :func:`keras.models.load_models()`. Default: ``None``
allow_growth: bool, optional
Enable the Tensorflow GPU allow_growth configuration option. This option prevents
Tensorflow from allocating all of the GPU VRAM, but can lead to higher fragmentation and
slower performance. Default: ``False``
exclude_gpus: list, optional
A list of indices correlating to connected GPUs that Tensorflow should not use. Pass
``None`` to not exclude any GPUs. Default: ``None``
"""
def __init__(self, name, model_path, model_kwargs=None, allow_growth=False, exclude_gpus=None):
logger.trace("Initializing: %s (name: %s, model_path: %s, model_kwargs: %s, "
"allow_growth: %s, exclude_gpus)", self.__class__.__name__, name, model_path,
model_kwargs, allow_growth, exclude_gpus)
self._name = name
self._backend = get_backend()
self._set_session(allow_growth, exclude_gpus)
self._model_path = model_path
self._model_kwargs = dict() if not model_kwargs else model_kwargs
self._model = None
logger.trace("Initialized: %s", self.__class__.__name__,)
def predict(self, feed, batch_size=None):
""" Get predictions from the model.
This method is a wrapper for :func:`keras.predict()` function. For Tensorflow backends
this is a straight call to the predict function. For PlaidML backends, this attempts
to optimize the inference batch sizes to reduce the number of kernels that need to be
compiled.
Parameters
----------
feed: numpy.ndarray or list
The feed to be provided to the model as input. This should be a :class:`numpy.ndarray`
for single inputs or a `list` of :class:`numpy.ndarray` objects for multiple inputs.
"""
if self._backend == "amd" and batch_size is not None:
return self._amd_predict_with_optimized_batchsizes(feed, batch_size)
return self._model.predict(feed, batch_size=batch_size)
def _amd_predict_with_optimized_batchsizes(self, feed, batch_size):
""" Minimizes the amount of kernels to be compiled when using the ``amd`` backend with
varying batch sizes while trying to keep the batchsize as high as possible.
Parameters
----------
feed: numpy.ndarray or list
The feed to be provided to the model as input. This should be a ``numpy.ndarray``
for single inputs or a ``list`` of ``numpy.ndarray`` objects for multiple inputs.
batch_size: int
The upper batchsize to use.
"""
if isinstance(feed, np.ndarray):
feed = [feed]
items = feed[0].shape[0]
done_items = 0
results = list()
while done_items < items:
if batch_size < 4: # Not much difference in BS < 4
batch_size = 1
batch_items = ((items - done_items) // batch_size) * batch_size
if batch_items:
pred_data = [x[done_items:done_items + batch_items] for x in feed]
pred = self._model.predict(pred_data, batch_size=batch_size)
done_items += batch_items
results.append(pred)
batch_size //= 2
if isinstance(results[0], np.ndarray):
return np.concatenate(results)
return [np.concatenate(x) for x in zip(*results)]
def _set_session(self, allow_growth, exclude_gpus):
""" Sets the backend session options.
For AMD backend this does nothing.
For CPU backends, this hides any GPUs from Tensorflow.
For Nvidia backends, this hides any GPUs that Tensorflow should not use and applies
any allow growth settings
Parameters
----------
allow_growth: bool, optional
Enable the Tensorflow GPU allow_growth configuration option. This option prevents
Tensorflow from allocating all of the GPU VRAM, but can lead to higher fragmentation
and slower performance. Default: False
exclude_gpus: list, optional
A list of indices correlating to connected GPUs that Tensorflow should not use. Pass
``None`` to not exclude any GPUs. Default: ``None``
"""
if self._backend == "amd":
return
if self._backend == "cpu":
logger.verbose("Hiding GPUs from Tensorflow")
tf.config.set_visible_devices([], "GPU")
return
gpus = tf.config.list_physical_devices('GPU')
if exclude_gpus:
gpus = [gpu for idx, gpu in enumerate(gpus) if idx not in exclude_gpus]
logger.debug("Filtering devices to: %s", gpus)
tf.config.set_visible_devices(gpus, "GPU")
if allow_growth:
for gpu in gpus:
logger.info("Setting allow growth for GPU: %s", gpu)
tf.config.experimental.set_memory_growth(gpu, True)
def load_model(self):
""" Loads a model.
This method is a wrapper for :func:`keras.models.load_model()`. Loads a model and its
weights from :attr:`model_path` defined during initialization of this class. Any additional
``kwargs`` to be passed to :func:`keras.models.load_model()` should also be defined during
initialization of the class.
For Tensorflow backends, the `make_predict_function` method is called on the model to make
it thread safe.
"""
logger.verbose("Initializing plugin model: %s", self._name)
self._model = k_load_model(self._model_path, compile=False, **self._model_kwargs)
if self._backend != "amd":
self._model.make_predict_function()
def define_model(self, function):
""" Defines a model from the given function.
This method acts as a wrapper for :class:`keras.models.Model()`.
Parameters
----------
function: function
A function that defines a :class:`keras.Model` and returns it's ``inputs`` and
``outputs``. The function that generates these results should be passed in, NOT the
results themselves, as the function needs to be executed within the correct context.
"""
self._model = Model(*function())
def load_model_weights(self):
""" Load model weights for a defined model inside the correct session.
This method is a wrapper for :class:`keras.load_weights()`. Once a model has been defined
in :func:`define_model()` this method can be called to load its weights from the
:attr:`model_path` defined during initialization of this class.
For Tensorflow backends, the `make_predict_function` method is called on the model to make
it thread safe.
"""
logger.verbose("Initializing plugin model: %s", self._name)
self._model.load_weights(self._model_path)
if self._backend != "amd":
self._model.make_predict_function()
def append_softmax_activation(self, layer_index=-1):
""" Append a softmax activation layer to a model
Occasionally a softmax activation layer needs to be added to a model's output.
This is a convenience function to append this layer to the loaded model.
Parameters
----------
layer_index: int, optional
The layer index of the model to select the output from to use as an input to the
softmax activation layer. Default: `-1` (The final layer of the model)
"""
logger.debug("Appending Softmax Activation to model: (layer_index: %s)", layer_index)
softmax = Activation("softmax", name="softmax")(self._model.layers[layer_index].output)
self._model = Model(inputs=self._model.input, outputs=[softmax])