3d-mesh-quality-control / src /14_bundle.py
kaktusik21's picture
Upload folder using huggingface_hub (part 2)
2a18db6 verified
Raw History Blame Contribute Delete
5.51 kB
# 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))