masterofaudio2077 commited on
Commit
18dcb10
Β·
verified Β·
1 Parent(s): 99e07e0

Upload 11 files

Browse files
Files changed (11) hide show
  1. README.md +68 -14
  2. app.py +105 -0
  3. checkpoint.pkl +3 -0
  4. configuration.py +23 -0
  5. generate.py +26 -0
  6. imports.py +9 -0
  7. inference.py +114 -0
  8. inspect_checkpoint.py +111 -0
  9. layers.py +317 -0
  10. loading_model.py +4 -0
  11. requirements.txt +6 -0
README.md CHANGED
@@ -1,14 +1,68 @@
1
- ---
2
- title: StyleGAN
3
- emoji: πŸ“Š
4
- colorFrom: purple
5
- colorTo: red
6
- sdk: gradio
7
- sdk_version: 6.9.0
8
- app_file: app.py
9
- pinned: false
10
- license: apache-2.0
11
- short_description: this a Style GAN V1 Model For High Quality Face Generation.
12
- ---
13
-
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: StyleGAN v1
3
+ emoji: 🎨
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: "4.0.0"
8
+ app_file: app.py
9
+ pinned: false
10
+ license: mit
11
+ ---
12
+
13
+ # StyleGAN v1 – Image generation
14
+
15
+ Generate images with your trained StyleGAN v1 generator.
16
+
17
+ ## How to use
18
+
19
+ 1. **Number of images**: Choose how many images to generate (1–16).
20
+ 2. **Resolution**: Select output resolution (4 up to 256).
21
+ 3. **Random seed** (optional): Set a seed for reproducible samples.
22
+ 4. Click **Generate**.
23
+
24
+ ## Adding your weights to this Space
25
+
26
+ Your trained weights must be in this repo so the app can load them.
27
+
28
+ ### Option A: Keras weights (recommended)
29
+
30
+ 1. Save your generator in training with:
31
+ ```python
32
+ generator.save_weights("weights/generator.weights.h5")
33
+ ```
34
+ 2. Create a `weights` folder in this repo and upload `generator.weights.h5` there.
35
+ 3. Or push the file with Git LFS if it is large:
36
+ ```bash
37
+ git lfs install
38
+ git lfs track "weights/*.h5"
39
+ git add weights/generator.weights.h5 .gitattributes
40
+ git commit -m "Add generator weights"
41
+ git push
42
+ ```
43
+
44
+ ### Option B: State checkpoint (gen_state)
45
+
46
+ If your training script saves a checkpoint with `ema_trainable` and `non_trainable` (e.g. with `pickle`):
47
+
48
+ 1. Save it as `weights/gen_state.pkl`.
49
+ 2. Add the `weights` folder and `gen_state.pkl` to this repo (or use Git LFS for large files).
50
+
51
+ ## Run locally
52
+
53
+ ```bash
54
+ pip install -r requirements.txt
55
+ # Add your weights to weights/generator.weights.h5 (or weights/gen_state.pkl)
56
+ gradio app.py
57
+ ```
58
+
59
+ Open http://localhost:7860 in your browser.
60
+
61
+ ## Project structure
62
+
63
+ - `app.py` – Gradio UI and entrypoint for the Space
64
+ - `inference.py` – Loads weights and runs generation
65
+ - `loading_model.py` – Builds the StyleGAN generator
66
+ - `layers.py` – Generator/discriminator layers
67
+ - `configuration.py` – Resolutions and hyperparameters
68
+ - `weights/` – Put your generator weights here
app.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Gradio app for StyleGAN v1 image generation.
3
+ Run: gradio app.py
4
+ For Hugging Face Spaces, the Space runs this file.
5
+ """
6
+ import os
7
+ import gradio as gr
8
+
9
+ # Backend must be set before any Keras/JAX imports
10
+ os.environ["KERAS_BACKEND"] = "jax"
11
+
12
+ from inference import load_weights, generate_images, WEIGHTS_PATH, STATE_PATH
13
+ from configuration import RESOLUTIONS
14
+
15
+ # Load model once at startup
16
+ _model_loaded = False
17
+
18
+
19
+ def load_model_once(resolution=256):
20
+ global _model_loaded
21
+ if _model_loaded:
22
+ return True
23
+ ok = load_weights(resolution=resolution)
24
+ if ok:
25
+ _model_loaded = True
26
+ return ok
27
+
28
+
29
+ def generate(n_images, resolution, seed):
30
+ n_images = max(1, min(int(n_images), 16))
31
+ resolution = int(resolution)
32
+ seed = int(seed) if seed is not None and str(seed).strip() else None
33
+
34
+ if not load_model_once(resolution):
35
+ return (
36
+ [],
37
+ "No weights found. Add generator weights to the repo:\n"
38
+ f"β€’ Keras: {WEIGHTS_PATH}\n"
39
+ f"β€’ Or state dict: {STATE_PATH}\n"
40
+ "See README for Hugging Face setup.",
41
+ )
42
+
43
+ try:
44
+ images = generate_images(n_images, resolution=resolution, seed=seed)
45
+ if not images:
46
+ return [], "No images generated."
47
+ return images, f"Generated {len(images)} image(s) at {resolution}Γ—{resolution}."
48
+ except Exception as e:
49
+ import traceback
50
+ return [], f"Error: {e}\n{traceback.format_exc()}"
51
+
52
+
53
+ def main():
54
+ resolutions = [int(r) for r in RESOLUTIONS]
55
+
56
+ with gr.Blocks(
57
+ title="StyleGAN v1",
58
+ theme=gr.themes.Soft(),
59
+ ) as demo:
60
+ gr.Markdown("# StyleGAN v1 – Image generation")
61
+ gr.Markdown("Choose how many images to generate and the resolution. Then click **Generate**.")
62
+
63
+ with gr.Row():
64
+ n_images = gr.Slider(
65
+ minimum=1,
66
+ maximum=16,
67
+ value=4,
68
+ step=1,
69
+ label="Number of images",
70
+ )
71
+ resolution = gr.Dropdown(
72
+ choices=[str(r) for r in resolutions],
73
+ value="256",
74
+ label="Resolution",
75
+ )
76
+ seed = gr.Number(
77
+ value=None,
78
+ label="Random seed (optional)",
79
+ precision=0,
80
+ )
81
+ with gr.Row():
82
+ gen_btn = gr.Button("Generate", variant="primary")
83
+ status = gr.Textbox(label="Status", interactive=False)
84
+ gallery = gr.Gallery(
85
+ label="Generated images",
86
+ show_label=True,
87
+ columns=4,
88
+ object_fit="contain",
89
+ height="auto",
90
+ )
91
+
92
+ gen_btn.click(
93
+ fn=generate,
94
+ inputs=[n_images, resolution, seed],
95
+ outputs=[gallery, status],
96
+ )
97
+
98
+ demo.launch(
99
+ server_name="0.0.0.0",
100
+ server_port=int(os.environ.get("PORT", 7860)),
101
+ )
102
+
103
+
104
+ if __name__ == "__main__":
105
+ main()
checkpoint.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d77ad4ceca48e1c0580682e3f336303bda95c33d079b75f887c3dca0818ebd78
3
+ size 190231487
configuration.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ RESOLUTIONS = [4, 8, 16, 32, 64, 128, 256]
2
+
3
+ RES_TO_FILTERS = {
4
+ 4: 512,
5
+ 8: 512,
6
+ 16: 512,
7
+ 32: 512,
8
+ 64: 256,
9
+ 128: 128,
10
+ 256: 64,
11
+ }
12
+
13
+ LATENT_DIM = 512
14
+ BATCH_SIZE = 32
15
+ LEARNING_RATE = 1e-4
16
+
17
+ def normalize(img):
18
+ img = img / 127.5 - 1.0 # Normalize images to [-1,1]
19
+ return img
20
+
21
+ def denormalize(img):
22
+ return (img + 1) / 2
23
+
generate.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from imports import jax,jnp,np
2
+ from loading_model import generator
3
+ def inference_and_plot(gen_state, resolution, phase='stable'):
4
+ rng = gen_state['rng']
5
+
6
+ # ── split into 3 keys: next state, z, noise ──────────
7
+ rng, z_key, noise_key = jax.random.split(rng, 3)
8
+
9
+ fixed_z = jax.random.normal(z_key, [16, 512])
10
+
11
+ fake_images, _ = generator.stateless_call(
12
+ gen_state['ema_trainable'],
13
+ gen_state['non_trainable'],
14
+ jnp.array(fixed_z),
15
+ jnp.array(1.0),
16
+ noise_key, # ← dedicated noise key
17
+ )
18
+
19
+ print(f"Fake image range: {float(jnp.min(fake_images)):.3f} to {float(jnp.max(fake_images)):.3f}")
20
+
21
+ gen_state = {**gen_state, 'rng': rng} # ← advance state with consumed rng
22
+
23
+ fake_images = (fake_images + 1.0) / 2.0
24
+ fake_images = jnp.clip(fake_images, 0.0, 1.0)
25
+ fake_images = np.array(fake_images)
26
+ return gen_state
imports.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ os.environ["KERAS_BACKEND"] = "jax" # must be set BEFORE importing keras
3
+
4
+ import numpy as np
5
+ import keras
6
+ import jax
7
+ import jax.numpy as jnp
8
+ from keras import ops, layers, Model, random, activations
9
+ from keras.models import load_model
inference.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ StyleGAN v1 inference: load weights and generate images.
3
+ Supports Keras .weights.h5 and optional state checkpoint (gen_state).
4
+ """
5
+ import os
6
+ os.environ["KERAS_BACKEND"] = "jax"
7
+
8
+ import numpy as np
9
+ import jax
10
+ import jax.numpy as jnp
11
+
12
+ # Import after backend set
13
+ from loading_model import generator
14
+ from configuration import LATENT_DIM, RESOLUTIONS
15
+
16
+ # Default paths (override with env or put files in repo)
17
+ WEIGHTS_DIR = os.environ.get("STYLEGAN_WEIGHTS_DIR", "weights")
18
+ WEIGHTS_PATH = os.environ.get("STYLEGAN_WEIGHTS_PATH", os.path.join(WEIGHTS_DIR, "generator.weights.h5"))
19
+ STATE_PATH = os.environ.get("STYLEGAN_STATE_PATH", os.path.join(WEIGHTS_DIR, "gen_state.pkl"))
20
+
21
+ _gen_state = None
22
+ _use_stateless = False
23
+
24
+
25
+ def _build_model():
26
+ """Build generator with a dummy forward pass so weights can be loaded."""
27
+ rng = jax.random.PRNGKey(0)
28
+ dummy_z = jnp.zeros((1, LATENT_DIM))
29
+ _ = generator(dummy_z, 1.0, rng)
30
+
31
+
32
+ def load_weights(resolution=256):
33
+ """
34
+ Load generator weights from disk.
35
+ Prefer WEIGHTS_PATH (Keras .weights.h5). If not found, try STATE_PATH (gen_state pickle).
36
+ """
37
+ global _gen_state, _use_stateless
38
+ _build_model()
39
+
40
+ if os.path.isfile(WEIGHTS_PATH):
41
+ generator.load_weights(WEIGHTS_PATH)
42
+ _use_stateless = False
43
+ generator.current_resolution = min(resolution, max(RESOLUTIONS))
44
+ if resolution not in RESOLUTIONS:
45
+ generator.current_resolution = max(r for r in RESOLUTIONS if r <= resolution)
46
+ return True
47
+
48
+ try:
49
+ import pickle
50
+ if os.path.isfile(STATE_PATH):
51
+ with open(STATE_PATH, "rb") as f:
52
+ _gen_state = pickle.load(f)
53
+ _use_stateless = True
54
+ return True
55
+ except Exception:
56
+ pass
57
+
58
+ return False
59
+
60
+
61
+ def generate_images(n_images, resolution=256, seed=None):
62
+ """
63
+ Generate n_images fake images.
64
+ Returns list of numpy arrays (H, W, 3) in [0, 255] uint8 for display.
65
+ """
66
+ if seed is None:
67
+ seed = int(np.random.randint(0, 2**31))
68
+ rng = jax.random.PRNGKey(seed)
69
+
70
+ if _use_stateless and _gen_state is not None:
71
+ return _generate_stateless(n_images, resolution, rng)
72
+ return _generate_keras(n_images, resolution, rng)
73
+
74
+
75
+ def _generate_keras(n_images, resolution, rng):
76
+ res = min(resolution, max(RESOLUTIONS))
77
+ res = max(r for r in RESOLUTIONS if r <= res)
78
+ generator.current_resolution = res
79
+
80
+ rng, z_key, noise_key = jax.random.split(rng, 3)
81
+ z = jax.random.normal(z_key, (n_images, LATENT_DIM))
82
+
83
+ out_call = generator(z, 1.0, noise_key)
84
+ images = out_call[0] if isinstance(out_call, (list, tuple)) else out_call
85
+ images = (np.array(images) + 1.0) / 2.0
86
+ images = np.clip(images, 0.0, 1.0)
87
+ out = []
88
+ for i in range(n_images):
89
+ img = (images[i] * 255).astype(np.uint8)
90
+ out.append(img)
91
+ return out
92
+
93
+
94
+ def _generate_stateless(n_images, resolution, rng):
95
+ gen_state = _gen_state
96
+ res = min(resolution, max(RESOLUTIONS))
97
+ res = max(r for r in RESOLUTIONS if r <= res)
98
+ # If state was saved with a resolution, we use generator.current_resolution from state
99
+ # Otherwise we'd need to store it in gen_state; for now use generator attribute
100
+ generator.current_resolution = res
101
+
102
+ rng, z_key, noise_key = jax.random.split(rng, 3)
103
+ z = jax.random.normal(z_key, (n_images, LATENT_DIM))
104
+
105
+ fake_images, _ = generator.stateless_call(
106
+ gen_state["ema_trainable"],
107
+ gen_state["non_trainable"],
108
+ z,
109
+ 1.0,
110
+ noise_key,
111
+ )
112
+ fake_images = (np.array(fake_images) + 1.0) / 2.0
113
+ fake_images = np.clip(fake_images, 0.0, 1.0)
114
+ return [(fake_images[i] * 255).astype(np.uint8) for i in range(n_images)]
inspect_checkpoint.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Inspect a training checkpoint and write a cleaned inference-only state.
3
+ - Drops optimizer variables.
4
+ - If ema_trainable exists, keeps only ema_trainable + non_trainable (drops trainable).
5
+ - Saves to weights/gen_state.pkl for use by inference.py / app.py.
6
+ """
7
+ import os
8
+ import sys
9
+ import pickle
10
+
11
+ def _describe(obj, depth=0, max_depth=3):
12
+ """Return a short description of the object for printing."""
13
+ if depth > max_depth:
14
+ return "..."
15
+ if hasattr(obj, "shape"):
16
+ return f"array shape={obj.shape} dtype={getattr(obj, 'dtype', '?')}"
17
+ if isinstance(obj, dict):
18
+ return "dict(" + ", ".join(f"{k!r}" for k in list(obj.keys())[:8]) + ("..." if len(obj) > 8 else "") + ")"
19
+ if isinstance(obj, (list, tuple)):
20
+ return f"{type(obj).__name__} len={len(obj)}"
21
+ return type(obj).__name__
22
+
23
+
24
+ def inspect_checkpoint(path):
25
+ """Load checkpoint and print top-level keys and a short description of each value."""
26
+ with open(path, "rb") as f:
27
+ data = pickle.load(f)
28
+
29
+ if not isinstance(data, dict):
30
+ print(f"Top-level type: {type(data)}")
31
+ return data
32
+
33
+ print("Checkpoint keys and value summary:")
34
+ print("-" * 60)
35
+ for k in sorted(data.keys()):
36
+ v = data[k]
37
+ desc = _describe(v)
38
+ print(f" {k!r}: {desc}")
39
+ print("-" * 60)
40
+ return data
41
+
42
+
43
+ # Keys we consider optimizer / training-only (to drop)
44
+ OPT_OR_TRAINING_KEYS = {
45
+ "opt_state", "optimizer", "optimizer_state", "opt",
46
+ "step", "steps", "epoch", "epochs",
47
+ "trainable", # dropped when ema_trainable exists
48
+ "rng", # inference creates its own; optional to keep for reproducibility
49
+ }
50
+
51
+
52
+ def clean_checkpoint(data, keep_rng=False):
53
+ """
54
+ Build a state dict for inference only.
55
+ - Drop any key in OPT_OR_TRAINING_KEYS (and 'trainable' if ema exists).
56
+ - If 'ema_trainable' exists, drop 'trainable'.
57
+ - Keep only ema_trainable (or trainable) + non_trainable.
58
+ """
59
+ has_ema = "ema_trainable" in data
60
+ out = {}
61
+
62
+ if has_ema:
63
+ out["ema_trainable"] = data["ema_trainable"]
64
+ # drop trainable (we use ema only)
65
+ else:
66
+ if "trainable" in data:
67
+ out["ema_trainable"] = data["trainable"] # inference expects key "ema_trainable"
68
+ # else no trainable params in checkpoint
69
+
70
+ if "non_trainable" in data:
71
+ out["non_trainable"] = data["non_trainable"]
72
+
73
+ if keep_rng and "rng" in data:
74
+ out["rng"] = data["rng"]
75
+
76
+ return out
77
+
78
+
79
+ def main():
80
+ import argparse
81
+ p = argparse.ArgumentParser(description="Inspect checkpoint and optionally write cleaned gen_state.pkl")
82
+ p.add_argument("checkpoint", nargs="?", default="weights/checkpoint.pkl",
83
+ help="Path to full training checkpoint (default: weights/checkpoint.pkl)")
84
+ p.add_argument("-o", "--output", default="weights/gen_state.pkl",
85
+ help="Output path for cleaned state (default: weights/gen_state.pkl)")
86
+ p.add_argument("--no-write", action="store_true", help="Only inspect, do not write cleaned state")
87
+ p.add_argument("--keep-rng", action="store_true", help="Keep rng in cleaned state")
88
+ args = p.parse_args()
89
+
90
+ if not os.path.isfile(args.checkpoint):
91
+ print(f"Checkpoint not found: {args.checkpoint}", file=sys.stderr)
92
+ sys.exit(1)
93
+
94
+ data = inspect_checkpoint(args.checkpoint)
95
+
96
+ if not isinstance(data, dict):
97
+ print("Checkpoint is not a dict; cannot clean.", file=sys.stderr)
98
+ sys.exit(1)
99
+
100
+ cleaned = clean_checkpoint(data, keep_rng=args.keep_rng)
101
+ print("Cleaned state keys:", list(cleaned.keys()))
102
+
103
+ if not args.no_write:
104
+ os.makedirs(os.path.dirname(args.output) or ".", exist_ok=True)
105
+ with open(args.output, "wb") as f:
106
+ pickle.dump(cleaned, f)
107
+ print(f"Wrote {args.output}")
108
+
109
+
110
+ if __name__ == "__main__":
111
+ main()
layers.py ADDED
@@ -0,0 +1,317 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from imports import * # gets all the libs
2
+ from configuration import * # gets all the constants
3
+
4
+ class EqualizedConv2D(keras.layers.Layer):
5
+ def __init__(self, filters, kernel_size, gain=2.0, **kwargs):
6
+ super().__init__(**kwargs)
7
+ self.filters = filters
8
+ self.kernel_size = kernel_size
9
+ self.gain = gain
10
+
11
+ def build(self, input_shape):
12
+ self.kernel = self.add_weight(
13
+ shape=(self.kernel_size, self.kernel_size,
14
+ input_shape[-1], self.filters),
15
+ initializer='random_normal',
16
+ trainable=True
17
+ )
18
+ self.bias = self.add_weight(
19
+ shape=(self.filters,),
20
+ initializer='zeros',
21
+ trainable=True
22
+ )
23
+ fan_in = self.kernel_size * self.kernel_size * input_shape[-1]
24
+ self.c = float(np.sqrt(self.gain / fan_in))
25
+
26
+ def call(self, x):
27
+ x = keras.ops.conv(
28
+ x, self.kernel * self.c,
29
+ strides=1, padding='SAME', data_format='channels_last'
30
+ )
31
+ return x + self.bias
32
+
33
+
34
+ class PixelNorm(keras.layers.Layer):
35
+ def __init__(self, epsilon=1e-8):
36
+ super().__init__()
37
+ self.epsilon = epsilon
38
+
39
+ def call(self, x):
40
+ x_sq = keras.ops.square(x)
41
+ mean_sq = keras.ops.mean(x_sq, axis=-1, keepdims=True)
42
+ x_norm = x / keras.ops.sqrt(mean_sq + self.epsilon)
43
+ return x_norm
44
+
45
+
46
+ class EqualizedDense(keras.layers.Layer):
47
+ def __init__(self, units, gain=2.0, lr_multiplier=1.0, **kwargs):
48
+ super().__init__(**kwargs)
49
+ self.units = units
50
+ self.gain = gain
51
+ self.lr_multiplier = lr_multiplier
52
+
53
+ def build(self, input_shape):
54
+ self.kernel = self.add_weight(
55
+ shape=(input_shape[-1], self.units),
56
+ initializer=keras.initializers.RandomNormal(
57
+ mean=0.0, stddev=1.0 / self.lr_multiplier
58
+ ),
59
+ trainable=True
60
+ )
61
+ self.bias = self.add_weight(
62
+ shape=(self.units,),
63
+ initializer='zeros',
64
+ trainable=True
65
+ )
66
+ fan_in = input_shape[-1]
67
+ self.c = float(np.sqrt(self.gain / fan_in))
68
+
69
+ def call(self, x):
70
+ return (keras.ops.matmul(x, self.kernel * self.c) + self.bias) * self.lr_multiplier
71
+
72
+ class MinibatchStd(keras.layers.Layer):
73
+ def __init__(self, group_size=4, epsilon=1e-8):
74
+ super().__init__()
75
+ self.group_size = group_size
76
+ self.epsilon = epsilon
77
+
78
+ def call(self, x):
79
+ batch = x.shape[0] # ← static shape not tracer
80
+ h = x.shape[1]
81
+ w = x.shape[2]
82
+ c = x.shape[3]
83
+
84
+ group_size = min(self.group_size, batch) # ← plain Python min
85
+
86
+ y = keras.ops.reshape(x, [group_size, -1, h, w, c])
87
+ mean = keras.ops.mean(y, axis=0, keepdims=True)
88
+ var = keras.ops.mean(keras.ops.square(y - mean), axis=0)
89
+ std = keras.ops.sqrt(var + self.epsilon)
90
+
91
+ avg_std = keras.ops.mean(std, axis=[1, 2, 3], keepdims=True)
92
+ avg_std = keras.ops.tile(avg_std, [group_size, h, w, 1])
93
+
94
+ return keras.ops.concatenate([x, avg_std], axis=-1)
95
+
96
+ # ── gain=1.0 for linear projections ──────────────────────
97
+ def toRGB():
98
+ return EqualizedConv2D(filters=3, kernel_size=1, gain=1.0) # no activation
99
+
100
+ def fromRGB(filters):
101
+ return keras.Sequential([
102
+ EqualizedConv2D(filters, kernel_size=1, gain=2.0),
103
+ keras.layers.LeakyReLU(0.2), # activation here
104
+ ])
105
+
106
+
107
+ class GenBlock(keras.layers.Layer):
108
+ def __init__(self, filters):
109
+ super().__init__()
110
+ self.conv1 = EqualizedConv2D(filters, kernel_size=4)
111
+ self.conv2 = EqualizedConv2D(filters, kernel_size=4)
112
+ self.act = keras.layers.LeakyReLU(0.2)
113
+ self.pnorm = PixelNorm()
114
+ def call(self, x):
115
+ x = self.act(self.conv1(x))
116
+ x = self.pnorm(x)
117
+ x = self.act(self.conv2(x))
118
+ x = self.pnorm(x)
119
+ return x
120
+
121
+
122
+ class DiscBlock(keras.layers.Layer):
123
+ def __init__(self, in_filters, out_filters, **kwargs):
124
+ super().__init__(**kwargs)
125
+ self.conv1 = EqualizedConv2D(in_filters, 4)
126
+ self.conv2 = EqualizedConv2D(out_filters, 4)
127
+ self.act = keras.layers.LeakyReLU(0.2)
128
+ self.pool = keras.layers.AveragePooling2D(pool_size=2)
129
+
130
+ def call(self, x):
131
+ x = self.act(self.conv1(x))
132
+ x = self.act(self.conv2(x))
133
+ x = self.pool(x)
134
+ return x
135
+
136
+ def build_mapping_network():
137
+ return keras.Sequential([
138
+ EqualizedDense(512, gain=2.0, lr_multiplier=0.01), # ← 100x slower
139
+ keras.layers.LeakyReLU(0.2),
140
+ EqualizedDense(512, gain=2.0, lr_multiplier=0.01),
141
+ keras.layers.LeakyReLU(0.2),
142
+ EqualizedDense(512, gain=2.0, lr_multiplier=0.01),
143
+ keras.layers.LeakyReLU(0.2),
144
+ EqualizedDense(512, gain=2.0, lr_multiplier=0.01),
145
+ keras.layers.LeakyReLU(0.2),
146
+ EqualizedDense(512, gain=2.0, lr_multiplier=0.01),
147
+ keras.layers.LeakyReLU(0.2),
148
+ EqualizedDense(512, gain=2.0, lr_multiplier=0.01),
149
+ keras.layers.LeakyReLU(0.2),
150
+ EqualizedDense(512, gain=2.0, lr_multiplier=0.01),
151
+ keras.layers.LeakyReLU(0.2),
152
+ EqualizedDense(512, gain=2.0, lr_multiplier=0.01),
153
+ keras.layers.LeakyReLU(0.2),
154
+ ], name="mapping_network")
155
+
156
+ class AdaIN(keras.layers.Layer):
157
+ def __init__(self, channels, w_dim=512):
158
+ super().__init__()
159
+ self.channels = channels
160
+ self.w_dim = w_dim
161
+
162
+ # single affine layer β†’ 2 * channels, then split into ys and yb
163
+ self.style_transform = EqualizedDense(2 * channels, gain=1.0)
164
+
165
+ def call(self, x, w):
166
+ # ── explicit mean and std over H, W per channel ──────
167
+ mean = keras.ops.mean(x, axis=[1, 2], keepdims=True) # (batch, 1, 1, channels)
168
+ std = keras.ops.std(x, axis=[1, 2], keepdims=True) + 1e-8
169
+
170
+ normalized_x = (x - mean) / std
171
+
172
+ # ── single affine β†’ split into ys and yb ─────────────
173
+ style = self.style_transform(w) # (batch, 2 * channels)
174
+ ys, yb = keras.ops.split(style, 2, axis=-1) # each (batch, channels)
175
+
176
+ ys = keras.ops.reshape(ys, [-1, 1, 1, self.channels])
177
+ yb = keras.ops.reshape(yb, [-1, 1, 1, self.channels])
178
+
179
+ return ys * normalized_x + yb
180
+
181
+ # ─── Noise Injection ──────────────────────────────────────
182
+ class NoiseInjection(keras.layers.Layer):
183
+ def __init__(self, channels):
184
+ super().__init__()
185
+ self.B = self.add_weight(
186
+ shape=(1, 1, 1, channels),
187
+ initializer="zeros",
188
+ trainable=True,
189
+ name="noise_scale"
190
+ )
191
+
192
+ def call(self, x, rng_key):
193
+ batch = keras.ops.shape(x)[0]
194
+ h = keras.ops.shape(x)[1]
195
+ w = keras.ops.shape(x)[2]
196
+ noise = jax.random.normal(rng_key, shape=(batch, h, w, 1)) # ← pure JAX rng
197
+ return x + self.B * noise
198
+
199
+
200
+ # ─── StyleGAN Block ───────────────────────────────────────
201
+ class StyleBlock(keras.layers.Layer):
202
+ def __init__(self, channels, w_dim=512):
203
+ super().__init__()
204
+ self.conv = EqualizedConv2D(channels, 4)
205
+ self.noise = NoiseInjection(channels)
206
+ self.adain = AdaIN(channels, w_dim)
207
+ self.act = keras.layers.LeakyReLU(0.2)
208
+
209
+ def call(self, x, w,rng_key):
210
+ x = self.conv(x) # Conv 3x3
211
+ x = self.noise(x,rng_key)
212
+ x = self.act(x)
213
+ x = self.adain(x, w) # AdaIN with style from w
214
+ return x
215
+
216
+ class StyleGAN_Generator(keras.Model):
217
+ def __init__(self):
218
+ super().__init__()
219
+
220
+ self.mapping_network = build_mapping_network()
221
+
222
+ self.const = self.add_weight(
223
+ shape=(1, 4, 4, 512),
224
+ initializer="ones",
225
+ trainable=True,
226
+ name="const"
227
+ )
228
+
229
+ # ── register via setattr so Keras tracks them ─────
230
+ for res in RESOLUTIONS:
231
+ setattr(self, f"block_{res}_0", StyleBlock(RES_TO_FILTERS[res]))
232
+ setattr(self, f"block_{res}_1", StyleBlock(RES_TO_FILTERS[res]))
233
+ setattr(self, f"to_rgb_{res}", toRGB())
234
+
235
+ self.upsample = keras.layers.UpSampling2D(size=2,interpolation="bilinear")
236
+ self.current_resolution = 4
237
+
238
+ def call(self, z, alpha, rng_key):
239
+
240
+ w = self.mapping_network(z)
241
+
242
+ batch = keras.ops.shape(z)[0]
243
+ x = keras.ops.tile(self.const, [batch, 1, 1, 1])
244
+
245
+ rng_key, subkey = jax.random.split(rng_key)
246
+ x = getattr(self, "block_4_0")(x, w, subkey)
247
+ rng_key, subkey = jax.random.split(rng_key)
248
+ x = getattr(self, "block_4_1")(x, w, subkey)
249
+
250
+ if self.current_resolution == 4:
251
+ return getattr(self, "to_rgb_4")(x)
252
+
253
+ for res in RESOLUTIONS[1:]:
254
+ x_prev = x
255
+ x = self.upsample(x)
256
+ rng_key, subkey = jax.random.split(rng_key)
257
+ x = getattr(self, f"block_{res}_0")(x, w, subkey)
258
+ rng_key, subkey = jax.random.split(rng_key)
259
+ x = getattr(self, f"block_{res}_1")(x, w, subkey)
260
+
261
+ if res == self.current_resolution:
262
+ old_rgb = getattr(self, f"to_rgb_{res // 2}")(self.upsample(x_prev))
263
+ new_rgb = getattr(self, f"to_rgb_{res}")(x)
264
+ return (1 - alpha) * old_rgb + alpha * new_rgb
265
+
266
+ return getattr(self, f"to_rgb_{self.current_resolution}")(x)
267
+
268
+
269
+ class ProGAN_Discriminator(keras.Model):
270
+ def __init__(self):
271
+ super().__init__()
272
+
273
+ # ── register via setattr so Keras tracks them ─────
274
+ for res in RESOLUTIONS[1:]:
275
+ setattr(self, f"block_{res}", DiscBlock(RES_TO_FILTERS[res], RES_TO_FILTERS[res // 2]))
276
+ for res in RESOLUTIONS:
277
+ setattr(self, f"from_rgb_{res}", fromRGB(RES_TO_FILTERS[res]))
278
+
279
+ self.minibatch_std = MinibatchStd()
280
+ self.final_conv = EqualizedConv2D(512, kernel_size=3)
281
+ self.final_dense_1 = EqualizedDense(512) # ← missing intermediate dense
282
+ self.final_dense_2 = EqualizedDense(1)
283
+ self.flatten = keras.layers.Flatten()
284
+ self.act = keras.layers.LeakyReLU(0.2)
285
+ self.downsample = keras.layers.AveragePooling2D(pool_size=2)
286
+ self.current_resolution = 4
287
+
288
+ def call(self, img, alpha):
289
+ if self.current_resolution == 4:
290
+ x = getattr(self, "from_rgb_4")(img)
291
+ x = self.minibatch_std(x)
292
+ x = self.act(self.final_conv(x))
293
+ x = self.flatten(x)
294
+ x = self.act(self.final_dense_1(x)) # ← intermediate dense
295
+ return self.final_dense_2(x)
296
+
297
+ cur_res = self.current_resolution
298
+ prev_res = cur_res // 2
299
+
300
+ x_new = getattr(self, f"from_rgb_{cur_res}")(img)
301
+ x_new = getattr(self, f"block_{cur_res}")(x_new)
302
+
303
+ x_old = self.downsample(img)
304
+ x_old = getattr(self, f"from_rgb_{prev_res}")(x_old)
305
+
306
+ x = (1 - alpha) * x_old + alpha * x_new
307
+
308
+ for res in reversed(RESOLUTIONS[1:]):
309
+ if res >= cur_res:
310
+ continue
311
+ x = getattr(self, f"block_{res}")(x)
312
+
313
+ x = self.minibatch_std(x)
314
+ x = self.act(self.final_conv(x))
315
+ x = self.flatten(x)
316
+ x = self.act(self.final_dense_1(x)) # ← intermediate dense
317
+ return self.final_dense_2(x)
loading_model.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from imports import * # gets all the libs
2
+ from layers import *
3
+ # ─── build models ────────────────────────────────────────
4
+ generator = StyleGAN_Generator()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # StyleGAN v1 – Gradio + Hugging Face Space
2
+ gradio>=4.0.0
3
+ keras>=3.0.0
4
+ jax>=0.4.0
5
+ jaxlib>=0.4.0
6
+ numpy>=1.24.0,<2.0.0