# KAGGLE: {"gpu": false, "internet": false, "kernels": ["alekstop/03-embed", "alekstop/07-finetune", "alekstop/08-stack2", "alekstop/11-release"]} # %% [markdown] # # 14 — Сборка релиза в одну папку # # Артефакты решения разбросаны по четырём ядрам, и каждое из них отдаёт вместе # с нужным файлом гигабайты ненужного (`03-embed` — 1.9 ГБ эмбеддингов ради # одного `emb_meta.json`). Ядро собирает только то, что требуется # `final_inference.ipynb`, в одну папку `release/`, которую остаётся скачать и # залить на HuggingFace. # # | что | откуда | зачем | # |---|---|---| # | `l1_*.joblib`, `l2_*.joblib`, `release_config.json` | `11-release` | ансамбль и пороги | # | `ft_fold{0..4}.pt`, `ft_meta.json` | `07-finetune` | дообученный DINOv3 | # | `pte_ft.npy`, `submission.csv` | `07`, `08` | сверка воспроизводимости | # | `emb_meta.json` | `03-embed` | какие бэкбоны и с каким разрешением считать | # %% import os, json, shutil, time from pathlib import Path T0 = time.time() WORK = Path(os.environ.get('MQC_WORK', '/kaggle/working')) IN = Path(os.environ.get('MQC_IN', '/kaggle/input')) REL = WORK / 'release' REL.mkdir(exist_ok=True) find = lambda pat: next(p for p in IN.rglob(pat)) # %% # --- копирование --- src_rel = find('release_config.json').parent n = 0 for p in sorted(src_rel.glob('*')): if p.is_file(): shutil.copy2(p, REL / p.name) n += 1 print('из 11-release:', n, 'файлов') FT_DIR = find('ft_meta.json').parent for name in ['ft_meta.json', 'pte_ft.npy', 'oof_ft.npy']: shutil.copy2(FT_DIR / name, REL / name) for p in sorted(FT_DIR.glob('ft_fold*.pt')): shutil.copy2(p, REL / p.name) print('из 07-finetune:', len(list(REL.glob('ft_fold*.pt'))), 'весов') shutil.copy2(find('emb_meta.json'), REL / 'emb_meta.json') # submission из 08 — эталон для сверки в Colab shutil.copy2(find('final_config.json').parent / 'submission.csv', REL / 'submission.csv') # %% # --- карточка репозитория --- CFG = json.load(open(REL / 'release_config.json')) EM = json.load(open(REL / 'emb_meta.json')) FTM = json.load(open(REL / 'ft_meta.json')) README = """--- license: mit tags: - 3d - mesh-quality - multi-label-classification --- # 3D Mesh Quality Control — артефакты решения Предсказание 10 дефектов 3D-меша и метки пригодности `quality` по мешу и шести рендерам карт нормалей. OOF-метрика соревнования: **f1_final = %.3f из 20** (quality %.4f, artefacts %.4f). ## Состав ансамбля | уровень | модель | OOF | |---|---|---| | 1 | дообученный %s @%d, multi-view | 14.236 | | 1 | LightGBM (эмбеддинги 4 бэкбонов + 472 признака) | 13.826 | | 1 | LightGBM (только признаки) | 13.048 | | 1 | логрег по эмбеддингам | 12.692 | | 2 | LightGBM по %d мета-признакам | 14.469 | | бленд | %.2f · уровень 2 + %.2f · дообученный ViT | **%.3f** | Замороженные бэкбоны: %s. ## Файлы * `lib_features.py`, `lib_embed.py`, `lib_model.py` — препроцессинг и модель, **сгенерированы из обучающих ядер**, а не написаны заново; * `l1_*.joblib`, `l2_*.joblib` — модели обоих уровней, по 5 фолдов; * `ft_fold{0..4}.pt` — веса дообученного ViT (fp16); * `release_config.json` — пороги пер-лейбл, состав ансамбля, порядок колонок; * `submission.csv`, `pte_ft.npy` — эталоны для сверки воспроизводимости. ## Использование Ноутбук `final_inference.ipynb` скачивает этот репозиторий целиком (`snapshot_download`, токен не нужен), считает признаки и выдаёт `submission.csv`, сверяя его с приложенным эталоном. """ % (CFG['oof_final'], CFG.get('oof_quality', 0.7442), CFG.get('oof_artefacts', 0.7114), FTM['backbone'], FTM['img'], 4 * 11 + 4, 1 - CFG['blend_w'], CFG['blend_w'], CFG['oof_final'], ', '.join(CFG['emb_order'])) (REL / 'README.md').write_text(README, encoding='utf-8') # %% # --- проверка комплектности --- need = (['release_config.json', 'emb_meta.json', 'ft_meta.json', 'pte_ft.npy', 'submission.csv', 'README.md'] + ['ft_fold%d.pt' % k for k in range(CFG['nfold'])] + ['l2_f%d_%s.joblib' % (k, t) for k in range(CFG['nfold']) for t in CFG['targets'] if t != 'intersection']) miss = [f for f in need if not (REL / f).exists()] print('НЕ ХВАТАЕТ:' if miss else 'комплект полный', miss[:10]) files = sorted(REL.glob('*')) size = sum(p.stat().st_size for p in files) print('релиз: %d файлов, %.1f МБ' % (len(files), size / 1e6)) for p in files: if p.stat().st_size > 5e6: print(' %-28s %7.1f МБ' % (p.name, p.stat().st_size / 1e6)) print('всего: %.1f мин' % ((time.time() - T0) / 60))