firgiawann commited on
Commit
3f8003f
·
verified ·
1 Parent(s): 919efac

Update 37 run training preparation in background worker

Browse files
CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
  # Changelog
2
 
 
 
 
 
 
 
3
  ## Update ke-36 - 2026-05-01
4
  - Reset alur training agar `best.pt` dan artefak training lama bisa dikosongkan tanpa menghapus opsi `Best.pt` dari UI.
5
  - `Best.pt` tidak lagi fallback diam-diam ke `YOLOv8n`; jika belum ada, aplikasi menampilkan pesan model belum tersedia.
 
1
  # Changelog
2
 
3
+ ## Update ke-37 - 2026-05-01
4
+ - Perbaiki training yang berhenti pada tahap `preparing_dataset`.
5
+ - Persiapan dataset, pseudo-label, training YOLO, penyimpanan `best.pt`, grafik PNG, confusion matrix, log, dan sync artefak kini berjalan dalam background worker sejak awal.
6
+ - Tombol training sekarang hanya memulai worker dan langsung mengembalikan status, sehingga proses tidak bergantung pada koneksi browser/request Gradio.
7
+ - Minor update dinaikkan ke-37.
8
+
9
  ## Update ke-36 - 2026-05-01
10
  - Reset alur training agar `best.pt` dan artefak training lama bisa dikosongkan tanpa menghapus opsi `Best.pt` dari UI.
11
  - `Best.pt` tidak lagi fallback diam-diam ke `YOLOv8n`; jika belum ada, aplikasi menampilkan pesan model belum tersedia.
README.md CHANGED
@@ -51,6 +51,7 @@ Versi ini difokuskan untuk tahap awal:
51
  - file `training.log`, `training_curve_latest.png`, dan `confusion_matrix_latest.png` juga disimpan lokal
52
  - jika env Hugging Face diisi, `best.pt` dan artefak training otomatis di-sync ke repo HF
53
  - grafik training disimpan permanen sebagai gambar PNG di `training_outputs/training_curve_latest.png` dan `space_training_archive/<run>/training_curve.png`
 
54
  - **Tahap 3 - Input Gambar**
55
  - input gambar (upload/webcam)
56
  - tombol proses
@@ -128,6 +129,7 @@ Catatan auto-save hasil training:
128
  - File grafik terbaru juga disalin ke `training_outputs/training_curve_latest.png` agar tetap bisa ditampilkan ulang setelah Space rebuild.
129
  - Proses training juga langsung menyalin grafik YOLO `results.png` menjadi PNG permanen saat training selesai, sehingga tidak bergantung pada refresh browser.
130
  - Jika koneksi browser terputus saat training, artefak tetap aman setelah proses training selesai dan sync ke repo berhasil. Jika container berhenti sebelum training selesai, artefak final memang belum sempat dibuat.
 
131
 
132
  Catatan auto-sync update file repo:
133
  - Default `HF_AUTOSYNC_ENABLED=false`, jadi tidak ada sync repo saat startup/runtime.
 
51
  - file `training.log`, `training_curve_latest.png`, dan `confusion_matrix_latest.png` juga disimpan lokal
52
  - jika env Hugging Face diisi, `best.pt` dan artefak training otomatis di-sync ke repo HF
53
  - grafik training disimpan permanen sebagai gambar PNG di `training_outputs/training_curve_latest.png` dan `space_training_archive/<run>/training_curve.png`
54
+ - persiapan dataset dan pseudo-label berjalan di background worker, sehingga training tidak berhenti hanya karena koneksi browser terputus
55
  - **Tahap 3 - Input Gambar**
56
  - input gambar (upload/webcam)
57
  - tombol proses
 
129
  - File grafik terbaru juga disalin ke `training_outputs/training_curve_latest.png` agar tetap bisa ditampilkan ulang setelah Space rebuild.
130
  - Proses training juga langsung menyalin grafik YOLO `results.png` menjadi PNG permanen saat training selesai, sehingga tidak bergantung pada refresh browser.
131
  - Jika koneksi browser terputus saat training, artefak tetap aman setelah proses training selesai dan sync ke repo berhasil. Jika container berhenti sebelum training selesai, artefak final memang belum sempat dibuat.
