mirror of
https://github.com/deepfakes/faceswap.git
synced 2026-10-19 15:37:18 +08:00
837bc2d51d
* FaceSwap 3 (#1515) * Update extract pipeline * Update requirements + setup for nvidia * Remove allow-growth option * tf.keras to keras updates * lib.model.losses - Port + fix all loss functions for Keras3 * lib.model - port initializers, layers. normalization to Keras3 * lib.model.autoclip to Keras 3 * Update mixed precision layer storage * model file to .keras format * Restructure nn_blocks to initialize layers in __init__ * Tensorboard - Trainer: Add Torch compatible Tensorboard callbacks - GUI event reader remove TF dependency * Loss logging - Flush TB logs on save - Replace TB live iterator for GUI * Backup models on total loss drop rather than per side * Update all models to Keras3 Compat * Remove lib.model.session * Update clip ViT to Keras 3 * plugins.extract.mask.unet-dfl - Fix for Keras3/Torch backend * Port AdaBelief to Keras 3 * setup.py: - Add --dev flag for dev tool install * Fix Keras 3 syntax * Fix LR Finder for Keras 3 * Fix mixed precision switching for Keras 3 * Add more optimizers + open up config setting * train: Remove updating FS1 weights to FS2 models * Alignments: Remove support for legacy .json files * tools.model: - Remove TF Saved Format saving - Fix Backup/Restore + Nan-Scan * Fix inference model creation for Keras 3 * Preview tool: Fix for Keras3 * setup.py: Configure keras backend * train: Migration of FS2 models to FS3 * Training: Default coverage to 100% * Remove DirectML backend * Update setup for MacOS * GUI: Force line reading to UTF-8 * Remove redundant Tensorflow references * Remove redundant code * Legacy model loading: Fix TFLamdaOp scalar ops and DepthwiseConv2D * Add vertical offset option for training * Github actions: Add more python versions * Add python version to workflow names * Github workflow: Exclude Python 3.12 for macOS * Implement custom training loop * Fs3 - Add RTX5xxx and ROCm 6.1-6.4 support (#1511) * setup.py: Add Cuda/ROCm version select options * bump minimum python version to 3.11 * Switch from setup.cgf to pyproject.toml * Documentation: Update all docs to use automodapi * Allow sysinfo to run with missing packages + correctly install tk under Linux * Bugfix: dot naming convention in clip models * lib.config: Centralise globally rather than passing as object - Add torch DataParallel for multi-gpu training - GUI: Group switches together when generating cli args - CLI: Remove deprecated multi-character argparse args - Refactor: - Centralise tensorboard reading/writing + unit tests - Create trainer plugin interfaces + add original + distributed * Update installers
167 lines
6.0 KiB
Python
167 lines
6.0 KiB
Python
#! /usr/env/bin/python3
|
|
""" Unit test for :mod:`lib.training.tensorboard` """
|
|
import os
|
|
|
|
import pytest
|
|
|
|
from keras import layers, Sequential
|
|
import numpy as np
|
|
from tensorboard.compat.proto import event_pb2
|
|
from torch.utils.tensorboard import SummaryWriter
|
|
|
|
from lib.training import tensorboard as mod_tb
|
|
|
|
# pylint:disable=protected-access,invalid-name
|
|
|
|
|
|
@pytest.fixture()
|
|
def _gen_events_file(tmpdir):
|
|
log_dir = tmpdir.mkdir("logs")
|
|
|
|
def _apply(keys=["test1"], # pylint:disable=dangerous-default-value
|
|
values=[0.42],
|
|
global_steps=[4]):
|
|
writer = SummaryWriter(log_dir)
|
|
for key, val, step in zip(keys, values, global_steps):
|
|
writer.add_scalar(key, val, global_step=step)
|
|
writer.flush()
|
|
return os.path.join(log_dir, os.listdir(log_dir)[0])
|
|
|
|
return _apply
|
|
|
|
|
|
@pytest.mark.parametrize("entries", ({"loss1": np.random.rand()},
|
|
{f"test{i}": np.random.rand() for i in range(4)},
|
|
{f"another_test{i}": np.random.rand() for i in range(10)}))
|
|
@pytest.mark.parametrize("batch", [1, 42, 69, 1024, 143432])
|
|
@pytest.mark.parametrize("is_live", (True, False), ids=("live", "not_live"))
|
|
def test_RecordIterator(entries, batch, is_live, _gen_events_file):
|
|
""" Test that our :class:`lib.training.tensorboard.RecordIterator` returns expected results """
|
|
keys = list(entries)
|
|
vals = list(entries.values())
|
|
batches = [batch + i for i in range(len(keys))]
|
|
|
|
file = _gen_events_file(keys, vals, batches)
|
|
iterator = mod_tb.RecordIterator(file, is_live=is_live)
|
|
|
|
results = list(event_pb2.Event.FromString(v) for v in iterator)
|
|
valid = [r for r in results if r.summary.value]
|
|
|
|
assert len(valid) == len(keys)
|
|
for entry, key, val, btc in zip(valid, keys, vals, batches):
|
|
assert len(entry.summary.value) == 1
|
|
assert entry.step == btc
|
|
assert entry.summary.value[0].tag == key
|
|
assert np.isclose(entry.summary.value[0].simple_value, val)
|
|
|
|
if is_live:
|
|
assert iterator._is_live is True
|
|
assert os.path.getsize(file) == iterator._position # At end of file
|
|
else:
|
|
assert iterator._is_live is False
|
|
assert iterator._position == 0
|
|
|
|
|
|
@pytest.fixture()
|
|
def _get_ttb_instance(tmpdir):
|
|
log_dir = tmpdir.mkdir("logs")
|
|
|
|
def _apply(write_graph=False, update_freq="batch"):
|
|
instance = mod_tb.TorchTensorBoard(log_dir=log_dir,
|
|
write_graph=write_graph,
|
|
update_freq=update_freq)
|
|
return log_dir, instance
|
|
|
|
return _apply
|
|
|
|
|
|
def _get_logs(temp_path):
|
|
train_logs = os.path.join(temp_path, "train")
|
|
log_files = os.listdir(train_logs)
|
|
assert len(log_files) == 1
|
|
records = [event_pb2.Event.FromString(record)
|
|
for record in mod_tb.RecordIterator(os.path.join(train_logs, log_files[0]))]
|
|
return records
|
|
|
|
|
|
@pytest.mark.parametrize("write_graph", (True, False), ids=("write_graph", "no_write_graph"))
|
|
def test_TorchTensorBoard_set_model(write_graph, _get_ttb_instance):
|
|
""" Test that :class:`lib.training.tensorboard.set_model` functions """
|
|
log_dir, instance = _get_ttb_instance(write_graph=write_graph)
|
|
|
|
model = Sequential()
|
|
model.add(layers.Input(shape=(8, )))
|
|
model.add(layers.Dense(4))
|
|
model.add(layers.Dense(4))
|
|
|
|
assert not os.path.exists(os.path.join(log_dir, "train"))
|
|
instance.set_model(model)
|
|
instance.on_save()
|
|
|
|
logs = [x for x in _get_logs(os.path.join(log_dir))
|
|
if x.summary.value]
|
|
|
|
if not write_graph:
|
|
assert not logs
|
|
return
|
|
|
|
# Only a single logged entry
|
|
assert len(logs) == 1 and len(logs[0].summary.value) == 1
|
|
# Should be our Keras model summary
|
|
assert logs[0].summary.value[0].tag == "keras/text_summary"
|
|
|
|
|
|
def test_TorchTensorBoard_on_train_begin(_get_ttb_instance):
|
|
""" Test that :class:`lib.training.tensorboard.on_train_begin` functions """
|
|
_, instance = _get_ttb_instance()
|
|
instance.on_train_begin()
|
|
assert instance._global_train_batch == 0
|
|
assert instance._previous_epoch_iterations == 0
|
|
|
|
|
|
@pytest.mark.parametrize("batch", (1, 3, 57, 124))
|
|
@pytest.mark.parametrize("logs", ({"loss_a": 2.45, "loss_b": 1.56},
|
|
{"loss_c": 0.54, "loss_d": 0.51},
|
|
{"loss_c": 0.69, "loss_d": 0.42, "loss_g": 2.69}))
|
|
def test_TorchTensorBoard_on_train_batch_end(batch, logs, _get_ttb_instance):
|
|
""" Test that :class:`lib.training.tensorboard.on_train_batch_end` functions """
|
|
log_dir, instance = _get_ttb_instance()
|
|
|
|
assert not os.path.exists(os.path.join(log_dir, "train"))
|
|
|
|
instance.on_train_batch_end(batch, logs)
|
|
instance.on_save()
|
|
|
|
tb_logs = [x for x in _get_logs(os.path.join(log_dir))
|
|
if x.summary.value]
|
|
|
|
assert len(tb_logs) == len(logs)
|
|
for (k, v), out in zip(logs.items(), tb_logs):
|
|
assert len(out.summary.value) == 1
|
|
assert out.summary.value[0].tag == f"batch_{k}"
|
|
assert np.isclose(out.summary.value[0].simple_value, v)
|
|
assert out.step == batch
|
|
|
|
|
|
def test_TorchTensorBoard_on_save(_get_ttb_instance, mocker):
|
|
""" Test that :class:`lib.training.tensorboard.on_save` functions """
|
|
# Implicitly checked in other tests, so just make sure it calls flush on the writer
|
|
_, instance = _get_ttb_instance()
|
|
instance._train_writer.flush = mocker.MagicMock()
|
|
|
|
instance.on_save()
|
|
instance._train_writer.flush.assert_called_once()
|
|
|
|
|
|
def test_TorchTensorBoard_on_train_end(_get_ttb_instance, mocker):
|
|
""" Test that :class:`lib.training.tensorboard.on_train_end` functions """
|
|
# Saving is already implicitly checked in other tests, so just make sure it calls flush and
|
|
# close on the train writer
|
|
_, instance = _get_ttb_instance()
|
|
instance._train_writer.flush = mocker.MagicMock()
|
|
instance._train_writer.close = mocker.MagicMock()
|
|
|
|
instance.on_train_end()
|
|
instance._train_writer.flush.assert_called_once()
|
|
instance._train_writer.close.assert_called_once()
|