run_prod.py
BIN
Scene.jpeg
Normal file
|
After Width: | Height: | Size: 215 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
BIN
inference_results/Scene_frame000000.jpg
Normal file
|
After Width: | Height: | Size: 532 KiB |
@ -33,7 +33,7 @@ Usage
|
|||||||
# First time — creates the Kaggle dataset and notebook from scratch
|
# First time — creates the Kaggle dataset and notebook from scratch
|
||||||
python kaggle_train.py \
|
python kaggle_train.py \
|
||||||
--dataset_dir crops_dataset \
|
--dataset_dir crops_dataset \
|
||||||
--notebook train_classifier_kaggle.ipynb \
|
--notebook 2_product-classifier.ipynb \
|
||||||
--dataset_name my-product-crops \
|
--dataset_name my-product-crops \
|
||||||
--kernel_name product-classifier-train \
|
--kernel_name product-classifier-train \
|
||||||
--output_dir runs/classify
|
--output_dir runs/classify
|
||||||
@ -41,16 +41,16 @@ python kaggle_train.py \
|
|||||||
# Subsequent runs — detects existing dataset/kernel and updates them
|
# Subsequent runs — detects existing dataset/kernel and updates them
|
||||||
python kaggle_train.py \
|
python kaggle_train.py \
|
||||||
--dataset_dir crops_dataset \
|
--dataset_dir crops_dataset \
|
||||||
--notebook train_classifier_kaggle.ipynb \
|
--notebook 2_product-classifier.ipynb \
|
||||||
--dataset_name my-product-crops \
|
--dataset_name my-product-crops \
|
||||||
--kernel_name product-classifier-train
|
--kernel_name product-classifier-train
|
||||||
|
|
||||||
Arguments
|
Arguments
|
||||||
---------
|
---------
|
||||||
--dataset_dir Local crops_dataset/ folder to upload (default: crops_dataset)
|
--dataset_dir Local crops_dataset/ folder to upload (default: crops_dataset)
|
||||||
--notebook Kaggle notebook .ipynb file (default: train_classifier_kaggle.ipynb)
|
--notebook Kaggle notebook .ipynb file (default: 2_product-classifier.ipynb)
|
||||||
--dataset_name Kaggle dataset slug (lowercase, hyphens) (default: product-crops)
|
--dataset_name Kaggle dataset slug (lowercase, hyphens) (default: product-dataset)
|
||||||
--kernel_name Kaggle kernel slug (lowercase, hyphens) (default: product-classifier)
|
--kernel_name Kaggle kernel slug (lowercase, hyphens) (default: product-predection)
|
||||||
--output_dir Where to save downloaded weights (default: runs/classify)
|
--output_dir Where to save downloaded weights (default: runs/classify)
|
||||||
--poll_interval Seconds between status checks (default: 30)
|
--poll_interval Seconds between status checks (default: 30)
|
||||||
--timeout Max minutes to wait for kernel (default: 180)
|
--timeout Max minutes to wait for kernel (default: 180)
|
||||||
@ -79,10 +79,10 @@ def parse_args():
|
|||||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||||
)
|
)
|
||||||
p.add_argument("--dataset_dir", default="crops_dataset")
|
p.add_argument("--dataset_dir", default="crops_dataset")
|
||||||
p.add_argument("--notebook", default="train_classifier_kaggle.ipynb")
|
p.add_argument("--notebook", default="2_product-classifier.ipynb")
|
||||||
p.add_argument("--dataset_name", default="product-crops",
|
p.add_argument("--dataset_name", default="product-dataset",
|
||||||
help="Kaggle dataset slug — lowercase, hyphens, no spaces")
|
help="Kaggle dataset slug — lowercase, hyphens, no spaces")
|
||||||
p.add_argument("--kernel_name", default="product-classifier",
|
p.add_argument("--kernel_name", default="product-predection",
|
||||||
help="Kaggle kernel slug — lowercase, hyphens, no spaces")
|
help="Kaggle kernel slug — lowercase, hyphens, no spaces")
|
||||||
p.add_argument("--output_dir", default="runs/classify")
|
p.add_argument("--output_dir", default="runs/classify")
|
||||||
p.add_argument("--poll_interval", type=int, default=30,
|
p.add_argument("--poll_interval", type=int, default=30,
|
||||||
@ -132,6 +132,7 @@ def _detect_auth_method() -> str:
|
|||||||
if os.getenv("KAGGLE_USERNAME") and os.getenv("KAGGLE_KEY"):
|
if os.getenv("KAGGLE_USERNAME") and os.getenv("KAGGLE_KEY"):
|
||||||
return "env_legacy"
|
return "env_legacy"
|
||||||
if _ACCESS_TOKEN_PATH.exists():
|
if _ACCESS_TOKEN_PATH.exists():
|
||||||
|
#print(f"this is the PATH: {_ACCESS_TOKEN_PATH}")
|
||||||
return "access_token"
|
return "access_token"
|
||||||
if _KAGGLE_JSON_PATH.exists():
|
if _KAGGLE_JSON_PATH.exists():
|
||||||
return "kaggle_json"
|
return "kaggle_json"
|
||||||
@ -320,7 +321,7 @@ def patch_notebook(notebook_path: Path, dataset_ref: str,
|
|||||||
DATA_DIR = Path("/kaggle/input/your-dataset-name")
|
DATA_DIR = Path("/kaggle/input/your-dataset-name")
|
||||||
We replace the path to match the actual uploaded dataset slug.
|
We replace the path to match the actual uploaded dataset slug.
|
||||||
"""
|
"""
|
||||||
nb = json.loads(notebook_path.read_text())
|
nb = json.loads(notebook_path.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
# Extract the dataset slug from "username/slug" → "slug"
|
# Extract the dataset slug from "username/slug" → "slug"
|
||||||
ds_slug = dataset_ref.split("/")[-1]
|
ds_slug = dataset_ref.split("/")[-1]
|
||||||
@ -351,7 +352,7 @@ def patch_notebook(notebook_path: Path, dataset_ref: str,
|
|||||||
log("⚠ DATA_DIR line not found in notebook — using notebook as-is", "⚠")
|
log("⚠ DATA_DIR line not found in notebook — using notebook as-is", "⚠")
|
||||||
|
|
||||||
out_path = tmp_dir / notebook_path.name
|
out_path = tmp_dir / notebook_path.name
|
||||||
out_path.write_text(json.dumps(nb, indent=1))
|
out_path.write_text(json.dumps(nb, indent=1), encoding="utf-8")
|
||||||
return out_path
|
return out_path
|
||||||
|
|
||||||
|
|
||||||
@ -389,11 +390,15 @@ def push_kernel(api, username: str, kernel_name: str, notebook_path: Path,
|
|||||||
"competition_sources": [],
|
"competition_sources": [],
|
||||||
"kernel_sources": [],
|
"kernel_sources": [],
|
||||||
"model_sources": [],
|
"model_sources": [],
|
||||||
|
"machine_shape": "t4" if gpu else None,
|
||||||
}
|
}
|
||||||
(kernel_dir / "kernel-metadata.json").write_text(json.dumps(meta, indent=2))
|
(kernel_dir / "kernel-metadata.json").write_text(json.dumps(meta, indent=2))
|
||||||
|
|
||||||
log(f"Pushing kernel to Kaggle: {ref} (GPU={'yes' if gpu else 'no'})", "🚀")
|
log(f"Pushing kernel to Kaggle: {ref} (GPU={'yes' if gpu else 'no'})", "🚀")
|
||||||
api.kernels_push(str(kernel_dir))
|
try:
|
||||||
|
api.kernels_push(str(kernel_dir), timeout=None, acc="t4" if gpu else None)
|
||||||
|
except TypeError:
|
||||||
|
api.kernels_push(str(kernel_dir))
|
||||||
log(f"Kernel pushed — run started: https://www.kaggle.com/code/{ref}", "✓")
|
log(f"Kernel pushed — run started: https://www.kaggle.com/code/{ref}", "✓")
|
||||||
return ref
|
return ref
|
||||||
|
|
||||||
@ -405,12 +410,11 @@ def poll_kernel(api, kernel_ref: str, poll_interval: int, timeout_minutes: int)
|
|||||||
Poll the kernel until it reaches a terminal status.
|
Poll the kernel until it reaches a terminal status.
|
||||||
Returns True if successful, False if failed or timed out.
|
Returns True if successful, False if failed or timed out.
|
||||||
"""
|
"""
|
||||||
username, slug = kernel_ref.split("/")
|
|
||||||
deadline = time.time() + timeout_minutes * 60
|
deadline = time.time() + timeout_minutes * 60
|
||||||
attempt = 0
|
attempt = 0
|
||||||
|
|
||||||
TERMINAL = {"complete", "error", "cancelled"}
|
TERMINAL = {"complete", "error", "cancelled", "cancel_acknowledged"}
|
||||||
RUNNING = {"running", "queued"}
|
RUNNING = {"running", "queued", "initializing", "pending"}
|
||||||
|
|
||||||
log(f"Polling kernel status every {poll_interval}s "
|
log(f"Polling kernel status every {poll_interval}s "
|
||||||
f"(timeout {timeout_minutes} min) …", "⏳")
|
f"(timeout {timeout_minutes} min) …", "⏳")
|
||||||
@ -418,16 +422,27 @@ def poll_kernel(api, kernel_ref: str, poll_interval: int, timeout_minutes: int)
|
|||||||
while time.time() < deadline:
|
while time.time() < deadline:
|
||||||
attempt += 1
|
attempt += 1
|
||||||
try:
|
try:
|
||||||
status_obj = api.kernel_status(username, slug)
|
# kernels_status takes the full kernel ref "username/slug"
|
||||||
# The API returns an object with a .status attribute
|
status_obj = api.kernels_status(kernel_ref)
|
||||||
status = getattr(status_obj, "status", str(status_obj)).lower()
|
# Extract status from the response object
|
||||||
|
raw_status = getattr(status_obj, "status", None)
|
||||||
|
if raw_status is not None:
|
||||||
|
if hasattr(raw_status, 'name'):
|
||||||
|
status = raw_status.name.lower()
|
||||||
|
else:
|
||||||
|
status = str(raw_status).lower()
|
||||||
|
else:
|
||||||
|
status = str(status_obj).lower()
|
||||||
|
|
||||||
|
# Clean up enum prefixes
|
||||||
|
status = status.replace("kernelworkerstatus.", "")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log(f"Poll error (will retry): {e}", "⚠")
|
log(f"Poll error (will retry): {e}", "⚠")
|
||||||
time.sleep(poll_interval)
|
time.sleep(poll_interval)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
elapsed_min = (time.time() + timeout_minutes * 60 - deadline) / 60 + timeout_minutes
|
elapsed_min = (time.time() + timeout_minutes * 60 - deadline) / 60
|
||||||
print(f" [{attempt:>3}] status={status:<12} elapsed={elapsed_min:.1f} min",
|
print(f" [{attempt:>3}] status={status:<20} elapsed={elapsed_min:.1f} min",
|
||||||
end="\r", flush=True)
|
end="\r", flush=True)
|
||||||
|
|
||||||
if status == "complete":
|
if status == "complete":
|
||||||
@ -441,7 +456,7 @@ def poll_kernel(api, kernel_ref: str, poll_interval: int, timeout_minutes: int)
|
|||||||
f"https://www.kaggle.com/code/{kernel_ref}", "✗")
|
f"https://www.kaggle.com/code/{kernel_ref}", "✗")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if status == "cancelled":
|
if "cancel" in status:
|
||||||
print()
|
print()
|
||||||
log("Kernel was cancelled.", "✗")
|
log("Kernel was cancelled.", "✗")
|
||||||
return False
|
return False
|
||||||
@ -515,8 +530,8 @@ def main():
|
|||||||
|
|
||||||
# Import here so missing kaggle package gives a clean error
|
# Import here so missing kaggle package gives a clean error
|
||||||
try:
|
try:
|
||||||
from kaggle.api.kaggle_api_extended import KaggleApiExtended
|
from kaggle.api.kaggle_api_extended import KaggleApi
|
||||||
api = KaggleApiExtended()
|
api = KaggleApi()
|
||||||
api.authenticate()
|
api.authenticate()
|
||||||
except ImportError:
|
except ImportError:
|
||||||
fatal("kaggle package not installed.\n Run: pip install kaggle")
|
fatal("kaggle package not installed.\n Run: pip install kaggle")
|
||||||
|
|||||||
12
run_prod.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
"""
|
||||||
|
pip install hypercorn
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from hypercorn.config import Config
|
||||||
|
from hypercorn.asyncio import serve
|
||||||
|
from api_server import app
|
||||||
|
|
||||||
|
config = Config()
|
||||||
|
config.bind = ["0.0.0.0:2000"]
|
||||||
|
asyncio.run(serve(app, config))
|
||||||