132
+ - Pseudo-label dari dataset tanpa label `.txt` dapat memakan waktu di CPU Space karena semua gambar harus diproses YOLOv8n sebelum epoch dimulai.
133
 
134
  Catatan auto-sync update file repo:
135
  - Default `HF_AUTOSYNC_ENABLED=false`, jadi tidak ada sync repo saat startup/runtime.
app.py CHANGED
@@ -24,7 +24,7 @@ from ultralytics import YOLO
24
  CM_INIT = {"tp": 0, "fp": 0, "fn": 0, "tn": 0, "person": 0, "not_person": 0}
25
  EVAL_CLASSES = ["Person", "Not Person/Background"]
26
  APP_VERSION = os.getenv("APP_VERSION", "v0.9.0")
27
- APP_MINOR_UPDATE = os.getenv("APP_MINOR_UPDATE", "36")
28
 
29
 
30
  # Gradio/Spaces can trigger noisy asyncio cleanup traces ("Invalid file descriptor: -1")
@@ -1662,6 +1662,144 @@ def poll_training_status() -> Tuple[str, Any, Any, Any, Any, np.ndarray, Any, np
1662
  )
1663
 
1664
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1665
  def run_training(
1666
  train_model_choice: str,
1667
  epochs: float,
@@ -1681,198 +1819,27 @@ def run_training(
1681
  return poll_training_status()
1682
 
1683
  try:
1684
- _write_status(
1685
- paths["status"],
1686
- {
1687
- "status": "running",
1688
- "stage": "preparing_dataset",
1689
- "run_name": current.get("run_name", "-"),
1690
- "started_at": datetime.now().isoformat(),
1691
- },
1692
- )
1693
- with paths["log"].open("a", encoding="utf-8") as lf:
1694
- lf.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Preparing dataset...\n")
1695
-
1696
- dataset_root = base_dir / "dataset"
1697
- data_yaml, prep_info = _prepare_training_dataset(
1698
- dataset_root=dataset_root,
1699
- use_pseudo_if_missing=use_pseudo_if_missing,
1700
- pseudo_conf=float(pseudo_conf),
1701
- log_file=paths["log"],
1702
- )
1703
-
1704
- if train_model_choice == "Best.pt":
1705
- model_path = resolve_model_path("Best.pt")
1706
- else:
1707
- model_path = str(base_dir / "yolov8n.pt") if (base_dir / "yolov8n.pt").exists() else resolve_model_path("YOLOv8n")
1708
  run_name = f"classroom_train_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
1709
  project_dir = base_dir / "runs_train"
1710
  save_dir = project_dir / run_name
1711
  results_csv = save_dir / "results.csv"
1712
-
1713
- with paths["log"].open("a", encoding="utf-8") as lf:
1714
- lf.write(f"\n[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Start training {run_name}\n")
1715
- lf.write(f"Model awal training: {train_model_choice} ({model_path})\n")
1716
- lf.write(f"{prep_info}\n")
1717
-
1718
- train_code = r"""
1719
- import os, json, shutil
1720
- from datetime import datetime
1721
- from pathlib import Path
1722
- from ultralytics import YOLO
1723
- from huggingface_hub import HfApi
1724
-
1725
- def write_status(path, payload):
1726
- p = Path(path)
1727
- tmp = p.with_suffix(".tmp")
1728
- tmp.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
1729
- tmp.replace(p)
1730
-
1731
- status_path = os.environ["TRAIN_STATUS_PATH"]
1732
- base_dir = Path(os.environ["TRAIN_BASE_DIR"])
1733
- run_name = os.environ["TRAIN_RUN_NAME"]
1734
- save_dir = Path(os.environ["TRAIN_SAVE_DIR"])
1735
- results_csv = save_dir / "results.csv"
1736
-
1737
- state = {
1738
- "status": "running",
1739
- "run_name": run_name,
1740
- "pid": os.getpid(),
1741
- "save_dir": str(save_dir),
1742
- "results_csv": str(results_csv),
1743
- "started_at": datetime.now().isoformat(),
1744
- }
1745
- write_status(status_path, state)
1746
-
1747
- try:
1748
- model = YOLO(os.environ["TRAIN_MODEL_PATH"])
1749
- res = model.train(
1750
- data=os.environ["TRAIN_DATA_YAML"],
1751
- epochs=int(os.environ["TRAIN_EPOCHS"]),
1752
- imgsz=int(os.environ["TRAIN_IMGSZ"]),
1753
- batch=int(os.environ["TRAIN_BATCH"]),
1754
- device="cpu",
1755
- project=os.environ["TRAIN_PROJECT_DIR"],
1756
- name=run_name,
1757
- exist_ok=False,
1758
- verbose=True,
1759
- )
1760
- final_save = Path(str(res.save_dir))
1761
- best_pt = final_save / "weights" / "best.pt"
1762
- best_out = base_dir / "best.pt"
1763
- artifact_dir = base_dir / "training_outputs"
1764
- artifact_dir.mkdir(parents=True, exist_ok=True)
1765
- chart_out = artifact_dir / "training_curve_latest.png"
1766
- cm_out = artifact_dir / "confusion_matrix_latest.png"
1767
- summary_out = artifact_dir / "training_summary_latest.json"
1768
-
1769
- chart_src = final_save / "results.png"
1770
- if chart_src.exists():
1771
- shutil.copy2(chart_src, chart_out)
1772
-
1773
- for cm_src in (final_save / "confusion_matrix.png", final_save / "confusion_matrix_normalized.png"):
1774
- if cm_src.exists():
1775
- shutil.copy2(cm_src, cm_out)
1776
- break
1777
-
1778
- hf_sync_msg = "HF sync dilewati: best.pt tidak ditemukan."
1779
- if best_pt.exists():
1780
- shutil.copy2(best_pt, best_out)
1781
- hf_repo = os.getenv("HF_MODEL_REPO", "").strip()
1782
- hf_file = os.getenv("HF_MODEL_FILE", "best.pt").strip() or "best.pt"
1783
- hf_repo_type = os.getenv("HF_MODEL_REPO_TYPE", "space").strip() or "space"
1784
- hf_token = os.getenv("HF_TOKEN", "").strip() or None
1785
- if hf_repo:
1786
- api = HfApi(token=hf_token)
1787
- api.upload_file(
1788
- path_or_fileobj=str(best_out),
1789
- path_in_repo=hf_file,
1790
- repo_id=hf_repo,
1791
- repo_type=hf_repo_type,
1792
- commit_message=f"Auto-update {hf_file} ({datetime.now().strftime('%Y-%m-%d %H:%M:%S')})",
1793
- )
1794
- hf_sync_msg = f"HF sync sukses: {hf_repo} ({hf_repo_type}) -> {hf_file}"
1795
-
1796
- artifact_sync_msg = "HF training artifact sync dilewati: set HF_MODEL_REPO."
1797
- try:
1798
- hf_repo = os.getenv("HF_MODEL_REPO", "").strip()
1799
- hf_repo_type = os.getenv("HF_MODEL_REPO_TYPE", "space").strip() or "space"
1800
- hf_token = os.getenv("HF_TOKEN", "").strip() or None
1801
- repo_dir = os.getenv("HF_TRAINING_DIR", "space_training_archive").strip().strip("/") or "space_training_archive"
1802
- if hf_repo:
1803
- api = HfApi(token=hf_token)
1804
- summary_payload = {
1805
- "saved_at": datetime.now().isoformat(),
1806
- "status": "done",
1807
- "run_name": run_name,
1808
- "save_dir": str(final_save),
1809
- "results_csv": str(final_save / "results.csv"),
1810
- "best_path": str(best_out if best_out.exists() else best_pt),
1811
- "hf_sync_msg": hf_sync_msg,
1812
- }
1813
- summary_out.write_text(json.dumps(summary_payload, ensure_ascii=False, indent=2), encoding="utf-8")
1814
- upload_items = [
1815
- (final_save / "results.csv", f"{repo_dir}/{run_name}/results.csv"),
1816
- (Path(os.environ["TRAIN_LOG_PATH"]), f"{repo_dir}/{run_name}/training.log"),
1817
- (chart_out, f"{repo_dir}/{run_name}/training_curve.png"),
1818
- (cm_out, f"{repo_dir}/{run_name}/confusion_matrix.png"),
1819
- (summary_out, f"{repo_dir}/{run_name}/training_summary.json"),
1820
- (chart_out, "training_outputs/training_curve_latest.png"),
1821
- (cm_out, "training_outputs/confusion_matrix_latest.png"),
1822
- (summary_out, "training_outputs/training_summary_latest.json"),
1823
- ]
1824
- uploaded = 0
1825
- for local_path, repo_path in upload_items:
1826
- if local_path.exists() and local_path.is_file():
1827
- api.upload_file(
1828
- path_or_fileobj=str(local_path),
1829
- path_in_repo=repo_path,
1830
- repo_id=hf_repo,
1831
- repo_type=hf_repo_type,
1832
- commit_message=f"Save permanent training artifact {run_name}",
1833
- )
1834
- uploaded += 1
1835
- artifact_sync_msg = f"HF training artifact sync sukses: {uploaded} file -> {repo_dir}/{run_name}"
1836
- except Exception as sync_err:
1837
- artifact_sync_msg = f"HF training artifact sync gagal: {sync_err}"
1838
-
1839
- state.update({
1840
- "status": "done",
1841
- "ended_at": datetime.now().isoformat(),
1842
- "save_dir": str(final_save),
1843
- "results_csv": str(final_save / "results.csv"),
1844
- "best_path": str(best_out if best_out.exists() else best_pt),
1845
- "hf_sync_msg": hf_sync_msg,
1846
- "artifacts_sync_attempted": True,
1847
- "artifacts_sync_msg": artifact_sync_msg,
1848
- })
1849
- write_status(status_path, state)
1850
- print("TRAIN_DONE", flush=True)
1851
- except Exception as e:
1852
- state.update({
1853
- "status": "failed",
1854
- "ended_at": datetime.now().isoformat(),
1855
- "error": str(e),
1856
- })
1857
- write_status(status_path, state)
1858
- print("TRAIN_FAILED: " + str(e), flush=True)
1859
- """
1860
  env = os.environ.copy()
1861
  env["TRAIN_BASE_DIR"] = str(base_dir)
1862
- env["TRAIN_MODEL_PATH"] = str(model_path)
1863
- env["TRAIN_DATA_YAML"] = str(data_yaml)
1864
  env["TRAIN_EPOCHS"] = str(int(epochs))
1865
  env["TRAIN_IMGSZ"] = str(int(imgsz))
1866
  env["TRAIN_BATCH"] = str(int(batch))
 
 
1867
  env["TRAIN_PROJECT_DIR"] = str(project_dir)
1868
  env["TRAIN_RUN_NAME"] = run_name
1869
  env["TRAIN_SAVE_DIR"] = str(save_dir)
1870
  env["TRAIN_STATUS_PATH"] = str(paths["status"])
1871
- env["TRAIN_LOG_PATH"] = str(paths["log"])
1872
 
1873
  with paths["log"].open("a", encoding="utf-8") as lf:
 
1874
  proc = subprocess.Popen(
1875
- [sys.executable, "-u", "-c", train_code],
1876
  stdout=lf,
1877
  stderr=subprocess.STDOUT,
1878
  cwd=str(base_dir),
@@ -1884,7 +1851,7 @@ def run_training(
1884
  paths["status"],
1885
  {
1886
  "status": "running",
1887
- "stage": "training",
1888
  "run_name": run_name,
1889
  "pid": proc.pid,
1890
  "save_dir": str(save_dir),
@@ -1895,7 +1862,7 @@ def run_training(
1895
  return poll_training_status()
1896
  except Exception as err:
1897
  with paths["log"].open("a", encoding="utf-8") as lf:
1898
- lf.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Training gagal: {err}\n")
1899
  _write_status(
1900
  paths["status"],
1901
  {
@@ -2251,10 +2218,14 @@ with gr.Blocks(title="Pengecekan dan Klasifikasi Mahasiswa - YOLOv8") as demo:
2251
  )
2252
 
2253
 
2254
- if __name__ == "__main__":
2255
- if _env_true("HF_AUTOSYNC_ENABLED", default=False):
2256
- print(start_repo_autosync(Path(__file__).resolve().parent))
2257
- else:
 
 
 
 
2258
  print("HF autosync nonaktif (HF_AUTOSYNC_ENABLED=false).")
2259
  demo.queue()
2260
  demo.launch(css=CUSTOM_CSS, ssr_mode=False)
 
24
  CM_INIT = {"tp": 0, "fp": 0, "fn": 0, "tn": 0, "person": 0, "not_person": 0}
25
  EVAL_CLASSES = ["Person", "Not Person/Background"]
26
  APP_VERSION = os.getenv("APP_VERSION", "v0.9.0")
27
+ APP_MINOR_UPDATE = os.getenv("APP_MINOR_UPDATE", "37")
28
 
29
 
30
  # Gradio/Spaces can trigger noisy asyncio cleanup traces ("Invalid file descriptor: -1")
 
1662
  )
1663
 
1664
 
1665
+ def _append_training_log(log_path: Path, message: str) -> None:
1666
+ with log_path.open("a", encoding="utf-8") as lf:
1667
+ lf.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {message}\n")
1668
+
1669
+
1670
+ def training_worker_main() -> None:
1671
+ base_dir = Path(os.environ["TRAIN_BASE_DIR"]).resolve()
1672
+ paths = _train_paths(base_dir)
1673
+ run_name = os.environ["TRAIN_RUN_NAME"]
1674
+ project_dir = Path(os.environ["TRAIN_PROJECT_DIR"])
1675
+ save_dir = Path(os.environ["TRAIN_SAVE_DIR"])
1676
+ results_csv = save_dir / "results.csv"
1677
+ train_model_choice = os.environ.get("TRAIN_MODEL_CHOICE", "YOLOv8n")
1678
+
1679
+ state: Dict[str, Any] = {
1680
+ "status": "running",
1681
+ "stage": "preparing_dataset",
1682
+ "run_name": run_name,
1683
+ "pid": os.getpid(),
1684
+ "save_dir": str(save_dir),
1685
+ "results_csv": str(results_csv),
1686
+ "started_at": datetime.now().isoformat(),
1687
+ }
1688
+ _write_status(paths["status"], state)
1689
+
1690
+ try:
1691
+ _append_training_log(paths["log"], f"Worker training aktif. PID={os.getpid()}")
1692
+ _append_training_log(paths["log"], "Preparing dataset dan pseudo-label di background...")
1693
+ data_yaml, prep_info = _prepare_training_dataset(
1694
+ dataset_root=base_dir / "dataset",
1695
+ use_pseudo_if_missing=_env_true("TRAIN_USE_PSEUDO", default=True),
1696
+ pseudo_conf=float(os.environ.get("TRAIN_PSEUDO_CONF", "0.30")),
1697
+ log_file=paths["log"],
1698
+ )
1699
+
1700
+ model_path = resolve_model_path("Best.pt" if train_model_choice == "Best.pt" else "YOLOv8n")
1701
+ state.update(
1702
+ {
1703
+ "stage": "training",
1704
+ "data_yaml": str(data_yaml),
1705
+ "model_path": str(model_path),
1706
+ }
1707
+ )
1708
+ _write_status(paths["status"], state)
1709
+ _append_training_log(paths["log"], f"Dataset siap: {prep_info}")
1710
+ _append_training_log(paths["log"], f"Start training {run_name}")
1711
+ _append_training_log(paths["log"], f"Model awal training: {train_model_choice} ({model_path})")
1712
+
1713
+ model = YOLO(str(model_path))
1714
+ res = model.train(
1715
+ data=str(data_yaml),
1716
+ epochs=int(os.environ["TRAIN_EPOCHS"]),
1717
+ imgsz=int(os.environ["TRAIN_IMGSZ"]),
1718
+ batch=int(os.environ["TRAIN_BATCH"]),
1719
+ device="cpu",
1720
+ project=str(project_dir),
1721
+ name=run_name,
1722
+ exist_ok=False,
1723
+ verbose=True,
1724
+ )
1725
+
1726
+ final_save = Path(str(res.save_dir))
1727
+ final_results_csv = final_save / "results.csv"
1728
+ best_pt = final_save / "weights" / "best.pt"
1729
+ best_out = base_dir / "best.pt"
1730
+ hf_sync_msg = "HF sync dilewati: best.pt tidak ditemukan."
1731
+ if best_pt.exists() and best_pt.is_file():
1732
+ shutil.copy2(best_pt, best_out)
1733
+ hf_sync_msg = sync_best_to_hf(best_out)
1734
+
1735
+ chart_file: str | None = None
1736
+ chart_src = final_save / "results.png"
1737
+ if chart_src.exists() and chart_src.is_file():
1738
+ paths["chart"].parent.mkdir(parents=True, exist_ok=True)
1739
+ shutil.copy2(chart_src, paths["chart"])
1740
+ chart_file = str(paths["chart"])
1741
+ elif final_results_csv.exists() and final_results_csv.is_file():
1742
+ chart_file = save_training_curve_image(plot_training_curves(final_results_csv), paths["chart"])
1743
+
1744
+ cm_file = save_training_confusion_matrix({"save_dir": str(final_save)}, paths["train_cm"])
1745
+ rows = _read_results_rows(final_results_csv if final_results_csv.exists() else None)
1746
+ final_state = {
1747
+ **state,
1748
+ "status": "done",
1749
+ "stage": "done",
1750
+ "ended_at": datetime.now().isoformat(),
1751
+ "save_dir": str(final_save),
1752
+ "results_csv": str(final_results_csv),
1753
+ "best_path": str(best_out if best_out.exists() else best_pt),
1754
+ "hf_sync_msg": hf_sync_msg,
1755
+ }
1756
+ summary_file = save_training_summary(paths, final_state, len(rows))
1757
+ artifact_sync_msg = sync_training_artifacts_to_hf(
1758
+ run_name=run_name,
1759
+ results_csv=final_results_csv if final_results_csv.exists() else None,
1760
+ log_file=paths["log"] if paths["log"].exists() else None,
1761
+ chart_file=Path(chart_file) if chart_file else None,
1762
+ confusion_matrix_file=Path(cm_file) if cm_file else None,
1763
+ summary_file=Path(summary_file),
1764
+ )
1765
+ final_state.update(
1766
+ {
1767
+ "artifacts_sync_attempted": True,
1768
+ "artifacts_sync_msg": artifact_sync_msg,
1769
+ }
1770
+ )
1771
+ save_training_summary(paths, final_state, len(rows))
1772
+ _write_status(paths["status"], final_state)
1773
+ _append_training_log(paths["log"], "TRAIN_DONE")
1774
+ except Exception as err:
1775
+ _append_training_log(paths["log"], f"TRAIN_FAILED: {err}")
1776
+ failed_state = {
1777
+ **state,
1778
+ "status": "failed",
1779
+ "stage": "failed",
1780
+ "ended_at": datetime.now().isoformat(),
1781
+ "error": str(err),
1782
+ }
1783
+ summary_file = save_training_summary(paths, failed_state, 0)
1784
+ artifact_sync_msg = sync_training_artifacts_to_hf(
1785
+ run_name=run_name,
1786
+ results_csv=None,
1787
+ log_file=paths["log"] if paths["log"].exists() else None,
1788
+ chart_file=paths["chart"] if paths["chart"].exists() else None,
1789
+ confusion_matrix_file=paths["train_cm"] if paths["train_cm"].exists() else None,
1790
+ summary_file=Path(summary_file),
1791
+ )
1792
+ failed_state.update(
1793
+ {
1794
+ "artifacts_sync_attempted": True,
1795
+ "artifacts_sync_msg": artifact_sync_msg,
1796
+ }
1797
+ )
1798
+ save_training_summary(paths, failed_state, 0)
1799
+ _write_status(paths["status"], failed_state)
1800
+ raise
1801
+
1802
+
1803
  def run_training(
1804
  train_model_choice: str,
1805
  epochs: float,
 
1819
  return poll_training_status()
1820
 
1821
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1822
  run_name = f"classroom_train_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
1823
  project_dir = base_dir / "runs_train"
1824
  save_dir = project_dir / run_name
1825
  results_csv = save_dir / "results.csv"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1826
  env = os.environ.copy()
1827
  env["TRAIN_BASE_DIR"] = str(base_dir)
1828
+ env["TRAIN_MODEL_CHOICE"] = str(train_model_choice)
 
1829
  env["TRAIN_EPOCHS"] = str(int(epochs))
1830
  env["TRAIN_IMGSZ"] = str(int(imgsz))
1831
  env["TRAIN_BATCH"] = str(int(batch))
1832
+ env["TRAIN_USE_PSEUDO"] = "true" if use_pseudo_if_missing else "false"
1833
+ env["TRAIN_PSEUDO_CONF"] = str(float(pseudo_conf))
1834
  env["TRAIN_PROJECT_DIR"] = str(project_dir)
1835
  env["TRAIN_RUN_NAME"] = run_name
1836
  env["TRAIN_SAVE_DIR"] = str(save_dir)
1837
  env["TRAIN_STATUS_PATH"] = str(paths["status"])
 
1838
 
1839
  with paths["log"].open("a", encoding="utf-8") as lf:
1840
+ lf.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Worker background dimulai: {run_name}\n")
1841
  proc = subprocess.Popen(
1842
+ [sys.executable, "-u", __file__, "--train-worker"],
1843
  stdout=lf,
1844
  stderr=subprocess.STDOUT,
1845
  cwd=str(base_dir),
 
1851
  paths["status"],
1852
  {
1853
  "status": "running",
1854
+ "stage": "preparing_dataset",
1855
  "run_name": run_name,
1856
  "pid": proc.pid,
1857
  "save_dir": str(save_dir),
 
1862
  return poll_training_status()
1863
  except Exception as err:
1864
  with paths["log"].open("a", encoding="utf-8") as lf:
1865
+ lf.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Training gagal sebelum worker aktif: {err}\n")
1866
  _write_status(
1867
  paths["status"],
1868
  {
 
2218
  )
2219
 
2220
 
2221
+ if __name__ == "__main__":
2222
+ if len(sys.argv) > 1 and sys.argv[1] == "--train-worker":
2223
+ training_worker_main()
2224
+ raise SystemExit(0)
2225
+
2226
+ if _env_true("HF_AUTOSYNC_ENABLED", default=False):
2227
+ print(start_repo_autosync(Path(__file__).resolve().parent))
2228
+ else:
2229
  print("HF autosync nonaktif (HF_AUTOSYNC_ENABLED=false).")
2230
  demo.queue()
2231
  demo.launch(css=CUSTOM_CSS, ssr_mode=False)
space_training_archive/-/training.log DELETED
@@ -1,4 +0,0 @@
1
-
2
- [2026-05-01 16:09:37] Trigger training request
3
- [2026-05-01 16:09:37] Preparing dataset...
4
- [2026-05-01 16:09:37] Prepare split `train`: total gambar=80
 
 
 
 
 
space_training_archive/-/training_summary.json DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "saved_at": "2026-05-01T16:09:38.448561",
3
- "status": "failed",
4
- "stage": "preparing_dataset",
5
- "run_name": "-",
6
- "rows_count": 0,
7
- "results_csv": null,
8
- "best_path": null,
9
- "hf_sync_msg": null,
10
- "error": "Proses berhenti tanpa status akhir."
11
- }
 
 
 
 
 
 
 
 
 
 
 
 
training_outputs/training_summary_latest.json DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "saved_at": "2026-05-01T16:09:38.448561",
3
- "status": "failed",
4
- "stage": "preparing_dataset",
5
- "run_name": "-",
6
- "rows_count": 0,
7
- "results_csv": null,
8
- "best_path": null,
9
- "hf_sync_msg": null,
10
- "error": "Proses berhenti tanpa status akhir."
11
- }