mirror of
https://github.com/deepfakes/faceswap
synced 2025-06-07 10:43:27 -04:00
* Remove tensorflow_probability requirement * setup.py - fix progress bars * requirements.txt: Remove pre python 3.9 packages * update apple requirements.txt * update INSTALL.md * Remove python<3.9 code * setup.py - fix Windows Installer * typing: python3.9 compliant * Update pytest and readthedocs python versions * typing fixes * Python Version updates - Reduce max version to 3.10 - Default to 3.10 in installers - Remove incompatible 3.11 tests * Update dependencies * Downgrade imageio dep for Windows * typing: merge optional unions and fixes * Updates - min python version 3.10 - typing to python 3.10 spec - remove pre-tf2.10 code - Add conda tests * train: re-enable optimizer saving * Update dockerfiles * Update setup.py - Apple Conda deps to setup.py - Better Cuda + dependency handling * bugfix: Patch logging to prevent Autograph errors * Update dockerfiles * Setup.py - Setup.py - stdout to utf-8 * Add more OSes to github Actions * suppress mac-os end to end test
216 lines
7.2 KiB
Python
216 lines
7.2 KiB
Python
#!/usr/bin/env python3
|
|
""" Ports of existing NN Architecture for use in faceswap.py """
|
|
from __future__ import annotations
|
|
import logging
|
|
import typing as T
|
|
|
|
import tensorflow as tf
|
|
|
|
# Fix intellisense/linting for tf.keras' thoroughly broken import system
|
|
keras = tf.keras
|
|
layers = keras.layers
|
|
Model = keras.models.Model
|
|
|
|
if T.TYPE_CHECKING:
|
|
from tensorflow import Tensor
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class _net(): # pylint:disable=too-few-public-methods
|
|
""" Base class for existing NeuralNet architecture
|
|
|
|
Notes
|
|
-----
|
|
All architectures assume channels_last format
|
|
|
|
Parameters
|
|
----------
|
|
input_shape, Tuple, optional
|
|
The input shape for the model. Default: ``None``
|
|
"""
|
|
def __init__(self,
|
|
input_shape: tuple[int, int, int] | None = None) -> None:
|
|
logger.debug("Initializing: %s (input_shape: %s)", self.__class__.__name__, input_shape)
|
|
self._input_shape = (None, None, 3) if input_shape is None else input_shape
|
|
assert len(self._input_shape) == 3 and self._input_shape[-1] == 3, (
|
|
"Input shape must be in the format (height, width, channels) and the number of "
|
|
f"channels must equal 3. Received: {self._input_shape}")
|
|
logger.debug("Initialized: %s", self.__class__.__name__)
|
|
|
|
|
|
class AlexNet(_net): # pylint:disable=too-few-public-methods
|
|
""" AlexNet ported from torchvision version.
|
|
|
|
Notes
|
|
-----
|
|
This port only contains the features portion of the model.
|
|
|
|
References
|
|
----------
|
|
https://papers.nips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf
|
|
|
|
Parameters
|
|
----------
|
|
input_shape, Tuple, optional
|
|
The input shape for the model. Default: ``None``
|
|
"""
|
|
def __init__(self, input_shape: tuple[int, int, int] | None = None) -> None:
|
|
super().__init__(input_shape)
|
|
self._feature_indices = [0, 3, 6, 8, 10] # For naming equivalent to PyTorch
|
|
self._filters = [64, 192, 384, 256, 256] # Filters at each block
|
|
|
|
@classmethod
|
|
def _conv_block(cls,
|
|
inputs: Tensor,
|
|
padding: int,
|
|
filters: int,
|
|
kernel_size: int,
|
|
strides: int,
|
|
block_idx: int,
|
|
max_pool: bool) -> Tensor:
|
|
"""
|
|
The Convolutional block for AlexNet
|
|
|
|
Parameters
|
|
----------
|
|
inputs: :class:`tf.Tensor`
|
|
The input tensor to the block
|
|
padding: int
|
|
The amount of zero paddin to apply prior to convolution
|
|
filters: int
|
|
The number of filters to apply during convolution
|
|
kernel_size: int
|
|
The kernel size of the convolution
|
|
strides: int
|
|
The number of strides for the convolution
|
|
block_idx: int
|
|
The index of the current block (for standardized naming convention)
|
|
max_pool: bool
|
|
``True`` to apply a max pooling layer at the beginning of the block otherwise ``False``
|
|
|
|
Returns
|
|
-------
|
|
:class:`tf.Tensor`
|
|
The output of the Convolutional block
|
|
"""
|
|
name = f"features.{block_idx}"
|
|
var_x = inputs
|
|
if max_pool:
|
|
var_x = layers.MaxPool2D(pool_size=3, strides=2, name=f"{name}.pool")(var_x)
|
|
var_x = layers.ZeroPadding2D(padding=padding, name=f"{name}.pad")(var_x)
|
|
var_x = layers.Conv2D(filters,
|
|
kernel_size=kernel_size,
|
|
strides=strides,
|
|
padding="valid",
|
|
activation="relu",
|
|
name=name)(var_x)
|
|
return var_x
|
|
|
|
def __call__(self) -> tf.keras.models.Model:
|
|
""" Create the AlexNet Model
|
|
|
|
Returns
|
|
-------
|
|
:class:`keras.models.Model`
|
|
The compiled AlexNet model
|
|
"""
|
|
inputs = layers.Input(self._input_shape)
|
|
var_x = inputs
|
|
kernel_size = 11
|
|
strides = 4
|
|
|
|
for idx, (filters, block_idx) in enumerate(zip(self._filters, self._feature_indices)):
|
|
padding = 2 if idx < 2 else 1
|
|
do_max_pool = 0 < idx < 3
|
|
var_x = self._conv_block(var_x,
|
|
padding,
|
|
filters,
|
|
kernel_size,
|
|
strides,
|
|
block_idx,
|
|
do_max_pool)
|
|
kernel_size = max(3, kernel_size // 2)
|
|
strides = 1
|
|
return Model(inputs=inputs, outputs=[var_x])
|
|
|
|
|
|
class SqueezeNet(_net): # pylint:disable=too-few-public-methods
|
|
""" SqueezeNet ported from torchvision version.
|
|
|
|
Notes
|
|
-----
|
|
This port only contains the features portion of the model.
|
|
|
|
References
|
|
----------
|
|
https://arxiv.org/abs/1602.07360
|
|
|
|
Parameters
|
|
----------
|
|
input_shape, Tuple, optional
|
|
The input shape for the model. Default: ``None``
|
|
"""
|
|
|
|
@classmethod
|
|
def _fire(cls,
|
|
inputs: Tensor,
|
|
squeeze_planes: int,
|
|
expand_planes: int,
|
|
block_idx: int) -> Tensor:
|
|
""" The fire block for SqueezeNet.
|
|
|
|
Parameters
|
|
----------
|
|
inputs: :class:`tf.Tensor`
|
|
The input to the fire block
|
|
squeeze_planes: int
|
|
The number of filters for the squeeze convolution
|
|
expand_planes: int
|
|
The number of filters for the expand convolutions
|
|
block_idx: int
|
|
The index of the current block (for standardized naming convention)
|
|
|
|
Returns
|
|
-------
|
|
:class:`tf.Tensor`
|
|
The output of the SqueezeNet fire block
|
|
"""
|
|
name = f"features.{block_idx}"
|
|
squeezed = layers.Conv2D(squeeze_planes, 1,
|
|
activation="relu", name=f"{name}.squeeze")(inputs)
|
|
expand1 = layers.Conv2D(expand_planes, 1,
|
|
activation="relu", name=f"{name}.expand1x1")(squeezed)
|
|
expand3 = layers.Conv2D(expand_planes,
|
|
3,
|
|
activation="relu",
|
|
padding="same",
|
|
name=f"{name}.expand3x3")(squeezed)
|
|
return layers.Concatenate(axis=-1, name=name)([expand1, expand3])
|
|
|
|
def __call__(self) -> tf.keras.models.Model:
|
|
""" Create the SqueezeNet Model
|
|
|
|
Returns
|
|
-------
|
|
:class:`keras.models.Model`
|
|
The compiled SqueezeNet model
|
|
"""
|
|
inputs = layers.Input(self._input_shape)
|
|
var_x = layers.Conv2D(64, 3, strides=2, activation="relu", name="features.0")(inputs)
|
|
|
|
block_idx = 2
|
|
squeeze = 16
|
|
expand = 64
|
|
for idx in range(4):
|
|
if idx < 3:
|
|
var_x = layers.MaxPool2D(pool_size=3, strides=2)(var_x)
|
|
block_idx += 1
|
|
var_x = self._fire(var_x, squeeze, expand, block_idx)
|
|
block_idx += 1
|
|
var_x = self._fire(var_x, squeeze, expand, block_idx)
|
|
block_idx += 1
|
|
squeeze += 16
|
|
expand += 64
|
|
return Model(inputs=inputs, outputs=[var_x])
|